Custom BLE application won't function unless debugger is attached, or immediately following device flashing. Power reset leaves device in non-functional state.

Hello,

I've developed a custom PCB which uses an nRF52810 SOC. I've been developing the software for the device using NRF Connect SDK 2.6.1 in Visual Studio Code, and I flash the device using a J-Link debug probe via SWD. 

The software involves BLE (utilizing the Nordic UART Service NUS), some simple kernel-based timers, ADC readings, I2C read/writes, and GPIO writes. I also have no main function defined, if that would change anything.

In the software, I am defining two separate threads, one as a "thread_main" function, and the other to initialize and handle BLE.

The main problem I'm facing is that the device seems to only function when I start debugging, or immediately following a flash. Any subsequent power resets (disabling/enabling the power supply) leave the device in a non-functional state.

See below for my prj.conf file.

CONFIG_HEAP_MEM_POOL_SIZE=2048

CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DEVICE_NAME="DEV"
CONFIG_BT_DEVICE_APPEARANCE=833
CONFIG_BT_MAX_CONN=1
CONFIG_BT_MAX_PAIRED=1

# Enable the NUS service
CONFIG_BT_NUS=y

# Enable bonding
CONFIG_BT_SETTINGS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_SETTINGS=y

# Enable DK LED and Buttons library
# CONFIG_DK_LIBRARY=y

# Drivers and peripherals
CONFIG_I2C=y
CONFIG_WATCHDOG=y
CONFIG_SPI=n
CONFIG_GPIO=y

# Power management

# Interrupts
CONFIG_DYNAMIC_INTERRUPTS=n
CONFIG_IRQ_OFFLOAD=n

# Memory protection
CONFIG_THREAD_STACK_INFO=n
CONFIG_THREAD_CUSTOM_DATA=n
CONFIG_FPU=n

# Boot
CONFIG_BOOT_BANNER=n
CONFIG_BOOT_DELAY=0

# Console
CONFIG_CONSOLE=n
CONFIG_UART_CONSOLE=n
CONFIG_STDOUT_CONSOLE=n
CONFIG_PRINTK=n
CONFIG_EARLY_CONSOLE=n

# Build
CONFIG_SIZE_OPTIMIZATIONS=y

# ARM
CONFIG_ARM_MPU=y

# In order to correctly tune the stack sizes for the threads the following
# Configurations can enabled to print the current use:
# CONFIG_THREAD_NAME=y
# CONFIG_THREAD_ANALYZER=y
# CONFIG_THREAD_ANALYZER_AUTO=y
# CONFIG_THREAD_ANALYZER_RUN_UNLOCKED=y
# CONFIG_THREAD_ANALYZER_USE_PRINTK=y
#CONFIG_CONSOLE=y
#CONFIG_UART_CONSOLE=y
#CONFIG_SERIAL=y
#CONFIG_PRINTK=y

# Example output of thread analyzer
#SDC RX              : unused 800 usage 224 / 1024 (21 %)
#BT ECC              : unused 216 usage 888 / 1104 (80 %)
#BT RX               : unused 1736 usage 464 / 2200 (21 %)
#BT TX               : unused 1008 usage 528 / 1536 (34 %)
#ble_write_thread_id : unused 688 usage 336 / 1024 (32 %)
#sysworkq            : unused 1912 usage 136 / 2048 (6 %)
#MPSL signal         : unused 928 usage 96 / 1024 (9 %)
#idle 00             : unused 224 usage 96 / 320 (30 %)
#main                : unused 568 usage 456 / 1024 (44 %)
CONFIG_BT_RX_STACK_SIZE=1024
CONFIG_BT_HCI_TX_STACK_SIZE_WITH_PROMPT=y
CONFIG_BT_HCI_TX_STACK_SIZE=640
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=1536
CONFIG_MPSL_WORK_STACK_SIZE=256
CONFIG_MAIN_STACK_SIZE=864
CONFIG_IDLE_STACK_SIZE=128
CONFIG_ISR_STACK_SIZE=1024
CONFIG_BT_NUS_THREAD_STACK_SIZE=512

# Disable features not needed
CONFIG_TIMESLICING=n
CONFIG_MINIMAL_LIBC_MALLOC=n
CONFIG_LOG=n
CONFIG_ASSERT=n

# Disable Bluetooth features not needed
CONFIG_BT_DEBUG_NONE=y
CONFIG_BT_ASSERT=n
CONFIG_BT_DATA_LEN_UPDATE=n
CONFIG_BT_PHY_UPDATE=n
CONFIG_BT_GATT_CACHING=n
CONFIG_BT_GATT_SERVICE_CHANGED=n
CONFIG_BT_GAP_PERIPHERAL_PREF_PARAMS=n
CONFIG_BT_SETTINGS_CCC_LAZY_LOADING=y
CONFIG_BT_HCI_VS_EXT=n

