发布时间:2026/8/13 17:59:19
嵌入式裸机模块——软定时器 仓库地址Gitee - TimTask模块概述在嵌入式领域中比如单片机尤其STM32这种32位ARM架构的MCU当然这个模块也可以用在51单片机上不过51的资源太少了建议用精简版的上裸机运行业务代码的规模较大且逻辑复杂的裸机框架一般用的都是队列、状态机、软定时器行业内隐形规则呢就是少用阻塞式的延时比如HAL库自带的HAL_Delay()目前我接触到的就这些有更多的好技术也还请朋友评论区留言。针对软定时器在以前复刻了我之前工作的公司的软定时器是面向对象设计的就是cpp里的class(类)这么一个技术。后来我用C99的模式重写了一遍也就是用结构体指针的方式。用了很久一段时间发现每次使用前场景比如某个模块的结构体里包含了这个软定时器那么使用之前需要初始化(init)设置时间(set)判断超时(timeout)这么几个操作当项目渐渐变大的时候这些代码全部堆在里面阅读起来也渐渐比较的烦心了。那么有没有一种更方便的操作方式呢比如初始化自动完成、设置时间或者单次/循环运行、一个函数里判断所有的超时判断然后执行函数呢答案肯定是有的不然也不会有这篇文章了我使用了链表函数指针的方式来新增任务或者删除任务并在主任务执行函数中对链表遍历判断软定时器是否超时然后执行对应任务里的函数指针指向的待执行的函数。本模块只需要定义好硬件定时器时基来源然后声明定义任务链表的头指针TIM_TASK TimMain并在主函数中执行TimTaskHandle(TimMain);就可以自动完成软定时器的超时判断了。需要添加或者删除定时任务呢也只用两条函数即可分别是INT32U TimTaskAddTask(TIM_TASK * pTask,INT32U ulTime,void (*pfun)(void))其中ulTime是超时时间pfun超时执行的函数。添加任务成功后会返回任务id。INT8U TimTaskDelTask(TIM_TASK * pTask,INT32U id)删除指定id任务。INT8U TimTaskStartID(TIM_TASK * pTask,INT32U id)指定ID的软定时器启动注从当前时间线开始判断超时INT8U TimTaskStopID(TIM_TASK * pTask,INT32U id)指定ID的软定时器停止 该模块需要的资源非常简单仅需片上资源有硬件TIM定时器或者计数器就行。移值在keil 或者iar工程中添加.c文件不必细说了,初始化定时器。我用的TIM4作为时基来源分频系数7199也就是定时器内部计数器是每10us 就 1。voidMX_TIM4_Init(void){/* USER CODE BEGIN TIM4_Init 0 *//* USER CODE END TIM4_Init 0 */TIM_ClockConfigTypeDef sClockSourceConfig{0};TIM_MasterConfigTypeDef sMasterConfig{0};/* USER CODE BEGIN TIM4_Init 1 *//* USER CODE END TIM4_Init 1 */htim4.InstanceTIM4;htim4.Init.Prescaler7199;htim4.Init.CounterModeTIM_COUNTERMODE_UP;htim4.Init.Period65535;htim4.Init.ClockDivisionTIM_CLOCKDIVISION_DIV1;htim4.Init.AutoReloadPreloadTIM_AUTORELOAD_PRELOAD_ENABLE;if(HAL_TIM_Base_Init(htim4)!HAL_OK){Error_Handler();}sClockSourceConfig.ClockSourceTIM_CLOCKSOURCE_INTERNAL;if(HAL_TIM_ConfigClockSource(htim4,sClockSourceConfig)!HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTriggerTIM_TRGO_RESET;sMasterConfig.MasterSlaveModeTIM_MASTERSLAVEMODE_DISABLE;if(HAL_TIMEx_MasterConfigSynchronization(htim4,sMasterConfig)!HAL_OK){Error_Handler();}/* USER CODE BEGIN TIM4_Init 2 *//* USER CODE END TIM4_Init 2 */}然后启动时基HAL_TIM_Base_Start(htim4);在TimTask.h文件的顶部修改BASE来源下面的ratio根据你的分频系数来修改我的软定时器是毫秒级别的。实验效果主函数代码TIM_TASK TimMain;voidTaskTest(void){SEGGER_RTT_printf(0,id1 1000!%d\n,GET_TIM_BASE);}voidTaskTest1(void){TimTaskDelTask(TimMain,2);TimTaskDelTask(TimMain,3);TimTaskDelTask(TimMain,1);SEGGER_RTT_printf(0,id2 1001!%d\n,GET_TIM_BASE);}voidTaskTest2(void){SEGGER_RTT_printf(0,id3 2000!%d\n,GET_TIM_BASE);}voidTaskTest3(void){SEGGER_RTT_printf(0,id4 3000!%d\n,GET_TIM_BASE);}voidTaskTest4(void){SEGGER_RTT_printf(0,id5 4000!%d\n,GET_TIM_BASE);}intmain(void){TimTask_Init(TimMain);TimTaskAddTask(TimMain,1000,TaskTest);TimTaskAddTask(TimMain,1001,TaskTest1);TimTaskAddTask(TimMain,2000,TaskTest2);TimTaskAddTask(TimMain,3000,TaskTest3);TimTaskAddTask(TimMain,4000,TaskTest4);MX_TIM4_Init();HAL_TIM_Base_Start(htim4);while(1){TimTaskHandle(TimMain);}}效果1 删除任务id1 100010000指的是id号为1的任务设置超时为1000ms的任务10000是硬件定时器的计数器为每10us自增。可以看出误差是小于10us的至少两个id4任务的定时器计数器差值为0即 (60000-30000)us-(3000x10)us0x10(us)现象效果如下TaskTest1将id为 1 2 3的任务都删除了所以先输出了id 为 1 2 的信息id3由于超时时间未到并且在此之前就被id2 任务删除了所以没有输出id 4 id 5的任务循环定时输出信息。效果2 暂停和启动任务这个效果是暂停任务在输出id1 id2的任务后暂停id为1 2 3的任务并不是删除链表并释放内存空间只是没有进超时判断函数中在经过4000ms后在id5任务中重新启动id为3的任务所以在6000ms时id3的任务输出信息。总结和观望经过上诉的设计和自主测试定时任务的时间误差是非常非常小的自测也没发现什么重大BUG如有广大开发者朋友在使用该模块时遇到BUG或者其他问题均可在本文或者github、gitee中提出或者修复并提交非常感谢。附录代码TimTask.c#includeTimTask.htypedefstruct{INT32U ulSoftTim;INT32U ulInterval;INT32U ulTmr;INT16U uinTimerRec;INT8U uchStop;INT8U uchReadState;}TIM_TASK_SOFTTIM;typedefstructt_TIM_TASK{INT32U id;TIM_TASK_SOFTTIM stTimer;void(*pTaskFun)(INT32U);INT32U Parameter;INT8U uchDeleteMark;// 统一回收,避免回调中修改出错structt_TIM_TASK*next;}TIM_TASK;staticTIM_TASK TaskMain{0};staticvolatileINT32U TaskLast_ID;// 记录最后的任务ID,用于创建staticvolatileINT8U g_isDelTask0;staticvolatileINT32U g_currentTaskId0;staticvoidReadTimBase(TIM_TASK_SOFTTIM*pstTimVal);staticvoidSTimInit(TIM_TASK_SOFTTIM*pstSoftTimClass);staticvoidset(TIM_TASK_SOFTTIM*pstTimVal,INT32U ulTime);staticvoidStart(TIM_TASK_SOFTTIM*pstTimVal);staticvoidStop(TIM_TASK_SOFTTIM*pstTimVal);staticINT8STimout(TIM_TASK_SOFTTIM*pstTimVal);/***************************************************************** * brief 初始化任务管理器 * param pstTT * author lize * date 2025-05-08 *****************************************************************/voidTimTask_Init(void){TaskMain.id0;TaskMain.pTaskFunNULL;TaskMain.nextNULL;TaskLast_ID1;TaskMain.Parameter0;STimInit(TaskMain.stTimer);}/***************************************************************** * brief 查找ID任务是否存在 * param ulID ID * return INT8U 1 存在 * author lize * date 2025-05-08 *****************************************************************/INT8UTimTaskFindID(INT32U ID){if(TaskMain.nextNULL)return0;TIM_TASK*pTempTaskMain.next;while(pTemp!NULL){if(pTemp-idID){return1;}pTemppTemp-next;}return0;}/***************************************************************** * brief 获取当前正在执行的回调任务ID *****************************************************************/INT32UTimTaskGetCurrentID(void){returng_currentTaskId;}/***************************************************************** * brief 启动指定ID的软定时器 * param id * return INT8U 1:成功 * author lize * date 2025-05-09 *****************************************************************/INT8UTimTaskStartID(INT32U id){if(TaskMain.nextNULL)return0;TIM_TASK*pTempTaskMain.next;while(pTemp!NULL){if(pTemp-ididpTemp-uchDeleteMark0){Start(pTemp-stTimer);return1;}pTemppTemp-next;}return0;}/***************************************************************** * brief 暂停指定ID的软定时器 * param id * return INT8U 1:成功 * author lize * date 2025-05-09 *****************************************************************/INT8UTimTaskStopID(INT32U id){if(TaskMain.nextNULL)return0;TIM_TASK*pTempTaskMain.next;while(pTemp!NULL){if(pTemp-ididpTemp-uchDeleteMark0){Stop(pTemp-stTimer);return1;}pTemppTemp-next;}return0;}/***************************************************************** * brief 添加任务 * param uiTime 时间 * param pfun 待执行函数 * return INT32U 0 : Err ; other : 任务的id * author lize * date 2025-05-08 *****************************************************************/INT32UTimTaskAddTask(INT32U ulTime,void(*pfun)(INT32U),INT32U ulParameter){TIM_TASK*pnew;INT32U startId;INT32U ret0;// 默认失败返回0if(!pfun)return0;TT_inter_critical;pnew(TIM_TASK*)malloc(sizeof(TIM_TASK));if(pnewNULL)gotocleanup;memset(pnew,0,sizeof(TIM_TASK));startIdTaskLast_ID;do{if(0TimTaskFindID(TaskLast_ID))break;TaskLast_ID;if(TaskLast_ID0)TaskLast_ID1;}while(TaskLast_ID!startId);if(TimTaskFindID(TaskLast_ID)!0){// 兜底错误,如果都存在的情况free(pnew);gotocleanup;}pnew-idTaskLast_ID;pnew-nextNULL;pnew-pTaskFunpfun;pnew-ParameterulParameter;STimInit(pnew-stTimer);set(pnew-stTimer,ulTime);TIM_TASK*scanTaskMain;while(scan-next!NULL){scanscan-next;}scan-nextpnew;retpnew-id;cleanup:TT_exit_critical;returnret;}/***************************************************************** * brief 删除任务 * param id 任务ID * return INT8U 1 : 找到对应ID任务并标记删除 * author lize * date 2025-05-08 * note 仅置 uchDeleteMark1 并立即停止定时器,不真正 free. * 节点由 TimTaskHandle 在一轮遍历结束后统一回收, * 这样回调执行期间删除任意节点都不会导致 未知错误. * TimTaskFindID/StartID/StopID 已做处理,删除立即生效. *****************************************************************/INT8UTimTaskDelTask(INT32U id){TIM_TASK*pTemp;INT8U ret0;if(TaskMain.nextNULL)return0;TT_inter_critical;pTempTaskMain.next;while(pTemp!NULL){if(pTemp-ididpTemp-uchDeleteMark0){pTemp-uchDeleteMark1;pTemp-stTimer.uchStop1;// 立即停止定时,不再触发回调g_isDelTask1;ret1;break;}pTemppTemp-next;}TT_exit_critical;returnret;}/***************************************************************** * brief 定时任务主程序 * author lize * date 2025-05-08 *****************************************************************/voidTimTaskHandle(void){TIM_TASK*prev;TIM_TASK*current;if(TaskMain.nextNULL)return;if(g_isDelTask1){prevTaskMain;currentTaskMain.next;while(current!NULL){if(current-uchDeleteMark){prev-nextcurrent-next;current-nextNULL;free(current);currentprev-next;}else{prevcurrent;currentcurrent-next;}}g_isDelTask0;}currentTaskMain.next;while(current!NULL){if(current-uchDeleteMark){// 跳过已标记删除的节点currentcurrent-next;continue;}if(Timout(current-stTimer)){g_currentTaskIdcurrent-id;current-pTaskFun(current-Parameter);g_currentTaskId0;}currentcurrent-next;}}/***************************************************************** 函数名称 函数功能 输入参数 返回参数 修改记录: 日 期 版 本 修改人 内容 2023.9.21 1.0 lzj 初版 2023.11.3 1.1 lzj 结构体版 *****************************************************************/staticvoidReadTimBase(TIM_TASK_SOFTTIM*pstTimVal){INT16U uinTemp;INT16U uinTimeStamp;if(pstTimVal-uchReadState){pstTimVal-uchReadState0;pstTimVal-uinTimerRecTASK_GET_TIM_BASE;// 进入时取时基初始时间}uinTimeStampTASK_GET_TIM_BASE;uinTempuinTimeStamp-pstTimVal-uinTimerRec;// 计算差值pstTimVal-ulSoftTimuinTemp;pstTimVal-uinTimerRecuinTimeStamp;// 记录本次变化}/******************** 初始化 ****************************/staticvoidSTimInit(TIM_TASK_SOFTTIM*pstTimVal){//ulTmr 0;pstTimVal-uchStop1;// 0:启动 1停止pstTimVal-uchReadState1;}/******************** 设置时间 ****************************/staticvoidset(TIM_TASK_SOFTTIM*pstTimVal,INT32U ulTime){pstTimVal-ulIntervalulTime*TASK_Tim_ratio;Start(pstTimVal);}/******************** 启动 ****************************/staticvoidStart(TIM_TASK_SOFTTIM*pstTimVal){ReadTimBase(pstTimVal);pstTimVal-ulTmrpstTimVal-ulSoftTim;pstTimVal-uchStop0;}/******************** 暂停 ****************************/staticvoidStop(TIM_TASK_SOFTTIM*pstTimVal){pstTimVal-uchStop1;}/******************** 超时判断 ***************************/staticINT8STimout(TIM_TASK_SOFTTIM*pstTimVal){if(pstTimValNULL||pstTimVal-uchStop1)return0;ReadTimBase(pstTimVal);if(pstTimVal-ulSoftTimpstTimVal-ulTmr){if((pstTimVal-ulSoftTim-pstTimVal-ulTmr)pstTimVal-ulInterval){pstTimVal-ulTmrpstTimVal-ulSoftTim;return1;}}else{if((0xFFFFFFFFu-pstTimVal-ulTmrpstTimVal-ulSoftTim1u)pstTimVal-ulInterval){pstTimVal-ulTmrpstTimVal-ulSoftTim;return1;}}return0;}TimTask.h#ifndef__TIMTASK_H#define__TIMTASK_H#includePub_typedef.h#includestdlib.h#includeTimer.h#defineTASK_GET_TIM_BASE(TIM2-CNT)//定时器时钟除预分频值为定时器频率周期为1ms。软定时为毫秒级// TIM: 主频 预分频#defineTASK_Tim_ratio((24000000/24000)/1000)/***************************************************************** * todo 如果只在主程序中使用可以不用考虑中断安全 * 在中断中删除或者增加任务需要进入临界区 *****************************************************************/#ifdefTASK_INTERRUPT_SAFETY#defineTT_inter_critical__disable_irq()#defineTT_exit_critical__enable_irq()#else#defineTT_inter_critical#defineTT_exit_critical#endifvoidTimTask_Init(void);INT8UTimTaskFindID(INT32U ID);INT32UTimTaskGetCurrentID(void);INT8UTimTaskStartID(INT32U id);INT8UTimTaskStopID(INT32U id);INT32UTimTaskAddTask(INT32U ulTime,void(*pfun)(INT32U),INT32U ulParameter);INT8UTimTaskDelTask(INT32U id);voidTimTaskHandle(void);#endif

相关新闻

2026/8/13 17:59:19

【免费下载】 Embabel Agent 项目启动与配置教程

Embabel Agent 项目启动与配置教程 【免费下载链接】embabel-agent Agent framework for the JVM. Pronounced Em-BAY-bel /ɛmˈbeɪbəl/ 项目地址: https://gitcode.com/GitHub_Trending/em/embabel-agent 1. 项目的目录结构及介绍 Embabel Agent 是一个用于编写代理…

2026/8/13 18:59:25

Route性能优化:缓存路由器实现与自动失效策略

Route性能优化:缓存路由器实现与自动失效策略 【免费下载链接】route Fast PSR-7 based routing and dispatch component including PSR-15 middleware, built on top of FastRoute. 项目地址: https://gitcode.com/gh_mirrors/route1/route GitHub 加速计划…

2026/8/13 18:59:25

如何快速掌握Fusion框架:面向开发者的完整Luau开发教程

如何快速掌握Fusion框架:面向开发者的完整Luau开发教程 【免费下载链接】Fusion Futuristic Luau for every universe. 项目地址: https://gitcode.com/gh_mirrors/fusion4/Fusion Fusion是一款面向未来的Luau框架,专为各种开发场景设计&#xff…

2026/8/13 18:59:25

斯坦福AI速查表:5分钟掌握人工智能核心概念

斯坦福AI速查表:5分钟掌握人工智能核心概念 【免费下载链接】stanford-cs-221-artificial-intelligence VIP cheatsheets for Stanfords CS 221 Artificial Intelligence 项目地址: https://gitcode.com/gh_mirrors/st/stanford-cs-221-artificial-intelligence …

2026/8/12 10:37:12

如何快速生成中国车牌图片:Python开源工具完整指南

如何快速生成中国车牌图片:Python开源工具完整指南 【免费下载链接】chinese_license_plate_generator 中国车牌生成器 项目地址: https://gitcode.com/gh_mirrors/ch/chinese_license_plate_generator 中国车牌生成器是一个基于Python的开源项目&#xff0c…

2026/8/12 5:35:25

当 LLM 遇见大文档:主流开源项目如何处理上下文超限

从 Agentic Loop 到 Repo Map,七种策略与六类陷阱引言:128K vs 10MB 的硬冲突 2026 年的 LLM 上下文窗口已达到 128K ~ 1M token(≈ 0.5MB ~ 4MB 文本),但 LLM 想要处理的真实数据规模远远超过这个量级:真实…

2026/8/13 0:02:21

Prefix Cache

Prefix Cache(前缀缓存) 是大模型推理引擎(如 vLLM、SGLang、TensorRT-LLM)中用于跨请求复用已计算 KV Cache 的核心内存与计算优化技术。 它的核心目的在于:彻底消除重复 Prompt 的 Prefill 阶段计算,将首…

2026/8/13 0:02:21

VSCode插件精选:从AI补全到代码规范,打造高效开发环境

1. 项目概述:为什么说插件是VSCode的灵魂?如果你和我一样,每天有超过8小时的时间是在VSCode里度过的,那你肯定明白,一个顺手的开发环境有多重要。VSCode本身已经足够优秀了,但真正让它从“好用的编辑器”蜕…

2026/8/13 0:02:21

如何快速完成文件批量重命名:FreeReNamer终极指南

如何快速完成文件批量重命名:FreeReNamer终极指南 【免费下载链接】FreeReNamer 功能强大又易用的文件批量重命名软件 项目地址: https://gitcode.com/gh_mirrors/fr/FreeReNamer 你是否曾经面对成百上千个杂乱无章的文件感到头疼?传统的手动重命…

2026/8/10 11:20:30

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/11 17:06:59

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/11 3:05:11

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…