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

Merge pull request #13583 from benpicco/at86rf215-minimal

drivers/at86rf215: add basic support for AT86RF215 dual-band radio
This commit is contained in:
Francisco 2020-03-20 09:33:50 +01:00 committed by GitHub
commit cdbf0b2d69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 8641 additions and 4 deletions

View File

@ -64,6 +64,7 @@
/drivers/ad7746/ @leandrolanzieri
/drivers/at24mac/ @benpicco
/drivers/at86rf215/ @benpicco
/drivers/at86rf2xx/ @daniel-k @Hyungsin @jia200x @smlng @miri64
/drivers/bh1900nux/ @wosym
/drivers/cc110x/ @maribu

View File

@ -1,5 +1,13 @@
ifneq (,$(filter gnrc_netdev_default netdev_default,$(USEMODULE)))
USEMODULE += cc2538_rf
ifeq (,$(filter cc2538_rf,$(USEMODULE)))
USEMODULE += at86rf215
endif
endif
# at86rf215 is hard-wired to sub-GHz, but 2.4 GHz can be switched between
# at86rf215 and cc2538_rf. Use 2.4 GHz for cc2538_rf if both are used.
ifeq (at86rf215 cc2538_rf, $(filter at86rf215 cc2538_rf,$(USEMODULE)))
DISABLE_MODULE += at86rf215_24ghz
endif
ifneq (,$(filter saul_default,$(USEMODULE)))

View File

@ -36,9 +36,15 @@ void board_init(void)
gpio_init(RF24_SWITCH_CC2538_PIN, GPIO_OUT);
gpio_init(RF24_SWITCH_AT86RF215_PIN, GPIO_OUT);
/* start with cc2538 2.4ghz radio*/
RF24_SWITCH_CC2538_ON;
#ifdef MODULE_CC2538_RF
/* use cc2538 2.4ghz radio*/
RF24_SWITCH_AT86RF215_OFF;
RF24_SWITCH_CC2538_ON;
#else
/* use at86rf215 2.4ghz radio*/
RF24_SWITCH_CC2538_OFF;
RF24_SWITCH_AT86RF215_ON;
#endif
/* initialize the CPU */
cpu_init();

View File

@ -76,6 +76,16 @@
#define RF24_SWITCH_AT86RF215_TOGGLE (RF_SWITCH_PORT->DATA ^= RF24_SWITCH_AT86RF215_MASK)
/** @} */
/**
* @name AT86RF215 configuration
* @{
*/
#define AT86RF215_PARAM_SPI SPI_DEV(0)
#define AT86RF215_PARAM_CS GPIO_PIN(0, 3) /* A3 */
#define AT86RF215_PARAM_INT GPIO_PIN(3, 0) /* D0 */
#define AT86RF215_PARAM_RESET GPIO_PIN(3, 1) /* D1 */
/** @} */
/**
* @name xtimer configuration
* @{

View File

@ -60,9 +60,30 @@ ifneq (,$(filter at30tse75x,$(USEMODULE)))
FEATURES_REQUIRED += periph_i2c
endif
ifneq (,$(filter at86rf%,$(USEMODULE)))
ifneq (,$(filter at86rf215%,$(USEMODULE)))
USEMODULE += at86rf215
DEFAULT_MODULE += auto_init_at86rf215
DEFAULT_MODULE += at86rf215_subghz
ifeq (,$(filter at86rf215m,$(USEMODULE)))
DEFAULT_MODULE += at86rf215_24ghz
endif
FEATURES_REQUIRED += periph_gpio
FEATURES_REQUIRED += periph_gpio_irq
FEATURES_REQUIRED += periph_spi
USEMODULE += xtimer
USEMODULE += luid
USEMODULE += netif
USEMODULE += ieee802154
USEMODULE += netdev_ieee802154
endif
ifneq (,$(filter at86rf%, $(filter-out at86rf215%, $(USEMODULE))))
USEMODULE += at86rf2xx
DEFAULT_MODULE += auto_init_at86rf2xx
USEMODULE += xtimer
USEMODULE += luid
USEMODULE += netif

View File

@ -36,6 +36,10 @@ ifneq (,$(filter at86rf2xx,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/at86rf2xx/include
endif
ifneq (,$(filter at86rf215,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/at86rf215/include
endif
ifneq (,$(filter ata8520e,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/ata8520e/include
endif

View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,342 @@
/*
* 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 Implementation of public functions for AT86RF215 driver
*
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
* @}
*/
#include "luid.h"
#include "byteorder.h"
#include "net/ieee802154.h"
#include "net/gnrc.h"
#include "unaligned.h"
#include "at86rf215_internal.h"
#include "at86rf215_netdev.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static void _setup_interface(at86rf215_t *dev, const at86rf215_params_t *params)
{
netdev_t *netdev = (netdev_t *)dev;
netdev->driver = &at86rf215_driver;
dev->params = *params;
dev->state = AT86RF215_STATE_OFF;
}
void at86rf215_setup(at86rf215_t *dev_09, at86rf215_t *dev_24, const at86rf215_params_t *params)
{
/* configure the sub-GHz interface */
if (dev_09) {
dev_09->RF = &RF09_regs;
dev_09->BBC = &BBC0_regs;
_setup_interface(dev_09, params);
dev_09->sibling = dev_24;
}
/* configure the 2.4 GHz interface */
if (dev_24) {
dev_24->RF = &RF24_regs;
dev_24->BBC = &BBC1_regs;
_setup_interface(dev_24, params);
dev_24->sibling = dev_09;
}
}
void at86rf215_reset_and_cfg(at86rf215_t *dev)
{
netdev_ieee802154_reset(&dev->netdev);
/* set device address */
luid_get_short((network_uint16_t *)&dev->netdev.short_addr);
luid_get_eui64((eui64_t *)&dev->netdev.long_addr);
if (is_subGHz(dev)) {
dev->netdev.chan = AT86RF215_DEFAULT_SUBGHZ_CHANNEL;
} else {
dev->netdev.chan = AT86RF215_DEFAULT_CHANNEL;
}
dev->netdev.pan = IEEE802154_DEFAULT_PANID;
/* set default options */
dev->retries_max = AT86RF215_RETRIES_MAX_DEFAULT;
dev->csma_retries_max = AT86RF215_CSMA_RETRIES_MAX_DEFAULT;
dev->csma_maxbe = AT86RF215_CSMA_MAX_BE_DEFAULT;
dev->csma_minbe = AT86RF215_CSMA_MIN_BE_DEFAULT;
dev->flags |= AT86RF215_OPT_AUTOACK
| AT86RF215_OPT_CSMA;
/* apply the configuration */
at86rf215_reset(dev);
/* default to requesting ACKs, just like at86rf2xx */
const netopt_enable_t enable = NETOPT_ENABLE;
netdev_ieee802154_set(&dev->netdev, NETOPT_ACK_REQ, &enable, sizeof(enable));
}
void at86rf215_reset(at86rf215_t *dev)
{
uint8_t reg;
dev->state = AT86RF215_STATE_OFF;
/* Reset state machine to ensure a known state */
at86rf215_rf_cmd(dev, CMD_RF_TRXOFF);
at86rf215_await_state(dev, RF_STATE_TRXOFF);
if (!dev->sibling) {
/* disable 2.4-GHz IRQs if the interface is not enabled */
if (is_subGHz(dev)) {
at86rf215_reg_write(dev, RG_BBC1_IRQM, 0);
at86rf215_reg_write(dev, RG_RF24_IRQM, 0);
at86rf215_reg_write(dev, RG_RF24_CMD, CMD_RF_SLEEP);
/* disable sub-GHz IRQs if the interface is not enabled */
} else {
at86rf215_reg_write(dev, RG_BBC0_IRQM, 0);
at86rf215_reg_write(dev, RG_RF09_IRQM, 0);
at86rf215_reg_write(dev, RG_RF09_CMD, CMD_RF_SLEEP);
}
}
/* disable clock output */
#if AT86RF215_USE_CLOCK_OUTPUT == 0
at86rf215_reg_write(dev, RG_RF_CLKO, 0);
#endif
/* allow to configure board-specific trim */
#ifdef AT86RF215_TRIM_VAL
at86rf215_reg_write(dev, RG_RF_XOC, AT86RF215_TRIM_VAL | XOC_FS_MASK);
#endif
/* enable TXFE & RXFE IRQ */
at86rf215_reg_write(dev, dev->BBC->RG_IRQM, BB_IRQ_TXFE | BB_IRQ_RXFE);
/* enable EDC IRQ */
at86rf215_reg_write(dev, dev->RF->RG_IRQM, RF_IRQ_EDC | RF_IRQ_TRXRDY);
/* set energy detect threshold to -84 dBm */
at86rf215_set_cca_threshold(dev, AT86RF215_EDT_DEFAULT);
/* enable address filter 0 */
at86rf215_reg_write(dev, dev->BBC->RG_AFC0, AFC0_AFEN0_MASK );
at86rf215_reg_write(dev, dev->BBC->RG_AMAACKPD, AMAACKPD_PD0_MASK);
/* enable auto-ACK with Frame Checksum & Data Rate derived from RX frame */
reg = AMCS_AACKFA_MASK | AMCS_AACKDR_MASK;
if (dev->flags & AT86RF215_OPT_AUTOACK) {
reg |= AMCS_AACK_MASK;
}
at86rf215_reg_write(dev, dev->BBC->RG_AMCS, reg);
/* set compatibility with first-gen 802.15.4 devices */
at86rf215_configure_legacy_OQPSK(dev, 0);
/* set default channel */
at86rf215_set_chan(dev, dev->netdev.chan);
/* set short and long address */
uint64_t long_addr;
memcpy(&long_addr, dev->netdev.long_addr, sizeof(long_addr));
at86rf215_set_addr_long(dev, long_addr);
at86rf215_set_addr_short(dev, 0, unaligned_get_u16(dev->netdev.short_addr));
/* set default PAN id */
at86rf215_set_pan(dev, 0, dev->netdev.pan);
/* set default TX power */
at86rf215_set_txpower(dev, AT86RF215_DEFAULT_TXPOWER);
/* start listening for incoming packets */
at86rf215_rf_cmd(dev, CMD_RF_RX);
at86rf215_await_state(dev, RF_STATE_RX);
dev->state = AT86RF215_STATE_IDLE;
}
ssize_t at86rf215_send(at86rf215_t *dev, const void *data, size_t len)
{
/* check data length */
if (len > AT86RF215_MAX_PKT_LENGTH) {
DEBUG("[at86rf215] Error: data to send exceeds max packet size\n");
return -EOVERFLOW;
}
if (at86rf215_tx_prepare(dev)) {
return -EBUSY;
}
at86rf215_tx_load(dev, data, len, 0);
at86rf215_tx_exec(dev);
return len;
}
void at86rf215_tx_done(at86rf215_t *dev)
{
uint8_t amcs = at86rf215_reg_read(dev, dev->BBC->RG_AMCS);
/* re-enable AACK, disable TX2RX */
amcs &= ~AMCS_TX2RX_MASK;
if (dev->flags & AT86RF215_OPT_AUTOACK) {
amcs |= AMCS_AACK_MASK;
}
at86rf215_reg_write(dev, dev->BBC->RG_AMCS, amcs);
}
static bool _tx_ongoing(at86rf215_t *dev)
{
if (dev->flags & AT86RF215_OPT_TX_PENDING) {
return true;
}
if (dev->state == AT86RF215_STATE_TX ||
dev->state == AT86RF215_STATE_TX_WAIT_ACK) {
return true;
}
return false;
}
/*
* As there is no packet queue in RIOT we have to block in send()
* when the device is busy sending a previous frame.
*
* Since both _send() and _isr() are running in the same thread
* we have to service radio events while waiting in order to
* advance the previous transmission.
*/
static void _block_while_busy(at86rf215_t *dev)
{
gpio_irq_disable(dev->params.int_pin);
do {
if (gpio_read(dev->params.int_pin) || dev->timeout) {
at86rf215_driver.isr((netdev_t *) dev);
}
/* allow the other interface to process events */
thread_yield();
} while (_tx_ongoing(dev));
gpio_irq_enable(dev->params.int_pin);
}
void at86rf215_block_while_busy(at86rf215_t *dev)
{
if (_tx_ongoing(dev)) {
DEBUG("[at86rf215] Block while TXing\n");
_block_while_busy(dev);
}
}
int at86rf215_tx_prepare(at86rf215_t *dev)
{
if (dev->state == AT86RF215_STATE_SLEEP) {
return -EAGAIN;
}
at86rf215_block_while_busy(dev);
dev->tx_frame_len = IEEE802154_FCS_LEN;
return 0;
}
size_t at86rf215_tx_load(at86rf215_t *dev, const uint8_t *data,
size_t len, size_t offset)
{
/* set bit if ACK was requested and retransmission is enabled */
if (offset == 0 && (data[0] & IEEE802154_FCF_ACK_REQ) && dev->retries_max) {
dev->flags |= AT86RF215_OPT_ACK_REQUESTED;
}
at86rf215_reg_write_bytes(dev, dev->BBC->RG_FBTXS + offset, data, len);
dev->tx_frame_len += (uint16_t) len;
return offset + len;
}
int at86rf215_tx_exec(at86rf215_t *dev)
{
/* write frame length */
at86rf215_reg_write16(dev, dev->BBC->RG_TXFLL, dev->tx_frame_len);
dev->retries = dev->retries_max;
dev->csma_retries = dev->csma_retries_max;
dev->flags |= AT86RF215_OPT_TX_PENDING;
if ((dev->flags & AT86RF215_OPT_CSMA) && !(dev->flags & AT86RF215_OPT_CCATX)) {
dev->flags |= AT86RF215_OPT_CCA_PENDING;
}
if (dev->state == AT86RF215_STATE_IDLE) {
at86rf215_rf_cmd(dev, CMD_RF_TXPREP);
} else {
DEBUG("[at86rf215] will TX after %s\n", at86rf215_sw_state2a(dev->state));
}
return 0;
}
void at86rf215_tx_abort(at86rf215_t *dev)
{
dev->flags &= ~(AT86RF215_OPT_CCA_PENDING | AT86RF215_OPT_TX_PENDING);
at86rf215_tx_done(dev);
at86rf215_enable_baseband(dev);
at86rf215_rf_cmd(dev, CMD_RF_RX);
dev->state = AT86RF215_STATE_IDLE;
}
bool at86rf215_cca(at86rf215_t *dev)
{
bool clear;
uint8_t old_state;
if (dev->state != AT86RF215_STATE_IDLE) {
return false;
}
if (dev->flags & AT86RF215_OPT_TX_PENDING) {
return false;
}
if (!at86rf215_set_rx_from_idle(dev, &old_state)) {
return false;
}
/* disable ED IRQ, baseband */
at86rf215_reg_and(dev, dev->RF->RG_IRQM, ~(RF_IRQ_EDC | RF_IRQ_TRXRDY));
at86rf215_reg_and(dev, dev->BBC->RG_PC, ~PC_BBEN_MASK);
/* start energy detect */
at86rf215_reg_write(dev, dev->RF->RG_EDC, RF_EDSINGLE);
while (!(at86rf215_reg_read(dev, dev->RF->RG_IRQS) & RF_IRQ_EDC)) {}
clear = !(at86rf215_reg_read(dev, dev->BBC->RG_AMCS) & AMCS_CCAED_MASK);
/* enable ED IRQ, baseband */
at86rf215_reg_or(dev, dev->RF->RG_IRQM, RF_IRQ_EDC | RF_IRQ_TRXRDY);
at86rf215_reg_or(dev, dev->BBC->RG_PC, PC_BBEN_MASK);
at86rf215_set_idle_from_rx(dev, old_state);
return clear;
}

View File

