当前位置: 首页 > news >正文

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

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 配置
    • 回调未执行:确认定时器是否已启动
    • 内存泄漏:确保删除不再使用的定时器
http://www.jsqmd.com/news/1169885/

相关文章:

  • 从零到一 | CV转多模态大模型 | week17 | LLM 推理优化 vLLM 详解
  • 保安岗亭厂家测评:日硕科技质量定制出色,服务响应却稍慢!
  • 产品经理开会:2026年5款视频号内容总结工具,快速生成会议纪要
  • SQL性能优化实战
  • Tab补全总卡顿、不精准、漏提示?这5个配置项改完立竿见影,团队已全员落地
  • Unity中5自由度机械臂物理仿真:从ArticulationBody到关节控制实战
  • AI Agent市场2026年关键词:从“能不能聊”到“能不能干” | 企业级数字员工落地深度实测
  • 终极解决方案:3步彻底修复TranslucentTB的Microsoft.VCLibs依赖缺失问题
  • 2026 年国内真人数字人制作平台TOP10:晟诺科讯达第一,企业选型正规参考
  • 8086 实模式寻址:20位地址总线下的基址偏移计算与 1MB 内存访问
  • 解决设备状态黑盒:注塑机数据采集从现场实施到报表生成
  • 2026家装瓷砖十大品牌盘点:主流瓷砖品类特点汇总
  • Windows内核驱动中C++异常处理(vcrtl)原理与实践指南
  • 系统装好了,高拍仪等外设却“罢工”?这个老大难终于有解了
  • 2026年7月最新太原劳力士官方售后客服中心地址电话及服务网点分布 - 劳力士官方服务中心
  • 为什么Google内部团队弃用JetBrains改用Windsurf IDE?——源码级性能压测报告首次公开
  • 实现RAG系统的24小时极速原型:从Paper到可运行Demo的工程笔记
  • 三星AR眼镜2026量产:MicroLED与Android XR生态协同落地
  • 如何实现跨平台直播内容的智能捕获与永久保存?
  • 亚马逊多店铺独立运营如何选择环境隔离工具(隐私隔离浏览器技术解析)
  • 如何三步完成微博备份到PDF:Speechless 终极指南
  • Windows 11优化终极指南:如何用Win11Debloat让你的系统更干净更快速
  • LM358/LM393 选型实战:3个关键参数对比与5个典型电路应用场景
  • ChatGPT-o1推理模型性能真相:实测128K上下文下P99延迟达4.8s——但通过3层缓存预热+逻辑单元隔离可压至1.2s!
  • Rust实现Windows本地搜索工具
  • 不掉屑卫生纸哪家性价比高:联盛森宝专业一流 - MXyuyu
  • 别踩 2026做B站视频总结的常见误区:我总结了控成本实操经验
  • 如何在老旧Android电视上获得流畅的4K直播体验?mytv-android原生解决方案
  • 2026年工业胶带厂家推荐榜:德莎/tesa胶带,汽车线束胶带,耐高温布基胶带,水溶性飞接胶带,泡棉双面胶带源头工厂详解 - 甄选服务推荐
  • 2026年真人数字人制作平台选购攻略:3 步筛选法,选出适配靠谱工具