This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

RBC mesh timer 0 usage

Hi I am using the RBC mesh "github.com/.../nRF51-ble-bcast-mesh", sd_110_7.1 and sdk_6.2

The rbc mesh is using timer0 as well as the softdevice. I removed the app timers from my project so it compiles and works but there is a problem.

It craches after some time due to timer0 beeing protected by the MPU and write reqests beeing sent from the rbc mesh to timer0 peripheral which results in hardfaults.

What can I do to fix this? Also problem occures when writing to PPI but this can be fixed by running ppi accesses through the sd_ppi api.

For example my code halts here (timer_control.c):

void timer_abort(uint8_t timer_index){
if (timer_index < 4)
{
    [HERE] NRF_TIMER0->INTENCLR = (1 << (TIMER_INTENCLR_COMPARE0_Pos + timer_index));
    active_callbacks &= ~(1 << timer_index);   
    NRF_PPI->CHENCLR = (1 << (TIMER_PPI_CH_START + timer_index));
    never_used_bitmap |= (1 << timer_index);
}}

And NRF_MPU->PERR0= 0xc300f903

  • Hi,

    This is caused by the function being called outside of the RBC mesh timeslot granted by the Softdevice Timeslot API, and is probably the result of a race condition in the framework.

    I am unable to reproduce the crash on my setup, are you able to produce a stack trace of the crash?

    Assuming it happens in the SWI0_IRQHandler() context, this can (assumably) be fixed by avoiding calling the end_timer_handler() callback while an async_evt is being processed in SWI0. This could be solved by three minor changes in timeslot_handler.c:

    • Change both calls to

    timer_order_cb_sync_exec(g_timeslot_length - TIMESLOT_END_SAFETY_MARGIN_US, end_timer_handler);

    in timeslot_handler.c to

    timer_order_cb(g_timeslot_length - TIMESLOT_END_SAFETY_MARGIN_US, end_timer_handler);

    (notice the removed sync_exec from the function name). This will put the end_timer_handler in the async_evt queue, instead of interrupting any ongoing async events.

    timeslot_handler.c, L290:

     static bool g_end_timer_triggered = false;
     /**
     * @brief Timeslot end guard timer callback. Attempts to extend the timeslot. 
     */
     static void end_timer_handler(void)
     {
        g_end_timer_triggered = true;
    
        /* Fake a radio IRQ, making the SD call radio_signal_callback(), letting us end timeslot */
        NVIC_SetPendingIRQ(RADIO_IRQn);
     }`
    
    • Finally, put the following 10 lines right after the large switch-case in radio_signal_callback():

    _

    if (g_end_timer_triggered)
    {
        uint32_t length_us = ((g_timeslot_length > 100000)? 100000 : g_timeslot_length);
        radio_request_earliest.params.earliest.length_us = length_us;
        g_ret_param.callback_action = NRF_RADIO_SIGNAL_CALLBACK_ACTION_REQUEST_AND_END;
        g_ret_param.params.request.p_next = &radio_request_earliest;
        
        g_next_timeslot_length = length_us;
        
        g_end_timer_triggered = false;
    }
    

    This is a dirty hotfix, and lacks some proper verification before it should be considered completely safe, but it does not introduce any immediate new errors on my setup. Does this solve your issue (preferrably without introducing new ones)?

    If you could post this issue to the project's issue tracker on GitHub, I'll see if I can provide a cleaner fix in the next patch, as it appears to be a bug in the framework.

  • Thanks for you response I tried your solution but the problem remains. Stack trace:

    Thread [1] (Suspended: Signal 'SIGTRAP' received. Description: Trace/breakpoint trap.) 13 error_loop() main.c:69 0x000161f4 12 HardFault_Handler() main.c:113 0x00016284 11 () 0xfffffff1 10 get_available_timer() timer_control.c:80 0x0001bb3a 9 timer_get_timestamp() timer_control.c:237 0x0001c0b2 8 timeslot_get_remaining_time() timeslot_handler.c:578 0x0001c9ce 7 trickle_step_callback() transport_control.c:191 0x0001cbd2 6 async_event_execute() timeslot_handler.c:229 0x0001c49e 5 SWI0_IRQHandler() timeslot_handler.c:323 0x0001c5c8 4 () 0xfffffff9 3 0x0001193e 2 0x0000116a 1 0x0000116a

    The line that failed:

            NRF_PPI->CHENCLR = (1 << (TIMER_PPI_CH_START + i));
    

    NRF_MPU->PERR0=0xc300f903 so even if I change to sdoftdevice compatible ppi calls the line 2 lines down will fail: TIMER0->EVENTS_COMPARE[i] = 0;

    Can I change timer 0 to some other timer, will this help?

    I think that with your solution the program craches sooner.

    I will attach my rbc mesh code: rbc_mesh.zip

    I start the rbc mesh with the following code: rbc_mesh_init_params_t init_params;

    init_params.access_addr = 0xA541A68F;
    init_params.adv_int_ms = 100;
    init_params.channel = 38;
    init_params.handle_count = 255;
    init_params.packet_format = RBC_MESH_PACKET_FORMAT_ADV_COMPATIBLE;
    init_params.radio_mode = RBC_MESH_RADIO_MODE_BLE_1MBIT;
    
    error_code = rbc_mesh_init(init_params);
    APP_ERROR_CHECK(error_code);
    

    Note that I have changed the RBC mesh to suite my needs as I want 255 handles and dont want each handle to be a characteristic.

  • You can't use Timer0 outside the time slot. This timer is used by the softdevice and will hardfault if accessed outside the timeslot. I would recommend you to have a look at the shared resources table in the S110 softdevice specification. As you suggest yourself, try using a timer that's not controlled by the softdevice and you should at least not have the timer issue.

  • I see you are using handle_count at 255, the max value is 155. "The maximum number of handle-value pairs available to the application. May not be higher than 155 due to BLE namespace requirements" (from: rbc_mesh/rbc_mesh.h)

    could you test it with lower count?

  • I am not using the default GAP stuff, I am storing the data in a vector instead to avoid haveing a lot of characteristics. So I can use 255 hadle value pairs.

Related