BLOG8
跳转至

软件定时器实现:构建灵活的时间管理系统

概述

软件定时器(Software Timer)是嵌入式系统中非常重要的组件,它允许我们在指定的时间后执行特定的操作,而不需要占用硬件定时器资源。通过本教程,你将学会:

  • 理解软件定时器的工作原理和应用场景
  • 掌握基于链表的定时器管理方法
  • 实现单次和周期性定时器
  • 处理定时器超时和回调函数
  • 优化定时器精度和性能

背景知识

什么是软件定时器?

**软件定时器**是通过软件方式实现的定时功能,它依赖于一个硬件定时器(通常是SysTick)提供的时基,但可以创建多个独立的定时器实例。

类比理解: - 硬件定时器:像一个闹钟,只能设置一个时间 - 软件定时器:像手机上的多个提醒事项,可以设置很多个不同的时间

核心概念: - 时基(Time Base):提供基准时钟,通常为1ms - 定时器实例:每个定时器有自己的超时时间和回调函数 - 定时器链表:管理多个定时器实例 - 超时处理:时间到达时执行回调函数

为什么需要软件定时器?

在嵌入式系统中,我们经常需要在不同的时间点执行不同的任务:

问题场景:

// 需要多个定时功能
void main_loop(void) {
    // LED每500ms闪烁
    // 按键每20ms检测
    // 传感器每1000ms读取
    // 显示每100ms更新
    // 通信每5000ms发送心跳
}

硬件定时器的限制: - 数量有限(通常只有2-4个) - 配置复杂 - 不够灵活

软件定时器的优势: - 可以创建任意数量的定时器 - 使用简单,接口统一 - 灵活的超时处理 - 支持单次和周期定时

软件定时器的工作原理

软件定时器的基本工作流程:

[硬件定时器] → 1ms中断 → [更新所有定时器] → [检查超时] → [执行回调]
     ↓                          ↓                    ↓              ↓
  SysTick              减少剩余时间          时间到达?      调用回调函数

关键步骤: 1. 初始化:创建定时器,设置超时时间和回调函数 2. 启动定时器:将定时器加入活动链表 3. 时基更新:每1ms更新所有活动定时器 4. 超时检测:检查哪些定时器已经超时 5. 回调执行:执行超时定时器的回调函数 6. 周期处理:如果是周期定时器,重新启动

核心内容

定时器数据结构设计

一个完整的软件定时器需要包含以下信息:

#include <stdint.h>
#include <stdbool.h>

// 定时器类型
typedef enum {
    TIMER_TYPE_ONE_SHOT,    // 单次定时器
    TIMER_TYPE_PERIODIC     // 周期定时器
} TimerType_t;

// 定时器状态
typedef enum {
    TIMER_STATE_STOPPED,    // 停止
    TIMER_STATE_RUNNING,    // 运行中
    TIMER_STATE_EXPIRED     // 已超时
} TimerState_t;

// 定时器回调函数类型
typedef void (*TimerCallback_t)(void *arg);

// 软件定时器结构体
typedef struct SoftwareTimer {
    uint32_t timeout;           // 超时时间(ms)
    uint32_t remaining;         // 剩余时间(ms)
    TimerType_t type;           // 定时器类型
    TimerState_t state;         // 定时器状态
    TimerCallback_t callback;   // 回调函数
    void *callback_arg;         // 回调函数参数
    struct SoftwareTimer *next; // 链表指针
    char name[16];              // 定时器名称(调试用)
} SoftwareTimer_t;

字段说明: - timeout:定时器的超时时间,单位为毫秒 - remaining:剩余时间,每次时基更新时递减 - type:单次定时器超时后停止,周期定时器自动重启 - state:定时器当前状态 - callback:超时时调用的函数 - callback_arg:传递给回调函数的参数 - next:链表指针,用于管理多个定时器 - name:定时器名称,方便调试

基础实现:简单的定时器管理

让我们从最简单的实现开始:

方法1:数组管理(固定数量)

适合定时器数量固定的场景:

#include <stdint.h>
#include <stdbool.h>
#include <string.h>

// 配置
#define MAX_TIMERS 10

// 定时器池
SoftwareTimer_t timer_pool[MAX_TIMERS];
uint8_t timer_count = 0;

// 系统时钟(1ms)
volatile uint32_t system_ticks = 0;

// SysTick中断(1ms)
void SysTick_Handler(void) {
    system_ticks++;
    Timer_Update();  // 更新所有定时器
}

// 创建定时器
SoftwareTimer_t* Timer_Create(uint32_t timeout_ms,
                               TimerType_t type,
                               TimerCallback_t callback,
                               void *arg,
                               const char *name) {
    if(timer_count >= MAX_TIMERS) {
        return NULL;  // 定时器池已满
    }

    SoftwareTimer_t *timer = &timer_pool[timer_count++];

    timer->timeout = timeout_ms;
    timer->remaining = timeout_ms;
    timer->type = type;
    timer->state = TIMER_STATE_STOPPED;
    timer->callback = callback;
    timer->callback_arg = arg;
    timer->next = NULL;
    strncpy(timer->name, name, sizeof(timer->name) - 1);

    return timer;
}

// 启动定时器
void Timer_Start(SoftwareTimer_t *timer) {
    if(timer == NULL) return;

    timer->remaining = timer->timeout;
    timer->state = TIMER_STATE_RUNNING;
}

// 停止定时器
void Timer_Stop(SoftwareTimer_t *timer) {
    if(timer == NULL) return;

    timer->state = TIMER_STATE_STOPPED;
}

