/* 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 #include #include #include #include #include #include #include #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> 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 */