/** * \file * CC2430 RF driver * \author * Zach Shelby * * bankable code for cc2430 rf driver. this code can be placed in any bank. * */ #include #include "contiki.h" #include "dev/radio.h" #include "dev/cc2430_rf.h" #include "cc2430_sfr.h" #include "sys/clock.h" #include "net/packetbuf.h" #include "net/rime/rimestats.h" extern void (* receiver_callback)(const struct radio_driver *); #ifndef RF_DEFAULT_POWER #define RF_DEFAULT_POWER 100 #endif #ifndef RF_DEFAULT_CHANNEL #define RF_DEFAULT_CHANNEL 18 #endif #ifndef CC2430_CONF_CHECKSUM #define CC2430_CONF_CHECKSUM 0 #endif /* CC2420_CONF_CHECKSUM */ #if CC2430_CONF_CHECKSUM #include "lib/crc16.h" #define CHECKSUM_LEN 2 #else #define CHECKSUM_LEN 2 #endif /* CC2430_CONF_CHECKSUM */ #if DEBUG_LEDS /* moved leds code to BANK1 to make space for cc2430_rf_process in HOME */ /* can't call code in BANK1 from alternate banks unless it is marked with __banked */ #include "dev/leds.h" #define RF_RX_LED_ON() leds_on(LEDS_RED); #define RF_RX_LED_OFF() leds_off(LEDS_RED); #define RF_TX_LED_ON() leds_on(LEDS_GREEN); #define RF_TX_LED_OFF() leds_off(LEDS_GREEN); #else #define RF_RX_LED_ON() #define RF_RX_LED_OFF() #define RF_TX_LED_ON() #define RF_TX_LED_OFF() #endif #define DEBUG 0 #if DEBUG #define PRINTF(...) printf(__VA_ARGS__) #else #define PRINTF(...) do {} while (0) #endif #define RX_ACTIVE 0x80 #define TX_ACK 0x40 #define TX_ON_AIR 0x20 #define RX_NO_DMA #ifdef HAVE_RF_ERROR uint8_t rf_error = 0; #endif uint8_t rf_initialized = 0; uint8_t rf_tx_power; uint8_t rf_flags; uint8_t rf_channel = 0; rf_address_mode_t rf_addr_mode; uint16_t rf_manfid; uint8_t rf_softack; uint16_t rf_panid; /*---------------------------------------------------------------------------*/ PROCESS_NAME(cc2430_rf_process); /*---------------------------------------------------------------------------*/ const struct radio_driver cc2430_rf_driver = { cc2430_rf_send, cc2430_rf_read, cc2430_rf_set_receiver, cc2430_rf_on, cc2430_rf_off, }; /*---------------------------------------------------------------------------*/ void cc2430_rf_init(void) __banked { if(rf_initialized) { return; } PRINTF("cc2430_rf_init called\n"); RFPWR &= ~RREG_RADIO_PD; /*make sure it's powered*/ while((RFPWR & ADI_RADIO_PD) == 1); while((RFIF & IRQ_RREG_ON) == 0); /*wait for power up*/ SLEEP &= ~OSC_PD; /*Osc on*/ while((SLEEP & XOSC_STB) == 0); /*wait for power up*/ rf_flags = 0; rf_softack = 0; FSMTC1 = 1; /*don't abort reception, if enable called, accept ack, auto rx after tx*/ MDMCTRL0H = 0x02; /* Generic client, standard hysteresis, decoder on 0x0a */ MDMCTRL0L = 0xE2; /* automatic ACK and CRC, standard CCA and preamble 0xf2 */ MDMCTRL1H = 0x30; /* Defaults */ MDMCTRL1L = 0x0; RXCTRL0H = 0x32; /* RX tuning optimized */ RXCTRL0L = 0xf5; /* get ID for MAC */ rf_manfid = CHVER; rf_manfid <<= 8; rf_manfid += CHIPID; cc2430_rf_channel_set(RF_DEFAULT_CHANNEL); cc2430_rf_command(ISFLUSHTX); cc2430_rf_command(ISFLUSHRX); cc2430_rf_set_addr(0xffff, 0x0000, NULL); cc2430_rf_address_decoder_mode(RF_DECODER_NONE); RFIM = IRQ_FIFOP; RFIF &= ~(IRQ_FIFOP); S1CON &= ~(RFIF_0 | RFIF_1); IEN2 |= RFIE; RF_TX_LED_OFF(); RF_RX_LED_OFF(); rf_initialized = 1; process_start(&cc2430_rf_process, NULL); } /*---------------------------------------------------------------------------*/ int cc2430_rf_send_b(void *payload, unsigned short payload_len) __banked { uint8_t i, counter; if(rf_flags & TX_ACK) { return -1; } if((rf_flags & RX_ACTIVE) == 0) { cc2430_rf_rx_enable(); } /* Check packet attributes */ /*printf("packetbuf_attr: txpower = %d\n", packetbuf_attr(PACKETBUF_ATTR_RADIO_TXPOWER));*/ /* Should set TX power according to this if > 0 */ PRINTF("cc2430_rf: sending %ud byte payload\n", payload_len); RIMESTATS_ADD(lltx); cc2430_rf_command(ISFLUSHTX); PRINTF("cc2430_rf: sent = "); /* Send the phy length byte first */ RFD = payload_len+CHECKSUM_LEN; /* Payload plus FCS */ PRINTF("(%d)", payload_len+CHECKSUM_LEN); for(i = 0 ; i < payload_len; i++) { RFD = ((unsigned char*)(payload))[i]; PRINTF("%02X", ((unsigned char*)(payload))[i]); } PRINTF("\n"); /* Leave space for the FCS */ RFD = 0; RFD = 0; if(cc2430_rf_cca_check(0,0) == -1) { return -1; } /* Start the transmission */ RFIF &= ~IRQ_TXDONE; cc2430_rf_command(ISTXON); counter = 0; while(!(RFSTATUS & TX_ACTIVE) && (counter++ < 3)) { clock_delay(10); } if(!(RFSTATUS & TX_ACTIVE)) { PRINTF("cc2430_rf: TX never active.\n"); cc2430_rf_command(ISFLUSHTX); return -1; } else { RF_RX_LED_OFF(); RF_TX_LED_ON(); // rf_flags |= TX_ON_AIR; } return 1; } /*---------------------------------------------------------------------------*/ int cc2430_rf_read_banked(void *buf, unsigned short bufsize) __banked { uint8_t i, len; #if CC2420_CONF_CHECKSUM uint16_t checksum; #endif /* CC2420_CONF_CHECKSUM */ /* RX interrupt polled the cc2430_rf_process, now read the RX FIFO */ /* Check the length */ len = RFD; /* Check for validity */ if(len > CC2430_MAX_PACKET_LEN) { /* Oops, we must be out of sync. */ PRINTF("error: bad sync\n"); cc2430_rf_command(ISFLUSHRX); RIMESTATS_ADD(badsynch); return 0; } if(len <= CC2430_MIN_PACKET_LEN) { PRINTF("error: too short\n"); cc2430_rf_command(ISFLUSHRX); RIMESTATS_ADD(tooshort); return 0; } if(len - CHECKSUM_LEN > bufsize) { PRINTF("error: too long\n"); cc2430_rf_command(ISFLUSHRX); RIMESTATS_ADD(toolong); return 0; } /* Read the buffer */ PRINTF("cc2430_rf: read = "); PRINTF("(%d)", len); for(i = 0; i < (len - CHECKSUM_LEN); i++) { ((unsigned char*)(buf))[i] = RFD; PRINTF("%02X", ((unsigned char*)(buf))[i]); } PRINTF("\n"); #if CC2430_CONF_CHECKSUM /* Deal with the checksum */ checksum = RFD * 256; checksum += RFD; #endif /* CC2430_CONF_CHECKSUM */ packetbuf_set_attr(PACKETBUF_ATTR_RSSI, ((int8_t) RFD) - 45); packetbuf_set_attr(PACKETBUF_ATTR_LINK_QUALITY, RFD); RFIF &= ~IRQ_FIFOP; RFSTATUS &= ~FIFO; cc2430_rf_command(ISFLUSHRX); cc2430_rf_command(ISFLUSHRX); RF_RX_LED_OFF(); RIMESTATS_ADD(llrx); return (len - CHECKSUM_LEN); } /** * Execute a single CSP command. * * \param command command to execute * */ void cc2430_rf_command(uint8_t command) __banked { if(command >= 0xE0) { /*immediate strobe*/ uint8_t fifo_count; switch(command) { /*hardware bug workaround*/ case ISRFOFF: case ISRXON: case ISTXON: fifo_count = RXFIFOCNT; RFST = command; clock_delay(2); if(fifo_count != RXFIFOCNT) { RFST = ISFLUSHRX; RFST = ISFLUSHRX; } break; default: RFST = command; } } else if(command == SSTART) { RFIF &= ~IRQ_CSP_STOP; /*clear IRQ flag*/ RFST = SSTOP; /*make sure there is a stop in the end*/ RFST = ISSTART; /*start execution*/ while((RFIF & IRQ_CSP_STOP) == 0); } else { RFST = command; /*write command*/ } } /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /** * Select RF channel. * * \param channel channel number to select * * \return channel value or negative (invalid channel number) */ /* channel freqdiv = (2048 + FSCTRL(9:0)) / 4 freq = (2048 + FSCTRL(9:0)) MHz */ int8_t cc2430_rf_channel_set(uint8_t channel) { uint16_t freq; if((channel < 11) || (channel > 26)) { return -1; } cc2430_rf_command(ISSTOP); /*make sure CSP is not running*/ cc2430_rf_command(ISRFOFF); /* Channel values: 11-26 */ freq = (uint16_t) channel - 11; freq *= 5; /*channel spacing*/ freq += 357; /*correct channel range*/ freq |= 0x4000; /*LOCK_THR = 1*/ FSCTRLH = (freq >> 8); FSCTRLL = (uint8_t)freq; cc2430_rf_command(ISRXON); rf_channel = channel; return (int8_t) channel; } /*---------------------------------------------------------------------------*/ /*PA_LEVEL TXCTRL register Output Power [dBm] Current Consumption [mA] 31 0xA0FF 0 17.4 27 0xA0FB -1 16.5 23 0xA0F7 -3 15.2 19 0xA0F3 -5 13.9 15 0xA0EF -7 12.5 11 0xA0EB -10 11.2 7 0xA0E7 -15 9.9 3 0xA0E3 -25 8.5*/ /** * Select RF transmit power. * * \param new_power new power level (in per cent) * * \return new level or negative (value out of range) */ int8_t cc2430_rf_power_set(uint8_t new_power) { uint16_t power; if(new_power > 100) { return -1; } power = 31 * new_power; power /= 100; power += 0xA160; /* Set transmitter power */ TXCTRLH = (power >> 8); TXCTRLL = (uint8_t)power; rf_tx_power = (int8_t) new_power; return rf_tx_power; } /*---------------------------------------------------------------------------*/ /** * Enable RF receiver. * * * \return pdTRUE * \return pdFALSE bus not free */ int8_t cc2430_rf_rx_enable(void) __banked { PRINTF("cc2430_rf_rx_enable called\n"); if(!(rf_flags & RX_ACTIVE)) { IOCFG0 = 0x7f; // Set the FIFOP threshold 127 RSSIH = 0xd2; /* -84dbm = 0xd2 default, 0xe0 -70 dbm */ rf_flags |= RX_ACTIVE; RFPWR &= ~RREG_RADIO_PD; /*make sure it's powered*/ while((RFIF & IRQ_RREG_ON) == 0); /*wait for power up*/ SLEEP &= ~OSC_PD; /*Osc on*/ while((SLEEP & XOSC_STB) == 0); /*wait for power up*/ cc2430_rf_command(ISRXON); cc2430_rf_command(ISFLUSHRX); } PRINTF("cc2430_rf_rx_enable done\n"); return 1; } /*---------------------------------------------------------------------------*/ /** * Disable RF receiver. * * * \return pdTRUE * \return pdFALSE bus not free */ int8_t cc2430_rf_rx_disable(void) __banked { cc2430_rf_command(ISSTOP); /*make sure CSP is not running*/ cc2430_rf_command(ISRFOFF); RFPWR |= RREG_RADIO_PD; /*RF powerdown*/ rf_flags = 0; return 1; } /*---------------------------------------------------------------------------*/ /** * Enable RF transmitter. * * * \return pdTRUE * \return pdFALSE bus not free */ int8_t cc2430_rf_tx_enable(void) { DMAARM = 0x80 + (1 << 0); /*ABORT + channel bit*/ return 1; } /** * Set MAC addresses * * \param pan The PAN address to set * \param adde The short address to set * \param ieee_addr The 64-bit IEEE address to set */ void cc2430_rf_set_addr(unsigned pan, unsigned addr, const uint8_t *ieee_addr) { uint8_t f; __xdata unsigned char *ptr; rf_panid = pan; PANIDH = pan >> 8; PANIDL = pan & 0xff; SHORTADDRH = addr >> 8; SHORTADDRL = addr & 0xff; if(ieee_addr != NULL) { ptr = &IEEE_ADDR0; /* LSB first, MSB last for 802.15.4 addresses in CC2420 */ for (f = 0; f < 8; f++) { *ptr++ = ieee_addr[f]; } } } /*---------------------------------------------------------------------------*/ /** * Set address decoder on/off. * * \param param 1=on 0=off. * \return pdTRUE operation successful */ int8_t cc2430_rf_address_decoder_mode(rf_address_mode_t mode) { int8_t retval = -1; rf_softack = 0; /* set oscillator on*/ switch(mode) { case RF_SOFTACK_MONITOR: rf_softack = 1; case RF_MONITOR: MDMCTRL0H |= 0x10; /*Address-decode off , coordinator*/ MDMCTRL0L &= ~0x10; /*no automatic ACK */ break; case RF_DECODER_COORDINATOR: MDMCTRL0H |= 0x18; /*Address-decode on , coordinator*/ MDMCTRL0L |= 0x10; /*automatic ACK */ break; case RF_DECODER_ON: MDMCTRL0H |= 0x08; /*Address-decode on */ MDMCTRL0L &= ~0x10; /* no automatic ACK */ break; default: MDMCTRL0H &= ~0x18; /* Generic client */ MDMCTRL0L &= ~0x10; /* no automatic ACK */ break; } rf_addr_mode = mode; retval = 1; return retval; } /*---------------------------------------------------------------------------*/ /** * Channel energy detect. * * Coordinator use this function detect best channel for PAN-network. * \return RSSI-energy level dBm. * \return 0 operation failed. */ int8_t cc2430_rf_analyze_rssi(void) { int8_t retval = -128; /*pause_us(128);*/ retval = (int8_t)RSSIL; retval -= 45; return retval; } /*---------------------------------------------------------------------------*/ /** * Clear channel assesment check. * * \return pdTRUE CCA clear * \return pdFALSE CCA reserved */ int8_t cc2430_rf_cca_check(uint8_t backoff_count, uint8_t slotted) { uint8_t counter, cca = 1; int8_t retval = 1; backoff_count; cc2430_rf_command(ISRXON); clock_delay(64); switch(slotted) { case 1: if(RFSTATUS & CCA) { counter = 0; cca = 1; while(cca != 0) { if(counter > 1) { cca = 0; } clock_delay(256); if(!(RFSTATUS & CCA)) { cca = 0; retval = -1; } counter++; } } else { retval = -1; } break; case 0: if(!(RFSTATUS & CCA)) { retval = -1; } else { } break; } return retval; } /*---------------------------------------------------------------------------*/ /** * Send ACK. * *\param pending set up pending flag if pending > 0. */ void cc2430_rf_send_ack(uint8_t pending) __banked { if(pending) { cc2430_rf_command(ISACKPEND); } else { cc2430_rf_command(ISACK); } } /*---------------------------------------------------------------------------*/