// 更新所有定时器(在SysTick中断中调用)
void Timer_Update(void) {
    for(uint8_t i = 0; i < timer_count; i++) {
        SoftwareTimer_t *timer = &timer_pool[i];

        // 只更新运行中的定时器
        if(timer->state != TIMER_STATE_RUNNING) {
            continue;
        }

        // 递减剩余时间
        if(timer->remaining > 0) {
            timer->remaining--;
        }

        // 检查是否超时
        if(timer->remaining == 0) {
            timer->state = TIMER_STATE_EXPIRED;

            // 执行回调函数
            if(timer->callback != NULL) {
                timer->callback(timer->callback_arg);
            }

            // 处理周期定时器
            if(timer->type == TIMER_TYPE_PERIODIC) {
                timer->remaining = timer->timeout;
                timer->state = TIMER_STATE_RUNNING;
            } else {
                timer->state = TIMER_STATE_STOPPED;
            }
        }
    }
}

// 示例:LED闪烁定时器
void LED_Toggle_Callback(void *arg) {
    LED_Toggle();
}

// 示例:传感器读取定时器
void Sensor_Read_Callback(void *arg) {
    uint16_t value = ReadSensor();
    printf("Sensor value: %u\n", value);
}

// 使用示例
int main(void) {
    SystemInit();
    SysTick_Init();  // 配置1ms中断

    // 创建LED闪烁定时器(周期500ms)
    SoftwareTimer_t *led_timer = Timer_Create(
        500,                      // 500ms
        TIMER_TYPE_PERIODIC,      // 周期定时器
        LED_Toggle_Callback,      // 回调函数
        NULL,                     // 无参数
        "LED"                     // 名称
    );

    // 创建传感器读取定时器(周期1000ms)
    SoftwareTimer_t *sensor_timer = Timer_Create(
        1000,
        TIMER_TYPE_PERIODIC,
        Sensor_Read_Callback,
        NULL,
        "Sensor"
    );

    // 启动定时器
    Timer_Start(led_timer);
    Timer_Start(sensor_timer);

    // 主循环
    while(1) {
        // 其他任务
    }

    return 0;
}

代码说明: - 定时器池:使用数组存储所有定时器 - 时基更新:SysTick中断每1ms调用Timer_Update() - 超时检测:遍历所有定时器,检查剩余时间 - 回调执行:超时时调用回调函数 - 周期处理:周期定时器自动重启

优点: - 实现简单 - 内存占用固定 - 适合定时器数量少的场景

缺点: - 定时器数量固定 - 每次更新需要遍历所有定时器(包括停止的) - 效率较低

方法2:链表管理(动态数量)

使用链表可以更灵活地管理定时器:

#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

// 活动定时器链表头
SoftwareTimer_t *active_timer_list = NULL;

// 系统时钟
volatile uint32_t system_ticks = 0;

// SysTick中断
void SysTick_Handler(void) {
    system_ticks++;
    Timer_UpdateList();
}

// 创建定时器(动态分配)
SoftwareTimer_t* Timer_Create(uint32_t timeout_ms,
                               TimerType_t type,
                               TimerCallback_t callback,
                               void *arg,
                               const char *name) {
    // 分配内存
    SoftwareTimer_t *timer = (SoftwareTimer_t*)malloc(sizeof(SoftwareTimer_t));
    if(timer == NULL) {
        return NULL;
    }

    // 初始化
    timer->timeout = timeout_ms;
    timer->remaining = timeout_ms;
    timer->type = type;
    timer->state = TIMER_STATE_STOPPED;
    timer->callback = callback;
    timer->callback_arg = arg;
    timer->next = NULL;
    strncpy(timer->name, name, sizeof(timer->name) - 1);

    return timer;
}

// 启动定时器(加入活动链表)
void Timer_Start(SoftwareTimer_t *timer) {
    if(timer == NULL) return;

    // 如果已经在链表中,先移除
    Timer_Stop(timer);

    // 重置剩余时间
    timer->remaining = timer->timeout;
    timer->state = TIMER_STATE_RUNNING;

    // 加入链表头部
    timer->next = active_timer_list;
    active_timer_list = timer;
}

// 停止定时器(从活动链表移除)
void Timer_Stop(SoftwareTimer_t *timer) {
    if(timer == NULL) return;

    timer->state = TIMER_STATE_STOPPED;

    // 从链表中移除
    SoftwareTimer_t **current = &active_timer_list;
    while(*current != NULL) {
        if(*current == timer) {
            *current = timer->next;
            timer->next = NULL;
            break;
        }
        current = &((*current)->next);
    }
}

// 删除定时器(释放内存)
void Timer_Delete(SoftwareTimer_t *timer) {
    if(timer == NULL) return;

    // 先停止
    Timer_Stop(timer);

    // 释放内存
    free(timer);
}

// 更新活动定时器链表
void Timer_UpdateList(void) {
    SoftwareTimer_t *current = active_timer_list;
    SoftwareTimer_t *prev = NULL;

    while(current != NULL) {
        SoftwareTimer_t *next = current->next;

        // 递减剩余时间
        if(current->remaining > 0) {
            current->remaining--;
        }

        // 检查超时
        if(current->remaining == 0) {
            current->state = TIMER_STATE_EXPIRED;

            // 执行回调
            if(current->callback != NULL) {
                current->callback(current->callback_arg);
            }

            // 处理周期定时器
            if(current->type == TIMER_TYPE_PERIODIC) {
                current->remaining = current->timeout;
                current->state = TIMER_STATE_RUNNING;
                prev = current;
            } else {
                // 单次定时器,从链表移除
                if(prev == NULL) {
                    active_timer_list = next;
                } else {
                    prev->next = next;
                }
                current->state = TIMER_STATE_STOPPED;
                current->next = NULL;
            }
        } else {
            prev = current;
        }

        current = next;
    }
}

// 获取活动定时器数量
uint32_t Timer_GetActiveCount(void) {
    uint32_t count = 0;
    SoftwareTimer_t *current = active_timer_list;

    while(current != NULL) {
        count++;
        current = current->next;
    }

    return count;
}

