// ****************************************************************

//      Includes

// ****************************************************************

 

#include <Mega32.h>              

#include <stdio.h>

#include <string.h>

#include <lcd.h>

#include <math.h>

#include "ls7266r1.h"

#include "uart.h"

#include "steps_lookup.h"

 

// ****************************************************************

//      LCD Setup

// ****************************************************************

 

// LCD PORT = B (0x18)

#asm

    .equ __lcd_port=0x18

#endasm

#define LCDwidth 16 // 16 character width

 

// ****************************************************************

//     Instructions Setup

// ****************************************************************

 

// Instruction values

#define INST_ACCEL_TIME 0

#define INST_BASE_RATE 1

#define INST_CONV_UNITS 2

#define INST_STEADY_RATE 3

#define INST_BACKLASH 4

#define INST_SLIP_WAIT 5

#define INST_ENABLE_DRIVE 6

#define INST_ASSOC_LIMITS 7

#define INST_DIAL_INDICATOR 8

#define INST_LCD_CONTROL 9

#define INST_MID_STATUS 10

#define INST_USER_POSITION 11

#define INST_MULTIPLEXER_SET 12

#define INST_LOWER_SLIMIT 13

#define INST_UPPER_SLIMIT 14

#define INST_DECODER_A_POS 15

#define INST_DECODER_B_POS 16

#define INST_LOWER_HLIMIT 17

#define INST_UPPER_HLIMIT 18

#define INST_PROGRAM_DECODER 19

#define INST_TWEEK_MOTOR 20

#define INST_MOVE_INCREMENTAL 21

#define INST_MOVE_ABSOLUTE 22 

#define INST_MOVE_MICROSTEP 23

 

// Limits on parameters

#define MIN_ACCEL_TIME 128

#define MAX_ACCEL_TIME 2048

#define MIN_BASE_RATE 200

#define MAX_BASE_RATE 2048

#define MIN_CONV_UNITS 0

#define MAX_CONV_UNITS 50000

#define MIN_STEADY_RATE 200

#define MAX_STEADY_RATE 5000

#define MIN_BACKLASH 0

#define MAX_BACKLASH 5000

 

// Return values

#define RTRN_OK 0

#define RTRN_NOT_VALID 1

#define RTRN_MOVE_STOPPED 2

#define RTRN_NOT_ENABLED 3

#define RTRN_SLIP_COMPENSATE 4

#define RTRN_INTERRUPT 5

#define RTRN_SOFTWARE_LIMIT 6

#define RTRN_CHKSUM_ERROR 7

 

// Macros to decode instructions

// If the MSB of an instruction is set, it is a write instruction

#define IsWriteInstruction(a) (a & 0x80)

#define IsReadInstruction(a) (~a & 0x80)

#define GetInst(a) (a & 0x7f)

 

// ****************************************************************

//     I/O Setup

// ****************************************************************

 

// Port setup

// Motor direction port

#define MotorDir_DDR DDRD.6

#define MotorDir_OUT PORTD.6

 

// Motor step port

#define MotorStep_DDR DDRD.5

#define MotorStep_OUT PORTD.5

#define MotorStep_IN PIND.5

 

// Motor enable port

#define MotorEn_DDR DDRC.0

#define MotorEn_OUT PORTC.0

 

// Limit input ports

#define HCWLimit_DDR DDRD.2

#define HCWLimit_IN PIND.2

#define HCCWLimit_DDR DDRD.3

#define HCCWLimit_IN PIND.3

 

// Multiplexer port

#define MUXLSB_DDR DDRC.1

#define MUXLSB_OUT PORTC.1

#define MUXMSB_DDR DDRC.2

#define MUXMSB_OUT PORTC.2

 

// Output temporary storage

unsigned char output_buffer[7];

unsigned char lcd_buffer[16];

unsigned char lcd_buffer2[16];

 

// ****************************************************************

//     Software Setup

// ****************************************************************

 

// The address of this MID

#define MID_ADDR 0x80

 

// Slip variables

#define MAX_SLIP 200

#define MIN_SLIP 2

 

// Position in microsteps

signed long int master_pos;

signed long decoder_a_pos, decoder_b_pos;

 

// Error handling variable

unsigned char error;

 

// Motor and MID move parameters

unsigned int accel_time, base_rate, steady_rate, conv_units, backlash, slip_wait;

signed long int user_position;

unsigned long dial_indicator_fine, lower_slimit, upper_slimit;

unsigned char enable_drive, assoc_limits, lcd_control, multiplexer_set;

 

// Current move parameters

unsigned char cur_direction;

unsigned int cur_base_rate, cur_steady_rate, cur_backlashupsteps, cur_backlashdownsteps, cur_slipsteps, cur_slipwait;

unsigned long cur_accelsteps, cur_steadysteps, cur_decelsteps, cur_rate, cur_accel_rate, cur_backaccel_rate;

signed int cur_slip;

 

// Motor state

enum motor_states {STATE_ACCEL, STATE_STEADY, STATE_DECEL, STATE_BACKLASH_UP, STATE_BACKLASH_DOWN,