# Disable Bluetooth controller features not needed
CONFIG_BT_CTLR_PRIVACY=n
CONFIG_BT_CTLR_PHY_2M=n

# Reduce Bluetooth buffers
CONFIG_BT_BUF_EVT_DISCARDABLE_COUNT=1
CONFIG_BT_BUF_EVT_DISCARDABLE_SIZE=43
CONFIG_BT_BUF_EVT_RX_COUNT=2

CONFIG_BT_CONN_TX_MAX=2
CONFIG_BT_L2CAP_TX_BUF_COUNT=2
CONFIG_BT_BUF_ACL_TX_COUNT=3
CONFIG_BT_BUF_ACL_TX_SIZE=27

# Enable timers
CONFIG_TIMER=y
CONFIG_CLOCK_CONTROL_NRF=y

# Enable ADC
CONFIG_ADC=y

CONFIG_BOARD_ENABLE_DCDC=n

CONFIG_RESET_ON_FATAL_ERROR=y
CONFIG_GPIO_AS_PINRESET=n

CONFIG_BT_NUS_SECURITY_ENABLED=n

I've had to make lots of modifications for the device to be able to use BLE.

Also of note, I've had to add a software reset to the board.cmake file to allow the device to run in debug mode.

board_runner_args(nrfjprog "--softreset")

For the present, I've been interfacing with the device using the nRF Connect App for iOS. I would like to note that after a power-on reset, a single advertising packet is sent from the device roughly 15 seconds after power on. Before and after this, no packets are sent. In normal operation, the device advertises every 1.8s.

What I've tried:

  • Adding CONFIG_BOARD_ENABLE_DCDC=n to prj.conf AND board_defconfig with the accompanying code in the Kconfig file at the recommendation of several successful fixes on this forum. No result.
  • Checking return values of every NUS BLE function call, and upon return errors apply NVIC_SystemReset(), No result. I assume this function call does the same as the software reset that is performed by the debugger?
  • Attaching debugger to device following power reset, and upon doing so, it appears that the device is locked in an idle state. No threads are shown in the thread viewer, but I believe this is because of the need to build with optimization for size. Changing the build type to either optimization for speed or optimization for debugging messes with the BLE, and as such I can only use the first option.
  • Changing the NRF Connect SDK version, for some reason no other versions than 2.6.1 and 2.6.2 compile.
  • Adding a main() function that performs a k_thread_resume() on both threads. No result.

If need be, I can add the device tree file as well.

Is there anything I'm not thinking of that I should try in order to get the device to function?

