Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Creating application timer with context pointer

I am trying to use an application timer along with some variables. I am using ble_app_template as base. To start of I've got the timer without any parameters being passed, which works just fine.

int counter = 0;
void valueIncrementer()
{
  counter++;
  NRF_LOG_INFO("counter = %d", counter);
}

static void timers_init(void)
{
    // Initialize timer module.
    ret_code_t err_code = app_timer_init();           //Initializes the library
    APP_ERROR_CHECK(err_code);

    /* YOUR_JOB: Create any timers to be used by the application.*/
       err_code = app_timer_create(&valueIncrementer_timer_id, 
                                   APP_TIMER_MODE_REPEATED,
                                   valueIncrementer);
       APP_ERROR_CHECK(err_code);
       /*^MY_JOB^*/
}

But I'm not managing to pass any variables. I have tried passing counter by value and by reference. I have also tried to declare 

app_timer_timeout_handler_t valueIncrementer() {//same as before}
 which does not work either.

Originally I thought the way to go would be function pointers, which works if I use the global counter. (I've only included changes to the original here, duplicate lines have been removed)

void (*valueIncrementerFPtr)();

static void timers_init(void)
{
    // Initialize timer module.

    /* YOUR_JOB: Create any timers to be used by the application.*/
       err_code = app_timer_create(&valueIncrementer_timer_id, 
                                   APP_TIMER_MODE_REPEATED,
                                   *valueIncrementerFPtr);
       APP_ERROR_CHECK(err_code);
       /*^MY_JOB^*/
}

int main(void)
{
    valueIncrementerFPtr = &valueIncrementer;
    ...//no more changes to main
}

As soon as I try to add a variable to valueIncrementer() (and *valueIncrementerFPtr)() ) I get warnings or errors. The best I've managed is use a void pointer and cast it back to an integer.

void valueIncrementer(void *countValue)
{
  (int *)countValue ++;
  NRF_LOG_INFO("counter = %d", (int)countValue);
}
void (*valueIncrementerFPtr)(void *);

static void timers_init(void)
{
    // Initialize timer module.
    
    /* YOUR_JOB: Create any timers to be used by the application.*/
       err_code = app_timer_create(&valueIncrementer_timer_id, 
                                   APP_TIMER_MODE_REPEATED,
                                   *valueIncrementerFPtr);
       APP_ERROR_CHECK(err_code);
       /*^MY_JOB^*/
}

int main(void)
{
    valueIncrementerFPtr = &valueIncrementer;
    ...
    }
 My output here is random as I am not passing anything to valueIncrementer.

I assume that I'm misunderstanding function pointers and/or the use of application timers in general (I have been using this tutorial on function pointers). Can someone explain what I'm doing wrong or point me in the right direction?

A working example of how to create a timer and passing a variable by reference to the time out handler would be much appreciated.

  • Hi, 

    The context pointer should be stored in static memory. Eg., 

    static uint32_t context;  // Can be defined globally 

    // Start application timers.
    err_code = app_timer_start(m_battery_timer_id, BATTERY_LEVEL_MEAS_INTERVAL, &context);
    APP_ERROR_CHECK(err_code);

    I'm not completely sure I understand what you want to achieve, but in your case, would it make sense to just increment the "context" variable from the timeout handler? 

  • Thank you for the fast reply. My implementation of the timer was correct all along, the extraction of the rValue in the timeout handler was erroneous.

    One question remains: Why should the context pointer be in static memory? I have tried automatic memory allocation, which works just fine.

    Below my work progress for future visitors with the same problem.

    ------------------

    What I want to achieve is to do a periodical check and change a variable accordingly, the incrementation is for testing of the timeout handler call only. I want to avoid globals for good coding practice.

    I have changed my context variable to be static. I noticed that I forgot to include my implementation starting the timer where I had been passing the context pointer already.

    Changes made:

    • making the counter a static uint32_t
    • casting to uint32_t *
    • %d to %u
    • implementation of valueIncrementer()

    APP_TIMER_DEF(valueIncrementer_timer_id);       //Had  not included this before
    static uint32_t counter = 0;
    
    void valueIncrementer(void *countValue)
    {
      uint32_t *pCountValue = countValue;
      uint32_t newValue = *pCountValue;
      newValue++;
      counter = (int)newValue;
      NRF_LOG_INFO("counter = %u, %d", newValue, counter);
    }
    void (*valueIncrementerFPtr)(void *);
    
    static void timers_init(void)
    {
        // Initialize timer module.
    
           err_code = app_timer_create(&valueIncrementer_timer_id, 
                                       APP_TIMER_MODE_REPEATED,
                                       //valueIncrementer);
                                       *valueIncrementerFPtr);
           APP_ERROR_CHECK(err_code);
           /*^MY_JOB^*/
    }
    
    static void application_timers_start(void)
    {
           ret_code_t err_code;
           int* pointer = &counter;
           err_code = app_timer_start(valueIncrementer_timer_id, TIMER_VALUE_INCREMENTER, &counter);    //Breakpoint: &context is correct
           APP_ERROR_CHECK(err_code); 
    }
    
    int main(void)
    {
        valueIncrementerFPtr = &valueIncrementer;
        ...
        }

    The context pointer passed to app_timer_start is correct, as well as the address received in the timeout handler (I've checked this with *pointer = &counter). Knowing that the received pointer address is correct, it had to be something about retrieving the rValue.

    At first I did not succeed in easily retrieving the value and incrementing the global counter (implementation of valueIncrementer()). I tried incrementing and casting in the same statement. To solve it, I extracted every step into the simplest possible form.

    I am going to reduce this to proper code tomorrow, and calling it a day for now.

    ------

    Edit:

    So I have not managed to reduce valueIncrementer() any while keeping the functionality. Underneeth I have only removed the redundant code/checks.

    void valueIncrementer(void *countValue)
    {
      uint32_t *pCountValue = countValue;
      uint32_t value = *pCountValue;
      value++;
      counter = value;
      NRF_LOG_INFO("counter = %u", counter);
    }
    
    static void application_timers_start(void)
    {
           ret_code_t err_code;
           err_code = app_timer_start(valueIncrementer_timer_id, TIMER_VALUE_INCREMENTER, &counter);
           APP_ERROR_CHECK(err_code); 
    }

  • Glad to hear that you got it to work. As for the remaining question, it's unsafe to use an auto variable because it will be pushed to the call stack thus only valid in the scope of which the variable was defined (will likely be overwritten at some point). 

     

  • I understand that applying to local variables. But globals are accessible during the complete execution anyway. Should they still be made static so another file cannot declare a global variable with the same name?

  • I think it's good practice to use 'static' for global variables for the reason you mentioned, but it's not required. A local static variable is also accessible during the complete execution. 

Related