                              STATE_SLIPWAIT, STATE_SLIPCOMPENSATE, STATE_MANUALMOVE, STATE_OFF} motor_state;

 

// Motor timer setup

#define Motor_TCCR TCCR1B

#define MOTORON 0b00001010

#define MOTOROFF 0b00001000 

 

#define Motor_TCCR2 TCCR1A

#define OUTPUTON 0b01000000

#define OUTPUTOFF 0b00000000

 

// ****************************************************************

//     Strings Setup

// ****************************************************************

 

flash char* string_welcome = "Welcome!";

flash char* string_accel = "Accelerating";

flash char* string_steady = "Steady";

flash char* string_decel = "Decelerating";

flash char* string_backlash = "Backlash";

flash char* string_slipwait = "Slip Delay";

flash char* string_slipcompensate = "Slip Comp";

flash char* string_manualmove = "Manual Move";

flash char* string_off = "Move Done";

flash char* string_slip = "ERR: Motor Slip";    

flash char* string_cwhlimit = "ERR: CW HLimit";

flash char* string_ccwhlimit = "ERR: CCW HLimit";

flash char* string_slimit = "ERR: SLimit Trig";

 

// ****************************************************************

//     Functions and tasks

// ****************************************************************

 

#define time_lcd 399

#define time_serial 49

#define time_checkmotor 99

unsigned char count_lcd, count_serial, count_checkmotor;

 

void init(void);

void task_serial(void);

void task_lcd(void);

void task_checkmotor(void);

void task_error(void);   

 

// ****************************************************************

//     Limit Interrupts

// ****************************************************************

 

// Clockwise limit

interrupt [EXT_INT0] void cw_limit(void)

{        

      // If the motor is moving, and it is moving into the limit switch

      if ((motor_state != STATE_OFF)&&(assoc_limits == cur_direction))

      {

            // Stop the motor

            Motor_TCCR = MOTOROFF;

            motor_state = STATE_OFF;

 

            // Return an error

            error = RTRN_INTERRUPT;

 

            // Display error message on the LCD screen

            strcpyf(lcd_buffer2, string_cwhlimit);  

      }

}

 

// Counterclockwise limit

interrupt [EXT_INT1] void ccw_limit(void)

{

      // If the motor is moving, and it is moving into the limit switch

      if ((motor_state != STATE_OFF)&&(assoc_limits != cur_direction))

      {

            // Stop the motor

            Motor_TCCR = MOTOROFF;

            motor_state = STATE_OFF;

 

            // Return an error

            error = RTRN_INTERRUPT;

 

            // Display error message on the LCD screen

            strcpyf(lcd_buffer2, string_ccwhlimit);  

      }

}

 

// ****************************************************************

//     Timer interrupts

// ****************************************************************

 

// OS Timer interrupt

interrupt [TIM0_COMP] void t0_cmp(void) 

{

      if (count_lcd > 0) count_lcd--;

      if (count_serial > 0) count_serial--;

      if (count_checkmotor > 0) count_checkmotor--;

}

 

// Motor timer interrupt

interrupt [TIM1_COMPA] void t1_cmpA(void) 

