RT-Thread 软件定时器 5 大 API 实战:单次/周期模式在 STM32 上的 3 个应用案例 RT-Thread 软件定时器 5 大 API 实战单次/周期模式在 STM32 上的 3 个应用案例1. 软件定时器基础与核心 API 解析在嵌入式实时系统中定时器是控制任务执行节奏的关键组件。RT-Thread 提供的软件定时器功能允许开发者突破硬件定时器数量的限制创建灵活的定时任务。与硬件定时器不同软件定时器的超时函数执行在 RT-Thread 的定时器线程上下文中而非中断环境这使得开发者可以在回调函数中使用更多的系统 API。RT-Thread 软件定时器主要提供以下 5 个核心 API创建定时器 (rt_timer_create)动态分配内存并初始化定时器控制块适用于需要频繁创建/销毁的场景。关键参数包括rt_timer_t rt_timer_create(const char* name, void (*timeout)(void*), void* parameter, rt_tick_t time, rt_uint8_t flag);name定时器名称用于调试timeout超时回调函数指针time定时周期单位tickflag组合标志单次/周期、软件/硬件模式启动定时器 (rt_timer_start)将定时器激活并插入到系统定时器链表中开始计时rt_err_t rt_timer_start(rt_timer_t timer);停止定时器 (rt_timer_stop)将定时器从活动链表中移除停止计时但不释放资源rt_err_t rt_timer_stop(rt_timer_t timer);删除定时器 (rt_timer_delete)彻底释放定时器占用的内存资源rt_err_t rt_timer_delete(rt_timer_t timer);控制定时器 (rt_timer_control)动态修改定时器参数支持以下命令rt_err_t rt_timer_control(rt_timer_t timer, rt_uint8_t cmd, void* arg);RT_TIMER_CTRL_SET_TIME修改定时周期RT_TIMER_CTRL_SET_ONESHOT切换为单次模式RT_TIMER_CTRL_SET_PERIODIC切换为周期模式关键区别软件定时器RT_TIMER_FLAG_SOFT_TIMER的超时函数执行在线程上下文允许调用如rt_thread_delay等可能阻塞的 API而硬件定时器模式RT_TIMER_FLAG_HARD_TIMER的超时函数执行在中断上下文要求快速执行完毕。2. 案例一单次定时器控制 LED 闪烁2.1 硬件连接与初始化本案例基于 STM32F407 开发板使用 PE3 引脚连接 LED。首先完成硬件初始化#define LED_PIN GET_PIN(E, 3) static void led_init(void) { rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT); rt_pin_write(LED_PIN, PIN_HIGH); // 初始熄灭 }2.2 单次定时器实现创建单次定时器在超时后翻转 LED 状态并自动销毁static rt_timer_t led_timer RT_NULL; static void led_timeout(void *param) { rt_uint32_t *count (rt_uint32_t *)param; rt_pin_write(LED_PIN, !rt_pin_read(LED_PIN)); rt_kprintf(LED toggled %d times\n, (*count)); // 定时器自动销毁单次模式 if (*count 5) { rt_kprintf(Timer completed its work\n); } } int led_blink_sample(void) { static rt_uint32_t toggle_count 0; led_init(); led_timer rt_timer_create(led_timer, led_timeout, toggle_count, 1000, // 1000 ticks 1s (RT_TICK_PER_SECOND1000) RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER); if (led_timer ! RT_NULL) { rt_timer_start(led_timer); rt_kprintf(Single-shot timer started\n); } return RT_EOK; }关键点说明使用RT_TIMER_FLAG_ONE_SHOT标志创建单次定时器定时器超时后自动从系统移除但控制块仍存在需手动删除通过参数传递计数变量实现状态保持2.3 进阶用法链式单次定时器实现连续闪烁效果每次超时后重新启动定时器static void chained_timeout(void *param) { rt_pin_write(LED_PIN, !rt_pin_read(LED_PIN)); // 重新配置并启动定时器 rt_timer_control(led_timer, RT_TIMER_CTRL_SET_TIME, (void*)500); rt_timer_start(led_timer); }3. 案例二周期定时器实现数据采样3.1 模拟数据采集环境假设通过 ADC 采集传感器数据此处用随机数模拟static rt_uint16_t mock_adc_read(void) { return rand() % 4096; // 12位ADC模拟值 }3.2 周期定时器配置创建 100Hz 采样率的周期定时器#define SAMPLE_RATE_HZ 100 static rt_timer_t adc_timer RT_NULL; static rt_uint32_t sample_count 0; static void adc_sample_timeout(void *param) { rt_uint16_t adc_val mock_adc_read(); rt_kprintf([%04d] ADC: %04d\n, sample_count, adc_val); } int adc_sample_init(void) { rt_tick_t interval RT_TICK_PER_SECOND / SAMPLE_RATE_HZ; adc_timer rt_timer_create(adc_timer, adc_sample_timeout, RT_NULL, interval, RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER); if (adc_timer ! RT_NULL) { rt_timer_start(adc_timer); rt_kprintf(ADC sampling started at %dHz\n, SAMPLE_RATE_HZ); } return RT_EOK; }性能优化技巧使用RT_TIMER_FLAG_PERIODIC避免重复创建/启动开销计算 tick 间隔时考虑整除问题避免累积误差在回调函数中避免耗时操作防止影响定时精度3.3 动态调整采样率通过rt_timer_control实现运行时参数调整void set_adc_sample_rate(rt_uint32_t new_rate_hz) { rt_tick_t new_interval RT_TICK_PER_SECOND / new_rate_hz; rt_timer_control(adc_timer, RT_TIMER_CTRL_SET_TIME, new_interval); rt_kprintf(Sample rate changed to %dHz\n, new_rate_hz); }4. 案例三动态修改定时周期实现 PWM 调光4.1 PWM 原理与软件实现通过动态调整 LED 亮灭时间的占空比模拟 PWM 输出参数说明周期固定为 20ms (50Hz)占空比0-100% 可调分辨率1% (200μs/步进)4.2 代码实现使用两个定时器分别控制亮灭时间static rt_timer_t pwm_on_timer RT_NULL; static rt_timer_t pwm_off_timer RT_NULL; static rt_uint8_t pwm_duty 50; // 初始50%占空比 static void pwm_on_timeout(void *param) { rt_pin_write(LED_PIN, PIN_LOW); // LED亮 rt_timer_start(pwm_off_timer); // 启动灭定时器 } static void pwm_off_timeout(void *param) { rt_pin_write(LED_PIN, PIN_HIGH); // LED灭 rt_timer_start(pwm_on_timer); // 启动亮定时器 } void set_pwm_duty(rt_uint8_t duty) { if (duty 100) duty 100; pwm_duty duty; rt_tick_t on_time 20 * duty; // 单位tick (假设1tick1ms) rt_tick_t off_time 2000 - on_time; rt_timer_control(pwm_on_timer, RT_TIMER_CTRL_SET_TIME, on_time); rt_timer_control(pwm_off_timer, RT_TIMER_CTRL_SET_TIME, off_time); } int pwm_init(void) { led_init(); pwm_on_timer rt_timer_create(pwm_on, pwm_on_timeout, RT_NULL, 1000, // 初始1ms RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER); pwm_off_timer rt_timer_create(pwm_off, pwm_off_timeout, RT_NULL, 1000, // 初始1ms RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER); if (pwm_on_timer pwm_off_timer) { set_pwm_duty(pwm_duty); rt_timer_start(pwm_on_timer); rt_kprintf(Software PWM initialized at %d%% duty\n, pwm_duty); } return RT_EOK; }关键优化使用两个单次定时器形成循环触发动态计算亮灭时间实现占空比调整确保总周期时间恒定20ms5. 软件定时器高级应用技巧5.1 定时器精度优化策略虽然软件定时器精度受限于系统 tick但可通过以下方法提高提高系统 tick 频率修改rtconfig.h中的RT_TICK_PER_SECOND但会增加系统开销补偿技术记录实际超时时间与理论时间的偏差下次定时进行补偿static rt_tick_t last_time; static rt_int32_t accum_error 0; static void precise_timeout(void *param) { rt_tick_t now rt_tick_get(); rt_int32_t deviation (now - last_time) - target_interval; accum_error deviation; // 下次定时补偿误差 rt_tick_t adj_interval target_interval - (accum_error / 4); rt_timer_control(timer, RT_TIMER_CTRL_SET_TIME, adj_interval); last_time now; // ... 业务逻辑 ... }5.2 多定时器协同工作通过一个管理器定时器调度多个任务减少系统资源占用#define MAX_TASKS 5 struct timer_task { rt_tick_t interval; rt_tick_t last_run; void (*func)(void); }; static struct timer_task task_list[MAX_TASKS]; static void scheduler_timeout(void *param) { rt_tick_t now rt_tick_get(); for (int i 0; i MAX_TASKS; i) { if (now - task_list[i].last_run task_list[i].interval) { task_list[i].func(); task_list[i].last_run now; } } rt_timer_start((rt_timer_t)param); // 重新启动 }5.3 资源管理与注意事项内存管理动态定时器需配对使用create/delete静态定时器使用init/detach线程安全在中断中操作定时器需使用rt_interrupt_enter/leave性能监控通过list_timer命令查看定时器状态msh /list_timer timer periodic timeout flag ------ --------- --------- ------- t1 1000 500 SOFT,PERIODIC t2 2000 1500 SOFT,ONE_SHOT常见问题排查定时不准确检查系统负载和 tick 配置回调未执行确认定时器是否已启动内存泄漏确保删除不再使用的定时器