Timer API¶
The Timer API provides timing functions including delays, timeouts, and performance measurement.
Overview¶
The timer module provides:
- Millisecond and microsecond timing
- Blocking and non-blocking delays
- Timeout checking
- One-shot timers with callbacks
- Performance measurement
- CPU cycle counting
Header¶
Initialization¶
embsec_timer_init¶
Initialize the timer subsystem. This is automatically called by embsec_init().
Configures:
- SysTick timer for 1ms resolution
- Performance counters
- Timer interrupt handlers
Millisecond Timing¶
embsec_millis¶
Get current millisecond tick count since system start.
Returns:
- Milliseconds since initialization
- Wraps after approximately 49.7 days
Example:
uint32_t start = embsec_millis();
do_something();
uint32_t elapsed = embsec_millis() - start;
embsec_printf("Operation took %u ms\n", elapsed);
embsec_timeout¶
Check if a timeout has occurred (non-blocking).
Parameters:
start_ms: Start time fromembsec_millis()delay_ms: Desired delay in milliseconds
Returns:
trueif delay has elapsedfalseotherwise
Example:
uint32_t start = embsec_millis();
while (!embsec_timeout(start, 5000)) {
// Do work for up to 5 seconds
if (operation_complete()) {
break;
}
}
embsec_elapsed_ms¶
Calculate elapsed time, handling wraparound correctly.
Parameters:
start_ms: Start time fromembsec_millis()
Returns:
- Elapsed milliseconds
Example:
uint32_t operation_start = embsec_millis();
// ... do work ...
embsec_printf("Total time: %u ms\n", embsec_elapsed_ms(operation_start));
Microsecond Timing¶
embsec_micros¶
Get current microsecond tick count.
Returns:
- Microseconds since initialization
- Wraps after approximately 71.6 minutes
Note: Less accurate than millisecond timing due to implementation constraints.
embsec_delay_us¶
Delay for specified microseconds (blocking).
Parameters:
us: Microseconds to delay
Example:
// Generate 1kHz square wave
while (1) {
embsec_gpio_write('A', 0x01, true);
embsec_delay_us(500);
embsec_gpio_write('A', 0x01, false);
embsec_delay_us(500);
}
One-Shot Timer¶
embsec_timer_start¶
Start a one-shot timer that calls a function after the specified delay.
Parameters:
ms: Milliseconds until callbackcallback: Function to call when timer expires
Example:
void timer_expired(void) {
embsec_printf("Timer fired!\n");
embsec_led_toggle(EMBSEC_LED_RED);
}
int main(void) {
embsec_init();
// Start 2-second timer
embsec_timer_start(2000, timer_expired);
while (1) {
// Do other work
}
}
embsec_timer_stop¶
Stop the one-shot timer if it's running.
embsec_timer_active¶
Check if the one-shot timer is currently active.
Returns:
trueif timer is runningfalseotherwise
Performance Measurement¶
embsec_perf_start¶
Start a high-resolution performance counter.
Returns:
- Counter start value (opaque)
embsec_perf_end¶
End performance measurement.
Parameters:
start: Value fromembsec_perf_start()
Returns:
- Elapsed CPU cycles
embsec_cycles_to_us¶
Convert CPU cycles to microseconds.
Parameters:
cycles: CPU cycle count
Returns:
- Microseconds (assuming 80MHz clock)
Example:
// Measure function performance
uint32_t start = embsec_perf_start();
// Function to measure
complex_algorithm();
uint32_t cycles = embsec_perf_end(start);
uint32_t us = embsec_cycles_to_us(cycles);
embsec_printf("Function took %u cycles (%u us)\n", cycles, us);
Common Patterns¶
Periodic Tasks¶
void periodic_1hz_task(void) {
static uint32_t last_run = 0;
if (embsec_elapsed_ms(last_run) >= 1000) {
last_run = embsec_millis();
// Do periodic work
embsec_led_toggle(EMBSEC_LED_GREEN);
embsec_printf("Tick\n");
}
}
int main(void) {
embsec_init();
while (1) {
periodic_1hz_task();
// Other tasks...
}
}
Multiple Timers¶
typedef struct {
uint32_t start_ms;
uint32_t period_ms;
void (*callback)(void);
bool active;
} soft_timer_t;
#define MAX_TIMERS 5
soft_timer_t timers[MAX_TIMERS] = {0};
void timer_tick(void) {
for (int i = 0; i < MAX_TIMERS; i++) {
if (timers[i].active) {
if (embsec_timeout(timers[i].start_ms, timers[i].period_ms)) {
timers[i].start_ms = embsec_millis();
if (timers[i].callback) {
timers[i].callback();
}
}
}
}
}
int add_timer(uint32_t period_ms, void (*callback)(void)) {
for (int i = 0; i < MAX_TIMERS; i++) {
if (!timers[i].active) {
timers[i].start_ms = embsec_millis();
timers[i].period_ms = period_ms;
timers[i].callback = callback;
timers[i].active = true;
return i;
}
}
return -1; // No free timers
}
Timeout with Retry¶
bool send_with_timeout(const uint8_t *data, size_t len, uint32_t timeout_ms) {
uint32_t start = embsec_millis();
while (!embsec_timeout(start, timeout_ms)) {
if (try_send(data, len)) {
return true; // Success
}
embsec_delay_ms(10); // Small delay between retries
}
return false; // Timeout
}
Watchdog Timer¶
#define WATCHDOG_TIMEOUT_MS 5000
void watchdog_task(void) {
static uint32_t last_feed = 0;
// Feed watchdog
last_feed = embsec_millis();
}
void watchdog_check(void) {
static uint32_t last_check = 0;
if (embsec_elapsed_ms(last_check) >= 1000) {
last_check = embsec_millis();
if (embsec_elapsed_ms(last_feed) > WATCHDOG_TIMEOUT_MS) {
embsec_printf("Watchdog timeout! Resetting...\n");
embsec_uart_flush();
embsec_system_reset();
}
}
}
Rate Limiting¶
bool rate_limit_check(uint32_t *last_time, uint32_t min_interval_ms) {
uint32_t now = embsec_millis();
if (embsec_elapsed_ms(*last_time) >= min_interval_ms) {
*last_time = now;
return true; // Allowed
}
return false; // Rate limited
}
// Usage
void handle_button(void) {
static uint32_t last_press = 0;
if (embsec_switch_read(EMBSEC_SW1)) {
if (rate_limit_check(&last_press, 500)) {
embsec_printf("Button pressed\n");
// Handle press
}
}
}
Profiling Code Sections¶
typedef struct {
const char *name;
uint32_t total_cycles;
uint32_t count;
} profile_t;
#define PROFILE_START() uint32_t _prof_start = embsec_perf_start()
#define PROFILE_END(prof) do { \
(prof)->total_cycles += embsec_perf_end(_prof_start); \
(prof)->count++; \
} while(0)
void print_profile(const profile_t *prof) {
if (prof->count > 0) {
uint32_t avg_cycles = prof->total_cycles / prof->count;
uint32_t avg_us = embsec_cycles_to_us(avg_cycles);
embsec_printf("%s: %u calls, avg %u cycles (%u us)\n",
prof->name, prof->count, avg_cycles, avg_us);
}
}
// Usage
profile_t crypto_profile = { .name = "Crypto" };
void do_crypto(void) {
PROFILE_START();
// Crypto operations
embsec_sha256(data, len, hash);
PROFILE_END(&crypto_profile);
}
Timing Accuracy¶
Resolution¶
- Millisecond timing: 1ms resolution, interrupt-driven
- Microsecond timing: ~12.5ns resolution (80MHz clock)
- Performance counter: Single CPU cycle resolution
Considerations¶
- Interrupt latency: Timer callbacks have interrupt latency
- Blocking delays: Use sparingly, prevents other code execution
- Wraparound: Handle properly for long-running applications
- Clock drift: Crystal accuracy affects long-term timing
Best Practices¶
1. Non-blocking Design¶
Prefer timeout checking over blocking delays:
// BAD - blocks everything
embsec_delay_ms(1000);
// GOOD - allows other work
uint32_t wait_start = embsec_millis();
while (!embsec_timeout(wait_start, 1000)) {
// Can do other work here
process_events();
}
2. Overflow Handling¶
Always use provided functions for elapsed time:
// BAD - doesn't handle overflow
if (embsec_millis() > start_time + 1000) { }
// GOOD - handles overflow correctly
if (embsec_timeout(start_time, 1000)) { }
3. Timer Callbacks¶
Keep timer callbacks short:
volatile bool timer_flag = false;
void timer_callback(void) {
// BAD - lengthy processing in interrupt
// process_complex_data();
// GOOD - just set flag
timer_flag = true;
}
// In main loop
if (timer_flag) {
timer_flag = false;
process_complex_data();
}
4. Precise Timing¶
For precise timing, account for code execution:
// Generate accurate 1kHz square wave
uint32_t next_toggle = embsec_micros();
while (1) {
if (embsec_micros() >= next_toggle) {
embsec_gpio_toggle('A', 0x01);
next_toggle += 500; // Exactly 500us later
}
}
Example: Complete Timer Demo¶
#include <embsec/embsec.h>
#include <embsec/uart.h>
#include <embsec/gpio.h>
#include <embsec/timer.h>
// Global state
volatile uint32_t seconds = 0;
volatile bool alarm_active = false;
// Timer callback
void second_timer(void) {
seconds++;
embsec_led_toggle(EMBSEC_LED_BLUE);
// Restart timer
embsec_timer_start(1000, second_timer);
}
// Performance test function
uint32_t fibonacci(uint32_t n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
int main(void) {
embsec_init();
embsec_printf("\n=== Timer Demo ===\n");
embsec_printf("SW1: Set 10-second alarm\n");
embsec_printf("SW2: Run performance test\n\n");
// Start second timer
embsec_timer_start(1000, second_timer);
// Main loop variables
uint32_t last_display = 0;
uint32_t alarm_start = 0;
while (1) {
// Update display every second
if (embsec_elapsed_ms(last_display) >= 1000) {
last_display = embsec_millis();
uint32_t uptime_sec = seconds;
embsec_printf("\rUptime: %02u:%02u:%02u",
uptime_sec / 3600,
(uptime_sec / 60) % 60,
uptime_sec % 60);
if (alarm_active) {
uint32_t remaining = 10 - (embsec_elapsed_ms(alarm_start) / 1000);
embsec_printf(" | Alarm in %u sec", remaining);
}
}
// Check alarm
if (alarm_active && embsec_timeout(alarm_start, 10000)) {
alarm_active = false;
embsec_printf("\n\n*** ALARM! ***\n");
// Flash red LED rapidly
for (int i = 0; i < 10; i++) {
embsec_led_toggle(EMBSEC_LED_RED);
embsec_delay_ms(100);
}
embsec_led_set(EMBSEC_LED_RED, false);
}
// Handle SW1 - set alarm
if (embsec_switch_read(EMBSEC_SW1)) {
embsec_switch_wait(EMBSEC_SW1, 50);
if (!alarm_active) {
alarm_active = true;
alarm_start = embsec_millis();
embsec_printf("\nAlarm set for 10 seconds\n");
}
}
// Handle SW2 - performance test
if (embsec_switch_read(EMBSEC_SW2)) {
embsec_switch_wait(EMBSEC_SW2, 50);
embsec_printf("\n\nRunning performance test...\n");
// Test different Fibonacci numbers
for (uint32_t n = 10; n <= 25; n += 5) {
uint32_t start = embsec_perf_start();
uint32_t result = fibonacci(n);
uint32_t cycles = embsec_perf_end(start);
uint32_t us = embsec_cycles_to_us(cycles);
embsec_printf("fib(%u) = %u: %u cycles (%u.%03u ms)\n",
n, result, cycles, us / 1000, us % 1000);
}
embsec_printf("Test complete\n\n");
}
// Small delay to reduce CPU usage
embsec_delay_ms(10);
}
}
This example demonstrates:
- One-shot timer with automatic restart
- Non-blocking timeout checking
- Performance measurement
- Elapsed time calculation
- Multiple concurrent time-based operations
- Proper overflow handling