// 打印所有活动定时器
void Timer_PrintActive(void) {
    printf("Active Timers:\n");
    printf("%-16s %10s %10s\n", "Name", "Timeout", "Remaining");

    SoftwareTimer_t *current = active_timer_list;
    while(current != NULL) {
        printf("%-16s %10lu %10lu\n",
               current->name,
               current->timeout,
               current->remaining);
        current = current->next;
    }
}

改进说明: - 动态分配:使用malloc/free管理内存 - 链表管理:只遍历活动的定时器 - 自动移除:单次定时器超时后自动从链表移除 - 效率提升:不需要遍历停止的定时器

优点: - 定时器数量灵活 - 只更新活动定时器,效率高 - 内存按需分配

缺点: - 需要动态内存管理 - 代码稍复杂 - 可能有内存碎片

链表管理优化

为了提高效率,我们可以使用有序链表:

// 按剩余时间排序的链表
// 最快超时的定时器在链表头部

void Timer_Start_Sorted(SoftwareTimer_t *timer) {
    if(timer == NULL) return;

    timer->remaining = timer->timeout;
    timer->state = TIMER_STATE_RUNNING;

    // 插入到有序链表中
    SoftwareTimer_t **current = &active_timer_list;

    while(*current != NULL && (*current)->remaining < timer->remaining) {
        current = &((*current)->next);
    }

    timer->next = *current;
    *current = timer;
}

// 优化的更新函数
void Timer_UpdateList_Optimized(void) {
    SoftwareTimer_t *current = active_timer_list;
    SoftwareTimer_t *prev = NULL;

    while(current != NULL) {
        // 递减剩余时间
        if(current->remaining > 0) {
            current->remaining--;
        }

        // 如果第一个定时器还没超时,后面的都不会超时
        if(current->remaining > 0) {
            break;
        }

        // 处理超时
        SoftwareTimer_t *next = current->next;

        current->state = TIMER_STATE_EXPIRED;

        if(current->callback != NULL) {
            current->callback(current->callback_arg);
        }

        if(current->type == TIMER_TYPE_PERIODIC) {
            // 周期定时器,重新插入
            if(prev == NULL) {
                active_timer_list = next;
            } else {
                prev->next = next;
            }
            Timer_Start_Sorted(current);
        } else {
            // 单次定时器,移除
            if(prev == NULL) {
                active_timer_list = next;
            } else {
                prev->next = next;
            }
            current->state = TIMER_STATE_STOPPED;
            current->next = NULL;
        }

        current = next;
    }
}

优化效果: - 链表按剩余时间排序 - 只需检查链表头部 - 如果头部未超时,后面的都不会超时 - 大幅减少检查次数

超时处理机制

超时处理是定时器的核心功能,需要考虑以下几个方面:

1. 回调函数设计

// 简单回调(无参数)
void SimpleCallback(void *arg) {
    LED_Toggle();
}

// 带参数回调
typedef struct {
    uint8_t led_id;
    bool state;
} LEDControl_t;

void LEDCallback(void *arg) {
    LEDControl_t *ctrl = (LEDControl_t*)arg;
    LED_Set(ctrl->led_id, ctrl->state);
}

// 使用示例
LEDControl_t led_ctrl = {.led_id = 1, .state = true};
SoftwareTimer_t *timer = Timer_Create(1000, TIMER_TYPE_ONE_SHOT,
                                      LEDCallback, &led_ctrl, "LED1");

2. 中断安全性

回调函数在中断上下文中执行,需要注意:

// 不安全的回调(可能阻塞)
void BadCallback(void *arg) {
    printf("Timer expired\n");  // printf可能很慢
    Delay_ms(100);              // 绝对不能在中断中延时!
}

// 安全的回调(快速返回)
volatile bool timer_flag = false;

void GoodCallback(void *arg) {
    timer_flag = true;  // 只设置标志
}

// 在主循环中处理
void main_loop(void) {
    while(1) {
        if(timer_flag) {
            timer_flag = false;
            // 在这里执行耗时操作
            ProcessTimerEvent();
        }
    }
}

3. 延迟执行队列

对于复杂的超时处理,可以使用延迟执行队列:

// 延迟执行队列
#define DEFERRED_QUEUE_SIZE 16

typedef struct {
    TimerCallback_t callback;
    void *arg;
} DeferredTask_t;

DeferredTask_t deferred_queue[DEFERRED_QUEUE_SIZE];
volatile uint8_t queue_write = 0;
volatile uint8_t queue_read = 0;

// 在中断中添加到队列
void Timer_DeferredCallback(void *arg) {
    uint8_t next_write = (queue_write + 1) % DEFERRED_QUEUE_SIZE;

    if(next_write != queue_read) {
        deferred_queue[queue_write].callback = (TimerCallback_t)arg;
        deferred_queue[queue_write].arg = NULL;
        queue_write = next_write;
    }
}

// 在主循环中处理队列
void Timer_ProcessDeferred(void) {
    while(queue_read != queue_write) {
        DeferredTask_t *task = &deferred_queue[queue_read];

        if(task->callback != NULL) {
            task->callback(task->arg);
        }

        queue_read = (queue_read + 1) % DEFERRED_QUEUE_SIZE;
    }
}

周期任务实现

周期定时器是最常用的功能之一:

// 示例1:周期性LED闪烁
void LED_Blink_Callback(void *arg) {
    static bool state = false;
    state = !state;
    LED_Set(state);
}

SoftwareTimer_t *blink_timer = Timer_Create(
    500,                    // 500ms周期
    TIMER_TYPE_PERIODIC,    // 周期定时器
    LED_Blink_Callback,
    NULL,
    "Blink"
);
Timer_Start(blink_timer);

// 示例2:周期性数据采集
typedef struct {
    uint16_t *buffer;
    uint8_t index;
    uint8_t size;
} SampleBuffer_t;

