mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
d35511bee7
This adds a driver for the SPI based AT86RF215 transceiver. The chip supports the IEEE Std 802.15.4-2015 and IEEE Std 802.15.4g-2012 standard. This driver supports two versions of the chip: - AT86RF215: dual sub-GHz & 2.4 GHz radio & baseband - AT86RF215M: sub-GHz radio & baseband only Both radios support the following PHY modes: - MR-FSK - MR-OFDM - MR-O-QPKS - O-QPSK (legacy) The driver currently only implements support for legacy O-QPSK. To use both interfaces, add GNRC_NETIF_NUMOF := 2 to your Makefile. The transceiver is able to send frames of up to 2047 bytes according to IEEE 802.15.4g-2012 when operating in non-legacy mode. Known issues: - [ ] dBm setting values are bogus - [ ] Channel spacing for sub-GHz MR-O-QPSK might be wrong - [ ] TX/RX stress test will lock up the driver on openmote-b
278 lines
7.9 KiB
C
278 lines
7.9 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-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 868–870 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 */
|
||
|
||
/* 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;
|
||
}
|
||
}
|
||
|
||
/* Table 6-100. MR-O-QPSK Modes */
|
||
static uint32_t _get_bitrate(uint8_t chips, uint8_t mode)
|
||
{
|
||
switch (chips) {
|
||
case BB_FCHIP100:
|
||
return 6250 * (1 << mode);
|
||
case BB_FCHIP200:
|
||
return 12500 * (1 << mode);
|
||
case BB_FCHIP1000:
|
||
case BB_FCHIP2000:
|
||
return mode ? 125000 * (1 << (mode - 1)) : 31250;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
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 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);
|
||
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)
|
||
{
|
||
dev->csma_backoff_period = AT86RF215_BACKOFF_PERIOD_IN_BITS * US_PER_SEC / _get_bitrate(chips, mode);
|
||
DEBUG("[%s] CSMA BACKOFF: %"PRIu32" µs\n", "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);
|
||
|
||
at86rf215_enable_radio(dev, BB_MROQPSK);
|
||
}
|
||
|
||
int at86rf215_configure_legacy_OQPSK(at86rf215_t *dev, bool high_rate)
|
||
{
|
||
/* 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;
|
||
|
||
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_ack_timeout(dev, chips, mode);
|
||
|
||
_end_configure_OQPSK(dev);
|
||
|
||
return 0;
|
||
}
|