mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
424 lines
16 KiB
C
424 lines
16 KiB
C
/*
|
|
* Copyright (C) 2017 Neo Nenaco <neo@nenaco.de>
|
|
* Copyright (C) 2016 Koen Zandberg <koen@bergzand.net>
|
|
*
|
|
* 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_mrf24j40
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief Getter and setter functions for the MRF24J40 drivers
|
|
*
|
|
* @author Koen Zandberg <koen@bergzand.net>
|
|
* @author Neo Nenaco <neo@nenaco.de>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include "byteorder.h"
|
|
#include "mrf24j40.h"
|
|
#include "mrf24j40_internal.h"
|
|
#include "mrf24j40_registers.h"
|
|
#include "ztimer.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
/* Values of RFCON3 - Address: 0x203
|
|
* 0b00000000 -> 0dB -> 0
|
|
* 0b00001000 -> -0.5dB -> 0
|
|
* 0b00010000 -> -1.2dB -> -1
|
|
* 0b00011000 -> -1.9dB -> -2
|
|
* 0b00100000 -> -2.8dB -> -3
|
|
* 0b00101000 -> -3.7dB -> -4
|
|
* 0b00110000 -> -4.9dB -> -5
|
|
* 0b00111000 -> -6.3dB -> -6
|
|
* 0b01000000 -> -10dB -> -10
|
|
* 0b01001000 -> -10.5dB-> -10
|
|
* 0b01010000 -> -11.2dB-> -11
|
|
* 0b01011000 -> -11.9dB-> -12
|
|
* 0b01100000 -> -12.8dB-> -13
|
|
* 0b01101000 -> -13.7dB-> -14
|
|
* 0b01110000 -> -14.9dB-> -15
|
|
* 0b01111000 -> -16.3dB-> -16
|
|
* 0b10000000 -> -20dB -> -20
|
|
* 0b10001000 -> -20.5dB-> -20
|
|
* 0b10010000 -> -21.2dB-> -21
|
|
* 0b10011000 -> -21.9dB-> -22
|
|
* 0b10100000 -> -22.8dB-> -23
|
|
* 0b10101000 -> -23.7dB-> -24
|
|
* 0b10110000 -> -24.9dB-> -25
|
|
* 0b10111000 -> -26.3dB-> -26
|
|
* 0b11000000 -> -30dB -> -30
|
|
* 0b11001000 -> -30.5dB-> -30
|
|
* 0b11010000 -> -31.2dB-> -31
|
|
* 0b11011000 -> -31.9dB-> -32
|
|
* 0b11100000 -> -32.8dB-> -33
|
|
* 0b11101000 -> -33.7dB-> -34
|
|
* 0b11110000 -> -34.9dB-> -35
|
|
* 0b11111000 -> -36.3dB-> -36
|
|
*/
|
|
|
|
static const uint8_t dbm_to_tx_pow[] = { 0x00, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38, 0x38, 0x38, 0x40,
|
|
0x40, 0x50, 0x58, 0x60, 0x68, 0x70, 0x78, 0x78, 0x78, 0x80,
|
|
0x80, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xb8, 0xb8, 0xc0,
|
|
0xc0, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8 };
|
|
|
|
/* take a look onto datasheet table 3-8 */
|
|
static const int8_t dBm_value[] = { -90, -89, -88, -88, -87, -87, -87, -87, \
|
|
-86, -86, -86, -86, -85, -85, -85, -85, \
|
|
-84, -84, -84, -84, -84, -84, -83, -83, \
|
|
-83, -83, -82, -82, -82, -82, -81, -81, \
|
|
-81, -81, -81, -80, -80, -80, -80, -80, \
|
|
-80, -79, -79, -79, -79, -79, -78, -78, \
|
|
-78, -78, -78, -77, -77, -77, -77, -77, \
|
|
-76, -76, -76, -76, -76, -75, -75, -75, \
|
|
-75, -75, -75, -74, -74, -74, -74, -73, \
|
|
-73, -73, -73, -73, -72, -72, -72, -72, \
|
|
-72, -71, -71, -71, -71, -71, -70, -70, \
|
|
-70, -70, -70, -70, -70, -69, -69, -69, \
|
|
-69, -69, -68, -68, -68, -68, -68, -68, \
|
|
-68, -67, -67, -67, -67, -66, -66, -66, \
|
|
-66, -66, -66, -65, -65, -65, -65, -65, \
|
|
-64, -64, -64, -64, -63, -63, -63, -63, \
|
|
-62, -62, -62, -62, -61, -61, -61, -61, \
|
|
-60, -60, -60, -60, -60, -59, -59, -59, \
|
|
-59, -59, -58, -58, -58, -58, -58, -57, \
|
|
-57, -57, -57, -57, -57, -56, -56, -56, \
|
|
-56, -56, -56, -56, -55, -55, -55, -55, \
|
|
-54, -54, -54, -54, -54, -54, -53, -53, \
|
|
-53, -53, -53, -53, -53, -52, -52, -52, \
|
|
-52, -52, -52, -51, -51, -51, -51, -51, \
|
|
-50, -50, -50, -50, -50, -49, -49, -49, \
|
|
-49, -49, -48, -48, -48, -48, -47, -47, \
|
|
-47, -47, -47, -46, -46, -46, -46, -45, \
|
|
-45, -45, -45, -44, -44, -44, -44, -44, \
|
|
-43, -43, -43, -42, -42, -42, -42, -41, \
|
|
-41, -41, -41, -41, -41, -40, -40, -40, \
|
|
-40, -40, -39, -39, -39, -39, -39, -38, \
|
|
-38, -38, -38, -37, -37, -37, -36, -35 };
|
|
|
|
/* take a look onto datasheet table 3-8 */
|
|
static const uint8_t RSSI_value[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfd, 0xfa, 0xf5, \
|
|
0xef, 0xe9, 0xe4, 0xe1, 0xdd, 0xd8, 0xd4, 0xcf, 0xcb, 0xc6, \
|
|
0xc1, 0xbc, 0xb7, 0xb0, 0xaa, 0xa5, 0x9f, 0x99, 0x94, 0x8f, \
|
|
0x8a, 0x85, 0x81, 0x7d, 0x79, 0x75, 0x6f, 0x6b, 0x64, 0x5f, \
|
|
0x59, 0x53, 0x4e, 0x49, 0x44, 0x3f, 0x3a, 0x35, 0x30, 0x2b, \
|
|
0x25, 0x20, 0x1b, 0x17, 0x12, 0x0d, 0x09, 0x05, 0x02, 0x01, \
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
static void mrf24j40_baseband_reset(mrf24j40_t *dev)
|
|
{
|
|
uint8_t softrst;
|
|
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_SOFTRST, MRF24J40_SOFTRST_RSTBB);
|
|
do {
|
|
softrst = mrf24j40_reg_read_short(dev, MRF24J40_REG_SOFTRST);
|
|
} while (softrst != 0); /* wait until soft-reset has finished */
|
|
}
|
|
|
|
void mrf24j40_set_addr_short(mrf24j40_t *dev, uint16_t addr)
|
|
{
|
|
network_uint16_t naddr;
|
|
naddr.u16 = addr;
|
|
|
|
#ifdef MODULE_SIXLOWPAN
|
|
/* https://tools.ietf.org/html/rfc4944#section-12 requires the first bit to
|
|
* 0 for unicast addresses */
|
|
naddr.u8[0] &= 0x7F;
|
|
#endif
|
|
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_SADRL,
|
|
naddr.u8[1]);
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_SADRH,
|
|
naddr.u8[0]);
|
|
}
|
|
|
|
void mrf24j40_set_addr_long(mrf24j40_t *dev, const uint8_t *addr)
|
|
{
|
|
for (int i = 0; i < 8; i++) {
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_EADR0 + i, addr[7 - i]);
|
|
}
|
|
}
|
|
|
|
void mrf24j40_set_chan(mrf24j40_t *dev, uint8_t channel)
|
|
{
|
|
uint8_t channel_value;
|
|
|
|
if ((channel < IEEE802154_CHANNEL_MIN) ||
|
|
(channel > IEEE802154_CHANNEL_MAX)) {
|
|
return;
|
|
}
|
|
|
|
/* Channel settings
|
|
* 11 -> Value = 0x03
|
|
* 12 -> Value = 0x13
|
|
* 13 -> Value = 0x23
|
|
* 14 -> Value = 0x33
|
|
* 15 -> Value = 0x43
|
|
* 16 -> Value = 0x53
|
|
* 17 -> Value = 0x63
|
|
* 18 -> Value = 0x73
|
|
* 19 -> Value = 0x83
|
|
* 20 -> Value = 0x93
|
|
* 21 -> Value = 0xA3
|
|
* 22 -> Value = 0xB3
|
|
* 23 -> Value = 0xC3
|
|
* 24 -> Value = 0xD3
|
|
* 25 -> Value = 0xE3
|
|
* 26 -> Value = 0xF3
|
|
*/
|
|
/* not using an array here because it's not starting at zero */
|
|
switch (channel) {
|
|
case 11: channel_value = 0x03;
|
|
break;
|
|
case 12: channel_value = 0x13;
|
|
break;
|
|
case 13: channel_value = 0x23;
|
|
break;
|
|
case 14: channel_value = 0x33;
|
|
break;
|
|
case 15: channel_value = 0x43;
|
|
break;
|
|
case 16: channel_value = 0x53;
|
|
break;
|
|
case 17: channel_value = 0x63;
|
|
break;
|
|
case 18: channel_value = 0x73;
|
|
break;
|
|
case 19: channel_value = 0x83;
|
|
break;
|
|
case 20: channel_value = 0x93;
|
|
break;
|
|
case 21: channel_value = 0xa3;
|
|
break;
|
|
case 22: channel_value = 0xb3;
|
|
break;
|
|
case 23: channel_value = 0xc3;
|
|
break;
|
|
case 24: channel_value = 0xd3;
|
|
break;
|
|
case 25: channel_value = 0xe3;
|
|
break;
|
|
case 26: channel_value = 0xf3;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
mrf24j40_reg_write_long(dev, MRF24J40_REG_RFCON0, channel_value);
|
|
/*
|
|
* Note: Perform an RF State Machine Reset (see Section 3.1 Reset)
|
|
* after a channel frequency change. Then, delay at least 192 us after
|
|
* the RF State Machine Reset, to allow the RF circuitry to calibrate.
|
|
*/
|
|
mrf24j40_reset_state_machine(dev);
|
|
}
|
|
|
|
void mrf24j40_set_pan(mrf24j40_t *dev, uint16_t pan)
|
|
{
|
|
le_uint16_t le_pan = byteorder_htols(pan);
|
|
|
|
DEBUG("pan0: %u, pan1: %u\n", le_pan.u8[0], le_pan.u8[1]);
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_PANIDL, le_pan.u8[0]);
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_PANIDH, le_pan.u8[1]);
|
|
}
|
|
|
|
void mrf24j40_set_txpower(mrf24j40_t *dev, int16_t txpower)
|
|
{
|
|
uint8_t txpower_reg_value;
|
|
|
|
/* positive values are better with a conversion array */
|
|
txpower = (txpower < 0) ? -1 * txpower : txpower;
|
|
txpower = (txpower > 36) ? 36 : txpower;
|
|
txpower_reg_value = dbm_to_tx_pow[txpower];
|
|
|
|
mrf24j40_reg_write_long(dev, MRF24J40_REG_RFCON3, txpower_reg_value);
|
|
|
|
}
|
|
|
|
void mrf24j40_set_csma_max_retries(mrf24j40_t *dev, int8_t retries)
|
|
{
|
|
uint8_t tmp;
|
|
|
|
/* get current register settings */
|
|
tmp = mrf24j40_reg_read_short(dev, MRF24J40_REG_TXMCR);
|
|
/* clear csma bits */
|
|
tmp &= ~(MRF24J40_TXMCR_CSMA_BACKOFF_MASK);
|
|
/* apply new settings */
|
|
tmp |= (retries & MRF24J40_TXMCR_CSMA_BACKOFF_MASK);
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_TXMCR, tmp);
|
|
}
|
|
|
|
void mrf24j40_set_cca_threshold(mrf24j40_t *dev, int8_t value)
|
|
{
|
|
/* ensure the given value is negative, since a CCA threshold > 0 is
|
|
just impossible: thus, any positive value given is considered
|
|
to be the absolute value of the actually wanted threshold */
|
|
if (value < 0) {
|
|
value = -value;
|
|
}
|
|
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_CCAEDTH, RSSI_value[value]);
|
|
}
|
|
|
|
void mrf24j40_set_turbo(mrf24j40_t *dev, bool on)
|
|
{
|
|
uint8_t tmp;
|
|
static const uint8_t cfg[2][2] = {
|
|
{0xD0, 0x80}, /* IEEE mode */
|
|
{0x30, 0x40} /* Turbo mode */
|
|
};
|
|
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_BBREG0, on);
|
|
|
|
/* Set Preamble Search Energy Valid Threshold */
|
|
tmp = mrf24j40_reg_read_short(dev, MRF24J40_REG_BBREG3) & 0xF;
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_BBREG3, tmp | cfg[on][0]);
|
|
|
|
/* Set Carrier Sense Threshold */
|
|
tmp = mrf24j40_reg_read_short(dev, MRF24J40_REG_BBREG4) & 0x1F;
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_BBREG4, tmp | cfg[on][1]);
|
|
|
|
mrf24j40_baseband_reset(dev);
|
|
}
|
|
|
|
void mrf24j40_set_option(mrf24j40_t *dev, uint16_t option, bool state)
|
|
{
|
|
uint8_t tmp;
|
|
|
|
DEBUG("set option %i to %i\n", option, state);
|
|
|
|
/* set option field */
|
|
if (state) {
|
|
/* trigger option specific actions */
|
|
switch (option) {
|
|
case MRF24J40_OPT_CSMA:
|
|
DEBUG("[mrf24j40] opt: enabling CSMA mode (4 retries, macMinBE: 3 max BE: 5)\n");
|
|
/* Initialize CSMA seed with hardware address */
|
|
tmp = mrf24j40_reg_read_short(dev, MRF24J40_REG_TXMCR);
|
|
tmp &= ~MRF24J40_TXMCR_NOCSMA;
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_TXMCR, tmp);
|
|
break;
|
|
case MRF24J40_OPT_PROMISCUOUS:
|
|
DEBUG("[mrf24j40] opt: enabling PROMISCUOUS mode\n");
|
|
/* disable auto ACKs in promiscuous mode */
|
|
tmp = mrf24j40_reg_read_short(dev, MRF24J40_REG_RXMCR);
|
|
tmp |= MRF24J40_RXMCR_NOACKRSP;
|
|
/* enable promiscuous mode */
|
|
tmp |= MRF24J40_RXMCR_PROMI;
|
|
tmp &= ~MRF24J40_RXMCR_ERRPKT;
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_RXMCR, tmp);
|
|
break;
|
|
case NETDEV_IEEE802154_ACK_REQ:
|
|
DEBUG("[mrf24j40] opt: enabling auto ACKs\n");
|
|
tmp = mrf24j40_reg_read_short(dev, MRF24J40_REG_RXMCR);
|
|
tmp &= ~MRF24J40_RXMCR_NOACKRSP;
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_RXMCR, tmp);
|
|
break;
|
|
default:
|
|
/* do nothing */
|
|
break;
|
|
}
|
|
}
|
|
/* clear option field */
|
|
else {
|
|
/* trigger option specific actions */
|
|
switch (option) {
|
|
case MRF24J40_OPT_CSMA:
|
|
DEBUG("[mrf24j40] opt: disabling CSMA mode\n");
|
|
tmp = mrf24j40_reg_read_short(dev, MRF24J40_REG_TXMCR);
|
|
tmp |= MRF24J40_TXMCR_NOCSMA;
|
|
/* MACMINBE<1:0>: The minimum value of the backoff exponent
|
|
* in the CSMA-CA algorithm. Note that if this value is set
|
|
* to 0, collision avoidance is disabled. */
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_TXMCR, tmp);
|
|
break;
|
|
case MRF24J40_OPT_PROMISCUOUS:
|
|
DEBUG("[mrf24j40] opt: disabling PROMISCUOUS mode\n");
|
|
/* disable promiscuous mode */
|
|
tmp = mrf24j40_reg_read_short(dev, MRF24J40_REG_RXMCR);
|
|
tmp &= ~MRF24J40_RXMCR_PROMI;
|
|
tmp &= ~MRF24J40_RXMCR_ERRPKT;
|
|
/* re-enable AUTOACK only if the option is set */
|
|
tmp &= ~(MRF24J40_RXMCR_NOACKRSP);
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_RXMCR, tmp);
|
|
break;
|
|
case NETDEV_IEEE802154_ACK_REQ:
|
|
DEBUG("[mrf24j40] opt: disabling auto ACKs\n");
|
|
tmp = mrf24j40_reg_read_short(dev, MRF24J40_REG_RXMCR);
|
|
tmp |= MRF24J40_RXMCR_NOACKRSP;
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_RXMCR, tmp);
|
|
break;
|
|
default:
|
|
/* do nothing */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void mrf24j40_sleep(mrf24j40_t *dev)
|
|
{
|
|
DEBUG("[mrf24j40] Putting into sleep mode\n");
|
|
|
|
/* disable the PA & LNA */
|
|
mrf24j40_disable_auto_pa_lna(dev);
|
|
/* Datasheet chapter 3.15.2 IMMEDIATE SLEEP AND WAKE-UP MODE */
|
|
/* First force a Power Management Reset */
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_SOFTRST, MRF24J40_SOFTRST_RSTPWR);
|
|
/* Go to SLEEP mode */
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_SLPACK, MRF24J40_SLPACK_SLPACK);
|
|
}
|
|
|
|
void mrf24j40_wake_up(mrf24j40_t *dev)
|
|
{
|
|
DEBUG("[mrf24j40] Waking up from sleep mode\n");
|
|
/* Wake mrf up */
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_WAKECON, MRF24J40_WAKECON_IMMWAKE | MRF24J40_WAKECON_REGWAKE);
|
|
/* undocumented delay, needed for stable wakeup */
|
|
ztimer_sleep(ZTIMER_USEC, MRF24J40_DELAY_SLEEP_TOGGLE);
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_WAKECON, MRF24J40_WAKECON_IMMWAKE);
|
|
/* reset state machine */
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_RFCTL, MRF24J40_RFCTL_RFRST);
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_RFCTL, 0x00);
|
|
/* After wake-up, delay at least 2 ms to allow 20 MHz main
|
|
* oscillator time to stabilize before transmitting or receiving.
|
|
*/
|
|
ztimer_sleep(ZTIMER_USEC, MRF24J40_WAKEUP_DELAY);
|
|
/* reset interrupts */
|
|
mrf24j40_reg_read_short(dev, MRF24J40_REG_INTSTAT);
|
|
mrf24j40_enable_auto_pa_lna(dev);
|
|
}
|
|
|
|
void mrf24j40_reset_state_machine(mrf24j40_t *dev)
|
|
{
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_RFCTL, MRF24J40_RFCTL_RFRST);
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_RFCTL, 0x00);
|
|
ztimer_sleep(ZTIMER_USEC, MRF24J40_STATE_RESET_DELAY); /* Delay at least 192us */
|
|
}
|
|
|
|
void mrf24j40_software_reset(mrf24j40_t *dev)
|
|
{
|
|
uint8_t softrst;
|
|
|
|
mrf24j40_reg_write_short(dev, MRF24J40_REG_SOFTRST, MRF24J40_SOFTRST_RSTPWR |
|
|
MRF24J40_SOFTRST_RSTBB |
|
|
MRF24J40_SOFTRST_RSTMAC );
|
|
do {
|
|
softrst = mrf24j40_reg_read_short(dev, MRF24J40_REG_SOFTRST);
|
|
} while (softrst != 0); /* wait until soft-reset has finished */
|
|
}
|
|
|
|
int8_t mrf24j40_dbm_from_reg(uint8_t value)
|
|
{
|
|
return dBm_value[value];
|
|
}
|