{

      // Temporary variable

      signed int decoder_read;

 

      // Slip delay state

      if (motor_state == STATE_SLIPWAIT)

      {

            // Decrement the slip delay

            if (cur_slipwait > 0) cur_slipwait--;

 

            // If the slip delay is 0

            if (cur_slipwait == 0)

            {

                  // Read the real position from the decoder

                  decoder_read = LS_read_output(select_X);

 

                  // Calculate the discrepency between the position we read and the

                  // our stored position, and the direction we need to move to correct

                  // any slippage

                  cur_slip = decoder_read - master_pos;

                  cur_slipsteps = abs(cur_slip);

                  cur_direction = (cur_slip > 0)? 1 : 0;

 

                  // Set our position to the real position

                  master_pos = decoder_read;

 

                  // If we have slipped more than the minimum tolerance

                  if (cur_slipsteps > MAX_SLIP)

                  {

                        // Stop the motor

                        motor_state = STATE_OFF;

                        Motor_TCCR = MOTOROFF;

 

                        // Return an error message

                        error = RTRN_MOVE_STOPPED;

 

                        // Display error message on the LCD screen

                        strcpyf(lcd_buffer2, string_slip);

                  }

                  // If we have slipped more than the maximum tolerance

                  else if (cur_slipsteps > MIN_SLIP)

                  {

                        // Compensate for the slip

                        motor_state = STATE_SLIPCOMPENSATE;

                        MotorDir_OUT = cur_direction;

 

                        // Set the motor to minimum speed

                        OCR1A = StepsToCycles[cur_base_rate - 200];

 

                        // Turn the motor on

                        Motor_TCCR2 = OUTPUTON;

 

                        // Display the current state on the LCD screen

                        strcpyf(lcd_buffer2, string_slipcompensate);

                  }

                  else

                  {

                        // Turn of the motor

                        motor_state = STATE_OFF; 

 

                        // Display the current state on the LCD screen

                        strcpyf(lcd_buffer2, string_off);        

                  }

            }

      }

      // If we are on the falling edge

      else if (!MotorStep_IN)

      {

            // If the motor is on

            if (motor_state != STATE_OFF)

            {

                  // Add a step to the current position in the correct

                  // direction

                  if (cur_direction)

                  {

                        master_pos++;

                        dial_indicator_fine--;

                  }

                  else

                  {

                        master_pos--;        

                        dial_indicator_fine++;

                  }

            }

 

            // If we have hit a software limit and are moving into it

            if ((motor_state != STATE_MANUALMOVE)&&(((dial_indicator_fine >= upper_slimit)

                  &&(cur_direction == 0))||((dial_indicator_fine <= lower_slimit)&&(cur_direction == 1))))

            {

                  // Turn off the motor

                  Motor_TCCR = MOTOROFF;

 

                  // Return an error

                  error = RTRN_SOFTWARE_LIMIT;

 

                  // Display an error on the LCD screen

                  strcpyf(lcd_buffer2, string_slimit);

            }

 

            // Handle the current motor state

            else switch(motor_state)

            {

                  // If we are accelerating

                  case STATE_ACCEL:

                        // Increment the current speed by a fixed point binary number

                        cur_rate += cur_accel_rate;

 

                        // Set the timer compare match value to the current speed

                        OCR1A = StepsToCycles[(cur_rate >> 16) - 200];

 

                        // Decrement the acceleration steps

                        if (cur_accelsteps > 0) cur_accelsteps--;

                       

                        // If there are no acceleration steps left

                        if (cur_accelsteps == 0)

                        {

                              // Set the timer compare match value to the steady speed

                              OCR1A = StepsToCycles[cur_steady_rate - 200];

 

                              // Go to the steady state

                              motor_state = STATE_STEADY;

 

                              // Display the current state on the LCD screen

                              strcpyf(lcd_buffer2, string_steady);

                        }

                        break;

 

                  // If we are in the steady state

                  case STATE_STEADY:

                        // Decrement the steady steps

                        if (cur_steadysteps > 0) cur_steadysteps--;

 

                        // If there are no steady steps left

                        if (cur_steadysteps == 0)

                        {

                              // Set the speed to the steady speed

                              cur_rate = (unsigned long)(cur_steady_rate) << 16;

 

                              // Go to the deceleration state

                              motor_state = STATE_DECEL;

 

                              // Display the current state on the LCD screen

                              strcpyf(lcd_buffer2, string_decel);

                        }

                        break;

 

                  // If we are in the decelleration state

                  case STATE_DECEL:

                        // Decrement the current speed by a fixed point binary number

                        cur_rate -= cur_accel_rate;

 

                        // Set the timer compare match value to the current speed

                        OCR1A = StepsToCycles[(cur_rate >> 16) - 200];

 

                        // Decrement the deceleration steps

                        if (cur_decelsteps > 0) cur_decelsteps--;

 

                        // If there are no decelration steps left

                        if (cur_decelsteps == 0)

                        {    

                              // If the motor is going downward, backlash the motor

                              if (cur_direction == 0)

                              {

                                    // Set the speed to the base speed

                                    cur_rate = (unsigned long)(cur_base_rate) << 16;

 

                                    // Go to the backlash up state

                                    motor_state = STATE_BACKLASH_UP;   

 

                                    // Display the current state on the LCD screen

                                    strcpyf(lcd_buffer2, string_backlash);

 

                                    // Reverse the direction of the motor

                                    cur_direction = 1;

                                    MotorDir_OUT = 1;

                              }

                              // Otherwise, go straight to compensating for slip

                              else

                              {

                                    // Go to the slip delay state

                                    motor_state = STATE_SLIPWAIT;

 

                                    // Display the current state on the LCD screen

                                    strcpyf(lcd_buffer2, string_slipwait);

 

                                    // Set the motor compare match value to 1 ms

                                    OCR1A = 1992;

 

                                    // Turn the motor output off

                                    Motor_TCCR2 = OUTPUTOFF;                             

                              }

                        }

                        break;

 

                  // If we are in the backlash acceleration state

                  case STATE_BACKLASH_UP:

                        // Increment the current speed by a fixed point binary number

                        cur_rate += cur_backaccel_rate;

 

                        // Set the timer compare match to the current speed

                        OCR1A = StepsToCycles[(cur_rate >> 16) - 200];                         

 

                        // Decrement the backlash acceleration steps

                        if (cur_backlashupsteps > 0) cur_backlashupsteps--;

 

                        // If there are no backlash acceleration steps left

                        if (cur_backlashupsteps == 0)

                        {

                              // Go to the backlash deceleration station

                              motor_state = STATE_BACKLASH_DOWN;

                        }

                        break;

 

                  // If we are in the backlash deceleration state

                  case STATE_BACKLASH_DOWN:

                        // Decrement the current speed by a fixed point binary number

                        cur_rate -= cur_backaccel_rate;

 

                        // Set the timer compare match to the current speed

                        OCR1A = StepsToCycles[(cur_rate >> 16) - 200];                         

 

                        // Decrement the backlash deceleration steps

                        if (cur_backlashdownsteps > 0) cur_backlashdownsteps--;

 

                        // If there are no backlash deceleration steps left

                        if (cur_backlashdownsteps == 0)

                        {

                              // Go to the slip delay state

                              motor_state = STATE_SLIPWAIT;

 

                              // Display the current state on the LCD screen

                              strcpyf(lcd_buffer2, string_slipwait);

 

                              // Set the timer compare match to 1 ms

                              OCR1A = 1992;

 

                              // Turn off the motor output

                              Motor_TCCR2 = OUTPUTOFF;

                        }

                        break;

 

                  // If we are in the slip compensation state

                  case STATE_SLIPCOMPENSATE:

                        // Decrement the slip compensation steps

                        if (cur_slipsteps > 0) cur_slipsteps--;

 

                        // If there are no slip compensation steps left

                        if (cur_slipsteps == 0)

                        {

                              // Go to the motor off state

                              motor_state = STATE_OFF; 

 

                              // Display the current state on the LCD screen

                              strcpyf(lcd_buffer2, string_off);

                        }

                        break;     

 

                  // If we are moving manually

                  case STATE_MANUALMOVE:

                        if (cur_slipsteps > 0) cur_slipsteps--;

                        if (cur_slipsteps == 0)

                        {

                              motor_state = STATE_OFF; 

                              strcpyf(lcd_buffer2, string_off);

                        }

                        break;     

 

                  // If the motor is off

                  case STATE_OFF:

                        // Turn off the motor interrupt

                        Motor_TCCR = MOTOROFF;

                        break;

            }

 

      }

}

 