Parents
  • Hi Jacob,

    You should be able to flash your custom board with a simple BLE sample from the SDK and get it to work without too many modifications. If that doesnt work, then it could indicate that there is something wrong with the board design. I see that you have already tried to disable the DC/DC, does this mean that you don't have these components placed on your board? Do you have a LFXO and HFXO crystal? What is the internal capacitance of the crystals, and what is the size of the parallel load capacitors? Maybe you can share the schematics?

    I can make the case private first if you prefer it,

    regards

    Jared 

  • Hi Jared!

    You are correct, I do not have components for the DC/DC converter. I also only have the HFXO, from which I am calibrating the internal RC Oscillator.

    I am on holiday for today and I cannot re-verify some of the examples, though I am fairly certain that I was able to get some samples functioning from just flashing them. Tomorrow I will be back in office, and I will be able to see if they work following the power on reset.

    Please see attached for the MCU schematics. I'd like to keep the case public as long as possible in case anyone else has this problem in the future.


    Furthermore, here is the device defconfig file:

    # Copyright (c) 2024 Nordic Semiconductor ASA
    # SPDX-License-Identifier: Apache-2.0
    
    CONFIG_SOC_SERIES_NRF52X=y
    CONFIG_SOC_NRF52810_QFAA=y
    CONFIG_BOARD_DEV=y
    
    # Enable MPU
    CONFIG_ARM_MPU=y
    
    # Enable hardware stack protection
    CONFIG_HW_STACK_PROTECTION=y
    
    # Enable GPIO
    CONFIG_GPIO=y
    
    # Enable 32KHz internal oscillator since I don't have an external oscillator
    CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y
    CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION=y
    
    # Enable I2C
    CONFIG_I2C=y
    
    CONFIG_NRF_RTC_TIMER=y
    CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y
    CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_LF_ALWAYS_ON=y
    CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD=64
    
    CONFIG_COUNTER=y
    
    CONFIG_BT=y
    CONFIG_ADC=y
    
    CONFIG_BOARD_ENABLE_DCDC=n

  • Jared,

    I'm inclined to believe that the hardware is working, as I am able to flash the device and then remove the debugger from the circuit while retaining functionality. However, when I try to flash the Bluetooth beacon example, the device exhibits the same behavior. Functions after flash, non-functional after power cycling. A Blinky program does the same.

    I have 14 boards built, and I've tried out 3 so far. They all behave the same.  I'll continue to test some more later.

    Another interesting point is that I just tested the Bluetooth beacon sample with the nRF52 DK (PCA10040) and the behavior of the DK is the same as I'm seeing on my custom boards: flash works, sample functions, unresponsive after power cycling.

  • On the off-chance the supply is a coin cell or similar, note a slow voltage rise time on VDD will exhibit the behaviour described as the power-on reset is not a fixed voltage threshold but a capacitively-coupled level inside the nRF52. The debugger or J-Link can override such a lost reset. If using a bench power supply or Li-ion battery then that wouldn't be an issue.

    I2C is enabled; note external pull-ups to SDA and SCL (ie. on a supply different to VDD) will exhibit the same behaviour.

  • After some more tinkering, I was able to get the Bluetooth Beacon sample to work. To do this, I had to increase the calibration period, and remove the counter config line in my device's defconfig file:

    CONFIG_CLOCK_CONTROL_NRF_CALIBRATION_PERIOD=1000
    
    # CONFIG_COUNTER=y

    Neglecting either one of these changes results with a non-functional device. This allowed the program I wrote to function properly. 

    Though I've solved the problem, I'd like to explore why this was the root of the problem. Luckily I can get my application to work without using a hardware counter, but I'm sure others may need to know how to get around this problem.

    EDIT: It looks like while I can do many of the functions that the system requires, the timers that the system depends on are now extremely inconsistent. I may need to have the counters enabled after all.

  • Hi

    Jacob-Lundstrom said:
    I'm inclined to believe that the hardware is working, as I am able to flash the device and then remove the debugger from the circuit while retaining functionality. However, when I try to flash the Bluetooth beacon example, the device exhibits the same behavior. Functions after flash, non-functional after power cycling. A Blinky program does the same.

    When the debugger is connected it will force the chip into debug mode. Debug mode behave different than normal mode in that it will force certain settings in the chip such as LDO and sometimes internal RC for the LCFLK. To exit debug mode you have to power cycle the board.

    If neither Blinky or BLE sample works after power cycling then it could indicate that something is not functional on your board.

    My hunch is that it's related to either the LFCLK or your HFXO crystal. 

    Can you try this simple modification of blinky sample and confirm that the chip is able to start the HF crystal successfully?

    /*
     * Copyright (c) 2016 Intel Corporation
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <stdio.h>
    #include <zephyr/kernel.h>
    #include <zephyr/drivers/gpio.h>
    
    /* 1000 msec = 1 sec */
    #define SLEEP_TIME_MS   1000
    
    /* The devicetree node identifier for the "led0" alias. */
    #define LED0_NODE DT_ALIAS(led0)
    
    /*
     * A build error on this line means your board is unsupported.
     * See the sample documentation for information on how to fix this.
     */
    static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
    
    int main(void)
    {
    	int ret;
    	bool led_state = true;
    
    	if (!gpio_is_ready_dt(&led)) {
    		return 0;
    	}
    
    	ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
    	if (ret < 0) {
    		return 0;
    	}
    
    	NRF_CLOCK->TASKS_HFCLKSTART = 1;
    	while(!NRF_CLOCK->EVENTS_HFCLKSTARTED){}
    
    	//If the program pass this point then it was able to start HF successfully
    
    	while (1) {
    		ret = gpio_pin_toggle_dt(&led);
    		if (ret < 0) {
    			return 0;
    		}
    
    		led_state = !led_state;
    		printf("LED state: %s\n", led_state ? "ON" : "OFF");
    		k_msleep(SLEEP_TIME_MS);
    	}
    	return 0;
    }
    

    If it doesn't work, can you share your build folder? Remember to power cycle the board to exit debug mode. 

    Jacob-Lundstrom said:
    To do this, I had to increase the calibration period, and remove the counter config line in my device's defconfig file:

    Default for this config is 4 sec, what was it before you modified it and what did you modify it to? Not sure if 1000 is the pre or post value. If the period is too big then you can get issues with clock accuracy. 

    regards

    Jared

  • After testing out the modified sample you've provided using the same prj.config file for my custom program, the device does indeed pass the HFXO initialization step, including after a power cycle with no debugger attached.

    However, I did have to re-enable the ARM MPU for the project to compile, but I assume this is because we're directly interfacing with registers? I don't exactly know the line of reasoning there.

    As for the initial value of the calibration period, it was 64ms, as you can see in one of my previous comments with the schematic where I included the device defconfig file. I have since changed that value to 1000 ms.

