This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Timing+counting while using BLE

Hi,

I have combined a couple of examples in order to achieve the following on a custom nRF52810 board:

  1. Count the number of pulses on a pin (using TIMER1)
  2. Read the counter at regular intervals (using TIMER2) and start advertising the counter value on BLE
  3. 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

Related