From 502786b9f7608a238ce86f71412b8e6fe6ac5e0b Mon Sep 17 00:00:00 2001 From: daniel-k <github@daniel-krebs.net> Date: Wed, 16 Sep 2015 18:48:10 +0200 Subject: [PATCH] at86rf2xx: implement sleep mode --- drivers/at86rf2xx/at86rf2xx.c | 18 +- drivers/at86rf2xx/at86rf2xx_getset.c | 10 +- drivers/at86rf2xx/at86rf2xx_internal.c | 35 ++- drivers/at86rf2xx/at86rf2xx_netdev.c | 253 ++++++++++++------ .../at86rf2xx/include/at86rf2xx_internal.h | 32 +++ 5 files changed, 244 insertions(+), 104 deletions(-) diff --git a/drivers/at86rf2xx/at86rf2xx.c b/drivers/at86rf2xx/at86rf2xx.c index 7a1d91e5e3..75da3e77fc 100644 --- a/drivers/at86rf2xx/at86rf2xx.c +++ b/drivers/at86rf2xx/at86rf2xx.c @@ -23,7 +23,6 @@ * @} */ -#include "xtimer.h" #include "periph/cpuid.h" #include "byteorder.h" #include "net/ieee802154.h" @@ -36,9 +35,6 @@ #include "debug.h" -#define RESET_DELAY (1U) /* must be > 625ns */ - - static void _irq_handler(void *arg) { msg_t msg; @@ -62,6 +58,7 @@ int at86rf2xx_init(at86rf2xx_t *dev, spi_t spi, spi_speed_t spi_speed, dev->sleep_pin = sleep_pin; dev->reset_pin = reset_pin; dev->idle_state = AT86RF2XX_STATE_TRX_OFF; + dev->state = AT86RF2XX_STATE_SLEEP; /* initialise SPI */ spi_init_master(dev->spi, SPI_CONF_FIRST_RISING, spi_speed); @@ -74,6 +71,9 @@ int at86rf2xx_init(at86rf2xx_t *dev, spi_t spi, spi_speed_t spi_speed, gpio_set(dev->reset_pin); gpio_init_int(dev->int_pin, GPIO_NOPULL, GPIO_RISING, _irq_handler, dev); + /* make sure device is not sleeping, so we can query part number */ + at86rf2xx_assert_awake(dev); + /* test if the SPI is set up correctly and the device is responding */ if (at86rf2xx_reg_read(dev, AT86RF2XX_REG__PART_NUM) != AT86RF2XX_PARTNUM) { @@ -83,6 +83,7 @@ int at86rf2xx_init(at86rf2xx_t *dev, spi_t spi, spi_speed_t spi_speed, /* reset device to default values and put it into RX state */ at86rf2xx_reset(dev); + return 0; } @@ -93,12 +94,7 @@ void at86rf2xx_reset(at86rf2xx_t *dev) eui64_t addr_long; #endif - /* wake from sleep in case radio is sleeping */ - gpio_clear(dev->sleep_pin); - /* trigger hardware reset */ - gpio_clear(dev->reset_pin); - xtimer_usleep(RESET_DELAY); - gpio_set(dev->reset_pin); + at86rf2xx_hardware_reset(dev); /* Reset state machine to ensure a known state */ at86rf2xx_reset_state_machine(dev); @@ -184,6 +180,8 @@ bool at86rf2xx_cca(at86rf2xx_t *dev) uint8_t tmp; uint8_t status; + at86rf2xx_assert_awake(dev); + /* trigger CCA measurment */ tmp = at86rf2xx_reg_read(dev, AT86RF2XX_REG__PHY_CC_CCA); tmp &= AT86RF2XX_PHY_CC_CCA_MASK__CCA_REQUEST; diff --git a/drivers/at86rf2xx/at86rf2xx_getset.c b/drivers/at86rf2xx/at86rf2xx_getset.c index 26e5d3d2eb..fe0f31af7e 100644 --- a/drivers/at86rf2xx/at86rf2xx_getset.c +++ b/drivers/at86rf2xx/at86rf2xx_getset.c @@ -399,6 +399,7 @@ static inline void _set_state(at86rf2xx_t *dev, uint8_t state) { at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_STATE, state); while (at86rf2xx_get_status(dev) != state); + dev->state = state; } void at86rf2xx_set_state(at86rf2xx_t *dev, uint8_t state) @@ -426,15 +427,17 @@ void at86rf2xx_set_state(at86rf2xx_t *dev, uint8_t state) /* check if we need to wake up from sleep mode */ else if (old_state == AT86RF2XX_STATE_SLEEP) { DEBUG("at86rf2xx: waking up from sleep mode\n"); - gpio_clear(dev->sleep_pin); - while (at86rf2xx_get_status(dev) != AT86RF2XX_STATE_TRX_OFF); + at86rf2xx_assert_awake(dev); } if (state == AT86RF2XX_STATE_SLEEP) { /* First go to TRX_OFF */ 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 */ gpio_set(dev->sleep_pin); + dev->state = state; } else { _set_state(dev, state); } @@ -444,8 +447,7 @@ void at86rf2xx_reset_state_machine(at86rf2xx_t *dev) { uint8_t old_state; - /* Wake up */ - gpio_clear(dev->sleep_pin); + at86rf2xx_assert_awake(dev); /* Wait for any state transitions to complete before forcing TRX_OFF */ do { diff --git a/drivers/at86rf2xx/at86rf2xx_internal.c b/drivers/at86rf2xx/at86rf2xx_internal.c index 44a9eb05b0..fc9ea8d403 100644 --- a/drivers/at86rf2xx/at86rf2xx_internal.c +++ b/drivers/at86rf2xx/at86rf2xx_internal.c @@ -24,6 +24,7 @@ #include "periph/spi.h" #include "periph/gpio.h" +#include "xtimer.h" #include "at86rf2xx_internal.h" #include "at86rf2xx_registers.h" @@ -101,8 +102,38 @@ void at86rf2xx_fb_read(const at86rf2xx_t *dev, uint8_t at86rf2xx_get_status(const at86rf2xx_t *dev) { - return (at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_STATUS) - & AT86RF2XX_TRX_STATUS_MASK__TRX_STATUS); + /* if sleeping immediately return state */ + if(dev->state == AT86RF2XX_STATE_SLEEP) + return dev->state; + + return at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_STATUS) + & AT86RF2XX_TRX_STATUS_MASK__TRX_STATUS; +} + +void at86rf2xx_assert_awake(at86rf2xx_t *dev) +{ + if(at86rf2xx_get_status(dev) == AT86RF2XX_STATE_SLEEP) { + + /* wake up and wait for transition to TRX_OFF */ + gpio_clear(dev->sleep_pin); + xtimer_usleep(AT86RF2XX_WAKEUP_DELAY); + + /* update state */ + dev->state = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_STATUS) + & AT86RF2XX_TRX_STATUS_MASK__TRX_STATUS; + } +} + +void at86rf2xx_hardware_reset(at86rf2xx_t *dev) +{ + /* wake up from sleep in case radio is sleeping */ + at86rf2xx_assert_awake(dev); + + /* trigger hardware reset */ + gpio_clear(dev->reset_pin); + xtimer_usleep(AT86RF2XX_RESET_PULSE_WIDTH); + gpio_set(dev->reset_pin); + xtimer_usleep(AT86RF2XX_RESET_DELAY); } void at86rf2xx_force_trx_off(const at86rf2xx_t *dev) diff --git a/drivers/at86rf2xx/at86rf2xx_netdev.c b/drivers/at86rf2xx/at86rf2xx_netdev.c index 1f8f9dd1b4..e57b09ab88 100644 --- a/drivers/at86rf2xx/at86rf2xx_netdev.c +++ b/drivers/at86rf2xx/at86rf2xx_netdev.c @@ -322,7 +322,7 @@ static int _set_state(at86rf2xx_t *dev, netopt_state_t state) { switch (state) { case NETOPT_STATE_SLEEP: - at86rf2xx_set_state(dev, AT86RF2XX_STATE_TRX_OFF); + at86rf2xx_set_state(dev, AT86RF2XX_STATE_SLEEP); break; case NETOPT_STATE_IDLE: at86rf2xx_set_state(dev, AT86RF2XX_STATE_RX_AACK_ON); @@ -359,11 +359,13 @@ netopt_state_t _get_state(at86rf2xx_t *dev) static int _get(gnrc_netdev_t *device, netopt_t opt, void *val, size_t max_len) { + at86rf2xx_t *dev = (at86rf2xx_t *) device; + if (device == NULL) { return -ENODEV; } - at86rf2xx_t *dev = (at86rf2xx_t *) device; + /* getting these options doesn't require the transceiver to be responsive */ switch (opt) { case NETOPT_ADDRESS: @@ -435,13 +437,6 @@ 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_TX_POWER: - if (max_len < sizeof(int16_t)) { - return -EOVERFLOW; - } - *((uint16_t *)val) = at86rf2xx_get_txpower(dev); - return sizeof(uint16_t); - case NETOPT_MAX_PACKET_SIZE: if (max_len < sizeof(int16_t)) { return -EOVERFLOW; @@ -454,7 +449,7 @@ static int _get(gnrc_netdev_t *device, netopt_t opt, void *val, size_t max_len) return -EOVERFLOW; } *((netopt_state_t*)val) = _get_state(dev); - break; + return sizeof(netopt_state_t); case NETOPT_PRELOADING: if (dev->options & AT86RF2XX_OPT_PRELOADING) { @@ -474,13 +469,6 @@ static int _get(gnrc_netdev_t *device, netopt_t opt, void *val, size_t max_len) } return sizeof(netopt_enable_t); - case NETOPT_RETRANS: - if (max_len < sizeof(uint8_t)) { - return -EOVERFLOW; - } - *((uint8_t *)val) = at86rf2xx_get_max_retries(dev); - return sizeof(uint8_t); - case NETOPT_PROMISCUOUSMODE: if (dev->options & AT86RF2XX_OPT_PROMISCUOUS) { *((netopt_enable_t *)val) = NETOPT_ENABLE; @@ -499,15 +487,6 @@ static int _get(gnrc_netdev_t *device, netopt_t opt, void *val, size_t max_len) } return sizeof(netopt_enable_t); - case NETOPT_IS_CHANNEL_CLR: - if (at86rf2xx_cca(dev)) { - *((netopt_enable_t *)val) = NETOPT_ENABLE; - } - else { - *((netopt_enable_t *)val) = NETOPT_DISABLE; - } - return sizeof(netopt_enable_t); - case NETOPT_RX_START_IRQ: *((netopt_enable_t *)val) = !!(dev->options & AT86RF2XX_OPT_TELL_RX_START); @@ -533,161 +512,253 @@ static int _get(gnrc_netdev_t *device, netopt_t opt, void *val, size_t max_len) !!(dev->options & AT86RF2XX_OPT_CSMA); return sizeof(netopt_enable_t); + default: + /* Can still be handled in second switch */ + break; + } + + + uint8_t old_state = at86rf2xx_get_status(dev); + int res = 0; + + /* temporarily wake up if sleeping */ + if(old_state == AT86RF2XX_STATE_SLEEP) { + at86rf2xx_assert_awake(dev); + } + + /* these options require the transceiver to be not sleeping*/ + switch (opt) { + case NETOPT_TX_POWER: + if (max_len < sizeof(int16_t)) { + res = -EOVERFLOW; + } else { + *((uint16_t *)val) = at86rf2xx_get_txpower(dev); + res = sizeof(uint16_t); + } + break; + + case NETOPT_RETRANS: + if (max_len < sizeof(uint8_t)) { + res = -EOVERFLOW; + } else { + *((uint8_t *)val) = at86rf2xx_get_max_retries(dev); + res = sizeof(uint8_t); + } + break; + + case NETOPT_IS_CHANNEL_CLR: + if (at86rf2xx_cca(dev)) { + *((netopt_enable_t *)val) = NETOPT_ENABLE; + } + else { + *((netopt_enable_t *)val) = NETOPT_DISABLE; + } + res = sizeof(netopt_enable_t); + break; + case NETOPT_CSMA_RETRIES: if (max_len < sizeof(uint8_t)) { - return -EOVERFLOW; + res = -EOVERFLOW; + } else { + *((uint8_t *)val) = at86rf2xx_get_csma_max_retries(dev); + res = sizeof(uint8_t); } - *((uint8_t *)val) = at86rf2xx_get_csma_max_retries(dev); - return sizeof(uint8_t); + break; default: - return -ENOTSUP; + res = -ENOTSUP; } - return 0; + /* go back to sleep if were sleeping */ + if(old_state == AT86RF2XX_STATE_SLEEP) { + at86rf2xx_set_state(dev, AT86RF2XX_STATE_SLEEP); + } + + return res; } static int _set(gnrc_netdev_t *device, netopt_t opt, void *val, size_t len) { at86rf2xx_t *dev = (at86rf2xx_t *) device; + uint8_t old_state = at86rf2xx_get_status(dev); + int res = 0; if (dev == NULL) { return -ENODEV; } + /* temporarily wake up if sleeping */ + if(old_state == AT86RF2XX_STATE_SLEEP) { + at86rf2xx_assert_awake(dev); + } + switch (opt) { case NETOPT_ADDRESS: if (len > sizeof(uint16_t)) { - return -EOVERFLOW; + res = -EOVERFLOW; + } else { + at86rf2xx_set_addr_short(dev, *((uint16_t*)val)); + res = sizeof(uint16_t); } - at86rf2xx_set_addr_short(dev, *((uint16_t*)val)); - return sizeof(uint16_t); + break; case NETOPT_ADDRESS_LONG: if (len > sizeof(uint64_t)) { - return -EOVERFLOW; + res = -EOVERFLOW; + } else { + at86rf2xx_set_addr_long(dev, *((uint64_t*)val)); + res = sizeof(uint64_t); } - at86rf2xx_set_addr_long(dev, *((uint64_t*)val)); - return sizeof(uint64_t); + break; case NETOPT_SRC_LEN: if (len > sizeof(uint16_t)) { - return -EOVERFLOW; - } - if (*((uint16_t *)val) == 2) { - at86rf2xx_set_option(dev, AT86RF2XX_OPT_SRC_ADDR_LONG, - false); - } - else if (*((uint16_t *)val) == 8) { - at86rf2xx_set_option(dev, AT86RF2XX_OPT_SRC_ADDR_LONG, - true); - } - else { - return -ENOTSUP; + res = -EOVERFLOW; + } else { + if (*((uint16_t *)val) == 2) { + at86rf2xx_set_option(dev, AT86RF2XX_OPT_SRC_ADDR_LONG, + false); + } + else if (*((uint16_t *)val) == 8) { + at86rf2xx_set_option(dev, AT86RF2XX_OPT_SRC_ADDR_LONG, + true); + } + else { + res = -ENOTSUP; + break; + } + res = sizeof(uint16_t); } - return sizeof(uint16_t); + break; case NETOPT_NID: if (len > sizeof(uint16_t)) { - return -EOVERFLOW; + res = -EOVERFLOW; + } else { + at86rf2xx_set_pan(dev, *((uint16_t *)val)); + res = sizeof(uint16_t); } - at86rf2xx_set_pan(dev, *((uint16_t *)val)); - return sizeof(uint16_t); + break; case NETOPT_CHANNEL: if (len != sizeof(uint16_t)) { - return -EINVAL; - } - uint8_t chan = ((uint8_t *)val)[0]; - if (chan < AT86RF2XX_MIN_CHANNEL || - chan > AT86RF2XX_MAX_CHANNEL) { - return -ENOTSUP; + res = -EINVAL; + } else { + uint8_t chan = ((uint8_t *)val)[0]; + if (chan < AT86RF2XX_MIN_CHANNEL || + chan > AT86RF2XX_MAX_CHANNEL) { + res = -ENOTSUP; + break; + } + at86rf2xx_set_chan(dev, chan); + res = sizeof(uint16_t); } - at86rf2xx_set_chan(dev, chan); - return sizeof(uint16_t); + break; case NETOPT_TX_POWER: if (len > sizeof(int16_t)) { - return -EOVERFLOW; + res = -EOVERFLOW; + } else { + at86rf2xx_set_txpower(dev, *((int16_t *)val)); + res = sizeof(uint16_t); } - at86rf2xx_set_txpower(dev, *((int16_t *)val)); - return sizeof(uint16_t); + break; case NETOPT_STATE: if (len > sizeof(netopt_state_t)) { - return -EOVERFLOW; + res = -EOVERFLOW; + } else { + res = _set_state(dev, *((netopt_state_t *)val)); } - return _set_state(dev, *((netopt_state_t *)val)); + break; case NETOPT_AUTOACK: at86rf2xx_set_option(dev, AT86RF2XX_OPT_AUTOACK, ((bool *)val)[0]); - return sizeof(netopt_enable_t); + res = sizeof(netopt_enable_t); + break; case NETOPT_RETRANS: if (len > sizeof(uint8_t)) { - return -EOVERFLOW; + res = -EOVERFLOW; + } else { + at86rf2xx_set_max_retries(dev, *((uint8_t *)val)); + res = sizeof(uint8_t); } - at86rf2xx_set_max_retries(dev, *((uint8_t *)val)); - return sizeof(uint8_t); + break; case NETOPT_PRELOADING: at86rf2xx_set_option(dev, AT86RF2XX_OPT_PRELOADING, ((bool *)val)[0]); - return sizeof(netopt_enable_t); + res = sizeof(netopt_enable_t); + break; case NETOPT_PROMISCUOUSMODE: at86rf2xx_set_option(dev, AT86RF2XX_OPT_PROMISCUOUS, ((bool *)val)[0]); - return sizeof(netopt_enable_t); + res = sizeof(netopt_enable_t); + break; case NETOPT_RAWMODE: at86rf2xx_set_option(dev, AT86RF2XX_OPT_RAWDUMP, ((bool *)val)[0]); - return sizeof(netopt_enable_t); + res = sizeof(netopt_enable_t); + break; case NETOPT_RX_START_IRQ: at86rf2xx_set_option(dev, AT86RF2XX_OPT_TELL_RX_START, ((bool *)val)[0]); - return sizeof(netopt_enable_t); + res = sizeof(netopt_enable_t); + break; case NETOPT_RX_END_IRQ: at86rf2xx_set_option(dev, AT86RF2XX_OPT_TELL_RX_END, ((bool *)val)[0]); - return sizeof(netopt_enable_t); + res = sizeof(netopt_enable_t); + break; case NETOPT_TX_START_IRQ: at86rf2xx_set_option(dev, AT86RF2XX_OPT_TELL_TX_START, ((bool *)val)[0]); - return sizeof(netopt_enable_t); + res = sizeof(netopt_enable_t); + break; case NETOPT_TX_END_IRQ: at86rf2xx_set_option(dev, AT86RF2XX_OPT_TELL_TX_END, ((bool *)val)[0]); - return sizeof(netopt_enable_t); + res = sizeof(netopt_enable_t); + break; case NETOPT_CSMA: at86rf2xx_set_option(dev, AT86RF2XX_OPT_CSMA, ((bool *)val)[0]); - return sizeof(netopt_enable_t); + res = sizeof(netopt_enable_t); + break; case NETOPT_CSMA_RETRIES: if( (len > sizeof(uint8_t)) || (*((uint8_t *)val) > 5) ) { - return -EOVERFLOW; - } - /* If CSMA is disabled, don't allow setting retries */ - if( !(dev->options & AT86RF2XX_OPT_CSMA) ) { - return -ENOTSUP; + res = -EOVERFLOW; + } else if( !(dev->options & AT86RF2XX_OPT_CSMA) ) { + /* If CSMA is disabled, don't allow setting retries */ + res = -ENOTSUP; + } else { + at86rf2xx_set_csma_max_retries(dev, *((uint8_t *)val)); + res = sizeof(uint8_t); } - at86rf2xx_set_csma_max_retries(dev, *((uint8_t *)val)); - return sizeof(uint8_t); + break; default: - return -ENOTSUP; + res = -ENOTSUP; } - return 0; + /* go back to sleep if were sleeping and state hasn't been changed */ + if( (old_state == AT86RF2XX_STATE_SLEEP) && + (opt != NETOPT_STATE) ) { + at86rf2xx_set_state(dev, AT86RF2XX_STATE_SLEEP); + } + + return res; } static int _add_event_cb(gnrc_netdev_t *dev, gnrc_netdev_event_cb_t cb) @@ -723,10 +794,16 @@ static void _isr_event(gnrc_netdev_t *device, uint32_t event_type) uint8_t state; uint8_t trac_status; + /* If transceiver is sleeping register access is impossible and frames are + * lost anyway, so return immediately. + */ + state = at86rf2xx_get_status(dev); + if(state == AT86RF2XX_STATE_SLEEP) + return; + /* read (consume) device status */ irq_mask = at86rf2xx_reg_read(dev, AT86RF2XX_REG__IRQ_STATUS); - state = at86rf2xx_get_status(dev); trac_status = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_STATE) & AT86RF2XX_TRX_STATE_MASK__TRAC; diff --git a/drivers/at86rf2xx/include/at86rf2xx_internal.h b/drivers/at86rf2xx/include/at86rf2xx_internal.h index 90381cbe36..863f1fedf3 100644 --- a/drivers/at86rf2xx/include/at86rf2xx_internal.h +++ b/drivers/at86rf2xx/include/at86rf2xx_internal.h @@ -31,6 +31,23 @@ extern "C" { #endif +/** + * @brief Transition time from SLEEP to TRX_OFF in us, refer figure 7-4, p.42. + * For different environments refer figure 13-13, p.201 + */ +#define AT86RF2XX_WAKEUP_DELAY (300U) + +/** + * @brief Minimum reset pulse width, refer p.190 + */ +#define AT86RF2XX_RESET_PULSE_WIDTH (1U) + +/** + * @brief Transition time to TRX_OFF after reset pulse in us, refer + * figure 7-8, p. 44. + */ +#define AT86RF2XX_RESET_DELAY (26U) + /** * @brief Read from a register at address `addr` from device `dev`. * @@ -106,6 +123,21 @@ void at86rf2xx_force_trx_off(const at86rf2xx_t *dev); */ uint8_t at86rf2xx_get_status(const at86rf2xx_t *dev); +/** + * @brief Make sure that device is not sleeping + * + * @param[in] dev device to eventually wake up + */ +void at86rf2xx_assert_awake(at86rf2xx_t *dev); + +/** + * @brief Trigger a hardware reset + * + * @param[in] dev device to reset + */ +void at86rf2xx_hardware_reset(at86rf2xx_t *dev); + + #ifdef __cplusplus } #endif -- GitLab