Add nrf5 platform (#830)

* Add NVRAM support

This is an EEPROM replacement based on MCU's internal Flash memory.
The NVRAM class is designed to be stateless. Each operation is
written to flash immediately.

The last 16k(nRF51)/32k(nRF52) of Flash memory are managed by the
VirtualPage class.  This class allows writing a single emulated 4k
page up 40,000 times.

The NVRAM class is a log based byte-wise storage allowing up to
40,000,000 writes depending on the number of used addresses.  Each
four allocated addresses from the beginning of the NVRAM reduce the
write log by one.

When first 256 bytes of the emulated storage are used, there is an
average writing capacity of 145,000 cycles per byte.

When the write log is full, the NVRAM is compressed into a new flash
page. After a new page is ready, the old page is released. This
process can take up to 4500 ms and cannot be interrupted.

The NVRAM functionality can easily be ported to ESP8266 or SAMD by
providing a Flash interface.

* Add NRF5 platform support

This is the support of the Nordic Semiconductor NRF51 (nrf51822,
nrf51422) and NRF52 (nrf52832) platform. Based on arduino-nRF5
(https://github.com/sandeepmistry/arduino-nRF5).

The included radio driver is compatible with the NRF24 devices.
Using a SoftDevice (BLE)is not supported and must be removed before
flashing MySensors with a "mass_erase" operation.
This commit is contained in:
d00616
2017-05-14 11:53:01 +02:00
committed by Olivier
parent a4c7b249c1
commit 726a20dad9
26 changed files with 3576 additions and 11 deletions
+148
View File
@@ -0,0 +1,148 @@
/*
Flash.cpp - Flash library
Original Copyright (c) 2017 Frank Holtz. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "drivers/NVM/Flash.h"
FlashClass Flash;
uint32_t FlashClass::page_size() const
{
return (size_t)NRF_FICR->CODEPAGESIZE;
}
uint8_t FlashClass::page_size_bits() const
{
#if defined(NRF51)
return 10;
#elif defined(NRF52)
return 12;
#endif
}
uint32_t FlashClass::page_count() const
{
return (uint32_t)NRF_FICR->CODESIZE;
}
uint32_t FlashClass::specified_erase_cycles() const
{
return FLASH_ERASE_CYCLES;
}
uint32_t *FlashClass::page_address(size_t page)
{
return (uint32_t *)(page << page_size_bits());
}
void FlashClass::erase(uint32_t *address, size_t size)
{
size_t end_address = (size_t)address + size;
// align address
address =
(uint32_t *)((size_t)address & (size_t)((size_t)(~0) - FLASH_PAGE_SIZE));
// Wrong parameters?
if ((size_t)address >= end_address) {
return;
}
// get old nvm controller state
uint32_t old_config = NRF_NVMC->CONFIG;
// Enable erasing flash
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos;
// Erase page(s)
while ((size_t)address < end_address) {
wait_for_ready();
// Erase one 1k/4k page
NRF_NVMC->ERASEPAGE = (size_t)(address);
address = (uint32_t *)((size_t)address + FLASH_PAGE_SIZE);
}
// Disable erasing
wait_for_ready();
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
// Restore old state
wait_for_ready();
NRF_NVMC->CONFIG = old_config;
// Go back if controller is ready
wait_for_ready();
}
void FlashClass::erase_all()
{
// Enable erasing flash
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos;
wait_for_ready();
// Erase Flash and UICR
NRF_NVMC->ERASEALL = 1;
wait_for_ready();
// Disable erasing
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
wait_for_ready();
}
void FlashClass::write(uint32_t *address, uint32_t value)
{
// Compare word
if (*address != value) {
// Enable write
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
wait_for_ready();
// Write word
*address = value;
// Disable write
wait_for_ready();
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
wait_for_ready();
}
}
void FlashClass::write_block(uint32_t *dst_address, uint32_t *src_address,
uint16_t word_count)
{
// Enable write
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos;
wait_for_ready();
while (word_count > 0) {
if (*dst_address != *src_address) {
*dst_address = *src_address;
}
word_count--;
dst_address++;
src_address++;
}
// Disable write
wait_for_ready();
NRF_NVMC->CONFIG = NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos;
wait_for_ready();
}
void FlashClass::wait_for_ready()
{
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {
};
}
+88
View File
@@ -0,0 +1,88 @@
#include "drivers/NRF5/Radio.h"
int16_t NRF5_getTxPowerPercent(void)
{
// NRF5_PA_MAX = 100% NRF5_PA_MIN=0%
int16_t dbm = NRF5_getTxPowerLevel();
int16_t dbm_diff = ((int8_t)NRF5_PA_MAX-(int8_t)NRF5_PA_MIN);
int16_t dbm_min = (int8_t)NRF5_PA_MIN;
return ((dbm-dbm_min)*100)/dbm_diff;
}
int16_t NRF5_getTxPowerLevel(void)
{
return (int8_t)NRF_RADIO->TXPOWER;
}
bool NRF5_setTxPowerPercent(const uint8_t powerPercent)
{
/* Current mapping:
* NRF51/NRF52822:
* 0.. 2% -> -40dBm (0%)
* 3..56% -> -16dBm (54%)
* 57..65% -> -12dBm (63%)
* 66..72% -> -8dBm (72%)
* 75..84% -> -4dBm (81%)
* 85..95% -> 0dBm (90%)
* NRF51 96..100%-> 4dBm (100%)
* NRF52 96..99% -> 3dBm (97%)
* NRF52 100% -> 4dBm (100%)
*/
// Calculate dbm level
int16_t dbm_diff = ((int8_t)NRF5_PA_MAX-(int8_t)NRF5_PA_MIN);
int16_t dbm_min = (int8_t)NRF5_PA_MIN;
int8_t dbm = ((dbm_diff * powerPercent)/100)+dbm_min;
if (dbm >= (int8_t)NRF5_PA_MAX) {
NRF_RADIO->TXPOWER = NRF5_PA_MAX;
return true;
}
if (dbm > 1) {
#ifdef RADIO_TXPOWER_TXPOWER_Pos2dBm
// NRF52840
NRF_RADIO->TXPOWER = dbm;
#elif defined(NRF51)
// nRF51x22
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Pos4dBm;
#else
// NRF52822
if (dbm > 3) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Pos4dBm;
} else {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Pos3dBm;
}
#endif
return true;
}
if (dbm > (int8_t)RADIO_TXPOWER_TXPOWER_Neg4dBm) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_0dBm;
return true;
}
if (dbm > (int8_t)RADIO_TXPOWER_TXPOWER_Neg4dBm) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_0dBm;
return true;
}
if (dbm > (int8_t)RADIO_TXPOWER_TXPOWER_Neg8dBm) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg4dBm;
return true;
}
if (dbm > (int8_t)RADIO_TXPOWER_TXPOWER_Neg12dBm) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg8dBm;
return true;
}
if (dbm > (int8_t)RADIO_TXPOWER_TXPOWER_Neg16dBm) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg12dBm;
return true;
}
if (dbm > (int8_t)RADIO_TXPOWER_TXPOWER_Neg20dBm) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg16dBm;
return true;
}
if (dbm > (int8_t)NRF5_PA_MIN) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg16dBm;
return true;
}
NRF_RADIO->TXPOWER = NRF5_PA_MIN;
return true;
}
+82
View File
@@ -0,0 +1,82 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of
* the
* network topology allowing messages to be routed to nodes.
*
* Created by Frank Holtz
* Copyright (C) 2017 Frank Holtz
* Full contributor list:
* https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#ifndef __NRF_RADIO_H__
#define __NRF_RADIO_H__
#if !defined(ARDUINO)
#error "NRF5 Radio is not supported for this platform."
#endif
#if defined(NRF5_SOFTDEVICE)
#warning "NRF5 SoftDevice cannot be used with NRF5 Radio."
#endif
#include <nrf.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
// Timer to use
#define NRF5_RADIO_TIMER NRF_TIMER2
#define NRF5_RADIO_TIMER_IRQ_HANDLER TIMER2_IRQHandler
#define NRF5_RADIO_TIMER_IRQN TIMER2_IRQn
// debug
#if defined(MY_DEBUG_VERBOSE_NRF5_ESB)
#define NRF5_RADIO_DEBUG(x, ...) debug(x, ##__VA_ARGS__)
#else
#define NRF5_RADIO_DEBUG(x, ...)
#endif
// tx power
typedef enum {
#ifdef NRF51
NRF5_PA_MIN = RADIO_TXPOWER_TXPOWER_Neg30dBm, // Deprecated
#else
NRF5_PA_MIN = RADIO_TXPOWER_TXPOWER_Neg40dBm,
#endif
NRF5_PA_LOW = RADIO_TXPOWER_TXPOWER_Neg16dBm,
NRF5_PA_HIGH = RADIO_TXPOWER_TXPOWER_0dBm,
#ifdef RADIO_TXPOWER_TXPOWER_Pos9dBm
// nRF52840
NRF5_PA_MAX = RADIO_TXPOWER_TXPOWER_Pos9dBm,
#else
// nRF51x22/nRF52822
NRF5_PA_MAX = RADIO_TXPOWER_TXPOWER_Pos4dBm,
#endif
} nrf5_txpower_e;
// Radio mode (Data rate)
typedef enum {
NRF5_1MBPS = RADIO_MODE_MODE_Nrf_1Mbit,
NRF5_2MBPS = RADIO_MODE_MODE_Nrf_2Mbit,
NRF5_250KBPS = RADIO_MODE_MODE_Nrf_250Kbit, // Deprecated!!!
NRF5_BLE_1MBPS = RADIO_MODE_MODE_Ble_1Mbit,
} nrf5_mode_e;
int16_t NRF5_getTxPowerPercent(void);
int16_t NRF5_getTxPowerLevel(void);
bool NRF5_setTxPowerPercent(const uint8_t powerPercent);
#endif // __NRF_RADIO_H__
+678
View File
@@ -0,0 +1,678 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of
* the
* network topology allowing messages to be routed to nodes.
*
* Created by Frank Holtz
* Copyright (C) 2017 Frank Holtz
* Full contributor list:
* https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#include "Radio.h"
#include "Radio_ESB.h"
#include "hal/architecture/MyHwNRF5.h"
#include "drivers/CircularBuffer/CircularBuffer.h"
#include <stdio.h>
// internal functions
static uint8_t reverse_byte(uint8_t address);
// RX Buffer
static NRF5_ESB_Packet rx_circular_buffer_buffer[MY_NRF5_ESB_RX_BUFFER_SIZE];
// Poiter to rx circular buffer
static NRF5_ESB_Packet *rx_buffer;
// Circular buffer
static CircularBuffer<NRF5_ESB_Packet>
rx_circular_buffer(rx_circular_buffer_buffer, MY_NRF5_ESB_RX_BUFFER_SIZE);
// Dedect duplicate packages for every pipe available
static volatile uint32_t package_ids[8];
// ACK Buffer
static NRF5_ESB_Packet ack_buffer;
// TX Buffer
static NRF5_ESB_Packet tx_buffer;
// remaining TX retries
static volatile int8_t tx_retries;
// PID number for ACK
static volatile int8_t ack_pid;
// Flag for ack received
static volatile bool ack_received;
// Flag for end TX event
static volatile bool events_end_tx;
// Flag if radio is disabled
static volatile bool radio_disabled;
// Last RSSI sample provided by NRF5_ESB_readMessage
static volatile int16_t rssi_rx;
// Last RSSI sample by last package
static volatile int16_t rssi_tx;
// Buffer node address
static uint8_t node_address = 0;
// TX power level
static int8_t tx_power_level = (MY_NRF5_ESB_PA_LEVEL << RADIO_TXPOWER_TXPOWER_Pos);
// Initialize radio unit
static bool NRF5_ESB_initialize()
{
NRF5_RADIO_DEBUG(PSTR("NRF5:INIT:ESB\n"));
// Power on radio unit
NRF_RADIO->POWER = 1;
// Enable radio interrupt
NVIC_SetPriority(RADIO_IRQn, 1);
NVIC_ClearPendingIRQ(RADIO_IRQn);
NVIC_EnableIRQ(RADIO_IRQn);
// Enable timer interrupt
NVIC_SetPriority(NRF5_RADIO_TIMER_IRQN, 2);
NVIC_ClearPendingIRQ(NRF5_RADIO_TIMER_IRQN);
NVIC_EnableIRQ(NRF5_RADIO_TIMER_IRQN);
// Clear events
NRF_RADIO->EVENTS_END = 0;
NRF_RADIO->EVENTS_READY = 0;
NRF_RADIO->EVENTS_BCMATCH = 0;
// Disable all interrupts
NRF_RADIO->INTENCLR = (uint32_t)~0;
// Select interrupt events
NRF_RADIO->INTENSET = RADIO_INTENSET_END_Msk | RADIO_INTENSET_BCMATCH_Msk;
// Configure radio parameters: tx power
NRF_RADIO->TXPOWER = tx_power_level;
// Configure radio parameters: radio channel
NRF_RADIO->FREQUENCY = MY_NRF5_ESB_CHANNEL;
// Configure radio parameters: data rate
NRF_RADIO->MODE = MY_NRF5_ESB_MODE;
// Configure radio parameters: CRC16
NRF_RADIO->CRCCNF = (RADIO_CRCCNF_LEN_Two << RADIO_CRCCNF_LEN_Pos);
NRF_RADIO->CRCINIT = 0xFFFFUL;
NRF_RADIO->CRCPOLY = 0x11021UL;
// Radio address config
uint8_t address[MY_NRF5_ESB_ADDR_WIDTH] = {MY_NRF5_ESB_BASE_RADIO_ID};
// Configure addresses
NRF_RADIO->PREFIX0 = (NRF5_ESB_NODE_ADDR_MSK | reverse_byte(node_address) <<
(NRF5_ESB_NODE_ADDR << 5));
NRF_RADIO->BASE0 = reverse_byte(address[1]) << 24 |
reverse_byte(address[2]) << 16 |
reverse_byte(address[3]) << 8 | reverse_byte(address[4]);
NRF_RADIO->BASE1 = reverse_byte(address[1]) << 24 |
reverse_byte(address[2]) << 16 |
reverse_byte(address[3]) << 8 | reverse_byte(address[4]);
NRF_RADIO->PREFIX1 = NRF5_ESB_TX_ADDR_MSK; // Broadcast and send address
// Enable listening on Node and BC address
NRF_RADIO->RXADDRESSES = (1 << NRF5_ESB_NODE_ADDR) | (1 << NRF5_ESB_BC_ADDR);
// Packet configuration for NRF24 compatibility
NRF_RADIO->PCNF0 = (6 << RADIO_PCNF0_LFLEN_Pos) | // 6 Bits length field
(0 << RADIO_PCNF0_S0LEN_Pos) | // No S0
#ifdef RADIO_PCNF0_S1INCL_Pos
(1 << RADIO_PCNF0_S1INCL_Pos) | // Force include S1 in RAM
#endif
(3 << RADIO_PCNF0_S1LEN_Pos); // 3 Bits S1 (NOACK and PID)
// Packet configuration
NRF_RADIO->PCNF1 =
(MAX_MESSAGE_LENGTH << RADIO_PCNF1_MAXLEN_Pos) | // maximum length
(0 << RADIO_PCNF1_STATLEN_Pos) | // minimum message length
((MY_NRF5_ESB_ADDR_WIDTH - 1) << RADIO_PCNF1_BALEN_Pos) | // Set base address length
(RADIO_PCNF1_ENDIAN_Big << RADIO_PCNF1_ENDIAN_Pos) | // Big endian
(RADIO_PCNF1_WHITEEN_Disabled << RADIO_PCNF1_WHITEEN_Pos); // Disable whitening
// HINT: Fast ramp up can enabled here. Needs more code on other lines
// Fast ramp up isn't supported by NRF24 and NRF51 series.
// Set bitcounter to trigger interrupt after ACK bit
NRF_RADIO->BCC = NRF5_ESB_BITCOUNTER;
#ifdef NRF51
// Enable timer
NRF5_RADIO_TIMER->POWER = 1;
#endif
// Stop timer, if running
NRF5_RADIO_TIMER->TASKS_STOP = 1;
// Prepare timer running at 1 MHz/1us
NRF5_RADIO_TIMER->PRESCALER = 4;
// Timer mode
NRF5_RADIO_TIMER->MODE = TIMER_MODE_MODE_Timer;
// in 16 Bit mode
NRF5_RADIO_TIMER->BITMODE = TIMER_BITMODE_BITMODE_16Bit << TIMER_BITMODE_BITMODE_Pos;
// Stop timer when CC0 reached
NRF5_RADIO_TIMER->SHORTS =
TIMER_SHORTS_COMPARE1_CLEAR_Msk | TIMER_SHORTS_COMPARE1_STOP_Msk;
// Reset timer
NRF5_RADIO_TIMER->TASKS_CLEAR = 1;
// Reset compare events
NRF5_RADIO_TIMER->EVENTS_COMPARE[0] = 0;
NRF5_RADIO_TIMER->EVENTS_COMPARE[1] = 0;
// Enable interrupt
NRF5_RADIO_TIMER->INTENSET = TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos;
NRF5_RADIO_TIMER->INTENSET = TIMER_INTENSET_COMPARE1_Enabled << TIMER_INTENSET_COMPARE1_Pos;
/** Configure PPI (Programmable peripheral interconnect) */
// Start timer on END event
NRF_PPI->CH[NRF5_ESB_PPI_TIMER_START].EEP = (uint32_t)&NRF_RADIO->EVENTS_END;
NRF_PPI->CH[NRF5_ESB_PPI_TIMER_START].TEP = (uint32_t)&NRF5_RADIO_TIMER->TASKS_START;
// Disable Radio after CC[0]
NRF_PPI->CH[NRF5_ESB_PPI_TIMER_RADIO_DISABLE].EEP = (uint32_t)&NRF5_RADIO_TIMER->EVENTS_COMPARE[0];
NRF_PPI->CH[NRF5_ESB_PPI_TIMER_RADIO_DISABLE].TEP = (uint32_t)&NRF_RADIO->TASKS_DISABLE;
// Set internal variables
radio_disabled = true;
return true;
}
static void NRF5_ESB_powerDown()
{
NRF5_RADIO_DEBUG(PSTR("NRF5:PD\n"));
// Disable inerrupt
NVIC_DisableIRQ(RADIO_IRQn);
NVIC_DisableIRQ(NRF5_RADIO_TIMER_IRQN);
// Clear PPI
NRF_PPI->CHENCLR = NRF5_ESB_PPI_BITS;
// Save power level
tx_power_level = NRF_RADIO->TXPOWER;
// Power off readio unit
NRF_RADIO->POWER = 0;
// Shutdown timer
NRF5_RADIO_TIMER->TASKS_SHUTDOWN = 1;
#ifdef NRF51
// Power off timer
NRF5_RADIO_TIMER->POWER = 0;
#endif
}
static void NRF5_ESB_powerUp()
{
// Not needed
}
static void NRF5_ESB_sleep()
{
NRF5_RADIO_DEBUG(PSTR("NRF5:SLP\n"));
NRF5_ESB_powerDown();
}
static void NRF5_ESB_standBy()
{
NRF5_RADIO_DEBUG(PSTR("NRF5:SBY\n"));
NRF5_ESB_initialize();
}
static bool NRF5_ESB_sanityCheck()
{
// always true
return true;
}
static void NRF5_ESB_setNodeAddress(const uint8_t address)
{
node_address = address;
NRF_RADIO->PREFIX0 = (NRF_RADIO->PREFIX0 & NRF5_ESB_NODE_ADDR_MSK) |
reverse_byte(node_address) << (NRF5_ESB_NODE_ADDR << 5);
}
static uint8_t NRF5_ESB_getNodeID()
{
return reverse_byte((NRF_RADIO->PREFIX0 & NRF5_ESB_NODE_ADDR_MSK) >> (NRF5_ESB_NODE_ADDR << 5));
}
static void NRF5_ESB_startListening()
{
NRF5_RADIO_DEBUG(PSTR("NRF5:STL\n"));
// Check if radio is initialized
if (NRF_RADIO->POWER == 0) {
NRF5_ESB_initialize();
}
// Enable Ready interrupt
NRF_RADIO->INTENSET = RADIO_INTENSET_READY_Msk;
// Enable RX when ready, Enable RX after disabling task
NRF_RADIO->SHORTS = NRF5_ESB_SHORTS_RX;
// Switch to RX
if (NRF_RADIO->STATE == RADIO_STATE_STATE_Disabled) {
NRF_RADIO->TASKS_RXEN = 1;
} else {
NRF_RADIO->TASKS_DISABLE = 1;
}
radio_disabled = false;
}
static bool NRF5_ESB_isDataAvailable()
{
return rx_circular_buffer.available() > 0;
}
static uint8_t NRF5_ESB_readMessage(void *data)
{
// get content from rx buffer
NRF5_ESB_Packet *buffer = rx_circular_buffer.getBack();
// Nothing to read?
if (buffer == NULL) {
return 0;
}
// copy content
memcpy(data, buffer->data, buffer->len);
uint8_t ret = buffer->len;
rssi_rx = 0-buffer->rssi;
// Debug message
#ifdef MY_DEBUG_VERBOSE_NRF5_ESB
NRF5_RADIO_DEBUG(PSTR("NRF5:RX:LEN=%d,NOACK=%d,PID=%d,RSSI=%d,RX=%d\n"),
buffer->len, buffer->noack, buffer->pid, rssi_rx, buffer->rxmatch);
#endif
// release buffer
rx_circular_buffer.popBack();
// Check if radio was disabled by buffer end
if (radio_disabled == true) {
// Wait until disabling radio is finised
while (NRF_RADIO->EVENTS_DISABLED == 0)
;
// Enable radio
NRF5_ESB_startListening();
}
return ret;
}
void NRF5_ESB_endtx();
void NRF5_ESB_starttx()
{
if (tx_retries > 0) {
// Prevent radio to write into TX memory while receiving
if (NRF_RADIO->PACKETPTR != (uint32_t)&tx_buffer) {
// Disable shorts
NRF_RADIO->SHORTS = 0;
// Disable radio
NRF_RADIO->TASKS_DISABLE = 1;
}
// Mark TX as unfinised
events_end_tx = false;
// Configure TX address to address at index NRF5_ESB_TX_ADDR
NRF_RADIO->TXADDRESS = NRF5_ESB_TX_ADDR;
// Enable TX when ready, Enable TX after disabling task
NRF_RADIO->SHORTS = NRF5_ESB_SHORTS_TX;
// reset timer
NRF_RESET_EVENT(NRF5_RADIO_TIMER->EVENTS_COMPARE[0]);
NRF_RESET_EVENT(NRF5_RADIO_TIMER->EVENTS_COMPARE[1]);
NRF5_RADIO_TIMER->TASKS_STOP = 1;
NRF5_RADIO_TIMER->TASKS_CLEAR = 1;
// Set retransmit time
NRF5_RADIO_TIMER->CC[1] = NRF5_ESB_ARD - NRF5_ESB_RAMP_UP_TIME;
// Set radio disable time to ACK_WAIT time
NRF5_RADIO_TIMER->CC[0] = NRF5_ESB_ACK_WAIT;
// Set PPI
NRF_PPI->CHENSET = NRF5_ESB_PPI_BITS;
// Disable Ready interrupt
NRF_RADIO->INTENCLR = RADIO_INTENSET_READY_Msk;
// Set buffer
NRF_RADIO->PACKETPTR = (uint32_t)&tx_buffer;
// Switch to TX
if (NRF_RADIO->STATE == RADIO_STATE_STATE_Disabled) {
NRF_RADIO->TASKS_TXEN = 1;
} else {
NRF_RADIO->TASKS_DISABLE = 1;
}
} else {
// finised TX
NRF5_ESB_endtx();
}
tx_retries--;
}
void NRF5_ESB_endtx()
{
// Clear PPI
NRF_PPI->CHENCLR = NRF5_ESB_PPI_BITS;
// Enable Ready interrupt
NRF_RADIO->INTENSET = RADIO_INTENSET_READY_Msk;
// Stop Timer
NRF5_RADIO_TIMER->TASKS_STOP = 1;
// Shutdown timer
NRF5_RADIO_TIMER->TASKS_SHUTDOWN = 1;
// Mark TX as end
events_end_tx = true;
// Debug output
#ifdef NRF5_ESB_DEBUG_INT_TX_END
NRF5_RADIO_DEBUG(PSTR("NRF5:INT:ENDTX\n"));
#endif
}
static bool NRF5_ESB_sendMessage(uint8_t recipient, const void *buf, uint8_t len, const bool noACK)
{
NRF5_RADIO_DEBUG(PSTR("NRF5:SND:TO=%d,LEN=%d,PID=%d,NOACK=%d\n"), recipient, len, tx_buffer.pid,
tx_buffer.noack); // send message
// Check if radio is initialized
if (NRF_RADIO->POWER == 0) {
NRF5_ESB_initialize();
}
// check length and truncate data
if (len > MAX_MESSAGE_LENGTH) {
len = MAX_MESSAGE_LENGTH;
}
// copy data to tx_buffer
memcpy(&tx_buffer.data[0], buf, len);
// build metadata
tx_buffer.len = len;
#ifndef MY_NRF5_ESB_REVERSE_ACK
tx_buffer.noack = noACK || recipient==BROADCAST_ADDRESS;
#else
// reverse the noack bit
tx_buffer.noack = !(noACK || recipient==BROADCAST_ADDRESS);
#endif
tx_buffer.pid++;
// Calculate number of retries
tx_retries = ((recipient == BROADCAST_ADDRESS)?(NRF5_ESB_BC):(NRF5_ESB_ARC));
int8_t tx_retries_start = tx_retries;
// This line disables sending a broadcast packet multiple times
// ack_received = (recipient == BROADCAST_ADDRESS);
// This line force to send an boradcast packet multiple times like NRF24
ack_received = tx_buffer.noack;
// configure TX address
NRF_RADIO->PREFIX1 = (NRF_RADIO->PREFIX1 & NRF5_ESB_TX_ADDR_MSK) |
(reverse_byte(recipient) << (NRF5_ESB_TX_ADDR - 4));
// Enable listening on Node, BC and TX address
NRF_RADIO->RXADDRESSES = (1 << NRF5_ESB_NODE_ADDR) | (1 << NRF5_ESB_BC_ADDR) |
(1 << NRF5_ESB_TX_ADDR);
// Set RSSI to invalid
rssi_tx = INVALID_RSSI;
NRF5_ESB_starttx();
// Wait for end of transmission
#ifdef MY_DEBUG_VERBOSE_NRF5_ESB
uint32_t wakeups = 0;
#endif
while (events_end_tx == false) {
// Power off CPU until next interrupt
hwSleep();
// hwWaitForInterrupt();
#ifdef MY_DEBUG_VERBOSE_NRF5_ESB
wakeups++;
#endif
}
// Enable listening on Node and BC address
NRF_RADIO->RXADDRESSES = (1 << NRF5_ESB_NODE_ADDR) | (1 << NRF5_ESB_BC_ADDR);
// Calculate RSSI
if (rssi_tx == INVALID_RSSI) {
// calculate pseudo-RSSI based on retransmission counter (ARC)
// min -104dBm at 250kBps
// Arbitrary definition: ARC 0 == -29, ARC 15 = -104
rssi_tx = (-29 - (8 * (tx_retries_start - tx_retries)));
}
#ifdef MY_DEBUG_VERBOSE_NRF5_ESB
NRF5_RADIO_DEBUG(PSTR("NRF5:SND:END=%d,ACK=%d,RTRY=%d,RSSI=%d,WAKE=%d\n"),
events_end_tx, ack_received, tx_retries_start - tx_retries, rssi_tx, wakeups);
#endif
return ack_received;
};
static int16_t NRF5_ESB_getSendingRSSI()
{
return rssi_tx;
}
static int16_t NRF5_ESB_getReceivingRSSI()
{
return rssi_rx;
}
/*
* Internal helper functions
*/
// Reverse a byte for address
static uint8_t reverse_byte(uint8_t address)
{
#if __CORTEX_M >= (0x01U)
return __REV(__RBIT(address));
#else
address = ((address * 0x0802LU & 0x22110LU) | (address * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
#endif
return address;
}
// Calculate time to transmit an byte in us as bit shift -> 2^X
static inline uint8_t NRF5_ESB_byte_time()
{
if ((MY_NRF5_ESB_MODE == NRF5_1MBPS) or
(MY_NRF5_ESB_MODE == NRF5_BLE_1MBPS)) {
return (3);
} else if (MY_NRF5_ESB_MODE == NRF5_2MBPS) {
return (2);
} else if (MY_NRF5_ESB_MODE == NRF5_250KBPS) {
return (5);
}
}
extern "C" {
/** Radio Interrupt handler */
void RADIO_IRQHandler()
{
/** Bitcounter event is used to switch between RX/TX
* In RX mode, when an ACK required packet is received, switch to TX,
* elsewhere start RX again.
* In TX mode switch always to RX.
*/
if (NRF_RADIO->EVENTS_BCMATCH == 1) {
NRF_RESET_EVENT(NRF_RADIO->EVENTS_BCMATCH);
// Disable bitcounter
NRF_RADIO->TASKS_BCSTOP = 1;
// In RX mode -> prepare ACK or RX
if (NRF_RADIO->STATE == RADIO_STATE_STATE_Rx) {
// ACK only for node address and unset noack bit
#ifndef MY_NRF5_ESB_REVERSE_ACK
if ((NRF_RADIO->RXMATCH != NRF5_ESB_NODE_ADDR) || (rx_buffer->noack)) {
#else
if ((NRF_RADIO->RXMATCH != NRF5_ESB_NODE_ADDR) || (!rx_buffer->noack)) {
#endif
// Handle incoming ACK packet
if (NRF_RADIO->RXMATCH == NRF5_ESB_TX_ADDR) {
/** Calculate time to switch radio off
* This is an ACK packet, the radio is disabled by Timer
* event after CC[0], calculate the time switching of the
* radio.
*/
// Read current timer value
NRF5_RADIO_TIMER->TASKS_CAPTURE[0] = 1;
// Set Timer compare register 0 to end of packet (len+CRC)
NRF5_RADIO_TIMER->CC[0] += ((rx_buffer->len + 3) << NRF5_ESB_byte_time());
#if __CORTEX_M == 0x04
// read back to clear cache
(void)NRF5_RADIO_TIMER->CC[0];
#endif
}
// No ACK -> Start RX after END
NRF_RADIO->SHORTS = NRF5_ESB_SHORTS_RX;
} else {
// Send ACK after END, an empty packet is provided in READY event
NRF_RADIO->SHORTS = NRF5_ESB_SHORTS_RX_TX;
// HINT: Fast ramp must be disabled for every device with unknown
// support (payload). Needs more code on other lines
}
} else {
// Current mode is TX:
// After TX the Radio has to be always in RX mode to
// receive ACK or start implicit listen mode after send.
NRF_RADIO->SHORTS = NRF5_ESB_SHORTS_TX_RX;
// HINT: Fast ramp up can enabled here. Needs more code on other lines
}
}
/** Ready event is generated before RX starts
* An free rx buffer is allocated or radio is disabled on failures
*/
if (NRF_RADIO->EVENTS_READY == 1) {
NRF_RESET_EVENT(NRF_RADIO->EVENTS_READY);
// Fetch a new buffer
rx_buffer = rx_circular_buffer.getFront();
// Is buffer valid?
if (rx_buffer == NULL) {
// No buffer -> disable listening
radio_disabled = true;
NRF_RADIO->SHORTS = 0;
NRF_RADIO->EVENTS_DISABLED = 0;
NRF_RADIO->TASKS_DISABLE = 1;
return;
}
// Configure DMA target address
NRF_RADIO->PACKETPTR = (uint32_t)rx_buffer;
/* Don't care about if next packet RX or ACK,
* prepare current rx_buffer to send an ACK */
// Set outgoing address to node address for ACK packages
NRF_RADIO->TXADDRESS = NRF5_ESB_NODE_ADDR;
// HINT: Fast ramp up: Add a fast ramp up signaling payload here. Needs more
// code on other lines
}
/** This event is generated after TX or RX finised
*/
if (NRF_RADIO->EVENTS_END == 1) {
NRF_RESET_EVENT(NRF_RADIO->EVENTS_END);
// Enable ACK bitcounter for next packet
NRF_RADIO->BCC = NRF5_ESB_BITCOUNTER;
// End of RX packet
if ((NRF_RADIO->STATE == RADIO_STATE_STATE_Rx) or
(NRF_RADIO->STATE == RADIO_STATE_STATE_RxIdle) or
(NRF_RADIO->STATE == RADIO_STATE_STATE_RxDisable) or
(NRF_RADIO->STATE == RADIO_STATE_STATE_TxRu)) {
if (NRF_RADIO->CRCSTATUS) {
// Ensure no ACK package is recieved
if (NRF_RADIO->RXMATCH != NRF5_ESB_TX_ADDR) {
// calculate a package id
uint32_t pkgid = rx_buffer->pid << 16 | NRF_RADIO->RXCRC;
if (pkgid != package_ids[NRF_RADIO->RXMATCH]) {
// correct package -> store id to dedect duplicates
package_ids[NRF_RADIO->RXMATCH] = pkgid;
rx_buffer->rssi = ack_buffer.data[0] = NRF_RADIO->RSSISAMPLE;
#ifdef MY_DEBUG_VERBOSE_NRF5_ESB
// Store debug data
rx_buffer->rxmatch = NRF_RADIO->RXMATCH;
#endif
// Push data to buffer
rx_circular_buffer.pushFront(rx_buffer);
// Prepare ACK package
ack_buffer.pid++;
ack_buffer.len=1; // data[0] is set some lines before
#ifndef MY_NRF5_ESB_REVERSE_ACK
ack_buffer.noack = 1;
#else
ack_buffer.noack = 0;
#endif
// Set pointer to ACK packet
NRF_RADIO->PACKETPTR = (uint32_t)&ack_buffer;
}
} else {
// ACK package received, ducplicates are accepted
// rssi value in ACK included?
if (rx_buffer->len == 1) {
rssi_tx = 0-rx_buffer->data[0];
}
// notify TX process
ack_received = true;
// End TX
NRF5_ESB_endtx();
}
} else {
/** Invalid CRC -> Switch back to RX, Stop sending ACK */
// Enable RX when ready, Enable RX after disabling task
NRF_RADIO->SHORTS = NRF5_ESB_SHORTS_RX;
// Start disabling radio -> switch to rx by shorts
NRF_RADIO->TASKS_DISABLE = 1;
}
} else {
// TX end
}
}
}
/** Timer Interrupt Handler
* This timer is used to handle TX retransmit timing
*
*/
void NRF5_RADIO_TIMER_IRQ_HANDLER()
{
if (NRF5_RADIO_TIMER->EVENTS_COMPARE[0] == 1) {
NRF_RESET_EVENT(NRF5_RADIO_TIMER->EVENTS_COMPARE[0]);
}
if (NRF5_RADIO_TIMER->EVENTS_COMPARE[1] == 1) {
NRF5_RADIO_TIMER->TASKS_STOP = 1;
NRF_RESET_EVENT(NRF5_RADIO_TIMER->EVENTS_COMPARE[1]);
if (ack_received == false) {
// missing ACK, start TX again
NRF5_ESB_starttx();
} else {
// finised TX
NRF5_ESB_endtx();
}
}
}
} // extern "C"
+157
View File
@@ -0,0 +1,157 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of
* the
* network topology allowing messages to be routed to nodes.
*
* Created by Frank Holtz
* Copyright (C) 2017 Frank Holtz
* Full contributor list:
* https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#ifndef __NRF5_ESB_H__
#define __NRF5_ESB_H__
#include "Radio.h"
#include <Arduino.h>
// Check maximum messae length
#if MAX_MESSAGE_LENGTH > (32)
#error "Unsupported message length. (MAX_MESSAGE_LENGTH)"
#endif
// check rx buffer size
#if MY_NRF5_ESB_RX_BUFFER_SIZE < (4)
#error "MY_NRF5_ESB_RX_BUFFER_SIZE must be greater than 3."
#endif
/** Wait for start of an ACK packet in us
* Calculation: ramp up time + packet header (57 Bit): round to 9 Byte
* If you don't receive ACK packages, you have to increase this value.
* My measured (Aurduino Uno+NRF24L01P) minimal timeouts:
* 250kbit 411us -> 182 us to ACK start
* 1MBit 205us -> 147 us
* 2MBit 173us -> 143 us
*/
#define NRF5_ESB_ACK_WAIT \
((NRF5_ESB_RAMP_UP_TIME << 1) + (9 << NRF5_ESB_byte_time()))
// auto retry delay in us, don't set this value < 1500us@250kbit
#define NRF5_ESB_ARD (1500)
// auto retry count
#define NRF5_ESB_ARC (15)
// How often broadcast messages are send
#define NRF5_ESB_BC (1)
// Node address index
#define NRF5_ESB_NODE_ADDR (0)
#define NRF5_ESB_NODE_ADDR_MSK (0xffffff00UL)
// TX address index
#define NRF5_ESB_TX_ADDR (4)
#define NRF5_ESB_TX_ADDR_MSK (0xffffff00UL)
// BC address index
#define NRF5_ESB_BC_ADDR (7)
#define NRF5_ESB_BC_ADDR_MSK (0xffffffffUL)
// Shorts for RX mode
#define NRF5_ESB_SHORTS_RX \
(RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_START_Msk | \
RADIO_SHORTS_DISABLED_RXEN_Msk | RADIO_SHORTS_ADDRESS_BCSTART_Msk | \
RADIO_SHORTS_ADDRESS_RSSISTART_Msk | RADIO_SHORTS_DISABLED_RSSISTOP_Msk)
// Shorts for TX mode
#define NRF5_ESB_SHORTS_TX \
(RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_START_Msk | \
RADIO_SHORTS_DISABLED_TXEN_Msk | RADIO_SHORTS_ADDRESS_BCSTART_Msk)
// Shorts to switch from RX to TX
#define NRF5_ESB_SHORTS_RX_TX \
(RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_DISABLED_TXEN_Msk | \
RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_ADDRESS_BCSTART_Msk)
// Shorts to switch from TX to RX
#define NRF5_ESB_SHORTS_TX_RX \
(RADIO_SHORTS_END_DISABLE_Msk | RADIO_SHORTS_DISABLED_RXEN_Msk | \
RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_ADDRESS_BCSTART_Msk | \
RADIO_SHORTS_ADDRESS_RSSISTART_Msk | RADIO_SHORTS_DISABLED_RSSISTOP_Msk)
// PPI Channels
#define NRF5_ESB_PPI_TIMER_START 14
#define NRF5_ESB_PPI_TIMER_RADIO_DISABLE 15
#define NRF5_ESB_PPI_BITS \
((1 << NRF5_ESB_PPI_TIMER_START) | \
(1 << NRF5_ESB_PPI_TIMER_RADIO_DISABLE))
/** Bitcounter for Packet Control Field length
* 6 Bits address length + 3 Bits S1 (NOACK + PID)
*/
#define NRF5_ESB_BITCOUNTER (9)
/** ramp up time
* Time to activate radio TX or RX mode
*/
#define NRF5_ESB_RAMP_UP_TIME (130)
static bool NRF5_ESB_initialize();
static void NRF5_ESB_powerDown();
static void NRF5_ESB_powerUp();
static void NRF5_ESB_sleep();
static void NRF5_ESB_standBy();
static bool NRF5_ESB_sanityCheck();
static void NRF5_ESB_setNodeAddress(const uint8_t address);
static uint8_t NRF5_ESB_getNodeID();
static void NRF5_ESB_startListening();
static bool NRF5_ESB_isDataAvailable();
static uint8_t NRF5_ESB_readMessage(void *data);
static bool NRF5_ESB_sendMessage(uint8_t recipient, const void *buf, uint8_t len, const bool noACK);
static int16_t NRF5_ESB_getSendingRSSI();
static int16_t NRF5_ESB_getReceivingRSSI();
// Calculate time to transmit an byte in us as bit shift -> 2^X
static inline uint8_t NRF5_ESB_byte_time();
/** Structure of radio rackets
*/
typedef struct nrf5_radio_packet_s {
// structure written by radio unit
struct {
uint8_t len;
union {
uint8_t s1;
struct {
uint8_t noack : 1;
uint8_t pid : 2;
};
};
uint8_t data[MAX_MESSAGE_LENGTH];
int8_t rssi;
/** Debug data structure */
#ifdef MY_DEBUG_VERBOSE_NRF5_ESB
uint32_t rxmatch;
#endif
}
#ifndef DOXYGEN
__attribute__((packed));
#endif
} NRF5_ESB_Packet;
#endif // __NRF5_H__
+42
View File
@@ -0,0 +1,42 @@
/*
* nRF5 output modes
*/
// Standard 0, Standard 1
#ifndef OUTPUT_S0S1
#define OUTPUT_S0S1 (0x10)
#endif
// High Drive 0, Standard 1
#ifndef OUTPUT_H0S1
#define OUTPUT_H0S1 (0x11)
#endif
// Standard 0, High Drive 1
#ifndef OUTPUT_S0H1
#define OUTPUT_S0H1 (0x12)
#endif
// High Drive both
#ifndef OUTPUT_H0H1
#define OUTPUT_H0H1 (0x13)
#endif
// Disconnected 0, Standard 1
#ifndef OUTPUT_D0S1
#define OUTPUT_D0S1 (0x14)
#endif
// Disconnected 0, High Drive 1
#ifndef OUTPUT_D0H1
#define OUTPUT_D0H1 (0x15)
#endif
// Standard 0, Disconnected 1
#ifndef OUTPUT_S0D1
#define OUTPUT_S0D1 (0x16)
#endif
// High Drive 0, Disconnected 1
#ifndef OUTPUT_H0D1
#define OUTPUT_H0D1 (0x17)
#endif
+96
View File
@@ -0,0 +1,96 @@
/*
Copyright (c) 2015 Arduino LLC. All right reserved.
Copyright (c) 2016 Sandeep Mistry All right reserved.
Copyright (c) 2017 Frank Holtz All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrf.h"
#include "Arduino.h"
#include "nrf5_wiring_constants.h"
#ifdef __cplusplus
extern "C" {
#endif
void nrf5_pinMode(uint32_t ulPin, uint32_t ulMode)
{
if (ulPin >= PINS_COUNT) {
return;
}
ulPin = g_ADigitalPinMap[ulPin];
// Set pin mode according to chapter '22.6.3 I/O Pin Configuration'
switch (ulMode) {
case INPUT:
// Set pin to input mode
NRF_GPIO->PIN_CNF[ulPin] =
((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) |
((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
((uint32_t)GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) |
((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
break;
case INPUT_PULLUP:
// Set pin to input mode with pull-up resistor enabled
NRF_GPIO->PIN_CNF[ulPin] =
((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) |
((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
((uint32_t)GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos) |
((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
break;
case INPUT_PULLDOWN:
// Set pin to input mode with pull-down resistor enabled
NRF_GPIO->PIN_CNF[ulPin] =
((uint32_t)GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos) |
((uint32_t)GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
((uint32_t)GPIO_PIN_CNF_PULL_Pulldown << GPIO_PIN_CNF_PULL_Pos) |
((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
break;
case OUTPUT:
// Set pin to output mode
NRF_GPIO->PIN_CNF[ulPin] =
((uint32_t)GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos) |
((uint32_t)GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos) |
((uint32_t)GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) |
((uint32_t)GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
break;
default:
// calculate nRF specific output modes
if ((ulMode >= OUTPUT_S0S1) && (ulMode <= OUTPUT_H0D1)) {
// Set pin to given output mode
NRF_GPIO->PIN_CNF[ulPin] =
((uint32_t)GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos) |
((uint32_t)GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos) |
((uint32_t)GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) |
((uint32_t)(ulMode - OUTPUT_S0S1) << GPIO_PIN_CNF_DRIVE_Pos) |
((uint32_t)GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos);
}
break;
}
}
#ifdef __cplusplus
}
#endif
+185
View File
@@ -0,0 +1,185 @@
/* Copyright (c) 2002, 2004 Marek Michalkiewicz
Copyright (c) 2005, 2006, 2007 Eric B. Weddington
Copyright (c) 2016 Frank Holtz
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of the copyright holders nor the names of
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. */
/* $Id$ */
/*
avr/wdt.h - macros for AVR watchdog timer
*/
#ifndef _NRF5_WDT_H_
#define _NRF5_WDT_H_
#include <nrf.h>
#include <stdint.h>
/** \file */
/** \defgroup avr_watchdog <avr/wdt.h>: Watchdog timer handling
\code #include <avr/wdt.h> \endcode
This header file declares the interface to some inline macros
handling the watchdog timer like present in many AVR devices.
In order to prevent the watchdog timer configuration from being
accidentally altered by a crashing application, a special timed
sequence is required in order to change it. The macros within
this header file handle the required sequence automatically
before changing any value. Interrupts will be disabled during
the manipulation.
*/
/**
\ingroup avr_watchdog
Reset the watchdog timer. When the watchdog timer is enabled,
a call to this instruction is required before the timer expires,
otherwise a watchdog-initiated device reset will occur.
*/
#define wdt_reset() NRF_WDT->RR[0] = WDT_RR_RR_Reload
/**
\ingroup avr_watchdog
Enable the watchdog timer, configuring it for expiry after
\c timeout (ms).
The WDT is running in sleep mode.
See also the symbolic constants \c WDTO_15MS et al.
*/
#define wdt_enable(timeout) \
NRF_WDT->CONFIG = NRF_WDT->CONFIG = (WDT_CONFIG_HALT_Pause << WDT_CONFIG_HALT_Pos) | ( WDT_CONFIG_SLEEP_Run << WDT_CONFIG_SLEEP_Pos); \
NRF_WDT->CRV = (32768*timeout)/1000; \
NRF_WDT->RREN |= WDT_RREN_RR0_Msk; \
NRF_WDT->TASKS_START = 1
/**
\ingroup avr_watchdog
Disable the watchdog timer. On nRF5 the timer cannot disabled.
The period is set to 36h of CPU run time. The WDT is stopped in sleep mode.
*/
#define wdt_disable() \
NRF_WDT->CONFIG = NRF_WDT->CONFIG = (WDT_CONFIG_HALT_Pause << WDT_CONFIG_HALT_Pos) | ( WDT_CONFIG_SLEEP_Pause << WDT_CONFIG_SLEEP_Pos); \
NRF_WDT->CRV = 4294967295
/**
\ingroup avr_watchdog
Symbolic constants for the watchdog timeout.
Possible timeout values are: 15 ms, 30 ms, 60 ms, 120 ms, 250 ms,
500 ms, 1 s, 2 s, 4s, 8s. (Not all devices allow 4 s or 8 s.)
Symbolic constants are formed by the prefix
\c WDTO_, followed by the time.
Example that would select a watchdog timer expiry of approximately
500 ms:
\code
wdt_enable(WDTO_500MS);
\endcode
*/
#define WDTO_15MS 15
/** \ingroup avr_watchdog
See \c WDTO_15MS */
#define WDTO_30MS 30
/** \ingroup avr_watchdog
See \c WDTO_15MS */
#define WDTO_60MS 60
/** \ingroup avr_watchdog
See \c WDTO_15MS */
#define WDTO_120MS 120
/** \ingroup avr_watchdog
See \c WDTO_15MS */
#define WDTO_250MS 250
/** \ingroup avr_watchdog
See \c WDTO_15MS */
#define WDTO_500MS 500
/** \ingroup avr_watchdog
See \c WDTO_15MS */
#define WDTO_1S 1000
/** \ingroup avr_watchdog
See \c WDTO_15MS */
#define WDTO_2S 2000
/** \ingroup avr_watchdog
See \c WDTO_15MS
Note: This is only available on the
ATtiny2313,
ATtiny24, ATtiny44, ATtiny84, ATtiny84A,
ATtiny25, ATtiny45, ATtiny85,
ATtiny261, ATtiny461, ATtiny861,
ATmega48, ATmega88, ATmega168,
ATmega48P, ATmega88P, ATmega168P, ATmega328P,
ATmega164P, ATmega324P, ATmega644P, ATmega644,
ATmega640, ATmega1280, ATmega1281, ATmega2560, ATmega2561,
ATmega8HVA, ATmega16HVA, ATmega32HVB,
ATmega406, ATmega1284P,
AT90PWM1, AT90PWM2, AT90PWM2B, AT90PWM3, AT90PWM3B, AT90PWM216, AT90PWM316,
AT90PWM81, AT90PWM161,
AT90USB82, AT90USB162,
AT90USB646, AT90USB647, AT90USB1286, AT90USB1287,
ATtiny48, ATtiny88,
nRF51822, nRF52832
*/
#define WDTO_4S 4000
/** \ingroup avr_watchdog
See \c WDTO_15MS
Note: This is only available on the
ATtiny2313,
ATtiny24, ATtiny44, ATtiny84, ATtiny84A,
ATtiny25, ATtiny45, ATtiny85,
ATtiny261, ATtiny461, ATtiny861,
ATmega48, ATmega48A, ATmega48PA, ATmega88, ATmega168,
ATmega48P, ATmega88P, ATmega168P, ATmega328P,
ATmega164P, ATmega324P, ATmega644P, ATmega644,
ATmega640, ATmega1280, ATmega1281, ATmega2560, ATmega2561,
ATmega8HVA, ATmega16HVA, ATmega32HVB,
ATmega406, ATmega1284P,
ATmega2564RFR2, ATmega256RFR2, ATmega1284RFR2, ATmega128RFR2, ATmega644RFR2, ATmega64RFR2
AT90PWM1, AT90PWM2, AT90PWM2B, AT90PWM3, AT90PWM3B, AT90PWM216, AT90PWM316,
AT90PWM81, AT90PWM161,
AT90USB82, AT90USB162,
AT90USB646, AT90USB647, AT90USB1286, AT90USB1287,
ATtiny48, ATtiny88,
ATxmega16a4u, ATxmega32a4u,
ATxmega16c4, ATxmega32c4,
ATxmega128c3, ATxmega192c3, ATxmega256c3,
nRF51822, nRF52832
*/
#define WDTO_8S 8000
#endif /* _NRF5_WDT_H_ */