From 0ec1eda75ed5c2c0e4cc5a4cf2bbe699817034b9 Mon Sep 17 00:00:00 2001 From: George Oikonomou Date: Sun, 13 Apr 2014 15:43:32 +0100 Subject: [PATCH] Implement extended RF API for the CC2538 --- cpu/cc2538/dev/cc2538-rf.c | 334 ++++++++++++++++++++++++++++++++----- cpu/cc2538/dev/cc2538-rf.h | 38 +---- 2 files changed, 297 insertions(+), 75 deletions(-) diff --git a/cpu/cc2538/dev/cc2538-rf.c b/cpu/cc2538/dev/cc2538-rf.c index 735464708..bedd92168 100644 --- a/cpu/cc2538/dev/cc2538-rf.c +++ b/cpu/cc2538/dev/cc2538-rf.c @@ -126,10 +126,39 @@ static uint8_t rf_flags; static int on(void); static int off(void); /*---------------------------------------------------------------------------*/ +/* 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[] = { + { 7, 0xFF }, + { 5, 0xED }, + { 3, 0xD5 }, + { 1, 0xC5 }, + { 0, 0xB6 }, + { -1, 0xB0 }, + { -3, 0xA1 }, + { -5, 0x91 }, + { -7, 0x88 }, + { -9, 0x72 }, + {-11, 0x62 }, + {-13, 0x58 }, + {-15, 0x42 }, + {-24, 0x00 }, +}; + +#define OUTPUT_CONFIG_COUNT (sizeof(output_power) / sizeof(output_config_t)) +/*---------------------------------------------------------------------------*/ PROCESS(cc2538_rf_process, "cc2538 RF driver"); /*---------------------------------------------------------------------------*/ -uint8_t -cc2538_rf_channel_get() +/** + * \brief Get the current operating channel + * \return Returns a value in [11,26] representing the current channel + */ +static uint8_t +get_channel() { uint8_t chan = REG(RFCORE_XREG_FREQCTRL) & RFCORE_XREG_FREQCTRL_FREQ; @@ -137,13 +166,19 @@ cc2538_rf_channel_get() + CC2538_RF_CHANNEL_MIN); } /*---------------------------------------------------------------------------*/ -int8_t -cc2538_rf_channel_set(uint8_t channel) +/** + * \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) { PRINTF("RF: Set Channel\n"); if((channel < CC2538_RF_CHANNEL_MIN) || (channel > CC2538_RF_CHANNEL_MAX)) { - return -1; + return CC2538_RF_CHANNEL_SET_ERROR; } /* Changes to FREQCTRL take effect after the next recalibration */ @@ -155,41 +190,43 @@ cc2538_rf_channel_set(uint8_t channel) return (int8_t) channel; } /*---------------------------------------------------------------------------*/ -uint8_t -cc2538_rf_power_set(uint8_t new_power) +static radio_value_t +get_pan_id(void) { - PRINTF("RF: Set Power\n"); - - REG(RFCORE_XREG_TXPOWER) = new_power; - - return (REG(RFCORE_XREG_TXPOWER) & 0xFF); + return (radio_value_t)(REG(RFCORE_FFSM_PAN_ID1) << 8 | REG(RFCORE_FFSM_PAN_ID0)); } /*---------------------------------------------------------------------------*/ -/* ToDo: Check once we have info on the... infopage */ -void -cc2538_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) { - ((uint32_t *)RFCORE_FFSM_EXT_ADDR0)[i] = - linkaddr_node_addr.u8[LINKADDR_SIZE - 1 - i]; - } -#endif - REG(RFCORE_FFSM_PAN_ID0) = pan & 0xFF; REG(RFCORE_FFSM_PAN_ID1) = pan >> 8; - - REG(RFCORE_FFSM_SHORT_ADDR0) = linkaddr_node_addr.u8[LINKADDR_SIZE - 1]; - REG(RFCORE_FFSM_SHORT_ADDR1) = linkaddr_node_addr.u8[LINKADDR_SIZE - 2]; } /*---------------------------------------------------------------------------*/ -int -cc2538_rf_read_rssi(void) +static radio_value_t +get_short_addr(void) { - int rssi; + return (radio_value_t)(REG(RFCORE_FFSM_SHORT_ADDR1) << 8 | REG(RFCORE_FFSM_SHORT_ADDR0)); +} +/*---------------------------------------------------------------------------*/ +static void +set_short_addr(uint16_t addr) +{ + REG(RFCORE_FFSM_SHORT_ADDR0) = addr & 0xFF; + REG(RFCORE_FFSM_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; /* If we are off, turn on first */ if((REG(RFCORE_XREG_FSMSTAT0) & RFCORE_XREG_FSMSTAT0_FSM_FFCTRL_STATE) == 0) { @@ -200,7 +237,7 @@ cc2538_rf_read_rssi(void) /* Wait on RSSI_VALID */ while((REG(RFCORE_XREG_RSSISTAT) & RFCORE_XREG_RSSISTAT_RSSI_VALID) == 0); - rssi = ((int8_t)REG(RFCORE_XREG_RSSI)) - RSSI_OFFSET; + rssi = (int8_t)(REG(RFCORE_XREG_RSSI) & RFCORE_XREG_RSSI_RSSI_VAL) - RSSI_OFFSET; /* If we were off, turn back off */ if((rf_flags & WAS_OFF) == WAS_OFF) { @@ -211,6 +248,80 @@ cc2538_rf_read_rssi(void) return rssi; } /*---------------------------------------------------------------------------*/ +/* Returns the current CCA threshold in dBm */ +static radio_value_t +get_cca_threshold(void) +{ + return (int8_t)(REG(RFCORE_XREG_CCACTRL0) & RFCORE_XREG_CCACTRL0_CCA_THR) - RSSI_OFFSET; +} +/*---------------------------------------------------------------------------*/ +/* Sets the CCA threshold in dBm */ +static void +set_cca_threshold(radio_value_t value) +{ + REG(RFCORE_XREG_CCACTRL0) = (value & 0xFF) + RSSI_OFFSET; +} +/*---------------------------------------------------------------------------*/ +/* Returns the current TX power in dBm */ +static radio_value_t +get_tx_power(void) +{ + int i; + uint8_t reg_val = REG(RFCORE_XREG_TXPOWER) & 0xFF; + + /* + * 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 CC2538_RF_TX_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; + + for(i = OUTPUT_CONFIG_COUNT - 1; i >= 0; --i) { + if(power <= output_power[i].power) { + REG(RFCORE_XREG_TXPOWER) = output_power[i].txpower_val; + return; + } + } +} +/*---------------------------------------------------------------------------*/ +static void +set_frame_filtering(uint8_t enable) +{ + if(enable) { + REG(RFCORE_XREG_FRMFILT0) |= RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN; + } else { + REG(RFCORE_XREG_FRMFILT0) &= ~RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN; + } +} +/*---------------------------------------------------------------------------*/ +static void +set_auto_ack(uint8_t enable) +{ + if(enable) { + REG(RFCORE_XREG_FRMCTRL0) |= RFCORE_XREG_FRMCTRL0_AUTOACK; + } else { + REG(RFCORE_XREG_FRMCTRL0) &= ~RFCORE_XREG_FRMCTRL0_AUTOACK; + } +} +/*---------------------------------------------------------------------------*/ /* Netstack API radio driver functions */ /*---------------------------------------------------------------------------*/ static int @@ -327,8 +438,10 @@ init(void) /* MAX FIFOP threshold */ REG(RFCORE_XREG_FIFOPCTRL) = CC2538_RF_MAX_PACKET_LEN; - cc2538_rf_power_set(CC2538_RF_TX_POWER); - cc2538_rf_channel_set(CC2538_RF_CHANNEL); + /* Set TX Power */ + REG(RFCORE_XREG_TXPOWER) = CC2538_RF_TX_POWER; + + set_channel(CC2538_RF_CHANNEL); /* Acknowledge RF interrupts, FIFOP only */ REG(RFCORE_XREG_RFIRQM0) |= RFCORE_XREG_RFIRQM0_FIFOP; @@ -641,24 +754,150 @@ pending_packet(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 = (REG(RFCORE_XREG_RXENABLE) && RFCORE_XREG_RXENABLE_RXENMASK) == 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(REG(RFCORE_XREG_FRMFILT0) & RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN) { + *value |= RADIO_RX_MODE_ADDRESS_FILTER; + } + if(REG(RFCORE_XREG_FRMCTRL0) & RFCORE_XREG_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 = CC2538_RF_CHANNEL_MIN; + return RADIO_RESULT_OK; + case RADIO_CONST_CHANNEL_MAX: + *value = CC2538_RF_CHANNEL_MAX; + return RADIO_RESULT_OK; + case RADIO_CONST_TXPOWER_MIN: + *value = CC2538_RF_TX_POWER_MIN; + return RADIO_RESULT_OK; + case RADIO_CONST_TXPOWER_MAX: + *value = CC2538_RF_TX_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 < CC2538_RF_CHANNEL_MIN || + value > CC2538_RF_CHANNEL_MAX) { + return RADIO_RESULT_INVALID_VALUE; + } + if(set_channel(value) == CC2538_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 < CC2538_RF_TX_POWER_MIN || value > CC2538_RF_TX_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 || LINKADDR_SIZE != 8) { + return RADIO_RESULT_INVALID_VALUE; + } + + target = dest; + for(i = 0; i < size; i++) { + target[size - 1 - i] = ((uint32_t *)RFCORE_FFSM_EXT_ADDR0)[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 || LINKADDR_SIZE != 8) { + return RADIO_RESULT_INVALID_VALUE; + } + + for(i = 0; i < size; i++) { + ((uint32_t *)RFCORE_FFSM_EXT_ADDR0)[i] = ((uint8_t *)src)[size - 1 - i]; + } + + return RADIO_RESULT_OK; + } return RADIO_RESULT_NOT_SUPPORTED; } /*---------------------------------------------------------------------------*/ @@ -776,11 +1015,26 @@ cc2538_rf_err_isr(void) void cc2538_rf_set_promiscous_mode(char p) { - if(p) { - REG(RFCORE_XREG_FRMFILT0) &= ~RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN; - } else { - REG(RFCORE_XREG_FRMFILT0) |= RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN; + set_frame_filtering(p); +} +/*---------------------------------------------------------------------------*/ +void +cc2538_rf_set_addr(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) { + ((uint32_t *)RFCORE_FFSM_EXT_ADDR0)[i] = + linkaddr_node_addr.u8[LINKADDR_SIZE - 1 - i]; } +#endif + + set_pan_id(pan); + + REG(RFCORE_FFSM_SHORT_ADDR0) = linkaddr_node_addr.u8[LINKADDR_SIZE - 1]; + REG(RFCORE_FFSM_SHORT_ADDR1) = linkaddr_node_addr.u8[LINKADDR_SIZE - 2]; } /*---------------------------------------------------------------------------*/ /** @} */ diff --git a/cpu/cc2538/dev/cc2538-rf.h b/cpu/cc2538/dev/cc2538-rf.h index 9d48929cf..3606ce78f 100644 --- a/cpu/cc2538/dev/cc2538-rf.h +++ b/cpu/cc2538/dev/cc2538-rf.h @@ -56,10 +56,13 @@ #define CC2538_RF_CHANNEL_MIN 11 #define CC2538_RF_CHANNEL_MAX 26 #define CC2538_RF_CHANNEL_SPACING 5 +#define CC2538_RF_CHANNEL_SET_ERROR -1 #define CC2538_RF_MAX_PACKET_LEN 127 #define CC2538_RF_MIN_PACKET_LEN 4 #define CC2538_RF_CCA_CLEAR 1 #define CC2538_RF_CCA_BUSY 0 +#define CC2538_RF_TX_POWER_MIN -24 +#define CC2538_RF_TX_POWER_MAX 7 /*---------------------------------------------------------------------------*/ #ifdef CC2538_RF_CONF_TX_POWER #define CC2538_RF_TX_POWER CC2538_RF_CONF_TX_POWER @@ -132,31 +135,6 @@ /** The NETSTACK data structure for the cc2538 RF driver */ extern const struct radio_driver cc2538_rf_driver; /*---------------------------------------------------------------------------*/ -/** - * \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 - */ -int8_t cc2538_rf_channel_set(uint8_t channel); - -/** - * \brief Get the current operating channel - * \return Returns a value in [11,26] representing the current channel - */ -uint8_t cc2538_rf_channel_get(void); - -/** - * \brief Sets RF TX power - * \param new_power The desired power level - * \return The power level in use after the adjustment - * - * The value specified in \e new_power will be written directly to the - * RFCORE_XREG_TXPOWER register. See the datasheet for more details on - * possible values. - */ -uint8_t cc2538_rf_power_set(uint8_t new_power); - /** * \brief Sets addresses and PAN identifier to the relevant RF hardware * registers @@ -168,15 +146,6 @@ uint8_t cc2538_rf_power_set(uint8_t new_power); */ void cc2538_rf_set_addr(uint16_t pan); -/** - * \brief Reads the current signal strength (RSSI) - * \return The current RSSI - * - * This function reads the current RSSI on the currently configured - * channel. - */ -int cc2538_rf_read_rssi(void); - /** * \brief Turn promiscous mode on or off * \param p If promiscous mode should be on (1) or off (0) @@ -187,7 +156,6 @@ int cc2538_rf_read_rssi(void); * address as the receive address are returned from the RF core. */ void cc2538_rf_set_promiscous_mode(char p); - /*---------------------------------------------------------------------------*/ #endif /* CC2538_RF_H__ */