/* Ant weight combat robot control program
   Copyright 2004 under GPL by Dale Alan Heatherington - dale.h at wa4dsy.net
   
   Vers 2.1 April 26 2004

Code for ant weight spinner combat robot "Thrasher" March 20 2004
Atmel AVR ATMEGA8535 microcontroller

Compiled with avr-gcc  (See Makefile)
avr-gcc can be downloaded from:
http://sourceforge.net/projects/cdk4avr/



-------------------------------------------
          Revisions
--------------------------------------------

April 25 2004,  v2.1 - code cleanup

Fixed some bugs in weapon motor control function that caused
yellow LED to flicker when it was supposed to be off.

Now I convert the Joystick values to 16 bit signed integers
before doing any math.  This simplified several functions
including speed_control_curve().

No more "junk DNA".
Removed many unused variables and functions left over from
previous projects and experiments. 

Fixed reversed left and right motor wiring in hardware
and made corrections to inversion detector code.  Now LEFT
really controls the LEFT motor!  No more left/right confusion.

---------------------------------------


April 21 2004, vers 2.0
Changed speed control code for use with new PCB hardware
with locked anti-phase PWM with signed current feedback.
This simplified the motor control code a lot.  No more 
integrator!       PWM frequency is 28.8 khz.

---------------------------------------
April 4 2004, vers 1.3:
Added "speed_control_curve()" function to make the
joystick response non-linear.  Improves bot handling at low speeds
by expanding the low end joystick range.  The high end is compressed.
-----------------------------------------


*/


/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA 

The GNU General Public License can also be found online at:
http://www.fsf.org/licenses/licenses.html#GPL

*/
 

/***************************************************************************/

//Note: CPU type defined in Makefile

#include <avr/io.h>
#include <stdio.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#include <avr/eeprom.h>
#include <stdlib.h>
#include <avr/wdt.h>


#define SPINNER

#define WDTO_250MS 4        /* Watchdog timer value = 250ms */
#define UCHAR uint8_t
                
#define F_CPU    7372800        /* crystal frequency of CPU */

#define TRUE 1
#define FALSE 0
#define SYNC1 0xf0
#define SYNC2 0x55
#define MODE_STOP 0
#define REV 0
#define FWD 1

#define MAX_FWD_SPEED -127
#define MAX_REV_SPEED 127




/* Packet: 0xff 0xf0 0x55 ADDRESS COUNT DATA... CRCH CRCL */

#define COUNT 1      /* offset to count field in tx/rx buffers */
#define ADDRESS 0    /* offset to address field in tx/rx buffers */
#define DATA 2       /* offset to start of data in tx/rx buffers */
#define RIGHTJOY_Y 3  //offset to right joystick Y value
#define RIGHTJOY_X 4  //offset to right joystick X value
#define LEFTJOY_Y 5
#define LEFTJOY_X 6
#define MATRIX0 7     //offset to matrix 0 byte  (These are the controller buttons)
#define MATRIX1 8     //offset to matrix 1 byte
#define BOT_ADDR 1   /* Address of this robot */

//Bits in port C
#define TS_UPRIGHT 0     /* input, low true, from mercury switch */
#define TS_INVERTED 1    /* input, low true, from mercury switch */


//Bits in Port C (LED Outputs)
#define LED_YELLOW 6      /* low true, RX data error */
#define LED_GREEN 2       /* low true, good data from radio present */


/* Switch Matrix bit defs (buttons on controller)*/

/*matrix0*/

#define m_mode 0x80
#define m_east 0x40
#define m_north 0x20
#define m_rumble 0x10
#define m_south 0x08
#define m_s 0x04
#define m_left_fire 0x02
#define m_west  0x01

/*matrix1*/

#define m_right_fire 0x80
#define m_A          0x40
#define m_B          0x20
#define m_C          0x10
#define m_Z          0x08
#define m_Y          0x04
#define m_X          0x02


/*--- Motor control variables and defs ----- */

//Bit in PORTD that enables drive motor h-bridges
#define DRIVE_ENABLE 6
                             

int r_speed;
int l_speed;


UCHAR bot_mode, testmode;
volatile UCHAR timer_RX_comm_data, timer_led_yellow;
volatile UCHAR timer_mstop;
UCHAR inverted;
UCHAR lm_pwm, rm_pwm;

//Analog to digital converter mux addresses
#define AD_POT  0

