drivers/at86rf215: implement MR-O-QPSK

This commit is contained in:
Benjamin Valentin 2020-03-23 22:44:03 +01:00
parent 0cf9f6aa7f
commit e6d47aa825
6 changed files with 430 additions and 26 deletions

View File

@ -73,6 +73,7 @@ ifneq (,$(filter at86rf215%,$(USEMODULE)))
DEFAULT_MODULE += netdev_ieee802154_multimode
DEFAULT_MODULE += netdev_ieee802154_oqpsk
DEFAULT_MODULE += netdev_ieee802154_mr_oqpsk
ifeq (,$(filter at86rf215m,$(USEMODULE)))
DEFAULT_MODULE += at86rf215_24ghz

View File

@ -17,7 +17,6 @@
* @}
*/
#include "luid.h"
#include "byteorder.h"
#include "net/ieee802154.h"
@ -145,8 +144,13 @@ void at86rf215_reset(at86rf215_t *dev)
at86rf215_reg_write(dev, dev->BBC->RG_AMCS, reg);
/* set compatibility with first-gen 802.15.4 devices */
at86rf215_configure_legacy_OQPSK(dev, 0);
if (AT86RF215_DEFAULT_PHY_MODE == IEEE802154_PHY_OQPSK) {
at86rf215_configure_legacy_OQPSK(dev, 0);
}
if (AT86RF215_DEFAULT_PHY_MODE == IEEE802154_PHY_MR_OQPSK) {
at86rf215_configure_OQPSK(dev, AT86RF215_DEFAULT_MR_OQPSK_CHIPS,
AT86RF215_DEFAULT_MR_OQPSK_RATE);
}
/* set default channel */
at86rf215_set_chan(dev, dev->netdev.chan);

View File

@ -327,6 +327,10 @@ static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len)
case NETOPT_CHANNEL_PAGE:
assert(max_len >= sizeof(uint16_t));
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_OQPSK) {
return -ENOTSUP;
}
((uint8_t *)val)[1] = 0;
((uint8_t *)val)[0] = is_subGHz(dev) ? 2 : 0;
return sizeof(uint16_t);
@ -390,6 +394,29 @@ static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len)
res = max_len;
break;
case NETOPT_MR_OQPSK_CHIPS:
assert(max_len >= sizeof(int16_t));
switch (at86rf215_OQPSK_get_chips(dev)) {
case 0: *((int16_t *)val) = 100; break;
case 1: *((int16_t *)val) = 200; break;
case 2: *((int16_t *)val) = 1000; break;
case 3: *((int16_t *)val) = 2000; break;
}
res = max_len;
break;
case NETOPT_MR_OQPSK_RATE:
assert(max_len >= sizeof(int8_t));
*((int8_t *)val) = at86rf215_OQPSK_get_mode(dev);
res = max_len;
break;
case NETOPT_OQPSK_RATE:
assert(max_len >= sizeof(int8_t));
*((int8_t *)val) = at86rf215_OQPSK_get_mode_legacy(dev);
res = max_len;
break;
default:
res = -ENOTSUP;
break;
@ -541,9 +568,70 @@ static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len)
at86rf215_configure_legacy_OQPSK(dev, at86rf215_OQPSK_get_mode_legacy(dev));
res = sizeof(uint8_t);
break;
case IEEE802154_PHY_MR_OQPSK:
at86rf215_configure_OQPSK(dev,
at86rf215_OQPSK_get_chips(dev),
at86rf215_OQPSK_get_mode(dev));
res = sizeof(uint8_t);
break;
default:
return -ENOTSUP;
}
break;
case NETOPT_MR_OQPSK_CHIPS:
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_MR_OQPSK) {
return -ENOTSUP;
}
uint8_t chips;
assert(len <= sizeof(uint16_t));
if (*((const uint16_t *)val) == 100) {
chips = 0;
} else if (*((const uint16_t *)val) == 200) {
chips = 1;
} else if (*((const uint16_t *)val) == 1000) {
chips = 2;
} else if (*((const uint16_t *)val) == 2000) {
chips = 3;
} else {
res = -EINVAL;
break;
}
if (at86rf215_OQPSK_set_chips(dev, chips) == 0) {
res = sizeof(uint8_t);
} else {
res = -ERANGE;
}
break;
case NETOPT_MR_OQPSK_RATE:
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_MR_OQPSK) {
return -ENOTSUP;
}
assert(len <= sizeof(uint8_t));
if (at86rf215_OQPSK_set_mode(dev, *(uint8_t *)val) == 0) {
res = sizeof(uint8_t);
} else {
res = -ERANGE;
}
break;
case NETOPT_OQPSK_RATE:
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_OQPSK) {
return -ENOTSUP;
}
assert(len <= sizeof(uint8_t));
if (at86rf215_OQPSK_set_mode_legacy(dev, *(uint8_t *)val) == 0) {
res = sizeof(uint8_t);
} else {
res = -ERANGE;
}
break;
default:
break;
}

