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
2022-09-16 14:00:35 +02:00

480 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 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 */
#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;
}
}
static inline uint16_t _get_symbol_duration_us(uint8_t chips)
{
/* 802.15.4g, Table 183 / Table 165 */
switch (chips) {
case BB_FCHIP100:
return 320;
case BB_FCHIP200:
return 160;
case BB_FCHIP1000:
case BB_FCHIP2000:
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;
}
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 octets 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)
{
/* 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 inline void _set_ack_timeout_legacy(at86rf215_t *dev)
{
dev->ack_timeout_usec = IEEE802154_ACK_TIMEOUT_SYMS * 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)
{
/* 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 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);
}
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;
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);
/* disable FSK preamble switching */
#ifdef MODULE_NETDEV_IEEE802154_MR_FSK
dev->fsk_pl = 0;
#endif
at86rf215_enable_radio(dev, BB_MROQPSK);
}
int at86rf215_configure_OQPSK(at86rf215_t *dev, uint8_t chips, uint8_t mode)
{
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_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;
}