@ -0,0 +1,333 @@
/*
* 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 Getter and setter functions for the AT86RF215 driver
*
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
* @}
*/
#include <string.h>
#include "at86rf215.h"
#include "at86rf215_internal.h"
#include "periph/spi.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
/* we can still go +3 dBm higher by increasing PA current */
#define PAC_DBM_MIN (-31) /* dBm */
uint16_t at86rf215_get_addr_short(const at86rf215_t *dev, uint8_t filter)
{
if (filter > 3) {
return 0;
}
/* Each frame filter has a 2 byte short addr + 2 byte PAN ID, so we have to
skip 4 bytes per filter */
return ntohs(at86rf215_reg_read16(dev, dev->BBC->RG_MACSHA0F0 + (4 * filter)));
}
void at86rf215_set_addr_short(at86rf215_t *dev, uint8_t filter, uint16_t addr)
{
/* do not overwrite node address for filters other than 0 */
if (filter == 0) {
dev->netdev.short_addr[0] = (uint8_t)(addr);
dev->netdev.short_addr[1] = (uint8_t)(addr >> 8);
}
if (filter > 3) {
return;
}
at86rf215_reg_write16(dev, dev->BBC->RG_MACSHA0F0 + (4 * filter), htons(addr));
}
bool at86rf215_get_framefilter_enabled(at86rf215_t *dev, uint8_t filter)
{
return (at86rf215_reg_read(dev, dev->BBC->RG_AFC0) >> (AFC0_AFEN0_SHIFT + filter)) & 1;
}
void at86rf215_enable_framefilter(at86rf215_t *dev, uint8_t filter)
{
at86rf215_reg_or(dev, dev->BBC->RG_AFC0, 1 << (AFC0_AFEN0_SHIFT + filter));
}
void at86rf215_disable_framefilter(at86rf215_t *dev, uint8_t filter)
{
at86rf215_reg_and(dev, dev->BBC->RG_AFC0, 1 << (AFC0_AFEN0_SHIFT + filter));
}
uint64_t at86rf215_get_addr_long(const at86rf215_t *dev)
{
uint64_t addr;
at86rf215_reg_read_bytes(dev, dev->BBC->RG_MACEA0, &addr, sizeof(addr));
return addr;
}
void at86rf215_set_addr_long(at86rf215_t *dev, uint64_t addr)
{
memcpy(dev->netdev.long_addr, &addr, sizeof(addr));
addr = ntohll(addr);
at86rf215_reg_write_bytes(dev, dev->BBC->RG_MACEA0, &addr, sizeof(addr));
}
uint8_t at86rf215_get_chan(const at86rf215_t *dev)
{
return at86rf215_reg_read16(dev, dev->RF->RG_CNL);
}
void at86rf215_set_chan(at86rf215_t *dev, uint16_t channel)
{
at86rf215_await_state_end(dev, RF_STATE_TX);
uint8_t old_state = at86rf215_get_rf_state(dev);
/* frequency has to be updated in TRXOFF or TXPREP (datatsheet: 6.3.2) */
if (old_state == RF_STATE_RX) {
at86rf215_rf_cmd(dev, CMD_RF_TXPREP);
}
at86rf215_reg_write16(dev, dev->RF->RG_CNL, channel);
dev->netdev.chan = channel;
/* enable the radio again */
if (old_state == RF_STATE_RX) {
at86rf215_rf_cmd(dev, old_state);
}
}
uint16_t at86rf215_get_channel_spacing(at86rf215_t *dev) {
/* 25 kHz resolution */
return 25 * at86rf215_reg_read(dev, dev->RF->RG_CS);
}
uint16_t at86rf215_get_pan(const at86rf215_t *dev, uint8_t filter)
{
if (filter > 3) {
return 0;
}
return at86rf215_reg_read16(dev, dev->BBC->RG_MACPID0F0 + (4 * filter));
}
void at86rf215_set_pan(at86rf215_t *dev, uint8_t filter, uint16_t pan)
{
if (filter == 0) {
dev->netdev.pan = pan;
}
if (filter > 3) {
return;
}
at86rf215_reg_write16(dev, dev->BBC->RG_MACPID0F0 + (4 * filter), pan);
}
// TODO: take modulation into account
int16_t at86rf215_get_txpower(const at86rf215_t *dev)
{
uint8_t pac = at86rf215_reg_read(dev, dev->RF->RG_PAC);
/* almost linear, each PACUR step adds ~1 dBm */
return PAC_DBM_MIN + (pac & PAC_TXPWR_MASK) +
((pac & PAC_PACUR_MASK) >> PAC_PACUR_SHIFT);
}
// TODO: take modulation into account
void at86rf215_set_txpower(const at86rf215_t *dev, int16_t txpower)
{
uint8_t pacur = 0;
txpower -= PAC_DBM_MIN;
if (txpower < 0) {
txpower = 0;
}
if (txpower > PAC_TXPWR_MASK) {
switch (txpower - PAC_TXPWR_MASK) {
case 1:
pacur = 1 << PAC_PACUR_SHIFT;
break;
case 2:
pacur = 2 << PAC_PACUR_SHIFT;
break;
default:
pacur = 3 << PAC_PACUR_SHIFT;
break;
}
txpower = PAC_TXPWR_MASK;
}
at86rf215_reg_write(dev, dev->RF->RG_PAC, pacur | txpower);
}
int8_t at86rf215_get_cca_threshold(const at86rf215_t *dev)
{
return dev->csma_ed;
}
void at86rf215_set_cca_threshold(at86rf215_t *dev, int8_t value)
{
dev->csma_ed = value;
at86rf215_reg_write(dev, dev->BBC->RG_AMEDT, value);
}
int8_t at86rf215_get_ed_level(at86rf215_t *dev)
{
return at86rf215_reg_read(dev, dev->RF->RG_EDV);
}
void at86rf215_set_option(at86rf215_t *dev, uint16_t option, bool state)
{
/* set option field */
dev->flags = (state) ? (dev->flags | option)
: (dev->flags & ~option);
switch (option) {
case AT86RF215_OPT_TELL_RX_START:
if (state) {
at86rf215_reg_or(dev, dev->BBC->RG_IRQM, BB_IRQ_RXAM);
} else {
at86rf215_reg_and(dev, dev->BBC->RG_IRQM, ~BB_IRQ_RXAM);
}
break;
case AT86RF215_OPT_PROMISCUOUS:
if (state) {
at86rf215_reg_or(dev, dev->BBC->RG_AFC0, AFC0_PM_MASK);
} else {
at86rf215_reg_and(dev, dev->BBC->RG_AFC0, ~AFC0_PM_MASK);
}
break;
case AT86RF215_OPT_AUTOACK:
if (state) {
at86rf215_reg_or(dev, dev->BBC->RG_AMCS, AMCS_AACK_MASK);
} else {
at86rf215_reg_and(dev, dev->BBC->RG_AMCS, ~AMCS_AACK_MASK);
}
break;
case AT86RF215_OPT_CCATX:
if (state){
at86rf215_reg_or(dev, dev->BBC->RG_AMCS, AMCS_CCATX_MASK);
} else {
at86rf215_reg_and(dev, dev->BBC->RG_AMCS, ~AMCS_CCATX_MASK);
}
break;
default:
/* do nothing */
break;
}
}
static void _wake_from_sleep(at86rf215_t *dev)
{
/* wake the transceiver */
at86rf215_rf_cmd(dev, CMD_RF_TRXOFF);
at86rf215_await_state(dev, RF_STATE_TRXOFF);
/* config is lost after SLEEP */
at86rf215_reset(dev);
/* if both transceivers were sleeping, the chip entered DEEP_SLEEP.
Waking one device in that mode wakes the other one too. */
if (dev->sibling && dev->sibling->state == AT86RF215_STATE_SLEEP) {
at86rf215_rf_cmd(dev->sibling, CMD_RF_SLEEP);
}
}
bool at86rf215_set_rx_from_idle(at86rf215_t *dev, uint8_t *state)
{
if (dev->state == AT86RF215_STATE_SLEEP) {
_wake_from_sleep(dev);
}
uint8_t s;
while ((s = at86rf215_get_rf_state(dev)) == RF_STATE_TRANSITION) {}
if (state) {
*state = s;
}
if (s == RF_STATE_RESET) {
at86rf215_rf_cmd(dev, CMD_RF_TRXOFF);
at86rf215_await_state(dev, RF_STATE_TRXOFF);
s = RF_STATE_TRXOFF;
}
if (s == RF_STATE_TRXOFF) {
at86rf215_rf_cmd(dev, CMD_RF_RX);
at86rf215_await_state(dev, RF_STATE_RX);
s = RF_STATE_RX;
}
if (s == RF_STATE_RX) {
return true;
}
return false;
}
bool at86rf215_set_idle_from_rx(at86rf215_t *dev, uint8_t state)
{
if (state == RF_STATE_TX || state == CMD_RF_TXPREP) {
return false;
}
if (dev->state == AT86RF215_STATE_SLEEP) {
if (state == CMD_RF_SLEEP) {
return true;
}
_wake_from_sleep(dev);
}
uint8_t s;
while ((s = at86rf215_get_rf_state(dev)) == RF_STATE_TRANSITION) {}
if (s != RF_STATE_RX) {
return false;
}
if (state == RF_STATE_RX) {
return true;
}
at86rf215_rf_cmd(dev, CMD_RF_TRXOFF);
at86rf215_await_state(dev, RF_STATE_TRXOFF);
if (state == RF_STATE_TRXOFF) {
return true;
}
/* clear IRQ */
at86rf215_reg_read(dev, dev->BBC->RG_IRQS);
at86rf215_reg_read(dev, dev->RF->RG_IRQS);
at86rf215_rf_cmd(dev, CMD_RF_SLEEP);
dev->state = AT86RF215_STATE_SLEEP;
if (state == RF_STATE_RESET) {
return true;
}
return false;
}

View File

