Multiple IPC channels

Hello,

Referencing this link here, I am looking for any updates to using IPC for some custom inter-core communication WHILE ALSO running Bluetooth on an nrf5340.

Long story short, I have a project with custom code for both the cpu_app and cpu_net cores of an nrf5340, and I am looking for a way to communicate between both. Right now, the cpu_net code is running the hci_rpmsg sample so that I can use BLE from the application core. I have not found a clean way to modify this to provide some hooks for my custom use case, nor do I want to try.

If there are any updates on being able to use a custom IPC channel and BLE at the same time, or if IPC is not the right abstraction layer to go through, please let me know! The link I provided mention using the BTS but I am not sure how that is relevant or possible.

I know that there are some complex structures being put to use here and I have tried to spend some time learning it but perhaps there is an easy-to-follow overview of inter-process communication that shows us how IPC, RMPSG, ICMSG, MBOX, shared SRAM and all other abstraction layers play together.

Thank you much.

Parents Reply Children
  • Hi!

    I was able to get it to run in a BLE application. I'm not seeing any crashes here.

    Did some minor modifications to the original example code.

    Network core:

    On hci_rpmsg sample I added this:

    /////////////
    #include <nrfx_timer.h>
    #include <nrfx_dppi.h>
    #include <nrfx_ipc.h>
    
    // Get a reference to the TIMER instance
    static const nrfx_timer_t sync_timer = NRFX_TIMER_INSTANCE(2);
    
    // Interrupt handler for the timer
    // NOTE: This callback is triggered by an interrupt. Many drivers or modules in Zephyr can not be accessed directly from interrupts, 
    //		 and if you need to access one of these from the timer callback it is necessary to use something like a k_work item to move execution out of the interrupt context. 
    void sync_timer_event_handler(nrf_timer_event_t event_type, void * p_context)
    {
    	static int counter = 0;
    	switch(event_type) {
    		case NRF_TIMER_EVENT_COMPARE0:
    			printk("Sync timer callback. Counter = %d\n", counter++);
    			break;
    		
    		default:
    			break;
    	}
    }
    
    // Function for initializing the TIMER peripheral using the nrfx driver
    static void sync_timer_init(void)
    {
    	uint32_t base_frequency = NRF_TIMER_BASE_FREQUENCY_GET(sync_timer.p_reg);
    	nrfx_timer_config_t timer_config = NRFX_TIMER_DEFAULT_CONFIG(base_frequency);
    	timer_config.bit_width = NRF_TIMER_BIT_WIDTH_16;
    
    	int err = nrfx_timer_init(&sync_timer, &timer_config, sync_timer_event_handler);
    	if (err != NRFX_SUCCESS) {
    		printk("Error initializing timer: %x\n", err);
    	}
    	
    	// Trigger a callback 10us after the timer is reloaded by the appcore
    	nrfx_timer_extended_compare(&sync_timer, NRF_TIMER_CC_CHANNEL0, 10, 0, true);
    
    	IRQ_DIRECT_CONNECT(TIMER2_IRQn, 1, nrfx_timer_2_irq_handler, 0);
    	irq_enable(TIMER2_IRQn);
    }
    
    static void synchronized_start_init(void)
    {
    	uint8_t dppi_ch;
    
    	// DPPI init
    	nrfx_dppi_channel_alloc(&dppi_ch);
    	NRF_TIMER0->SUBSCRIBE_CLEAR = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | dppi_ch;
    	nrfx_dppi_channel_enable(dppi_ch);
    
    	// IPC init
    	uint8_t ipc_ch = 5;
    	NRF_IPC->RECEIVE_CNF[ipc_ch] = BIT(ipc_ch);
    	NRF_IPC->PUBLISH_RECEIVE[ipc_ch] = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | dppi_ch;
    
    	nrfx_timer_enable(&sync_timer);
    }
    
    /////////////
    
    int main(void)
    {
    	int err;
    
    	sync_timer_init();
    	synchronized_start_init();

    Added to prj.conf (samples\bluetooth\hci_rpmsg\prj.conf)

    CONFIG_NRFX_DPPI=y
    CONFIG_NRFX_EGU1=y
    CONFIG_NRFX_IPC=y
    CONFIG_NRFX_TIMER2=y

    Application core:

    On the BLE app:

    ///////////////////////
    
    #include <nrfx_timer.h>
    #include <nrfx_dppi.h>
    #include <nrfx_egu.h>
    #include <nrfx_ipc.h>
    
    
    // Get a reference to the TIMER instance
    static const nrfx_timer_t sync_timer = NRFX_TIMER_INSTANCE(2);
    
    // Interrupt handler for the timer
    // NOTE: This callback is triggered by an interrupt. Many drivers or modules in Zephyr can not be accessed directly from interrupts, 
    //		 and if you need to access one of these from the timer callback it is necessary to use something like a k_work item to move execution out of the interrupt context. 
    void sync_timer_event_handler(nrf_timer_event_t event_type, void * p_context)
    {
    	static int counter = 0;
    	switch(event_type) {
    		case NRF_TIMER_EVENT_COMPARE1:
    			printk("Sync timer callback. Counter = %d\n", counter++);
    			break;
    		
    		default:
    			break;
    	}
    }
    
    // Function for initializing the TIMER peripheral using the nrfx driver
    static void sync_timer_init(void)
    {
    	uint32_t base_frequency = NRF_TIMER_BASE_FREQUENCY_GET(sync_timer.p_reg);
    	nrfx_timer_config_t timer_config = NRFX_TIMER_DEFAULT_CONFIG(base_frequency);
    	timer_config.bit_width = NRF_TIMER_BIT_WIDTH_16;
    
    	int err = nrfx_timer_init(&sync_timer, &timer_config, sync_timer_event_handler);
    	if (err != NRFX_SUCCESS) {
    		printk("Error initializing timer: %x\n", err);
    	}
    
    	IRQ_DIRECT_CONNECT(TIMER2_IRQn, 1, nrfx_timer_2_irq_handler, 0);
    	irq_enable(TIMER2_IRQn);
    }
    
    // Function for scheduling repeated callbacks from the timer
    static void sync_timer_repeated_configure(uint32_t timeout_us)
    {
    	nrfx_timer_extended_compare(&sync_timer, NRF_TIMER_CC_CHANNEL0, timeout_us, 
                                    NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);
    	
    	// Trigger a callback 10us after the timer is reloaded
    	nrfx_timer_extended_compare(&sync_timer, NRF_TIMER_CC_CHANNEL1, 10, 0, true);
    }
    
    static void synchronized_start_init(void)
    {
    	uint8_t dppi_ch;
    
    	// DPPI init
    	nrfx_dppi_channel_alloc(&dppi_ch);
    	//NRF_EGU1->PUBLISH_TRIGGERED[0] = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | dppi_ch;
    	NRF_TIMER2->PUBLISH_COMPARE[0] = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | dppi_ch;
    	nrfx_dppi_channel_enable(dppi_ch);
    
    	// IPC init
    	uint8_t ipc_ch = 5;
    	NRF_IPC->SEND_CNF[ipc_ch] = BIT(ipc_ch);
    	NRF_IPC->SUBSCRIBE_SEND[ipc_ch] = DPPIC_SUBSCRIBE_CHG_EN_EN_Msk | dppi_ch;
    }
    
    static void synchronized_start_activate(void)
    {
    	NRF_TIMER2->TASKS_START = 1;
    }
    
    //////////////////////

    Added to prj.conf

    CONFIG_NRFX_DPPI=y
    CONFIG_NRFX_EGU1=y
    CONFIG_NRFX_IPC=y
    CONFIG_NRFX_TIMER2=y

    BR,

    Sigurd

  • I am still getting the same crash as before. Here is what I am doing in the BLE app (cpu_app main.c file).

    Note this code runs AFTER the ble_ready callback is hit.

    Any other ideas on what is causing this crash? I am iterating through my code to toggle different sections/features to narrow down what might be causing the crash.

  • I tested it with the peripheral_lbs example, and did not see a crash.

    Here is how and where in the main() I added it: (cpu_app main.c file)

    int main(void)
    {
    	int blink_status = 0;
    	int err;
    
    	printk("Starting Bluetooth Peripheral LBS example\n");
    
    	err = dk_leds_init();
    	if (err) {
    		printk("LEDs init failed (err %d)\n", err);
    		return 0;
    	}
    
    	err = init_button();
    	if (err) {
    		printk("Button init failed (err %d)\n", err);
    		return 0;
    	}
    
    	if (IS_ENABLED(CONFIG_BT_LBS_SECURITY_ENABLED)) {
    		err = bt_conn_auth_cb_register(&conn_auth_callbacks);
    		if (err) {
    			printk("Failed to register authorization callbacks.\n");
    			return 0;
    		}
    
    		err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks);
    		if (err) {
    			printk("Failed to register authorization info callbacks.\n");
    			return 0;
    		}
    	}
    
    	err = bt_enable(NULL);
    	if (err) {
    		printk("Bluetooth init failed (err %d)\n", err);
    		return 0;
    	}
    
    	printk("Bluetooth initialized\n");
    
    	if (IS_ENABLED(CONFIG_SETTINGS)) {
    		settings_load();
    	}
    
    	err = bt_lbs_init(&lbs_callbacs);
    	if (err) {
    		printk("Failed to init LBS (err:%d)\n", err);
    		return 0;
    	}
    
    	err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad),
    			      sd, ARRAY_SIZE(sd));
    	if (err) {
    		printk("Advertising failed to start (err %d)\n", err);
    		return 0;
    	}
    
    	printk("Advertising successfully started\n");
    
    	sync_timer_init();
    
    	sync_timer_repeated_configure(1000000);
    
    	synchronized_start_init();
    	k_msleep(1000);
    	synchronized_start_activate();
    
    
    
    	for (;;) {
    		dk_set_led(RUN_STATUS_LED, (++blink_status) % 2);
    		k_sleep(K_MSEC(RUN_LED_BLINK_INTERVAL));
    	}
    }

  • Thanks for this. I validated your sample then I was able to integrate it into my project pretty well.

    This will be very helpful moving forward. My question now is that I will need a little bit of shared data to go back and forth. Up to 6 bytes.

    This does not need to be triggered in the backend with DPPI or other peripherals. It can look more like the RPC sample - however I also struggled with this alongside BLE.

    First of all, is RPC the correct one to use? This is what I wanted to get to work: developer.nordicsemi.com/.../README.html

  • Hi!

    Robert de Brum said:
    Thanks for this. I validated your sample then I was able to integrate it into my project pretty well.

    Great!

    Robert de Brum said:
    First of all, is RPC the correct one to use?

    It looks to fulfill your use-case. Only thing I can think of right now for why it should not work with BLE, is that there could be a conflict with SDC/MPSL and the entropy driver. Maybe try to send some predefined bytes instead of random ones gathered from the entropy driver.

Related