1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

drivers/cc110x: add power off (sleep) functions

This commit is contained in:
Francisco Acosta 2019-08-14 17:31:33 +02:00 committed by Marian Buschsieweke
parent 8f93e42e9c
commit 0efc13602f
No known key found for this signature in database
GPG Key ID: 61F64C6599B1539F
6 changed files with 118 additions and 40 deletions

View File

@ -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);
}

View File

@ -18,13 +18,16 @@
*/
#include <errno.h>
#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;
}

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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