/* ps2-rf.c Radio Control transmitter Interfaces Sony Playstation(TM) game controller pad to 9600 baud RF link for controlling combat robots. Hardware schematic: ps2-rf-xmit.sch Atmel AVR AT90S8535 microcontroller Compiled with avr-gcc (See Makefile) Note: This code is alpha. It does not set the PSx game pad to analog mode. The user must push the "analog" button on the pad. There may be other bugs. It has had limited testing. By Dale Heatherington Aug 25 2003 */ /* 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 #define F_CPU 7372800 /* crystal frequency of CPU */ #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 LED_RED 2 #define LED_YELLOW 3 /* Port C interface to game pad bit defs */ #define PAD_DAT_MASK 0x01 /*in*/ #define PAD_CMD 1 /*out*/ #define PAD_SEL 2 /*out*/ #define PAD_CLK 3 /*out*/ #define PAD_ACK_MASK 0x10 /*in*/ uint8_t matrix0,matrix1; uint16_t ad0,ad1,ad2,ad3,ad4; //A-D converter values uint8_t tx_buf[16]; //Serial tx buffer uint8_t tx_ptr,tx_ctr ; uint16_t crc; int8_t txstate, counter,val; int ii; unsigned char prescale ; volatile char prescale16ms; volatile char flag16ms; unsigned char prescale100; unsigned char prescale1sec; unsigned char prescaleMinute; unsigned char timer10ms ; unsigned char timer1sec; unsigned int minutes; char do_btu_flag; char status; char lan_watchdog; void set_tx_buffer_crc(); uint16_t get_adc(uint8_t chan); void calc_crc(char data); void make_info_pkt(); void delay_100us(uint8_t count); /* Analog joystick values note: x is left-right and y is up-down */ unsigned char left_x_joy; unsigned char left_y_joy; unsigned char right_x_joy; unsigned char right_y_joy; //-------------------------------------------------------------------------- /* Read the 10 bit analog to digital converter */ uint16_t get_adc(uint8_t chan) { outp(chan,ADMUX); //Select channel sbi(ADCSR,ADSC); //Start conversion while( bit_is_clear(ADCSR,ADIF)); //Wait for ADC output registers to be loaded return __inw_atomic(ADCL); //Return 16 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 final output value will be in crc. void calc_crc(char data){ 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 is 1 then flip crc msb data = data << 1; // get next data bit if (crc & 0x8000) { //crc msb is 1 crc = crc << 1; crc = crc ^ 0x8005; } else crc = crc << 1; } } //----------------------------------------------------------------------- void set_tx_buffer_crc(){ int j = tx_buf[COUNT + 0] + 2; //get data size int 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); } //----------------------------------------------------------------------- /* Build a packet */ void make_info_pkt(){ uint8_t i; i = ADDRESS; tx_buf[i] = 1; // address of T-Zero battle bot i = DATA; tx_buf[i++] = 0x01; //data type = 1 tx_buf[i++] = (uint8_t)right_y_joy; //1 tx_buf[i++] = (uint8_t)right_x_joy; //2 tx_buf[i++] = (uint8_t)left_y_joy; //3 tx_buf[i++] = (uint8_t)left_x_joy; //4 tx_buf[i++] = matrix0; //5 tx_buf[i++] = matrix1; //6 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 outp(0x28,UCR); //enable UART TX and interrupt outp(0xff,UDR); // Send a FF to get things started } //--------------------------------------------------------------------------- /* Clock data to and from the game pad. Data is sent and received LSB first (right shifts) Clock cycle time is about 4 microseconds. */ char byte_to_game_pad(unsigned char c) { unsigned char i,j,cmd,dat; cmd = c; dat = 0; cli(); //Disable interrupts for(i=0;i<8;i++){ //Shift 8 bits out and in if(cmd & 1) {sbi(PORTC,PAD_CMD);} else {cbi(PORTC,PAD_CMD);} //output a command bit cbi(PORTC,PAD_CLK); //Clock low cmd = cmd >> 1; //Shift command right for(j=0;j<3;j++) cbi(PORTC,PAD_CLK); cbi(PORTC,PAD_CLK); cbi(PORTC,PAD_CLK); dat = dat >> 1; //shift input data right dat = dat & 0x7f; //clear msb sbi(PORTC,PAD_CLK); //Clock high sbi(PORTC,PAD_CLK); if(PAD_DAT_MASK & inp(PINC)) dat = dat | 0x80; //Set input data msb if PAD bit is set } sbi(PORTC,PAD_CMD); //Wait for ACK- with a timeout for(j=0;j<64;j++){ if(~inp(PINC) & PAD_ACK_MASK) break; //Break out of loop when ACK- goes low } sei(); //Enable interrupts return dat; } //--------------------------------------------------------------------------- void get_game_pad_data() { unsigned char i, rv, id, m0,m1; for(i=0;i<8;i++) sbi(PORTC,PAD_CMD); //CMD high for(i=0;i<8;i++) cbi(PORTC,PAD_SEL); //SEL- low rv = byte_to_game_pad(0x01); // CMD 0x01 gets it started id = byte_to_game_pad(0x42); // id = 0x41 digital mode or 0x73 analog rv = byte_to_game_pad(0); // ignore this byte m0 = ~byte_to_game_pad(0); //Get buttons m1 = ~byte_to_game_pad(0); //Get more buttons if(id == 0x73){ //Check for "Analog" mode and get joy stick values right_x_joy = byte_to_game_pad(0); right_y_joy = byte_to_game_pad(0); left_x_joy = byte_to_game_pad(0); left_y_joy = byte_to_game_pad(0); } else //If not Analog mode set joy stick values to neutral. { right_x_joy = 0x80; right_y_joy = 0x80; left_x_joy = 0x80; left_y_joy = 0x80; } sbi(PORTC,PAD_SEL); //SEL- high matrix0 = 0; matrix1 = 0; /* Map the Sony Playstaton buttons to the same bits I used for the hacked Logitech Wingman game pad */ if(m0 & 1) matrix0 |= 0x80; //Select -> mode if(m0 & 8) matrix1 |= 0x08; //Start -> Z if(m0 & 0x10) matrix0 |= 0x20; //North -> North if(m0 & 0x20) matrix0 |= 0x40; // East -> East if(m0 & 0x40) matrix0 |= 0x08; // South -> South if(m0 & 0x80) matrix0 |= 0x01; // West -> West if(m1 & 0x01) matrix0 |= 0x02; // L2 -> Left Fire if(m1 & 0x02) matrix1 |= 0x80; // R2 -> Right Fire if(m1 & 0x04) matrix0 |= 0x02; // L1 -> Left Fire if(m1 & 0x08) matrix1 |= 0x80; // R1 -> Right Fire if(m1 & 0x10) matrix1 |= 0x20; // triangle -> B if(m1 & 0x20) matrix1 |= 0x10; //Circle -> C if(m1 & 0x40) matrix1 |= 0x02; // X -> X if(m1 & 0x80) matrix1 |= 0x40; //Square -> A } //--------------------------------------------------------------------------- int main(void) { outp(BV(CS01), TCCR0); /* use CLK/8 source for counter */ outp(0xff, DDRD); /* port D all outputs */ outp(0xff, PORTD); /* turn all leds off */ outp(0x00,DDRA); //8 bits analog inputs outp(0,PORTA); //Clear port A outp(0x85,ADCSR); //Enable A to D converter with /32 conv clk. outp(0xff,PORTC); //Set port C bits outp(0xee,DDRC); //Bits 0,4 are inputs, others outputs on PORTC outp(0x0b,TCCR2); //clear on compare, Prescale = 32 on T/C 2 outp(192,OCR2); //T/C 2 compare register sbi(TIMSK,OCIE2); //Enable timer 2 compare interrupts outp(47,UBRR); /* UART baud rate set to 9600 */ txstate = 0; sei(); /* enable global interrupts */ right_x_joy = 0x80; right_y_joy = 0x80; left_x_joy = 0x80; left_y_joy = 0x80; matrix0 = 0; matrix1 = 0; for(;;){ //MAIN LOOP /* Get A-D converter values */ //ad0 = get_adc(0) >> 2; //Convert 10 bit value to 8 bits by right shifts //ad1 = get_adc(1) >> 2; // ad2 = get_adc(2) >> 2; // ad3 = get_adc(3) >> 2; ad4 = get_adc(4) >> 2; if((ad4 & 0xff) < 166){ cbi(PORTD,LED_YELLOW); //Yellow LED on if battery less than 6.5 volts }else{ sbi(PORTD,LED_YELLOW); } if(flag16ms != 0) { //Get game pad data every 16 milliseconds flag16ms = 0; get_game_pad_data(); } while(txstate > 0) delay_100us(1) ; //Wait for transmitter to be ready if(txstate == 0) make_info_pkt(); //Transmit data to robot } } //--------------------------------------- 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--; } } //----------------------------------------------------------------------- /* Come here 1200 times per second */ SIGNAL(SIG_OUTPUT_COMPARE2) { if(--prescale == 0){ //This executes 100 times per sec. prescale = 12; if(prescale1sec > 0) prescale1sec--; } if(--prescale16ms == 0){ prescale16ms = 19; //every 15.8 ms flag16ms = 1; } if(prescale1sec == 0){ //This executes once per second prescale1sec = 100; if(prescaleMinute > 0) prescaleMinute--; } if(prescaleMinute == 0){ //This executes once per minute prescaleMinute = 60; minutes++; } } //---------------------------------------------------------------------- /* UART interrupt handler */ SIGNAL(SIG_UART_DATA) { switch(txstate) { case 0: break; case 1: case 2: case 3: outp(0xff,UDR) ; txstate++; cbi(PORTD,LED_RED); break; case 4: outp(0xf0,UDR); txstate++; break; case 5: outp(0x55,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 outp(UCR,0); //Stop the UART transmitter and Interrupts sbi(PORTD,LED_RED); //LED off break; } } //--------------------------------------------------------------------------- /* end of file */