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

618 lines
18 KiB
C

/*
* 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-FSK PHY on the AT86RF215 chip
*
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
* @}
*/
#include "at86rf215.h"
#include "at86rf215_internal.h"
#define ENABLE_DEBUG 0
#include "debug.h"
/* symbol time is always 20 µs for MR-FSK (table 0, pg. 7) */
#define FSK_SYMBOL_TIME_US 20
/* also used by at86rf215_netdev.c */
const uint8_t _at86rf215_fsk_srate_10kHz[] = {
[FSK_SRATE_50K] = 5,
[FSK_SRATE_100K] = 10,
[FSK_SRATE_150K] = 15,
[FSK_SRATE_200K] = 20,
[FSK_SRATE_300K] = 30,
[FSK_SRATE_400K] = 40
};
/* also used by at86rf215_netdev.c */
const uint8_t _at86rf215_fsk_channel_spacing_25kHz[] = {
[FSK_CHANNEL_SPACING_200K] = 8,
[FSK_CHANNEL_SPACING_400K] = 16,
};
/* IEEE Std 802.15.4™-2015
* Table 10-10—Channel numbering for SUN PHYs,
* index is channel spacing */
static const uint16_t _chan_center_freq0_subghz_25khz[] = {
[FSK_CHANNEL_SPACING_200K] = 863125U / 25,
[FSK_CHANNEL_SPACING_400K] = 863225U / 25
};
/* IEEE Std 802.15.4™-2015
* Table 10-10—Channel numbering for SUN PHYs,
* index is channel spacing */
static const uint16_t _chan_center_freq0_24ghz_25khz[] = {
[FSK_CHANNEL_SPACING_200K] = (2400200U - CCF0_24G_OFFSET) / 25,
[FSK_CHANNEL_SPACING_400K] = (2400400U - CCF0_24G_OFFSET) / 25
};
/* Table 6-57, index is symbol rate */
static const uint8_t _FSKPE_Val[3][6] = {
{ 0x02, 0x0E, 0x3E, 0x74, 0x05, 0x13 }, /* FSKPE0 */
{ 0x03, 0x0F, 0x3F, 0x7F, 0x3C, 0x29 }, /* FSKPE1 */
{ 0xFC, 0xF0, 0xC0, 0x80, 0xC3, 0xC7 } /* FSKPE2 */
};
/* table 6-51: Symbol Rate Settings */
static uint8_t _TXDFE_SR(at86rf215_t *dev, uint8_t srate)
{
const uint8_t version = at86rf215_reg_read(dev, RG_RF_VN);
switch (srate) {
case FSK_SRATE_50K:
return version == 1 ? RF_SR_400K : RF_SR_500K;
case FSK_SRATE_100K:
return version == 1 ? RF_SR_800K : RF_SR_1000K;
case FSK_SRATE_150K:
case FSK_SRATE_200K:
return RF_SR_2000K;
case FSK_SRATE_300K:
case FSK_SRATE_400K:
return RF_SR_4000K;
}
return 0;
}
/* table 6-51: Symbol Rate Settings */
static uint8_t _RXDFE_SR(uint8_t srate)
{
switch (srate) {
case FSK_SRATE_50K:
return RF_SR_400K;
case FSK_SRATE_100K:
return RF_SR_800K;
case FSK_SRATE_150K:
case FSK_SRATE_200K:
return RF_SR_1000K;
case FSK_SRATE_300K:
case FSK_SRATE_400K:
return RF_SR_2000K;
}
return 0;
}
/* table 6-53 Recommended Configuration of the Transmitter Frontend */
static uint8_t _TXCUTC_PARAMP(uint8_t srate)
{
switch (srate) {
case FSK_SRATE_50K:
return RF_PARAMP32U;
case FSK_SRATE_100K:
case FSK_SRATE_150K:
case FSK_SRATE_200K:
return RF_PARAMP16U;
case FSK_SRATE_300K:
case FSK_SRATE_400K:
return RF_PARAMP8U;
}
return 0;
}
/* Table 6-53. Recommended Configuration of the Transmitter Frontend (modulation index 0.5) */
static uint8_t _TXCUTC_LPFCUT_half(uint8_t srate)
{
switch (srate) {
case FSK_SRATE_50K:
return RF_FLC80KHZ;
case FSK_SRATE_100K:
return RF_FLC100KHZ;
case FSK_SRATE_150K:
return RF_FLC160KHZ;
case FSK_SRATE_200K:
return RF_FLC200KHZ;
case FSK_SRATE_300K:
return RF_FLC315KHZ;
case FSK_SRATE_400K:
return RF_FLC400KHZ;
}
return 0;
}
/* Table 6-54. Recommended Configuration of the Transmitter Frontend (modulation index 1) */
static uint8_t _TXCUTC_LPFCUT_full(uint8_t srate)
{
switch (srate) {
case FSK_SRATE_50K:
return RF_FLC80KHZ;
case FSK_SRATE_100K:
return RF_FLC160KHZ;
case FSK_SRATE_150K:
return RF_FLC250KHZ;
case FSK_SRATE_200K:
return RF_FLC315KHZ;
case FSK_SRATE_300K:
return RF_FLC500KHZ;
case FSK_SRATE_400K:
return RF_FLC625KHZ;
}
return 0;
}
static inline uint8_t _TXCUTC_LPFCUT(uint8_t srate, bool half)
{
return half ? _TXCUTC_LPFCUT_half(srate) : _TXCUTC_LPFCUT_full(srate);
}
/* Table 6-60. Recommended Configuration values of the sub-1GHz Receiver Frontend (modulation index 1/2) */
static uint8_t _RXBWC_BW_subGHz_half(uint8_t srate)
{
switch (srate) {
case FSK_SRATE_50K:
return RF_BW160KHZ_IF250KHZ;
case FSK_SRATE_100K:
return RF_BW200KHZ_IF250KHZ;
case FSK_SRATE_150K:
case FSK_SRATE_200K:
return RF_BW320KHZ_IF500KHZ;
case FSK_SRATE_300K:
return (RF_BW500KHZ_IF500KHZ | (1 << RXBWC_IFS_SHIFT));
case FSK_SRATE_400K:
return RF_BW630KHZ_IF1000KHZ;
}
return 0;
}
/* Table 6-61. Recommended Configuration values of the 2.4GHz Receiver Frontend (modulation index 1/2) */
static uint8_t _RXBWC_BW_2dot4GHz_half(uint8_t srate)
{
switch (srate) {
case FSK_SRATE_50K:
return RF_BW160KHZ_IF250KHZ;
case FSK_SRATE_100K:
return RF_BW200KHZ_IF250KHZ;
case FSK_SRATE_150K:
return RF_BW320KHZ_IF500KHZ;
case FSK_SRATE_200K:
return RF_BW400KHZ_IF500KHZ;
case FSK_SRATE_300K:
return RF_BW630KHZ_IF1000KHZ;
case FSK_SRATE_400K:
return RF_BW800KHZ_IF1000KHZ;
}
return 0;
}
/* Table 6-62. Recommended Configuration values of the sub-1GHz Receiver Frontend (modulation index 1) */
static uint8_t _RXBWC_BW_subGHz_full(uint8_t srate)
{
switch (srate) {
case FSK_SRATE_50K:
return RF_BW160KHZ_IF250KHZ;
case FSK_SRATE_100K:
return RF_BW320KHZ_IF500KHZ;
case FSK_SRATE_150K:
return RF_BW400KHZ_IF500KHZ;
case FSK_SRATE_200K:
return (RF_BW500KHZ_IF500KHZ | (1 << RXBWC_IFS_SHIFT));
case FSK_SRATE_300K:
return RF_BW630KHZ_IF1000KHZ;
case FSK_SRATE_400K:
return (RF_BW1000KHZ_IF1000KHZ | (1 << RXBWC_IFS_SHIFT));
}
return 0;
}
/* Table 6-63. Recommended Configuration values of the 2.4 GHz Receiver Frontend (modulation index 1) */
static uint8_t _RXBWC_BW_2dot4GHz_full(uint8_t srate)
{
switch (srate) {
case FSK_SRATE_50K:
return RF_BW200KHZ_IF250KHZ;
case FSK_SRATE_100K:
return RF_BW400KHZ_IF500KHZ;
case FSK_SRATE_150K:
case FSK_SRATE_200K:
return RF_BW630KHZ_IF1000KHZ;
case FSK_SRATE_300K:
return RF_BW800KHZ_IF1000KHZ;
case FSK_SRATE_400K:
return (RF_BW1000KHZ_IF1000KHZ | (1 << RXBWC_IFS_SHIFT));
}
return 0;
}
static inline uint8_t _RXBWC_BW(uint8_t srate, bool subGHz, bool half)
{
if (subGHz) {
return half ? _RXBWC_BW_subGHz_half(srate) : _RXBWC_BW_subGHz_full(srate);
} else {
return half ? _RXBWC_BW_2dot4GHz_half(srate) : _RXBWC_BW_2dot4GHz_full(srate);
}
}
static uint8_t _RXDFE_RCUT_half(uint8_t srate, bool subGHz)
{
if (srate == FSK_SRATE_200K) {
return RF_RCUT_FS_BY_5P3;
}
if (srate == FSK_SRATE_400K && !subGHz) {
return RF_RCUT_FS_BY_5P3;
}
return RF_RCUT_FS_BY_8;
}
static uint8_t _RXDFE_RCUT_full(uint8_t srate, bool subGHz)
{
switch (srate) {
case FSK_SRATE_50K:
case FSK_SRATE_100K:
case FSK_SRATE_300K:
return RF_RCUT_FS_BY_5P3;
case FSK_SRATE_150K:
case FSK_SRATE_400K:
return subGHz ? RF_RCUT_FS_BY_5P3 : RF_RCUT_FS_BY_4;
case FSK_SRATE_200K:
return subGHz ? RF_RCUT_FS_BY_4 : RF_RCUT_FS_BY_2P6;
}
return 0;
}
/* Table 6-64. Minimum Preamble Length */
static uint8_t _FSKPL(uint8_t srate)
{
switch (srate) {
case FSK_SRATE_50K:
return 2;
case FSK_SRATE_100K:
return 3;
case FSK_SRATE_150K:
case FSK_SRATE_200K:
case FSK_SRATE_300K:
return 8;
case FSK_SRATE_400K:
return 10;
}
return 0;
}
/* fsk modulation indices / 8 */
static const uint8_t _fsk_mod_idx[] = {
3, 4, 6, 8, 10, 12, 14, 16
};
/* FSK modulation scale / 8 */
static const uint8_t _fsk_mod_idx_scale[] = {
7, 8, 9, 10
};
static void _fsk_mod_idx_get(uint8_t num, uint8_t *idx, uint8_t *scale)
{
*idx = 0;
*scale = 0;
uint8_t diff = 0xFF;
for (uint8_t i = 0; i < ARRAY_SIZE(_fsk_mod_idx_scale); ++i) {
for (uint8_t j = 0; j < ARRAY_SIZE(_fsk_mod_idx); ++j) {
if (abs(num - _fsk_mod_idx_scale[i] * _fsk_mod_idx[j]) < diff) {
diff = abs(num - _fsk_mod_idx_scale[i] * _fsk_mod_idx[j]);
*idx = j;
*scale = i;
}
}
}
}
static inline uint8_t _RXDFE_RCUT(uint8_t srate, bool subGHz, bool half)
{
return half ? _RXDFE_RCUT_half(srate, subGHz) : _RXDFE_RCUT_full(srate, subGHz);
}
void at86rf215_FSK_prepare_rx(at86rf215_t *dev)
{
at86rf215_reg_write(dev, dev->BBC->RG_FSKPLL, dev->fsk_pl);
/* Preamble detection takes RSSI values into account if the preamble length is less than 8 octets. */
if (dev->fsk_pl < 8) {
at86rf215_reg_or(dev, dev->BBC->RG_FSKC2, FSKC2_PDTM_MASK);
} else {
at86rf215_reg_and(dev, dev->BBC->RG_FSKC2, (uint8_t) ~FSKC2_PDTM_MASK);
}
}
void at86rf215_FSK_prepare_tx(at86rf215_t *dev)
{
/* send long preamble when TXing */
at86rf215_reg_write(dev, dev->BBC->RG_FSKPLL, dev->fsk_pl * 8);
}
static void _set_srate(at86rf215_t *dev, uint8_t srate, bool mod_idx_half)
{
/* Set Receiver Bandwidth: fBW = 160 kHz, fIF = 250 kHz */
at86rf215_reg_write(dev, dev->RF->RG_RXBWC, _RXBWC_BW(srate, is_subGHz(dev), mod_idx_half));
/* fS = 400 kHz; fCUT = fS/5.333 = 75 kHz */
at86rf215_reg_write(dev, dev->RF->RG_RXDFE, _RXDFE_SR(srate)
| _RXDFE_RCUT(srate, is_subGHz(dev), mod_idx_half));
/* Power Amplifier Ramp Time = 32 µs; fLPCUT = 80 kHz */
at86rf215_reg_write(dev, dev->RF->RG_TXCUTC, _TXCUTC_PARAMP(srate)
| _TXCUTC_LPFCUT(srate, mod_idx_half));
/* fS = 500 kHz; fCUT = fS/2 = 250 kHz */
at86rf215_reg_write(dev, dev->RF->RG_TXDFE, _TXDFE_SR(dev, srate)
| (mod_idx_half ? RF_RCUT_FS_BY_8 : RF_RCUT_FS_BY_2)
| TXDFE_DM_MASK);
/* configure pre-emphasis */
at86rf215_reg_write(dev, dev->BBC->RG_FSKPE0, _FSKPE_Val[0][srate]);
at86rf215_reg_write(dev, dev->BBC->RG_FSKPE1, _FSKPE_Val[1][srate]);
at86rf215_reg_write(dev, dev->BBC->RG_FSKPE2, _FSKPE_Val[2][srate]);
/* set preamble length in octets */
dev->fsk_pl = _FSKPL(srate);
at86rf215_FSK_prepare_rx(dev);
/* t_on = t_off = t_min (time to TX minimal preamble length) */
uint8_t t_on = 4 * _FSKPL(srate) * 100 / _at86rf215_fsk_srate_10kHz[srate];
at86rf215_reg_write(dev, dev->BBC->RG_FSKRPCONT, t_on);
at86rf215_reg_write(dev, dev->BBC->RG_FSKRPCOFFT, t_on);
DEBUG("[at86rf215] t_on: %d µs\n", t_on);
/* set symbol rate, preamble is less than 256 so set high bits 0 */
at86rf215_reg_write(dev, dev->BBC->RG_FSKC1, srate);
}
static void _set_ack_timeout(at86rf215_t *dev, bool mord4, bool fec)
{
uint8_t ack_len = AT86RF215_ACK_PSDU_BYTES;
/* PHR uses same data rate as PSDU */
ack_len += 2;
/* 4-FSK doubles data rate */
if (mord4) {
ack_len /= 2;
}
/* forward error correction halves data rate */
if (fec) {
ack_len *= 2;
}
dev->ack_timeout_usec = dev->csma_backoff_period
+ IEEE802154G_ATURNAROUNDTIME_US
/* long Preamble + SFD; SFD=2 */
+ ((dev->fsk_pl * 8 + 2)
+ ack_len) * 8 * FSK_SYMBOL_TIME_US;
DEBUG("[%s] ACK timeout: %"PRIu32" µs\n", "FSK", dev->ack_timeout_usec);
}
static void _set_csma_backoff_period(at86rf215_t *dev)
{
dev->csma_backoff_period = IEEE802154_CCA_DURATION_IN_SYMBOLS * FSK_SYMBOL_TIME_US
+ IEEE802154G_ATURNAROUNDTIME_US;
DEBUG("[%s] CSMA BACKOFF: %"PRIu32" µs\n", "FSK", dev->csma_backoff_period);
}
int at86rf215_configure_FSK(at86rf215_t *dev, uint8_t srate, uint8_t mod_idx, uint8_t mod_order, uint8_t fec)
{
if (srate > FSK_SRATE_400K) {
DEBUG("[%s] invalid symbol rate: %d\n", __func__, srate);
return -EINVAL;
}
bool mod_idx_half = mod_idx <= 32;
uint8_t _mod_idx, _mod_idx_scale;
_fsk_mod_idx_get(mod_idx, &_mod_idx, &_mod_idx_scale);
at86rf215_await_state_end(dev, RF_STATE_TX);
/* disable radio */
at86rf215_reg_write(dev, dev->BBC->RG_PC, 0);
_set_srate(dev, srate, mod_idx_half);
/* set receiver gain target according to data sheet */
at86rf215_reg_write(dev, dev->RF->RG_AGCS, 1 << AGCS_TGT_SHIFT);
/* enable automatic receiver gain */
at86rf215_reg_write(dev, dev->RF->RG_AGCC, AGCC_EN_MASK);
/* set Bandwidth Time Product, Modulation Index & Modulation Order */
/* effective modulation index = MIDXS * MIDX */
at86rf215_reg_write(dev, dev->BBC->RG_FSKC0, FSK_BT_20
| (_mod_idx << FSKC0_MIDX_SHIFT)
| (_mod_idx_scale << FSKC0_MIDXS_SHIFT)
| mod_order
);
/* enable direct modulation */
at86rf215_reg_write(dev, dev->BBC->RG_FSKDM, FSKDM_EN_MASK | FSKDM_PE_MASK);
/* 16 µs base time */
uint8_t fskrpc = 0x5;
/* Enable / Disable Reduced Power Consumption */
if (dev->flags & AT86RF215_OPT_RPC) {
fskrpc |= FSKRPC_EN_MASK;
}
at86rf215_reg_write(dev, dev->BBC->RG_FSKRPC, fskrpc);
/* set forward error correction */
at86rf215_FSK_set_fec(dev, fec);
at86rf215_FSK_set_channel_spacing(dev, FSK_CHANNEL_SPACING_400K);
at86rf215_enable_radio(dev, BB_MRFSK);
return 0;
}
uint8_t at86rf215_FSK_get_mod_order(at86rf215_t *dev)
{
return at86rf215_reg_read(dev, dev->BBC->RG_FSKC0) & FSKC0_MORD_MASK;
}
int at86rf215_FSK_set_mod_order(at86rf215_t *dev, uint8_t mod_order) {
at86rf215_await_state_end(dev, RF_STATE_TX);
if (mod_order) {
at86rf215_reg_or(dev, dev->BBC->RG_FSKC0, FSK_MORD_4SFK);
} else {
at86rf215_reg_and(dev, dev->BBC->RG_FSKC0, ~FSK_MORD_4SFK);
}
_set_ack_timeout(dev, mod_order, at86rf215_FSK_get_fec(dev));
return 0;
}
uint8_t at86rf215_FSK_get_mod_idx(at86rf215_t *dev)
{
uint8_t fskc0 = at86rf215_reg_read(dev, dev->BBC->RG_FSKC0);
uint8_t _mod_idx = (fskc0 & FSKC0_MIDX_MASK) >> FSKC0_MIDX_SHIFT;
uint8_t _mod_idx_scale = (fskc0 & FSKC0_MIDXS_MASK) >> FSKC0_MIDXS_SHIFT;
return _fsk_mod_idx[_mod_idx] * _fsk_mod_idx_scale[_mod_idx_scale];
}
int at86rf215_FSK_set_mod_idx(at86rf215_t *dev, uint8_t mod_idx)
{
uint8_t _mod_idx, _mod_idx_scale;
at86rf215_await_state_end(dev, RF_STATE_TX);
_set_srate(dev, at86rf215_FSK_get_srate(dev), mod_idx <= 32);
_fsk_mod_idx_get(mod_idx, &_mod_idx, &_mod_idx_scale);
at86rf215_reg_write(dev, dev->BBC->RG_FSKC0, FSK_BT_20
| (_mod_idx << FSKC0_MIDX_SHIFT)
| (_mod_idx_scale << FSKC0_MIDXS_SHIFT)
| at86rf215_FSK_get_mod_order(dev)
);
return 0;
}
uint8_t at86rf215_FSK_get_srate(at86rf215_t *dev)
{
return at86rf215_reg_read(dev, dev->BBC->RG_FSKC1) & FSKC1_SRATE_MASK;
}
int at86rf215_FSK_set_srate(at86rf215_t *dev, uint8_t srate)
{
if (srate > FSK_SRATE_400K) {
return -1;
}
at86rf215_await_state_end(dev, RF_STATE_TX);
_set_srate(dev, srate, at86rf215_FSK_get_mod_idx(dev) <= 32);
_set_csma_backoff_period(dev);
return 0;
}
int at86rf215_FSK_set_fec(at86rf215_t *dev, uint8_t mode)
{
at86rf215_await_state_end(dev, RF_STATE_TX);
switch (mode) {
case IEEE802154_FEC_NONE:
at86rf215_reg_and(dev, dev->BBC->RG_FSKPHRTX, ~FSKPHRTX_SFD_MASK);
break;
case IEEE802154_FEC_NRNSC:
at86rf215_reg_or(dev, dev->BBC->RG_FSKPHRTX, FSKPHRTX_SFD_MASK);
at86rf215_reg_and(dev, dev->BBC->RG_FSKC2, ~FSKC2_FECS_MASK);
break;
case IEEE802154_FEC_RSC:
at86rf215_reg_or(dev, dev->BBC->RG_FSKPHRTX, FSKPHRTX_SFD_MASK);
at86rf215_reg_or(dev, dev->BBC->RG_FSKC2, FSKC2_FECS_MASK);
break;
default:
return -1;
}
_set_ack_timeout(dev, mode, at86rf215_FSK_get_mod_order(dev));
_set_csma_backoff_period(dev);
return 0;
}
uint8_t at86rf215_FSK_get_fec(at86rf215_t *dev)
{
/* SFD0 -> Uncoded IEEE mode */
/* SFD1 -> Coded IEEE mode */
if (!(at86rf215_reg_read(dev, dev->BBC->RG_FSKPHRTX) & FSKPHRTX_SFD_MASK)) {
return IEEE802154_FEC_NONE;
}
if (at86rf215_reg_read(dev, dev->BBC->RG_FSKC2) & FSKC2_FECS_MASK) {
return IEEE802154_FEC_RSC;
} else {
return IEEE802154_FEC_NRNSC;
}
}
int at86rf215_FSK_set_channel_spacing(at86rf215_t *dev, uint8_t ch_space)
{
if (ch_space > FSK_CHANNEL_SPACING_400K) {
return -1;
}
at86rf215_await_state_end(dev, RF_STATE_TX);
/* set channel spacing, same for both sub-GHz & 2.4 GHz */
at86rf215_reg_write(dev, dev->RF->RG_CS, _at86rf215_fsk_channel_spacing_25kHz[ch_space]);
/* set center frequency */
if (is_subGHz(dev)) {
at86rf215_reg_write16(dev, dev->RF->RG_CCF0L, _chan_center_freq0_subghz_25khz[ch_space]);
} else {
at86rf215_reg_write16(dev, dev->RF->RG_CCF0L, _chan_center_freq0_24ghz_25khz[ch_space]);
}
DEBUG("CCF0 configured as: %lu kHz\n",
25UL * at86rf215_reg_read16(dev, dev->RF->RG_CCF0L) + (is_subGHz(dev) ? 0 : CCF0_24G_OFFSET));
/* adjust channel spacing */
dev->num_chans = is_subGHz(dev) ? 34 / (ch_space + 1) : (416 / (ch_space + 1)) - (ch_space * 2);
dev->netdev.chan = at86rf215_chan_valid(dev, dev->netdev.chan);
at86rf215_reg_write16(dev, dev->RF->RG_CNL, dev->netdev.chan);
return 0;
}