#define AD_WM_CURRENT 3
#define AD_RM_CFB_MINUS 4
#define AD_RM_CFB_PLUS 5
#define AD_LM_CFB_MINUS 6
#define AD_LM_CFB_PLUS 7

//A-D values stored in these variables
UCHAR ad_lm_cfb_plus , ad_lm_cfb_minus, ad_rm_cfb_plus, ad_rm_cfb_minus ;

void adjust_motor_speed(int l_speed, int r_speed);
UCHAR speed_to_pwm(int speed);


/* ------- End of motor control -------------*/


UCHAR matrix0, matrix1, last_matrix0, last_matrix1, test;
UCHAR ad_m_current, ad_rssi;   //A-D converter values


#define RXSIZE 20
UCHAR tx_buf[32];      //Serial tx buffer for debug telemetry
UCHAR rx_buf[RXSIZE];     //Serial receive buffer for RC commands
UCHAR rx_pkt[RXSIZE-2];
UCHAR tx_ptr,tx_ctr , rx_ptr, rx_ctr;
UCHAR txstate,rxstate;
uint16_t crc, rxcrc,last_rx;
UCHAR  counter,val,rssi;
UCHAR ir_sens_left, ir_sens_right;
int16_t x_offset, y_offset;

UCHAR ad_pot;

#ifdef SPINNER
UCHAR spinner_ctrl;
UCHAR ad_w_current;
#endif

int ii;
UCHAR prescale ;
UCHAR prescale100;
UCHAR prescale1sec;
UCHAR prescaleMinute;
UCHAR timer10ms ;
UCHAR timer1sec;
volatile unsigned int minutes;
volatile UCHAR run_seconds;

void make_info_pkt();
void set_tx_buffer_crc();
uint16_t get_adc(uint8_t chan);
uint16_t calc_crc(char data, uint16_t crc);
void make_info_pkt();
void delay_100us(uint8_t count);
int speed_control_curve(int js);

//-------------------------------------------------------------------------


int hard_limit_range(int x, int lim_hi, int lim_lo)
{
   if(x < lim_lo) x = lim_lo; 
   if(x > lim_hi) x = lim_hi; 
   return x;

}
//--------------------------------------------------------------------------
/* Read the 10 bit analog to digital converter */

uint16_t get_adc(UCHAR chan)
{  
    uint16_t result;

    outp(chan,ADMUX);               //Select channel
    sbi(ADCSRA,ADSC);               //Start conversion 
    while( bit_is_clear(ADCSRA,ADIF)); //Wait for ADC output registers to be loaded
    cli();
    result = inw(ADCL);
    sei();
    return result;       //Return 16 bit value
}

//---------------------------------------------------------------------------

/* Read 10 bit AD converter but return 
   only 8 bits discarding low 2 bits. */

uint8_t get_adc8(UCHAR chan)
{  
    uint16_t result;

    outp(chan,ADMUX);               //Select channel
    sbi(ADCSRA,ADSC);                //Start conversion 
    
    while( bit_is_clear(ADCSRA,ADIF)); //Wait for ADC output registers to be loaded
    cli();
    result = inw(ADCL);     //Get 10 bit AD value 
    sei();
    return result >> 2;     //Return 8 bit value
}
//---------------------------------------------------------------------------

   // compute 16 bit CRC on 1 data byte. 
   //   crc is a  variable which accumulates the 16 bit CRC. 
   //   It must be cleared before starting to compute 16 bit CRC on a string.
   //   The output value will be returned as a 16 bit int.
   
uint16_t calc_crc(char data, uint16_t crc){

      uint8_t i;
     
      for (i=0;i<8;i++) {                 //Do all 8 bits in the data byte
         if (data & 0x80) crc ^= 0x8000;  //if data bit 7 is set then flip crc msb
         data = data << 1;                // get next data bit
         if (crc & 0x8000) {              //if crc msb is 1...
             crc = crc << 1;              // ..shift left 1
             crc = crc ^ 0x8005;          // ..xor with 0x8005
          } else crc = crc << 1;          //else just shift left 1 with no xor
        
      }

      return crc;

}   