Reply
  • After testing out the modified sample you've provided using the same prj.config file for my custom program, the device does indeed pass the HFXO initialization step, including after a power cycle with no debugger attached.

    However, I did have to re-enable the ARM MPU for the project to compile, but I assume this is because we're directly interfacing with registers? I don't exactly know the line of reasoning there.

    As for the initial value of the calibration period, it was 64ms, as you can see in one of my previous comments with the schematic where I included the device defconfig file. I have since changed that value to 1000 ms.

Children
  • Hi,

    Jacob-Lundstrom said:
    After testing out the modified sample you've provided using the same prj.config file for my custom program, the device does indeed pass the HFXO initialization step, including after a power cycle with no debugger attached.

    Ok so that is a different from when you tested the blink sample earlier?

    Jacob-Lundstrom said:
    However, when I try to flash the Bluetooth beacon example, the device exhibits the same behavior. Functions after flash, non-functional after power cycling. A Blinky program does the same

    next, lets try to modify the zephyr beacon sample by adding this at the start of the HF crystal early in the code.

    /* main.c - Application main entry point */
    
    /*
     * Copyright (c) 2015-2016 Intel Corporation
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    #include <zephyr/types.h>
    #include <stddef.h>
    #include <zephyr/sys/printk.h>
    #include <zephyr/sys/util.h>
    
    #include <zephyr/bluetooth/bluetooth.h>
    #include <zephyr/bluetooth/hci.h>
    
    #define DEVICE_NAME CONFIG_BT_DEVICE_NAME
    #define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
    
    /*
     * Set Advertisement data. Based on the Eddystone specification:
     * https://github.com/google/eddystone/blob/master/protocol-specification.md
     * https://github.com/google/eddystone/tree/master/eddystone-url
     */
    static const struct bt_data ad[] = {
    	BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_NO_BREDR),
    	BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0xaa, 0xfe),
    	BT_DATA_BYTES(BT_DATA_SVC_DATA16,
    		      0xaa, 0xfe, /* Eddystone UUID */
    		      0x10, /* Eddystone-URL frame type */
    		      0x00, /* Calibrated Tx power at 0m */
    		      0x00, /* URL Scheme Prefix http://www. */
    		      'z', 'e', 'p', 'h', 'y', 'r',
    		      'p', 'r', 'o', 'j', 'e', 'c', 't',
    		      0x08) /* .org */
    };
    
    /* Set Scan Response data */
    static const struct bt_data sd[] = {
    	BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
    };
    
    static void bt_ready(int err)
    {
    	char addr_s[BT_ADDR_LE_STR_LEN];
    	bt_addr_le_t addr = {0};
    	size_t count = 1;
    
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return;
    	}
    
    	printk("Bluetooth initialized\n");
    
    	/* Start advertising */
    	err = bt_le_adv_start(BT_LE_ADV_NCONN_IDENTITY, ad, ARRAY_SIZE(ad),
    			      sd, ARRAY_SIZE(sd));
    	if (err) {
    		printk("Advertising failed to start (err %d)\n", err);
    		return;
    	}
    
    
    	/* For connectable advertising you would use
    	 * bt_le_oob_get_local().  For non-connectable non-identity
    	 * advertising an non-resolvable private address is used;
    	 * there is no API to retrieve that.
    	 */
    
    	bt_id_get(&addr, &count);
    	bt_addr_le_to_str(&addr, addr_s, sizeof(addr_s));
    
    	printk("Beacon started, advertising as %s\n", addr_s);
    }
    
    int main(void)
    {
    
    	NRF_CLOCK->TASKS_HFCLKSTART = 1;
    	while(!NRF_CLOCK->EVENTS_HFCLKSTARTED){}
    
    	//If the program pass this point then it was able to start HF successfully
    
    	int err;
    
    	printk("Starting Beacon Demo\n");
    
    	/* Initialize the Bluetooth Subsystem */
    	err = bt_enable(bt_ready);
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    	}
    	return 0;
    }
    

    Please try without modifying any of the configs file first. We want to add minimal change between each step so that we can pinpoint exactly what step that is causing the issue. 

    Does it work when you power cycle the board now?

    regards

    Jared 

Related