// ****************************************************************

//     Uart utility functions

// ****************************************************************

 

// Parse the uart buffer into an unsigned integer

unsigned int Uart2UnsignedInt(void)

{

      return ((unsigned int)(((unsigned int)uart_buffer[3])<<8) + (unsigned int)uart_buffer[2]);

}

 

// Parse the uart buffer into a signed integer

signed int Uart2SignedInt(void)

{

      return (signed int)( (signed int)(((signed int)uart_buffer[3])<<8) + (unsigned int)uart_buffer[2]);

}

 

// Parse the uart buffer into a signed long

signed long int Uart2SignedLong(void)

{

      return (signed long int)(((0x80 && uart_buffer[4])? (signed long int)0xff000000 : (signed long int)0x00000000)

                                                + (unsigned long int)(((unsigned long int) uart_buffer[4]) << 16)

                                                + (unsigned long int)(((unsigned long int) uart_buffer[3]) << 8)

                                                + (unsigned long int)uart_buffer[2]);

}

 

// Place an unsigned character in the uart output buffer

void UnsignedChar2Uart(unsigned char inst, unsigned char value)

{

      output_buffer[0] = MID_ADDR;

      output_buffer[1] = inst;

      output_buffer[2] = RTRN_OK;

      output_buffer[3] = value;

      output_buffer[4] = 0x00;

      output_buffer[5] = 0x00;

      output_buffer[6] = (unsigned char)(output_buffer[0] + output_buffer[1] + output_buffer[2] + output_buffer[3]);

}

 

// Place an unsigned integer in the uart output buffer

void UnsignedInt2Uart(unsigned char inst, unsigned int value)

{

      output_buffer[0] = MID_ADDR;

      output_buffer[1] = inst;

      output_buffer[2] = RTRN_OK;

      output_buffer[3] = (value & 0xff);

      output_buffer[4] = (value >> 8);

      output_buffer[5] = 0x00;

      output_buffer[6] = (unsigned char)(output_buffer[0] + output_buffer[1] + output_buffer[2]

                                       + output_buffer[3] + output_buffer[4]);

}

 

// Place a signed long in the uart output buffer

void SignedLong2Uart(unsigned char inst, signed long int value)

{

      output_buffer[0] = MID_ADDR;

      output_buffer[1] = inst;

      output_buffer[2] = RTRN_OK;

      output_buffer[3] = (value & 0xff);

      output_buffer[4] = (value >> 8) & 0xff;

      output_buffer[5] = (value >> 16) & 0xff;

      output_buffer[6] = (unsigned char)(output_buffer[0] + output_buffer[1] + output_buffer[2]

                                                 + output_buffer[3] + output_buffer[4] + output_buffer[5]);

}

 

// Place an instruction related error value in the uart output buffer

void InstError2Uart(unsigned char inst, unsigned char err)

{

      output_buffer[0] = MID_ADDR;

      output_buffer[1] = inst;

      output_buffer[2] = err;

      output_buffer[3] = 0x00;

      output_buffer[4] = 0x00;

      output_buffer[5] = 0x00;

      output_buffer[6] = (unsigned char)(output_buffer[0] + output_buffer[1] + output_buffer[2]);

}

 

// Place a non-instruction related error in the uart output buffer

void Error2Uart(unsigned char err)