void Sample_Callback(void *arg) {
    SampleBuffer_t *buf = (SampleBuffer_t*)arg;

    // 读取ADC
    buf->buffer[buf->index] = ADC_Read();
    buf->index = (buf->index + 1) % buf->size;
}

uint16_t sample_buffer[100];
SampleBuffer_t sample_buf = {
    .buffer = sample_buffer,
    .index = 0,
    .size = 100
};

SoftwareTimer_t *sample_timer = Timer_Create(
    10,                     // 10ms采样周期
    TIMER_TYPE_PERIODIC,
    Sample_Callback,
    &sample_buf,
    "Sample"
);
Timer_Start(sample_timer);

// 示例3:看门狗喂狗
void Watchdog_Feed_Callback(void *arg) {
    Watchdog_Refresh();
}

SoftwareTimer_t *wdt_timer = Timer_Create(
    500,                    // 每500ms喂狗
    TIMER_TYPE_PERIODIC,
    Watchdog_Feed_Callback,
    NULL,
    "Watchdog"
);
Timer_Start(wdt_timer);

精度优化

软件定时器的精度受多种因素影响:

1. 时基精度

// 使用高精度时基
// 方法1:使用更快的中断(0.1ms)
void SysTick_Init_HighPrecision(void) {
    // 配置100us中断
    SysTick_Config(SystemCoreClock / 10000);
}

// 定时器使用0.1ms单位
SoftwareTimer_t *timer = Timer_Create(
    5000,  // 500ms = 5000 * 0.1ms
    TIMER_TYPE_PERIODIC,
    Callback,
    NULL,
    "Timer"
);

// 方法2:使用硬件定时器的计数值
uint32_t GetHighResolutionTicks(void) {
    // 读取硬件定时器计数值
    uint32_t ticks = system_ticks;
    uint32_t counter = SysTick->VAL;
    uint32_t reload = SysTick->LOAD;

    // 计算精确时间(微秒)
    return ticks * 1000 + (reload - counter) * 1000 / reload;
}

2. 中断延迟补偿

// 记录上次更新时间
volatile uint32_t last_update_time = 0;

void Timer_UpdateList_Compensated(void) {
    uint32_t current_time = system_ticks;
    uint32_t elapsed = current_time - last_update_time;
    last_update_time = current_time;

    // 如果中断延迟,elapsed可能大于1
    SoftwareTimer_t *current = active_timer_list;

    while(current != NULL) {
        if(current->remaining > elapsed) {
            current->remaining -= elapsed;
        } else {
            current->remaining = 0;
        }

        // 处理超时...
        current = current->next;
    }
}

3. 累积误差处理

// 使用绝对时间而不是相对时间
typedef struct {
    uint32_t start_time;    // 启动时的系统时间
    uint32_t timeout;       // 超时时间
    // ... 其他字段
} SoftwareTimer_Absolute_t;

void Timer_Start_Absolute(SoftwareTimer_Absolute_t *timer) {
    timer->start_time = system_ticks;
    timer->state = TIMER_STATE_RUNNING;
}

void Timer_Update_Absolute(void) {
    SoftwareTimer_Absolute_t *current = active_timer_list;

    while(current != NULL) {
        uint32_t elapsed = system_ticks - current->start_time;

        if(elapsed >= current->timeout) {
            // 超时处理
            if(current->callback != NULL) {
                current->callback(current->callback_arg);
            }

            if(current->type == TIMER_TYPE_PERIODIC) {
                // 使用绝对时间,避免累积误差
                current->start_time += current->timeout;
            }
        }

        current = current->next;
    }
}

实践示例

示例1:多功能定时器系统

实现一个包含多种定时功能的完整系统:

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>

// 全局变量
volatile uint32_t system_ticks = 0;
SoftwareTimer_t *active_timer_list = NULL;

// SysTick中断
void SysTick_Handler(void) {
    system_ticks++;
    Timer_UpdateList();
}

// 任务1:LED闪烁(500ms周期)
void LED_Blink_Callback(void *arg) {
    static bool state = false;
    state = !state;
    LED_Set(state);
    printf("[%lu] LED: %s\n", system_ticks, state ? "ON" : "OFF");
}

// 任务2:按键消抖(20ms单次)
typedef struct {
    uint8_t button_id;
    bool pressed;
} ButtonEvent_t;

ButtonEvent_t button_event;
SoftwareTimer_t *debounce_timer = NULL;

void Button_Debounce_Callback(void *arg) {
    ButtonEvent_t *event = (ButtonEvent_t*)arg;

    // 再次检查按键状态
    if(Button_Read(event->button_id) == event->pressed) {
        // 状态确认,处理按键事件
        if(event->pressed) {
            printf("[%lu] Button %d pressed\n", system_ticks, event->button_id);
            OnButtonPressed(event->button_id);
        } else {
            printf("[%lu] Button %d released\n", system_ticks, event->button_id);
        }
    }
}

void Button_IRQ_Handler(uint8_t button_id, bool pressed) {
    // 停止之前的消抖定时器
    if(debounce_timer != NULL) {
        Timer_Stop(debounce_timer);
    }

    // 记录按键事件
    button_event.button_id = button_id;
    button_event.pressed = pressed;

    // 启动消抖定时器
    if(debounce_timer == NULL) {
        debounce_timer = Timer_Create(20, TIMER_TYPE_ONE_SHOT,
                                      Button_Debounce_Callback,
                                      &button_event, "Debounce");
    }
    Timer_Start(debounce_timer);
}

// 任务3:传感器读取(1000ms周期)
void Sensor_Read_Callback(void *arg) {
    uint16_t temperature = ReadTemperature();
    uint16_t humidity = ReadHumidity();

    printf("[%lu] Temp: %u.%u°C, Humidity: %u%%\n",
           system_ticks,
           temperature / 10, temperature % 10,
           humidity);
}