@ -0,0 +1,204 @@
/*
* 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);
uint8_t state = _get_reg_with_lock(dev, dev->RF->RG_STATE) & STATE_STATE_MASK;
if (state != RF_STATE_TRXOFF && state != RF_STATE_RESET) {
spi_release(SPIDEV);
return -ENODEV;
}
/* While the device is in RESET / DEEP SLEEP, all registers
but STATE will read 0xFF.
WAKEUP IRQ signals that the device is ready. */
state = 0;
while (state == 0xFF || !(state & IRQS_WAKEUP_MASK)) {
state = _get_reg_with_lock(dev, dev->RF->RG_IRQS);
}
spi_release(SPIDEV);
/* 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";
}
}

View File

@ -0,0 +1,936 @@
/*
* 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 Netdev adaption for the AT86RF215 driver
*
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
* @author Georg von Zengen <vonzengen@ibr.cs.tu-bs.de>
* @}
*/
#include <string.h>
#include <assert.h>
#include <errno.h>
#include "iolist.h"
#include "net/eui64.h"
#include "net/ieee802154.h"
#include "net/netdev.h"
#include "net/netdev/ieee802154.h"
#include "net/gnrc/netif/internal.h"
#include "at86rf215.h"
#include "at86rf215_netdev.h"
#include "at86rf215_internal.h"
#include "debug.h"
static int _send(netdev_t *netdev, const iolist_t *iolist);
static int _recv(netdev_t *netdev, void *buf, size_t len, void *info);
static int _init(netdev_t *netdev);
static void _isr(netdev_t *netdev);
static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len);
static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len);
const netdev_driver_t at86rf215_driver = {
.send = _send,
.recv = _recv,
.init = _init,
.isr = _isr,
.get = _get,
.set = _set,
};
/* executed in the GPIO ISR context */
static void _irq_handler(void *arg)
{
netdev_t *netdev = (netdev_t *) arg;
netdev->event_callback(netdev, NETDEV_EVENT_ISR);
}
/* if only one interface is active, but the other one to sleep */
static inline void _put_sibling_to_sleep(at86rf215_t *dev) {
if (is_subGHz(dev)) {
at86rf215_reg_write(dev, RG_RF24_CMD, CMD_RF_SLEEP);
} else {
at86rf215_reg_write(dev, RG_RF09_CMD, CMD_RF_SLEEP);
}
}
static int _init(netdev_t *netdev)
{
int res;
at86rf215_t *dev = (at86rf215_t *)netdev;
/* don't call HW init for both radios */
if (is_subGHz(dev) || dev->sibling == NULL) {
/* initialize GPIOs */
spi_init_cs(dev->params.spi, dev->params.cs_pin);
gpio_init(dev->params.reset_pin, GPIO_OUT);
gpio_set(dev->params.reset_pin);
/* reset the entire chip */
if ((res = at86rf215_hardware_reset(dev))) {
return res;
}
/* turn off unused interface */
if (dev->sibling == NULL) {
_put_sibling_to_sleep(dev);
}
gpio_init_int(dev->params.int_pin, GPIO_IN, GPIO_RISING, _irq_handler, dev);
}
res = at86rf215_reg_read(dev, RG_RF_PN);
if ((res != AT86RF215_PN) && (res != AT86RF215M_PN)) {
DEBUG("[at86rf215] error: unable to read correct part number: %x\n", res);
return -ENOTSUP;;
}
/* reset device to default values and put it into RX state */
at86rf215_reset_and_cfg(dev);
return 0;
}
static int _send(netdev_t *netdev, const iolist_t *iolist)
{
at86rf215_t *dev = (at86rf215_t *)netdev;
size_t len = 0;
if (at86rf215_tx_prepare(dev)) {
return -EBUSY;
}
/* load packet data into FIFO */
for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
/* current packet data + FCS too long */
if ((len + iol->iol_len + IEEE802154_FCS_LEN) > AT86RF215_MAX_PKT_LENGTH) {
DEBUG("[at86rf215] error: packet too large (%u byte) to be send\n",
(unsigned)len + IEEE802154_FCS_LEN);
at86rf215_tx_abort(dev);
return -EOVERFLOW;
}
if (iol->iol_len) {
len = at86rf215_tx_load(dev, iol->iol_base, iol->iol_len, len);
}
}
/* send data out directly if pre-loading id disabled */
if (!(dev->flags & AT86RF215_OPT_PRELOADING)) {
at86rf215_tx_exec(dev);
}
/* return the number of bytes that were actually loaded into the frame
* buffer/send out */
return (int)len;
}
static int _recv(netdev_t *netdev, void *buf, size_t len, void *info)
{
at86rf215_t *dev = (at86rf215_t *)netdev;
int16_t pkt_len;
/* get the size of the received packet */
at86rf215_reg_read_bytes(dev, dev->BBC->RG_RXFLL, &pkt_len, sizeof(pkt_len));
/* subtract length of FCS field */
pkt_len = (pkt_len & 0x7ff) - IEEE802154_FCS_LEN;
/* just return length when buf == NULL */
if (buf == NULL) {
return pkt_len;
}
/* not enough space in buf */
if (pkt_len > (int) len) {
return -ENOBUFS;
}
/* copy payload */
at86rf215_reg_read_bytes(dev, dev->BBC->RG_FBRXS, buf, pkt_len);
if (info != NULL) {
netdev_ieee802154_rx_info_t *radio_info = info;
radio_info->rssi = (int8_t) at86rf215_reg_read(dev, dev->RF->RG_EDV);
}
return pkt_len;
}
static int _set_state(at86rf215_t *dev, netopt_state_t state)
{
at86rf215_block_while_busy(dev);
switch (state) {
case NETOPT_STATE_STANDBY:
at86rf215_set_idle_from_rx(dev, CMD_RF_TRXOFF);
break;
case NETOPT_STATE_SLEEP:
at86rf215_set_idle_from_rx(dev, CMD_RF_SLEEP);
break;
case NETOPT_STATE_RX:
case NETOPT_STATE_IDLE:
at86rf215_set_rx_from_idle(dev, NULL);
break;
case NETOPT_STATE_TX:
if (dev->flags & AT86RF215_OPT_PRELOADING) {
return at86rf215_tx_exec(dev);
}
break;
case NETOPT_STATE_RESET:
at86rf215_reset(dev);
break;
default:
return -ENOTSUP;
}
return sizeof(netopt_state_t);
}
static netopt_state_t _get_state(at86rf215_t *dev)
{
switch (dev->state) {
case AT86RF215_STATE_SLEEP:
return NETOPT_STATE_SLEEP;
case AT86RF215_STATE_RX_SEND_ACK:
return NETOPT_STATE_RX;
case AT86RF215_STATE_TX:
case AT86RF215_STATE_TX_WAIT_ACK:
return NETOPT_STATE_TX;
case AT86RF215_STATE_OFF:
return NETOPT_STATE_OFF;
case AT86RF215_STATE_IDLE:
default:
return NETOPT_STATE_IDLE;
}
}
static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len)
{
at86rf215_t *dev = (at86rf215_t *) netdev;
if (netdev == NULL) {
return -ENODEV;
}
/* getting these options doesn't require the transceiver to be responsive */
switch (opt) {
case NETOPT_STATE:
assert(max_len >= sizeof(netopt_state_t));
*((netopt_state_t *)val) = _get_state(dev);
return sizeof(netopt_state_t);
case NETOPT_PRELOADING:
if (dev->flags & AT86RF215_OPT_PRELOADING) {
*((netopt_enable_t *)val) = NETOPT_ENABLE;
}
else {
*((netopt_enable_t *)val) = NETOPT_DISABLE;
}
return sizeof(netopt_enable_t);
case NETOPT_PROMISCUOUSMODE:
if (dev->flags & AT86RF215_OPT_PROMISCUOUS) {
*((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->flags & AT86RF215_OPT_TELL_RX_START);
return sizeof(netopt_enable_t);
case NETOPT_RX_END_IRQ:
*((netopt_enable_t *)val) =
!!(dev->flags & AT86RF215_OPT_TELL_RX_END);
return sizeof(netopt_enable_t);
case NETOPT_TX_START_IRQ:
*((netopt_enable_t *)val) =
!!(dev->flags & AT86RF215_OPT_TELL_TX_START);
return sizeof(netopt_enable_t);
case NETOPT_TX_END_IRQ:
*((netopt_enable_t *)val) =
!!(dev->flags & AT86RF215_OPT_TELL_TX_END);
return sizeof(netopt_enable_t);
case NETOPT_CSMA:
*((netopt_enable_t *)val) =
!!(dev->flags & AT86RF215_OPT_CSMA);
return sizeof(netopt_enable_t);
case NETOPT_CSMA_RETRIES:
assert(max_len >= sizeof(uint8_t));
*((uint8_t *)val) = dev->csma_retries_max;
return sizeof(uint8_t);
case NETOPT_CSMA_MAXBE:
assert(max_len >= sizeof(uint8_t));
*((uint8_t *)val) = dev->csma_maxbe;
return sizeof(uint8_t);
case NETOPT_CSMA_MINBE:
assert(max_len >= sizeof(uint8_t));
*((uint8_t *)val) = dev->csma_minbe;
return sizeof(uint8_t);
case NETOPT_RETRANS:
assert(max_len >= sizeof(uint8_t));
*((uint8_t *)val) = dev->retries_max;
return sizeof(uint8_t);
case NETOPT_TX_RETRIES_NEEDED:
assert(max_len >= sizeof(uint8_t));
*((uint8_t *)val) = dev->retries_max - dev->retries;
return sizeof(uint8_t);
case NETOPT_AUTOACK:
*((netopt_enable_t *)val) =
!!(dev->flags & AT86RF215_OPT_AUTOACK);
return sizeof(netopt_enable_t);
case NETOPT_CHANNEL_PAGE:
assert(max_len >= sizeof(uint16_t));
((uint8_t *)val)[1] = 0;
((uint8_t *)val)[0] = is_subGHz(dev) ? 2 : 0;
return sizeof(uint16_t);
case NETOPT_AUTOCCA:
*((netopt_enable_t *)val) =
!!(dev->flags & AT86RF215_OPT_CCATX);
return sizeof(netopt_enable_t);
default:
/* Can still be handled in second switch */
break;
}
int res;
if (((res = netdev_ieee802154_get((netdev_ieee802154_t *)netdev, opt, val,
max_len)) >= 0) || (res != -ENOTSUP)) {
return res;
}
/* properties are not available if the device is sleeping */
if (dev->state == AT86RF215_STATE_SLEEP) {
return -ENOTSUP;
}
/* these options require the transceiver to be not sleeping*/
switch (opt) {
case NETOPT_TX_POWER:
assert(max_len >= sizeof(int16_t));
*((uint16_t *)val) = at86rf215_get_txpower(dev);
res = sizeof(uint16_t);
break;
case NETOPT_CCA_THRESHOLD:
assert(max_len >= sizeof(int8_t));
*((int8_t *)val) = at86rf215_get_cca_threshold(dev);
res = sizeof(int8_t);
break;
case NETOPT_IS_CHANNEL_CLR:
assert(max_len >= sizeof(netopt_enable_t));
*((netopt_enable_t *)val) = at86rf215_cca(dev);
res = sizeof(netopt_enable_t);
break;
case NETOPT_LAST_ED_LEVEL:
assert(max_len >= sizeof(int8_t));
*((int8_t *)val) = at86rf215_get_ed_level(dev);
res = sizeof(int8_t);
break;
case NETOPT_RANDOM:
at86rf215_get_random(dev, val, max_len);
res = max_len;
break;
default:
res = -ENOTSUP;
break;
}
return res;
}
static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len)
{
at86rf215_t *dev = (at86rf215_t *) netdev;
int res = -ENOTSUP;
if (dev == NULL) {
return -ENODEV;
}
/* no need to wake up the device when it's sleeping - all registers
are reset on wakeup. */
switch (opt) {
case NETOPT_ADDRESS:
assert(len <= sizeof(uint16_t));
at86rf215_set_addr_short(dev, 0, *((const uint16_t *)val));
/* don't set res to set netdev_ieee802154_t::short_addr */
break;
case NETOPT_ADDRESS_LONG:
assert(len <= sizeof(uint64_t));
at86rf215_set_addr_long(dev, *((const uint64_t *)val));
/* don't set res to set netdev_ieee802154_t::long_addr */
break;
case NETOPT_NID:
assert(len <= sizeof(uint16_t));
at86rf215_set_pan(dev, 0, *((const uint16_t *)val));
/* don't set res to set netdev_ieee802154_t::pan */
break;
case NETOPT_CHANNEL:
assert(len == sizeof(uint16_t));
uint16_t chan = *((const uint16_t *)val);
if (at86rf215_chan_valid(dev, chan) != chan) {
res = -EINVAL;
break;
}
at86rf215_set_chan(dev, chan);
/* don't set res to set netdev_ieee802154_t::chan */
break;
case NETOPT_TX_POWER:
assert(len <= sizeof(int16_t));
at86rf215_set_txpower(dev, *((const int16_t *)val));
res = sizeof(uint16_t);
break;
case NETOPT_STATE:
assert(len <= sizeof(netopt_state_t));
res = _set_state(dev, *((const netopt_state_t *)val));
break;
case NETOPT_AUTOACK:
at86rf215_set_option(dev, AT86RF215_OPT_AUTOACK,
((const bool *)val)[0]);
res = sizeof(netopt_enable_t);
break;
case NETOPT_AUTOCCA:
at86rf215_set_option(dev, AT86RF215_OPT_CCATX,
((const bool *)val)[0]);
res = sizeof(netopt_enable_t);
break;
case NETOPT_RETRANS:
assert(len <= sizeof(uint8_t));
dev->retries_max = *((const uint8_t *)val);
res = sizeof(uint8_t);
break;
case NETOPT_PRELOADING:
at86rf215_set_option(dev, AT86RF215_OPT_PRELOADING,
((const bool *)val)[0]);
res = sizeof(netopt_enable_t);
break;
case NETOPT_PROMISCUOUSMODE:
at86rf215_set_option(dev, AT86RF215_OPT_PROMISCUOUS,
((const bool *)val)[0]);
res = sizeof(netopt_enable_t);
break;
case NETOPT_RX_START_IRQ:
at86rf215_set_option(dev, AT86RF215_OPT_TELL_RX_START,
((const bool *)val)[0]);
res = sizeof(netopt_enable_t);
break;
case NETOPT_RX_END_IRQ:
at86rf215_set_option(dev, AT86RF215_OPT_TELL_RX_END,
((const bool *)val)[0]);
res = sizeof(netopt_enable_t);
break;
case NETOPT_TX_START_IRQ:
at86rf215_set_option(dev, AT86RF215_OPT_TELL_TX_START,
((const bool *)val)[0]);
res = sizeof(netopt_enable_t);
break;
case NETOPT_TX_END_IRQ:
at86rf215_set_option(dev, AT86RF215_OPT_TELL_TX_END,
((const bool *)val)[0]);
res = sizeof(netopt_enable_t);
break;
case NETOPT_CSMA:
at86rf215_set_option(dev, AT86RF215_OPT_CSMA,
((const bool *)val)[0]);
res = sizeof(netopt_enable_t);
break;
case NETOPT_CSMA_RETRIES:
assert(len <= sizeof(uint8_t));
dev->csma_retries_max = *((const uint8_t *)val);
res = sizeof(uint8_t);
break;
case NETOPT_CSMA_MAXBE:
assert(len <= sizeof(uint8_t));
dev->csma_maxbe = *((const uint8_t *)val);
res = sizeof(uint8_t);
break;
case NETOPT_CSMA_MINBE:
assert(len <= sizeof(uint8_t));
dev->csma_minbe = *((const uint8_t *)val);
res = sizeof(uint8_t);
break;
case NETOPT_CCA_THRESHOLD:
assert(len <= sizeof(int8_t));
at86rf215_set_cca_threshold(dev, *((const int8_t *)val));
res = sizeof(int8_t);
break;
default:
break;
}
if (res == -ENOTSUP) {
res = netdev_ieee802154_set((netdev_ieee802154_t *)netdev, opt, val, len);
}
return res;
}
static void _enable_tx2rx(at86rf215_t *dev)
{
uint8_t amcs = at86rf215_reg_read(dev, dev->BBC->RG_AMCS);
/* disable AACK, enable TX2RX */
amcs |= AMCS_TX2RX_MASK;
amcs &= ~AMCS_AACK_MASK;
at86rf215_reg_write(dev, dev->BBC->RG_AMCS, amcs);
}
static void _tx_end(at86rf215_t *dev, netdev_event_t event)
{
netdev_t *netdev = (netdev_t *)dev;
/* listen to non-ACK packets again */
if (dev->flags & AT86RF215_OPT_ACK_REQUESTED) {
dev->flags &= ~AT86RF215_OPT_ACK_REQUESTED;
at86rf215_filter_ack(dev, false);
}
at86rf215_tx_done(dev);
if (dev->flags & AT86RF215_OPT_TELL_TX_END) {
netdev->event_callback(netdev, event);
}
dev->timeout = 0;
dev->state = AT86RF215_STATE_IDLE;
}
static void _ack_timeout_cb(void* arg) {
at86rf215_t *dev = arg;
dev->timeout = AT86RF215_TIMEOUT_ACK;
msg_send_int(&dev->timer_msg, dev->timer_msg.sender_pid);
}
static void _backoff_timeout_cb(void* arg) {
at86rf215_t *dev = arg;
dev->timeout = AT86RF215_TIMEOUT_CSMA;
msg_send_int(&dev->timer_msg, dev->timer_msg.sender_pid);
}
static void _set_idle(at86rf215_t *dev)
{
dev->state = AT86RF215_STATE_IDLE;
uint8_t next_state;
if (dev->flags & AT86RF215_OPT_TX_PENDING) {
next_state = CMD_RF_TXPREP;
} else {
next_state = CMD_RF_RX;
}
at86rf215_rf_cmd(dev, next_state);
}
/* wake up the radio thread after ACK timeout */
static void _start_ack_timer(at86rf215_t *dev)
{
dev->timer_msg.type = NETDEV_MSG_TYPE_EVENT;
dev->timer_msg.sender_pid = thread_getpid();
dev->timer.arg = dev;
dev->timer.callback = _ack_timeout_cb;
xtimer_set(&dev->timer, dev->ack_timeout_usec);
}
/* wake up the radio thread after CSMA backoff period */
static void _start_backoff_timer(at86rf215_t *dev)
{
uint8_t be; /* backoff exponent */
uint32_t base;
/* energy detect interrupt happened -> hardware is still in RX mode */
at86rf215_get_random(dev, &base, sizeof(base));
be = ((dev->csma_retries_max - dev->csma_retries) - 1) + dev->csma_minbe;
if (be > dev->csma_maxbe) {
be = dev->csma_maxbe;
}
uint32_t csma_backoff_usec = ((1LU << be) - 1) * dev->csma_backoff_period;
/* limit the 32bit random value to the current backoff */
csma_backoff_usec = base % csma_backoff_usec;
DEBUG("Set CSMA backoff to %"PRIu32" (be %u min %u max %u base: %"PRIu32")\n",
csma_backoff_usec, be, dev->csma_minbe, dev->csma_maxbe, base);
dev->timer_msg.type = NETDEV_MSG_TYPE_EVENT;
dev->timer_msg.sender_pid = thread_getpid();
dev->timer.arg = dev;
dev->timer.callback = _backoff_timeout_cb;
xtimer_set(&dev->timer, csma_backoff_usec);
}
static inline bool _ack_frame_received(at86rf215_t *dev)
{
/* check if the sequence numbers (3rd byte) match */
return at86rf215_reg_read(dev, dev->BBC->RG_FBRXS + 2)
== at86rf215_reg_read(dev, dev->BBC->RG_FBTXS + 2);
}
static void _handle_ack_timeout(at86rf215_t *dev)
{
if (dev->retries) {
--dev->retries;
if (dev->flags & AT86RF215_OPT_CSMA) {
dev->csma_retries = dev->csma_retries_max;
if (!(dev->flags & AT86RF215_OPT_CCATX)){
dev->flags |= AT86RF215_OPT_CCA_PENDING;
}
}
dev->flags |= AT86RF215_OPT_TX_PENDING;
at86rf215_rf_cmd(dev, CMD_RF_TXPREP);
} else {
/* no retransmissions left */
_tx_end(dev, NETDEV_EVENT_TX_NOACK);
}
}
/* clear the other IRQ if the sibling is not ready yet */
static inline void _clear_sibling_irq(at86rf215_t *dev) {
if (is_subGHz(dev)) {
at86rf215_reg_read(dev, RG_RF24_IRQS);
at86rf215_reg_read(dev, RG_BBC1_IRQS);
} else {
at86rf215_reg_read(dev, RG_RF09_IRQS);
at86rf215_reg_read(dev, RG_BBC0_IRQS);
}
}
static void _handle_edc(at86rf215_t *dev)
{
netdev_t *netdev = (netdev_t *) dev;
/* In CCATX mode this function is only triggered if busy */
if (!(dev->flags & AT86RF215_OPT_CCATX)) {
/* channel clear -> TX */
if ((int8_t)at86rf215_reg_read(dev, dev->RF->RG_EDV) <= at86rf215_get_cca_threshold(dev)) {
dev->flags &= ~AT86RF215_OPT_CCA_PENDING;
at86rf215_enable_baseband(dev);
at86rf215_rf_cmd(dev, CMD_RF_TXPREP);
return;
}
}
DEBUG("CSMA busy\n");
if (dev->csma_retries) {
--dev->csma_retries;
_start_backoff_timer(dev);
} else {
/* channel busy and no retries left */
dev->flags &= ~(AT86RF215_OPT_CCA_PENDING | AT86RF215_OPT_TX_PENDING);
dev->state = AT86RF215_STATE_IDLE;
at86rf215_enable_baseband(dev);
at86rf215_tx_done(dev);
netdev->event_callback(netdev, NETDEV_EVENT_TX_MEDIUM_BUSY);
DEBUG("CSMA give up");
/* radio is still in RX mode, tx_done sets IDLE state */
}
}
/* executed in the radio thread */
static void _isr(netdev_t *netdev)
{
at86rf215_t *dev = (at86rf215_t *) netdev;
uint8_t bb_irq_mask, rf_irq_mask;
uint8_t bb_irqs_enabled = BB_IRQ_RXFE | BB_IRQ_TXFE;
/* not using IRQMM because we want to know about AGCH */
if (dev->flags & AT86RF215_OPT_TELL_RX_START) {
bb_irqs_enabled |= BB_IRQ_RXAM;
}
rf_irq_mask = at86rf215_reg_read(dev, dev->RF->RG_IRQS);
bb_irq_mask = at86rf215_reg_read(dev, dev->BBC->RG_IRQS);
uint8_t timeout = dev->timeout;
if (timeout) {
dev->timeout = 0;
}
/* mark AGC Hold bit */
if (bb_irq_mask & BB_IRQ_AGCH) {
dev->flags |= AT86RF215_OPT_AGCH;
}
/* clear AGC Hold bit */
if (bb_irq_mask & BB_IRQ_AGCR) {
dev->flags &= ~AT86RF215_OPT_AGCH;
}
/* we got here because of CMSA timeout */
if (timeout & AT86RF215_TIMEOUT_CSMA) {
timeout = 0;
if (!(dev->flags & AT86RF215_OPT_CCATX)) {
at86rf215_reg_write(dev, dev->RF->RG_EDC, 1);
} else {
at86rf215_rf_cmd(dev, CMD_RF_TXPREP);
}
}
/* If the interrupt pin is still high, there was an IRQ on the other radio */
if (gpio_read(dev->params.int_pin)) {
if (dev->sibling && dev->sibling->state != AT86RF215_STATE_OFF) {
netdev->event_callback((netdev_t *) dev->sibling, NETDEV_EVENT_ISR);
} else {
_clear_sibling_irq(dev);
}
}
/* exit early if the interrupt was not for this interface */
if (!((bb_irq_mask & bb_irqs_enabled) ||
(rf_irq_mask & (RF_IRQ_EDC | RF_IRQ_TRXRDY)) || timeout)) {
return;
}
/* check if the received packet has the ACK request bit set */
bool rx_ack_req;
if (bb_irq_mask & BB_IRQ_RXFE) {
rx_ack_req = at86rf215_reg_read(dev, dev->BBC->RG_FBRXS) & IEEE802154_FCF_ACK_REQ;
} else {
rx_ack_req = 0;
}
if (dev->flags & AT86RF215_OPT_CCA_PENDING) {
/* Start ED or handle result */
if (rf_irq_mask & RF_IRQ_EDC) {
_handle_edc(dev);
} else if (rf_irq_mask & RF_IRQ_TRXRDY) {
/* disable baseband for energy detection */
at86rf215_disable_baseband(dev);
/* switch to state RX for energy detection */
at86rf215_rf_cmd(dev, CMD_RF_RX);
/* start energy measurement */
at86rf215_reg_write(dev, dev->RF->RG_EDC, 1);
}
} else if (dev->flags & AT86RF215_OPT_TX_PENDING) {
/* start transmitting the frame */
if (rf_irq_mask & RF_IRQ_TRXRDY) {
/* automatically switch to RX when TX is done */
_enable_tx2rx(dev);
/* only listen for ACK frames */
if (dev->flags & AT86RF215_OPT_ACK_REQUESTED) {
at86rf215_filter_ack(dev, true);
}
/* switch to state TX */
dev->state = AT86RF215_STATE_TX;
dev->flags &= ~AT86RF215_OPT_TX_PENDING;
at86rf215_rf_cmd(dev, CMD_RF_TX);
/* This also tells the upper layer about retransmissions - should it be like that? */
if (netdev->event_callback &&
(dev->flags & AT86RF215_OPT_TELL_TX_START)) {
netdev->event_callback(netdev, NETDEV_EVENT_TX_STARTED);
}
}
}
/* CCATX signals medium busy */
if ((dev->flags & AT86RF215_OPT_CCATX) && (rf_irq_mask & RF_IRQ_EDC) && (bb_irq_mask & BB_IRQ_TXFE)) {
bb_irq_mask &= ~BB_IRQ_TXFE;
rf_irq_mask &= ~RF_IRQ_EDC;
_handle_edc(dev);
}
int iter = 0;
while (timeout || (bb_irq_mask & (BB_IRQ_RXFE | BB_IRQ_TXFE))) {
/* This should never happen */
if (++iter > 3) {
puts("AT86RF215: stuck in ISR");
printf("\tnum_channels: %d\n", dev->num_chans);
printf("\tHW: %s\n", at86rf215_hw_state2a(at86rf215_get_rf_state(dev)));
printf("\tSW: %s\n", at86rf215_sw_state2a(dev->state));
printf("\trf_irq_mask: %x\n", rf_irq_mask);
printf("\tbb_irq_mask: %x\n", bb_irq_mask);
printf("\ttimeout: %x\n", timeout);
break;
}
switch (dev->state) {
case AT86RF215_STATE_IDLE:
if (!(bb_irq_mask & (BB_IRQ_RXFE | BB_IRQ_RXAM))) {
DEBUG("IDLE: only RXFE/RXAM expected (%x)\n", bb_irq_mask);
break;
}
if ((bb_irq_mask & BB_IRQ_RXAM) &&
(dev->flags & AT86RF215_OPT_TELL_RX_END)) {
/* will be executed in the same thread */
netdev->event_callback(netdev, NETDEV_EVENT_RX_STARTED);
}
bb_irq_mask &= ~BB_IRQ_RXAM;
if (!(bb_irq_mask & BB_IRQ_RXFE)) {
break;
}
bb_irq_mask &= ~BB_IRQ_RXFE;
if (dev->flags & AT86RF215_OPT_TELL_RX_END) {
/* will be executed in the same thread */
netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE);
}
if (rx_ack_req) {
dev->state = AT86RF215_STATE_RX_SEND_ACK;
break;
}
_set_idle(dev);
break;
case AT86RF215_STATE_RX_SEND_ACK:
if (!(bb_irq_mask & BB_IRQ_TXFE)) {
DEBUG("RX_SEND_ACK: only TXFE expected (%x)\n", bb_irq_mask);
break;
}
bb_irq_mask &= ~BB_IRQ_TXFE;
_set_idle(dev);
break;
case AT86RF215_STATE_TX:
if (!(bb_irq_mask & BB_IRQ_TXFE)) {
DEBUG("TX: only TXFE expected (%x)\n", bb_irq_mask);
break;
}
bb_irq_mask &= ~BB_IRQ_TXFE;
if (dev->flags & AT86RF215_OPT_ACK_REQUESTED) {
dev->state = AT86RF215_STATE_TX_WAIT_ACK;
_start_ack_timer(dev);
} else {
_tx_end(dev, NETDEV_EVENT_TX_COMPLETE);
}
break;
case AT86RF215_STATE_TX_WAIT_ACK:
if (!((bb_irq_mask & BB_IRQ_RXFE) || timeout)) {
DEBUG("TX_WAIT_ACK: only RXFE or timeout expected (%x)\n", bb_irq_mask);
break;
}
/* handle timeout case */
if (!(bb_irq_mask & BB_IRQ_RXFE)) {
goto timeout;
}
bb_irq_mask &= ~BB_IRQ_RXFE;
if (_ack_frame_received(dev)) {
timeout = 0;
xtimer_remove(&dev->timer);
_tx_end(dev, NETDEV_EVENT_TX_COMPLETE);
at86rf215_rf_cmd(dev, CMD_RF_RX);
break;
}
/* we got a spurious ACK */
if (!timeout) {
at86rf215_rf_cmd(dev, CMD_RF_RX);
break;
}
timeout:
/* For a yet unknown reason, the device spends an excessive amount of time
* transmitting the preamble in non-legacy modes.
* This means the calculated ACK timeouts are often too short.
* To mitigate this, postpone the ACK timeout if the device is still RXign
* the ACK frame when the timeout expires.
*/
if (dev->flags & AT86RF215_OPT_AGCH) {
DEBUG("[at86rf215] Ack timeout postponed\n");
_start_ack_timer(dev);
} else {
DEBUG("[at86rf215] Ack timeout\n");
_handle_ack_timeout(dev);
}
timeout = 0;
break;
}
}
}