{

      output_buffer[0] = MID_ADDR;

      output_buffer[1] = 0x00;

      output_buffer[2] = err;

      output_buffer[3] = 0x00;

      output_buffer[4] = 0x00;

      output_buffer[5] = 0x00;

      output_buffer[6] = (unsigned char)(output_buffer[0] + output_buffer[2]);

}

 

// Write the output buffer to the UART

void Output2Uart(void)

{

      unsigned char a = 0;

      for (a = 0; a < 7; a++)

      {           

            t_buffer[t_end] = output_buffer[a];

            t_end = (t_end + 1) % TRANSMIT_BUFFER_SIZE;

      }                                             

     

      t_end = (t_end + 1) % TRANSMIT_BUFFER_SIZE;

 

      UCSRB.5 = 1;

}

 

// ****************************************************************

//     Motor setup function

// ****************************************************************

 

void SetupMove(unsigned char direction, unsigned long steps)

{

      // Turn off the motor and interrupts

    Motor_TCCR = MOTOROFF;

    Motor_TCCR2 = OUTPUTOFF;

      motor_state = STATE_OFF;

       

      // Check the hardware limits, if one of them is engaged make sure we cannot

      // move into them. Associate the limits correctly with the orientations

      if (assoc_limits == 1)

      {

            if (((HCCWLimit_IN==0) && (direction == 0))||((HCWLimit_IN==0) && (direction == 1)))

            {

                  steps = 0;

                  return;

            }

      }

      else

      {

            if (((HCWLimit_IN==0) && (direction == 0))||((HCCWLimit_IN==0) && (direction == 1)))

            {

                  return;

                  steps = 0;

            }

      }

 

      // If we are moving more than one step

      if (steps > 1)

      {                     

            // Setup the current move parameters

            cur_direction = direction;   

            cur_base_rate = base_rate;

            cur_steady_rate = steady_rate;

            cur_slipwait = slip_wait;

 

            // Calculate the number of steps in each state

            // Acceleration steps = change in step speed / acceleration time / 2 + base speed * acceleration time

            cur_accelsteps = (unsigned long)(steady_rate - base_rate) * (unsigned long)accel_time

                                              / (unsigned long)2000 + base_rate * accel_time / 1000;

            cur_decelsteps = cur_accelsteps;

           

            // Steady steps = Total steps - acceleration steps - decelrations steps

            // If we have no steady steps left, set it to zero

            if (2 * cur_accelsteps > steps)

                  cur_steadysteps = 0;

 

            // If we are going down, add in backlash

            else if (cur_direction == 0)

                  cur_steadysteps = steps - cur_accelsteps - cur_decelsteps + backlash;    

 

            // Otherwise neglect backlash

            else                                                                       

                  cur_steadysteps = steps - cur_accelsteps - cur_decelsteps;    

                          

            // Calculate acceleration rate

            // Acceleration rate = (steady speed - base speed) / acceleration steps

            cur_accel_rate = ((unsigned long)(steady_rate - base_rate) << 16) / cur_accelsteps;

 

            // Change base speed into fixed point binary number

            cur_rate = (unsigned long)(base_rate) << 16;

 

            // Calculate backlash up and down steps

            cur_backlashupsteps = backlash / 2;

            cur_backlashdownsteps = backlash - cur_backlashupsteps;    

 

            // Calculate backlash acceleration rate

            cur_backaccel_rate = ((unsigned long)(steady_rate - base_rate) << 16) / cur_backlashupsteps;

 

            // Go to the acceleration state

            motor_state = STATE_ACCEL;

 

            // Print the current state on the LCD screen

            strcpyf(lcd_buffer2, string_accel);

           

            // Set the timer compare match value to the base speed

            OCR1A = StepsToCycles[cur_base_rate - 200];

 

            // Set the direction

            MotorDir_OUT = cur_direction;

 

            // Reset the motor output

            MotorStep_OUT = 0;

       

            // Turn on the motor interrupt and output

            Motor_TCCR2 = OUTPUTON;

        Motor_TCCR = MOTORON;         

      }

}

 

// ****************************************************************

//     OS and task functions

// ****************************************************************

 

// Initialization function

void init(void)

