/* * 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 * @} */ #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; }