Soft Timers

Soft timers are software timers, as opposed to hardware timers (don’t worry about this). They are effectively a way to call a function after a delay without blocking execution. This means you can set a soft timer, and your function will execute after however long you set the timer for, but while you’re waiting, you can do other things in code.

The basic syntax for using soft timers is as follows:

StatusCode soft_timer_start(uint32_t duration_us, SoftTimerCallback callback, void *context, SoftTimerId *timer_id);

Let’s go through the parameters.

  1. uint32_t duration_us is the length of time you want to set the timer for in microseconds. Since microseconds are usually overly precise, we have another version of soft_timer_start called soft_timer_start_millis that takes nearly the same parameters, but the duration is in milliseconds rather than microseconds.

  2. SoftTimerCallback callback is the function you want to be called after the timer length. This may look weird to you since functions don’t usually have a type (SoftTimerCallback) associated with them, but we need to tell the compiler in advance what parameters the function will have, so we need the type. The type is defined as follows:

    typedef void (*SoftTimerCallback)(SoftTimerId timer_id, void *context);

    This means that the function you pass in to call needs to have exactly these parameters: a SoftTimerId and a void *, and it must return void. You can name them whatever you want, though. This may seem a bit restrictive - what if you want to pass in lots of parameters to your callback? this is where the void *context comes in. In short, for this argument, pass in the name of the function you want to call, and make sure it has the right parameters.

  3. void *context is a pointer of type void. You can pass NULL here if your function doesn’t need to take in any arguments. A void pointer is a pointer without a type. This means you can’t dereference it, but you can pass it around as an argument. This is useful because it makes our SoftTimerCallback type generic: we don’t need a specific callback type for every possible type, we just use a void * as a generic argument. The void pointer can then be cast to whatever type you want, including structs.

  4. SoftTimerId *timer_id is the ID of the timer. You can pass NULL for this. You won’t need to use this for the firmware 102 assignment, but in short, it lets you cancel the soft timer by calling soft_timer_cancel(timer_id). Take a look at soft_timer.h for more information.

That was kind of a lot for just one function, so let’s look at some sample code to flesh things out. This program flips a coin every second, and logs how many heads and tails there have been.

Note: I don’t actually know if this compiles since I just typed this in confluence, use this as a reference only

#include "interrupt.h" // interrupts are required for soft timers #include "soft_timer.h" // for soft timers #include "log.h" // for printing #include "wait.h" // for wait function #include <stdlib.h> // for random numbers #include <stdint.h> // for integer types #define COIN_FLIP_PERIOD_MS 1000 // milliseconds between coin flips typedef struct CoinFlipStorage { uint16_t num_heads; uint16_t num_tails; } CoinFlipStorage; void prv_timer_callback(SoftTimerId timer_id, void *context) { CoinFlipStorage *storage = context; // cast void* to our struct so we can use it uint8_t coinflip = rand() % 2; if (coinflip == 1) storage->num_heads++; else if (coinflip == 0) storage->num_tails++; // log output LOG_DEBUG("Num heads: %i, num tails: %i\n", storage->num_heads, storage->num_tails); // start the timer again, so it keeps periodically flipping coins soft_timer_start_millis(COIN_FLIP_PERIOD_MS, prv_timer_callback, storage, NULL); } int main() { srand(14); // seed the random number generator with (MS)XIV, our car number interrupt_init(); // interrupts must be initialized for soft timers to work soft_timer_init(); // soft timers must be initialized before using them CoinFlipStorage storage = { 0 }; // we use this to initialize a struct to be all 0 soft_timer_start_millis(COIN_FLIP_PERIOD_MS, // timer duration prv_timer_callback, // function to call after timer &storage, // automatically gets cast to void* NULL); // timer id - not needed here while (true) { wait(); // waits until an interrupt is triggered rather than endlessly spinning } return 0; }