Skip to content

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
#include <embsec/timer.h>

Initialization

embsec_timer_init

void embsec_timer_init(void);

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

uint32_t embsec_millis(void);

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

bool embsec_timeout(uint32_t start_ms, uint32_t delay_ms);

Check if a timeout has occurred (non-blocking).

Parameters:

  • start_ms: Start time from embsec_millis()
  • delay_ms: Desired delay in milliseconds

Returns:

  • true if delay has elapsed
  • false otherwise

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

uint32_t embsec_elapsed_ms(uint32_t start_ms);

Calculate elapsed time, handling wraparound correctly.

Parameters:

  • start_ms: Start time from embsec_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

uint32_t embsec_micros(void);

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

void embsec_delay_us(uint32_t 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

void embsec_timer_start(uint32_t ms, embsec_timer_callback_t callback);

Start a one-shot timer that calls a function after the specified delay.

Parameters:

  • ms: Milliseconds until callback
  • callback: 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

void embsec_timer_stop(void);

Stop the one-shot timer if it's running.

embsec_timer_active

bool embsec_timer_active(void);

Check if the one-shot timer is currently active.

Returns:

  • true if timer is running
  • false otherwise

Performance Measurement

embsec_perf_start

uint32_t embsec_perf_start(void);

Start a high-resolution performance counter.

Returns:

  • Counter start value (opaque)

embsec_perf_end

uint32_t embsec_perf_end(uint32_t start);

End performance measurement.

Parameters:

  • start: Value from embsec_perf_start()

Returns:

  • Elapsed CPU cycles

embsec_cycles_to_us

uint32_t embsec_cycles_to_us(uint32_t cycles);

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

  1. Interrupt latency: Timer callbacks have interrupt latency
  2. Blocking delays: Use sparingly, prevents other code execution
  3. Wraparound: Handle properly for long-running applications
  4. 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