mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
8f1eaae353
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>
234 lines
7.6 KiB
C
234 lines
7.6 KiB
C
/*
|
|
* Copyright (C) 2013 Alaeddine Weslati <alaeddine.weslati@inria.fr>
|
|
* Copyright (C) 2015 Freie Universität Berlin
|
|
* 2017 HAW Hamburg
|
|
*
|
|
* 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 public functions for AT86RF2xx drivers
|
|
*
|
|
* @author Alaeddine Weslati <alaeddine.weslati@inria.fr>
|
|
* @author Thomas Eichinger <thomas.eichinger@fu-berlin.de>
|
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
|
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
|
* @author Oliver Hahm <oliver.hahm@inria.fr>
|
|
* @author Sebastian Meiling <s@mlng.net>
|
|
* @}
|
|
*/
|
|
|
|
|
|
#include "luid.h"
|
|
#include "byteorder.h"
|
|
#include "net/ieee802154.h"
|
|
#include "net/gnrc.h"
|
|
#include "at86rf2xx_registers.h"
|
|
#include "at86rf2xx_internal.h"
|
|
#include "at86rf2xx_netdev.h"
|
|
|
|
#define ENABLE_DEBUG (0)
|
|
#include "debug.h"
|
|
|
|
|
|
void at86rf2xx_setup(at86rf2xx_t *dev, const at86rf2xx_params_t *params)
|
|
{
|
|
netdev_t *netdev = (netdev_t *)dev;
|
|
|
|
netdev->driver = &at86rf2xx_driver;
|
|
/* State to return after receiving or transmitting */
|
|
dev->idle_state = AT86RF2XX_STATE_TRX_OFF;
|
|
/* radio state is P_ON when first powered-on */
|
|
dev->state = AT86RF2XX_STATE_P_ON;
|
|
dev->pending_tx = 0;
|
|
|
|
#if defined(MODULE_AT86RFA1) || defined(MODULE_AT86RFR2)
|
|
(void) params;
|
|
/* set all interrupts off */
|
|
at86rf2xx_reg_write(dev, AT86RF2XX_REG__IRQ_MASK, 0x00);
|
|
#else
|
|
/* initialize device descriptor */
|
|
dev->params = *params;
|
|
#endif
|
|
}
|
|
|
|
static void at86rf2xx_disable_clock_output(at86rf2xx_t *dev)
|
|
{
|
|
#if defined(MODULE_AT86RFA1) || defined(MODULE_AT86RFR2)
|
|
(void) dev;
|
|
#else
|
|
uint8_t tmp = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_CTRL_0);
|
|
tmp &= ~(AT86RF2XX_TRX_CTRL_0_MASK__CLKM_CTRL);
|
|
tmp &= ~(AT86RF2XX_TRX_CTRL_0_MASK__CLKM_SHA_SEL);
|
|
tmp |= (AT86RF2XX_TRX_CTRL_0_CLKM_CTRL__OFF);
|
|
at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_CTRL_0, tmp);
|
|
#endif
|
|
}
|
|
|
|
static void at86rf2xx_enable_smart_idle(at86rf2xx_t *dev)
|
|
{
|
|
#if AT86RF2XX_SMART_IDLE_LISTENING
|
|
uint8_t tmp = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_RPC);
|
|
tmp |= (AT86RF2XX_TRX_RPC_MASK__RX_RPC_EN |
|
|
AT86RF2XX_TRX_RPC_MASK__PDT_RPC_EN |
|
|
AT86RF2XX_TRX_RPC_MASK__PLL_RPC_EN |
|
|
AT86RF2XX_TRX_RPC_MASK__XAH_TX_RPC_EN |
|
|
AT86RF2XX_TRX_RPC_MASK__IPAN_RPC_EN);
|
|
at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_RPC, tmp);
|
|
at86rf2xx_set_rxsensitivity(dev, RSSI_BASE_VAL);
|
|
#else
|
|
(void) dev;
|
|
#endif
|
|
}
|
|
|
|
void at86rf2xx_reset(at86rf2xx_t *dev)
|
|
{
|
|
eui64_t addr_long;
|
|
|
|
at86rf2xx_hardware_reset(dev);
|
|
|
|
netdev_ieee802154_reset(&dev->netdev);
|
|
|
|
/* Reset state machine to ensure a known state */
|
|
if (dev->state == AT86RF2XX_STATE_P_ON) {
|
|
at86rf2xx_set_state(dev, AT86RF2XX_STATE_FORCE_TRX_OFF);
|
|
}
|
|
|
|
/* get an 8-byte unique ID to use as hardware address */
|
|
luid_get(addr_long.uint8, IEEE802154_LONG_ADDRESS_LEN);
|
|
/* make sure we mark the address as non-multicast and not globally unique */
|
|
addr_long.uint8[0] &= ~(0x01);
|
|
addr_long.uint8[0] |= (0x02);
|
|
/* set short and long address */
|
|
at86rf2xx_set_addr_long(dev, ntohll(addr_long.uint64.u64));
|
|
at86rf2xx_set_addr_short(dev, ntohs(addr_long.uint16[0].u16));
|
|
|
|
/* set default channel */
|
|
at86rf2xx_set_chan(dev, AT86RF2XX_DEFAULT_CHANNEL);
|
|
/* set default TX power */
|
|
at86rf2xx_set_txpower(dev, AT86RF2XX_DEFAULT_TXPOWER);
|
|
/* set default options */
|
|
at86rf2xx_set_option(dev, AT86RF2XX_OPT_AUTOACK, true);
|
|
at86rf2xx_set_option(dev, AT86RF2XX_OPT_CSMA, true);
|
|
|
|
static const netopt_enable_t enable = NETOPT_ENABLE;
|
|
netdev_ieee802154_set(&dev->netdev, NETOPT_ACK_REQ,
|
|
&enable, sizeof(enable));
|
|
|
|
/* enable safe mode (protect RX FIFO until reading data starts) */
|
|
at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_CTRL_2,
|
|
AT86RF2XX_TRX_CTRL_2_MASK__RX_SAFE_MODE);
|
|
#ifdef MODULE_AT86RF212B
|
|
at86rf2xx_set_page(dev, AT86RF2XX_DEFAULT_PAGE);
|
|
#endif
|
|
|
|
#if !defined(MODULE_AT86RFA1) && !defined(MODULE_AT86RFR2)
|
|
/* don't populate masked interrupt flags to IRQ_STATUS register */
|
|
uint8_t tmp = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_CTRL_1);
|
|
tmp &= ~(AT86RF2XX_TRX_CTRL_1_MASK__IRQ_MASK_MODE);
|
|
at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_CTRL_1, tmp);
|
|
#endif
|
|
|
|
/* configure smart idle listening feature */
|
|
at86rf2xx_enable_smart_idle(dev);
|
|
|
|
/* disable clock output to save power */
|
|
at86rf2xx_disable_clock_output(dev);
|
|
|
|
/* enable interrupts */
|
|
at86rf2xx_reg_write(dev, AT86RF2XX_REG__IRQ_MASK,
|
|
AT86RF2XX_IRQ_STATUS_MASK__TRX_END);
|
|
/* clear interrupt flags */
|
|
at86rf2xx_reg_read(dev, AT86RF2XX_REG__IRQ_STATUS);
|
|
|
|
/* State to return after receiving or transmitting */
|
|
dev->idle_state = AT86RF2XX_STATE_RX_AACK_ON;
|
|
/* go into RX state */
|
|
at86rf2xx_set_state(dev, AT86RF2XX_STATE_RX_AACK_ON);
|
|
|
|
DEBUG("at86rf2xx_reset(): reset complete.\n");
|
|
}
|
|
|
|
size_t at86rf2xx_send(at86rf2xx_t *dev, const uint8_t *data, size_t len)
|
|
{
|
|
/* check data length */
|
|
if (len > AT86RF2XX_MAX_PKT_LENGTH) {
|
|
DEBUG("[at86rf2xx] Error: data to send exceeds max packet size\n");
|
|
return 0;
|
|
}
|
|
at86rf2xx_tx_prepare(dev);
|
|
at86rf2xx_tx_load(dev, data, len, 0);
|
|
at86rf2xx_tx_exec(dev);
|
|
return len;
|
|
}
|
|
|
|
void at86rf2xx_tx_prepare(at86rf2xx_t *dev)
|
|
{
|
|
uint8_t state;
|
|
|
|
dev->pending_tx++;
|
|
state = at86rf2xx_set_state(dev, AT86RF2XX_STATE_TX_ARET_ON);
|
|
if (state != AT86RF2XX_STATE_TX_ARET_ON) {
|
|
dev->idle_state = state;
|
|
}
|
|
dev->tx_frame_len = IEEE802154_FCS_LEN;
|
|
}
|
|
|
|
size_t at86rf2xx_tx_load(at86rf2xx_t *dev, const uint8_t *data,
|
|
size_t len, size_t offset)
|
|
{
|
|
dev->tx_frame_len += (uint8_t)len;
|
|
at86rf2xx_sram_write(dev, offset + 1, data, len);
|
|
return offset + len;
|
|
}
|
|
|
|
void at86rf2xx_tx_exec(const at86rf2xx_t *dev)
|
|
{
|
|
netdev_t *netdev = (netdev_t *)dev;
|
|
|
|
/* write frame length field in FIFO */
|
|
at86rf2xx_sram_write(dev, 0, &(dev->tx_frame_len), 1);
|
|
/* trigger sending of pre-loaded frame */
|
|
at86rf2xx_reg_write(dev, AT86RF2XX_REG__TRX_STATE,
|
|
AT86RF2XX_TRX_STATE__TX_START);
|
|
if (netdev->event_callback &&
|
|
(dev->flags & AT86RF2XX_OPT_TELL_TX_START)) {
|
|
netdev->event_callback(netdev, NETDEV_EVENT_TX_STARTED);
|
|
}
|
|
}
|
|
|
|
bool at86rf2xx_cca(at86rf2xx_t *dev)
|
|
{
|
|
uint8_t reg;
|
|
uint8_t old_state = at86rf2xx_set_state(dev, AT86RF2XX_STATE_TRX_OFF);
|
|
/* Disable RX path */
|
|
uint8_t rx_syn = at86rf2xx_reg_read(dev, AT86RF2XX_REG__RX_SYN);
|
|
|
|
reg = rx_syn | AT86RF2XX_RX_SYN__RX_PDT_DIS;
|
|
at86rf2xx_reg_write(dev, AT86RF2XX_REG__RX_SYN, reg);
|
|
/* Manually triggered CCA is only possible in RX_ON (basic operating mode) */
|
|
at86rf2xx_set_state(dev, AT86RF2XX_STATE_RX_ON);
|
|
/* Perform CCA */
|
|
reg = at86rf2xx_reg_read(dev, AT86RF2XX_REG__PHY_CC_CCA);
|
|
reg |= AT86RF2XX_PHY_CC_CCA_MASK__CCA_REQUEST;
|
|
at86rf2xx_reg_write(dev, AT86RF2XX_REG__PHY_CC_CCA, reg);
|
|
/* Spin until done (8 symbols + 12 µs = 128 µs + 12 µs for O-QPSK)*/
|
|
do {
|
|
reg = at86rf2xx_reg_read(dev, AT86RF2XX_REG__TRX_STATUS);
|
|
} while ((reg & AT86RF2XX_TRX_STATUS_MASK__CCA_DONE) == 0);
|
|
/* return true if channel is clear */
|
|
bool ret = !!(reg & AT86RF2XX_TRX_STATUS_MASK__CCA_STATUS);
|
|
/* re-enable RX */
|
|
at86rf2xx_reg_write(dev, AT86RF2XX_REG__RX_SYN, rx_syn);
|
|
/* Step back to the old state */
|
|
at86rf2xx_set_state(dev, AT86RF2XX_STATE_TRX_OFF);
|
|
at86rf2xx_set_state(dev, old_state);
|
|
return ret;
|
|
}
|