{

      // Initialize the LCD

      lcd_init(LCDwidth);

      strcpyf(lcd_buffer2, string_welcome);

 

      // Initialize the LS chip

      LS_init();

      LS_write_RLD(select_XY, RLD_reset_BP | RLD_reset_FLAGS);

      LS_write_PRS(select_XY, 0);

      LS_write_RLD(select_XY, RLD_trnsfr_PR0);

      LS_write_RLD(select_XY, RLD_reset_BP | RLD_reset_FLAGS | RLD_reset_CNTR);

      LS_write_CMR(select_XY, CMR_quad_X1);

      LS_write_IOR(select_XY, IOR_enable_AB | IOR_LOL); 

     

      LS_write_preset(select_X, 1);

      LS_write_RLD(select_XY, RLD_trnsfr_PR); 

     

      // Initialize the UART

      uart_init(103);

      uart_gets_int();

 

      // Setup the LS chip clock

      // The clock runs at 16 Mhz / ( 2 * 64 ) = 125 Khz

      TCCR2 = 0b00011001;

      OCR2 = 63;

      DDRD.7 = 1;

 

      // Setup the motor counter and I/O ports

      TCCR1B = MOTOROFF;

      TCCR1A = OUTPUTOFF;

      MotorDir_DDR = 1;

      MotorStep_DDR = 1;      

      MotorEn_DDR = 1;

      MotorEn_OUT = 1;

 

      // Setup the OS clock

      // The clock runs at 16 Mhz / (64 * 250) = 1 Khz

      TCCR0=0b00001011;

      OCR0=249;

 

      // Setup the timer interrupts

      // Interrupt on timer 0 and 1 compare match

      TIMSK=0b00010010;

 

      // Setup the task times

      count_lcd = time_lcd;

      count_serial = time_serial;

      count_checkmotor = time_checkmotor;

 

      // Setup multiplexer lines

      MUXLSB_DDR = 1;

      MUXMSB_DDR = 1;  

     

      // Setup external interrupts for clockwise and counterclockwise

      // interrupts

      MCUCR = 0b00001010;

    GICR = 0b11000000;       

      HCWLimit_DDR = 0;

      HCCWLimit_DDR = 0;

 

      // Setup variables

      master_pos = 1;

      lcd_control = 1;     

      accel_time = 1200;

      base_rate = 400;

      steady_rate = 1000;

      backlash = 500;

      conv_units = 2835;        

      assoc_limits = 1;

      slip_wait = 500;

      motor_state = STATE_OFF;

 

      lower_slimit = (unsigned long)39 * (unsigned long)conv_units;

      upper_slimit = (unsigned long)89 * (unsigned long)conv_units;

 

      // Turn on interrupts

      #asm

            sei

      #endasm

}

 

// Entry point

void main(void)

{

      // Initialize

      init();

 

      // Loop forever

      while (1)

      {

            // Motor checking task

            if (count_checkmotor == 0) task_checkmotor();

 

            // If the LCD is on, refresh the LCD

            if ((lcd_control)&&(count_lcd == 0)) task_lcd();

 

            // Serial input task

            if (count_serial == 0) task_serial();

 

            // If we have an error to return, return it

            if (error > 0) task_error();

      }

}

 

// LCD refreshing task

void task_lcd(void)

{

      // Reset the task time

      count_lcd = time_lcd;

 

      // Print the current dial position to lcd buffer

      sprintf(lcd_buffer,"Pos: %u", (dial_indicator_fine + (conv_units / 2)) / conv_units);   

 

      // Clear the LCD

      lcd_clear();

 

      // Print the LCD buffers to the LCD

      lcd_gotoxy(0,0);

      lcd_puts(lcd_buffer);

      lcd_gotoxy(0,1);

      lcd_puts(lcd_buffer2);

}

 

// Serial input task

void task_serial(void)

