Hi,
I have combined a couple of examples in order to achieve the following on a custom nRF52810 board:
- Count the number of pulses on a pin (using TIMER1)
- Read the counter at regular intervals (using TIMER2) and start advertising the counter value on BLE
- Stop the advertising again after a period of time
My issue is, that when I start the advertising, the application crashes:
[00:00:14.590,881] <inf> nrfx_sample: Starting advertisement
Advertising value: 20
ASSERTION FAIL [err == 0] @ WEST_TOPDIR/zephyr/subsys/bluetooth/host/hci_core.c:306
k_sem_take failed with err -11
[00:00:14.591,278] <err> os: r0/a1: 0x00000003 r1/a2: 0x00000000 r2/a3: 0x20000750
[00:00:14.591,278] <err> os: r3/a4: 0x00000005 r12/ip: 0x00000000 r14/lr: 0x00002fb1
[00:00:14.591,278] <err> os: xpsr: 0x4100001a
[00:00:14.591,278] <err> os: Faulting instruction address (r15/pc): 0x00002fbc
[00:00:14.591,308] <err> os: >>> ZEPHYR FATAL ERROR 3: Kernel oops on CPU 0
[00:00:14.591,308] <err> os: Fault during interrupt handling
[00:00:14.591,308] <err> os: Current thread: 0x200009a8 (unknown)
[00:00:14.901,306] <err> fatal_error: Resetting system
*** Booting Zephyr OS build v2.7.0-ncs1 ***
[00:00:00.001,190] <inf> bt_hci_core: HW Platform: Nordic Semiconductor (0x0002)
[00:00:00.001,190] <inf> bt_hci_core: HW Variant: nRF52x (0x0002)
[00:00:00.001,190] <inf> bt_hci_core: Firmware: Standard Bluetooth controller (0x00) Version 2.7 Build 0
[00:00:00.001,922] <inf> bt_hci_core: Identity: E3:60:81:6B:B3:57 (random)
[00:00:00.001,922] <inf> bt_hci_core: HCI: version 5.3 (0x0c) revision 0x0000, manufacturer 0x05f1
[00:00:00.001,953] <inf> bt_hci_core: LMP: version 5.3 (0x0c) subver 0xffff
Bluetooth initialized
[00:00:00.001,983] <inf> nrfx_sample: Example start
From the content of hci_core.c it looks like a command timeout, but I assume the cause must be a timer/interrupt conflict of some sort.
This is code I use:
#include <zephyr/types.h>
#include <stddef.h>
#include <sys/printk.h>
#include <sys/util.h>
#include <sys/__assert.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <AnalogIn.h>
#include <device.h>
#include <devicetree.h>
#include <drivers/gpio.h>
#include <nrfx_gpiote.h>
#include <helpers/nrfx_gppi.h>
#include <nrfx_ppi.h>
#include <nrfx_timer.h>
#include <logging/log.h>
#define COUNT_READ_INTERVAL 5000 // ms
#define ADV_TIME_MS 1000
#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
/* size of stack area used by each thread */
#define STACKSIZE 1024
/* scheduling priority used by each thread */
#define PRIORITY 7
/* The devicetree node identifier for the "led0" alias. */
#define LED0_NODE DT_ALIAS(led0)
#define LED0 DT_GPIO_LABEL(LED0_NODE, gpios)
#define PIN_LED0 DT_GPIO_PIN(LED0_NODE, gpios)
#define FLAGS_LED0 DT_GPIO_FLAGS(LED0_NODE, gpios)
#define INPUT_PIN DT_GPIO_PIN(DT_ALIAS(sw0), gpios)
LOG_MODULE_REGISTER(nrfx_sample, LOG_LEVEL_INF);
static uint8_t mfg_data[] = {0xab, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static const struct bt_data ad[] = {
BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data, 12),
};
const struct device *led0;
uint16_t pulses = 0;
bool isAdvertising = false;
volatile int battery = 0;
// But only use LSB six bytes
#define MAX_DEVICE_ID 0xfFfFfFfFfFfF
// FUTURE use a 48-bit bit field
uint64_t myID()
{
/*
* NRF_FICR->DEVICEADDR[] is array of 32-bit words.
* NRF_FICR->DEVICEADDR yields type (unit32_t*)
* Cast: (uint64_t*) NRF_FICR->DEVICEADDR yields type (unit64_t*)
* Dereferencing: *(uint64_t*) NRF_FICR->DEVICEADDR yields type uint64_t
*
* Nordic doc asserts upper two bytes read all ones.
*/
uint64_t result = *((uint64_t *)NRF_FICR->DEVICEADDR);
// Mask off upper bytes, to match over-the-air length of 6 bytes.
result = result & MAX_DEVICE_ID;
// assert(result <= MAX_DEVICE_ID);
return result;
}
static const nrfx_timer_t m_timer_count = NRFX_TIMER_INSTANCE(1);
static const nrfx_timer_t m_timer_read = NRFX_TIMER_INSTANCE(2);
nrf_ppi_channel_t ppi_channel_1, ppi_channel_2;
void check_error(nrfx_err_t err)
{
if (err != NRFX_SUCCESS)
LOG_ERR("Error %08x occoured\n", err);
}
void ble_init()
{
int err;
err = bt_enable(NULL);
if (err)
{
printk("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
// Set device ID
mfg_data[7] = (myID() & 0x0000000000ff);
mfg_data[6] = (myID() & 0x00000000ff00) >> 8;
mfg_data[5] = (myID() & 0x000000ff0000) >> 16;
mfg_data[4] = (myID() & 0x0000ff000000) >> 24;
mfg_data[3] = (myID() & 0x00ff00000000) >> 32;
mfg_data[2] = (myID() & 0xff0000000000) >> 40;
}
void ble_start_advertising(uint16_t pulses, uint16_t battery)
{
int err;
mfg_data[8] = ((pulses & 0xff00) >> 8);
mfg_data[9] = ((pulses & 0x00ff));
mfg_data[10] = ((battery & 0xff00) >> 8);
mfg_data[11] = ((battery & 0x00ff));
printk("Advertising value: %d\n", pulses);
pulses = 0;
// Use this to include BLE device name in advertising:
// err = bt_le_adv_start(BT_LE_ADV_NCONN, ad, ARRAY_SIZE(ad),
// sd, ARRAY_SIZE(sd));
err = bt_le_adv_start(BT_LE_ADV_NCONN, ad, ARRAY_SIZE(ad),
NULL, 0);
if (err)
{
printk("Advertising failed to start (err %d)\n", err);
return;
}
}
void ble_stop_advertising()
{
int err;
// Stop advertising
err = bt_le_adv_stop();
if (err)
{
printk("Advertising failed to stop (err %d)\n", err);
return;
}
else
{
printk("Advertising stopped\n");
}
}
void in_pin_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
return;
}
static void timer_handler_count(nrf_timer_event_t event_type, void *p_context)
{
return;
}
void timer_handler_read(nrf_timer_event_t event_type, void *p_context)
{
if (isAdvertising)
{
LOG_INF("Stopping advertisement");
// Stop advertising and set timer to the read-interval
isAdvertising = false;
ble_stop_advertising();
uint32_t ticks = nrfx_timer_ms_to_ticks(&m_timer_read, COUNT_READ_INTERVAL);
nrfx_timer_extended_compare(&m_timer_read,
NRF_TIMER_CC_CHANNEL0,
ticks,
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
true);
nrfx_timer_enable(&m_timer_read);
}
else
{
// Read the counter and check if there is new data to be advertised
uint32_t count = nrfx_timer_capture_get(&m_timer_count, NRF_TIMER_CC_CHANNEL0);
LOG_INF("Counter shows: %d \n", count);
if (count > 0)
{
uint32_t ticks = nrfx_timer_ms_to_ticks(&m_timer_read, ADV_TIME_MS);
nrfx_timer_extended_compare(&m_timer_read,
NRF_TIMER_CC_CHANNEL0,
ticks,
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
true);
LOG_INF("Starting advertisement");
ble_start_advertising(count, 100); // TO DO: Include battery data
isAdvertising = true;
nrfx_timer_enable(&m_timer_read);
}
else
{
LOG_INF("Nothing to advertise");
}
}
}
/**
* @brief Function for configuring: PIN_IN pin for input sensing
*/
static void gpiote_init(void)
{
nrfx_err_t err_code;
IRQ_DIRECT_CONNECT(GPIOTE_IRQn, 1, nrfx_gpiote_irq_handler, 0);
err_code = nrfx_gpiote_init(0);
check_error(err_code);
nrfx_gpiote_in_config_t in_config = NRFX_GPIOTE_CONFIG_IN_SENSE_LOTOHI(false);
in_config.pull = NRF_GPIO_PIN_PULLDOWN;
err_code = nrfx_gpiote_in_init(INPUT_PIN, &in_config, in_pin_handler);
check_error(err_code);
nrfx_gpiote_in_event_enable(INPUT_PIN, false);
}
void timer_init()
{
nrfx_err_t err_code;
// Configure TIMER1 for counting of low to high events on GPIO
//IRQ_DIRECT_CONNECT(TIMER1_IRQn, 1, nrfx_timer_1_irq_handler, 0);
nrfx_timer_config_t timer_cfg = NRFX_TIMER_DEFAULT_CONFIG;
timer_cfg.bit_width = NRF_TIMER_BIT_WIDTH_32;
timer_cfg.mode = NRF_TIMER_MODE_LOW_POWER_COUNTER;
err_code = nrfx_timer_init(&m_timer_count, &timer_cfg, timer_handler_count);
check_error(err_code);
// Configure TIMER2 for reading the counter timer at a given interval COUNT_READ_INTERVAL
IRQ_DIRECT_CONNECT(TIMER2_IRQn, 1, nrfx_timer_2_irq_handler, 0);
timer_cfg.mode = NRF_TIMER_MODE_TIMER;
err_code = nrfx_timer_init(&m_timer_read, &timer_cfg, timer_handler_read);
check_error(err_code);
uint32_t ticks = nrfx_timer_ms_to_ticks(&m_timer_read, COUNT_READ_INTERVAL);
nrfx_timer_extended_compare(&m_timer_read,
NRF_TIMER_CC_CHANNEL0,
ticks,
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
true);
nrfx_timer_enable(&m_timer_read);
}
void ppi_init()
{
nrfx_err_t err_code;
err_code = nrfx_ppi_channel_alloc(&ppi_channel_1);
check_error(err_code);
err_code = nrfx_ppi_channel_alloc(&ppi_channel_2);
check_error(err_code);
uint32_t gpiote_evt_addr_1 = nrfx_gpiote_in_event_addr_get(INPUT_PIN);
uint32_t timer_count_count_task_addr = nrfx_timer_task_address_get(&m_timer_count, NRF_TIMER_TASK_COUNT);
uint32_t timer_count_capture_task_addr = nrfx_timer_task_address_get(&m_timer_count, NRF_TIMER_TASK_CAPTURE0);
uint32_t timer_count_clear_task_addr = nrfx_timer_task_address_get(&m_timer_count, NRF_TIMER_TASK_CLEAR);
uint32_t timer_read_compare_event_addr = nrfx_timer_event_address_get(&m_timer_read, NRF_TIMER_EVENT_COMPARE0);
err_code = nrfx_ppi_channel_assign(ppi_channel_1, gpiote_evt_addr_1, timer_count_count_task_addr); // Trigger timer count task when GPIOTE pin go from low to high.
check_error(err_code);
err_code = nrfx_ppi_channel_assign(ppi_channel_2, timer_read_compare_event_addr, timer_count_capture_task_addr); // Capture counter timer using PPI
check_error(err_code);
err_code = nrfx_ppi_channel_fork_assign(ppi_channel_2, timer_count_clear_task_addr); // Clear counter timer using PPI
check_error(err_code);
err_code = nrfx_ppi_channel_enable(ppi_channel_1);
check_error(err_code);
err_code = nrfx_ppi_channel_enable(ppi_channel_2);
check_error(err_code);
}
void main(void)
{
gpiote_init();
timer_init();
ppi_init();
ble_init();
LOG_INF("Example start\r\n");
// nrf_gpio_cfg_output(OUTPUT_PIN);
// nrf_gpio_pin_clear(OUTPUT_PIN);
}
I hope you can give me a hint on how to make it work.
Thanks,
Erik