View File

@ -0,0 +1,277 @@
/*
* 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 Configuration of the MR-O-QPSK PHY on the AT86RF215 chip
*
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
* @}
*/
#include "at86rf215.h"
#include "at86rf215_internal.h"
#include "debug.h"
/* IEEE Std 802.15.4g™-2012 Amendment 3
* Table 68dTotal number of channels and first channel center frequencies for SUN PHYs
* Table 68eCenter frequencies for the MR-O-QPSK PHY operating in the 868870 MHz band
*/
/* currently only EU-868 MHz band is supported */
#define QPSK_CHANNEL_SPACING_SUBGHZ (650U) /* kHz */
#define QPSK_CENTER_FREQUENCY_SUBGHZ (868300U) /* Hz */
#define QPSK_CHANNEL_SPACING_24GHZ (5000U) /* kHz */
#define QPSK_CENTER_FREQUENCY_24GHZ (2350000U - CCF0_24G_OFFSET) /* Hz */
/* Table 6-103. O-QPSK Transmitter Frontend Configuration */
static uint8_t _TXCUTC_PARAMP(uint8_t chips)
{
switch (chips) {
case BB_FCHIP100:
return RF_PARAMP32U;
case BB_FCHIP200:
return RF_PARAMP16U;
case BB_FCHIP1000:
case BB_FCHIP2000:
return RF_PARAMP4U;
}
return 0;
}
/* Table 6-103. O-QPSK Transmitter Frontend Configuration */
static uint8_t _TXCUTC_LPFCUT(uint8_t chips)
{
switch (chips) {
case BB_FCHIP100:
case BB_FCHIP200:
return RF_FLC400KHZ;
case BB_FCHIP1000:
case BB_FCHIP2000:
return RF_FLC1000KHZ;
}
return 0;
}
/* Table 6-103. O-QPSK Transmitter Frontend Configuration */
static uint8_t _TXDFE_SR(uint8_t chips)
{
switch (chips) {
case BB_FCHIP100:
return RF_SR_400K;
case BB_FCHIP200:
return RF_SR_800K;
case BB_FCHIP1000:
return RF_SR_4000K;
case BB_FCHIP2000:
return RF_SR_4000K;
}
return 0;
}
/* Table 6-103. O-QPSK Transmitter Frontend Configuration */
static uint8_t _TXDFE_RCUT(uint8_t chips)
{
return (chips == BB_FCHIP2000 ? RF_RCUT_FS_BY_2 : RF_RCUT_FS_BY_2P6);
}
/* Table 6-105. O-QPSK Receiver Frontend Configuration (Filter Settings) */
static uint8_t _RXBWC_BW(uint8_t chips)
{
switch (chips) {
case BB_FCHIP100:
return RF_BW160KHZ_IF250KHZ;
case BB_FCHIP200:
return RF_BW250KHZ_IF250KHZ;
case BB_FCHIP1000:
return RF_BW1000KHZ_IF1000KHZ;
case BB_FCHIP2000:
return RF_BW2000KHZ_IF2000KHZ;
}
return 0;
}
/* Table 6-105. O-QPSK Receiver Frontend Configuration (Filter Settings) */
static uint8_t _RXDFE_SR(uint8_t chips)
{
switch (chips) {
case BB_FCHIP100:
return RF_SR_400K;
case BB_FCHIP200:
return RF_SR_800K;
case BB_FCHIP1000:
case BB_FCHIP2000:
return RF_SR_4000K;
}
return 0;
}
/* Table 6-105. O-QPSK Receiver Frontend Configuration (Filter Settings) */
static uint8_t _RXDFE_RCUT(uint8_t chips)
{
switch (chips) {
case BB_FCHIP100:
case BB_FCHIP200:
return RF_RCUT_FS_BY_5P3;
case BB_FCHIP1000:
return RF_RCUT_FS_BY_8;
case BB_FCHIP2000:
return RF_RCUT_FS_BY_4;
}
return 0;
}
/* Table 6-106. O-QPSK Receiver Frontend Configuration (AGC Settings) */
static inline uint8_t _AGCC(uint8_t chips)
{
if (chips > BB_FCHIP200) {
/* 32 samples */
return (2 << AGCC_AVGS_SHIFT) | AGCC_EN_MASK;
} else {
return AGCC_EN_MASK;
}
}
/* Table 6-100. MR-O-QPSK Modes */
static uint32_t _get_bitrate(uint8_t chips, uint8_t mode)
{
switch (chips) {
case BB_FCHIP100:
return 6250 * (1 << mode);
case BB_FCHIP200:
return 12500 * (1 << mode);
case BB_FCHIP1000:
case BB_FCHIP2000:
return mode ? 125000 * (1 << (mode - 1)) : 31250;
}
return 0;
}
static void _set_chips(at86rf215_t *dev, uint8_t chips)
{
/* enable direct modulation if the chip supports it */
uint8_t direct_modulation;
if (chips < BB_FCHIP1000 && at86rf215_reg_read(dev, RG_RF_VN) >= 3) {
direct_modulation = TXDFE_DM_MASK;
} else {
direct_modulation = 0;
}
/* Set Receiver Bandwidth */
at86rf215_reg_write(dev, dev->RF->RG_RXBWC, _RXBWC_BW(chips));
/* Set fS; fCUT for RX */
at86rf215_reg_write(dev, dev->RF->RG_RXDFE, _RXDFE_SR(chips)
| _RXDFE_RCUT(chips));
/* Set Power Amplifier Ramp Time; fLPCUT */
at86rf215_reg_write(dev, dev->RF->RG_TXCUTC, _TXCUTC_PARAMP(chips)
| _TXCUTC_LPFCUT(chips));
/* Set fS; fCUT for TX */
at86rf215_reg_write(dev, dev->RF->RG_TXDFE, _TXDFE_SR(chips)
| _TXDFE_RCUT(chips)
| direct_modulation);
/* set receiver gain target according to data sheet, p125 */
at86rf215_reg_write(dev, dev->RF->RG_AGCS, 3 << AGCS_TGT_SHIFT);
at86rf215_reg_write(dev, dev->RF->RG_AGCC, _AGCC(chips));
/* use RC-0.8 shaping */
at86rf215_reg_write(dev, dev->BBC->RG_OQPSKC0, chips | direct_modulation);
}
static void _set_legacy(at86rf215_t *dev, bool high_rate)
{
uint8_t chips;
/* enable/disable legacy high data rate, only use SFD_1 */
if (high_rate) {
at86rf215_reg_write(dev, dev->BBC->RG_OQPSKC3, OQPSKC3_HRLEG_MASK);
} else {
at86rf215_reg_write(dev, dev->BBC->RG_OQPSKC3, 0);
}
if (is_subGHz(dev)) {
chips = BB_FCHIP1000;
} else {
chips = BB_FCHIP2000;
}
_set_chips(dev, chips);
at86rf215_reg_write(dev, dev->BBC->RG_OQPSKPHRTX, AT86RF215_OQPSK_MODE_LEGACY);
at86rf215_reg_write(dev, dev->BBC->RG_OQPSKC2,
RXM_LEGACY_OQPSK /* receive mode, legacy O-QPSK */
| OQPSKC2_FCSTLEG_MASK /* 16 bit frame checksum */
| OQPSKC2_ENPROP_MASK); /* enable RX of proprietary modes */
}
static void _set_ack_timeout(at86rf215_t *dev, uint8_t chips, uint8_t mode)
{
dev->ack_timeout_usec = AT86RF215_ACK_PERIOD_IN_BITS * US_PER_SEC / _get_bitrate(chips, mode);
DEBUG("[%s] ACK timeout: %"PRIu32" µs\n", "O-QPSK", dev->ack_timeout_usec);
}
static void _set_csma_backoff_period(at86rf215_t *dev, uint8_t chips, uint8_t mode)
{
dev->csma_backoff_period = AT86RF215_BACKOFF_PERIOD_IN_BITS * US_PER_SEC / _get_bitrate(chips, mode);
DEBUG("[%s] CSMA BACKOFF: %"PRIu32" µs\n", "O-QPSK", dev->csma_backoff_period);
}
void _end_configure_OQPSK(at86rf215_t *dev)
{
/* set channel spacing with 25 kHz resolution */
if (is_subGHz(dev)) {
at86rf215_reg_write(dev, dev->RF->RG_CS, QPSK_CHANNEL_SPACING_SUBGHZ / 25);
at86rf215_reg_write16(dev, dev->RF->RG_CCF0L, QPSK_CENTER_FREQUENCY_SUBGHZ / 25);
} else {
at86rf215_reg_write(dev, dev->RF->RG_CS, QPSK_CHANNEL_SPACING_24GHZ / 25);
at86rf215_reg_write16(dev, dev->RF->RG_CCF0L, QPSK_CENTER_FREQUENCY_24GHZ / 25);
}
/* lowest preamble detection sensitivity, enable receiver override */
at86rf215_reg_write(dev, dev->BBC->RG_OQPSKC1, OQPSKC1_RXO_MASK | OQPSKC1_RXOLEG_MASK);
/* make sure the channel config is still valid */
dev->num_chans = is_subGHz(dev) ? 1 : 16;
dev->netdev.chan = at86rf215_chan_valid(dev, dev->netdev.chan);
at86rf215_reg_write16(dev, dev->RF->RG_CNL, dev->netdev.chan);
at86rf215_enable_radio(dev, BB_MROQPSK);
}
int at86rf215_configure_legacy_OQPSK(at86rf215_t *dev, bool high_rate)
{
/* select 'mode' that would result in the approprate MR-O-QPSK data rate */
uint8_t mode = high_rate ? 3 : 2;
uint8_t chips = is_subGHz(dev) ? BB_FCHIP1000 : BB_FCHIP2000;
at86rf215_await_state_end(dev, RF_STATE_TX);
/* disable radio */
at86rf215_reg_write(dev, dev->BBC->RG_PC, 0);
_set_legacy(dev, high_rate);
_set_csma_backoff_period(dev, chips, mode);
_set_ack_timeout(dev, chips, mode);
_end_configure_OQPSK(dev);
return 0;
}