{

      // Temporary variable

      signed long int tempSteps;

 

      // Reset the task time

      count_serial = time_serial;

 

      // If we have recieved a full message

      if (uart_ready)

      {             

            // If the checksum does not match the transmitted one, transmit an error message

            if ((unsigned char)(uart_buffer[0]+uart_buffer[1]+uart_buffer[2]

                                          +uart_buffer[3]+uart_buffer[4])!=uart_buffer[5])

                  InstError2Uart(uart_buffer[1], RTRN_CHKSUM_ERROR);

 

            // Otherwise, process the message

            else switch(GetInst(uart_buffer[1]))

            {

                  // Read or write the acceleration time

                  case INST_ACCEL_TIME:

                        if (IsWriteInstruction(uart_buffer[1]))

                        {

                              accel_time = Uart2UnsignedInt();

                              if (accel_time < MIN_ACCEL_TIME)

                                    accel_time = MIN_ACCEL_TIME;

                              else if (accel_time > MAX_ACCEL_TIME)

                                    accel_time = MAX_ACCEL_TIME;

                        }

                        UnsignedInt2Uart(INST_ACCEL_TIME, accel_time);

                        break;

 

                  // Read or write the base rate

                  case INST_BASE_RATE:

                        if (IsWriteInstruction(uart_buffer[1]))

                        {

                              base_rate = Uart2UnsignedInt();

                              if (base_rate < MIN_BASE_RATE)

                                    base_rate = MIN_BASE_RATE;

                              else if (base_rate > MAX_BASE_RATE)

                                    base_rate = MAX_BASE_RATE;

                        }

                        UnsignedInt2Uart(INST_BASE_RATE, base_rate);

                        break;

 

                  // Read or write the conversion units

                  case INST_CONV_UNITS:

                        if (IsWriteInstruction(uart_buffer[1]))

                        {

                              conv_units = Uart2UnsignedInt();

                              if (conv_units < MIN_CONV_UNITS)

                                    conv_units = MIN_CONV_UNITS;

                              else if (conv_units > MAX_CONV_UNITS)

                                    conv_units = MAX_CONV_UNITS;

                        }

                        UnsignedInt2Uart(INST_CONV_UNITS, conv_units);

                        break;

 

                  // Read or write the steady rate

                  case INST_STEADY_RATE:

                        if (IsWriteInstruction(uart_buffer[1]))

                        {

                              steady_rate = Uart2UnsignedInt();

                              if (steady_rate < MIN_STEADY_RATE)

                                    steady_rate = MIN_STEADY_RATE;

                              else if (steady_rate > MAX_STEADY_RATE)

                                    steady_rate = MAX_STEADY_RATE;

                        }

                        UnsignedInt2Uart(INST_STEADY_RATE, steady_rate);

                        break;

 

                  // Read or write the backlash steps

                  case INST_BACKLASH:

                        if (IsWriteInstruction(uart_buffer[1]))

                        {

                              backlash = Uart2UnsignedInt();

                              if (backlash < MIN_BACKLASH)

                                    backlash = MIN_BACKLASH;

                              else if (backlash > MAX_BACKLASH)

                                    backlash = MAX_BACKLASH;

                        }

                        UnsignedInt2Uart(INST_BACKLASH, backlash);

                        break;

 

                  // Read or write the slip delay time

                  case INST_SLIP_WAIT:

                        if (IsWriteInstruction(uart_buffer[1]))

                        {

                              slip_wait = Uart2UnsignedInt();

                        }

                        UnsignedInt2Uart(INST_SLIP_WAIT, slip_wait);

                        break;

 

                  // Enable or disable the drive

                  case INST_ENABLE_DRIVE:

                        if (IsWriteInstruction(uart_buffer[1])) enable_drive = uart_buffer[2];

                        UnsignedChar2Uart(INST_ENABLE_DRIVE, enable_drive);

                        break;

 

                  // Change the limit association

                  case INST_ASSOC_LIMITS:

                        if (IsWriteInstruction(uart_buffer[1])) assoc_limits = uart_buffer[2];

                        UnsignedChar2Uart(INST_ASSOC_LIMITS, assoc_limits);

                        break;

 

                  // Read or write the dial indicator value

                  case INST_DIAL_INDICATOR:

                        if (IsWriteInstruction(uart_buffer[1]))

                        {

                              dial_indicator_fine = (unsigned long)Uart2UnsignedInt() * (unsigned long)conv_units;

                        }

                        UnsignedInt2Uart(INST_DIAL_INDICATOR, (dial_indicator_fine + (conv_units / 2)) / conv_units);

                        break;

 

                  // Enable or disable the LCD screen

                  case INST_LCD_CONTROL:

                        if (IsWriteInstruction(uart_buffer[1]))

                        {

                               lcd_control = uart_buffer[2];

                               lcd_clear();

                        }

                        UnsignedChar2Uart(INST_LCD_CONTROL, lcd_control);

                        break;

 

                  // Read the mid status

                  case INST_MID_STATUS:

                        UnsignedChar2Uart(INST_MID_STATUS, 0xff);

                        break;

 

                  // Read or write the user position

                  case INST_USER_POSITION:

                        if (IsWriteInstruction(uart_buffer[1])) user_position = Uart2SignedLong();

                        SignedLong2Uart(INST_USER_POSITION, user_position);

                        break;

 

                  // Read or write the multiplexer select value

                  case INST_MULTIPLEXER_SET:

                        if (IsWriteInstruction(uart_buffer[1]))

                        {

                              multiplexer_set = uart_buffer[2];

                              MUXMSB_OUT = (multiplexer_set & 0x02) >> 1;

                              MUXLSB_OUT = multiplexer_set & 0x01;

                        }

                        UnsignedChar2Uart(INST_MULTIPLEXER_SET, multiplexer_set);

                        break;

 

                  // Read or write the lower software limit

                  case INST_LOWER_SLIMIT:

                        if (IsWriteInstruction(uart_buffer[1]))

                        {

                              lower_slimit = (unsigned long)Uart2UnsignedInt() * (unsigned long)conv_units;

                        }

                        UnsignedInt2Uart(INST_LOWER_SLIMIT, (lower_slimit + (conv_units / 2)) / conv_units);

                        break;

 

                  // Read or write the upper software limit

                  case INST_UPPER_SLIMIT:

                        if (IsWriteInstruction(uart_buffer[1]))

                        {

                              upper_slimit = (unsigned long)Uart2UnsignedInt() * (unsigned long)conv_units;

                        }

                        UnsignedInt2Uart(INST_UPPER_SLIMIT, (upper_slimit + (conv_units / 2)) / conv_units);

                        break;

 

                  // Read or write decoder a position

                  case INST_DECODER_A_POS:

                        if (IsWriteInstruction(uart_buffer[1]))

                        {

                              decoder_a_pos = Uart2SignedLong();

                              LS_write_preset(select_X, decoder_a_pos);

                              LS_write_RLD(select_X, RLD_trnsfr_PR);

                        }

                        SignedLong2Uart(INST_DECODER_A_POS, decoder_a_pos);

                        break;

 

                  // Read or write decoder b position

                  case INST_DECODER_B_POS:

                        if (IsWriteInstruction(uart_buffer[1]))

                        {

                              decoder_b_pos = Uart2SignedLong();

                              LS_write_preset(select_Y, decoder_b_pos);

                              LS_write_RLD(select_Y, RLD_trnsfr_PR);

                        }

                        SignedLong2Uart(INST_DECODER_B_POS, decoder_b_pos);

                        break;

 

                  // Read the lower hardware limit with respect to limit association

                  case INST_LOWER_HLIMIT:                 

                        if (assoc_limits)

                              UnsignedChar2Uart(INST_LOWER_HLIMIT, HCWLimit_IN);

                        else

                              UnsignedChar2Uart(INST_LOWER_HLIMIT, HCCWLimit_IN);

                        break;

 

                  // Read the upper hardware limit with respect to limit association

                  case INST_UPPER_HLIMIT:

                        if (assoc_limits)

                              UnsignedChar2Uart(INST_LOWER_HLIMIT, HCCWLimit_IN);

                        else

                              UnsignedChar2Uart(INST_LOWER_HLIMIT, HCWLimit_IN);

                        break;

 

                  // Program the decoders

                  case INST_PROGRAM_DECODER:

                        if (IsWriteInstruction(uart_buffer[1]));

                        break;

 

                  // Tweek the motor to the current dial position

                  case INST_TWEEK_MOTOR:

                        if (IsWriteInstruction(uart_buffer[1]))

                        {

                              if (motor_state == STATE_OFF)

                              {

                                    dial_indicator_fine = (unsigned long)Uart2UnsignedInt() * (unsigned long)conv_units;

                              }

                              UnsignedInt2Uart(INST_TWEEK_MOTOR, (dial_indicator_fine + (conv_units / 2)) / conv_units);

                        }

                        break;

 

                  // Move the dial indicator incrementally

                  case INST_MOVE_INCREMENTAL:

                        if (IsWriteInstruction(uart_buffer[1]))

                        {            

                              tempSteps =  (signed long int)Uart2SignedInt() * conv_units;

                              SetupMove((tempSteps > 0)? 0 : 1, labs(tempSteps)); 

                        }

                        break;

 

                  // Move the dial indicator to an absolute value

                  case INST_MOVE_ABSOLUTE:

                        if (IsWriteInstruction(uart_buffer[1]))

                        {

                              tempSteps = (unsigned long int)Uart2UnsignedInt() * conv_units;

                              SetupMove((tempSteps > dial_indicator_fine)? 0 : 1,

                                           labs(dial_indicator_fine - tempSteps));                         

                        };

                        break;

 

                  // Move the motor incremental microsteps

                  case INST_MOVE_MICROSTEP:

                        if (IsWriteInstruction(uart_buffer[1]))

                        {

                              // Calculate the number of steps to move and the direction 

                              tempSteps = Uart2SignedLong();   

                              cur_slipsteps = labs(tempSteps); 

                              cur_direction = (tempSteps > 0)? 1 : 0;  

                             

                              // Make sure we are not moving into a hardware limit

                              if (((assoc_limits == 1)&&(((HCCWLimit_IN==0) && (cur_direction == 0))

                                          ||((HCWLimit_IN==0) && (cur_direction == 1))))||

                                    ((assoc_limits == 0)&&(((HCWLimit_IN==0) && (cur_direction == 0))

                                          ||((HCCWLimit_IN==0) && (cur_direction == 1)))))

                              {

                                    // If we are, return an error

                                    InstError2Uart(uart_buffer[1], RTRN_INTERRUPT);

                              }

                              else

                              {

                                    // Setup the current direction

                                    MotorDir_OUT = cur_direction;

 

                                    // Set the motor

                                    OCR1A = StepsToCycles[0];

 

                                    // Go to the manual move change

                                    motor_state = STATE_MANUALMOVE;

 

                                    // Display the current state on the LCD screen

                                    strcpyf(lcd_buffer2, string_manualmove);

 

                                    // Turn on the motor output and interrupt

                                    Motor_TCCR = MOTORON; 

                                    Motor_TCCR2 = OUTPUTON;  

 

                                    // Return the number of steps moved

                                    SignedLong2Uart(INST_MOVE_MICROSTEP, tempSteps);

                              }

                        }

                        break;

 

                  // Otherwise, return an invalid command error

                  default:

                        InstError2Uart(uart_buffer[1], RTRN_NOT_VALID);

                        break;  

            }

           

            // Output the output buffer to the UART

            Output2Uart();

 

            // Get another input from the UART

            uart_gets_int();

      }

}

 

// Motor checking task

void task_checkmotor(void)

{

      // Reset the task time

      count_checkmotor = time_checkmotor;

 

      // Read the decoder positions

      decoder_a_pos = LS_read_output(select_X);

      decoder_b_pos = LS_read_output(select_Y);

}

 

// Error transmision task

void task_error(void)

{

      // Put the error in the UART buffer

      Error2Uart(error);

 

      // Output the output buffer to the UART

      Output2Uart();

 

      // Reset the error variable

      error = 0;

}