1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/at86rf2xx/at86rf2xx_internal.c
2022-10-29 20:15:16 +02:00

243 lines
7.9 KiB
C

/*
* Copyright (C) 2013 Alaeddine Weslati <alaeddine.weslati@inria.fr>
* Copyright (C) 2015 Freie Universität Berlin
*
* 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_at86rf2xx
* @{
*
* @file
* @brief Implementation of driver internal functions
*
* @author Alaeddine Weslati <alaeddine.weslati@inria.fr>
* @author Thomas Eichinger <thomas.eichinger@fu-berlin.de>
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @}
*/
#include "ztimer.h"
#include "at86rf2xx_internal.h"
#include "at86rf2xx_registers.h"
#if !defined(MODULE_AT86RFA1) && !defined(MODULE_AT86RFR2)
#include "periph/spi.h"
#include "periph/gpio.h"
#define SPIDEV (dev->params.spi)
#define CSPIN (dev->params.cs_pin)
static inline void getbus(const at86rf2xx_t *dev)
{
spi_acquire(SPIDEV, CSPIN, SPI_MODE_0, dev->params.spi_clk);
}
void at86rf2xx_reg_write(const at86rf2xx_t *dev, uint8_t addr, uint8_t value)
{
uint8_t reg = (AT86RF2XX_ACCESS_REG | AT86RF2XX_ACCESS_WRITE | addr);
getbus(dev);
spi_transfer_reg(SPIDEV, CSPIN, reg, value);
spi_release(SPIDEV);
}
uint8_t at86rf2xx_reg_read(const at86rf2xx_t *dev, uint8_t addr)
{
uint8_t reg = (AT86RF2XX_ACCESS_REG | AT86RF2XX_ACCESS_READ | addr);
uint8_t value;
getbus(dev);
value = spi_transfer_reg(SPIDEV, CSPIN, reg, 0);
spi_release(SPIDEV);
return value;
}
void at86rf2xx_sram_read(const at86rf2xx_t *dev, uint8_t offset,
uint8_t *data, size_t len)
{
uint8_t reg = (AT86RF2XX_ACCESS_SRAM | AT86RF2XX_ACCESS_READ);
getbus(dev);
spi_transfer_byte(SPIDEV, CSPIN, true, reg);
spi_transfer_byte(SPIDEV, CSPIN, true, offset);
spi_transfer_bytes(SPIDEV, CSPIN, false, NULL, data, len);
spi_release(SPIDEV);
}
void at86rf2xx_sram_write(const at86rf2xx_t *dev, uint8_t offset,
const uint8_t *data, size_t len)
{
uint8_t reg = (AT86RF2XX_ACCESS_SRAM | AT86RF2XX_ACCESS_WRITE);
getbus(dev);
spi_transfer_byte(SPIDEV, CSPIN, true, reg);
spi_transfer_byte(SPIDEV, CSPIN, true, offset);
spi_transfer_bytes(SPIDEV, CSPIN, false, data, NULL, len);
spi_release(SPIDEV);
}
void at86rf2xx_fb_start(const at86rf2xx_t *dev)
{
uint8_t reg = AT86RF2XX_ACCESS_FB | AT86RF2XX_ACCESS_READ;
getbus(dev);
spi_transfer_byte(SPIDEV, CSPIN, true, reg);
}
void at86rf2xx_fb_read(const at86rf2xx_t *dev,
uint8_t *data, size_t len)
{
spi_transfer_bytes(SPIDEV, CSPIN, true, NULL, data, len);
}
void at86rf2xx_fb_stop(const at86rf2xx_t *dev)
{
/* transfer one byte (which we ignore) to release the chip select */
spi_transfer_byte(SPIDEV, CSPIN, false, 1);
spi_release(SPIDEV);
}
#endif /* SPI based transceiver */
uint8_t at86rf2xx_get_status(const at86rf2xx_t *dev)
{
/* if sleeping immediately return state */
if (dev->state == AT86RF2XX_STATE_SLEEP) {
return dev->state;
}
return (at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_STATUS)
& AT86RF2XX_TRX_STATUS_MASK__TRX_STATUS);
}
void at86rf2xx_assert_awake(at86rf2xx_t *dev)
{
if (at86rf2xx_get_status(dev) == AT86RF2XX_STATE_SLEEP) {
/* wake up and wait for transition to TRX_OFF */
#if defined(MODULE_AT86RFA1) || defined(MODULE_AT86RFR2)
/* Setting SLPTR bit in TRXPR to 0 returns the radio transceiver
* to the TRX_OFF state */
*AT86RF2XX_REG__TRXPR &= ~(AT86RF2XX_TRXPR_SLPTR);
#else
gpio_clear(dev->params.sleep_pin);
#endif
ztimer_sleep(ZTIMER_USEC, AT86RF2XX_WAKEUP_DELAY);
/* update state: on some platforms, the timer behind ztimer
* may be inaccurate or the radio itself may take longer
* to wake up due to extra capacitance on the oscillator.
* Spin until we are actually awake
*/
do {
dev->state = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_STATUS)
& AT86RF2XX_TRX_STATUS_MASK__TRX_STATUS;
} while (dev->state != AT86RF2XX_TRX_STATUS__TRX_OFF);
}
}
void at86rf2xx_hardware_reset(at86rf2xx_t *dev)
{
/* trigger hardware reset */
#if defined(MODULE_AT86RFA1) || defined(MODULE_AT86RFR2)
/* set reset Bit */
*(AT86RF2XX_REG__TRXPR) |= AT86RF2XX_TRXPR_TRXRST;
#else
gpio_clear(dev->params.reset_pin);
ztimer_sleep(ZTIMER_USEC, AT86RF2XX_RESET_PULSE_WIDTH);
gpio_set(dev->params.reset_pin);
#endif
ztimer_sleep(ZTIMER_USEC, AT86RF2XX_RESET_DELAY);
/* update state: if the radio state was P_ON (initialization phase),
* it remains P_ON. Otherwise, it should go to TRX_OFF
*/
do {
dev->state = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_STATUS)
& AT86RF2XX_TRX_STATUS_MASK__TRX_STATUS;
} while ((dev->state != AT86RF2XX_STATE_TRX_OFF)
&& (dev->state != AT86RF2XX_STATE_P_ON));
}
void at86rf2xx_configure_phy(at86rf2xx_t *dev)
{
/* we must be in TRX_OFF before changing the PHY configuration */
uint8_t prev_state = at86rf2xx_set_state(dev, AT86RF2XX_STATE_TRX_OFF);
#ifdef MODULE_AT86RF212B
/* The TX power register must be updated after changing the channel if
* moving between bands. */
int16_t txpower = at86rf2xx_get_txpower(dev);
uint8_t trx_ctrl2 = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_CTRL_2);
uint8_t rf_ctrl0 = at86rf2xx_reg_read(dev, AT86RF2XX_REG__RF_CTRL_0);
/* Clear previous configuration for PHY mode */
trx_ctrl2 &= ~(AT86RF2XX_TRX_CTRL_2_MASK__FREQ_MODE);
/* Clear previous configuration for GC_TX_OFFS */
rf_ctrl0 &= ~AT86RF2XX_RF_CTRL_0_MASK__GC_TX_OFFS;
if (dev->netdev.chan != 0) {
/* Set sub mode bit on 915 MHz as recommended by the data sheet */
trx_ctrl2 |= AT86RF2XX_TRX_CTRL_2_MASK__SUB_MODE;
}
if (dev->page == 0) {
/* BPSK coding */
/* Data sheet recommends using a +2 dB setting for BPSK */
rf_ctrl0 |= AT86RF2XX_RF_CTRL_0_GC_TX_OFFS__2DB;
}
else if (dev->page == 2) {
/* O-QPSK coding */
trx_ctrl2 |= AT86RF2XX_TRX_CTRL_2_MASK__BPSK_OQPSK;
/* Data sheet recommends using a +1 dB setting for O-QPSK */
rf_ctrl0 |= AT86RF2XX_RF_CTRL_0_GC_TX_OFFS__1DB;
}
at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_CTRL_2, trx_ctrl2);
at86rf2xx_reg_write(dev, AT86RF2XX_REG__RF_CTRL_0, rf_ctrl0);
#endif
uint8_t phy_cc_cca = at86rf2xx_reg_read(dev, AT86RF2XX_REG__PHY_CC_CCA);
/* Clear previous configuration for channel number */
phy_cc_cca &= ~(AT86RF2XX_PHY_CC_CCA_MASK__CHANNEL);
/* Update the channel register */
phy_cc_cca |= (dev->netdev.chan & AT86RF2XX_PHY_CC_CCA_MASK__CHANNEL);
at86rf2xx_reg_write(dev, AT86RF2XX_REG__PHY_CC_CCA, phy_cc_cca);
#ifdef MODULE_AT86RF212B
/* Update the TX power register to achieve the same power (in dBm) */
at86rf2xx_set_txpower(dev, txpower);
#endif
/* Return to the state we had before reconfiguring */
at86rf2xx_set_state(dev, prev_state);
}
#if AT86RF2XX_RANDOM_NUMBER_GENERATOR
void at86rf2xx_get_random(at86rf2xx_t *dev, uint8_t *data, size_t len)
{
at86rf2xx_disable_smart_idle(dev);
for (size_t byteCount = 0; byteCount < len; ++byteCount) {
uint8_t rnd = 0;
for (uint8_t i = 0; i < 4; ++i) {
/* bit 5 and 6 of the AT86RF2XX_REG__PHY_RSSI register contain the RND_VALUE */
uint8_t regVal = at86rf2xx_reg_read(dev, AT86RF2XX_REG__PHY_RSSI)
& AT86RF2XX_PHY_RSSI_MASK__RND_VALUE;
/* shift the two random bits first to the right and then to the correct position of the return byte */
regVal = regVal >> 5;
regVal = regVal << 2 * i;
rnd |= regVal;
}
data[byteCount] = rnd;
}
at86rf2xx_enable_smart_idle(dev);
}
#endif