Ctimer configuration and freezes while using BLE

Hi,

I’ve been trying to make two sparkfun devices (either apollo3 blue or artemis nano) communicate through BLE.

I’ve started with the Amdtp example from SDK 2.2.0 as well as the Cordio Tag example. My current code has one server which advertises when not connected, and one client which will scan, connect to the server, send 500 messages, disconnect, then repeat.

On top of managing the BLE communication, I also want my client device to sample data using a ctimer interrupt. However, while experimenting with different clock configurations, I’ve noticed that one of the devices sometimes freezes.

Here is the slightly modified configuration of the clocks in the examples from the SDK:

void scheduler_timer_init(void) {

    // config for timer A: continuous timer that serves for time tracking
    am_hal_ctimer_clear(0, AM_HAL_CTIMER_TIMERA);
    am_hal_ctimer_config_single(0, AM_HAL_CTIMER_TIMERA, (AM_HAL_CTIMER_FN_CONTINUOUS | AM_HAL_CTIMER_LFRC_512HZ));

    // config for timer B: one-shot to provide interrupts for timed events
    am_hal_ctimer_clear(0, AM_HAL_CTIMER_TIMERB);
    am_hal_ctimer_config_single(0, AM_HAL_CTIMER_TIMERB, (AM_HAL_CTIMER_FN_ONCE | AM_HAL_CTIMER_INT_ENABLE | AM_HAL_CTIMER_LFRC_512HZ));

    am_hal_ctimer_int_clear(AM_HAL_CTIMER_INT_TIMERB0);
    am_hal_ctimer_int_enable(AM_HAL_CTIMER_INT_TIMERB0);

    NVIC_EnableIRQ(CTIMER_IRQn);

    am_hal_interrupt_master_enable();
    am_hal_ctimer_start(0, AM_HAL_CTIMER_TIMERA);
}

This configuration seems to work well, however if I use the XT clock at 32kHz (AM_HAL_CTIMER_XT_32_768KHZ) instead of the LFRC at 512Hz (AM_HAL_CTIMER_LFRC_512HZ), then I experience many freezes. By freeze I mean that the device doesn’t go through the main loop anymore. It seems to never wake up from the deep sleep.

In both cases, the code behaves exactly the same, I’ve edited the time tracking to take into account the new frequency of the clock.

Here’s what my main loop looks like:

while (1) {

    // Calculate the elapsed time from our free-running timer, and update
    // the software timers in the WSF scheduler.
    update_scheduler_timers();
    wsfOsDispatcher();
    
    // Enable an interrupt to wake us up next time we have a scheduled event.
    set_next_wakeup();

    am_hal_interrupt_master_disable();

    // Check to see if the WSF routines are ready to go to sleep.
    uint8_t readyToSleep = wsfOsReadyToSleep();

    am_hal_interrupt_master_enable();

    if (readyToSleep) {
        am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP);
    }
}

There are a few things I don’t quite understand:

  • - I've noticed that the source code from the Cordio Tag example calls am_hal_interrupt_master_enable() *after* the deep sleep command. Since interrupts are disabled when calling am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP), doesn't that mean that the ctimer won't wake it up?
  • - am_hal_interrupt_master_disable() doesn't seem to prevent BLE interrupts at all, am_ble_isr(), can be called right between am_hal_interrupt_master_disable() and am_hal_interrupt_master_enable().
  • Has anyone encountered such freezes or know what could be the cause?

    Here are some additional details that might help:

  • - Most often, the client freezes first, while sending messages. The server sometimes doesn't detect that the client froze and thinks that it is still connected. Even though it is supposed to disconnect automatically after 5 seconds of inactivity from the client.
  • - The freezes occur around 15 minutes after starting the device while using the XT clock.
  • - Once a device freezes, the other one never freezes, so the freezes seem to be related to BLE.
  • I am still experimenting. I’m not even sure that they never freeze using the LFRC, it could just be very rare. I’ll post any findings I have.

    Thanks

    Which interrupt routine is called once your one-shot TimerB runs out. The original (BLE_cordio_tag.c) had once TimerA : am_hal_ctimer_int_register(AM_HAL_CTIMER_INT_TIMERA0, radio_timer_handler); I found that the one-off timer is more a gate-keeper. It would only run out if there was NOT another interrupt raised ( e.g. by the BLE that it received something) between going in Deepsleep and time-out happening. In my test, with just displaying a console message in radio_time_handler(), it was hardly (if ever) called. The interrupt routine is checked in am_hal_ctimer_int_service(). If none is set… nothing is happening … which can be called a freeze. The Time-out interrupt routine should set to trigger the WSF to force an action of WSF handle.

    Thanks for your reply.

    I removed the am_hal_ctimer_int_register for the timer B. Initially, it was calling WsfTaskSetReady(0, 0), which does nothing so I removed it. I can confirm that this timer interrupts very rarely, especially on the cordio tag example since it doesn’t have additional WsfTimers running.

    I found out that the timer configuration wasn’t causing these issues, it was actually the main loop.

    The problem was that am_hal_interrupt_master_enable() wasn’t in the right place.

        // Check to see if the WSF routines are ready to go to sleep.
        uint8_t readyToSleep = wsfOsReadyToSleep();
    
        am_hal_interrupt_master_enable(); // <- HERE
    
        if (readyToSleep) {
            am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP);
        }
    
        // <- SHOULD BE HERE INSTEAD
    

    What was going on is that the ble interrupt was sometimes called between wsfOsReadyToSleep() and am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP), so the device was sometimes going to sleep when it wasn’t supposed to.

    Moving am_hal_interrupt_master_enable() to after the deep sleep command is the way to go. The disable command won’t prevent interrupts from waking up the device, it will just prevent the actual interrupt functions from being called, which I didn’t know.

    makes sense. The original loop would be :

          am_hal_interrupt_master_disable();
    
          //
          // Check to see if the WSF routines are ready to go to sleep.
          //
          if ( wsfOsReadyToSleep() )
          {
              am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP);
          }
    
          am_hal_interrupt_master_enable();
    

    so disable and enable interrrupts again.

    Personally I would still keep the one-off timer, as a gate-keeper, and in the Ctimer routine instead of

    WsfTaskSetReady(0, 0);
    

    do

    WsfTaskSetReady(0, WSF_TIMER_EVENT);