1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/nrf5x_common/radio/nrfble/nrfble.c
2020-10-23 00:46:26 +02:00

358 lines
9.5 KiB
C

/*
* Copyright (C) 2017-2018 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_nrf5x_nrfble
*
* @note This driver is not thread safe and should only be used by a
* single thread at once!
* @{
*
* @file
* @brief Bluetooth low energy radio driver for nRF5x SoCs
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*
* @}
*/
#include <errno.h>
#include "cpu.h"
#include "assert.h"
#include "nrfble.h"
#include "net/netdev/ble.h"
#define ENABLE_DEBUG 0
#include "debug.h"
/* driver specific device configuration */
#define CONF_MODE RADIO_MODE_MODE_Ble_1Mbit
#define CONF_LEN (8U)
#define CONF_S0 (1U)
#define CONF_S1 (0U)
#define CONF_STATLEN (0U)
#define CONF_BASE_ADDR_LEN (3U)
#define CONF_ENDIAN RADIO_PCNF1_ENDIAN_Little
#define CONF_WHITENING RADIO_PCNF1_WHITEEN_Enabled
#define CONF_TIFS (150U)
#define CONF_CRC_LEN (0X3 | RADIO_CRCCNF_SKIPADDR_Msk)
#define CONF_CRC_POLY (0x00065b)
/* used shortcuts */
#define SHORTS_BASE (RADIO_SHORTS_READY_START_Msk | \
RADIO_SHORTS_END_DISABLE_Msk)
#define SHORTS_RX (SHORTS_BASE | RADIO_SHORTS_DISABLED_TXEN_Msk)
#define SHORTS_TX (SHORTS_BASE | RADIO_SHORTS_DISABLED_RXEN_Msk)
/* interrupt masks */
#define INT_DIS (RADIO_INTENCLR_DISABLED_Msk | \
RADIO_INTENCLR_ADDRESS_Msk)
#define INT_EN (RADIO_INTENSET_DISABLED_Msk | \
RADIO_INTENSET_ADDRESS_Msk)
/* driver internal radio states */
#define STATE_IDLE (0x00)
#define STATE_RX (0x01)
#define STATE_TX (0x02)
#define STATE_BUSY (0x80)
/* forward declaration of the netdev driver struct */
static const netdev_driver_t netdev_driver;
/* current radio state */
static volatile uint8_t _state = STATE_IDLE;
/* active radio context */
static netdev_ble_ctx_t *_ctx = NULL;
/* allocate the netdev device descriptor */
netdev_t _nrfble_dev;
/* map logical BLE channel values to actual radio frequencies */
static const uint8_t _ble_chan_map[40] = {
[ 0] = 4,
[ 1] = 6,
[ 2] = 8,
[ 3] = 10,
[ 4] = 12,
[ 5] = 14,
[ 6] = 16,
[ 7] = 18,
[ 8] = 20,
[ 9] = 22,
[10] = 24,
[11] = 28,
[12] = 30,
[13] = 32,
[14] = 34,
[15] = 36,
[16] = 38,
[17] = 40,
[18] = 42,
[19] = 44,
[20] = 46,
[21] = 48,
[22] = 50,
[23] = 52,
[24] = 54,
[25] = 56,
[26] = 58,
[27] = 60,
[28] = 62,
[29] = 64,
[30] = 66,
[31] = 68,
[32] = 70,
[33] = 72,
[34] = 74,
[35] = 76,
[36] = 78,
[37] = 2,
[38] = 26,
[39] = 80,
};
/**
* @brief Set radio into idle (DISABLED) state
*/
static void _go_idle(void)
{
if (!(_state & STATE_BUSY)) {
NRF_RADIO->INTENCLR = INT_DIS;
NRF_RADIO->SHORTS = 0;
NRF_RADIO->EVENTS_DISABLED = 0;
NRF_RADIO->TASKS_DISABLE = 1;
while (NRF_RADIO->EVENTS_DISABLED == 0) {}
_state = STATE_IDLE;
}
}
/**
* @brief Set radio context (channel, CRC, whitening, and access address)
*/
static void _set_context(netdev_ble_ctx_t *ctx)
{
if (ctx) {
assert(ctx->chan <= NRFBLE_CHAN_MAX);
_ctx = ctx;
NRF_RADIO->FREQUENCY = _ble_chan_map[ctx->chan];
NRF_RADIO->PREFIX0 = ctx->aa.raw[3];
NRF_RADIO->BASE0 = (uint32_t)((ctx->aa.raw[0] << 8) |
(ctx->aa.raw[1] << 16) |
(ctx->aa.raw[2] << 24));
NRF_RADIO->DATAWHITEIV = ctx->chan;
NRF_RADIO->CRCINIT = (ctx->crc & NETDEV_BLE_CRC_MASK);
}
else {
_go_idle();
}
}
static int16_t _nrfble_get_txpower(void)
{
int8_t p = (int8_t)NRF_RADIO->TXPOWER;
if (p < 0) {
return (int16_t)(0xff00 | p);
}
return (int16_t)p;
}
static void _nrfble_set_txpower(int16_t power)
{
if (power > 2) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Pos4dBm;
}
else if (power > -2) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_0dBm;
}
else if (power > -6) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg4dBm;
}
else if (power > -10) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg8dBm;
}
else if (power > -14) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg12dBm;
}
else if (power > -18) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg16dBm;
}
else if (power > -25) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg20dBm;
}
else {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg30dBm;
}
}
/**
* @brief Radio interrupt routine
*/
void isr_radio(void)
{
if (NRF_RADIO->EVENTS_ADDRESS) {
NRF_RADIO->EVENTS_ADDRESS = 0;
_state |= STATE_BUSY;
}
if (NRF_RADIO->EVENTS_DISABLED) {
NRF_RADIO->EVENTS_DISABLED = 0;
if (_state == (STATE_BUSY | STATE_RX)) {
_state = STATE_TX;
if (NRF_RADIO->CRCSTATUS) {
_ctx->crc |= NETDEV_BLE_CRC_OK;
}
else {
_ctx->crc &= ~(NETDEV_BLE_CRC_OK);
}
_nrfble_dev.event_callback(&_nrfble_dev, NETDEV_EVENT_RX_COMPLETE);
}
else { /* on TX done */
_state = STATE_RX;
_nrfble_dev.event_callback(&_nrfble_dev, NETDEV_EVENT_TX_COMPLETE);
}
}
cortexm_isr_end();
}
netdev_t *nrfble_setup(void)
{
_nrfble_dev.driver = &netdev_driver;
_nrfble_dev.event_callback = NULL;
_nrfble_dev.context = NULL;
return &_nrfble_dev;
}
static int _nrfble_init(netdev_t *dev)
{
(void)dev;
assert(_nrfble_dev.driver && _nrfble_dev.event_callback);
/* power on the NRFs radio */
NRF_RADIO->POWER = 1;
/* configure variable parameters to default values */
NRF_RADIO->TXPOWER = NRFBLE_TXPOWER_DEFAULT;
/* always send from and listen to logical address 0 */
NRF_RADIO->TXADDRESS = 0x00UL;
NRF_RADIO->RXADDRESSES = 0x01UL;
/* load driver specific configuration */
NRF_RADIO->MODE = CONF_MODE;
/* configure data fields and packet length whitening and endianness */
NRF_RADIO->PCNF0 = ((CONF_S1 << RADIO_PCNF0_S1LEN_Pos) |
(CONF_S0 << RADIO_PCNF0_S0LEN_Pos) |
(CONF_LEN << RADIO_PCNF0_LFLEN_Pos));
NRF_RADIO->PCNF1 = ((CONF_WHITENING << RADIO_PCNF1_WHITEEN_Pos) |
(CONF_ENDIAN << RADIO_PCNF1_ENDIAN_Pos) |
(CONF_BASE_ADDR_LEN << RADIO_PCNF1_BALEN_Pos) |
(CONF_STATLEN << RADIO_PCNF1_STATLEN_Pos) |
(NETDEV_BLE_PDU_MAXLEN << RADIO_PCNF1_MAXLEN_Pos));
/* set inter frame spacing to 150us */
NRF_RADIO->TIFS = CONF_TIFS;
/* configure CRC length and polynomial */
NRF_RADIO->CRCCNF = CONF_CRC_LEN;
NRF_RADIO->CRCPOLY = CONF_CRC_POLY;
/* enable global interrupts, but mask all local interrupts for now */
NRF_RADIO->INTENCLR = 0xffffffff;
NVIC_EnableIRQ(RADIO_IRQn);
DEBUG("[nrfble] initialization successful\n");
return 0;
}
static int _nrfble_send(netdev_t *dev, const iolist_t *data)
{
(void)dev;
assert(data);
NRF_RADIO->PACKETPTR = (uint32_t)data->iol_base;
NRF_RADIO->SHORTS = SHORTS_TX;
/* in case no trx sequence is active, we start a new one now */
if (_state == STATE_IDLE) {
_state = STATE_TX;
NRF_RADIO->EVENTS_DISABLED = 0;
NRF_RADIO->INTENSET = INT_EN;
NRF_RADIO->TASKS_TXEN = 1;
}
return 0;
}
static int _nrfble_recv(netdev_t *dev, void *buf, size_t len, void *info)
{
(void)dev;
(void)len;
(void)info;
assert(buf && (len == sizeof(netdev_ble_pkt_t)));
NRF_RADIO->PACKETPTR = (uint32_t)buf;
NRF_RADIO->SHORTS = SHORTS_RX;
/* in case no trx sequence is active, we start a new one now */
if (_state == STATE_IDLE) {
_state = STATE_RX;
NRF_RADIO->EVENTS_DISABLED = 0;
NRF_RADIO->INTENSET = INT_EN;
NRF_RADIO->TASKS_RXEN = 1;
}
return 0;
}
static int _nrfble_get(netdev_t *dev, netopt_t opt, void *val, size_t max_len)
{
(void)dev;
(void)max_len;
switch (opt) {
case NETOPT_TX_POWER:
assert(max_len >= sizeof(int16_t));
*((int16_t *)val) = _nrfble_get_txpower();
return sizeof(int16_t);
case NETOPT_DEVICE_TYPE:
assert(max_len >= sizeof(uint16_t));
*((uint16_t *)val) = NETDEV_TYPE_BLE;
return sizeof(uint16_t);
default:
return -ENOTSUP;
}
}
static int _nrfble_set(netdev_t *dev, netopt_t opt, const void *val, size_t len)
{
(void)dev;
(void)len;
switch (opt) {
case NETOPT_BLE_CTX:
_set_context((netdev_ble_ctx_t *)val);
return sizeof(netdev_ble_ctx_t);
case NETOPT_TX_POWER:
assert(len == sizeof(int16_t));
_nrfble_set_txpower(*((const int16_t *)val));
return sizeof(int16_t);
default:
return -ENOTSUP;
}
}
/* export of the netdev interface */
static const netdev_driver_t netdev_driver = {
.send = _nrfble_send,
.recv = _nrfble_recv,
.init = _nrfble_init,
.isr = NULL,
.get = _nrfble_get,
.set = _nrfble_set
};