// 任务4:数据上传(5000ms周期)
void Data_Upload_Callback(void *arg) {
    printf("[%lu] Uploading data...\n", system_ticks);

    // 模拟数据上传
    if(Network_IsConnected()) {
        UploadSensorData();
        printf("[%lu] Upload complete\n", system_ticks);
    } else {
        printf("[%lu] Network not available\n", system_ticks);
    }
}

// 任务5:超时保护(10000ms单次)
void Timeout_Callback(void *arg) {
    printf("[%lu] Operation timeout!\n", system_ticks);
    CancelOperation();
}

// 主函数
int main(void) {
    // 硬件初始化
    SystemInit();
    LED_Init();
    Button_Init();
    Sensor_Init();
    Network_Init();
    SysTick_Init();

    printf("Software Timer System Started\n");

    // 创建周期定时器
    SoftwareTimer_t *led_timer = Timer_Create(
        500, TIMER_TYPE_PERIODIC,
        LED_Blink_Callback, NULL, "LED"
    );

    SoftwareTimer_t *sensor_timer = Timer_Create(
        1000, TIMER_TYPE_PERIODIC,
        Sensor_Read_Callback, NULL, "Sensor"
    );

    SoftwareTimer_t *upload_timer = Timer_Create(
        5000, TIMER_TYPE_PERIODIC,
        Data_Upload_Callback, NULL, "Upload"
    );

    // 启动定时器
    Timer_Start(led_timer);
    Timer_Start(sensor_timer);
    Timer_Start(upload_timer);

    // 主循环
    while(1) {
        // 处理延迟任务
        Timer_ProcessDeferred();

        // 其他任务
        __WFI();  // 等待中断
    }

    return 0;
}

示例2:状态机超时控制

使用定时器控制状态机的超时:

#include <stdint.h>
#include <stdbool.h>

// 通信状态机
typedef enum {
    COMM_STATE_IDLE,
    COMM_STATE_SENDING,
    COMM_STATE_WAITING_ACK,
    COMM_STATE_TIMEOUT,
    COMM_STATE_ERROR
} CommState_t;

typedef struct {
    CommState_t state;
    uint8_t retry_count;
    uint8_t *data;
    uint16_t data_len;
    SoftwareTimer_t *timeout_timer;
} CommContext_t;

CommContext_t comm_ctx = {0};

// 超时回调
void Comm_Timeout_Callback(void *arg) {
    CommContext_t *ctx = (CommContext_t*)arg;

    printf("Communication timeout in state %d\n", ctx->state);

    if(ctx->retry_count < 3) {
        // 重试
        ctx->retry_count++;
        printf("Retry %d/3\n", ctx->retry_count);

        // 重新发送
        ctx->state = COMM_STATE_SENDING;
        SendData(ctx->data, ctx->data_len);

        // 重启超时定时器
        Timer_Start(ctx->timeout_timer);
    } else {
        // 超过重试次数
        ctx->state = COMM_STATE_TIMEOUT;
        printf("Communication failed after 3 retries\n");
    }
}

// 发送数据
bool Comm_SendData(uint8_t *data, uint16_t len) {
    if(comm_ctx.state != COMM_STATE_IDLE) {
        return false;  // 忙碌
    }

    // 保存数据
    comm_ctx.data = data;
    comm_ctx.data_len = len;
    comm_ctx.retry_count = 0;

    // 创建超时定时器(如果还没有)
    if(comm_ctx.timeout_timer == NULL) {
        comm_ctx.timeout_timer = Timer_Create(
            1000,  // 1秒超时
            TIMER_TYPE_ONE_SHOT,
            Comm_Timeout_Callback,
            &comm_ctx,
            "CommTimeout"
        );
    }

    // 发送数据
    comm_ctx.state = COMM_STATE_SENDING;
    SendData(data, len);

    // 启动超时定时器
    comm_ctx.state = COMM_STATE_WAITING_ACK;
    Timer_Start(comm_ctx.timeout_timer);

    return true;
}

// 接收到ACK
void Comm_OnAckReceived(void) {
    if(comm_ctx.state == COMM_STATE_WAITING_ACK) {
        // 停止超时定时器
        Timer_Stop(comm_ctx.timeout_timer);

        // 通信成功
        comm_ctx.state = COMM_STATE_IDLE;
        printf("Communication successful\n");
    }
}

// 使用示例
void Example_StateMachine(void) {
    uint8_t data[] = {0x01, 0x02, 0x03, 0x04};

    if(Comm_SendData(data, sizeof(data))) {
        printf("Data sent, waiting for ACK...\n");
    }

    // 在主循环或中断中调用
    // Comm_OnAckReceived();  // 收到ACK时调用
}

示例3:定时器组管理

管理一组相关的定时器:

// 定时器组
typedef struct {
    SoftwareTimer_t *timers[10];
    uint8_t count;
    char group_name[16];
} TimerGroup_t;

// 创建定时器组
TimerGroup_t* TimerGroup_Create(const char *name) {
    TimerGroup_t *group = (TimerGroup_t*)malloc(sizeof(TimerGroup_t));
    if(group != NULL) {
        group->count = 0;
        strncpy(group->group_name, name, sizeof(group->group_name) - 1);
    }
    return group;
}

// 添加定时器到组
bool TimerGroup_Add(TimerGroup_t *group, SoftwareTimer_t *timer) {
    if(group == NULL || timer == NULL || group->count >= 10) {
        return false;
    }

    group->timers[group->count++] = timer;
    return true;
}

// 启动组内所有定时器
void TimerGroup_StartAll(TimerGroup_t *group) {
    if(group == NULL) return;

    for(uint8_t i = 0; i < group->count; i++) {
        Timer_Start(group->timers[i]);
    }
    printf("Started %d timers in group '%s'\n", 
           group->count, group->group_name);
}

