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

Parents
  • Hello!

    Looking through your code while looking at your debug prints, it seems like your code crashes when it calls bt_le_adv_start(BT_LE_ADV_NCONN, ad, ARRAY_SIZE(ad), NULL, 0);

    However I can not seem to find "BT_LE_ADV_NCONN" or "ad" defined in your code.

    Am I mistaken or have you forgotten to define them?

    Do you get warnings about this when compiling?

    Best regards,

    Einar

  • Hi Einar,

    I don't get any errors or warnings during compile. "ad" is defined in line 45 of the sourcecode, and BT_LE_ADV_NCONN is defined in bluetooth.h

    Best regards,

    Erik

  • Ah I see.

    With this type of crash I would then suspect that you either haven't inited ble properly, maybe an interrupt/callback isn't set up, or possibly something is missing in your prj.conf file.

    -Einar

  • Hi Einar,

    I can succesfully start and stop advertising from main(), even with timers and PPI initialized. But as soon as I try starting it from inside the event-handler of the timer, it crashes. 

    So my thought is that the BLE system is properly initialized and that prj.conf is OK, but that I'm doing something else wrong. This is my proj.conf for reference:

    CONFIG_ADC=y
    CONFIG_ADC_ASYNC=y
    
    CONFIG_NRFX_GPIOTE=y
    
    CONFIG_SENSOR=y
    
    CONFIG_NRFX_TIMER=y
    CONFIG_NRFX_TIMER0=y
    CONFIG_NRFX_TIMER1=y
    CONFIG_NRFX_TIMER2=y
    CONFIG_NRFX_PPI=y
    
    CONFIG_BT=y
    CONFIG_BT_BROADCASTER=y
    CONFIG_BT_OBSERVER=y
    CONFIG_BT_DEBUG_LOG=y
    CONFIG_BT_LL_SW_SPLIT=y
    
    CONFIG_HEAP_MEM_POOL_SIZE=256
    CONFIG_ASSERT=n
    
    # Clock Configs
    CONFIG_CLOCK_CONTROL=y
    CONFIG_CLOCK_CONTROL_NRF=y
    CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y
    CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION=n
    CONFIG_NRF_RTC_TIMER=y
    
    
    ##### PRINTING | LOGGING #####
    # Print configs
    
    CONFIG_LOG=y
    CONFIG_CONSOLE=y
    CONFIG_SERIAL=n
    CONFIG_LOG_MODE_DEFERRED=y
    CONFIG_PRINTK=y
    CONFIG_USE_SEGGER_RTT=y
    CONFIG_RTT_CONSOLE=y
    CONFIG_UART_CONSOLE=n
    
    # Power
    CONFIG_SYS_POWER_MANAGEMENT=y
    CONFIG_DEVICE_POWER_MANAGEMENT=y
    CONFIG_BOARD_ENABLE_DCDC=y
    
    
    

    Erik

Reply
  • Hi Einar,

    I can succesfully start and stop advertising from main(), even with timers and PPI initialized. But as soon as I try starting it from inside the event-handler of the timer, it crashes. 

    So my thought is that the BLE system is properly initialized and that prj.conf is OK, but that I'm doing something else wrong. This is my proj.conf for reference:

    CONFIG_ADC=y
    CONFIG_ADC_ASYNC=y
    
    CONFIG_NRFX_GPIOTE=y
    
    CONFIG_SENSOR=y
    
    CONFIG_NRFX_TIMER=y
    CONFIG_NRFX_TIMER0=y
    CONFIG_NRFX_TIMER1=y
    CONFIG_NRFX_TIMER2=y
    CONFIG_NRFX_PPI=y
    
    CONFIG_BT=y
    CONFIG_BT_BROADCASTER=y
    CONFIG_BT_OBSERVER=y
    CONFIG_BT_DEBUG_LOG=y
    CONFIG_BT_LL_SW_SPLIT=y
    
    CONFIG_HEAP_MEM_POOL_SIZE=256
    CONFIG_ASSERT=n
    
    # Clock Configs
    CONFIG_CLOCK_CONTROL=y
    CONFIG_CLOCK_CONTROL_NRF=y
    CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y
    CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION=n
    CONFIG_NRF_RTC_TIMER=y
    
    
    ##### PRINTING | LOGGING #####
    # Print configs
    
    CONFIG_LOG=y
    CONFIG_CONSOLE=y
    CONFIG_SERIAL=n
    CONFIG_LOG_MODE_DEFERRED=y
    CONFIG_PRINTK=y
    CONFIG_USE_SEGGER_RTT=y
    CONFIG_RTT_CONSOLE=y
    CONFIG_UART_CONSOLE=n
    
    # Power
    CONFIG_SYS_POWER_MANAGEMENT=y
    CONFIG_DEVICE_POWER_MANAGEMENT=y
    CONFIG_BOARD_ENABLE_DCDC=y
    
    
    

    Erik

Children
  • Hi Erik,

    If you're able to do advertising in main but not in your interrupt, then I can see a couple possible causes.

    First I would check if the variables you are putting into bt_le_adv_start are defined in your event-handler. A crash like this can happen if your program is trying to access a pointer that is defined with a scope that does not include the function it is called from.

    I would also consider whether it is actually a good idea to call bt_le_adv_start from an interrupt. If this function is waiting for the BLE device to become available then calling it from an interrupt wouldn't be optimal. If this is the case I would consider making a BLE controller thread that controls the advertising, and use a semaphore to signal to it from your event-handler.

    -Einar

  • Hi Einar,

    Thanks for the advice on best practise. I have implemented a separate thread like you suggested, and that has solved the issue - as well as the potential issue of the ISR waiting for the BLE device.

    Best regards,

    Erik

Related