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

480 lines
13 KiB
C
Raw Normal View History

/*
* Copyright (C) 2019 ML!PA Consulting GmbH
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_at86rf215
* @{
*
* @file
* @brief Configuration of the MR-O-QPSK PHY on the AT86RF215 chip
*
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
* @}
*/
#include "at86rf215.h"
#include "at86rf215_internal.h"
#include "debug.h"
/* IEEE Std 802.15.4g™-2012 Amendment 3
* Table 68dTotal number of channels and first channel center frequencies for SUN PHYs
* Table 68eCenter frequencies for the MR-O-QPSK PHY operating in the 868870 MHz band
*/
/* currently only EU-868 MHz band is supported */
#define QPSK_CHANNEL_SPACING_SUBGHZ (650U) /* kHz */
#define QPSK_CENTER_FREQUENCY_SUBGHZ (868300U) /* Hz */
#define QPSK_CHANNEL_SPACING_24GHZ (5000U) /* kHz */
#define QPSK_CENTER_FREQUENCY_24GHZ (2350000U - CCF0_24G_OFFSET) /* Hz */
2020-03-23 22:44:03 +01:00
#define LEGACY_QPSK_SYMBOL_TIME_US (16)
/* 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;
}
}
2020-03-23 22:44:03 +01:00
static inline uint16_t _get_symbol_duration_us(uint8_t chips)
{
2020-03-23 22:44:03 +01:00
/* 802.15.4g, Table 183 / Table 165 */
switch (chips) {
case BB_FCHIP100:
2020-03-23 22:44:03 +01:00
return 320;
case BB_FCHIP200:
2020-03-23 22:44:03 +01:00
return 160;
case BB_FCHIP1000:
case BB_FCHIP2000:
2020-03-23 22:44:03 +01:00
default:
return 64;
}
2020-03-23 22:44:03 +01:00
}
2020-03-23 22:44:03 +01:00
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;
}
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);
2022-09-16 13:28:48 +02:00
/* Nd == 63, since ACK length is 5 or 7 octets only */
2020-03-23 22:44:03 +01:00
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)
{
/* 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 */
}
2020-03-23 22:44:03 +01:00
static inline void _set_ack_timeout_legacy(at86rf215_t *dev)
{
dev->ack_timeout_usec = IEEE802154_ACK_TIMEOUT_SYMS * LEGACY_QPSK_SYMBOL_TIME_US;
2020-03-23 22:44:03 +01:00
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)
{
2020-03-23 22:44:03 +01:00
/* 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);
}
2020-03-23 22:44:03 +01:00
static inline void _set_csma_backoff_period(at86rf215_t *dev, uint8_t chips)
{
dev->csma_backoff_period = _get_cca_duration_syms(chips) * _get_symbol_duration_us(chips)
+ IEEE802154G_ATURNAROUNDTIME_US;
DEBUG("[%s] CSMA BACKOFF: %"PRIu32" µs\n", "O-QPSK", dev->csma_backoff_period);
}
2020-03-23 22:44:03 +01:00
static inline void _set_csma_backoff_period_legacy(at86rf215_t *dev)
{
dev->csma_backoff_period = (IEEE802154_ATURNAROUNDTIME_IN_SYMBOLS + IEEE802154_CCA_DURATION_IN_SYMBOLS)
* LEGACY_QPSK_SYMBOL_TIME_US;
2020-03-23 22:44:03 +01:00
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 */
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);
2020-06-13 18:03:32 +02:00
/* disable FSK preamble switching */
#ifdef MODULE_NETDEV_IEEE802154_MR_FSK
dev->fsk_pl = 0;
#endif
at86rf215_enable_radio(dev, BB_MROQPSK);
}
2020-03-23 22:44:03 +01:00
int at86rf215_configure_OQPSK(at86rf215_t *dev, uint8_t chips, uint8_t mode)
{
2020-03-23 22:44:03 +01:00
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);
2020-03-23 22:44:03 +01:00
_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;
}
2020-03-23 22:44:03 +01:00
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;
}