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

Merge pull request #14010 from benpicco/at86rf215-mr-ofdm

drivers/at86rf215: implement MR-OFDM
This commit is contained in:
benpicco 2020-06-03 16:39:04 +02:00 committed by GitHub
commit 3a1ee4983c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 513 additions and 9 deletions

View File

@ -74,6 +74,7 @@ ifneq (,$(filter at86rf215%,$(USEMODULE)))
DEFAULT_MODULE += netdev_ieee802154_oqpsk
DEFAULT_MODULE += netdev_ieee802154_mr_oqpsk
DEFAULT_MODULE += netdev_ieee802154_mr_ofdm
ifeq (,$(filter at86rf215m,$(USEMODULE)))
DEFAULT_MODULE += at86rf215_24ghz

View File

@ -151,6 +151,10 @@ if (!IS_ACTIVE(CONFIG_AT86RF215_USE_CLOCK_OUTPUT)){
at86rf215_configure_OQPSK(dev, AT86RF215_DEFAULT_MR_OQPSK_CHIPS,
AT86RF215_DEFAULT_MR_OQPSK_RATE);
}
if (AT86RF215_DEFAULT_PHY_MODE == IEEE802154_PHY_MR_OFDM) {
at86rf215_configure_OFDM(dev, CONFIG_AT86RF215_DEFAULT_MR_OFDM_OPT,
CONFIG_AT86RF215_DEFAULT_MR_OFDM_MCS);
}
/* set default channel */
at86rf215_set_chan(dev, dev->netdev.chan);

View File