View File

@ -0,0 +1,401 @@
/*
* 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>
* @}
*/
#ifndef AT86RF215_INTERNAL_H
#define AT86RF215_INTERNAL_H
#include <stdint.h>
#include "at86rf215.h"
#include "at86rf215_registers.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Minimum reset pulse width (tRST) in µs
*/
#define AT86RF215_RESET_PULSE_WIDTH_US (16U)
/**
* @brief The typical transition time to TRX_OFF after reset (tPOWERON) in µs
*/
#define AT86RF215_RESET_DELAY_US (16U)
/** Default energy detect threshold for CSMA (reset value) */
#define AT86RF215_EDT_DEFAULT (-84) /* dBm */
/**
* This is used to calculate the csma backoff based on the bitrate.
*/
/** 20 symbols is the std period length */
#define AT86RF215_BACKOFF_PERIOD_IN_SYMBOLS (20U)
/** in 802.15.4 oqpsk each symble is 4 bits, not about the others */
#define AT86RF215_BACKOFF_PERIOD_IN_BITS (AT86RF215_BACKOFF_PERIOD_IN_SYMBOLS * 4)
/**
* Default Parameters for 802.15.4 retransmissions & CSMA
* @{
*/
#define AT86RF215_RETRIES_MAX_DEFAULT (3)
#define AT86RF215_CSMA_RETRIES_MAX_DEFAULT (4)
#define AT86RF215_CSMA_MIN_BE_DEFAULT (3)
#define AT86RF215_CSMA_MAX_BE_DEFAULT (5)
/** @} */
/** For the SUN PHYs, the value is 1 ms expressed in symbol periods, rounded up to the next
integer number of symbol periods using the ceiling() function */
#define AT86RF215_TURNAROUND_TIME_US (1 * US_PER_MS)
/** An ACK consists of 5 payload bytes */
#define AT86RF215_ACK_PSDU_BYTES (5)
/**
* This is used to calculate the ACK timeout based on the bitrate.
* AT86RF233 uses an ACK timeout of 54 symbol periods, or 864 µs @ 250 kbit/s
* -> 864µs * 250kbit/s = 216 bit */
#define AT86RF215_ACK_PERIOD_IN_SYMBOLS (54U)
/** in 802.15.4 oqpsk each symble is 4 bits, not about the others */
#define AT86RF215_ACK_PERIOD_IN_BITS (AT86RF215_ACK_PERIOD_IN_SYMBOLS * 4)
#define AT86RF215_OQPSK_MODE_LEGACY (0x1) /**< legacy mode, 250 kbit/s */
#define AT86RF215_OQPSK_MODE_LEGACY_HDR (0x3) /**< legacy mode, high data rate */
#define AT86RF215_MR_OQPSK_MODE(n) ((n) << OQPSKPHRTX_MOD_SHIFT) /**< MR-QPSK */
/**
* @brief Perform a reset of the entire chip.
*
* @param dev device to reset, will also reset sibling device
* @return 0 on success, error if device is not available
*/
int at86rf215_hardware_reset(at86rf215_t *dev);
/**
* @brief Write to a register at address `addr` from device `dev`.
*
* @param[in] dev device to write to
* @param[in] reg address of the register to write
* @param[in] val value to write to the given register
*/
void at86rf215_reg_write(const at86rf215_t *dev, uint16_t reg, uint8_t val);
/**
* @brief Write a chunk of data into the memory of the given device
*
* @param[in] dev device to write to
* @param[in] reg address in the device to write to
* @param[in] data data to copy into the device
* @param[in] len number of bytes to write into the device
*/
void at86rf215_reg_write_bytes(const at86rf215_t *dev, uint16_t reg, const void *data, size_t len);
/**
* @brief Read from a register at address `addr` from device `dev`.
*
* @param[in] dev device to read from
* @param[in] reg address of the register to read
*
* @return the value of the specified register
*/
uint8_t at86rf215_reg_read(const at86rf215_t *dev, uint16_t reg);
/**
* @brief Read a chunk of data from the memory of the given device
*
* @param[in] dev device to read from
* @param[in] reg starting address to read from
* @param[out] data buffer to read data into
* @param[in] len number of bytes to read
*/
void at86rf215_reg_read_bytes(const at86rf215_t *dev, uint16_t reg, void *data, size_t len);
/**
* @brief Enable / Disable the ACK filter
*
* @param[in] dev device to configure
* @param[in] on if true, only ACK frames are received
* if false, only non-ACK frames are received
*/
void at86rf215_filter_ack(at86rf215_t *dev, bool on);
/**
* @brief Read random data from the RNG
*
* @pre The device has to be in state RX with PLL locked.
*
* @param[in] dev device to configure
* @param[out] data buffer to copy the random data to
* @param[in] len number of random bytes to store in data
*/
void at86rf215_get_random(at86rf215_t *dev, void *data, size_t len);
/**
* @brief Configure the radio to make use of O-QPSK modulation.
* The rate mode may be
* - 0 for compatibility with first-gen 802.15.4 devices (250 kbit/s)
* - 1 for compatibility with the proprietary high-data rate modes of at86rf2xx
*
* @param[in] dev device to configure
* @param[in] high_rate use proprietary high data rate compatible with at86rf2xx
*
* @return 0 on success, error otherwise
*/
int at86rf215_configure_legacy_OQPSK(at86rf215_t *dev, bool high_rate);
/**
* @brief Check if a channel number is valid.
* The function takes the current frequency band and modulation into
* account to determine if `chan` would be a legal channel number.
* If so, it is returned unmodified. Otherwise the next closest legal
* channel number is returned.
*
* @note This function does not change the configuration.
*
* @param[in] dev device to check against
* @param[in] chan the channel number to check
*
* @return If the channel number is legal, `chan` is returned.
* Otherwise the next closest legal channel number is
* returned.
*/
uint16_t at86rf215_chan_valid(at86rf215_t *dev, uint16_t chan);
/**
* @brief Converts radio state into human readable string.
*
* @param[in] state radio state
*
* @return fixed string representation of the radio state
*/
const char* at86rf215_hw_state2a(uint8_t state);
/**
* @brief Converts state machine state into human readable string.
*
* @param[in] state state of the driver's state machine
*
* @return fixed string representation of the state machine state
*/
const char* at86rf215_sw_state2a(at86rf215_state_t state);
/**
* @brief Reads the contents of `reg`, apply `val` with a bitwise AND
* and then writes the result back to `reg`.
*
* @param[in] dev device to write to
* @param[in] reg register to write to
* @param[in] val value to bitwise AND with the register content
*/
static inline void at86rf215_reg_and(const at86rf215_t *dev, uint16_t reg, uint8_t val)
{
val &= at86rf215_reg_read(dev, reg);
at86rf215_reg_write(dev, reg, val);
}
/**
* @brief Reads the contents of `reg`, apply `val` with a bitwise OR
* and then writes the result back to `reg`.
*
* @param[in] dev device to write to
* @param[in] reg register to write to
* @param[in] val value to bitwise OR with the register content
*/
static inline void at86rf215_reg_or(const at86rf215_t *dev, uint16_t reg, uint8_t val)
{
val |= at86rf215_reg_read(dev, reg);
at86rf215_reg_write(dev, reg, val);
}
/**
* @brief Write a 16-bit word to a register at address `addr` from device `dev`.
*
* @param[in] dev device to write to
* @param[in] reg address of the register to write
* @param[in] val value to write to the given register
*/
static inline void at86rf215_reg_write16(const at86rf215_t *dev, uint16_t reg, uint16_t val)
{
at86rf215_reg_write_bytes(dev, reg, &val, sizeof(val));
}
/**
* @brief Read a 16-bit word from a register at address `addr` from device `dev`.
*
* @param[in] dev device to read from
* @param[in] reg address of the register to read
*
* @return the value of the specified register
*/
static inline uint16_t at86rf215_reg_read16(const at86rf215_t *dev, uint16_t reg)
{
uint16_t value;
at86rf215_reg_read_bytes(dev, reg, &value, sizeof(value));
return value;
}
/**
* @brief Issue a radio command to the device
*
* @param[in] dev device to configure
* @param[in] rf_cmd command to send
*/
static inline void at86rf215_rf_cmd(const at86rf215_t *dev, uint8_t rf_cmd)
{
at86rf215_reg_write(dev, dev->RF->RG_CMD, rf_cmd);
}
/**
* @brief Get the radio state of the device
*
* @param[in] dev device to read from
*
* @return the current radio state
*/
static inline uint8_t at86rf215_get_rf_state(const at86rf215_t *dev)
{
return at86rf215_reg_read(dev, dev->RF->RG_STATE) & STATE_STATE_MASK;
}
/**
* @brief Blocks until the device has reached the given state
*
* @param[in] dev device to poll
* @param[in] state the expected state
*/
static inline void at86rf215_await_state(const at86rf215_t *dev, uint8_t state)
{
while (at86rf215_get_rf_state(dev) != state) {}
}
/**
* @brief Blocks until the device has reached the given state
*
* @param[in] dev device to poll
* @param[in] state the expected state
*/
static inline void at86rf215_await_state_end(const at86rf215_t *dev, uint8_t state)
{
while (at86rf215_get_rf_state(dev) == state) {}
}
/**
* @brief Switch device back to IDLE-RX from non-RX idle
*
* @param[in] dev device to update
* @param[out] old_state pointer to store the previous state, may be NULL
*
* @return true if the operation was possible
*/
bool at86rf215_set_rx_from_idle(at86rf215_t *dev, uint8_t *old_state);
/**
* @brief Switch device to non-RX idle state from RX
*
* @param[in] dev device to update
* @param[out] state the new state (may be CMD_RF_TRXOFF or CMD_RF_SLEEP)
*
* @return true if the operation was possible
*/
bool at86rf215_set_idle_from_rx(at86rf215_t *dev, uint8_t state);
/**
* @brief Enable the baseband processor of the device
*
* @param[in] dev device to enable the baseband on
*/
static inline void at86rf215_enable_baseband(const at86rf215_t *dev)
{
at86rf215_reg_or(dev, dev->BBC->RG_PC, PC_BBEN_MASK);
}
/**
* @brief Disable the baseband processor of the device
*
* @param[in] dev device to disable the baseband on
*/
static inline void at86rf215_disable_baseband(const at86rf215_t *dev) {
at86rf215_reg_and(dev, dev->BBC->RG_PC, ~PC_BBEN_MASK);
}
/**
* @brief Enable the radio hardware with a given modulation.
*
* @param[in] dev device to enable
* @param[in] modulation modulation to configure on the radio
*/
static inline void at86rf215_enable_radio(at86rf215_t *dev, uint8_t modulation)
{
/* 16 bit frame-checksum, baseband enabled, checksum calculated by chip,
frames with invalid cs are dropped */
at86rf215_reg_write(dev, dev->BBC->RG_PC, modulation | PC_BBEN_MASK
| PC_FCST_MASK | PC_TXAFCS_MASK
| PC_FCSFE_MASK);
}
/**
* @brief Internal convenience function to disable reduced power
* consumption (RPC) for energy detection.
*
* @param[in] dev device to configure
*/
void at86rf215_disable_rpc(at86rf215_t *dev);
/**
* @brief Internal convenience function to re-enable reduced power
* consumption (RPC) after energy detection.
*
* @param[in] dev device to configure
*/
void at86rf215_enable_rpc(at86rf215_t *dev);
/**
* @brief Notify the driver and stack about a change in transmission mode
* which may result in a change of PDU.
*
* @param[in] dev device that changed it's mode
* @param[in] new_mode the new transmission mode
*/
bool at86rf215_switch_mode(at86rf215_t *dev, uint8_t new_mode);
/**
* @brief Block while the device is busy sending
*
* @param[in] dev device that might be TXing
*/
void at86rf215_block_while_busy(at86rf215_t *dev);
/**
* @brief Checks whether the device operates in the sub-GHz band.
*
* @param[in] dev device to read from
*
* @return true if the device operates in the sub-GHz band
* false if the device operates in the 2.4-GHz band
*/
static inline bool is_subGHz(const at86rf215_t *dev)
{
return dev->RF->RG_IRQS == RG_RF09_IRQS;
}
#ifdef __cplusplus
}
#endif
#endif /* AT86RF215_INTERNAL_H */
/** @} */

View File

@ -0,0 +1,39 @@
/*
* 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 Netdev interface to AT86RF215 driver
*
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
* @}
*/
#ifndef AT86RF215_NETDEV_H
#define AT86RF215_NETDEV_H
#include "net/netdev.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Reference to the netdev device driver struct
*/
extern const netdev_driver_t at86rf215_driver;
#ifdef __cplusplus
}
#endif
#endif /* AT86RF215_NETDEV_H */
/** @} */

View File

