📋 个人简介
- 💖 作者简介:大家好,我是喜欢记录零碎知识点的小菜鸟。😎
- 📝 个人主页:欢迎访问我的 Ethernet_Comm 博客主页🔥
- 🎉 支持我:点赞👍+收藏⭐️+留言📝
-📣 系列专栏:嵌入式C编程🍁- 💬格言:写文档啊不是写文章,重要的还是直白!🔥
MultiTimer 的代码少,非常适合拿来学习单链表的操作,用此实例学习大佬是如何操作单链表的。
MultiTimer 是一个软件定时器扩展模块,可无限扩展你所需的定时器任务,取代传统的标志位判断方式, 更优雅更便捷地管理程序的时间触发时序。
1. 软件设计思路
1.1 MultiTimer 结构体
解析:MultiTimer
定义为 单链表 的形式; 链表的节点设计为一个软件定时器,所以理论上支持的定时器数量只受内存限制
每个 MultiTimer
都有一个 deadline
,以开发板系统的tick
为基准周期,时间到达 deadline
,就执行其对应的 callback
MultiTimer
中所有的结点都是定时器,每个定时器之间相互独立,不存在先后次序关系
typedef void (*MultiTimerCallback_t)(MultiTimer* timer, void* userData);
typedef struct MultiTimerHandle {
MultiTimer* next;
uint64_t deadline; // 超时时间
MultiTimerCallback_t callback; // 到达超时时间,就执行 callback
void* userData;
}MultiTimer;
1.2 main函数解析
1️⃣ : 【使用步骤】
- 提供Timer时基信号
MultiTimerInstall()
,如使用STM32HAL库,所以通过Systick来提供,无需设置额外的定时器。 - 创建Timer对象
timer1 / 2 / 3
,注册定时器回调处理函数,设置超时时间(ms) - 设置 Timer回调函数
- 遍历链表,循环触发
2️⃣ : 【思路解析】
-
获取系统的当前时间 单位:ms (移植到开发板中时,这个时间可以是开发板的硬件定时器中断)
-
初始化三个定时器,分别为
-
timer1
是1s
执行一次回调,周期执行; -
timer2
是5s
执行一次,单次; -
timer3
是 延迟3.456s
开始执行,执行周期为4567ms
-
-
MultiTimerYield()
遍历链表,执行到达deadline
的链表的callback
MultiTimer timer1;
MultiTimer timer2;
MultiTimer timer3;
int main(int argc, char *argv[])
{
MultiTimerInstall(PlatformTicksGetFunc); // 获取系统的当前时间 单位:ms (移植到开发板中时,这个时间可以是开发板的硬件定时器中断)
MultiTimerStart(&timer1, 1000, exampleTimer1Callback, "1000ms CYCLE timer");
MultiTimerStart(&timer2, 5000, exampleTimer2Callback, "5000ms ONCE timer");
MultiTimerStart(&timer3, 3456, exampleTimer3Callback, "3456ms delay start, 4567ms CYCLE timer");
while (1) {
MultiTimerYield();
}
}
1.3 MultiTimerStart() 函数解析
- 向单链表中插入
timer
时,先判断链表中是否有同样的节点,若有则先移除(避免插入同样的timer
节点)- 链表节点的删除参考下文【链表的删除】
- 本函数采用思路:创建二级指针,通过遍历链表的方式来寻找节点中next指针指向删除节点的那个节点,不如传统的遍历好理解,代码中注释部分就是传统的遍历方式(自己改写,比较容易理解)
- 链表插入机制通过超时时间排序插入:
int MultiTimerStart(MultiTimer* timer, uint64_t timing, MultiTimerCallback_t callback, void* userData)
{
if (!timer || !callback ) {
return -1;
}
MultiTimer** nextTimer = &timerList;
/* Remove the existing target timer. */
while(*nextTimer!=NULL){
if (timer == *nextTimer) {
*nextTimer = timer->next; /* remove from list */
break;
}
nextTimer = &(*nextTimer)->next
}
/* add by me 为了便于理解*/
/*MultiTimer *preTimer = timer; //要插入的节点
MultiTimer *currentTimer = timer->next; // 插入 节点的下一个节点
while(timer->next!=NULL){
if(timer == currentTimer){
timer = currentTimer->next; // remove from list
break;
}
preTimer = currentTimer;
currentTimer = currentTimer->next;
}*/
/* Init timer. */
timer->deadline = platformTicksFunction() + timing;
timer->callback = callback;
timer->userData = userData;
/* Insert timer. */
for (; nextTimer = &timerList; nextTimer = &(*nextTimer)->next) {
if (*nextTimer!=NULL) {
timer->next = NULL;
*nextTimer = timer;
break;
}
/*通过超时时间排序插入*/
if (timer->deadline < (*nextTimer)->deadline) {
timer->next = *nextTimer;
*nextTimer = timer;
break;
}
}
return 0;
}
1.4 MultiTimerStop()函数解析
链表的删除,没啥可说的,上章节已经讲过
二级指针不好理解的,可以自定义用一级指针
1.5 MultiTimerYield() 函数解析
因为链表插入时是按照 deadline
时间顺序从小到大插入的,因此循环遍历链表时,按照 deadline
,到时间则执行 callback
,不到时间则继续循环遍历
int MultiTimerYield(void)
{
MultiTimer* entry = timerList;
for (; entry; entry = entry->next) {
/* Sorted list, just process with the front part. */
if (platformTicksFunction() < entry->deadline) {
return (int)(entry->deadline - platformTicksFunction());
}
/* remove expired timer from list */
timerList = entry->next;
/* call callback */
if (entry->callback) {
entry->callback(entry, entry->userData);
}
}
return 0;
}
2. 参考资料
第6期 | MultiTimer,一款可无限扩展的软件定时器
项目地址:https://github.com/0x1abin/MultiTimer
3. 链表的操作
3.1 链表尾部增加一个节点
3.2 链表头部插入一个新节点
3.3 链表的删除
- 遍历链表,找到要删除节点
data1
的位置 - 通过临时变量,记录
data1
指向的下一个节点data2
的地址 - 将
data0->next
指向data2
- 将
data1
的内存释放掉