Achieve non-concurrent switching between ESB/BLE

Hi:

I use ncs3.1.1.

BLE and ESB clock conflict now.
ESB clock.

static int clocks_start(void)
{
	int err;
	int res;
	struct onoff_manager *clk_mgr;
	struct onoff_client clk_cli;

	clk_mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);
	if (!clk_mgr) {
		printk("Unable to get the Clock manager\n");
		return -ENXIO;
	}

	sys_notify_init_spinwait(&clk_cli.notify);

	err = onoff_request(clk_mgr, &clk_cli);
	if (err < 0) {
		printk("Clock request failed: %d", err);
		return err;
	}

	do {
		err = sys_notify_fetch_result(&clk_cli.notify, &res);
		if (!err && res) {
			printk("Clock could not be started: %d\n", res);
			return res;
		}
	} while (err);

#if NRF54L_ERRATA_20_PRESENT
	if (nrf54l_errata_20()) {
		nrf_power_task_trigger(NRF_POWER, NRF_POWER_TASK_CONSTLAT);
	}
#endif /* NRF54L_ERRATA_20_PRESENT */

#if defined(NRF54LM20A_ENGA_XXAA)
	/* MLTPAN-39 */
	nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_PLLSTART);
#endif

	printk("HF clock started\n");
	return 0;
}