@ -0,0 +1,72 @@
/*
* 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 Default configuration for the AT86RF215 driver
*
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
* @}
*/
#ifndef AT86RF215_PARAMS_H
#define AT86RF215_PARAMS_H
#include "board.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name Set default configuration parameters for the AT86RF215 driver
* Example config for EXT3 on same54-xpro
* @{
*/
#ifndef AT86RF215_PARAM_SPI
#define AT86RF215_PARAM_SPI (SPI_DEV(0))
#endif
#ifndef AT86RF215_PARAM_SPI_CLK
#define AT86RF215_PARAM_SPI_CLK (SPI_CLK_5MHZ)
#endif
#ifndef AT86RF215_PARAM_CS
#define AT86RF215_PARAM_CS (GPIO_PIN(3, 14))
#endif
#ifndef AT86RF215_PARAM_INT
#define AT86RF215_PARAM_INT (GPIO_PIN(3, 30))
#endif
#ifndef AT86RF215_PARAM_RESET
#define AT86RF215_PARAM_RESET (GPIO_PIN(4, 10))
#endif
#ifndef AT86RF215_PARAMS
#define AT86RF215_PARAMS { .spi = AT86RF215_PARAM_SPI, \
.spi_clk = AT86RF215_PARAM_SPI_CLK, \
.cs_pin = AT86RF215_PARAM_CS, \
.int_pin = AT86RF215_PARAM_INT, \
.reset_pin = AT86RF215_PARAM_RESET }
#endif
/**@}*/
/**
* @brief AT86RF215 configuration
*/
static const at86rf215_params_t at86rf215_params[] =
{
AT86RF215_PARAMS
};
#ifdef __cplusplus
}
#endif
#endif /* AT86RF215_PARAMS_H */
/** @} */

View File