// 停止组内所有定时器
void TimerGroup_StopAll(TimerGroup_t *group) {
    if(group == NULL) return;

    for(uint8_t i = 0; i < group->count; i++) {
        Timer_Stop(group->timers[i]);
    }
    printf("Stopped %d timers in group '%s'\n",
           group->count, group->group_name);
}

// 使用示例:传感器采集组
void Example_TimerGroup(void) {
    // 创建传感器组
    TimerGroup_t *sensor_group = TimerGroup_Create("Sensors");

    // 创建多个传感器定时器
    SoftwareTimer_t *temp_timer = Timer_Create(
        1000, TIMER_TYPE_PERIODIC,
        ReadTemperature_Callback, NULL, "Temp"
    );

    SoftwareTimer_t *humidity_timer = Timer_Create(
        1000, TIMER_TYPE_PERIODIC,
        ReadHumidity_Callback, NULL, "Humidity"
    );

    SoftwareTimer_t *pressure_timer = Timer_Create(
        1000, TIMER_TYPE_PERIODIC,
        ReadPressure_Callback, NULL, "Pressure"
    );

    // 添加到组
    TimerGroup_Add(sensor_group, temp_timer);
    TimerGroup_Add(sensor_group, humidity_timer);
    TimerGroup_Add(sensor_group, pressure_timer);

    // 统一启动
    TimerGroup_StartAll(sensor_group);

    // 需要时统一停止
    // TimerGroup_StopAll(sensor_group);
}

深入理解

内存管理策略

软件定时器的内存管理有多种方式:

1. 静态分配(内存池)

#define TIMER_POOL_SIZE 20

SoftwareTimer_t timer_pool[TIMER_POOL_SIZE];
bool timer_used[TIMER_POOL_SIZE] = {false};

SoftwareTimer_t* Timer_Alloc(void) {
    for(uint8_t i = 0; i < TIMER_POOL_SIZE; i++) {
        if(!timer_used[i]) {
            timer_used[i] = true;
            memset(&timer_pool[i], 0, sizeof(SoftwareTimer_t));
            return &timer_pool[i];
        }
    }
    return NULL;  // 池已满
}

void Timer_Free(SoftwareTimer_t *timer) {
    for(uint8_t i = 0; i < TIMER_POOL_SIZE; i++) {
        if(&timer_pool[i] == timer) {
            timer_used[i] = false;
            break;
        }
    }
}

优点: - 无内存碎片 - 分配速度快 - 适合实时系统

缺点: - 内存占用固定 - 可能浪费内存

2. 动态分配

SoftwareTimer_t* Timer_Alloc(void) {
    return (SoftwareTimer_t*)malloc(sizeof(SoftwareTimer_t));
}

void Timer_Free(SoftwareTimer_t *timer) {
    free(timer);
}

优点: - 内存按需分配 - 灵活性高

缺点: - 可能有内存碎片 - 分配速度较慢 - 不适合实时系统

性能分析

1. 时间复杂度

不同实现方式的时间复杂度:

实现方式 插入 删除 更新
数组遍历 O(1) O(n) O(n)
无序链表 O(1) O(n) O(n)
有序链表 O(n) O(n) O(1)
时间轮 O(1) O(1) O(1)

时间轮算法(高级):

// 时间轮:将时间分成多个槽位
#define WHEEL_SIZE 256
#define WHEEL_MASK (WHEEL_SIZE - 1)

SoftwareTimer_t *time_wheel[WHEEL_SIZE];
uint8_t current_slot = 0;

void Timer_Start_Wheel(SoftwareTimer_t *timer) {
    uint8_t slot = (current_slot + timer->timeout) & WHEEL_MASK;

    // 插入到对应槽位
    timer->next = time_wheel[slot];
    time_wheel[slot] = timer;
}

void Timer_Update_Wheel(void) {
    current_slot = (current_slot + 1) & WHEEL_MASK;

    // 只处理当前槽位的定时器
    SoftwareTimer_t *timer = time_wheel[current_slot];
    time_wheel[current_slot] = NULL;

    while(timer != NULL) {
        SoftwareTimer_t *next = timer->next;

        // 执行回调
        if(timer->callback != NULL) {
            timer->callback(timer->callback_arg);
        }

        // 处理周期定时器
        if(timer->type == TIMER_TYPE_PERIODIC) {
            Timer_Start_Wheel(timer);
        }

        timer = next;
    }
}

2. 内存占用

// 计算内存占用
size_t Timer_GetMemoryUsage(void) {
    size_t total = 0;

    // 定时器结构体大小
    total += sizeof(SoftwareTimer_t) * timer_count;

    // 链表指针开销
    total += sizeof(void*) * timer_count;

    return total;
}

// 打印内存统计
void Timer_PrintMemoryStats(void) {
    printf("Timer Memory Usage:\n");
    printf("  Timer count: %u\n", timer_count);
    printf("  Size per timer: %u bytes\n", sizeof(SoftwareTimer_t));
    printf("  Total memory: %u bytes\n", Timer_GetMemoryUsage());
}

常见陷阱和最佳实践

陷阱1:回调函数中操作定时器

错误示例:

void BadCallback(void *arg) {
    SoftwareTimer_t *timer = (SoftwareTimer_t*)arg;

    // 危险:在回调中删除自己
    Timer_Delete(timer);  // 可能导致链表损坏
}

正确做法:

// 方法1:标记删除,延迟处理
volatile bool timer_delete_pending = false;
SoftwareTimer_t *timer_to_delete = NULL;

void GoodCallback(void *arg) {
    timer_to_delete = (SoftwareTimer_t*)arg;
    timer_delete_pending = true;
}

void main_loop(void) {
    if(timer_delete_pending) {
        timer_delete_pending = false;
        Timer_Delete(timer_to_delete);
    }
}

