SparkFun Forums 

Where electronics enthusiasts find answers.

Everything with the AmbiqSuite SDK tools and software are welcome here.
User avatar
By BenjaminAW
#215327
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:
Code: Select all
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:
Code: Select all
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
By paulvha
#215338
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.
User avatar
By BenjaminAW
#215398
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.
Code: Select all
    // 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.
By paulvha
#215404
makes sense. The original loop would be :
Code: Select all
      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
Code: Select all
WsfTaskSetReady(0, 0);
do
Code: Select all
WsfTaskSetReady(0, WSF_TIMER_EVENT);
 Topic permissions

You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum