1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/at86rf215/at86rf215_internal.c
Benjamin Valentin 611f4161d6 drivers/at86rf215: fix reset after deep sleep
When the radio is in DEEP SLEEP, reading the state register will return 0.
This makes the reset routine abort early even though the device is connected
and still waking up.

Instead, just add a timeout to the polling of the WAKEUP IRQ bit.
In practise it has shown that the loop will be taken two times on 'normal'
reset and four times when the radio was in DEEP SLEEP.

Polling 255 times sure does not hurt.

To reproduce the issue, put both interfaces into SLEEP mode,
then reboot the module:

    ifconfig 7 set state sleep
    ifconfig 8 set state sleep
    reboot
2020-03-29 19:44:40 +02:00

205 lines
5.7 KiB
C

/*
* Copyright (C) 2019 ML!PA Consulting GmbH
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_at86rf215
* @{
*
* @file
* @brief Low-Level functions for the AT86RF215 driver
*
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
* @}
*/
#include "periph/spi.h"
#include "periph/gpio.h"
#include "xtimer.h"
#include "at86rf215_internal.h"
#include <string.h>
#define SPIDEV (dev->params.spi)
#define CSPIN (dev->params.cs_pin)
static inline void getbus(const at86rf215_t *dev)
{
spi_acquire(SPIDEV, CSPIN, SPI_MODE_0, dev->params.spi_clk);
}
/* only to be used by at86rf215_hardware_reset()
can't use normal at86rf215_reg_read() because
we already hold the lock */
static inline uint8_t _get_reg_with_lock(at86rf215_t *dev, uint16_t r)
{
uint16_t reg = htons(r | FLAG_READ);
spi_transfer_bytes(SPIDEV, CSPIN, true, &reg, NULL, sizeof(reg));
return spi_transfer_byte(SPIDEV, CSPIN, false, 0);
}
int at86rf215_hardware_reset(at86rf215_t *dev)
{
/* prevent access during reset */
getbus(dev);
/* trigger hardware reset */
gpio_clear(dev->params.reset_pin);
xtimer_usleep(AT86RF215_RESET_PULSE_WIDTH_US);
gpio_set(dev->params.reset_pin);
xtimer_usleep(AT86RF215_RESET_DELAY_US);
/* While the device is in RESET / DEEP SLEEP, all registers
but STATE will read 0xFF.
WAKEUP IRQ signals that the device is ready. */
uint8_t state = 0;
uint8_t tries = 255;
while (--tries && (state == 0xFF || !(state & IRQS_WAKEUP_MASK))) {
state = _get_reg_with_lock(dev, dev->RF->RG_IRQS);
}
spi_release(SPIDEV);
/* no device connected */
if (!tries) {
return -ENODEV;
}
/* clear interrupts */
at86rf215_reg_read(dev, RG_RF09_IRQS);
at86rf215_reg_read(dev, RG_RF24_IRQS);
at86rf215_reg_read(dev, RG_BBC0_IRQS);
at86rf215_reg_read(dev, RG_BBC1_IRQS);
return 0;
}
void at86rf215_reg_write(const at86rf215_t *dev, uint16_t reg, uint8_t value)
{
reg = htons(reg | FLAG_WRITE);
getbus(dev);
spi_transfer_bytes(SPIDEV, CSPIN, true, &reg, NULL, sizeof(reg));
spi_transfer_byte(SPIDEV, CSPIN, false, value);
spi_release(SPIDEV);
}
void at86rf215_reg_write_bytes(const at86rf215_t *dev, uint16_t reg, const void *data, size_t len)
{
reg = htons(reg | FLAG_WRITE);
getbus(dev);
spi_transfer_bytes(SPIDEV, CSPIN, true, &reg, NULL, sizeof(reg));
spi_transfer_bytes(SPIDEV, CSPIN, false, data, NULL, len);
spi_release(SPIDEV);
}
uint8_t at86rf215_reg_read(const at86rf215_t *dev, uint16_t reg)
{
uint8_t val;
reg = htons(reg | FLAG_READ);
getbus(dev);
spi_transfer_bytes(SPIDEV, CSPIN, true, &reg, NULL, sizeof(reg));
val = spi_transfer_byte(SPIDEV, CSPIN, false, 0);
spi_release(SPIDEV);
return val;
}
void at86rf215_reg_read_bytes(const at86rf215_t *dev, uint16_t reg, void *data, size_t len)
{
reg = htons(reg | FLAG_READ);
getbus(dev);
spi_transfer_bytes(SPIDEV, CSPIN, true, &reg, NULL, sizeof(reg));
spi_transfer_bytes(SPIDEV, CSPIN, false, NULL, data, len);
spi_release(SPIDEV);
}
void at86rf215_filter_ack(at86rf215_t *dev, bool on)
{
/* only listen for ACK frames */
uint8_t val = on ? (1 << IEEE802154_FCF_TYPE_ACK)
: (1 << IEEE802154_FCF_TYPE_BEACON)
| (1 << IEEE802154_FCF_TYPE_DATA)
| (1 << IEEE802154_FCF_TYPE_MACCMD);
at86rf215_reg_write(dev, dev->BBC->RG_AFFTM, val);
}
void at86rf215_get_random(at86rf215_t *dev, void *data, size_t len)
{
/* store previous PHY control state */
uint8_t state_pc = at86rf215_reg_read(dev, dev->BBC->RG_PC);
/* disable baseband processor */
at86rf215_reg_write(dev, dev->BBC->RG_PC, state_pc & ~PC_BBEN_MASK);
/* store previous RX bandwidth settings */
uint8_t rxbwc = at86rf215_reg_read(dev, dev->RF->RG_RXBWC);
/* The analog frontend of the radio must be configured to the
widest filter bandwidth; The bit RXBWC.IFS must be set to 1 */
at86rf215_reg_write(dev, dev->RF->RG_RXBWC, 0x1B);
uint8_t *data8 = data;
while (len--) {
*data8++ = at86rf215_reg_read(dev, dev->RF->RG_RNDV);
}
/* restore RX bandwidth settings */
at86rf215_reg_write(dev, dev->RF->RG_RXBWC, rxbwc);
/* restore PHY control settings */
at86rf215_reg_write(dev, dev->BBC->RG_PC, state_pc);
}
uint16_t at86rf215_chan_valid(at86rf215_t *dev, uint16_t chan)
{
if (is_subGHz(dev)) {
if (chan >= dev->num_chans) {
return dev->num_chans - 1;
}
} else {
if (chan < IEEE802154_CHANNEL_MIN) {
return IEEE802154_CHANNEL_MIN;
} else if (chan >= IEEE802154_CHANNEL_MIN + dev->num_chans) {
return IEEE802154_CHANNEL_MIN + dev->num_chans - 1;
}
}
return chan;
}
const char* at86rf215_hw_state2a(uint8_t state)
{
switch (state) {
case RF_STATE_TRXOFF: return "TRXOFF";
case RF_STATE_TXPREP: return "TXPREP";
case RF_STATE_TX: return "TX";
case RF_STATE_RX: return "RX";
case RF_STATE_TRANSITION: return "TRANSITION";
case RF_STATE_RESET: return "RESET";
default: return "invalid";
}
}
const char* at86rf215_sw_state2a(at86rf215_state_t state) {
switch (state) {
case AT86RF215_STATE_OFF: return "OFF";
case AT86RF215_STATE_IDLE: return "IDLE";
case AT86RF215_STATE_RX_SEND_ACK: return "RX (sending ACK)";
case AT86RF215_STATE_TX: return "TX";
case AT86RF215_STATE_TX_WAIT_ACK: return "TX (wait for ACK)";
case AT86RF215_STATE_SLEEP: return "SLEEP";
default: return "invalid";
}
}