// 方法2:使用单次定时器,自动删除
SoftwareTimer_t *timer = Timer_Create(
    1000,
    TIMER_TYPE_ONE_SHOT,  // 单次定时器会自动停止
    Callback,
    NULL,
    "OneShot"
);

陷阱2:定时器精度问题

问题:

// 期望每1000ms执行一次
// 但实际可能是1000ms + 回调执行时间
void SlowCallback(void *arg) {
    ProcessData();  // 可能需要50ms
}

解决方案:

// 使用绝对时间
typedef struct {
    uint32_t next_trigger_time;
    uint32_t period;
} AccurateTimer_t;

void AccurateCallback(void *arg) {
    AccurateTimer_t *timer = (AccurateTimer_t*)arg;

    // 计算下次触发时间(基于绝对时间)
    timer->next_trigger_time += timer->period;

    // 执行任务
    ProcessData();
}

陷阱3:定时器泄漏

问题:

void CreateTimer(void) {
    // 每次调用都创建新定时器,但从不删除
    SoftwareTimer_t *timer = Timer_Create(...);
    Timer_Start(timer);
    // 忘记保存timer指针,无法删除
}

解决方案:

// 方法1:使用全局变量或静态变量
static SoftwareTimer_t *my_timer = NULL;

void CreateTimer(void) {
    if(my_timer == NULL) {
        my_timer = Timer_Create(...);
    }
    Timer_Start(my_timer);
}

// 方法2:使用定时器池
SoftwareTimer_t* GetOrCreateTimer(const char *name) {
    // 先查找是否已存在
    SoftwareTimer_t *timer = Timer_FindByName(name);
    if(timer == NULL) {
        timer = Timer_Create(...);
    }
    return timer;
}

最佳实践

  1. 回调函数设计:
  2. 保持回调函数简短快速
  3. 避免在回调中阻塞或延时
  4. 使用标志位通知主循环

  5. 定时器生命周期:

  6. 明确定时器的创建和销毁时机
  7. 使用静态分配避免内存泄漏
  8. 单次定时器用完即删除

  9. 精度要求:

  10. 根据需求选择合适的时基
  11. 关键任务使用硬件定时器
  12. 非关键任务使用软件定时器

  13. 资源管理:

  14. 限制定时器数量
  15. 监控内存使用
  16. 定期检查定时器状态

  17. 调试支持:

  18. 为定时器命名
  19. 记录创建和销毁
  20. 提供统计信息接口

常见问题

Q1: 软件定时器和硬件定时器有什么区别?

A: 主要区别在于实现方式和资源占用:

硬件定时器: - 由硬件电路实现,独立于CPU运行 - 精度高,不受软件影响 - 数量有限(通常2-4个) - 配置复杂,需要设置寄存器 - 适合高精度、实时性要求高的场景

软件定时器: - 由软件实现,依赖硬件时基 - 精度受软件影响(中断延迟、回调执行时间) - 数量灵活,可以创建很多个 - 使用简单,接口统一 - 适合一般精度要求的场景

选择建议: - PWM输出、精确延时 → 使用硬件定时器 - LED闪烁、周期任务 → 使用软件定时器 - 系统时基 → 使用硬件定时器(SysTick) - 超时保护、状态机 → 使用软件定时器

Q2: 如何选择合适的时基频率?

A: 时基频率的选择需要权衡精度和开销:

常用时基频率: - 1ms(1kHz):最常用,适合大多数场景 - 100us(10kHz):高精度场景 - 10ms(100Hz):低功耗场景

选择依据:

  1. 精度要求:
  2. 如果最小定时单位是10ms,时基可以是1ms
  3. 如果需要1ms精度,时基应该是100us或更快

  4. 中断开销:

  5. 时基越快,中断越频繁
  6. 中断开销 = 中断频率 × 单次中断时间
  7. 建议中断开销 < 5% CPU时间

  8. 定时器数量:

  9. 定时器越多,每次中断处理时间越长
  10. 需要确保能在下次中断前处理完

计算公式:

最大中断处理时间 = 1 / 时基频率
例如:1ms时基 → 最大处理时间 = 1ms

示例:

// 场景1:10个定时器,每个处理10us
// 总处理时间 = 10 × 10us = 100us
// 可以使用1ms时基(1000us > 100us)

// 场景2:50个定时器,每个处理10us
// 总处理时间 = 50 × 10us = 500us
// 1ms时基可能不够,考虑优化或使用10ms时基

Q3: 定时器回调函数可以做什么?

A: 回调函数应该遵循以下原则:

可以做的:

void GoodCallback(void *arg) {
    // 1. 设置标志位
    flag = true;

    // 2. 简单的GPIO操作
    LED_Toggle();

    // 3. 读取硬件寄存器
    uint16_t value = ADC->DR;

    // 4. 写入缓冲区
    buffer[index++] = value;

    // 5. 启动/停止其他定时器
    Timer_Start(another_timer);
}

不能做的:

void BadCallback(void *arg) {
    // 1. 延时函数
    Delay_ms(100);  // 绝对禁止!

    // 2. 阻塞等待
    while(!DataReady());  // 会卡死系统

    // 3. 复杂计算
    float result = ComplexCalculation();  // 太慢

    // 4. 打印输出
    printf("Timer expired\n");  // printf很慢

    // 5. 动态内存分配
    void *ptr = malloc(100);  // 不安全
}

推荐模式:

// 在回调中设置标志,在主循环中处理
volatile bool timer_event = false;

void TimerCallback(void *arg) {
    timer_event = true;  // 只设置标志
}

void main_loop(void) {
    while(1) {
        if(timer_event) {
            timer_event = false;

            // 在这里执行复杂操作
            ProcessTimerEvent();
        }
    }
}

Q4: 如何调试软件定时器?