When I default to using ESB
int main(void)
{
//ESB
    err = clocks_start();
    if (err) {
         printk("clocks_start failed: %d\n", err);
        return 0;
    }
......

And CONFIG_BT=y, the runtime will crash.

MPSL ASSERT: file=2 (ptr=0x2000714c), line=62
E: ***** HARD FAULT *****
E:   Fault escalation (see below)
E: ARCH_EXCEPT with reason 4

E: r0/a1:  0x00000004  r1/a2:  0x2002199c  r2/a3:  0x00000000
E: r3/a4:  0x00000004 r12/ip:  0x00000000 r14/lr:  0
x20021603
E:  xpsr:  0x2100009a
E: Faulting instruction address (r15/pc): 0x20021612
E: >>> ZEPHYR FATAL ERROR 4: Kernel panic on CPU 0
E: Fault during interrupt handling

E: Current thread: 0x20003820 (idle)
E: Resetting system


#
# Copyright (c) 2019 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#
CONFIG_NCS_SAMPLES_DEFAULTS=y
CONFIG_ESB=y
CONFIG_ESB_RADIO_IRQ_PRIORITY=0
CONFIG_ESB_EVENT_IRQ_PRIORITY=1
CONFIG_CLOCK_CONTROL=y

CONFIG_ESB_FAST_SWITCHING=y
CONFIG_ESB_FAST_CHANNEL_SWITCHING=y

CONFIG_DK_LIBRARY=y

CONFIG_SPIN_VALIDATE=n
CONFIG_PM=n
CONFIG_TIMESLICING=n

CONFIG_CODE_DATA_RELOCATION=y

CONFIG_NRFX_DPPI10=y
CONFIG_NRFX_DPPI20=y
CONFIG_NRFX_TIMER21=y
CONFIG_NRFX_SPIM21=y
CONFIG_NRFX_GPIOTE20=y

CONFIG_SPI=y
CONFIG_SPI_NRFX=y
CONFIG_SPI_NRFX_RAM_BUFFER_SIZE=8

CONFIG_HEAP_MEM_POOL_SIZE=8192
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096
CONFIG_MAIN_STACK_SIZE=4096
#CONFIG_UDC_BUF_COUNT=16
#CONFIG_UDC_BUF_POOL_SIZE=4096

#CONFIG_USB_DEVICE_STACK_NEXT=y
#CONFIG_UDC_DRIVER_LOG_LEVEL_WRN=y

#CONFIG_USBD_HID_SUPPORT=y
#CONFIG_USBD_LOG_LEVEL_WRN=y
#CONFIG_USBD_HID_LOG_LEVEL_WRN=y

#CONFIG_SAMPLE_USBD_MANUFACTURER="Nordic Semiconductor"
#CONFIG_SAMPLE_USBD_PRODUCT="USB dongle demo"
#CONFIG_SAMPLE_USBD_SELF_POWERED=n
#CONFIG_SAMPLE_USBD_MAX_POWER=50

CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DEVICE_NAME="Nordic_HIDS_mouse"
CONFIG_BT_DEVICE_APPEARANCE=962
CONFIG_BT_L2CAP_TX_BUF_COUNT=5

CONFIG_BT_MAX_CONN=1
CONFIG_BT_MAX_PAIRED=4
CONFIG_BT_ID_MAX=4 
CONFIG_BT_CTLR_SDC_PERIPHERAL_COUNT=1

CONFIG_BT_EXT_ADV=y
CONFIG_BT_EXT_ADV_MAX_ADV_SET=1

# HID Service
CONFIG_BT_HIDS=y
CONFIG_BT_HIDS_MAX_CLIENT_COUNT=1
CONFIG_BT_CONN_CTX=y
CONFIG_BT_HIDS_INPUT_REP_MAX=1
CONFIG_BT_HIDS_OUTPUT_REP_MAX=0
CONFIG_BT_HIDS_FEATURE_REP_MAX=0
CONFIG_BT_ATT_PREPARE_COUNT=2
CONFIG_BT_GATT_SERVICE_CHANGED=y


# Battery Service
CONFIG_BT_BAS=y

# Device Information Service
CONFIG_BT_DIS=y
CONFIG_BT_DIS_PNP=y
CONFIG_BT_DIS_MANUF_NAME_STR="NordicSemiconductor"
CONFIG_BT_DIS_PNP_VID_SRC=2
CONFIG_BT_DIS_PNP_VID=0x1915
CONFIG_BT_DIS_PNP_PID=0xEEEE
CONFIG_BT_DIS_PNP_VER=0x0100


CONFIG_APP_EVENT_MANAGER=y
CONFIG_CAF=y

# SMP(Security Management Protocol)
CONFIG_BT_SMP=y

# 安全配置
CONFIG_BT_HIDS_DEFAULT_PERM_RW_ENCRYPT=y

# 启用 bond 持久化存储
CONFIG_BT_SETTINGS=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
#CONFIG_NVS=y # nRF54L 系列请改用 CONFIG_ZMS=y
CONFIG_ZMS=y
CONFIG_SETTINGS=y  

# 白名单 + 隐私特性
CONFIG_BT_FILTER_ACCEPT_LIST=y
#CONFIG_BT_PRIVACY=y

CONFIG_BT_UNINIT_MPSL_ON_DISABLE=y
CONFIG_DYNAMIC_INTERRUPTS=y
CONFIG_DYNAMIC_DIRECT_INTERRUPTS=y
CONFIG_MPSL_DYNAMIC_INTERRUPTS=y

CONFIG_ASSERT=y
CONFIG_MPSL_ASSERT_HANDLER=y
CONFIG_LOG=y






  • Hi:
    The exact location of the collapse.

                        struct main_msg msg;
                        msg.type = MAIN_MSG_MOTION;
                        msg.cnt = 1;
                        int err = k_msgq_put(&main_msgq, &msg, K_NO_WAIT);

    But after canceling CONFIG_BT=y, there's no problem.

  • Hi,

    The crash might not be caused by k_msgq_put(). That is most likely where the failure becomes visible.

    The MPSL assertion shows that Bluetooth/MPSL and ESB are conflicting over shared resources such as the radio, clock, or interrupts.

    Although you wish to run ESB and Bluetooth non-concurrently, enabling CONFIG_BT=y initializes components that manage these resources. Therefore, the transition between ESB and Bluetooth must fully release one protocol before starting the other. i.e. use only one protocol at a time.

    Please check the following:

    Configure MPSL and ESB to use dynamic interrupts.
    Fully stop ESB before starting Bluetooth.
    Call bt_disable() before returning to ESB.
    Enable CONFIG_BT_UNINIT_MPSL_ON_DISABLE=y so disabling Bluetooth also releases MPSL’s radio-related resources.
    Avoid manually controlling clocks or peripherals while MPSL owns them.

    john.liu said:
    But after canceling CONFIG_BT=y, there's no problem.

    when you do this, the Bluetooth controller and its MPSL-related resource management are no longer included or initialized. ESB then has exclusive ownership of the radio, clocks, and interrupts, so the conflict disappears.

    In simple words,  without Bluetooth enabled, only ESB is trying to drive the radio;

    with Bluetooth enabled, two different systems believe they control it.

    -Priyanka

  • Hi Priyanka:
    Thanks for your help!
    CONFIG_BT_UNINIT_MPSL_ON_DISABLE=y is already enabled.

    When I start up, it defaults to ESB mode.

    static K_SEM_DEFINE(bt_init_sem, 0, 1);
    
    static void bt_ready_cb(int err)
    {
        if (err) {
            printk("bt_enable failed in callback: %d\n", err);
        }
        /* 通知 main bt_enable 已完成 */
        k_sem_give(&bt_init_sem);
    }
    
    int main(void)
    {
    	int err;
        err = bt_enable(bt_ready_cb);
        if (err) {
            printk("bt_enable failed: %d\n", err);
            return 0;
        }
        /* 阻塞等待初始化完成 */
        k_sem_take(&bt_init_sem, K_FOREVER);
        printk("bt_enable complete\n");
    
        err = bt_disable();
        if (err) {
            printk("bt_disable failed: %d\n", err);
            return 0;
        }
    	k_sleep(K_MSEC(500));
    	printk("k_sleep done\n");
    	err = clocks_start();
    	printk("clocks_start ret: %d\n", err);
    	if (err) {
    		 printk("clocks_start failed: %d\n", err);
    		return 0;
    	}
    	..........

    It will get stuck at clocks_start()...

    static int clocks_start(void)
    {
    	int err;
    	int res;
    	struct onoff_manager *clk_mgr;
    	struct onoff_client clk_cli;
    
    	clk_mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);
    	if (!clk_mgr) {
    		printk("Unable to get the Clock manager\n");
    		return -ENXIO;
    	}
    
    	sys_notify_init_spinwait(&clk_cli.notify);
    
    	err = onoff_request(clk_mgr, &clk_cli);
    	if (err < 0) {
    		printk("Clock request failed: %d", err);
    		return err;
    	}
    
    	do {
    		err = sys_notify_fetch_result(&clk_cli.notify, &res);
    		if (!err && res) {
    			printk("Clock could not be started: %d\n", res);
    			return res;
    		}
    	} while (err);
    
    #if NRF54L_ERRATA_20_PRESENT
    	if (nrf54l_errata_20()) {
    		nrf_power_task_trigger(NRF_POWER, NRF_POWER_TASK_CONSTLAT);
    	}
    #endif /* NRF54L_ERRATA_20_PRESENT */
    
    #if defined(NRF54LM20A_ENGA_XXAA)
    	/* MLTPAN-39 */
    	nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_PLLSTART);
    #endif
    
    	printk("HF clock started\n");
    	return 0;
    }
    

Related