@ -0,0 +1,618 @@
/*
* 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 Register Definitions for the AT86RF215 chip
*
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
* @}
*/
#ifndef AT86RF215_REGISTERS_H
#define AT86RF215_REGISTERS_H
#include <stdint.h>
#include "vendor/at86rf215.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Radio Frontend registers
* @{
*/
struct at86rf215_RF_regs {
uint16_t RG_IRQS; /**< see datasheet */
uint16_t RG_IRQM; /**< see datasheet */
uint16_t RG_AUXS; /**< see datasheet */
uint16_t RG_STATE; /**< see datasheet */
uint16_t RG_CMD; /**< see datasheet */
uint16_t RG_CS; /**< see datasheet */
uint16_t RG_CCF0L; /**< see datasheet */
uint16_t RG_CCF0H; /**< see datasheet */
uint16_t RG_CNL; /**< see datasheet */
uint16_t RG_CNM; /**< see datasheet */
uint16_t RG_RXBWC; /**< see datasheet */
uint16_t RG_RXDFE; /**< see datasheet */
uint16_t RG_AGCC; /**< see datasheet */
uint16_t RG_AGCS; /**< see datasheet */
uint16_t RG_RSSI; /**< see datasheet */
uint16_t RG_EDC; /**< see datasheet */
uint16_t RG_EDD; /**< see datasheet */
uint16_t RG_EDV; /**< see datasheet */
uint16_t RG_RNDV; /**< see datasheet */
uint16_t RG_TXCUTC; /**< see datasheet */
uint16_t RG_TXDFE; /**< see datasheet */
uint16_t RG_PAC; /**< see datasheet */
uint16_t RG_PADFE; /**< see datasheet */
uint16_t RG_PLL; /**< see datasheet */
uint16_t RG_PLLCF; /**< see datasheet */
uint16_t RG_TXCI; /**< see datasheet */
uint16_t RG_TXCQ; /**< see datasheet */
uint16_t RG_TXDACI; /**< see datasheet */
uint16_t RG_TXDACQ; /**< see datasheet */
};
/** @} */
/**
* @brief Base Band Controller registers
* @{
*/
struct at86rf215_BBC_regs {
uint16_t RG_IRQS; /**< see datasheet */
uint16_t RG_FBRXS; /**< see datasheet */
uint16_t RG_FBRXE; /**< see datasheet */
uint16_t RG_FBTXS; /**< see datasheet */
uint16_t RG_FBTXE; /**< see datasheet */
uint16_t RG_IRQM; /**< see datasheet */
uint16_t RG_PC; /**< see datasheet */
uint16_t RG_PS; /**< see datasheet */
uint16_t RG_RXFLL; /**< see datasheet */
uint16_t RG_RXFLH; /**< see datasheet */
uint16_t RG_TXFLL; /**< see datasheet */
uint16_t RG_TXFLH; /**< see datasheet */
uint16_t RG_FBLL; /**< see datasheet */
uint16_t RG_FBLH; /**< see datasheet */
uint16_t RG_FBLIL; /**< see datasheet */
uint16_t RG_FBLIH; /**< see datasheet */
uint16_t RG_OFDMPHRTX; /**< see datasheet */
uint16_t RG_OFDMPHRRX; /**< see datasheet */
uint16_t RG_OFDMC; /**< see datasheet */
uint16_t RG_OFDMSW; /**< see datasheet */
uint16_t RG_OQPSKC0; /**< see datasheet */
uint16_t RG_OQPSKC1; /**< see datasheet */
uint16_t RG_OQPSKC2; /**< see datasheet */
uint16_t RG_OQPSKC3; /**< see datasheet */
uint16_t RG_OQPSKPHRTX; /**< see datasheet */
uint16_t RG_OQPSKPHRRX; /**< see datasheet */
uint16_t RG_AFC0; /**< see datasheet */
uint16_t RG_AFC1; /**< see datasheet */
uint16_t RG_AFFTM; /**< see datasheet */
uint16_t RG_AFFVM; /**< see datasheet */
uint16_t RG_AFS; /**< see datasheet */
uint16_t RG_MACEA0; /**< see datasheet */
uint16_t RG_MACEA1; /**< see datasheet */
uint16_t RG_MACEA2; /**< see datasheet */
uint16_t RG_MACEA3; /**< see datasheet */
uint16_t RG_MACEA4; /**< see datasheet */
uint16_t RG_MACEA5; /**< see datasheet */
uint16_t RG_MACEA6; /**< see datasheet */
uint16_t RG_MACEA7; /**< see datasheet */
uint16_t RG_MACPID0F0; /**< see datasheet */
uint16_t RG_MACPID1F0; /**< see datasheet */
uint16_t RG_MACSHA0F0; /**< see datasheet */
uint16_t RG_MACSHA1F0; /**< see datasheet */
uint16_t RG_MACPID0F1; /**< see datasheet */
uint16_t RG_MACPID1F1; /**< see datasheet */
uint16_t RG_MACSHA0F1; /**< see datasheet */
uint16_t RG_MACSHA1F1; /**< see datasheet */
uint16_t RG_MACPID0F2; /**< see datasheet */
uint16_t RG_MACPID1F2; /**< see datasheet */
uint16_t RG_MACSHA0F2; /**< see datasheet */
uint16_t RG_MACSHA1F2; /**< see datasheet */
uint16_t RG_MACPID0F3; /**< see datasheet */
uint16_t RG_MACPID1F3; /**< see datasheet */
uint16_t RG_MACSHA0F3; /**< see datasheet */
uint16_t RG_MACSHA1F3; /**< see datasheet */
uint16_t RG_AMCS; /**< see datasheet */
uint16_t RG_AMEDT; /**< see datasheet */
uint16_t RG_AMAACKPD; /**< see datasheet */
uint16_t RG_AMAACKTL; /**< see datasheet */
uint16_t RG_AMAACKTH; /**< see datasheet */
uint16_t RG_FSKC0; /**< see datasheet */
uint16_t RG_FSKC1; /**< see datasheet */
uint16_t RG_FSKC2; /**< see datasheet */
uint16_t RG_FSKC3; /**< see datasheet */
uint16_t RG_FSKC4; /**< see datasheet */
uint16_t RG_FSKPLL; /**< see datasheet */
uint16_t RG_FSKSFD0L; /**< see datasheet */
uint16_t RG_FSKSFD0H; /**< see datasheet */
uint16_t RG_FSKSFD1L; /**< see datasheet */
uint16_t RG_FSKSFD1H; /**< see datasheet */
uint16_t RG_FSKPHRTX; /**< see datasheet */
uint16_t RG_FSKPHRRX; /**< see datasheet */
uint16_t RG_FSKRPC; /**< see datasheet */
uint16_t RG_FSKRPCONT; /**< see datasheet */
uint16_t RG_FSKRPCOFFT; /**< see datasheet */
uint16_t RG_FSKRRXFLL; /**< see datasheet */
uint16_t RG_FSKRRXFLH; /**< see datasheet */
uint16_t RG_FSKDM; /**< see datasheet */
uint16_t RG_FSKPE0; /**< see datasheet */
uint16_t RG_FSKPE1; /**< see datasheet */
uint16_t RG_FSKPE2; /**< see datasheet */
uint16_t RG_PMUC; /**< see datasheet */
uint16_t RG_PMUVAL; /**< see datasheet */
uint16_t RG_PMUQF; /**< see datasheet */
uint16_t RG_PMUI; /**< see datasheet */
uint16_t RG_PMUQ; /**< see datasheet */
uint16_t RG_CNTC; /**< see datasheet */
uint16_t RG_CNT0; /**< see datasheet */
uint16_t RG_CNT1; /**< see datasheet */
uint16_t RG_CNT2; /**< see datasheet */
uint16_t RG_CNT3; /**< see datasheet */
};
/** @} */
/**
* @name sub-GHz Radio Frontend register map
* @{
*/
static const struct at86rf215_RF_regs RF09_regs = {
.RG_IRQS = 0x00,
.RG_IRQM = 0x100,
.RG_AUXS = 0x101,
.RG_STATE = 0x102,
.RG_CMD = 0x103,
.RG_CS = 0x104,
.RG_CCF0L = 0x105,
.RG_CCF0H = 0x106,
.RG_CNL = 0x107,
.RG_CNM = 0x108,
.RG_RXBWC = 0x109,
.RG_RXDFE = 0x10A,
.RG_AGCC = 0x10B,
.RG_AGCS = 0x10C,
.RG_RSSI = 0x10D,
.RG_EDC = 0x10E,
.RG_EDD = 0x10F,
.RG_EDV = 0x110,
.RG_RNDV = 0x111,
.RG_TXCUTC = 0x112,
.RG_TXDFE = 0x113,
.RG_PAC = 0x114,
.RG_PADFE = 0x116,
.RG_PLL = 0x121,
.RG_PLLCF = 0x122,
.RG_TXCI = 0x125,
.RG_TXCQ = 0x126,
.RG_TXDACI = 0x127,
.RG_TXDACQ = 0x128,
};
/** @} */
/**
* @name 2.4 GHz Radio Frontend register map
* @{
*/
static const struct at86rf215_RF_regs RF24_regs = {
.RG_IRQS = 0x01,
.RG_IRQM = 0x200,
.RG_AUXS = 0x201,
.RG_STATE = 0x202,
.RG_CMD = 0x203,
.RG_CS = 0x204,
.RG_CCF0L = 0x205,
.RG_CCF0H = 0x206,
.RG_CNL = 0x207,
.RG_CNM = 0x208,
.RG_RXBWC = 0x209,
.RG_RXDFE = 0x20A,
.RG_AGCC = 0x20B,
.RG_AGCS = 0x20C,
.RG_RSSI = 0x20D,
.RG_EDC = 0x20E,
.RG_EDD = 0x20F,
.RG_EDV = 0x210,
.RG_RNDV = 0x211,
.RG_TXCUTC = 0x212,
.RG_TXDFE = 0x213,
.RG_PAC = 0x214,
.RG_PADFE = 0x216,
.RG_PLL = 0x221,
.RG_PLLCF = 0x222,
.RG_TXCI = 0x225,
.RG_TXCQ = 0x226,
.RG_TXDACI = 0x227,
.RG_TXDACQ = 0x228,
};
/** @} */
/**
* @name sub-GHz Radio Frontend register map
* @{
*/
static const struct at86rf215_BBC_regs BBC0_regs = {
.RG_IRQS = 0x02,
.RG_FBRXS = 0x2000,
.RG_FBRXE = 0x27FE,
.RG_FBTXS = 0x2800,
.RG_FBTXE = 0x2FFE,
.RG_IRQM = 0x300,
.RG_PC = 0x301,
.RG_PS = 0x302,
.RG_RXFLL = 0x304,
.RG_RXFLH = 0x305,
.RG_TXFLL = 0x306,
.RG_TXFLH = 0x307,
.RG_FBLL = 0x308,
.RG_FBLH = 0x309,
.RG_FBLIL = 0x30A,
.RG_FBLIH = 0x30B,
.RG_OFDMPHRTX = 0x30C,
.RG_OFDMPHRRX = 0x30D,
.RG_OFDMC = 0x30E,
.RG_OFDMSW = 0x30F,
.RG_OQPSKC0 = 0x310,
.RG_OQPSKC1 = 0x311,
.RG_OQPSKC2 = 0x312,
.RG_OQPSKC3 = 0x313,
.RG_OQPSKPHRTX = 0x314,
.RG_OQPSKPHRRX = 0x315,
.RG_AFC0 = 0x320,
.RG_AFC1 = 0x321,
.RG_AFFTM = 0x322,
.RG_AFFVM = 0x323,
.RG_AFS = 0x324,
.RG_MACEA0 = 0x325,
.RG_MACEA1 = 0x326,
.RG_MACEA2 = 0x327,
.RG_MACEA3 = 0x328,
.RG_MACEA4 = 0x329,
.RG_MACEA5 = 0x32A,
.RG_MACEA6 = 0x32B,
.RG_MACEA7 = 0x32C,
.RG_MACPID0F0 = 0x32D,
.RG_MACPID1F0 = 0x32E,
.RG_MACSHA0F0 = 0x32F,
.RG_MACSHA1F0 = 0x330,
.RG_MACPID0F1 = 0x331,
.RG_MACPID1F1 = 0x332,
.RG_MACSHA0F1 = 0x333,
.RG_MACSHA1F1 = 0x334,
.RG_MACPID0F2 = 0x335,
.RG_MACPID1F2 = 0x336,
.RG_MACSHA0F2 = 0x337,
.RG_MACSHA1F2 = 0x338,
.RG_MACPID0F3 = 0x339,
.RG_MACPID1F3 = 0x33A,
.RG_MACSHA0F3 = 0x33B,
.RG_MACSHA1F3 = 0x33C,
.RG_AMCS = 0x340,
.RG_AMEDT = 0x341,
.RG_AMAACKPD = 0x342,
.RG_AMAACKTL = 0x343,
.RG_AMAACKTH = 0x344,
.RG_FSKC0 = 0x360,
.RG_FSKC1 = 0x361,
.RG_FSKC2 = 0x362,
.RG_FSKC3 = 0x363,
.RG_FSKC4 = 0x364,
.RG_FSKPLL = 0x365,
.RG_FSKSFD0L = 0x366,
.RG_FSKSFD0H = 0x367,
.RG_FSKSFD1L = 0x368,
.RG_FSKSFD1H = 0x369,
.RG_FSKPHRTX = 0x36A,
.RG_FSKPHRRX = 0x36B,
.RG_FSKRPC = 0x36C,
.RG_FSKRPCONT = 0x36D,
.RG_FSKRPCOFFT = 0x36E,
.RG_FSKRRXFLL = 0x370,
.RG_FSKRRXFLH = 0x371,
.RG_FSKDM = 0x372,
.RG_FSKPE0 = 0x373,
.RG_FSKPE1 = 0x374,
.RG_FSKPE2 = 0x375,
.RG_PMUC = 0x380,
.RG_PMUVAL = 0x381,
.RG_PMUQF = 0x382,
.RG_PMUI = 0x383,
.RG_PMUQ = 0x384,
.RG_CNTC = 0x390,
.RG_CNT0 = 0x391,
.RG_CNT1 = 0x392,
.RG_CNT2 = 0x393,
.RG_CNT3 = 0x394,
};
/** @} */
/**
* @name 2.4 GHz Radio Frontend register map
* @{
*/
static const struct at86rf215_BBC_regs BBC1_regs = {
.RG_IRQS = 0x03,
.RG_FBRXS = 0x3000,
.RG_FBRXE = 0x37FE,
.RG_FBTXS = 0x3800,
.RG_FBTXE = 0x3FFE,
.RG_IRQM = 0x400,
.RG_PC = 0x401,
.RG_PS = 0x402,
.RG_RXFLL = 0x404,
.RG_RXFLH = 0x405,
.RG_TXFLL = 0x406,
.RG_TXFLH = 0x407,
.RG_FBLL = 0x408,
.RG_FBLH = 0x409,
.RG_FBLIL = 0x40A,
.RG_FBLIH = 0x40B,
.RG_OFDMPHRTX = 0x40C,
.RG_OFDMPHRRX = 0x40D,
.RG_OFDMC = 0x40E,
.RG_OFDMSW = 0x40F,
.RG_OQPSKC0 = 0x410,
.RG_OQPSKC1 = 0x411,
.RG_OQPSKC2 = 0x412,
.RG_OQPSKC3 = 0x413,
.RG_OQPSKPHRTX = 0x414,
.RG_OQPSKPHRRX = 0x415,
.RG_AFC0 = 0x420,
.RG_AFC1 = 0x421,
.RG_AFFTM = 0x422,
.RG_AFFVM = 0x423,
.RG_AFS = 0x424,
.RG_MACEA0 = 0x425,
.RG_MACEA1 = 0x426,
.RG_MACEA2 = 0x427,
.RG_MACEA3 = 0x428,
.RG_MACEA4 = 0x429,
.RG_MACEA5 = 0x42A,
.RG_MACEA6 = 0x42B,
.RG_MACEA7 = 0x42C,
.RG_MACPID0F0 = 0x42D,
.RG_MACPID1F0 = 0x42E,
.RG_MACSHA0F0 = 0x42F,
.RG_MACSHA1F0 = 0x430,
.RG_MACPID0F1 = 0x431,
.RG_MACPID1F1 = 0x432,
.RG_MACSHA0F1 = 0x433,
.RG_MACSHA1F1 = 0x434,
.RG_MACPID0F2 = 0x435,
.RG_MACPID1F2 = 0x436,
.RG_MACSHA0F2 = 0x437,
.RG_MACSHA1F2 = 0x438,
.RG_MACPID0F3 = 0x439,
.RG_MACPID1F3 = 0x43A,
.RG_MACSHA0F3 = 0x43B,
.RG_MACSHA1F3 = 0x43C,
.RG_AMCS = 0x440,
.RG_AMEDT = 0x441,
.RG_AMAACKPD = 0x442,
.RG_AMAACKTL = 0x443,
.RG_AMAACKTH = 0x444,
.RG_FSKC0 = 0x460,
.RG_FSKC1 = 0x461,
.RG_FSKC2 = 0x462,
.RG_FSKC3 = 0x463,
.RG_FSKC4 = 0x464,
.RG_FSKPLL = 0x465,
.RG_FSKSFD0L = 0x466,
.RG_FSKSFD0H = 0x467,
.RG_FSKSFD1L = 0x468,
.RG_FSKSFD1H = 0x469,
.RG_FSKPHRTX = 0x46A,
.RG_FSKPHRRX = 0x46B,
.RG_FSKRPC = 0x46C,
.RG_FSKRPCONT = 0x46D,
.RG_FSKRPCOFFT = 0x46E,
.RG_FSKRRXFLL = 0x470,
.RG_FSKRRXFLH = 0x471,
.RG_FSKDM = 0x472,
.RG_FSKPE0 = 0x473,
.RG_FSKPE1 = 0x474,
.RG_FSKPE2 = 0x475,
.RG_PMUC = 0x480,
.RG_PMUVAL = 0x481,
.RG_PMUQF = 0x482,
.RG_PMUI = 0x483,
.RG_PMUQ = 0x484,
.RG_CNTC = 0x490,
.RG_CNT0 = 0x491,
.RG_CNT1 = 0x492,
.RG_CNT2 = 0x493,
.RG_CNT3 = 0x494,
};
/** @} */
/**
* @name Part Numbers
* @{
*/
#define AT86RF215_PN (0x34) /* sub-GHz & 2.4 GHz */
#define AT86RF215IQ_PN (0x35) /* I/Q radio only */
#define AT86RF215M_PN (0x36) /* sub-GHz only */
/** @} */
/**
* @name SPI command prefixes
* @{
*/
#define FLAG_WRITE 0x8000
#define FLAG_READ 0x0000
/** @} */
/**
* @name Radio Commands written to RF->RG_CMD
* @{
*/
#define CMD_RF_NOP 0x0
#define CMD_RF_SLEEP 0x1
#define CMD_RF_TRXOFF 0x2
#define CMD_RF_TXPREP 0x3
#define CMD_RF_TX 0x4
#define CMD_RF_RX 0x5
#define CMD_RF_RESET 0x7 /* transceiver reset, the transceiver state
will automatically end up in state TRXOFF */
/** @} */
/**
* @name Radio States, read from RF->RG_STATE
* @{
*/
#define RF_STATE_TRXOFF 0x2 /* Transceiver off, SPI active */
#define RF_STATE_TXPREP 0x3 /* Transmit preparation */
#define RF_STATE_TX 0x4 /* Transmit */
#define RF_STATE_RX 0x5 /* Receive */
#define RF_STATE_TRANSITION 0x6 /* State transition in progress */
#define RF_STATE_RESET 0x7 /* Transceiver is in state RESET or SLEEP */
/** @} */
/** offset (in Hz) for CCF0 in 2.4 GHz mode */
#define CCF0_24G_OFFSET 1500000U
/** The sub-register configures the sampling frequency of the received signal.
* Undefined values are mapped to default setting fS=4000kHz
* @{
*/
#define RF_SR_4000K 0x1
#define RF_SR_2000K 0x2
#define RF_SR_1333K 0x3
#define RF_SR_1000K 0x4
#define RF_SR_800K 0x5
#define RF_SR_666K 0x6
#define RF_SR_500K 0x8
#define RF_SR_400K 0xA
/** @} */
/* The sub-register configures the relative cut-off frequency fCUT
where 1.0 refers to half the sample frequency fS. */
/** Fcut = 0.25 * Fs/2 */
#define RF_RCUT_FS_BY_8 (0x0 << RXDFE_RCUT_SHIFT)
/** Fcut = 0.375 * Fs/2 */
#define RF_RCUT_FS_BY_5P3 (0x1 << RXDFE_RCUT_SHIFT)
/** Fcut = 0.5 * Fs/2 */
#define RF_RCUT_FS_BY_4 (0x2 << RXDFE_RCUT_SHIFT)
/** Fcut = 0.75 * Fs/2 */
#define RF_RCUT_FS_BY_2P6 (0x3 << RXDFE_RCUT_SHIFT)
/** Fcut = 1.0 * Fs/2 */
#define RF_RCUT_FS_BY_2 (0x4 << RXDFE_RCUT_SHIFT)
/** The averaging time is calculated by T[μs]=DF*DTB.
* @{
*/
#define RF_DTB_2_US 0x0
#define RF_DTB_8_US 0x1
#define RF_DTB_32_US 0x2
#define RF_DTB_128_US 0x3
/** @} */
/** BPSK, rate ½, 4 x frequency repetition */
#define BB_MCS_BPSK_REP4 0
/** BPSK, rate ½, 2 x frequency repetition */
#define BB_MCS_BPSK_REP2 1
/** QPSK, rate ½, 2 x frequency repetition */
#define BB_MCS_QPSK_REP2 2
/** QPSK, rate ½ */
#define BB_MCS_QPSK_1BY2 3
/** QPSK, rate ¾ */
#define BB_MCS_QPSK_3BY4 4
/** 16-QAM, rate ½ */
#define BB_MCS_16QAM_1BY2 5
/** 16-QAM, rate ¾ */
#define BB_MCS_16QAM_3BY4 6
/** receive only MR-O-QPSK */
#define RXM_MR_OQPSK 0x0
/** receive only legacy O-QPSK */
#define RXM_LEGACY_OQPSK 0x1
/** receive both legacy & MR-O-QPSK */
#define RXM_BOTH_OQPSK 0x2
/** receive nothing */
#define RXM_DISABLE 0x3
/** Modulation Order 2-FSK */
#define FSK_MORD_2SFK (0 << FSKC0_MORD_SHIFT)
/** Modulation Order 4-FSK */
#define FSK_MORD_4SFK (1 << FSKC0_MORD_SHIFT)
/**
* FSK modulation index
* @{
*/
#define FSK_MIDX_3_BY_8 (0 << FSKC0_MIDX_SHIFT)
#define FSK_MIDX_4_BY_8 (1 << FSKC0_MIDX_SHIFT)
#define FSK_MIDX_6_BY_8 (2 << FSKC0_MIDX_SHIFT)
#define FSK_MIDX_8_BY_8 (3 << FSKC0_MIDX_SHIFT)
#define FSK_MIDX_10_BY_8 (4 << FSKC0_MIDX_SHIFT)
#define FSK_MIDX_12_BY_8 (5 << FSKC0_MIDX_SHIFT)
#define FSK_MIDX_14_BY_8 (6 << FSKC0_MIDX_SHIFT)
#define FSK_MIDX_16_BY_8 (7 << FSKC0_MIDX_SHIFT)
/** @} */
/**
* FSK modulation index scale
* @{
*/
#define FSK_MIDXS_SCALE_7_BY_8 (0 << FSKC0_MIDXS_SHIFT)
#define FSK_MIDXS_SCALE_8_BY_8 (1 << FSKC0_MIDXS_SHIFT)
#define FSK_MIDXS_SCALE_9_BY_8 (2 << FSKC0_MIDXS_SHIFT)
#define FSK_MIDXS_SCALE_10_BY_8 (3 << FSKC0_MIDXS_SHIFT)
/** @} */
/**
* FSK bandwidth time product
* @{
*/
#define FSK_BT_05 (0 << FSKC0_BT_SHIFT)
#define FSK_BT_10 (1 << FSKC0_BT_SHIFT)
#define FSK_BT_15 (2 << FSKC0_BT_SHIFT)
#define FSK_BT_20 (3 << FSKC0_BT_SHIFT)
/** @} */
/**
* FSK symbol rate (kHz)
* @{
*/
#define FSK_SRATE_50K 0x0
#define FSK_SRATE_100K 0x1
#define FSK_SRATE_150K 0x2
#define FSK_SRATE_200K 0x3
#define FSK_SRATE_300K 0x4
#define FSK_SRATE_400K 0x5
/** @} */
/**
* FSK channel spacing (kHz)
* @{
*/
#define FSK_CHANNEL_SPACING_200K 0x0
#define FSK_CHANNEL_SPACING_400K 0x1
/** @} */
/** Lower values increase the SFD detector sensitivity.
Higher values increase the SFD selectivity.
The default value 8 is recommended for simultaneous sensing
of the SFD pairs according to IEEE 802.15.4g. */
#define FSKC3_SFDT(n) (((n) << FSKC3_SFDT_SHIFT) & FSKC3_SFDT_MASK)
/** Lower values increase the preamble detector sensitivity. */
#define FSKC3_PDT(n) (((n) << FSKC3_PDT_SHIFT) & FSKC3_PDT_MASK)
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* AT86RF215_REGISTERS_H */

File diff suppressed because it is too large Load Diff

429
drivers/include/at86rf215.h Normal file
View File

@ -0,0 +1,429 @@
/*
* Copyright (C) 2019 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.
*/
/**
* @defgroup drivers_at86rf215 AT86RF215 based drivers
* @ingroup drivers_netdev
*
* This module contains a driver for the Atmel AT86RF215 radio.
*
* @{
*
* @file
* @brief Interface definition for AT86RF215 based drivers
*
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
*/
#ifndef AT86RF215_H
#define AT86RF215_H
#include <stdint.h>
#include <stdbool.h>
#include "board.h"
#include "periph/spi.h"
#include "periph/gpio.h"
#include "net/netdev.h"
#include "net/netdev/ieee802154.h"
#include "net/gnrc/nettype.h"
#include "xtimer.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Registers for the Radio Frontend
*/
typedef struct at86rf215_RF_regs at86rf215_RF_regs_t;
/**
* @brief Registers for the BaseBand Controller
*/
typedef struct at86rf215_BBC_regs at86rf215_BBC_regs_t;
/**
* @brief Signature for the Battery monitor callback.
*
* @param[in] arg optional argument which is passed to the
* callback
*/
typedef void (*at86rf215_batmon_cb_t)(void *arg);
/**
* @brief Maximum possible packet size in byte
*/
#define AT86RF215_MAX_PKT_LENGTH (2047)
/**
* @brief Set to 1 if the clock output of the AT86RF215 is used
* as a clock source on the board.
* Otherwise it is turned off to save energy.
*/
#ifndef AT86RF215_USE_CLOCK_OUTPUT
#define AT86RF215_USE_CLOCK_OUTPUT (0)
#endif
/**
* @name Channel configuration
* @{
*/
#define AT86RF215_DEFAULT_CHANNEL (IEEE802154_DEFAULT_CHANNEL)
#define AT86RF215_DEFAULT_SUBGHZ_CHANNEL (IEEE802154_DEFAULT_SUBGHZ_CHANNEL)
/** @} */
/**
* @brief Default TX power (0dBm)
*/
#define AT86RF215_DEFAULT_TXPOWER (IEEE802154_DEFAULT_TXPOWER)
/**
* @name Flags for device internal states (see datasheet)
* @{
*/
typedef enum {
AT86RF215_STATE_OFF, /**< radio not configured */
AT86RF215_STATE_IDLE, /**< idle state, listening */
AT86RF215_STATE_RX_SEND_ACK,/**< receiving frame, sending ACK */
AT86RF215_STATE_TX, /**< sending frame */
AT86RF215_STATE_TX_WAIT_ACK,/**< sending frame, wait for ACK */
AT86RF215_STATE_SLEEP /**< sleep mode, not listening */
} at86rf215_state_t;
/** @} */
/**
* @name Internal device option flags
* @{
*/
#define AT86RF215_OPT_TELL_TX_START (0x0001) /**< notify MAC layer on TX start */
#define AT86RF215_OPT_TELL_TX_END (0x0002) /**< notify MAC layer on TX finished */
#define AT86RF215_OPT_TELL_RX_START (0x0004) /**< notify MAC layer on RX start */
#define AT86RF215_OPT_TELL_RX_END (0x0008) /**< notify MAC layer on RX finished */
#define AT86RF215_OPT_CSMA (0x0010) /**< CSMA active */
#define AT86RF215_OPT_PROMISCUOUS (0x0020) /**< promiscuous mode active */
#define AT86RF215_OPT_PRELOADING (0x0040) /**< preloading enabled */
#define AT86RF215_OPT_AUTOACK (0x0080) /**< Auto ACK active */
#define AT86RF215_OPT_ACK_REQUESTED (0x0100) /**< ACK requested for current frame */
#define AT86RF215_OPT_AGCH (0x0200) /**< AGC Hold active */
#define AT86RF215_OPT_TX_PENDING (0x0400) /**< Frame is loaded into TX buffer */
#define AT86RF215_OPT_CCA_PENDING (0x0800) /**< CCA needs to be done for the current frame */
#define AT86RF215_OPT_RPC (0x1000) /**< Enable Reduced Power Consumption */
#define AT86RF215_OPT_CCATX (0x2000) /**< TX after CCA performd automatically */
/** @} */
/**
* @name Internal timeout flags
* @{
*/
#define AT86RF215_TIMEOUT_ACK (0x0001) /**< ACK timeout */
#define AT86RF215_TIMEOUT_CSMA (0x0002) /**< CMSA timeout */
/** @} */
/**
* @brief struct holding all params needed for device initialization
*/
typedef struct at86rf215_params {
spi_t spi; /**< SPI bus the device is connected to */
spi_clk_t spi_clk; /**< SPI clock speed to use */
spi_cs_t cs_pin; /**< GPIO pin connected to chip select */
gpio_t int_pin; /**< GPIO pin connected to the interrupt pin */
gpio_t reset_pin; /**< GPIO pin connected to the reset pin */
} at86rf215_params_t;
/**
* @brief Device descriptor for AT86RF215 radio devices
*
* @extends netdev_ieee802154_t
*/
typedef struct at86rf215 {
netdev_ieee802154_t netdev; /**< netdev parent struct */
/* device specific fields */
at86rf215_params_t params; /**< parameters for initialization */
struct at86rf215 *sibling; /**< The other radio */
const at86rf215_RF_regs_t *RF; /**< Radio Frontend Registers */
const at86rf215_BBC_regs_t *BBC; /**< Baseband Registers */
xtimer_t timer; /**< timer for ACK & CSMA timeout */
msg_t timer_msg; /**< message for timeout timer */
uint32_t ack_timeout_usec; /**< time to wait before retransmission in µs */
uint32_t csma_backoff_period; /**< CSMA Backoff period */
uint16_t flags; /**< Device specific flags */
uint16_t num_chans; /**< Number of legal channel at current modulation */
uint16_t tx_frame_len; /**< length of the current TX frame */
uint8_t timeout; /**< indicates which timeout was reached */
uint8_t state; /**< current state of the radio */
uint8_t retries_max; /**< number of retries until ACK is received */
uint8_t retries; /**< retries left */
uint8_t csma_retries_max; /**< number of retries until channel is clear */
uint8_t csma_retries; /**< CSMA retries left */
uint8_t csma_minbe; /**< CSMA minimum backoff exponent */
uint8_t csma_maxbe; /**< CSMA maximum backoff exponent */
int8_t csma_ed; /**< CSMA energy detect threshold */
} at86rf215_t;
/**
* @brief Setup an AT86RF215 based device state
*
* @param[out] dev_09 sub-GHz device descriptor
* @param[out] dev_24 2.4 GHz device descriptor
* @param[in] params parameters for device initialization
*/
void at86rf215_setup(at86rf215_t *dev_09, at86rf215_t *dev_24, const at86rf215_params_t *params);
/**
* @brief Trigger a hardware reset and configure radio with default values.
*
* @param[in,out] dev device to configure
*/
void at86rf215_reset_and_cfg(at86rf215_t *dev);
/**
* @brief Trigger a hardware reset, configuration is retained.
*
* @param[in,out] dev device to reset
*/
void at86rf215_reset(at86rf215_t *dev);
/**
* @brief Get the short address of the given device form multi address filter
*
* @param[in] dev device to read from
* @param[in] filter address filter to read
*
* @return the currently set (2-byte) short address
*/
uint16_t at86rf215_get_addr_short(const at86rf215_t *dev, uint8_t filter);
/**
* @brief Set the short address of the given device to multi address filter
*
* @param[in,out] dev device to write to
* @param[in] filter (1-byte) address filter to set
* @param[in] addr (2-byte) short address to set
*/
void at86rf215_set_addr_short(at86rf215_t *dev, uint8_t filter, uint16_t addr);
/**
* @brief Get whether a frame filter is enabled or not
*
* @param[in] dev device to read from
* @param[in] filter (1-byte) filter to get
*
* @return (bool) the current state of the filter
*/
bool at86rf215_get_framefilter_enabled(at86rf215_t *dev, uint8_t filter);
/**
* @brief Enables a frame filter
*
* @param[in] dev device to read from
* @param[in] filter (1-byte) filter to get
*
*/
void at86rf215_disable_framefilter(at86rf215_t *dev, uint8_t filter);
/**
* @brief Disables a frame filter
*
* @param[in] dev device to read from
* @param[in] filter (1-byte) filter to get
*
*/
void at86rf215_enable_framefilter(at86rf215_t *dev, uint8_t filter);
/**
* @brief Get the configured long address of the given device
*
* @param[in] dev device to read from
*
* @return the currently set (8-byte) long address
*/
uint64_t at86rf215_get_addr_long(const at86rf215_t *dev);
/**
* @brief Set the long address of the given device
*
* @param[in,out] dev device to write to
* @param[in] addr (8-byte) long address to set
*/
void at86rf215_set_addr_long(at86rf215_t *dev, uint64_t addr);
/**
* @brief Get the configured channel number of the given device
*
* @param[in] dev device to read from
*
* @return the currently set channel number
*/
uint8_t at86rf215_get_chan(const at86rf215_t *dev);
/**
* @brief Set the channel number of the given device
*
* @param[in,out] dev device to write to
* @param[in] chan channel number to set
*/
void at86rf215_set_chan(at86rf215_t *dev, uint16_t chan);
/**
* @brief Get the configured PAN ID of the given device from multi address filter
*
* @param[in] dev device to read from
* @param[in] filter address filter to read from
*
* @return the currently set PAN ID
*/
uint16_t at86rf215_get_pan(const at86rf215_t *dev, uint8_t filter);
/**
* @brief Set the PAN ID of the given address filter
*
* @param[in,out] dev device to write to
* @param[in] filter address filter to set
* @param[in] pan PAN ID to set
*/
void at86rf215_set_pan(at86rf215_t *dev, uint8_t filter, uint16_t pan);
/**
* @brief Get the configured transmission power of the given device [in dBm]
*
* @param[in] dev device to read from
*
* @return configured transmission power in dBm
*/
int16_t at86rf215_get_txpower(const at86rf215_t *dev);
/**
* @brief Set the transmission power of the given device [in dBm]
*
* If the device does not support the exact dBm value given, it will set a value
* as close as possible to the given value. If the given value is larger or
* lower then the maximal or minimal possible value, the min or max value is
* set, respectively.
*
* @param[in] dev device to write to
* @param[in] txpower transmission power in dBm
*/
void at86rf215_set_txpower(const at86rf215_t *dev, int16_t txpower);
/**
* @brief Get the CCA threshold value
*
* @param[in] dev device to read value from
*
* @return the current CCA threshold value
*/
int8_t at86rf215_get_cca_threshold(const at86rf215_t *dev);
/**
* @brief Set the CCA threshold value
*
* @param[in] dev device to write to
* @param[in] value the new CCA threshold value
*/
void at86rf215_set_cca_threshold(at86rf215_t *dev, int8_t value);
/**
* @brief Get the latest ED level measurement
*
* @param[in] dev device to read value from
*
* @return the last ED level
*/
int8_t at86rf215_get_ed_level(at86rf215_t *dev);
/**
* @brief Enable or disable driver specific options
*
* @param[in,out] dev device to set/clear option flag for
* @param[in] option option to enable/disable
* @param[in] state true for enable, false for disable
*/
void at86rf215_set_option(at86rf215_t *dev, uint16_t option, bool state);
/**
* @brief Convenience function for simply sending data
*
* @note This function ignores the PRELOADING option
*
* @param[in,out] dev device to use for sending
* @param[in] data data to send (must include IEEE802.15.4 header)
* @param[in] len length of @p data
*
* @return number of bytes that were actually send
* @return or negative error code
*/
ssize_t at86rf215_send(at86rf215_t *dev, const void *data, size_t len);
/**
* @brief Prepare for sending of data
*
* This function puts the given device into the TX state, so no receiving of
* data is possible after it was called.
*
* @param[in,out] dev device to prepare for sending
*
* @return 0 on success, error otherwise
*/
int at86rf215_tx_prepare(at86rf215_t *dev);
/**
* @brief Load chunks of data into the transmit buffer of the given device
*
* @param[in,out] dev device to write data to
* @param[in] data buffer containing the data to load
* @param[in] len number of bytes in @p buffer
* @param[in] offset offset used when writing data to internal buffer
*
* @return offset + number of bytes written
*/
size_t at86rf215_tx_load(at86rf215_t *dev, const uint8_t *data,
size_t len, size_t offset);
/**
* @brief Trigger sending of data previously loaded into transmit buffer
*
* @param[in] dev device to trigger
*
* @return 0 on success, error otherwise
*/
int at86rf215_tx_exec(at86rf215_t *dev);
/**
* @brief Abort sending of data previously loaded into transmit buffer
*
* @param[in] dev device to abort TX on
*/
void at86rf215_tx_abort(at86rf215_t *dev);
/**
* @brief Signal that the transfer of the frame (and optional ACK reception)
* has finished. Sets the radio in RX mode.
*
* @param[in] dev device to use
*/
void at86rf215_tx_done(at86rf215_t *dev);
/**
* @brief Perform one manual channel clear assessment (CCA)
*
* The CCA mode and threshold level depends on the current transceiver settings.
*
* @param[in] dev device to use
*
* @return true if channel is determined clear
* @return false if channel is determined busy
*/
bool at86rf215_cca(at86rf215_t *dev);
#ifdef __cplusplus
}
#endif
#endif /* AT86RF215_H */
/** @} */

