Hi,
I'm trying to write a small sample code that performs BLE advertising without using SoftDevice but instead using the raw radio registers.
I've looked at the solar_sensor_beacon sample on the Nordic GitHub repo and my code looks very similar.
Basically I have one app_timer that runs in repeated mode and once per second it should send a fixed test advertisement packet on channels 37, 38, 39.
I attach the code below as it is very short.
The behaviour that I'm facing is that when the device tries to send the advertisemtent the code stops working, the timer never fires up again and no radio event is triggered.
I cannot understand which could be the cause, the lfclk is started at the beginning and the hfclk is started before configuring the RADIO registers... but no way...
#include <stdbool.h>
#include <stdint.h>
#include "nordic_common.h"
#include "ble.h"
#include "nrf_delay.h"
#include "nrf_drv_clock.h"
#include "app_timer.h"
#include "nrf_pwr_mgmt.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#define DEAD_BEEF 0xDEADBEEF
APP_TIMER_DEF(m_timer);
typedef enum
{
CHANNEL_37 = 0,
CHANNEL_38 = 1,
CHANNEL_39 = 2,
} channel_t;
typedef struct
{
uint8_t channel;
uint8_t frequency;
} channel_frequency_pair_t;
static const channel_frequency_pair_t m_channel_frequency_map[] =
{
{ .channel = 37, .frequency = 2 }, // channel 37, frequency 2402
{ .channel = 38, .frequency = 26 }, // channel 38, frequency 2426
{ .channel = 39, .frequency = 80 } // channel 39, frequency 2480
};
static uint8_t m_adv_pdu[40];
static uint8_t m_adv_ch = CHANNEL_37;
static bool m_send_adv = false;
static bool m_adv_sent = false;
void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
{
app_error_handler(DEAD_BEEF, line_num, p_file_name);
}
void nrf_radio_advertise(void)
{
m_adv_pdu[0] = 0x40 | 0x02; // First header byte: TxAdd|RxAdd (0x40) + PDU Type
m_adv_pdu[1] = 0; // Second header byte: LENGTH (will be updated at the end)
m_adv_pdu[2] = 0;
m_adv_pdu[3] = (NRF_FICR->DEVICEADDR[0] ) & 0xFF;
m_adv_pdu[4] = (NRF_FICR->DEVICEADDR[0] >> 8) & 0xFF;
m_adv_pdu[5] = (NRF_FICR->DEVICEADDR[0] >> 16) & 0xFF;
m_adv_pdu[6] = (NRF_FICR->DEVICEADDR[0] >> 24) ;
m_adv_pdu[7] = (NRF_FICR->DEVICEADDR[1] ) & 0xFF;
m_adv_pdu[8] = (NRF_FICR->DEVICEADDR[1] >> 8) & 0xFF;
m_adv_pdu[9] = 0x02;
m_adv_pdu[10] = 0x01;
m_adv_pdu[11] = 0x06;
m_adv_pdu[12] = 0x05;
m_adv_pdu[13] = 0x09;
m_adv_pdu[14] = 'T';
m_adv_pdu[15] = 'E';
m_adv_pdu[16] = 'S';
m_adv_pdu[17] = 'T';
// update data length
m_adv_pdu[1] = BLE_GAP_ADDR_LEN + 9;
// init first advertising channel
m_adv_ch = CHANNEL_37;
/* Reset all states in the radio peripheral */
NVIC_ClearPendingIRQ(RADIO_IRQn);
NVIC_DisableIRQ(RADIO_IRQn);
NRF_RADIO->POWER = ((RADIO_POWER_POWER_Disabled << RADIO_POWER_POWER_Pos) & RADIO_POWER_POWER_Msk);
NRF_RADIO->POWER = ((RADIO_POWER_POWER_Enabled << RADIO_POWER_POWER_Pos) & RADIO_POWER_POWER_Msk);
/* Do BLE specific radio setup */
NRF_RADIO->EVENTS_DISABLED = 0;
/* Set radio configuration parameters */
NRF_RADIO->TXPOWER = ((RADIO_TXPOWER_TXPOWER_Pos4dBm << RADIO_TXPOWER_TXPOWER_Pos) & RADIO_TXPOWER_TXPOWER_Msk);
NRF_RADIO->MODE = ((RADIO_MODE_MODE_Ble_1Mbit << RADIO_MODE_MODE_Pos) & RADIO_MODE_MODE_Msk);
NRF_RADIO->FREQUENCY = m_channel_frequency_map[m_adv_ch].frequency;
NRF_RADIO->DATAWHITEIV = m_channel_frequency_map[m_adv_ch].channel;
/* Configure Access Address to be the BLE standard */
NRF_RADIO->PREFIX0 = 0x8e;
NRF_RADIO->BASE0 = 0x89bed600;
NRF_RADIO->TXADDRESS = 0x00; // Use logical address 0 (prefix0 + base0) = 0x8E89BED6 when transmitting
NRF_RADIO->RXADDRESSES = 0x01; // Enable reception on logical address 0 (PREFIX0 + BASE0)
/* PCNF-> Packet Configuration. Now we need to configure the sizes S0, S1 and length field to match the datapacket format of the advertisement packets. */
NRF_RADIO->PCNF0 = (
(((1UL) << RADIO_PCNF0_S0LEN_Pos) & RADIO_PCNF0_S0LEN_Msk) // length of S0 field in bytes 0-1.
| (((2UL) << RADIO_PCNF0_S1LEN_Pos) & RADIO_PCNF0_S1LEN_Msk) // length of S1 field in bits 0-8.
| (((6UL) << RADIO_PCNF0_LFLEN_Pos) & RADIO_PCNF0_LFLEN_Msk) // length of length field in bits 0-8.
);
/* Packet configuration */
NRF_RADIO->PCNF1 = (
(((37UL) << RADIO_PCNF1_MAXLEN_Pos) & RADIO_PCNF1_MAXLEN_Msk) // maximum length of payload in bytes [0-255]
| (((0UL) << RADIO_PCNF1_STATLEN_Pos) & RADIO_PCNF1_STATLEN_Msk) // expand the payload with N bytes in addition to LENGTH [0-255]
| (((3UL) << RADIO_PCNF1_BALEN_Pos) & RADIO_PCNF1_BALEN_Msk) // base address length in number of bytes.
| (((RADIO_PCNF1_ENDIAN_Little) << RADIO_PCNF1_ENDIAN_Pos) & RADIO_PCNF1_ENDIAN_Msk) // endianess of the S0, LENGTH, S1 and PAYLOAD fields.
| (((1UL) << RADIO_PCNF1_WHITEEN_Pos) & RADIO_PCNF1_WHITEEN_Msk) // enable packet whitening
);
/* CRC config */
NRF_RADIO->CRCCNF = (RADIO_CRCCNF_LEN_Three << RADIO_CRCCNF_LEN_Pos) |
(RADIO_CRCCNF_SKIPADDR_Skip << RADIO_CRCCNF_SKIPADDR_Pos); // Skip Address when computing crc
NRF_RADIO->CRCINIT = 0x555555; // Initial value of CRC
NRF_RADIO->CRCPOLY = 0x00065B; // CRC polynomial function
/* Lock interframe spacing, so that the radio won't send too soon / start RX too early */
NRF_RADIO->TIFS = 145;
/* Enable radio interrupt propagation */
NVIC_EnableIRQ(RADIO_IRQn);
/* trigger task early, the rest of the setup can be done in RXRU */
NRF_RADIO->TASKS_TXEN = 1;
NRF_RADIO->SHORTS = (
((RADIO_SHORTS_READY_START_Enabled << RADIO_SHORTS_READY_START_Pos) & RADIO_SHORTS_READY_START_Msk)
| ((RADIO_SHORTS_END_DISABLE_Enabled << RADIO_SHORTS_END_DISABLE_Pos) & RADIO_SHORTS_END_DISABLE_Msk)
);
NRF_RADIO->INTENSET = ((RADIO_INTENSET_DISABLED_Set << RADIO_INTENSET_DISABLED_Pos) & RADIO_INTENSET_DISABLED_Msk);
/* Recover packet pointer */
NRF_RADIO->PACKETPTR = (uint32_t) &m_adv_pdu[0];
NRF_LOG_INFO("TXEN");
}
void nrf_radio_event(void)
{
NRF_LOG_INFO("nrf_radio_event");
if (NRF_RADIO->EVENTS_DISABLED)
{
m_adv_ch++;
if (m_adv_ch <= CHANNEL_39)
{
// send adv on next channel
NRF_RADIO->SHORTS = 0;
NRF_RADIO->FREQUENCY = m_channel_frequency_map[m_adv_ch].frequency;
NRF_RADIO->DATAWHITEIV = m_channel_frequency_map[m_adv_ch].channel;
NRF_RADIO->SHORTS = (
((1 << RADIO_SHORTS_READY_START_Pos) & RADIO_SHORTS_READY_START_Msk)
| ((1 << RADIO_SHORTS_END_DISABLE_Pos) & RADIO_SHORTS_END_DISABLE_Msk)
);
NRF_RADIO->INTENSET = ((RADIO_INTENSET_DISABLED_Set << RADIO_INTENSET_DISABLED_Pos) & RADIO_INTENSET_DISABLED_Msk);
NRF_RADIO->TASKS_TXEN = 1;
}
else
{
// turn off radio
NVIC_DisableIRQ(RADIO_IRQn);
NRF_RADIO->TASKS_DISABLE = 1;
NRF_RADIO->POWER = ((RADIO_POWER_POWER_Disabled << RADIO_POWER_POWER_Pos) & RADIO_POWER_POWER_Msk);
NRF_RADIO->SHORTS = 0;
NRF_RADIO->INTENCLR = ((RADIO_INTENCLR_DISABLED_Clear << RADIO_INTENCLR_DISABLED_Pos) & RADIO_INTENCLR_DISABLED_Msk);
NRF_RADIO->EVENTS_DISABLED = 0;
m_adv_sent = 1;
}
}
}
static void timer_handler(void * p_context)
{
NRF_LOG_INFO("TIMER");
m_send_adv = true;
}
static void log_init(void)
{
ret_code_t err_code = NRF_LOG_INIT(NULL);
APP_ERROR_CHECK(err_code);
NRF_LOG_DEFAULT_BACKENDS_INIT();
}
static void timers_init(void)
{
ret_code_t err_code = app_timer_init();
APP_ERROR_CHECK(err_code);
err_code = app_timer_create(&m_timer, APP_TIMER_MODE_REPEATED, timer_handler);
APP_ERROR_CHECK(err_code);
}
static void power_management_init(void)
{
ret_code_t err_code;
err_code = nrf_pwr_mgmt_init();
APP_ERROR_CHECK(err_code);
}
static void lfclk_config(void)
{
uint32_t err_code;
err_code = nrf_drv_clock_init();
APP_ERROR_CHECK(err_code);
nrf_drv_clock_lfclk_request(NULL);
}
static void idle_state_handle(void)
{
if (NRF_LOG_PROCESS() == false)
{
nrf_pwr_mgmt_run();
}
}
int main(void)
{
log_init();
lfclk_config();
timers_init();
power_management_init();
// start app_timer (1 sec.)
APP_ERROR_CHECK(app_timer_start(m_timer, APP_TIMER_TICKS(1000), NULL));
NRF_LOG_INFO("APP START");
for (;;)
{
// this is triggered from app_timer
if (m_send_adv)
{
m_send_adv = false;
// enable hfclk
NRF_LOG_INFO("ENABLING HFCLK");
nrf_drv_clock_hfclk_request(NULL);
// wait for hfclk to be up
while (!nrf_drv_clock_hfclk_is_running())
{
nrf_delay_ms(1);
}
NRF_LOG_INFO("HFCLK ENABLED");
// send advertisements
nrf_radio_advertise();
}
// this is triggered after advertisement on all 3-channels have been done
if (m_adv_sent)
{
m_adv_sent = false;
// turn off hfclk
NRF_LOG_INFO("DISABLING HFCLK");
nrf_drv_clock_hfclk_release();
}
idle_state_handle();
}
}
Thanks for any help / suggestions!