//-----------------------------------------------------------------------
//Compute CRC on the TX packet and put it on the end
void set_tx_buffer_crc(){
      UCHAR j = tx_buf[COUNT] + 2; //get data size
      UCHAR i;

      crc = 0;
      for (i=0;i<j;i++) crc = calc_crc(tx_buf[i],crc); //Compute crc16 over buffer
      tx_buf[i++] = (uint8_t)(crc >> 8);  //tack the crc bytes on the end of the buffer
      tx_buf[i++] = (uint8_t)(crc & 0xff);
      
}
//--------------------------------------------------------------------------
//Check CRC on a received packet.  If result is zero CRC is good.
uint16_t ck_rx_crc(){

   uint16_t rx_crc;
   UCHAR j = rx_buf[COUNT] + 4; //get size of complete packet including crc bytes
   UCHAR i;

   rx_crc = 0;
   for(i=0; i < j; i++) rx_crc = calc_crc(rx_buf[i],rx_crc);  //compute CRC over complete buffer
   return rx_crc;

}

//--------------------------------------------------------------------------
void inversion_check(UCHAR init)
{  static UCHAR inv_ctr ;

 /* Inversion detector switch logic. Changes state 
       of "inverted" variable depending on physical position.
       Port C input Bit 0 is high and bit 1 is low when upright. 
          */

       if(init == 1){
            inv_ctr = 0;     //reset debouncer if init is 1
            inverted = FALSE;
       }

    
       if((bit_is_set(PINC,1))
           && (bit_is_clear(PINC,0)))   {   //Inversion switch debouncer
           if(inv_ctr < 50) inv_ctr++;
       }
                 
       if((bit_is_clear(PINC,1))
          && (bit_is_set(PINC,0))){

           if(inv_ctr > 0) inv_ctr--;
       }

       if (inv_ctr == 50) inverted = TRUE;
       if(inv_ctr == 0) inverted = FALSE;

}
//--------------------------------------------------------------------------
#ifdef SPINNER
  
    /* Spinner weapon motor control */

 #define WM_ENAB 5
 #define WM_REV  4
 #define WM_FWD  3
 #define WM_PORT PORTC
 
void spinner_control(UCHAR m,UCHAR rev, UCHAR z )
{  static UCHAR lastm, weapon_motor;
   UCHAR hbridge, ilim;

    if(z > 0) {       //Initialize if z > 0
        lastm = m;
        weapon_motor = 0;
        
    }else{
        
        if(m && (lastm == 0) ){       //Toggle motor on and off
            weapon_motor = weapon_motor ^ 1;
        }
        
        lastm = m;

    }

    ilim = 15;                      //Set current limit to 600 ma

    cli();  //interrupts off
       hbridge = 0;
    if(weapon_motor > 0){ 
      if(inverted)
          hbridge = 2;
      else
          hbridge = 1;
    }

    if(rev){
       hbridge = 0; 
       if(weapon_motor > 0){ 
         ilim = 25;           //limit to 1 amp
         if(inverted)
             hbridge = 1;
         else
             hbridge = 2;
       }
   }



   if (ad_w_current > ilim){         //Limit weapon motor to current to ilim
       hbridge = 0;;  
   }

   if(timer_RX_comm_data < 3){
       hbridge = 0;             //Weapon off if no control data
   }                    //RFI from weapon motor goes away so a good packet can be received.


   if(hbridge == 0){
        cbi(WM_PORT,WM_FWD);
        cbi(WM_PORT,WM_REV);
        cbi(WM_PORT,WM_ENAB);
   }
   if(hbridge == 1){
        sbi(WM_PORT,WM_FWD);
        cbi(WM_PORT,WM_REV);
        sbi(WM_PORT,WM_ENAB);
   }


   if(hbridge == 2){
        cbi(WM_PORT,WM_FWD);
        sbi(WM_PORT,WM_REV);
        sbi(WM_PORT,WM_ENAB);
  }


   sei();   //interrupts on

}


  #endif
  
//---------------------------------------------------------------------------
/*Joystick to speed curve function.   New April 4 2004.

Input is 16 integer bit joystick value ranging from -127 to +127.
Output has same range but is non-linear.
The low speed ranges are expanded (requires more stick movement for a given speed change)
The high speed ranges are compressed.
Bot is much easier to control at low speeds now!

Function:  b = ((a * a) + (16 * a)) / (127 + 16) 

A short perl simulator for this function:

#!/usr/bin/perl
for($a=0 ;$a<128; $a++){
$c1 = 16;
$b = (($a * $a) + ($c1*$a)) / (127+$c1);
print ($a," ", $b,"\n");
}

*/ 