View File

@ -118,6 +118,7 @@ PSEUDOMODULES += at86rf23%
PSEUDOMODULES += at86rf21%
PSEUDOMODULES += at86rfa1
PSEUDOMODULES += at86rfr2
NO_PSEUDOMODULES += at86rf215
# include variants of the BME680 drivers as pseudo modules
PSEUDOMODULES += bme680_i2c

View File

@ -0,0 +1,122 @@
/*
* 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 sys_auto_init_gnrc_netif
* @{
*
* @file
* @brief Auto initialization for at86rf215 network interfaces
*
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
*/
#ifdef MODULE_AT86RF215
#define USED_BANDS (IS_USED(MODULE_AT86RF215_SUBGHZ) + IS_USED(MODULE_AT86RF215_24GHZ))
#include "log.h"
#include "board.h"
#include "net/gnrc/netif/ieee802154.h"
#ifdef MODULE_GNRC_LWMAC
#include "net/gnrc/lwmac/lwmac.h"
#endif
#ifdef MODULE_GNRC_GOMACH
#include "net/gnrc/gomach/gomach.h"
#endif
#include "net/gnrc.h"
#include "at86rf215.h"
#include "at86rf215_params.h"
/* If we don't have enough NETIFs configured, disable the sub-GHz band */
#if (GNRC_NETIF_NUMOF == 1) && IS_USED(MODULE_AT86RF215_SUBGHZ) && IS_USED(MODULE_AT86RF215_24GHZ)
#undef MODULE_AT86RF215_SUBGHZ
#undef USED_BANDS
#define USED_BANDS 1
#endif
/**
* @brief Define stack parameters for the MAC layer thread
* @{
*/
#define AT86RF215_MAC_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
#ifndef AT86RF215_MAC_PRIO
#define AT86RF215_MAC_PRIO (GNRC_NETIF_PRIO)
#endif
#ifndef AT86RF215_MAC_PRIO_SUBGHZ
#define AT86RF215_MAC_PRIO_SUBGHZ (AT86RF215_MAC_PRIO)
#endif
#define AT86RF215_NUM ARRAY_SIZE(at86rf215_params)
static at86rf215_t at86rf215_devs[AT86RF215_NUM * USED_BANDS];
static char _at86rf215_stacks[AT86RF215_NUM * USED_BANDS][AT86RF215_MAC_STACKSIZE];
static inline void _setup_netif(void* netdev, void* stack, int prio) {
if (netdev == NULL) {
return;
}
#if defined(MODULE_GNRC_GOMACH)
gnrc_netif_gomach_create(stack,
AT86RF215_MAC_STACKSIZE,
prio, "at86rf215-gomach",
netdev);
#elif defined(MODULE_GNRC_LWMAC)
gnrc_netif_lwmac_create(stack,
AT86RF215_MAC_STACKSIZE,
prio, "at86rf215-lwmac",
netdev);
#else
gnrc_netif_ieee802154_create(stack,
AT86RF215_MAC_STACKSIZE,
prio, "at86rf215",
netdev);
#endif
}
void auto_init_at86rf215(void)
{
unsigned i = 0;
unsigned j = 0;
while (j < AT86RF215_NUM) {
at86rf215_t *dev_09 = NULL;
at86rf215_t *dev_24 = NULL;
void *stack_09 = NULL;
void *stack_24 = NULL;
if (IS_USED(MODULE_AT86RF215_SUBGHZ)) {
dev_09 = &at86rf215_devs[i];
stack_09 = &_at86rf215_stacks[i];
++i;
}
if (IS_USED(MODULE_AT86RF215_24GHZ)) {
dev_24 = &at86rf215_devs[i];
stack_24 = &_at86rf215_stacks[i];
++i;
}
at86rf215_setup(dev_09, dev_24, &at86rf215_params[j++]);
/* setup sub-GHz interface */
_setup_netif(dev_09, stack_09, AT86RF215_MAC_PRIO_SUBGHZ);
/* setup 2.4-GHz interface */
_setup_netif(dev_24, stack_24, AT86RF215_MAC_PRIO);
}
}
#else
typedef int dont_be_pedantic;
#endif /* MODULE_AT86RF215 */
/** @} */

View File

@ -32,6 +32,11 @@ void gnrc_netif_init_devs(void)
auto_init_stm32_eth();
}
if (IS_USED(MODULE_AUTO_INIT_AT86RF215)) {
extern void auto_init_at86rf215(void);
auto_init_at86rf215();
}
if (IS_USED(MODULE_AUTO_INIT_AT86RF2XX)) {
extern void auto_init_at86rf2xx(void);
auto_init_at86rf2xx();

View File

@ -0,0 +1,28 @@
BOARD ?= openmote-b
include ../Makefile.tests_common
# Modules to include:
USEMODULE += shell
USEMODULE += shell_commands
USEMODULE += ps
# the radio driver to test
USEMODULE += at86rf215
GNRC_NETIF_NUMOF ?= 2
# gnrc is a meta module including all required, basic gnrc networking modules
USEMODULE += gnrc
# automatically initialize the network interface
USEMODULE += auto_init_gnrc_netif
# shell command to send L2 packets with a simple string
USEMODULE += gnrc_txtsnd
# the application dumps received packets to stdout
USEMODULE += gnrc_pktdump
include $(RIOTBASE)/Makefile.include
# Set a custom channel if needed
include $(RIOTMAKE)/default-radio-settings.inc.mk

View File

@ -0,0 +1,18 @@
BOARD_INSUFFICIENT_MEMORY := \
arduino-duemilanove \
arduino-leonardo \
arduino-mega2560 \
arduino-uno \
atmega328p \
i-nucleo-lrwan1 \
nucleo-f031k6 \
nucleo-f042k6 \
nucleo-f303k8 \
nucleo-f334r8 \
nucleo-l031k6 \
nucleo-l053r8 \
stm32f030f4-demo \
stm32f0discovery \
stm32l0538-disco \
waspmote-pro \
#

View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2020 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 tests
* @{
*
* @file
* @brief Test application for network device drivers
*
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
* @}
*/
#include "thread.h"
#include "shell.h"
#include "shell_commands.h"
#include "net/gnrc/pktdump.h"
#include "net/gnrc.h"
int main(void)
{
/* enable pktdump output */
gnrc_netreg_entry_t dump = GNRC_NETREG_ENTRY_INIT_PID(GNRC_NETREG_DEMUX_CTX_ALL,
gnrc_pktdump_pid);
gnrc_netreg_register(GNRC_NETTYPE_UNDEF, &dump);
/* start the shell */
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE);
return 0;
}