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
Benjamin Valentin 8f1eaae353 drivers/at86rf2xx: add support for ATmegaRF MCUs
The ATmega128RFA1 and ATmega256RFR2 contain a version of this IP
on the MCU.

The radio core behaves mostly like a at86rf231, but all registers
are mapped to memory and radio states can directly generate interrupts
on the CPU.

The ATmega256RFR2 adds support for automatic retransmissions.
This has not been implemented yet.

Co-authored-by: Josua Arndt <jarndt@ias.rwth-aachen.de>
2019-10-26 23:10:18 +02:00

241 lines
7.8 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 "xtimer.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
xtimer_usleep(AT86RF2XX_WAKEUP_DELAY);
/* update state: on some platforms, the timer behind xtimer
* 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);
xtimer_usleep(AT86RF2XX_RESET_PULSE_WIDTH);
gpio_set(dev->params.reset_pin);
#endif
xtimer_usleep(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(const at86rf2xx_t *dev, uint8_t *data, size_t len)
{
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;
}
}
#endif