int speed_control_curve(int js)
{   int16_t a,b;
    UCHAR flag;

    flag = 0;
    if(js < 0) flag = 1;    //Set flag if negative

    a = abs(js);            // Absolute value
    b = (a * a) + (a * 16); //Exponential function
    b = b / 143;            //Scale  to +/- 127 range

    if (flag == 1){         //If sign was negative then twos compliment result
        b = (b ^ 0xffff) + 1;
    }
    
    return b;

}

//--------------------------------------------------------------------------
#define DZ 6
int dead_zone(int spd)
{
    if((spd < DZ) && (spd > -DZ) ) spd = 0;
    return spd;

}
        
//---------------------------------------------------------------------------

 int main(void)
{
  UCHAR last_pos;
  int max_fwd_speed, max_rev_speed;
  int x,y,z,yy;

  outp(BV(CS01), TCCR0);      /* use CLK/8 source for counter */
  
  
  outp(0x00, PORTD);          
  outp(0xff, DDRD);           /* port D all outputs */

  outp(0x00,PORTB);           /* port B all bits off */
  outp(0x3f,DDRB);            /* low 3 bits outputs, RX frequency control */

  outp(0x00,DDRA);            // 8 bits analog inputs
  outp(0,PORTA);              // Clear port A
  outp(0x85,ADCSRA);           // Enable A to D converter with /32 conv clk.  ( 230.4 khz)

  
  outp(0x44,PORTC);           // 
  outp(0xfc,DDRC);            // bits 0,1 input on port C  (inversion switches)

  //Timer 1 dual PWM setup
  outp(0x09,TCCR1B);            // clk/1 prescale, 8 bit fast pwm  (28.8 khz pwm freq))
  outp(0xa1,TCCR1A);            // 8 bit pwm, OCnx clear at top
  outp(0,OCR1AH);               //Clear the output compare registers
  outp(0,OCR1AL);
  outp(0,OCR1BH);
  outp(0,OCR1BL);


  outp(0x0b,TCCR2);           // clear on compare, Prescale = 32 on T/C 2  (230400 hz)
  outp(24,OCR2);              // T/C 2 compare register for 9600 hz interrupts
  sbi(TIMSK,OCIE2);           // Enable timer 2 compare interrupts
  
  outp(47,UBRRL);              /* UART baud rate set to 9600 */
  sbi(UCSRB,RXEN);              // enable uart receiver
  sbi(UCSRB,RXCIE);             // receiver interupt enabled

 #ifdef SPINNER
 spinner_control(0,0,1);  //Initialize spinner control
 #endif
 
 /* Linx 900 mhz receiver frequency control */
  cbi(PORTB,0);
  cbi(PORTB,1);
  sbi(PORTB,2);     //Set receiver to channel 4, 912.37 mhz
  
  

  x_offset = 0;
  y_offset = 0;
  inverted = FALSE;
  last_pos = inverted;
  rssi = 0;
  
  minutes = 0;
  run_seconds = 0;
  txstate = 0;
  l_speed = 0;                //Motors set to zero speed
  r_speed = 0;
  matrix0 = 0;
  matrix1 = 0;
  timer_RX_comm_data = 0;
  timer_led_yellow = 0;

  inversion_check(1);       //Initialize inversion checker
  
  sbi(PORTC,LED_GREEN);     //good packet indicator set to off - power save enabled
  testmode = 0;

  
  sei();                      /* enable global interrupts */
  
  
  
   do {

       if(run_seconds & 1)
           cbi(PORTC,LED_YELLOW);        /* Blink yellow LED while no signal from controller*/
           else
               sbi(PORTC,LED_YELLOW);
       
   }                                /* Remain here until signal received */
   while(timer_RX_comm_data == 0); 


   sbi(PORTC,LED_YELLOW);           /* Reset complete, transmitter signal is being received, Yellow LED off*/
   wdt_enable(WDTO_250MS);          /* enable watchdog timer - 250 ms timeout. */ 

  


  for(;;){                   // ** MAIN LOOP starts here **

    if(timer_led_yellow > 0)
           cbi(PORTC,LED_YELLOW);        /* Blink yellow LED on to show receive data errors*/
           else
               sbi(PORTC,LED_YELLOW);



    inversion_check(0);  //Check our position. Set or clears "inverted" global variable.
            
               
    /* Get A-D converter values */

    ad_pot          = get_adc8(AD_POT);        //For test and debug only

    ad_lm_cfb_plus  = get_adc8(AD_LM_CFB_PLUS) ;     //Get motor current values
    ad_lm_cfb_minus = get_adc8(AD_LM_CFB_MINUS) ;
    ad_rm_cfb_plus  = get_adc8(AD_RM_CFB_PLUS) ;
    ad_rm_cfb_minus = get_adc8(AD_RM_CFB_MINUS) ;


#ifdef SPINNER
    ad_w_current    = get_adc8(AD_WM_CURRENT) ;  //Weapon motor current
#endif



    
    
    if(txstate == 0) make_info_pkt();     //Transmit debug telemetry
    

    if(timer_RX_comm_data == 0) sbi(PORTC,LED_GREEN); //Clear the green DATA LED if timeout

   
    z = rx_pkt[RIGHTJOY_Y] ;        //Get Y Joystick value
    z = (int)z - 128;               //Convert to signed 16 bit int
    z = z - y_offset;               //subtract the offset
    if(z < -127) z = -127;          //hard limit at -127  
    y = speed_control_curve(z);     //Linear to exponential curve on y (speed)

    
    z = rx_pkt[RIGHTJOY_X] ;        //Get X Joystick value
    z = (int)z - 128;               //Convert to signed 16 bit int
    z = z - x_offset;               //subtract the offset
    if(z < -127) z = -127;          //hard limit at -127
    x = speed_control_curve(z);     //Linear to exponential curve on x (steering)
     
           
    // Do speed sensitive steering 
    
    yy = abs(y);                    //Absolute value of y (speed)
    yy = 600 - (yy * 3);            //scale and offset the speed
    yy = yy >> 2;                   //Fast divide by 4
    x = (x * yy) / 200;             //adjust steering value based on speed
        


    l_speed = hard_limit_range(y - x, 127, -128);    //Now mix the x (steering) with y (speed)
    r_speed = hard_limit_range(y + x, 127, -128);

    
    /* Get controller switch matrix */

    if(timer_RX_comm_data > 0){    //If valid RC data get the switch matrix bits
       matrix0 = rx_pkt[MATRIX0];
       matrix1 = rx_pkt[MATRIX1];
    }else{
       matrix0 = 0;  //Set to zero if RC signal invalid
       matrix1 = 0;
    }


    if(matrix1 & m_Z){      //If Z button pushed... (or START on Sony PS2 controller)
       y_offset = rx_pkt[RIGHTJOY_Y] - 128;      //save the joystick neutral settings... 
       x_offset = rx_pkt[RIGHTJOY_X] - 128 ;     //...as signed 16 bit integers


    }
  
        
  #ifdef SPINNER
  
    /* Spinner weapon motor control */

   spinner_control((matrix0 & m_left_fire), (matrix1 & m_right_fire) ,0);

  #endif
  

     max_fwd_speed = MAX_FWD_SPEED;
     max_rev_speed = MAX_REV_SPEED;
    


     /* Now get the left motion controller bits */
     /* These will override the joy stick control */
    

 if(matrix0 & m_south){           //Full speed reverse 
   l_speed = max_rev_speed;
   r_speed = max_rev_speed;
   
}


 if(matrix0 & m_east){         //Spin clockwise
   l_speed = max_fwd_speed;
   r_speed = max_rev_speed;

   
 }

if(matrix0 & m_west){        //Spin counter clockwise  
   l_speed = max_rev_speed;
   r_speed = max_fwd_speed;

}


 if(matrix0 & m_north){       //Full Speed ahead 
    l_speed = max_fwd_speed;
    r_speed = max_fwd_speed;
 }

  
   
 
   adjust_motor_speed(l_speed,r_speed);   //Tell the motors how fast to spin
    
    
   last_matrix0 =  matrix0;         //remember current push button matrix values
   last_matrix1 =  matrix1;         //...so we can check for changes later

  }
  

}      //End of main loop




