//-------------------------------------------------------------------------- // // Collection of functions to access the LS Radio // //-------------------------------------------------------------------------- // $Archive: /WirelessUSB/WUSB Kits/CY4632 LS KBM RDK/DocSrc/CD_Root/Firmware/Source Code/RDK Keyboard/radio.c $ // $Modtime: 6/16/9/29/04 2:0019p $ // $Revision: 67 $ //-------------------------------------------------------------------------- // // Copyright 2003-2004, Cypress Semiconductor Corporation. // // This software is owned by Cypress Semiconductor Corporation (Cypress) // and is protected by and subject to worldwide patent protection (United // States and foreign), United States copyright laws and international // treaty provisions. Cypress hereby grants to licensee a personal, // non-exclusive, non-transferable license to copy, use, modify, create // derivative works of, and compile the Cypress Source Code and derivative // works for the sole purpose of creating custom software in support of // licensee product to be used only in conjunction with a Cypress integrated // circuit as specified in the applicable agreement. Any reproduction, // modification, translation, compilation, or representation of this // software except as specified above is prohibited without the express // written permission of Cypress. // // Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND,EXPRESS OR IMPLIED, // WITH REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // Cypress reserves the right to make changes without further notice to the // materials described herein. Cypress does not assume any liability arising // out of the application or use of any product or circuit described herein. // Cypress does not authorize its products for use as critical components in // life-support systems where a malfunction or failure may reasonably be // expected to result in significant injury to the user. The inclusion of // Cypress’ product in a life-support systems application implies that the // manufacturer assumes all risk of such use and in doing so indemnifies // Cypress against all charges. // // Use may be limited by and subject to the applicable Cypress software // license agreement. // //-------------------------------------------------------------------------- #include "spim_1.h" #include "ls_config.h" #include "radio.h" #include "protocol.h" #include "bind.h" #define RADIO_WAKEUP_TIME 5 3 // wait 53 ms for PLL to lock/stabliaze #define GOOD_CHANNEL 0x01 #define BAD_CHANNEL 0xff #define DEADMAN_MAX_COUNT 200 // Macros to support the radio_rx_isr handler. // These should functionaly match spi.c and are used here to // flatten the call tree for the isr due to compiler inefficiency // by using #pragma interrupt_handler. #pragma interrupt_handler radio_rx_isr #if defined (TWO_WAY) || defined (RX_MODE) #define mSPI_ADDRESS 0x3F #define bSPI_WRITE 0x80 #define SPI_ISR_RADIO_PUT(address, data) \ { \ RADIO_PORT &= ~nSS; \ SPIM_1_TX_BUFFER_REG = (((address) & mSPI_ADDRESS)| bSPI_WRITE); \ while(!(SPIM_1_CONTROL_REG & SPIM_1_SPIM_SPI_COMPLETE)); \ SPIM_1_TX_BUFFER_REG = (data); \ while(!(SPIM_1_CONTROL_REG & SPIM_1_SPIM_SPI_COMPLETE)); \ RADIO_PORT |= nSS; \ } #define SPI_ISR_RADIO_GET(address, data) \ { \ RADIO_PORT &= ~nSS; \ SPIM_1_TX_BUFFER_REG = ((address) & mSPI_ADDRESS); \ while(!(SPIM_1_CONTROL_REG & SPIM_1_SPIM_SPI_COMPLETE)); \ SPIM_1_TX_BUFFER_REG = 0xff; \ while(!(SPIM_1_CONTROL_REG & SPIM_1_SPIM_SPI_COMPLETE)); \ RADIO_PORT |= nSS; \ data = SPIM_1_RX_BUFFER_REG; \ } #define SPI_ISR_RADIO_OFF() \ { \ RADIO_ISR_DISABLE(); \ SPI_ISR_RADIO_PUT(REG_CONTROL, 0) \ } #endif // These globals are set by radioIsr UINT8 gChannelBytes = 0; UINT8 gChannelEof = 0; UINT8 gOverFlow = 0; #if defined (TWO_WAY) || defined (RX_MODE) UINT8 gChannelValid[LS_RX_PACKET_LEN]; #endif SYS_PARAMETERS sysParams; static UINT8 gLastChannel = 0xFF; const UINT8 radio_initData[] = { REG_CONTROL, 0, REG_DATA_RATE, RADIO_MODE, REG_CONFIG, bIRQ_ACTIVE_HIGH, REG_THOLD_L, THRESHOLD_L, REG_THOLD_H, THRESHOLD_H, REG_XTAL_ADJ, (bCLOCK_DISABLE), REG_PA, (PA_BIAS & mPA_BIAS), REG_RX_INT_EN, (bRX_FULL_A | bRX_EOF_A), REG_TX_INT_EN, (bTX_EMPTY), REG_SERDES_CTL, (bSERDES_ENABLE | (mEND_OF_FRAME_LEN & EOF_BITS)), REG_VALID_TX, 0xFF, REG_ANALOG_CTL, (bAGC_DISABLERSSI_CTL | bPACTL_EN), REG_AGC_CTL, bAGC_OFF, REG_SYN_LOCK_CNT, SYNTH_SETTLE_COUNT, // wait for the synth to settle REG_CLOCK_ENABLE, 0x41, // set per datasheet REG_CLOCK_MANUAL, 0x41, // set per datasheet REG_VCO_CAL, bMINUS5_PLUS5 // use the laser trimmed VCO value }; const UINT8 pnCodeTable[] = { 0x6A, 0xE7, 0x01, 0xEA, 0x03, 0xFD, 0x13, 0xD2, 0xDC, 0xC0, 0x6B, 0xB8, 0x2B, 0x09, 0xBB, 0xB2, 0xA3, 0x1E, 0xF2, 0xA4, 0x31, 0x32, 0x7A, 0xB3, 0x44, 0x83, 0x3B, 0xDD, 0x14, 0xCF, 0x8E, 0xC9, 0x35, 0x35, 0x4E, 0xC5, 0xF3, 0x52, 0x47, 0xB0, 0x7C, 0x23, 0x8A, 0xCE, 0x45, 0x5C, 0x54, 0xD7, 0x81, 0xAC, 0xFB, 0x83, 0x7A, 0x9A, 0x61, 0xAC, 0x3C, 0x12, 0x5F, 0x9C, 0x39, 0x98, 0xF6, 0x8A, // 0x99, 0x29, 0xE5, 0x96, }; /////////////////////////////////////////////////////////////////////////////// // // Function: radio_init // // Description: Reset and initialize the LS radio // // Inputs: Void // // Returns: Void // /////////////////////////////////////////////////////////////////////////////// void radio_init() { UINT8 i; UINT8 temp; SPI_RADIO_ON(); // load the configuration data for(i = 0; i < sizeof(radio_initData); i+=2) { temp = radio_initData[i]; SPI_ SPI_RADIO_PUT(radio_initData[i]temp , radio_initData[i+1]); } // use the laser trimmed VCO value SPI_RADIO_PUT( REG_VCO_CAL, bMINUS5_PLUS5 ); gChannelBytes = 0; } /////////////////////////////////////////////////////////////////////////////// // // Function: radio_receive_on // // Description: Set up the radio to receive // // Inputs: Void // // Returns: Void // /////////////////////////////////////////////////////////////////////////////// void radio_receive_on(UINT8 channel) { RADIO_ISR_DISABLE(); // clear the Radio control register if (channel != gLastChannel) { gLastChannel = channel; SPI_RADIO_PUT(REG_CHANNEL, (mCHANNEL & (channel + CHANNEL_OFFSET))); } SPI_RADIO_GET(REG_DATA_RX_A); gChannelBytes = 0; gOverFlow = 0; gChannelEof = 0; #if defined (TWO_WAY) || defined (RX_MODE) gChannelValid[0] = 0; rxPacket.first.byte = 0xff0xFF; #endif SPI_RADIO_PUT(REG_CONTROL, (bRX_ENABLE | bAUTO_SYNTH_COUNT)); RADIO_ISR_ENABLE(); } /////////////////////////////////////////////////////////////////////////////// // // Function: radio_switch_rxtransmit_on // // Description: Switch Set up the radio to receivetransmit // // Inputs: Void // // Returns: Void // /////////////////////////////////////////////////////////////////////////////// void radio_switch_rx(voidtransmit_on() { RADIO_ISR_DISABLE(); SPI_RADIO_GET(REG_DATA_RX_A); gChannelBytes = 0; gOverFlow = 0; gChannelEof = 0; #if defined (TWO_WAY) || defined (RX_MODE) gChannelValid[0] = 0; rxPacket.first.byte = 0xff; #endif SPI_RADIO_PUT(REG_CONTROL, (bRX_ENABLE | bAUTO_SYNTH_COUNT)); RADIO_ISR_ENABLE(); SPI_RADIO_PUT(REG_CONTROL, (bTX_ENABLE | bAUTO_SYNTH_COUNT)); } /////////////////////////////////////////////////////////////////////////////// // // Function: radio_transmit_onset_channel // // Description: Set up the radio to transmitchannel // // Inputs: Voidchannel // // Returns: Void // /////////////////////////////////////////////////////////////////////////////// void radio_transmit_onset_channel(UINT8 channel) { RADIO_ISR_DISABLE(); // clear the radio control bits if (channel != gLastChannel) { gLastChannel = channel; SPI_RADIO_PUT(REG_CHANNEL, (mCHANNEL & (channel + CHANNEL_OFFSET))); } SPI_RADIO_PUT(REG_CONTROL, (bTX_ENABLE | bAUTO_SYNTH_COUNT)); } /////////////////////////////////////////////////////////////////////////////// // // Function: radio_switch_tx // // Description: Switch the radio to transmit // // Inputs: Void // // Returns: Void // /////////////////////////////////////////////////////////////////////////////// void radio_switch_tx() { RADIO_ISR_DISABLE(); SPI_RADIO_PUT(REG_CONTROL, (bTX_ENABLE | bAUTO_SYNTH_COUNT)); } /////////////////////////////////////////////////////////////////////////////// // // Function: radio_off // // Description: Turn off the radio Transmitter // // Inputs: Void // // Returns: Void // /////////////////////////////////////////////////////////////////////////////// void radio_off() { RADIO_ISR_DISABLE(); // turn off tx and rx SPI_RADIO_PUT(REG_CONTROL, (0)); } /////////////////////////////////////////////////////////////////////////////// // // Function: radio_set_pn_code // // Description: Load the Channel Access Code based on the PN Code index, // By convention, the index must be an even number. // // Inputs: pnCodeIndex - index into the PN Code table, must be an even number // even number // // Returns: Void // /////////////////////////////////////////////////////////////////////////////// void radio_set_pn_code(int pnCodeIndex) { UINT8 i; if( pnCodeIndex > NUM_PNCODES - 1 ) #ifdef DEBUG if( pnCodeIndex >= NUM_PNCODES) { pnCodeIndex = NUM_PNCODES - 1; } #endif //DEBUG for(i = 0; i < 8; i++) for(i = 0; 7 >= i; i++) { SPI_RADIO_PUT(REG_PN_CODE+i, pnCodeTable[(pnCodeIndex*8)+i]); } } /////////////////////////////////////////////////////////////////////////////// // // Function: radio_transmit // // Description: Send a packet to the radio // // Inputs: len - length of packet // *data - pointer to the packet // // Returns: Void // /////////////////////////////////////////////////////////////////////////////// void radio_transmit(UINT8 len, UINT8 *data) { UINT8 i; UINT8 counter; SPI_RADIO_PUT(REG_TX_INT_EN, bTX_EMPTY); // send the packet for(i = 0; i < len; ++i, ++data) { counter = 0; while(!LS_IRQ_ASSERTED) { if( counter++ > 120 ) break; // dead man timeout, normal count 27-30 } for (counter = 0; (counter < DEADMAN_MAX_COUNT) && (!LS_IRQ_ASSERTED); counter++); //dead man timeout SPI_RADIO_PUT(REG_DATA_TX, *data); } // enable the EOF interrupt and disable the EMPTY interrupt SPI_RADIO_PUT(REG_TX_INT_EN, bTX_EOF); counter = 0; TIMER_DELAY_50_USEC(); TIMER_DELAY_50_USEC(); while(!LS_IRQ_ASSERTED) { if( counter++ > 120 ) { for (counter = 0; (counter < DEADMAN_MAX_COUNT) && (!LS_IRQ_ASSERTED); break; // counter++); //dead man timeout } } SPI_RADIO_PUT(REG_CONTROL, (0)); SPI_RADIO_GET(REG_TX_INT_STAT); } #ifdef SLEEP_ENABLED /////////////////////////////////////////////////////////////////////////////// // // Function: radio_wakeup // // Description: Turn off the LS radio to conserve power // // Inputs: void // // Returns: nothing // // /////////////////////////////////////////////////////////////////////////////// void radio_wakeup(void) { // Turn high-z back on for MISO PRT1DM1 = PORT_1_DRIVE_1; // Power up radio RADIO_PORT |= nPD; TIMER_DELAY_MSEC( 3RADIO_WAKEUP_TIME ); } /////////////////////////////////////////////////////////////////////////////// // // Function: radio_sleep // // Description: Turn off the LS radio to conserve power // // Inputs: void // // Returns: nothing // // /////////////////////////////////////////////////////////////////////////////// void radio_sleep(void) { // Power down radio RADIO_PORT &= ~nPD; // Turn on pull up for MISO to reduce current PRT1DM1 = PORT_1_DRIVE_1 & ~MISO_MASK; } #endif // SLEEP_ENABLED /////////////////////////////////////////////////////////////////////////////// // // Function: radio_rx_isr // // Description: Process the radio interrupt // // Inputs: void // // Returns: Void // /////////////////////////////////////////////////////////////////////////////// void radio_rx_isr() { #if defined (TWO_WAY) || defined (RX_MODE) UINT8 irqSource, data, valid; // poll for a Radio Module interrupt, interrupts are self clearing // when the appropiate status register is read while(LS_IRQ_ASSERTED) while( LS_IRQ_ASSERTED ) { // read the Radio Rx Interrupt register irqSource = SPI_RADIO_GET(REG_RX_INT_STAT); SPI_ISR_RADIO_GET(REG_RX_INT_STAT, irqSource); if ((irqSource & 0x0f) 0x0F) == 0) { break; } if(irqSource & bRX_FULL_A) { data = SPI_RADIO_GET(REG_DATA_RX_A); SPI_ISR_RADIO_GET(REG_DATA_RX_A, data); // don't read the valid register if it is all valid if( irqSource & bRX_VALID_A ) { valid = 0xFF; } else { valid = SPI_RADIO_GET(REG_VALID_RX_A); SPI_ISR_RADIO_GET(REG_VALID_RX_A, valid); } // filter out bad bytes and don't overwrite the globals if( (0x0F < valid > 0x0f) && (gChannelEof != 1) ) { #ifdef QUICK_RADIO_OFF // Power Savings Initiative if (((data & 0xF0) == 0x50) && (gChannelBytes == (ACK_LEN-1))) { { gChannelEof = 1; SPI_ISR_RADIO_ISR_DISABLEOFF(); SPI_RADIO_PUT(REG_CONTROL, (0)); } #endif if (gChannelBytes < LS_RX_PACKET_LEN) { *((UINT8 *) (&rxPacket.first.byte) + gChannelBytes) = data; gChannelValid[gChannelBytes] = valid; gChannelBytes++; } else { gOverFlow = 1; } } } if(irqSource & bRX_EOF_A) { if( gOverFlow == 0 ) { // only handle good packets if (gChannelBytes != 0) { gChannelEof = 1; #ifndef SPI_ISR_RADIO_DEFAULT_ON // Power Savings InitiativeOFF(); RADIO_ISR_DISABLE(); #endif #ifndef RADIO_DEFAULT_ON // Power Savings Initiative SPI_RADIO_PUT(REG_CONTROL, (0)); #endif } } else // overflow packet, dump it { gOverFlow = 0; gChannelBytes = 0; } } } // } // while(LS_IRQ_ASSERTED) #endif // TWO_WAY } /////////////////////////////////////////////////////////////////////////////// // // Function: radio_get_id // // Description: Read the radio ID registers // // Inputs: id byte number // // Returns: register value // // ////////////////////////////////////////////////////////////////////////////// UINT8 radio_get_id( UINT8 id ) { UINT8 mid; // enable the Radio ID registers SPI_RADIO_PUT( REG_ANALOG_CTL, bAGC_DISABLE | bPACTL_EN | bMFG_ID_EN ); // read the selected Radio ID register mid = SPI_RADIO_GET( REG_MFG_ID_1 + id ); // disable the Radio ID registers SPI_RADIO_PUT( REG_ANALOG_CTL, bAGC_DISABLE | bPACTL_EN ); return (mid); }