A: 常用的调试方法:

  1. 添加调试输出:

    void Timer_Start_Debug(SoftwareTimer_t *timer) {
        printf("[DEBUG] Start timer '%s', timeout=%lu ms\n",
               timer->name, timer->timeout);
        Timer_Start(timer);
    }
    
    void Timer_Callback_Debug(void *arg) {
        SoftwareTimer_t *timer = (SoftwareTimer_t*)arg;
        printf("[DEBUG] Timer '%s' expired at %lu ms\n",
               timer->name, system_ticks);
    
        // 原始回调
        if(timer->callback != NULL) {
            timer->callback(timer->callback_arg);
        }
    }
    

  2. 使用GPIO跟踪:

    void TimerCallback(void *arg) {
        GPIO_Set(DEBUG_PIN);  // 回调开始
    
        // 执行任务
        DoWork();
    
        GPIO_Clear(DEBUG_PIN);  // 回调结束
    }
    // 使用逻辑分析仪观察DEBUG_PIN
    

  3. 统计信息:

    typedef struct {
        uint32_t callback_count;
        uint32_t max_callback_time;
        uint32_t total_callback_time;
    } TimerStats_t;
    
    TimerStats_t timer_stats[MAX_TIMERS];
    
    void Timer_Callback_Wrapper(void *arg) {
        uint32_t start = GetCycleCount();
    
        // 执行回调
        timer->callback(timer->callback_arg);
    
        uint32_t end = GetCycleCount();
        uint32_t elapsed = end - start;
    
        // 更新统计
        timer_stats[timer_id].callback_count++;
        timer_stats[timer_id].total_callback_time += elapsed;
        if(elapsed > timer_stats[timer_id].max_callback_time) {
            timer_stats[timer_id].max_callback_time = elapsed;
        }
    }
    

  4. 断言检查:

    void Timer_Update(void) {
        // 检查是否有定时器超时太久
        for(uint8_t i = 0; i < timer_count; i++) {
            if(timer_pool[i].state == TIMER_STATE_EXPIRED) {
                uint32_t overdue = system_ticks - timer_pool[i].expire_time;
                ASSERT(overdue < 10);  // 超时不应超过10ms
            }
        }
    }
    

Q5: 软件定时器会影响系统性能吗?

A: 会有一定影响,但可以优化:

性能影响因素:

  1. 中断频率:
  2. 1ms时基 → 每秒1000次中断
  3. 每次中断有进入/退出开销(几微秒)

  4. 定时器数量:

  5. 每个定时器需要检查和更新
  6. 数量越多,处理时间越长

  7. 回调执行时间:

  8. 回调在中断中执行
  9. 会阻塞其他中断

性能测量:

void MeasureTimerOverhead(void) {
    uint32_t start, end;

    // 测量更新时间
    start = GetCycleCount();
    Timer_Update();
    end = GetCycleCount();

    uint32_t cycles = end - start;
    float time_us = (float)cycles / CPU_FREQ_MHZ;
    float overhead = time_us / 1000.0f * 100.0f;  // 占1ms的百分比

    printf("Timer update: %.2f us (%.1f%% of 1ms)\n", time_us, overhead);
}

优化方法:

  1. 使用有序链表:
  2. 只检查最近要超时的定时器
  3. 减少不必要的检查

  4. 延迟回调执行:

  5. 在中断中只设置标志
  6. 在主循环中执行回调

  7. 减少定时器数量:

  8. 合并相似的定时器
  9. 使用状态机代替多个定时器

  10. 选择合适的时基:

  11. 不需要高精度时使用较慢的时基
  12. 减少中断频率

性能基准: - 良好:定时器开销 < 5% CPU时间 - 可接受:定时器开销 < 10% CPU时间 - 需要优化:定时器开销 > 10% CPU时间

总结

软件定时器是嵌入式系统中非常实用的组件,它让我们能够灵活地管理多个定时任务:

核心要点: - 数据结构:使用结构体存储定时器信息,链表管理多个定时器 - 时基更新:依赖硬件定时器提供1ms时基 - 超时处理:检测超时并执行回调函数 - 周期任务:支持单次和周期两种模式 - 精度优化:使用绝对时间、补偿中断延迟

适用场景: - LED闪烁、按键消抖 - 周期性数据采集 - 超时保护、看门狗 - 状态机超时控制 - 延迟任务执行

优势: - 使用简单,接口统一 - 数量灵活,可创建多个 - 资源占用小(每个定时器约50字节) - 易于维护和调试

局限: - 精度受软件影响 - 回调在中断中执行,需要快速返回 - 不适合微秒级精度要求 - 定时器过多会影响性能

下一步学习: - 如果需要更复杂的数据管理,学习环形缓冲区 - 如果需要更好的任务调度,学习协作式调度器 - 如果需要更强大的功能,学习RTOS定时器 - 如果需要高精度定时,学习硬件定时器编程

延伸阅读

推荐进一步学习的资源:

参考资料

  1. "Embedded Systems Architecture" - Tammy Noergaard
  2. "Making Embedded Systems" - Elecia White
  3. "Real-Time Concepts for Embedded Systems" - Qing Li
  4. FreeRTOS Software Timer Documentation

练习题:

  1. 实现一个基于数组的软件定时器系统,支持5个定时器
  2. 为定时器系统添加暂停和恢复功能
  3. 实现一个基于链表的定时器系统,支持动态创建和删除
  4. 使用软件定时器实现一个按键消抖功能
  5. 测量你的定时器系统的性能开销,并尝试优化

实践项目:

设计一个完整的定时器管理系统,包含以下功能: - 支持单次和周期定时器 - 使用链表管理定时器 - 提供创建、启动、停止、删除接口 - 实现定时器统计和调试功能 - 编写测试程序验证功能

下一步:建议学习 环形缓冲区设计与实现,掌握另一个重要的数据结构。