//---------------------------------------


void delay_100us(uint8_t count)
{
  while(count){
    outp(256 - ((F_CPU/80000) -1), TCNT0);
    outp(BV(TOV0), TIFR);
    while(!(inp(TIFR) & BV(TOV0)));   
    count--;
  }
}


void delay_10ms(uint8_t count)
{
  while(count){
    delay_100us(100);
    count--;
  }
}



//----------------------------------------------------------------------
   
/* NEW April 21, 2004 */


/* Adjust motor speeds.  If bot is inverted right and left are exchanged
   and fwd/rev speeds swapped. 
   
   This function also adjusts the locked anti-phase PWM value based on
   motor current resulting from torque variations to maintain constant speed.
   
   eg: More current means more load so PWM duty cycle is increased to compensate.
   
   */

#define CURRENT_LIMIT 86
//800ma limit
/* LMD18200 puts out 377uA per amp on current sense pin (8).  
   current sense is 2.11 volts/amp or 108 A-D units/amp with a 5.6k resistor.
   This may be less due to effects of CD4053 mux and charging the track and hold caps.
   The real limit seems to be around 600 MA when CURRENT_LIMIT is 86.
*/

void adjust_motor_speed(int l_speed, int r_speed){
      UCHAR hbridge_enable;
      
      int spd, r_current, l_current, tmp;
      
      
      if(inverted){        //Swap left and right and fwd/rev when inverted
          tmp = l_speed;                        //Exchange left and right
          l_speed = r_speed;
          r_speed = tmp;
          r_speed = (r_speed ^ 0xffff) + 1 ;    //2s compliment makes it negative
          l_speed = (l_speed ^ 0xffff) + 1 ;
      }
      
      l_speed = dead_zone(l_speed);       //Create dead zones around zero
      r_speed = dead_zone(r_speed);

     
      if((l_speed != 0) || (r_speed != 0)) timer_mstop = 40;  //400ms breaking when speed goes to zero
     
      /* Right motor PWM control */
       spd = r_speed + ad_rm_cfb_plus - ad_rm_cfb_minus;  //Add motor current to speed setting
       r_current = ad_rm_cfb_plus + ad_rm_cfb_minus;      //Save total unsigned current value
       spd = hard_limit_range(spd,127,-128);    //Limit range to +127 , -128
       rm_pwm = (UCHAR)spd + 128;
       outp(0,OCR1BH);                      //set right PWM
       outp(rm_pwm,OCR1BL);


       /* Left motor PWM control */ 
       spd = l_speed + ad_lm_cfb_plus - ad_lm_cfb_minus;  //Add motor current to speed setting
       l_current = ad_lm_cfb_plus + ad_lm_cfb_minus;      //Save total unsigned current value
       spd = hard_limit_range(spd,127,-128);
       lm_pwm = (UCHAR)spd + 128; 
       outp(0,OCR1AH);                      //set left PWM
       outp(lm_pwm,OCR1AL);



       /* H-Bridge enable control */

       hbridge_enable = 1;      //Assume h-bridge will be enabled
                                //Then look for reasons to disable it below...

         if ((l_current > CURRENT_LIMIT) || (r_current > CURRENT_LIMIT)){
              hbridge_enable = 0;  // Limit drive motor current to 1000 ma
         }

         if ((l_speed == 0)
             && (r_speed == 0)
             && (timer_mstop == 0)) hbridge_enable = 0;   //Disable H-Bridge if bot is stopped

         if(testmode == 1) hbridge_enable = 0;
         if(timer_RX_comm_data == 0) hbridge_enable = 0;  //Stop the bot if no control data

         if(hbridge_enable > 0) 
             sbi(PORTD,DRIVE_ENABLE);  //Output ENABLE signal to the H-Bridge
         else
             cbi(PORTD,DRIVE_ENABLE);   //Or not...

 }


   