View File

@ -34,6 +34,8 @@
#define QPSK_CHANNEL_SPACING_24GHZ (5000U) /* kHz */
#define QPSK_CENTER_FREQUENCY_24GHZ (2350000U - CCF0_24G_OFFSET) /* Hz */
#define LEGACY_QPSK_SYMBOL_TIME_US (16)
/* Table 6-103. O-QPSK Transmitter Frontend Configuration */
static uint8_t _TXCUTC_PARAMP(uint8_t chips)
{
@ -148,20 +150,88 @@ static inline uint8_t _AGCC(uint8_t chips)
}
}
/* Table 6-100. MR-O-QPSK Modes */
static uint32_t _get_bitrate(uint8_t chips, uint8_t mode)
static inline uint16_t _get_symbol_duration_us(uint8_t chips)
{
/* 802.15.4g, Table 183 / Table 165 */
switch (chips) {
case BB_FCHIP100:
return 6250 * (1 << mode);
return 320;
case BB_FCHIP200:
return 12500 * (1 << mode);
return 160;
case BB_FCHIP1000:
case BB_FCHIP2000:
return mode ? 125000 * (1 << (mode - 1)) : 31250;
default:
return 64;
}
}
static inline uint8_t _get_cca_duration_syms(uint8_t chips)
{
/* 802.15.4g, Table 188 */
return (chips < BB_FCHIP1000) ? 4 : 8;
}
static inline uint8_t _get_shr_duration_syms(uint8_t chips)
{
/* 802.15.4g, Table 184 / Table 165 */
return (chips < BB_FCHIP1000) ? 48 : 72;
}
static uint8_t _get_spreading(uint8_t chips, uint8_t mode)
{
if (mode == 4) {
return 1;
}
return 0;
uint8_t spread = 1 << (3 - mode);
if (chips == BB_FCHIP1000) {
return 2 * spread;
}
if (chips == BB_FCHIP2000) {
return 4 * spread;
}
return spread;
}
static inline uint8_t _get_ack_psdu_duration_syms(uint8_t chips, uint8_t mode)
{
/* pg. 119, section 18.3.2.14 */
static const uint8_t sym_len[] = { 32, 32, 64, 128 };
const uint8_t Ns = sym_len[chips];
const uint8_t Rspread = _get_spreading(chips, mode);
/* Nd == 63, since ACK length is 5 or 7 octects only */
const uint16_t Npsdu = Rspread * 2 * 63;
/* phyPSDUDuration = ceiling(Npsdu / Ns) + ceiling(Npsdu / Mp) */
/* with Mp = Np * 16, see Table 182 */
return (Npsdu + Ns/2) / Ns + (Npsdu + 8 * Ns) / (16 * Ns);
}
static uint8_t _set_mode(at86rf215_t *dev, uint8_t mode)
{
mode = AT86RF215_MR_OQPSK_MODE(mode);
/* TX with selected rate mode */
at86rf215_reg_write(dev, dev->BBC->RG_OQPSKPHRTX, mode);
/* power save mode only works when not listening to legacy frames */
/* listening to both uses ~1mA more that just listening to legacy */
/* TODO: make this configurable */
uint8_t rxm = RXM_MR_OQPSK;
if (dev->flags & AT86RF215_OPT_RPC) {
rxm |= OQPSKC2_RPC_MASK; /* enable Reduced Power Consumption */
}
at86rf215_reg_write(dev, dev->BBC->RG_OQPSKC2,
rxm /* receive mode, MR-O-QPSK */
| OQPSKC2_FCSTLEG_MASK /* 16 bit frame checksum */
| OQPSKC2_ENPROP_MASK); /* enable RX of proprietary modes */
return mode;
}
static void _set_chips(at86rf215_t *dev, uint8_t chips)
@ -222,18 +292,38 @@ static void _set_legacy(at86rf215_t *dev, bool high_rate)
| OQPSKC2_ENPROP_MASK); /* enable RX of proprietary modes */
}
static inline void _set_ack_timeout_legacy(at86rf215_t *dev)
{
dev->ack_timeout_usec = AT86RF215_ACK_PERIOD_IN_SYMBOLS * LEGACY_QPSK_SYMBOL_TIME_US;
DEBUG("[%s] ACK timeout: %"PRIu32" µs\n", "legacy O-QPSK", dev->ack_timeout_usec);
}
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);
/* see 802.15.4g-2012, p. 30 */
uint16_t symbols = _get_cca_duration_syms(chips)
+ _get_shr_duration_syms(chips)
+ 15 /* PHR duration */
+ _get_ack_psdu_duration_syms(chips, mode);
dev->ack_timeout_usec = _get_symbol_duration_us(chips) * symbols
+ IEEE802154G_ATURNAROUNDTIME_US;
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)
static inline void _set_csma_backoff_period(at86rf215_t *dev, uint8_t chips)
{
dev->csma_backoff_period = AT86RF215_BACKOFF_PERIOD_IN_BITS * US_PER_SEC / _get_bitrate(chips, mode);
dev->csma_backoff_period = AT86RF215_BACKOFF_PERIOD_IN_SYMBOLS * _get_symbol_duration_us(chips);
DEBUG("[%s] CSMA BACKOFF: %"PRIu32" µs\n", "O-QPSK", dev->csma_backoff_period);
}
static inline void _set_csma_backoff_period_legacy(at86rf215_t *dev)
{
dev->csma_backoff_period = AT86RF215_BACKOFF_PERIOD_IN_SYMBOLS * LEGACY_QPSK_SYMBOL_TIME_US;
DEBUG("[%s] CSMA BACKOFF: %"PRIu32" µs\n", "legacy O-QPSK", dev->csma_backoff_period);
}
void _end_configure_OQPSK(at86rf215_t *dev)
{
/* set channel spacing with 25 kHz resolution */
@ -256,22 +346,127 @@ void _end_configure_OQPSK(at86rf215_t *dev)
at86rf215_enable_radio(dev, BB_MROQPSK);
}
int at86rf215_configure_legacy_OQPSK(at86rf215_t *dev, bool high_rate)
int at86rf215_configure_OQPSK(at86rf215_t *dev, uint8_t chips, uint8_t mode)
{
/* 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;
if (chips > BB_FCHIP2000) {
DEBUG("[%s] invalid chips: %d\n", __func__, chips);
return -EINVAL;
}
if (mode > 4) {
DEBUG("[%s] invalid mode: %d\n", __func__, mode);
return -EINVAL;
}
/* mode 4 only supports 2000 kchip/s */
if (mode == 4 && chips != BB_FCHIP2000) {
DEBUG("[%s] mode 4 only supports 2000 kChip/s\n", __func__);
return -EINVAL;
}
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_mode(dev, mode);
_set_chips(dev, chips);
_set_csma_backoff_period(dev, chips);
_set_ack_timeout(dev, chips, mode);
_end_configure_OQPSK(dev);
return 0;
}
int at86rf215_configure_legacy_OQPSK(at86rf215_t *dev, bool high_rate)
{
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_legacy(dev);
_set_ack_timeout_legacy(dev);
_end_configure_OQPSK(dev);
return 0;
}
int at86rf215_OQPSK_set_chips(at86rf215_t *dev, uint8_t chips)
{
uint8_t mode;
mode = at86rf215_reg_read(dev, dev->BBC->RG_OQPSKPHRTX);
if (mode & AT86RF215_OQPSK_MODE_LEGACY) {
DEBUG("[%s] can't set chip rate in legacy mode\n", __func__);
return -1;
}
at86rf215_await_state_end(dev, RF_STATE_TX);
_set_chips(dev, chips);
_set_csma_backoff_period(dev, chips);
_set_ack_timeout(dev, chips, mode >> OQPSKPHRTX_MOD_SHIFT);
return 0;
}
uint8_t at86rf215_OQPSK_get_chips(at86rf215_t *dev)
{
return at86rf215_reg_read(dev, dev->BBC->RG_OQPSKC0) & OQPSKC0_FCHIP_MASK;
}
int at86rf215_OQPSK_set_mode(at86rf215_t *dev, uint8_t mode)
{
if (mode > 4) {
return -1;
}
uint8_t chips = at86rf215_OQPSK_get_chips(dev);
at86rf215_await_state_end(dev, RF_STATE_TX);
if (mode == 4 && chips != BB_FCHIP2000) {
_set_chips(dev, BB_FCHIP2000);
}
_set_mode(dev, mode);
_set_csma_backoff_period(dev, chips);
_set_ack_timeout(dev, chips, mode);
return 0;
}
uint8_t at86rf215_OQPSK_get_mode(at86rf215_t *dev)
{
uint8_t mode = at86rf215_reg_read(dev, dev->BBC->RG_OQPSKPHRTX);
return (mode & OQPSKPHRTX_MOD_MASK) >> OQPSKPHRTX_MOD_SHIFT;
}
int at86rf215_OQPSK_set_mode_legacy(at86rf215_t *dev, bool high_rate)
{
/* enable/disable legacy high data rate */
if (high_rate) {
at86rf215_reg_write(dev, dev->BBC->RG_OQPSKC3, OQPSKC3_HRLEG_MASK);
} else {
at86rf215_reg_write(dev, dev->BBC->RG_OQPSKC3, 0);
}
_set_csma_backoff_period_legacy(dev);
_set_ack_timeout_legacy(dev);
return 0;
}
uint8_t at86rf215_OQPSK_get_mode_legacy(at86rf215_t *dev)
{
if (at86rf215_reg_read(dev, dev->BBC->RG_OQPSKC3) & OQPSKC3_HRLEG_MASK) {
return 1;
}
return 0;
}

