From e9f385ae1d749deea422b9166b9eade116327fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Nohlg=C3=A5rd?= Date: Wed, 16 Sep 2015 18:25:36 +0200 Subject: [PATCH] at86rf2xx: Add support for channel page --- drivers/at86rf2xx/at86rf2xx.c | 2 +- drivers/at86rf2xx/at86rf2xx_getset.c | 129 ++++++++---------- drivers/at86rf2xx/at86rf2xx_internal.c | 75 ++++++++++ drivers/at86rf2xx/at86rf2xx_netdev.c | 31 +++++ .../at86rf2xx/include/at86rf2xx_internal.h | 15 ++ drivers/include/at86rf2xx.h | 40 ++---- 6 files changed, 192 insertions(+), 100 deletions(-) diff --git a/drivers/at86rf2xx/at86rf2xx.c b/drivers/at86rf2xx/at86rf2xx.c index 6ec20a1fb4..b194a1df32 100644 --- a/drivers/at86rf2xx/at86rf2xx.c +++ b/drivers/at86rf2xx/at86rf2xx.c @@ -148,7 +148,7 @@ void at86rf2xx_reset(at86rf2xx_t *dev) at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_CTRL_2, AT86RF2XX_TRX_CTRL_2_MASK__RX_SAFE_MODE); #ifdef MODULE_AT86RF212B - at86rf2xx_set_freq(dev, AT86RF2XX_FREQ_915MHZ); + at86rf2xx_set_page(dev, 0); #endif /* don't populate masked interrupt flags to IRQ_STATUS register */ diff --git a/drivers/at86rf2xx/at86rf2xx_getset.c b/drivers/at86rf2xx/at86rf2xx_getset.c index 4f349498f1..57578eeccc 100644 --- a/drivers/at86rf2xx/at86rf2xx_getset.c +++ b/drivers/at86rf2xx/at86rf2xx_getset.c @@ -18,6 +18,7 @@ * @author Baptiste Clenet * @author Daniel Krebs * @author Kévin Roussel + * @author Joakim Nohlgård * * @} */ @@ -47,16 +48,29 @@ static const uint8_t dbm_to_tx_pow_915[] = {0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x17, 0x04, 0x03, 0x02, 0x01, 0x00, 0x86, 0x40, 0x84, 0x83, 0x82, 0x80, 0xc1, 0xc0}; -int16_t tx_pow_to_dbm(at86rf2xx_freq_t freq, uint8_t reg) { - for(int i = 0; i < 37; i++){ - if(freq == AT86RF2XX_FREQ_868MHZ){ - if (dbm_to_tx_pow_868[i] == reg) { - return i -25; - } - } else if (freq == AT86RF2XX_FREQ_915MHZ){ - if (dbm_to_tx_pow_915[i] == reg) { - return i -25; - } +static int16_t _tx_pow_to_dbm_212b(uint8_t channel, uint8_t page, uint8_t reg) { + const uint8_t *dbm_to_tx_pow; + size_t nelem; + + if (page == 0 || page == 2) { + /* Channel 0 is 868.3 MHz */ + if (channel == 0) { + dbm_to_tx_pow = &dbm_to_tx_pow_868[0]; + nelem = sizeof(dbm_to_tx_pow_868) / sizeof(dbm_to_tx_pow_868[0]); + } + else { + /* Channels 1+ are 915 MHz */ + dbm_to_tx_pow = &dbm_to_tx_pow_915[0]; + nelem = sizeof(dbm_to_tx_pow_915) / sizeof(dbm_to_tx_pow_915[0]); + } + } + else { + return 0; + } + + for(size_t i = 0; i < nelem; i++){ + if (dbm_to_tx_pow[i] == reg) { + return i - 25; } } return 0; @@ -124,61 +138,37 @@ uint8_t at86rf2xx_get_chan(at86rf2xx_t *dev) void at86rf2xx_set_chan(at86rf2xx_t *dev, uint8_t channel) { - uint8_t tmp; - - if (channel < AT86RF2XX_MIN_CHANNEL - || channel > AT86RF2XX_MAX_CHANNEL) { + if ((channel < AT86RF2XX_MIN_CHANNEL) || + (channel > AT86RF2XX_MAX_CHANNEL) || + (dev->chan == channel)) { return; } + dev->chan = channel; - tmp = at86rf2xx_reg_read(dev, AT86RF2XX_REG__PHY_CC_CCA); - tmp &= ~(AT86RF2XX_PHY_CC_CCA_MASK__CHANNEL); - tmp |= (channel & AT86RF2XX_PHY_CC_CCA_MASK__CHANNEL); - at86rf2xx_reg_write(dev, AT86RF2XX_REG__PHY_CC_CCA, tmp); + + at86rf2xx_configure_phy(dev); } +uint8_t at86rf2xx_get_page(at86rf2xx_t *dev) +{ #ifdef MODULE_AT86RF212B -at86rf2xx_freq_t at86rf2xx_get_freq(at86rf2xx_t *dev) -{ - return dev->freq; -} - -void at86rf2xx_set_freq(at86rf2xx_t *dev, at86rf2xx_freq_t freq) -{ - uint8_t trx_ctrl2 = 0, rf_ctrl0 = 0; - trx_ctrl2 = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_CTRL_2); - trx_ctrl2 &= ~(AT86RF2XX_TRX_CTRL_2_MASK__FREQ_MODE); - rf_ctrl0 = at86rf2xx_reg_read(dev, AT86RF2XX_REG__RF_CTRL_0); - /* Erase previous conf for GC_TX_OFFS */ - rf_ctrl0 &= ~AT86RF2XX_RF_CTRL_0_MASK__GC_TX_OFFS; - - trx_ctrl2 |= AT86RF2XX_TRX_CTRL_2_MASK__SUB_MODE; - rf_ctrl0 |= AT86RF2XX_RF_CTRL_0_GC_TX_OFFS__2DB; - - switch(freq) { - case AT86RF2XX_FREQ_915MHZ: - if (dev->chan == 0) { - at86rf2xx_set_chan(dev,AT86RF2XX_DEFAULT_CHANNEL); - } else { - at86rf2xx_set_chan(dev,dev->chan); - } - break; - - case AT86RF2XX_FREQ_868MHZ: - /* Channel = 0 for 868MHz means 868.3MHz, only one available */ - at86rf2xx_set_chan(dev,0x00); - break; - - default: - DEBUG("at86rf2xx: Trying to set unknown frequency 0x%lx\n", - (unsigned long) freq); - return; - } - dev->freq = freq; - at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_CTRL_2, trx_ctrl2); - at86rf2xx_reg_write(dev, AT86RF2XX_REG__RF_CTRL_0, rf_ctrl0); -} + return dev->page; +#else + return 0; #endif +} + +void at86rf2xx_set_page(at86rf2xx_t *dev, uint8_t page) +{ +#ifdef MODULE_AT86RF212B + if ((page != 0) && (page != 2)) { + return; + } + dev->page = page; + + at86rf2xx_configure_phy(dev); +#endif +} uint16_t at86rf2xx_get_pan(at86rf2xx_t *dev) { @@ -198,7 +188,7 @@ int16_t at86rf2xx_get_txpower(at86rf2xx_t *dev) #ifdef MODULE_AT86RF212B uint8_t txpower = at86rf2xx_reg_read(dev, AT86RF2XX_REG__PHY_TX_PWR); DEBUG("txpower value: %x\n", txpower); - return tx_pow_to_dbm(dev->freq, txpower); + return _tx_pow_to_dbm_212b(dev->chan, dev->page, txpower); #else uint8_t txpower = at86rf2xx_reg_read(dev, AT86RF2XX_REG__PHY_TX_PWR) & AT86RF2XX_PHY_TX_PWR_MASK__TX_PWR; @@ -230,16 +220,13 @@ void at86rf2xx_set_txpower(at86rf2xx_t *dev, int16_t txpower) #endif } #ifdef MODULE_AT86RF212B - if (dev->freq == AT86RF2XX_FREQ_915MHZ) { - at86rf2xx_reg_write(dev, AT86RF2XX_REG__PHY_TX_PWR, - dbm_to_tx_pow_915[txpower]); - } - else if (dev->freq == AT86RF2XX_FREQ_868MHZ) { + if (dev->chan == 0) { at86rf2xx_reg_write(dev, AT86RF2XX_REG__PHY_TX_PWR, dbm_to_tx_pow_868[txpower]); } - else { - return; + else if (dev->chan < 11) { + at86rf2xx_reg_write(dev, AT86RF2XX_REG__PHY_TX_PWR, + dbm_to_tx_pow_915[txpower]); } #else at86rf2xx_reg_write(dev, AT86RF2XX_REG__PHY_TX_PWR, @@ -433,12 +420,6 @@ static inline void _set_state(at86rf2xx_t *dev, uint8_t state) dev->state = state; } -static inline void _force_trx_off(at86rf2xx_t *dev) -{ - at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_STATE, AT86RF2XX_TRX_STATE__FORCE_TRX_OFF); - while (at86rf2xx_get_status(dev) != AT86RF2XX_STATE_TRX_OFF); -} - void at86rf2xx_set_state(at86rf2xx_t *dev, uint8_t state) { uint8_t old_state = at86rf2xx_get_status(dev); @@ -469,7 +450,7 @@ void at86rf2xx_set_state(at86rf2xx_t *dev, uint8_t state) if (state == AT86RF2XX_STATE_SLEEP) { /* First go to TRX_OFF */ - _force_trx_off(dev); + at86rf2xx_force_trx_off(dev); /* Discard all IRQ flags, framebuffer is lost anyway */ at86rf2xx_reg_read(dev, AT86RF2XX_REG__IRQ_STATUS); /* Go to SLEEP mode from TRX_OFF */ @@ -491,5 +472,5 @@ void at86rf2xx_reset_state_machine(at86rf2xx_t *dev) old_state = at86rf2xx_get_status(dev); } while (old_state == AT86RF2XX_STATE_IN_PROGRESS); - _force_trx_off(dev); + at86rf2xx_force_trx_off(dev); } diff --git a/drivers/at86rf2xx/at86rf2xx_internal.c b/drivers/at86rf2xx/at86rf2xx_internal.c index 4a9ef5c0bc..fe0e4b2dd9 100644 --- a/drivers/at86rf2xx/at86rf2xx_internal.c +++ b/drivers/at86rf2xx/at86rf2xx_internal.c @@ -135,3 +135,78 @@ void at86rf2xx_hardware_reset(at86rf2xx_t *dev) gpio_set(dev->reset_pin); xtimer_usleep(AT86RF2XX_RESET_DELAY); } + +void at86rf2xx_configure_phy(at86rf2xx_t *dev) +{ + /* make sure device is not sleeping */ + at86rf2xx_assert_awake(dev); + + uint8_t state; + + /* make sure ongoing transmissions are finished */ + do { + state = at86rf2xx_get_status(dev); + } + while ((state == AT86RF2XX_STATE_BUSY_TX_ARET) || (state == AT86RF2XX_STATE_BUSY_RX_AACK)); + + /* we must be in TRX_OFF before changing the PHY configuration */ + at86rf2xx_force_trx_off(dev); + + uint8_t phy_cc_cca = at86rf2xx_reg_read(dev, AT86RF2XX_REG__PHY_CC_CCA); + +#ifdef MODULE_AT86RF212B + /* The TX power register must be updated after changing the channel if + * moving between bands. */ + int16_t txpower = at86rf2xx_get_txpower(dev); + + uint8_t trx_ctrl2 = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_CTRL_2); + uint8_t rf_ctrl0 = at86rf2xx_reg_read(dev, AT86RF2XX_REG__RF_CTRL_0); + + /* Clear previous configuration for PHY mode */ + trx_ctrl2 &= ~(AT86RF2XX_TRX_CTRL_2_MASK__FREQ_MODE); + /* Clear previous configuration for GC_TX_OFFS */ + rf_ctrl0 &= ~AT86RF2XX_RF_CTRL_0_MASK__GC_TX_OFFS; + /* Clear previous configuration for channel number */ + phy_cc_cca &= ~(AT86RF2XX_PHY_CC_CCA_MASK__CHANNEL); + + if (dev->chan != 0) { + /* Set sub mode bit on 915 MHz as recommended by the data sheet */ + trx_ctrl2 |= AT86RF2XX_TRX_CTRL_2_MASK__SUB_MODE; + } + + if (dev->page == 0) { + /* BPSK coding */ + /* Data sheet recommends using a +2 dB setting for BPSK */ + rf_ctrl0 |= AT86RF2XX_RF_CTRL_0_GC_TX_OFFS__2DB; + } + else if (dev->page == 2) { + /* O-QPSK coding */ + trx_ctrl2 |= AT86RF2XX_TRX_CTRL_2_MASK__BPSK_OQPSK; + /* Data sheet recommends using a +1 dB setting for O-QPSK */ + rf_ctrl0 |= AT86RF2XX_RF_CTRL_0_GC_TX_OFFS__1DB; + } + + at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_CTRL_2, trx_ctrl2); + at86rf2xx_reg_write(dev, AT86RF2XX_REG__RF_CTRL_0, rf_ctrl0); +#endif + + /* Update the channel register */ + phy_cc_cca |= (dev->chan & AT86RF2XX_PHY_CC_CCA_MASK__CHANNEL); + at86rf2xx_reg_write(dev, AT86RF2XX_REG__PHY_CC_CCA, phy_cc_cca); + +#ifdef MODULE_AT86RF212B + /* Update the TX power register to achieve the same power (in dBm) */ + at86rf2xx_set_txpower(dev, txpower); +#endif + + /* Return to the state we had before reconfiguring */ + at86rf2xx_set_state(dev, state); +} + +void at86rf2xx_force_trx_off(const at86rf2xx_t *dev) +{ + at86rf2xx_reg_write(dev, + AT86RF2XX_REG__TRX_STATE, + AT86RF2XX_TRX_STATE__FORCE_TRX_OFF); + while (at86rf2xx_get_status(dev) != AT86RF2XX_STATE_TRX_OFF); +} diff --git a/drivers/at86rf2xx/at86rf2xx_netdev.c b/drivers/at86rf2xx/at86rf2xx_netdev.c index 80f091e6f6..a2fd5ed891 100644 --- a/drivers/at86rf2xx/at86rf2xx_netdev.c +++ b/drivers/at86rf2xx/at86rf2xx_netdev.c @@ -442,6 +442,14 @@ static int _get(gnrc_netdev_t *device, netopt_t opt, void *val, size_t max_len) ((uint8_t *)val)[0] = at86rf2xx_get_chan(dev); return sizeof(uint16_t); + case NETOPT_CHANNEL_PAGE: + if (max_len < sizeof(uint16_t)) { + return -EOVERFLOW; + } + ((uint8_t *)val)[1] = 0; + ((uint8_t *)val)[0] = at86rf2xx_get_page(dev); + return sizeof(uint16_t); + case NETOPT_MAX_PACKET_SIZE: if (max_len < sizeof(int16_t)) { return -EOVERFLOW; @@ -679,6 +687,29 @@ static int _set(gnrc_netdev_t *device, netopt_t opt, void *val, size_t len) } break; + case NETOPT_CHANNEL_PAGE: + if (len != sizeof(uint16_t)) { + res = -EINVAL; + } else { + uint8_t page = ((uint8_t *)val)[0]; +#ifdef MODULE_AT86RF212B + if ((page != 0) && (page != 2)) { + res = -ENOTSUP; + } else { + at86rf2xx_set_page(dev, page); + res = sizeof(uint16_t); + } +#else + /* rf23x only supports page 0, no need to configure anything in the driver. */ + if (page != 0) { + res = -ENOTSUP; + } else { + res = sizeof(uint16_t); + } +#endif + } + break; + case NETOPT_TX_POWER: if (len > sizeof(int16_t)) { res = -EOVERFLOW; diff --git a/drivers/at86rf2xx/include/at86rf2xx_internal.h b/drivers/at86rf2xx/include/at86rf2xx_internal.h index 616181bbab..21f5ba6783 100644 --- a/drivers/at86rf2xx/include/at86rf2xx_internal.h +++ b/drivers/at86rf2xx/include/at86rf2xx_internal.h @@ -107,6 +107,13 @@ void at86rf2xx_sram_write(const at86rf2xx_t *dev, void at86rf2xx_fb_read(const at86rf2xx_t *dev, uint8_t *data, const size_t len); +/** + * @brief Cancel ongoing transactions and switch to TRX_OFF state + * + * @param[in] dev device to manipulate + */ +void at86rf2xx_force_trx_off(const at86rf2xx_t *dev); + /** * @brief Convenience function for reading the status of the given device * @@ -131,6 +138,14 @@ void at86rf2xx_assert_awake(at86rf2xx_t *dev); void at86rf2xx_hardware_reset(at86rf2xx_t *dev); +/** + * @brief Set PHY parameters based on channel and page number + * + * @param[in] dev device to configure + */ +void at86rf2xx_configure_phy(at86rf2xx_t *dev); + + #ifdef __cplusplus } #endif diff --git a/drivers/include/at86rf2xx.h b/drivers/include/at86rf2xx.h index c13a3f4db8..adf2f4da85 100644 --- a/drivers/include/at86rf2xx.h +++ b/drivers/include/at86rf2xx.h @@ -23,6 +23,7 @@ * @author Kaspar Schleiser * @author Daniel Krebs * @author Kévin Roussel + * @author Joakim Nohlgård */ #ifndef AT86RF2XX_H_ @@ -124,16 +125,6 @@ extern "C" { * PAN ID */ /** @} */ -/** - * @brief Frequency configuration for sub-GHz devices. - * @{ - */ -typedef enum { - AT86RF2XX_FREQ_915MHZ, /**< frequency 915MHz enabled */ - AT86RF2XX_FREQ_868MHZ, /**< frequency 868MHz enabled */ -} at86rf2xx_freq_t; -/** @} */ - /** * @brief Device descriptor for AT86RF2XX radio devices */ @@ -153,9 +144,10 @@ typedef struct { uint8_t seq_nr; /**< sequence number to use next */ uint8_t frame_len; /**< length of the current TX frame */ uint16_t pan; /**< currently used PAN ID */ - uint8_t chan; /**< currently used channel */ + uint8_t chan; /**< currently used channel number */ #ifdef MODULE_AT86RF212B - at86rf2xx_freq_t freq; /**< currently used frequency */ + /* Only AT86RF212B supports multiple pages (PHY modes) */ + uint8_t page; /**< currently used channel page */ #endif uint8_t addr_short[2]; /**< the radio's short address */ uint8_t addr_long[8]; /**< the radio's long address */ @@ -245,40 +237,38 @@ uint64_t at86rf2xx_get_addr_long(at86rf2xx_t *dev); void at86rf2xx_set_addr_long(at86rf2xx_t *dev, uint64_t addr); /** - * @brief Get the configured channel of the given device + * @brief Get the configured channel number of the given device * * @param[in] dev device to read from * - * @return the currently set channel + * @return the currently set channel number */ uint8_t at86rf2xx_get_chan(at86rf2xx_t *dev); /** - * @brief Set the channel of the given device + * @brief Set the channel number of the given device * * @param[in] dev device to write to - * @param[in] chan channel to set + * @param[in] chan channel number to set */ void at86rf2xx_set_chan(at86rf2xx_t *dev, uint8_t chan); -#ifdef MODULE_AT86RF212B /** - * @brief Get the configured frequency of the given device + * @brief Get the configured channel page of the given device * * @param[in] dev device to read from * - * @return the currently set frequency + * @return the currently set channel page */ -at86rf2xx_freq_t at86rf2xx_get_freq(at86rf2xx_t *dev); +uint8_t at86rf2xx_get_page(at86rf2xx_t *dev); /** - * @brief Set the frequency of the given device + * @brief Set the channel page of the given device * * @param[in] dev device to write to - * @param[in] chan frequency to set + * @param[in] page channel page to set */ -void at86rf2xx_set_freq(at86rf2xx_t *dev, at86rf2xx_freq_t freq); -#endif +void at86rf2xx_set_page(at86rf2xx_t *dev, uint8_t page); /** * @brief Get the configured PAN ID of the given device @@ -358,7 +348,7 @@ uint8_t at86rf2xx_get_csma_max_retries(at86rf2xx_t *dev); * Valid values: 0 to 5, -1 means CSMA disabled * * @param[in] dev device to write to - * @param[in] max the maximum number of retries + * @param[in] retries the maximum number of retries */ void at86rf2xx_set_csma_max_retries(at86rf2xx_t *dev, int8_t retries);