//-----------------------------------------------------------------------
/* Come here 9600 times per second.   */
SIGNAL(SIG_OUTPUT_COMPARE2)
{
    

      if(--prescale == 0){      //This executes 100 times per sec.
         prescale = 96;
         
         if(prescale1sec > 0) prescale1sec--;
         if(timer_RX_comm_data > 0) timer_RX_comm_data--; 
         if(timer_led_yellow > 0) timer_led_yellow--;
   ;
         if(timer_mstop > 0) timer_mstop--;
         
                 
      }

      
     if(prescale1sec == 0){  //This executes once per second
        prescale1sec = 100;
        if(prescaleMinute > 0) prescaleMinute--;
        run_seconds++;
        
     }

     if(prescaleMinute == 0){ //This executes once per minute
        prescaleMinute = 60;
        minutes++;
        
        
     }


 
    /* End interrupt code */
    

}

//-----------------------------------------------------------------------
/* UART receive interrupt handler */

SIGNAL(SIG_UART_RECV)
{
   static UCHAR rxstate = 0;
 
    UCHAR ch,i;
    
    ch = inp(UDR);      //Get the current rx char
    

    switch (rxstate){
 
    case 0:    if((ch == SYNC2) && (last_rx == SYNC1)){
                   rxstate = 1;
                   
               }
               break;


    case 1:    rx_buf[ADDRESS] = ch;
               rxstate = 2;
               break;

    case 2:    rx_buf[COUNT] = ch;
               rxstate = 3;
               rx_ptr = DATA;
               rx_ctr = ch + 2;
               if((rx_ctr + 4) > RXSIZE) rxstate = 0;  //Quit if buffer would overflow
               break;

    case 3:    rx_buf[rx_ptr++] = ch;
               rx_ctr--;
               if(rx_ctr == 0){
                  if((ck_rx_crc() == 0) && (rx_buf[ADDRESS] == BOT_ADDR)){     //Good packet received for us
                     for(i=0; i < (rx_buf[COUNT] + 2); i++) rx_pkt[i] = rx_buf[i];  //Copy to packet buffer
                     cbi(PORTC,LED_GREEN);  // Light up the data LED
                     timer_RX_comm_data = 7;   //70ms timeout
                     wdt_reset() ;          //Reset Watchdog timer  	
                  }else{
                      timer_led_yellow = 2;
                  }

                  rxstate = 0;

               }

                   
               break;

               
    }


   last_rx = ch;       //Save last char

}

 //-----------------------------------------------------------------------

   /* Build a packet and send debug data */
  
   void make_info_pkt(){
      uint8_t i;
      i = ADDRESS;
      tx_buf[i] = 1;             // address of monitor
      i = DATA;
      tx_buf[i++] = 0x01;               //data type = 1
      tx_buf[i++] = l_speed;           //1  Left motor speed
      tx_buf[i++] = r_speed;           //2  right Motor speed
      tx_buf[i++] = ad_m_current;      //3  Weapon Motor current
      tx_buf[i++] = ad_lm_cfb_plus ;   //4 left motor PLUS current
      tx_buf[i++] = ad_lm_cfb_minus;   //5 left motor MINUS current
      tx_buf[i++] = ad_rm_cfb_plus;    //6 right motor PLUS current
      tx_buf[i++] = ad_rm_cfb_minus;   //7 right motor MINUS current
      tx_buf[i++] = matrix0;          //8   controller switch matrix byte 0
      tx_buf[i++] = matrix1;          //9   controller switch matrix byte 1
      tx_buf[i++] = run_seconds;       //10 run time in seconds
      tx_buf[i++] = lm_pwm;
      tx_buf[i++] = rm_pwm;
            
      tx_buf[COUNT] = i - DATA;       //Set byte count field to data length

      set_tx_buffer_crc();  //compute and add CRC16 bits to end of pkt
      
      txstate = 2;          //Tell scheduler it's ready to transmit
       
      sbi(UCSRB, TXEN); 
      sbi(UCSRB, UDRIE);
      
      outp(0xff,UDR);       // Send a FF to get things started 
   }


//----------------------------------------------------------------------


/* UART Transmit interrupt handler */  
SIGNAL(SIG_UART_DATA)
{ 
   

      switch(txstate) {
      case 0:  break;
      case 1:
      case 2:
      case 3:  outp(0xff,UDR) ; txstate++;
         break;
      case 4:  outp(SYNC1,UDR); txstate++;
         break;
      case 5:  outp(SYNC2,UDR); txstate++;
         break;

      case 6:  tx_ptr = 0;
         tx_ctr = tx_buf[COUNT] + 4; 
         txstate++;
         //fall through...

      case 7:  outp(tx_buf[tx_ptr++],UDR);  //Now send buffer contents
            tx_ctr--;
            if (tx_ctr == 0){ 
               txstate = 8;
               
            }
         
            break;

      case 8:   
               txstate = 0;
               outp(0xff,UDR);   //Feed the uart data register one last time to clear this interrupt
               cbi(UCSRB,UDRIE);   //Stop transmitter and interrupts
               cbi(UCSRB,TXEN);
               break;



      }

      
}


//---------------------------------------------------------------------------

/* end of file */


