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:
commit
cdbf0b2d69
@ -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
|
||||
|
@ -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)))
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
* @{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
1
drivers/at86rf215/Makefile
Normal file
1
drivers/at86rf215/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
342
drivers/at86rf215/at86rf215.c
Normal file
342
drivers/at86rf215/at86rf215.c
Normal 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;
|
||||
}
|
333
drivers/at86rf215/at86rf215_getset.c
Normal file
333
drivers/at86rf215/at86rf215_getset.c
Normal 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;
|
||||
}
|
204
drivers/at86rf215/at86rf215_internal.c
Normal file
204
drivers/at86rf215/at86rf215_internal.c
Normal 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, ®, 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, ®, 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, ®, 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, ®, 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, ®, 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";
|
||||
}
|
||||
}
|
936
drivers/at86rf215/at86rf215_netdev.c
Normal file
936
drivers/at86rf215/at86rf215_netdev.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
277
drivers/at86rf215/at86rf215_o-qpsk.c
Normal file
277
drivers/at86rf215/at86rf215_o-qpsk.c
Normal 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 68d—Total number of channels and first channel center frequencies for SUN PHYs
|
||||
* Table 68e—Center frequencies for the MR-O-QPSK PHY operating in the 868–870 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;
|
||||
}
|
401
drivers/at86rf215/include/at86rf215_internal.h
Normal file
401
drivers/at86rf215/include/at86rf215_internal.h
Normal 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 */
|
||||
/** @} */
|
39
drivers/at86rf215/include/at86rf215_netdev.h
Normal file
39
drivers/at86rf215/include/at86rf215_netdev.h
Normal 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 */
|
||||
/** @} */
|
72
drivers/at86rf215/include/at86rf215_params.h
Normal file
72
drivers/at86rf215/include/at86rf215_params.h
Normal 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 */
|
||||
/** @} */
|
618
drivers/at86rf215/include/at86rf215_registers.h
Normal file
618
drivers/at86rf215/include/at86rf215_registers.h
Normal 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 */
|
4722
drivers/at86rf215/include/vendor/at86rf215.h
vendored
Normal file
4722
drivers/at86rf215/include/vendor/at86rf215.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
429
drivers/include/at86rf215.h
Normal file
429
drivers/include/at86rf215.h
Normal 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 */
|
||||
/** @} */
|
@ -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
|
||||
|
122
sys/net/gnrc/netif/init_devs/auto_init_at86rf215.c
Normal file
122
sys/net/gnrc/netif/init_devs/auto_init_at86rf215.c
Normal 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 */
|
||||
|
||||
/** @} */
|
@ -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();
|
||||
|
28
tests/driver_at86rf215/Makefile
Normal file
28
tests/driver_at86rf215/Makefile
Normal 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
|
18
tests/driver_at86rf215/Makefile.ci
Normal file
18
tests/driver_at86rf215/Makefile.ci
Normal 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 \
|
||||
#
|
39
tests/driver_at86rf215/main.c
Normal file
39
tests/driver_at86rf215/main.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user