diff --git a/drivers/cc110x/cc110x.c b/drivers/cc110x/cc110x.c index 8c96111877..a175845831 100644 --- a/drivers/cc110x/cc110x.c +++ b/drivers/cc110x/cc110x.c @@ -89,6 +89,13 @@ int cc110x_apply_config(cc110x_t *dev, const cc110x_config_t *conf, return cc110x_full_calibration(dev); } +static void _set_tx_power(cc110x_t *dev, cc110x_tx_power_t power) +{ + uint8_t frend0 = 0x10 | (uint8_t)power; + cc110x_write(dev, CC110X_REG_FREND0, frend0); + dev->tx_power = power; +} + int cc110x_set_tx_power(cc110x_t *dev, cc110x_tx_power_t power) { DEBUG("[cc110x] Applying TX power setting at index %u\n", (unsigned)power); @@ -114,9 +121,8 @@ int cc110x_set_tx_power(cc110x_t *dev, cc110x_tx_power_t power) return -EAGAIN; } - uint8_t frend0 = 0x10 | (uint8_t)power; - cc110x_write(dev, CC110X_REG_FREND0, frend0); - dev->tx_power = power; + _set_tx_power(dev, power); + cc110x_release(dev); return 0; } @@ -176,3 +182,47 @@ int cc110x_set_channel(cc110x_t *dev, uint8_t channel) dev->netdev.event_callback(&dev->netdev, NETDEV_EVENT_FHSS_CHANGE_CHANNEL); return 0; } + +int cc110x_wakeup(cc110x_t *dev) +{ + int err = cc110x_power_on_and_acquire(dev); + + if (err) { + return err; + } + + /* PA_TABLE is lost on SLEEP, see 10.6 in the CC1101 data sheet */ + cc110x_burst_write(dev, CC110X_MULTIREG_PATABLE, + dev->params.patable->data, CC110X_PATABLE_LEN); + _set_tx_power(dev, dev->tx_power); + + cc110x_enter_rx_mode(dev); + cc110x_release(dev); + return 0; +} + +void cc110x_sleep(cc110x_t *dev) +{ + cc110x_acquire(dev); + if (dev->state == CC110X_STATE_OFF) { + cc110x_release(dev); + return; + } + + /* + * Datasheet page 9 table 4. + * + * To achieve the lowest power consumption GDO's must + * be programmed to 0x2F + */ + cc110x_write(dev, CC110X_REG_IOCFG2, CC110X_GDO_CONSTANT_LOW); + cc110x_write(dev, CC110X_REG_IOCFG1, CC110X_GDO_CONSTANT_LOW); + cc110x_write(dev, CC110X_REG_IOCFG0, CC110X_GDO_CONSTANT_LOW); + + /* transition to SLEEP only from state IDLE possible */ + cc110x_cmd(dev, CC110X_STROBE_IDLE); + /* go to SLEEP */ + cc110x_cmd(dev, CC110X_STROBE_OFF); + dev->state = CC110X_STATE_OFF; + cc110x_release(dev); +} diff --git a/drivers/cc110x/cc110x_communication.c b/drivers/cc110x/cc110x_communication.c index 7afac9c9e9..86ae38d1d6 100644 --- a/drivers/cc110x/cc110x_communication.c +++ b/drivers/cc110x/cc110x_communication.c @@ -18,13 +18,16 @@ */ #include + +#include "cc110x.h" +#include "cc110x_communication.h" +#include "cc110x_constants.h" +#include "cc110x_internal.h" #include "periph/gpio.h" #include "periph/spi.h" #include "xtimer.h" -#include "cc110x.h" -#include "cc110x_constants.h" -int cc110x_power_on(cc110x_t *dev) +int cc110x_power_on_and_acquire(cc110x_t *dev) { gpio_t cs = dev->params.cs; @@ -32,9 +35,19 @@ int cc110x_power_on(cc110x_t *dev) return -EIO; } gpio_clear(cs); - xtimer_usleep(150); + xtimer_usleep(CC110X_WAKEUP_TIME_US); gpio_set(cs); spi_init_cs(dev->params.spi, dev->params.cs); + + if (cc110x_acquire(dev) != SPI_OK) { + return -EIO; + } + + while (cc110x_state_from_status(cc110x_status(dev)) != CC110X_STATE_IDLE) { + cc110x_cmd(dev, CC110X_STROBE_IDLE); + xtimer_usleep(CC110X_WAKEUP_TIME_US); + } + return 0; } diff --git a/drivers/cc110x/cc110x_netdev.c b/drivers/cc110x/cc110x_netdev.c index e56753358e..b66cd763ef 100644 --- a/drivers/cc110x/cc110x_netdev.c +++ b/drivers/cc110x/cc110x_netdev.c @@ -248,37 +248,25 @@ static int cc110x_init(netdev_t *netdev) /* Make sure the crystal is stable and the chip ready. This is needed as * the reset is done via an SPI command, but the SPI interface must not be * used unless the chip is ready according to the data sheet. After the - * reset, a second call to cc110x_power_on() is needed to finally have + * reset, a second call to cc110x_power_on_and_acquire() is needed to finally have * the transceiver in a known state and ready for SPI communication. */ - if (cc110x_power_on(dev)) { + if (cc110x_power_on_and_acquire(dev)) { DEBUG("[cc110x] netdev_driver_t::init(): Failed to pull CS pin low\n"); return -EIO; } - if (cc110x_acquire(dev) != SPI_OK) { - DEBUG("[cc110x] netdev_driver_t::init(): Failed to setup/acquire SPI " - "interface\n"); - return -EIO; - } - /* Performing a reset of the transceiver to get it in a known state */ cc110x_cmd(dev, CC110X_STROBE_RESET); cc110x_release(dev); /* Again, make sure the crystal is stable and the chip ready */ - if (cc110x_power_on(dev)) { + if (cc110x_power_on_and_acquire(dev)) { DEBUG("[cc110x] netdev_driver_t::init(): Failed to pull CS pin low " "after reset\n"); return -EIO; } - if (cc110x_acquire(dev) != SPI_OK) { - DEBUG("[cc110x] netdev_driver_t::init(): Failed to setup/acquire SPI " - "interface after reset\n"); - return -EIO; - } - if (identify_device(dev)) { DEBUG("[cc110x] netdev_driver_t::init(): Device identification failed\n"); cc110x_release(dev); @@ -417,20 +405,23 @@ static int cc110x_send(netdev_t *netdev, const iolist_t *iolist) } switch (dev->state) { - case CC110X_STATE_FSTXON: - /* falls through */ - case CC110X_STATE_RX_MODE: - break; - case CC110X_STATE_RECEIVING: - cc110x_release(dev); - DEBUG("[cc110x] netdev_driver_t::send(): Refusing to send while " - "receiving a frame\n"); - return -EBUSY; - default: - cc110x_release(dev); - DEBUG("[cc110x] netdev_driver_t::send(): Driver state %i prevents " - "sending\n", (int)dev->state); - return -1; + case CC110X_STATE_FSTXON: + /* falls through */ + case CC110X_STATE_RX_MODE: + break; + case CC110X_STATE_RECEIVING: + cc110x_release(dev); + DEBUG("[cc110x] netdev_driver_t::send(): Refusing to send while " + "receiving a frame\n"); + return -EBUSY; + case CC110X_STATE_OFF: + cc110x_release(dev); + return -ENOTSUP; + default: + cc110x_release(dev); + DEBUG("[cc110x] netdev_driver_t::send(): Driver state %i prevents " + "sending\n", (int)dev->state); + return -1; } /* Copy data to send into frame buffer */ diff --git a/drivers/cc110x/include/cc110x_communication.h b/drivers/cc110x/include/cc110x_communication.h index 02e254bd7b..5d159c2064 100644 --- a/drivers/cc110x/include/cc110x_communication.h +++ b/drivers/cc110x/include/cc110x_communication.h @@ -35,8 +35,9 @@ extern "C" { * @retval SPI_NOMODE SPI mode 0 not supported by MCU * @retval SPI_NOCLK SPI clock given in @ref cc110x_params_t is not supported * - * @pre @ref cc110x_power_on has be called before calling this function. - * (Only needed *once* when the driver initializes.) + * @pre When first acquiring the device either after boot or after having put + * the device to sleep mode, use @ref cc110x_power_on_and_acquire + * instead. Subsequently, this function should be used (it is faster). */ static inline int cc110x_acquire(cc110x_t *dev) { @@ -200,10 +201,13 @@ uint8_t cc110x_status(cc110x_t *dev); * of messing with the SPI interface, this driver simply waits for this upper * bound, as suggested in the note below Table 22 on page 30 in the data sheet. * + * @pre The device was not acquired and in low power mode + * @post The device is in IDLE mode and acquired + * * @retval 0 Success * @retval -EIO Couldn't pull the CS pin down (@ref cc110x_params_t::cs) */ -int cc110x_power_on(cc110x_t *dev); +int cc110x_power_on_and_acquire(cc110x_t *dev); #ifdef __cplusplus diff --git a/drivers/cc110x/include/cc110x_constants.h b/drivers/cc110x/include/cc110x_constants.h index 45e5280183..ce4a49062c 100644 --- a/drivers/cc110x/include/cc110x_constants.h +++ b/drivers/cc110x/include/cc110x_constants.h @@ -570,6 +570,11 @@ extern "C" { #define CC110X_PKTCTRL1_GET_ADDR_MODE 0x03 /** @} */ +/** + * @brief Time in micro seconds the CC110X takes to wake up from SLEEP state + */ +#define CC110X_WAKEUP_TIME_US 150 + #ifdef __cplusplus } #endif diff --git a/drivers/include/cc110x.h b/drivers/include/cc110x.h index 845b279c0a..64f602e413 100644 --- a/drivers/include/cc110x.h +++ b/drivers/include/cc110x.h @@ -269,7 +269,7 @@ typedef enum { */ CC110X_STATE_FRAME_READY = 0x08, /** - * @brief Frame received, waiting for upper layer to retrieve it + * @brief Devices is powered down * * Transceiver is in SLEEP state. There is no matching representation in the * status byte, as reading the status byte will power up the transceiver in @@ -644,6 +644,21 @@ int cc110x_set_channel(cc110x_t *dev, uint8_t channel); */ int cc110x_set_tx_power(cc110x_t *dev, cc110x_tx_power_t power); +/** + * @brief Wakes the transceiver from SLEEP mode and enters RX mode + * + * @retval 0 Success + * @retval -EIO Communication with the transceiver failed + */ +int cc110x_wakeup(cc110x_t *dev); + +/** + * @brief Sets the transceiver into SLEEP mode. + * + * Only @ref cc110x_wakeup can awake the device again. + */ +void cc110x_sleep(cc110x_t *dev); + #ifdef __cplusplus } #endif