From 3183805ba440de8b18d1d2ee42ae8f27576009ff Mon Sep 17 00:00:00 2001 From: George Oikonomou Date: Fri, 18 Apr 2014 14:41:45 +0100 Subject: [PATCH] Implement extended RF API for the CC2530 RF --- cpu/cc253x/dev/cc2530-rf.c | 346 ++++++++++++++++++++++++++++--- cpu/cc253x/dev/cc2530-rf.h | 3 - cpu/cc253x/sfr-bits.h | 3 + platform/cc2530dk/contiki-main.c | 27 ++- 4 files changed, 344 insertions(+), 35 deletions(-) diff --git a/cpu/cc253x/dev/cc2530-rf.c b/cpu/cc253x/dev/cc2530-rf.c index fe52d79c3..3bd769142 100644 --- a/cpu/cc253x/dev/cc2530-rf.c +++ b/cpu/cc253x/dev/cc2530-rf.c @@ -96,6 +96,10 @@ /* 192 ms, radio off -> on interval */ #define ONOFF_TIME RTIMER_ARCH_SECOND / 3125 +#define CC2530_RF_CHANNEL_SET_ERROR -1 + +#define CC2530_RF_TX_POWER_TXCTRL_MIN_VAL 0x09 /* Value for min TX Power */ +#define CC2530_RF_TX_POWER_TXCTRL_DEF_VAL 0x69 /* Reset Value */ /*---------------------------------------------------------------------------*/ #if CC2530_RF_CONF_HEXDUMP #include "dev/io-arch.h" @@ -114,50 +118,215 @@ static int on(void); /* prepare() needs our prototype */ static int off(void); /* transmit() needs our prototype */ static int channel_clear(void); /* transmit() needs our prototype */ /*---------------------------------------------------------------------------*/ -int8_t -cc2530_rf_channel_set(uint8_t channel) +/* TX Power dBm lookup table. Values from SmartRF Studio v1.16.0 */ +typedef struct output_config { + radio_value_t power; + uint8_t txpower_val; +} output_config_t; + +static const output_config_t output_power[] = { + { 5, 0xF5 }, /* 4.5 */ + { 3, 0xE5 }, /* 2.5 */ + { 1, 0xD5 }, + { 0, 0xC5 }, /* -0.5 */ + { -1, 0xB5 }, /* -1.5 */ + { -3, 0xA5 }, + { -4, 0x95 }, + { -6, 0x85 }, + { -8, 0x75 }, + {-10, 0x65 }, + {-12, 0x55 }, + {-14, 0x45 }, + {-16, 0x35 }, + {-18, 0x25 }, + {-20, 0x15 }, + {-22, 0x05 }, + {-28, 0x05 }, /* TXCTRL must be set to 0x09 */ +}; + +#define OUTPUT_CONFIG_COUNT (sizeof(output_power) / sizeof(output_config_t)) + +/* Max and Min Output Power in dBm */ +#define OUTPUT_POWER_MIN (output_power[OUTPUT_CONFIG_COUNT - 1].power) +#define OUTPUT_POWER_MAX (output_power[0].power) +/*---------------------------------------------------------------------------*/ +/** + * \brief Get the current operating channel + * \return Returns a value in [11,26] representing the current channel + */ +static uint8_t +get_channel() +{ + return (uint8_t)((FREQCTRL + 44) / 5); +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Set the current operating channel + * \param channel The desired channel as a value in [11,26] + * \return Returns a value in [11,26] representing the current channel + * or a negative value if \e channel was out of bounds + */ +static int8_t +set_channel(uint8_t channel) { PUTSTRING("RF: Set Chan\n"); if((channel < CC2530_RF_CHANNEL_MIN) || (channel > CC2530_RF_CHANNEL_MAX)) { - return -1; + return CC2530_RF_CHANNEL_SET_ERROR; } + /* Changes to FREQCTRL take effect after the next recalibration */ /* Changes to FREQCTRL take effect after the next recalibration */ off(); FREQCTRL = (CC2530_RF_CHANNEL_MIN + (channel - CC2530_RF_CHANNEL_MIN) * CC2530_RF_CHANNEL_SPACING); on(); - return (int8_t) channel; + return (int8_t)channel; } /*---------------------------------------------------------------------------*/ -uint8_t -cc2530_rf_power_set(uint8_t new_power) +static radio_value_t +get_pan_id(void) { - PUTSTRING("RF: Set Power\n"); - /* off() */ - TXPOWER = new_power; - /* on() */ - - return TXPOWER; + return (radio_value_t)(PAN_ID1 << 8 | PAN_ID0); } /*---------------------------------------------------------------------------*/ -void -cc2530_rf_set_addr(uint16_t pan) +static void +set_pan_id(uint16_t pan) { -#if LINKADDR_SIZE==8 /* EXT_ADDR[7:0] is ignored when using short addresses */ - int i; - for(i = (LINKADDR_SIZE - 1); i >= 0; --i) { - ((uint8_t *)&EXT_ADDR0)[i] = linkaddr_node_addr.u8[LINKADDR_SIZE - 1 - i]; - } -#endif - PAN_ID0 = pan & 0xFF; PAN_ID1 = pan >> 8; +} +/*---------------------------------------------------------------------------*/ +static radio_value_t +get_short_addr(void) +{ + return (radio_value_t)(SHORT_ADDR1 << 8 | SHORT_ADDR0); +} +/*---------------------------------------------------------------------------*/ +static void +set_short_addr(uint16_t addr) +{ + SHORT_ADDR0 = addr & 0xFF; + SHORT_ADDR1 = addr >> 8; +} +/*---------------------------------------------------------------------------*/ +/** + * \brief Reads the current signal strength (RSSI) + * \return The current RSSI in dBm + * + * This function reads the current RSSI on the currently configured + * channel. + */ +static radio_value_t +get_rssi(void) +{ + int8_t rssi; - SHORT_ADDR0 = linkaddr_node_addr.u8[LINKADDR_SIZE - 1]; - SHORT_ADDR1 = linkaddr_node_addr.u8[LINKADDR_SIZE - 2]; + /* If we are off, turn on first */ + if(RXENABLE == 0) { + rf_flags |= WAS_OFF; + on(); + } + + /* Wait on RSSI_VALID */ + while((RSSISTAT & RSSISTAT_RSSI_VALID) == 0); + + rssi = (radio_value_t)RSSI - RSSI_OFFSET; + + /* If we were off, turn back off */ + if((rf_flags & WAS_OFF) == WAS_OFF) { + rf_flags &= ~WAS_OFF; + off(); + } + + return rssi; +} +/*---------------------------------------------------------------------------*/ +/* Returns the current CCA threshold in dBm */ +static radio_value_t +get_cca_threshold(void) +{ + return (int8_t)CCACTRL0 - RSSI_OFFSET; +} +/*---------------------------------------------------------------------------*/ +/* Sets the CCA threshold in dBm */ +static void +set_cca_threshold(radio_value_t value) +{ + CCACTRL0 = (value + RSSI_OFFSET) & 0xFF; +} +/*---------------------------------------------------------------------------*/ +/* Returns the current TX power in dBm */ +static radio_value_t +get_tx_power(void) +{ + int i; + uint8_t reg_val = TXPOWER; + + if(TXCTRL == CC2530_RF_TX_POWER_TXCTRL_MIN_VAL) { + return OUTPUT_POWER_MIN; + } + + /* + * Find the TXPOWER value in the lookup table + * If the value has been written with set_tx_power, we should be able to + * find the exact value. However, in case the register has been written in + * a different fashion, we return the immediately lower value of the lookup + */ + for(i = 0; i < OUTPUT_CONFIG_COUNT; i++) { + if(reg_val >= output_power[i].txpower_val) { + return output_power[i].power; + } + } + return OUTPUT_POWER_MIN; +} +/*---------------------------------------------------------------------------*/ +/* + * Set TX power to 'at least' power dBm + * This works with a lookup table. If the value of 'power' does not exist in + * the lookup table, TXPOWER will be set to the immediately higher available + * value + */ +static void +set_tx_power(radio_value_t power) +{ + int i; + + if(power <= output_power[OUTPUT_CONFIG_COUNT - 1].power) { + TXCTRL = CC2530_RF_TX_POWER_TXCTRL_MIN_VAL; + TXPOWER = output_power[OUTPUT_CONFIG_COUNT - 1].txpower_val; + return; + } + + for(i = OUTPUT_CONFIG_COUNT - 2; i >= 0; --i) { + if(power <= output_power[i].power) { + /* Perhaps an earlier call set TXCTRL to 0x09. Restore */ + TXCTRL = CC2530_RF_TX_POWER_TXCTRL_DEF_VAL; + TXPOWER = output_power[i].txpower_val; + return; + } + } +} +/*---------------------------------------------------------------------------*/ +static void +set_frame_filtering(uint8_t enable) +{ + if(enable) { + FRMFILT0 |= FRMFILT0_FRAME_FILTER_EN; + } else { + FRMFILT0 &= ~FRMFILT0_FRAME_FILTER_EN; + } +} +/*---------------------------------------------------------------------------*/ +static void +set_auto_ack(uint8_t enable) +{ + if(enable) { + FRMCTRL0 |= FRMCTRL0_AUTOACK; + } else { + FRMCTRL0 &= ~FRMCTRL0_AUTOACK; + } } /*---------------------------------------------------------------------------*/ /* Netstack API radio driver functions */ @@ -204,8 +373,7 @@ init(void) /* MAX FIFOP threshold */ FIFOPCTRL = CC2530_RF_MAX_PACKET_LEN; - cc2530_rf_power_set(CC2530_RF_TX_POWER); - cc2530_rf_channel_set(CC2530_RF_CHANNEL); + TXPOWER = CC2530_RF_TX_POWER; RF_TX_LED_OFF(); RF_RX_LED_OFF(); @@ -486,24 +654,148 @@ off(void) static radio_result_t get_value(radio_param_t param, radio_value_t *value) { - return RADIO_RESULT_NOT_SUPPORTED; + if(!value) { + return RADIO_RESULT_INVALID_VALUE; + } + + switch(param) { + case RADIO_PARAM_POWER_MODE: + *value = RXENABLE == 0 ? RADIO_POWER_MODE_OFF : RADIO_POWER_MODE_ON; + return RADIO_RESULT_OK; + case RADIO_PARAM_CHANNEL: + *value = (radio_value_t)get_channel(); + return RADIO_RESULT_OK; + case RADIO_PARAM_PAN_ID: + *value = get_pan_id(); + return RADIO_RESULT_OK; + case RADIO_PARAM_16BIT_ADDR: + *value = get_short_addr(); + return RADIO_RESULT_OK; + case RADIO_PARAM_RX_MODE: + *value = 0; + if(FRMFILT0 & FRMFILT0_FRAME_FILTER_EN) { + *value |= RADIO_RX_MODE_ADDRESS_FILTER; + } + if(FRMCTRL0 & FRMCTRL0_AUTOACK) { + *value |= RADIO_RX_MODE_AUTOACK; + } + return RADIO_RESULT_OK; + case RADIO_PARAM_TXPOWER: + *value = get_tx_power(); + return RADIO_RESULT_OK; + case RADIO_PARAM_CCA_THRESHOLD: + *value = get_cca_threshold(); + return RADIO_RESULT_OK; + case RADIO_PARAM_RSSI: + *value = get_rssi(); + return RADIO_RESULT_OK; + case RADIO_CONST_CHANNEL_MIN: + *value = CC2530_RF_CHANNEL_MIN; + return RADIO_RESULT_OK; + case RADIO_CONST_CHANNEL_MAX: + *value = CC2530_RF_CHANNEL_MAX; + return RADIO_RESULT_OK; + case RADIO_CONST_TXPOWER_MIN: + *value = OUTPUT_POWER_MIN; + return RADIO_RESULT_OK; + case RADIO_CONST_TXPOWER_MAX: + *value = OUTPUT_POWER_MAX; + return RADIO_RESULT_OK; + default: + return RADIO_RESULT_NOT_SUPPORTED; + } } /*---------------------------------------------------------------------------*/ static radio_result_t set_value(radio_param_t param, radio_value_t value) { - return RADIO_RESULT_NOT_SUPPORTED; + switch(param) { + case RADIO_PARAM_POWER_MODE: + if(value == RADIO_POWER_MODE_ON) { + on(); + return RADIO_RESULT_OK; + } + if(value == RADIO_POWER_MODE_OFF) { + off(); + return RADIO_RESULT_OK; + } + return RADIO_RESULT_INVALID_VALUE; + case RADIO_PARAM_CHANNEL: + if(value < CC2530_RF_CHANNEL_MIN || value > CC2530_RF_CHANNEL_MAX) { + return RADIO_RESULT_INVALID_VALUE; + } + if(set_channel(value) == CC2530_RF_CHANNEL_SET_ERROR) { + return RADIO_RESULT_ERROR; + } + return RADIO_RESULT_OK; + case RADIO_PARAM_PAN_ID: + set_pan_id(value & 0xffff); + return RADIO_RESULT_OK; + case RADIO_PARAM_16BIT_ADDR: + set_short_addr(value & 0xffff); + return RADIO_RESULT_OK; + case RADIO_PARAM_RX_MODE: + if(value & ~(RADIO_RX_MODE_ADDRESS_FILTER | + RADIO_RX_MODE_AUTOACK)) { + return RADIO_RESULT_INVALID_VALUE; + } + + set_frame_filtering((value & RADIO_RX_MODE_ADDRESS_FILTER) != 0); + set_auto_ack((value & RADIO_RX_MODE_AUTOACK) != 0); + + return RADIO_RESULT_OK; + case RADIO_PARAM_TXPOWER: + if(value < OUTPUT_POWER_MIN || value > OUTPUT_POWER_MAX) { + return RADIO_RESULT_INVALID_VALUE; + } + + set_tx_power(value); + return RADIO_RESULT_OK; + case RADIO_PARAM_CCA_THRESHOLD: + set_cca_threshold(value); + return RADIO_RESULT_OK; + default: + return RADIO_RESULT_NOT_SUPPORTED; + } } /*---------------------------------------------------------------------------*/ static radio_result_t get_object(radio_param_t param, void *dest, size_t size) { + uint8_t *target; + int i; + + if(param == RADIO_PARAM_64BIT_ADDR) { + if(size != 8 || !dest) { + return RADIO_RESULT_INVALID_VALUE; + } + + target = dest; + for(i = 0; i < 8; i++) { + target[i] = ((uint8_t *)&EXT_ADDR0)[7 - i] & 0xFF; + } + + return RADIO_RESULT_OK; + } return RADIO_RESULT_NOT_SUPPORTED; } /*---------------------------------------------------------------------------*/ static radio_result_t set_object(radio_param_t param, const void *src, size_t size) { + int i; + + if(param == RADIO_PARAM_64BIT_ADDR) { + if(size != 8 || !src) { + return RADIO_RESULT_INVALID_VALUE; + } + + for(i = 0; i < 8; i++) { + ((uint8_t *)&EXT_ADDR0)[i] = ((uint8_t *)src)[7 - i]; + } + + return RADIO_RESULT_OK; + } return RADIO_RESULT_NOT_SUPPORTED; } /*---------------------------------------------------------------------------*/ diff --git a/cpu/cc253x/dev/cc2530-rf.h b/cpu/cc253x/dev/cc2530-rf.h index f5722db5f..4e092ca7a 100644 --- a/cpu/cc253x/dev/cc2530-rf.h +++ b/cpu/cc253x/dev/cc2530-rf.h @@ -120,9 +120,6 @@ /*---------------------------------------------------------------------------*/ extern const struct radio_driver cc2530_rf_driver; /*---------------------------------------------------------------------------*/ -int8_t cc2530_rf_channel_set(uint8_t channel); -#define cc2530_rf_channel_get() ((uint8_t)((FREQCTRL + 44) / 5)) -uint8_t cc2530_rf_power_set(uint8_t new_power); void cc2530_rf_set_addr(uint16_t pan); /*---------------------------------------------------------------------------*/ #endif /* CC2530_RF_H_ */ diff --git a/cpu/cc253x/sfr-bits.h b/cpu/cc253x/sfr-bits.h index 7a4d72cac..09173cf18 100644 --- a/cpu/cc253x/sfr-bits.h +++ b/cpu/cc253x/sfr-bits.h @@ -184,6 +184,9 @@ /*--------------------------------------------------------------------------- * Radio Register Bits *---------------------------------------------------------------------------*/ +/* FRMFILT0 */ +#define FRMFILT0_FRAME_FILTER_EN 0x01 + /* FRMCTRL0 */ #define FRMCTRL0_APPEND_DATA_MODE 0x80 #define FRMCTRL0_AUTOCRC 0x40 diff --git a/platform/cc2530dk/contiki-main.c b/platform/cc2530dk/contiki-main.c index 7e63650f9..22f458da8 100644 --- a/platform/cc2530dk/contiki-main.c +++ b/platform/cc2530dk/contiki-main.c @@ -9,6 +9,7 @@ #include "dev/io-arch.h" #include "dev/dma.h" #include "dev/cc2530-rf.h" +#include "dev/radio.h" #include "dev/watchdog.h" #include "dev/clock-isr.h" #include "dev/port2.h" @@ -79,9 +80,11 @@ fade(int l) CC_NON_BANKED } /*---------------------------------------------------------------------------*/ static void -set_rime_addr(void) CC_NON_BANKED +set_rf_params(void) CC_NON_BANKED { char i; + uint16_t short_addr; + uint8_t ext_addr[8]; #if CC2530_CONF_MAC_FROM_PRIMARY __xdata unsigned char *macp = &X_IEEE_ADDR; @@ -114,8 +117,12 @@ set_rime_addr(void) CC_NON_BANKED FMAP = CC2530_LAST_FLASH_BANK; #endif - for(i = (LINKADDR_SIZE - 1); i >= 0; --i) { - linkaddr_node_addr.u8[i] = *macp; + /* + * Read IEEE address from flash, store in ext_addr. + * Invert endianness (from little to big endian) + */ + for(i = 7; i >= 0; --i) { + ext_addr[i] = *macp; macp++; } @@ -125,6 +132,12 @@ set_rime_addr(void) CC_NON_BANKED ENABLE_INTERRUPTS(); #endif + short_addr = ext_addr[7]; + short_addr |= ext_addr[6] << 8; + + /* Populate linkaddr_node_addr. Maintain endianness */ + memcpy(&linkaddr_node_addr, &ext_addr[8 - LINKADDR_SIZE], LINKADDR_SIZE); + /* Now the address is stored MSB first */ #if STARTUP_CONF_VERBOSE PUTSTRING("Rime configured with address "); @@ -136,7 +149,11 @@ set_rime_addr(void) CC_NON_BANKED PUTCHAR('\n'); #endif - cc2530_rf_set_addr(IEEE802154_PANID); + /* Write params to RF registers */ + NETSTACK_RADIO.set_value(RADIO_PARAM_PAN_ID, IEEE802154_PANID); + NETSTACK_RADIO.set_value(RADIO_PARAM_16BIT_ADDR, short_addr); + NETSTACK_RADIO.set_value(RADIO_PARAM_CHANNEL, CC2530_RF_CHANNEL); + NETSTACK_RADIO.set_object(RADIO_PARAM_64BIT_ADDR, ext_addr, 8); return; } /*---------------------------------------------------------------------------*/ @@ -235,7 +252,7 @@ main(void) CC_NON_BANKED /* initialize the netstack */ netstack_init(); - set_rime_addr(); + set_rf_params(); #if BUTTON_SENSOR_ON || ADC_SENSOR_ON process_start(&sensors_process, NULL);