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

nrf_delay_ms not working?

Hello Nordic Pros,

Doing some debugging using RTT Viewer and an nRF51822 under SDK12.3.  Here is the code lines of interest:

...
    NRF_LOG_INFO("IPHONE 'PPM' RECEIVED @ %d mS\r\n", m_life_count_msec);
    nrf_delay_ms(2000);
    NRF_LOG_INFO("ID QUERY '?' SENT AT: %d\r\n", m_life_count_msec);
...

And here are the log messages:

iDrink::INFO:IPHONE 'PPM' RECEIVED @ 683250 mS
iDrink::INFO:ID QUERY '?' SENT AT: 683250

m_life_count_msec is a copy of an app timer count in mSec that is set up to inc ~ every 50 mSec.

Here is the timer setup code:

...
  app_timer_create(&m_app_timer_id, APP_TIMER_MODE_REPEATED, timeout_handler);  //create and start timer...
  app_timer_start(m_app_timer_id, APP_TIMER_TIMEOUT_TICKS, (void*) 0);
...

And here is the timeout code showing the assignment to m_life_count_msec:

...
void timeout_handler(void * context)
{
...
  m_life_count_msec   += APP_TIMER_MS_COUNT;
...

Obviously the questions is, why is there not a 2000 mSec delay between the two log messages?

Does nrf_delay_ms() suspend m_app_timer while it is counting?

Help, please!

Robin @ TL

Parents
  • Hello again, Kenneth, Nguyen,

    So, I have made the variable "volatile"

    ...
    static volatile uint32_t m_life_count_msec = 0;
    ...
    

    I access it in a function called by the timeout handler:

    ...
    void timeout_handler(void * context)
    {
      connect_chk_count += APP_TIMER_MS_COUNT;
      board_life_chk_count += APP_TIMER_MS_COUNT;
      m_timer_msec        += APP_TIMER_MS_COUNT;
      m_counter_msec      += APP_TIMER_MS_COUNT;
      m_life_count_msec   += APP_TIMER_MS_COUNT;
      
      if (board_life_chk_count >= BOARD_LIFE_CHK_PERIOD)
      {
        board_life_chk_count = 0;
        board_life_chk();
      }
      switch (m_app_mode)
      {
        case APP_CONNECT_CHK:
          if (connect_chk_count >= 500)
          {
            connect_chk_count = 0;
            connect_check();
          }
        break;
    ...

    I then print the variable before and after the delay using RTT Viewer:

    ...
    void connect_check(void)
    {
    //    sprintf(message_buffer, "?");
    //    m_send_string(message_buffer);
      static uint8_t check_count = 0;
      
      if (iphone_connect)
      {
        NRF_LOG_INFO("DELAY TEST 1: %d mSec\r\n", m_life_count_msec);
        nrf_delay_ms(2000);
        NRF_LOG_INFO("DELAY TEST 2: %d mSec\r\n", m_life_count_msec);
        NRF_LOG_INFO("IPHONE CONNECT @ %d mSec\r\n", m_life_count_msec);
        NRF_LOG_INFO("CHECK COUNT = %d\r\n", check_count);
    ...

    Yet still, the variable is not updated while the delay is happening, (and it does appear to be happening if I manually time between the two prints:

    iDrink::INFO:DELAY TEST 1: 272600 mSec
    iDrink::INFO:DELAY TEST 2: 272600 mSec

    So it appears that this delay function does suspend any app delay functionality while it runs.

    Do you know of a way to use both functions successfully?

    Thanks

    Robin @ TL

  • does the connect_check get called from a BLE event ? If that is the case, it is likely be at higher priority or from an other event that has higher priority then your timer timeout would never get serviced during the delay.  

  • The code posted shows that connect_check() is called from the timeout_handler.  

  • I didn't see it.  That explained why the counter is the same.  You never exited the handler.

Reply Children
  • Hi Nguyen,

    • Yes, the handler is exited, I only showed the case that makes the call.  Note the break followed by ...

    There are several other cases below for other cases below.

    Robin @ TL

  • As said the connect_check was called within the timeout_handler.  You never exited the handler, so the counter can't be updated by the timer.

  • No the handler is only exited after you've done the 2 second delay, that nrf_delay_ms (as I told you) is just a spin on the CPU so it doesn't get past that line until the 2 seconds is finished and thus stays in the timeout handler. You are literally blocking your own code. While you're there hundreds or thousands of timeout interrupts occur, but you're in the timer ISR and none of them are taken until you finally exit and get one update. And this is what Kenneth asked you about in his first reply when he suggested you are calling the log (and delay) in an interrupt handler and thus blocking the variable update, that is exactly what you're doing. 

  • Hi Everyone,

    So apparently I don't understand the context of this interrupt.  Please suggest how it could be done outside the interrupt context.  It seems like a catch 22 situation.  

    Thanks for your help.

  • Not sure what you're not understanding here. timeout_handler() is called from the timer interrupt. I suggested earlier that you attach a debugger and break there, then you'll see the stack trace which will start with TIMER1_IRQHandler or something similar. Until you finish running your LOG/delay/LOG code, you're still in the ISR. 

    Neither do I understand what you're trying to do. Why are you logging, spinning the CPU for 2 seconds, then logging again? What's the purpose behind that code? If it's to delay the sending of a packet by approximately 2 seconds, you are accomplishing that, however at the cost of breaking your time counter. You're mixing sychronous (nrf_delay_ms) and asynchronous (timeout_handler) code. The obvious way to do this is .. be asynchronous. Set up a state machine, after your first log line, capture the current timer and move to a waiting state in your state machine, then exit the handler. Each time through the timeout_handler work out if 2 seconds have elapsed yet if you are in waiting state, if they have execute your second LOG line and the code which comes after it and move to another state. 

    Or start another timer at the first LOG line which calls a different handler when it times out 2 seconds later. You can have more than one timer, put the second half of the code in the other handler. 

    It's not a hard and fast rule, but usually if you're using nrf_delay_*() for anything other than a micro delay, a few cycles perhaps, often you're doing the wrong thing. 

    So state machine or second timer, get rid of the synchronous delays. 

Related