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.

Parents
  • 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); 
    }

Reply
  • 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); 
    }

Children
Related