View File

@ -46,8 +46,6 @@ extern "C" {
*/
/** 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
@ -59,10 +57,6 @@ extern "C" {
#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)
@ -71,8 +65,6 @@ extern "C" {
* 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 */
@ -158,6 +150,93 @@ void at86rf215_get_random(at86rf215_t *dev, void *data, size_t len);
*/
int at86rf215_configure_legacy_OQPSK(at86rf215_t *dev, bool high_rate);
/**
* @brief Configure the radio to make use of O-QPSK modulation.
* The chip rate is the number of bits per second (chips per second) used in the spreading signal.
* The rate mode may be
* - @ref AT86RF215_OQPSK_MODE_LEGACY for compatibility with first-gen 802.15.4 devices (250 kbit/s)
* - @ref AT86RF215_OQPSK_MODE_LEGACY_HDR for compatibility with the proprietary high-data rate mode
* of the at86rf233 (1000 kbit/s, 2.4 GHz) and at86rf212b (500 kbit/s, sub-GHz)
* - @ref AT86RF215_MR_OQPSK_MODE for the rate modes specified in 802.15.4g-2012
*
* @param[in] dev device to configure
* @param[in] chips chip rate, `BB_FCHIP100` `BB_FCHIP2000`
* @param[in] rate rate mode, may be @ref AT86RF215_OQPSK_MODE_LEGACY or @ref AT86RF215_MR_OQPSK_MODE
*
* @return 0 on success, error otherwise
*/
int at86rf215_configure_OQPSK(at86rf215_t *dev, uint8_t chips, uint8_t rate);
/**
* @brief Get the current O-QPSK chip rate
*
* @param[in] dev device to read from
*
* @return the current chip rate
*/
uint8_t at86rf215_OQPSK_get_chips(at86rf215_t *dev);
/**
* @brief Set the current O-QPSK chip rate
*
* @param[in] dev device to configure
* @param[in] chips chip rate in chip/s
*
* @return 0 on success, error otherwise
*/
int at86rf215_OQPSK_set_chips(at86rf215_t *dev, uint8_t chips);
/**
* @brief Get the current O-QPSK rate mode
*
* @param[in] dev device to read from
*
* @return the current rate mode
*/
uint8_t at86rf215_OQPSK_get_mode(at86rf215_t *dev);
/**
* @brief Set the current O-QPSK rate mode
*
* @param[in] dev device to configure
* @param[in] mode rate mode
*
* @return 0 on success, error otherwise
*/
int at86rf215_OQPSK_set_mode(at86rf215_t *dev, uint8_t mode);
/**
* @brief Get the current legacy O-QPSK mode
*
* @param[in] dev device to read from
*
* @return 0 for IEEE 802.15.4 mode, 1 for high data rate
*/
uint8_t at86rf215_OQPSK_get_mode_legacy(at86rf215_t *dev);
/**
* @brief Set the current legacy O-QPSK rate mode
*
* @param[in] dev device to configure
* @param[in] high_rate set to use proprietary high data rate
*
* @return 0 on success, error otherwise
*/
int at86rf215_OQPSK_set_mode_legacy(at86rf215_t *dev, bool high_rate);
/**
* @brief Test if O-QPSK PHY operates in legacy mode
*
* @param[in] dev device to test
*
* @return true if device operates in legacy mode
*/
static inline bool at86rf215_OQPSK_is_legacy(at86rf215_t *dev) {
return at86rf215_reg_read(dev, dev->BBC->RG_OQPSKPHRTX) & AT86RF215_OQPSK_MODE_LEGACY;
}
/** @} */
/**
* @brief Get the current PHY modulation.
* May be @ref IEEE802154_PHY_MR_FSK, @ref IEEE802154_PHY_MR_OFDM,

View File

@ -56,6 +56,16 @@ typedef struct at86rf215_BBC_regs at86rf215_BBC_regs_t;
*/
typedef void (*at86rf215_batmon_cb_t)(void *arg);
/**
* @brief MR-O-QPSK chip rates (kChip/s)
*/
enum {
AT86RF215_FCHIP_100,
AT86RF215_FCHIP_200,
AT86RF215_FCHIP_1000,
AT86RF215_FCHIP_2000,
};
/**
* @brief Maximum possible packet size in byte
*/
@ -100,6 +110,33 @@ typedef void (*at86rf215_batmon_cb_t)(void *arg);
#define AT86RF215_DEFAULT_SUBGHZ_CHANNEL (CONFIG_IEEE802154_DEFAULT_SUBGHZ_CHANNEL)
/** @} */
/**
* @name Default PHY Mode
* @{
*/
#ifndef AT86RF215_DEFAULT_PHY_MODE
#define AT86RF215_DEFAULT_PHY_MODE (IEEE802154_PHY_OQPSK)
#endif
/** @} */
/**
* @name Default MR-O-QPSK Chip Rate
* @{
*/
#ifndef AT86RF215_DEFAULT_MR_OQPSK_CHIPS
#define AT86RF215_DEFAULT_MR_OQPSK_CHIPS (AT86RF215_FCHIP_1000)
#endif
/** @} */
/**
* @name Default MR-O-QPSK Rate Mode
* @{
*/
#ifndef AT86RF215_DEFAULT_MR_OQPSK_RATE
#define AT86RF215_DEFAULT_MR_OQPSK_RATE (2)
#endif
/** @} */
/**
* @brief Default TX power (0dBm)
*/