@ -394,6 +394,18 @@ static int _get(netdev_t *netdev, netopt_t opt, void *val, size_t max_len)
res = max_len;
break;
case NETOPT_MR_OFDM_OPTION:
assert(max_len >= sizeof(int8_t));
*((int8_t *)val) = at86rf215_OFDM_get_option(dev);
res = max_len;
break;
case NETOPT_MR_OFDM_MCS:
assert(max_len >= sizeof(int8_t));
*((int8_t *)val) = at86rf215_OFDM_get_scheme(dev);
res = max_len;
break;
case NETOPT_MR_OQPSK_CHIPS:
assert(max_len >= sizeof(int16_t));
switch (at86rf215_OQPSK_get_chips(dev)) {
@ -574,11 +586,43 @@ static int _set(netdev_t *netdev, netopt_t opt, const void *val, size_t len)
at86rf215_OQPSK_get_mode(dev));
res = sizeof(uint8_t);
break;
case IEEE802154_PHY_MR_OFDM:
at86rf215_configure_OFDM(dev,
at86rf215_OFDM_get_option(dev),
at86rf215_OFDM_get_scheme(dev));
res = sizeof(uint8_t);
break;
default:
return -ENOTSUP;
}
break;
case NETOPT_MR_OFDM_OPTION:
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_MR_OFDM) {
return -ENOTSUP;
}
assert(len <= sizeof(uint8_t));
if (at86rf215_OFDM_set_option(dev, *((const uint8_t *)val)) == 0) {
res = sizeof(uint8_t);
} else {
res = -ERANGE;
}
break;
case NETOPT_MR_OFDM_MCS:
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_MR_OFDM) {
return -ENOTSUP;
}
assert(len <= sizeof(uint8_t));
if (at86rf215_OFDM_set_scheme(dev, *((const uint8_t *)val)) == 0) {
res = sizeof(uint8_t);
} else {
res = -ERANGE;
}
break;
case NETOPT_MR_OQPSK_CHIPS:
if (at86rf215_get_phy_mode(dev) != IEEE802154_PHY_MR_OQPSK) {
return -ENOTSUP;

View File

@ -0,0 +1,325 @@
/*
* 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-OFDM 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 120 µs for MR-OFDM */
#define OFDM_SYMBOL_TIME_US 120
/* IEEE Std 802.15.4g™-2012 Amendment 3
* Table 68dTotal number of channels and first channel center frequencies for SUN PHYs */
static uint32_t _channel_spacing_kHz(uint8_t option)
{
switch (option) {
case 1: return 1200;
case 2: return 800;
case 3: return 400;
case 4: return 200;
}
return 0;
}
/* IEEE Std 802.15.4g™-2012 Amendment 3
* Table 68dTotal number of channels and first channel center frequencies for SUN PHYs */
static uint32_t _channel_center_freq_kHz_868MHz(uint8_t option)
{
switch (option) {
case 1: return 863625;
case 2: return 863425;
case 3: return 863225;
case 4: return 863125;
}
return 0;
}
/* IEEE Std 802.15.4g™-2012 Amendment 3
* Table 68dTotal number of channels and first channel center frequencies for SUN PHYs */
static uint32_t _channel_center_freq_kHz_2400MHz(uint8_t option)
{
return 2400000 + _channel_spacing_kHz(option) - CCF0_24G_OFFSET;
}
/* IEEE Std 802.15.4g™-2012 Amendment 3
* Table 68dTotal number of channels and first channel center frequencies for SUN PHYs */
static uint16_t _get_max_chan(at86rf215_t *dev, uint8_t option)
{
if (is_subGHz(dev)) {
switch (option) {
case 1: return 5;
case 2: return 8;
case 3: return 17;
case 4: return 34;
}
}
else {
switch (option) {
case 1: return 64;
case 2: return 97;
case 3: return 207;
case 4: return 416;
}
}
return 0;
}
/* Table 6-90. Recommended Transmitter Frontend Configuration */
static uint32_t _TXCUTC_LPFCUT(uint8_t option)
{
switch (option) {
case 1: return 10 << TXCUTC_LPFCUT_SHIFT;
case 2: return 8 << TXCUTC_LPFCUT_SHIFT;
case 3: return 5 << TXCUTC_LPFCUT_SHIFT;
case 4: return 3 << TXCUTC_LPFCUT_SHIFT;
}
return 0;
}
/* Table 6-90. Recommended Transmitter Frontend Configuration */
static uint32_t _TXDFE_SR(uint8_t option)
{
switch (option) {
case 1:
case 2: return 3 << TXDFE_SR_SHIFT;
case 3:
case 4: return 6 << TXDFE_SR_SHIFT;
}
return 0;
}
/* Table 6-90. Recommended Transmitter Frontend Configuration */
static uint32_t _TXDFE_RCUT(uint8_t option)
{
switch (option) {
case 1: return 3 << TXDFE_RCUT_SHIFT;
case 2:
case 3: return 3 << TXDFE_RCUT_SHIFT;
case 4: return 2 << TXDFE_RCUT_SHIFT;
}
return 0;
}
/* Table 6-93. Recommended PHY Receiver and Digital Frontend Configuration */
static uint32_t _RXDFE_RCUT(uint8_t option, bool superGHz)
{
switch (option) {
case 1: return 4 << RXDFE_RCUT_SHIFT;
case 2: return 2 << RXDFE_RCUT_SHIFT;
case 3: return (2 + superGHz) << RXDFE_RCUT_SHIFT;
case 4: return 1 << RXDFE_RCUT_SHIFT;
}
return 0;
}
/* Table 6-93. Recommended PHY Receiver and Digital Frontend Configuration */
static uint32_t _RXBWC_BW(uint8_t option, bool superGHz)
{
switch (option) {
case 1: return (9 + superGHz) << RXBWC_BW_SHIFT;
case 2: return 7 << RXBWC_BW_SHIFT;
case 3: return (4 + superGHz) << RXBWC_BW_SHIFT;
case 4: return (2 + superGHz) << RXBWC_BW_SHIFT;
}
return 0;
}
/* Table 6-93. Recommended PHY Receiver and Digital Frontend Configuration */
static uint32_t _RXBWC_IFS(uint8_t option, bool superGHz)
{
switch (option) {
case 1:
case 2: return 1;
case 3: return superGHz;
case 4: return !superGHz;
}
return 0;
}
static void _set_option(at86rf215_t *dev, uint8_t option)
{
const bool superGHz = !is_subGHz(dev);
/* Set Receiver Bandwidth */
at86rf215_reg_write(dev, dev->RF->RG_RXBWC,
_RXBWC_BW(option, superGHz) | _RXBWC_IFS(option, superGHz));
/* Set fS (same as TX); fCUT for RX */
at86rf215_reg_write(dev, dev->RF->RG_RXDFE,
_TXDFE_SR(option) | _RXDFE_RCUT(option, superGHz));
/* Set Power Amplifier Ramp Time; fLPCUT */
at86rf215_reg_write(dev, dev->RF->RG_TXCUTC,
RF_PARAMP8U | _TXCUTC_LPFCUT(option));
/* Set fS; fCUT for TX */
at86rf215_reg_write(dev, dev->RF->RG_TXDFE,
_TXDFE_SR(option) | _TXDFE_RCUT(option));
/* set channel spacing with 25 kHz resolution */
at86rf215_reg_write(dev, dev->RF->RG_CS, _channel_spacing_kHz(option) / 25);
/* set channel center frequency with 25 kHz resolution */
if (superGHz) {
at86rf215_reg_write16(dev, dev->RF->RG_CCF0L,
1 + _channel_center_freq_kHz_2400MHz(option) / 25);
}
else {
at86rf215_reg_write16(dev, dev->RF->RG_CCF0L,
1 + _channel_center_freq_kHz_868MHz(option) / 25);
}
at86rf215_reg_write(dev, dev->BBC->RG_OFDMC, option - 1);
/* make sure channel config is still valid */
dev->num_chans = _get_max_chan(dev, option);
dev->netdev.chan = at86rf215_chan_valid(dev, dev->netdev.chan);
at86rf215_reg_write16(dev, dev->RF->RG_CNL, dev->netdev.chan);
}
static unsigned _get_frame_duration(uint8_t option, uint8_t scheme,
uint8_t bytes)
{
/* Table 150 - phySymbolsPerOctet values for MR-OFDM PHY, IEEE 802.15.4g-2012 */
static const uint8_t quot[] = { 3, 3, 6, 12, 18, 24, 36 };
--option;
/* phyMaxFrameDuration = phySHRDuration + phyPHRDuration + ceiling [(aMaxPHYPacketSize + 1) x phySymbolsPerOctet] */
const unsigned phySHRDuration = 6;
const unsigned phyPHRDuration = option ? 6 : 3;
const unsigned phyPDUDuration = ((bytes + 1) * (1 << option) + quot[scheme] - 1)
/ quot[scheme];
return (phySHRDuration + phyPHRDuration + phyPDUDuration) * OFDM_SYMBOL_TIME_US;
}
static void _set_ack_timeout(at86rf215_t *dev, uint8_t option, uint8_t scheme)
{
dev->ack_timeout_usec = dev->csma_backoff_period
+ IEEE802154G_ATURNAROUNDTIME_US
+ _get_frame_duration(option, scheme,
AT86RF215_ACK_PSDU_BYTES);
DEBUG("[%s] ACK timeout: %" PRIu32 " µs\n", "OFDM", dev->ack_timeout_usec);
}
static bool _option_mcs_valid(uint8_t option, uint8_t mcs)
{
if (option < 1 || option > 4) {
return false;
}
if (mcs > BB_MCS_16QAM_3BY4) {
return false;
}
if (mcs == BB_MCS_BPSK_REP4 && option > 2) {
return false;
}
if (mcs == BB_MCS_BPSK_REP2 && option == 4) {
return false;
}
return true;
}
int at86rf215_configure_OFDM(at86rf215_t *dev, uint8_t option, uint8_t scheme)
{
if (!_option_mcs_valid(option, scheme)) {
DEBUG("[%s] invalid option/MCS: %d | %d\n", __func__, option, scheme);
return -EINVAL;
}
at86rf215_await_state_end(dev, RF_STATE_TX);
/* disable radio */
at86rf215_reg_write(dev, dev->BBC->RG_PC, 0);
/* set receiver gain target according to data sheet */
at86rf215_reg_write(dev, dev->RF->RG_AGCS, 3 << AGCS_TGT_SHIFT);
/* enable automatic receiver gain */
at86rf215_reg_write(dev, dev->RF->RG_AGCC, AGCC_EN_MASK);
_set_option(dev, option);
at86rf215_reg_write(dev, dev->BBC->RG_OFDMPHRTX, scheme);
dev->csma_backoff_period = AT86RF215_BACKOFF_PERIOD_IN_SYMBOLS *
OFDM_SYMBOL_TIME_US;
DEBUG("[%s] CSMA BACKOFF: %" PRIu32 " µs\n", "OFDM",
dev->csma_backoff_period);
_set_ack_timeout(dev, option, scheme);
at86rf215_enable_radio(dev, BB_MROFDM);
return 0;
}
int at86rf215_OFDM_set_scheme(at86rf215_t *dev, uint8_t scheme)
{
uint8_t option = at86rf215_OFDM_get_option(dev);
if (!_option_mcs_valid(option, scheme)) {
DEBUG("[%s] invalid MCS: %d\n", __func__, scheme);
return -1;
}
at86rf215_await_state_end(dev, RF_STATE_TX);
at86rf215_reg_write(dev, dev->BBC->RG_OFDMPHRTX, scheme);
_set_ack_timeout(dev, at86rf215_OFDM_get_option(dev), scheme);
return 0;
}
uint8_t at86rf215_OFDM_get_scheme(at86rf215_t *dev)
{
return at86rf215_reg_read(dev, dev->BBC->RG_OFDMPHRTX) & OFDMPHRTX_MCS_MASK;
}
int at86rf215_OFDM_set_option(at86rf215_t *dev, uint8_t option)
{
uint8_t mcs = at86rf215_OFDM_get_scheme(dev);
if (!_option_mcs_valid(option, mcs)) {
DEBUG("[%s] invalid option: %d\n", __func__, option);
return -1;
}
at86rf215_await_state_end(dev, RF_STATE_TX);
_set_option(dev, option);
_set_ack_timeout(dev, option, mcs);
return 0;
}
uint8_t at86rf215_OFDM_get_option(at86rf215_t *dev)
{
return 1 + (at86rf215_reg_read(dev, dev->BBC->RG_OFDMC) & OFDMC_OPT_MASK);
}

View File

@ -137,6 +137,64 @@ void at86rf215_filter_ack(at86rf215_t *dev, bool on);
*/
void at86rf215_get_random(at86rf215_t *dev, void *data, size_t len);
/**
* @brief Configure the radio to make use of OFDM modulation.
* There are 4 OFDM options, each with a different number of active tones.
* The device supports BPSK, QPSK and 16-QAM modulation and coding schemes (MCS)
*
* @param[in] dev device to configure
* @param[in] option modulation option, each increment halves the data rate
* @param[in] mcs modulation scheme, `BB_MCS_BPSK_REP4` `BB_MCS_16QAM_3BY4`
*
* @return 0 on success, error otherwise
*/
int at86rf215_configure_OFDM(at86rf215_t *dev, uint8_t option, uint8_t mcs);
/**
* @brief Set the current modulation and coding scheme (MCS)
*
* @param[in] dev device to configure
* @param[in] mcs modulation and coding scheme
*
* @return 0 on success, error otherwise
*/
int at86rf215_OFDM_set_scheme(at86rf215_t *dev, uint8_t mcs);
/**
* @brief Get the current modulation and coding scheme (MCS)
*
* @param[in] dev device to read from
*
* @return the current modulation & coding scheme
*/
uint8_t at86rf215_OFDM_get_scheme(at86rf215_t *dev);
/**
* @brief Set the current OFDM option
*
* @param[in] dev device to configure
* @param[in] option OFDM option
*
* @return 0 on success, error otherwise
*/
int at86rf215_OFDM_set_option(at86rf215_t *dev, uint8_t option);
/**
* @brief Get the current OFDM option
*
* @param[in] dev device to read from
*
* @return the current OFDM option
*/
uint8_t at86rf215_OFDM_get_option(at86rf215_t *dev);
/** @} */
/**
* @defgroup drivers_at86rf215_oqpsk AT86RF215 MR-O-QPSK PHY
* @{
*/
/**
* @brief Configure the radio to make use of O-QPSK modulation.
* The rate mode may be
@ -455,15 +513,6 @@ void at86rf215_disable_rpc(at86rf215_t *dev);
*/
void at86rf215_enable_rpc(at86rf215_t *dev);
/**
* @brief Notify the driver and stack about a change in transmission mode
* which may result in a change of PDU.
*
* @param[in] dev device that changed it's mode
* @param[in] new_mode the new transmission mode
*/
bool at86rf215_switch_mode(at86rf215_t *dev, uint8_t new_mode);
/**
* @brief Checks whether the device operates in the sub-GHz band.
*

View File

@ -137,6 +137,24 @@ enum {
#endif
/** @} */
/**
* @name Default MR-OFDM Option
* @{
*/
#ifndef CONFIG_AT86RF215_DEFAULT_MR_OFDM_OPT
#define CONFIG_AT86RF215_DEFAULT_MR_OFDM_OPT (2)
#endif
/** @} */
/**
* @name Default MR-OFDM Modulation & Coding Scheme
* @{
*/
#ifndef CONFIG_AT86RF215_DEFAULT_MR_OFDM_MCS
#define CONFIG_AT86RF215_DEFAULT_MR_OFDM_MCS (2)
#endif
/** @} */
/**
* @brief Default TX power (0dBm)
*/

View File

@ -673,6 +673,16 @@ typedef enum {
*/
NETOPT_MR_OQPSK_RATE,
/**
* @brief (uint8_t) MR-OFDM PHY Option (Values: 1-4)
*/
NETOPT_MR_OFDM_OPTION,
/**
* @brief (uint8_t) MR-OFDM PHY Modulation and Coding Scheme (Values: 0-6)
*/
NETOPT_MR_OFDM_MCS,
/**
* @brief (uint8_t) PHY Channel Spacing (kHz)
*/

View File

@ -110,6 +110,8 @@ static const char *_netopt_strmap[] = {
[NETOPT_OQPSK_RATE] = "NETOPT_O-QPSK_RATE",
[NETOPT_MR_OQPSK_CHIPS] = "NETOPT_MR-O-QPSK_CHIPS",
[NETOPT_MR_OQPSK_RATE] = "NETOPT_MR-O-QPSK_RATE",
[NETOPT_MR_OFDM_OPTION] = "NETOPT_MR-OFDM_OPTION",
[NETOPT_MR_OFDM_MCS] = "NETOPT_MR-OFDM_MCS",
[NETOPT_CHANNEL_SPACING] = "NETOPT_CHANNEL_SPACING",
[NETOPT_SYNCWORD] = "NETOPT_SYNCWORD",
[NETOPT_RANDOM] = "NETOPT_RANDOM",

View File

@ -190,6 +190,10 @@ static void _set_usage(char *cmd_name)
#ifdef MODULE_NETDEV_IEEE802154_MR_OQPSK
" * \"chip_rate\" - BPSK/QPSK chip rate in kChip/s\n"
" * \"rate_mode\" - BPSK/QPSK rate mode\n"
#endif
#ifdef MODULE_NETDEV_IEEE802154_MR_OFDM
" * \"option\" - OFDM option\n"
" * \"scheme\" - OFDM modulation & coding scheme\n"
#endif
" * \"power\" - TX power in dBm\n"
" * \"retrans\" - max. number of retransmissions\n"
@ -346,6 +350,17 @@ static void _print_netopt(netopt_t opt)
break;
#endif /* MODULE_NETDEV_IEEE802154_MR_OQPSK */
#ifdef MODULE_NETDEV_IEEE802154_MR_OFDM
case NETOPT_MR_OFDM_OPTION:
printf("OFDM option");
break;
case NETOPT_MR_OFDM_MCS:
printf("modulation/coding scheme");
break;
#endif /* MODULE_NETDEV_IEEE802154_MR_OFDM */
case NETOPT_CHECKSUM:
printf("checksum");
@ -414,6 +429,18 @@ static const char *_netopt_ieee802154_phy_str[] = {
};
#endif
#ifdef MODULE_NETDEV_IEEE802154_MR_OFDM
static const char *_netopt_ofdm_mcs_str[] = {
[0] = "BPSK, rate 1/2, 4x frequency repetition",
[1] = "BPSK, rate 1/2, 2x frequency repetition",
[2] = "QPSK, rate 1/2, 2x frequency repetition",
[3] = "QPSK, rate 1/2",
[4] = "QPSK, rate 3/4",
[5] = "16-QAM, rate 1/2",
[6] = "16-QAM, rate 3/4",
};
#endif
/* for some lines threshold might just be 0, so we can't use _LINE_THRESHOLD
* here */
static unsigned _newline(unsigned threshold, unsigned line_thresh)
@ -580,6 +607,20 @@ static void _netif_list(netif_t *iface)
break;
#endif /* MODULE_NETDEV_IEEE802154_MR_OQPSK */
#ifdef MODULE_NETDEV_IEEE802154_MR_OFDM
case IEEE802154_PHY_MR_OFDM:
printf("\n ");
res = netif_get_opt(iface, NETOPT_MR_OFDM_OPTION, 0, &u8, sizeof(u8));
if (res >= 0) {
printf(" Option: %u ", u8);
}
res = netif_get_opt(iface, NETOPT_MR_OFDM_MCS, 0, &u8, sizeof(u8));
if (res >= 0) {
printf(" MCS: %u (%s) ", u8, _netopt_ofdm_mcs_str[u8]);
}
break;
#endif /* MODULE_NETDEV_IEEE802154_MR_OFDM */
}
}
#endif /* MODULE_NETDEV_IEEE802154 */
@ -1300,6 +1341,14 @@ static int _netif_set(char *cmd_name, netif_t *iface, char *key, char *value)
return _netif_set_u8(iface, NETOPT_MR_OQPSK_RATE, 0, value);
}
#endif /* MODULE_NETDEV_IEEE802154_MR_OQPSK */
#ifdef MODULE_NETDEV_IEEE802154_MR_OFDM
else if ((strcmp("option", key) == 0) || (strcmp("opt", key) == 0)) {
return _netif_set_u8(iface, NETOPT_MR_OFDM_OPTION, 0, value);
}
else if ((strcmp("scheme", key) == 0) || (strcmp("mcs", key) == 0)) {
return _netif_set_u8(iface, NETOPT_MR_OFDM_MCS, 0, value);
}
#endif /* MODULE_NETDEV_IEEE802154_MR_OFDM */
else if ((strcmp("channel", key) == 0) || (strcmp("chan", key) == 0)) {
return _netif_set_u16(iface, NETOPT_CHANNEL, 0, value);
}

View File

@ -5,7 +5,9 @@ BOARD_INSUFFICIENT_MEMORY := \
arduino-nano \
arduino-uno \
atmega328p \
derfmega128 \
i-nucleo-lrwan1 \
microduino-corerf \
nucleo-f031k6 \
nucleo-f042k6 \
nucleo-f303k8 \