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.
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 ofsoft_timer_start
calledsoft_timer_start_millis
that takes nearly the same parameters, but the duration is in milliseconds rather than microseconds.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 avoid *
, and it must returnvoid
. 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 thevoid *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.void *context
is a pointer of typevoid
. 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 ourSoftTimerCallback
type generic: we don’t need a specific callback type for every possible type, we just use avoid *
as a generic argument. Thevoid
pointer can then be cast to whatever type you want, including structs.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 callingsoft_timer_cancel(timer_id)
. Take a look atsoft_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;
}