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

  • 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