RT-Thread 软件定时器 5 大 API 实战:单次/周期模式在 STM32 上的 3 个应用案例
2026/7/11 21:37:04 网站建设 项目流程

RT-Thread 软件定时器 5 大 API 实战:单次/周期模式在 STM32 上的 3 个应用案例

1. 软件定时器基础与核心 API 解析

在嵌入式实时系统中,定时器是控制任务执行节奏的关键组件。RT-Thread 提供的软件定时器功能允许开发者突破硬件定时器数量的限制,创建灵活的定时任务。与硬件定时器不同,软件定时器的超时函数执行在 RT-Thread 的定时器线程上下文中,而非中断环境,这使得开发者可以在回调函数中使用更多的系统 API。

RT-Thread 软件定时器主要提供以下 5 个核心 API:

  1. 创建定时器 (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:定时周期(单位:tick)
    • flag:组合标志(单次/周期、软件/硬件模式)
  2. 启动定时器 (rt_timer_start)
    将定时器激活并插入到系统定时器链表中,开始计时:

    rt_err_t rt_timer_start(rt_timer_t timer);
  3. 停止定时器 (rt_timer_stop)
    将定时器从活动链表中移除,停止计时但不释放资源:

    rt_err_t rt_timer_stop(rt_timer_t timer);
  4. 删除定时器 (rt_timer_delete)
    彻底释放定时器占用的内存资源:

    rt_err_t rt_timer_delete(rt_timer_t timer);
  5. 控制定时器 (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_SECOND=1000) 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; }

性能优化技巧

  1. 使用RT_TIMER_FLAG_PERIODIC避免重复创建/启动开销
  2. 计算 tick 间隔时考虑整除问题,避免累积误差
  3. 在回调函数中避免耗时操作,防止影响定时精度

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 (假设1tick=1ms) 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; }

关键优化

  • 使用两个单次定时器形成循环触发
  • 动态计算亮灭时间实现占空比调整
  • 确保总周期时间恒定(20ms)

5. 软件定时器高级应用技巧

5.1 定时器精度优化策略

虽然软件定时器精度受限于系统 tick,但可通过以下方法提高:

  1. 提高系统 tick 频率
    修改rtconfig.h中的RT_TICK_PER_SECOND,但会增加系统开销

  2. 补偿技术
    记录实际超时时间与理论时间的偏差,下次定时进行补偿:

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 资源管理与注意事项

  1. 内存管理

    • 动态定时器需配对使用create/delete
    • 静态定时器使用init/detach
  2. 线程安全

    • 在中断中操作定时器需使用rt_interrupt_enter/leave
  3. 性能监控
    通过list_timer命令查看定时器状态:

msh />list_timer timer periodic timeout flag ------ --------- --------- ------- t1 1000 500 SOFT,PERIODIC t2 2000 1500 SOFT,ONE_SHOT
  1. 常见问题排查
    • 定时不准确:检查系统负载和 tick 配置
    • 回调未执行:确认定时器是否已启动
    • 内存泄漏:确保删除不再使用的定时器

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询