基于链表的软件定时器实现
目录
1. 应用需求
1.1 背景
- 主要是针对单片机的开发,一般单片机的硬件定时器是有限的,不太可能有一个定时任务就用硬件定时器实现。
1.2 前提
- 有一个已经实现的硬件定时器在进行计数。(本文的实现是基于time.h的库进行获取的时间)
1.3 功能
- 支持根据参数设置的优先级进行顺序插入,优先级的值越小,则轮询定时器的时候会更快执行。
- 支持配置软件定时器的定时时间,可以有两种模式配置,一种是持续性的自动装载,另外一种是设置重复运行的次数。
2. 功能实现
2.1 头文件定义
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "time.h"
//所用的参数类型定义
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned long uint32_t;
//超时回调函数,无参数无返回值
typedef void (*TimeoutCb)(void);
//软件定时器属性
typedef struct
{
uint8_t id; //软件定时器ID
uint8_t priority; //软件定时器优先级
uint16_t mode; //BIT15:1表示无限制连续重载,0表示启用重载次数,此时BIT[14:0]表示重载的次数
uint16_t timeout; //定时时间
uint32_t startTimer; //设置软件定时器的起始时间
TimeoutCb timeoutFun; //软件定时的超时回调
} SoftTimer;
//软件定时器节点
typedef struct _SoftTimerNode
{
SoftTimer property;
struct _SoftTimerNode *next;
} SoftTimerNode;
2.2 源文件功能
2.2.1 链表头的定义
//初始化一个软件定时器链表默认指向空
static SoftTimerNode *s_softTimerHead = NULL;
2.2.2 基准定时器的参考
- 在单片机的应用中,单片机中应该有一个基准定时器进行计数,可供外部文件获取计数值。本文中直接从系统中获取一个utc时间,模拟计数值的产生。
- 检测软件定时器是否超时,这里要特别注意溢出的情况,本文中实现没有考虑这个问题,实际应用中根据情况而定。
//获取系统当前UTC时间
uint32_t GetTimerCount(void)
{
time_t utcTimer = 0;
time(&utcTimer);
return utcTimer;
}
//软件定时器是否超时
uint8_t IsSoftTimerTimeout(SoftTimer *softTimer)
{
return ((softTimer->startTimer + softTimer->timeout) <= GetTimerCount());
}
2.2.3 获取软件定时器的个数
- 如果链表是空的话,直接返回0,表示没有定时器。
- 如果链表存在,则返回实际的软件定时器个数。
//获取软件定时器链表的长度
uint32_t GetSoftTimerCount(void)
{
SoftTimerNode *cur = NULL;
uint32_t cnt = 0;
if (s_softTimerHead == NULL)
{
return 0;
}
cur = s_softTimerHead;
//下一个数据不为空
while (cur->next != NULL)
{
cnt++;
cur = cur->next;
}
return 1 + cnt;
}
2.2.4 查找软件定时器的位置
- 主要用于查找固定的定时器ID是否存在于当前链表的位置,如果不存在返回一个-1。
- 如果链表中存在这个名称为ID的软件定时器,则返回当前存在的位置。这个位置从0开始表示,即链表头指向的是第一个数据,此时位置为0。
//查找定时器ID的位置
int SearchSoftTimerById(uint8_t id)
{
SoftTimerNode *cur = NULL;
int pos = 0;
if (s_softTimerHead == NULL)
{
return -1;
}
cur = s_softTimerHead;
for (;;)
{
if (cur->property.id == id)
{
break;
}
if (cur->next == NULL)
{
pos = -1; //找不到位置
break;
}
cur = cur->next;
pos++;
}
return pos;
}
2.2.5 根据优先级判断应该插入的位置
- 主要用于判断当前优先级应该位于链表的哪个位置,这个位置从0开始表示,即链表头指向的是第一个数据,此时位置为0。
- 链表的排放顺序是按照优先级进行顺序排放,优先级的参数值越小,则插入的位置应该越靠前。
- 如果优先级是相同的,则放在相同优先级结点的最后一个位置。
//获取插入的位置
uint32_t GetInsertPosByPriority(uint16_t priority)
{
SoftTimerNode *cur = NULL;
uint32_t pos = 0;
if (s_softTimerHead == NULL)
{
return pos; //插入在头部
}
cur = s_softTimerHead;
for (;;)
{
//优先级比当前的结点要高
if (priority < cur->property.priority)
{
break;
}
pos++;
if (cur->next == NULL)
{
break;
}
cur = cur->next;
}
return pos;
}
2.2.6 删除一个软件定时器
- 软件ID应该是唯一的,类似于软件定时的名称
//删除软件ID为id的软件定时器
void DeleteSoftTimer(uint8_t id)
{
SoftTimerNode *del = NULL;
SoftTimerNode *cur = NULL;
int pos = 0;
int i = 0;
pos = SearchSoftTimerById(id);
if (pos < 0)
{
//不存在当前ID
return;
}
if (pos == 0)
{
//删除头结点
del = s_softTimerHead;
s_softTimerHead = s_softTimerHead->next;
free(del);
return;
}
cur = s_softTimerHead;
//删除非头部结点
for (i = 0; i < pos - 1; i++)
{
cur = cur->next;
}
del = cur->next;
cur->next = del->next;
free(del);
return;
}
2.2.7 创建一个软件定时器
- 如果当前软件ID是重名的,会将重名的软件定时器删掉并用新创建的替代。
//创建软件定时器
void CreatSoftTimer(SoftTimer *softTimer)
{
SoftTimerNode *prev = NULL;
SoftTimerNode *cur = NULL;
SoftTimerNode *add = NULL;
uint32_t pos = 0;
uint32_t i = 0;
//动态分配空间
add = (SoftTimerNode*)malloc(sizeof(SoftTimerNode));
if (add == NULL)
{
return;
}
//生成结点
memcpy(&add->property, softTimer, sizeof(SoftTimerNode));
add->next = NULL;
//如果当前软件定时器ID存在则会直接删除
DeleteSoftTimer(softTimer->id);
//获取插入的位置
pos = GetInsertPosByPriority(softTimer->priority);
if (pos == 0)
{
add->next = s_softTimerHead;
s_softTimerHead = add;
return;
}
//插入的位置不是头部
cur = s_softTimerHead;
for (i = 0; i < pos - 1; i++)
{
cur = cur->next;
}
add->next = cur->next;
cur->next = add;
return;
}
2.2.8 轮询软件定时器
- 这个接口一般放在主函数中轮询调用,如果检测到计数时间到了,则运行软件定时器的回调函数。
- 如果软件定时器是连续自动重载的,在每次执行回调之后会在下一个超时继续执行。
- 如果软件定时器是设置的重载次数,则在重载次数清0之后,会自动删除该软件定时器。实际运行次数是重载次数+1。即设置的重载次数为0会执行1次。
//运行软件定时器
void RunSoftTimer(void)
{
SoftTimerNode *cur = NULL;
uint32_t reload = 0;
if (s_softTimerHead == NULL)
{
return;
}
cur = s_softTimerHead;
for (;;)
{
if (IsSoftTimerTimeout(&cur->property))
{
if (cur->property.timeoutFun != NULL)
{
cur->property.timeoutFun();
}
if (cur->property.mode & (1 << 15)) //自动重载
{
reload = GetTimerCount();
printf("[%d]+++ soft timer auto reload %d - %d = %d\r\n", cur->property.id,
reload, cur->property.startTimer, reload - cur->property.startTimer);
cur->property.startTimer = reload;
}
else if (cur->property.mode > 0)
{
cur->property.mode--; //限制重载次数
reload = GetTimerCount();
printf("[%d]+++ soft timer remain reload %d, %d - %d = %d\r\n", cur->property.id, cur->property.mode,
reload, cur->property.startTimer, reload - cur->property.startTimer);
cur->property.startTimer = reload;
}
else
{
printf("[%d]--- soft timer auto delete\r\n", cur->property.id);
DeleteSoftTimer(cur->property.id); //删除该结点
}
}
if (cur->next == NULL)
{
break;
}
cur = cur->next;
}
return;
}
3. 功能测试
3.1 打印软件定时器链表信息
//显示软件定时器列表
void ShowSoftTimerList(void)
{
SoftTimerNode *cur = NULL;
uint32_t i = 0;
uint32_t count = 0;
cur = s_softTimerHead;
count = GetSoftTimerCount();
printf("soft timer totol count: %d\r\n", count);
printf("------------------------------------------------------------------------\r\n");
for (i = 0; i < GetSoftTimerCount(); i++)
{
printf("[%d]: addr = %08X, id = %d; priority = %d; mode = %d, timeout = %d, startTimer = %d, next = %08X\r\n", cur->property.id, cur,
cur->property.id, cur->property.priority, cur->property.mode, cur->property.timeout, cur->property.startTimer, cur->next);
cur = cur->next;
}
printf("------------------------------------------------------------------------\r\n");
return;
}
3.2 测试函数
int main()
{
uint8_t i = 0;
uint8_t optMode = 0;
SoftTimer softTimer = {0};
SoftTimer softTimerList[] = {
{0, 4, 4, 4, 0, NULL}, //测试插入一个结点
{0, 7, 7, 7, 0, NULL}, //测试插入同名结点
{2, 3, 3, 3, 0, NULL}, //测试头部插入
{3, 8, 8, 8, 0, NULL}, //测试尾部插入
{4, 7, 7, 7, 0, NULL}, //测试中间插入且为同优先级
{5, 1, 1, 1, 0, NULL}, //优先级最高测试删除头
{6, 9, 9, 9, 0, NULL}, //优先级最低测试删除头
{7, 5, 5, 5, 0, NULL}, //在中间删除一个优先级
};
for (i = 0; i < sizeof(softTimerList) / sizeof(SoftTimer); i++)
{
printf("[%d]+++ Creat soft timer. ", softTimerList[i].id);
softTimerList[i].startTimer = GetTimerCount();
CreatSoftTimer(&softTimerList[i]);
ShowSoftTimerList();
}
for (i = 0; i < 3; i++)
{
printf("[%d]--- Delete soft timer. ", softTimerList[sizeof(softTimerList) / sizeof(SoftTimer) -1 - i].id);
softTimerList[sizeof(softTimerList) / sizeof(SoftTimer) -1 - i].startTimer = GetTimerCount();
DeleteSoftTimer(softTimerList[sizeof(softTimerList) / sizeof(SoftTimer) -1 - i].id);
ShowSoftTimerList();
}
do
{
RunSoftTimer();
} while (GetSoftTimerCount());
printf("All software timers are automatically released!\r\n");
return 0;
}
3.3 测试结果
[0]+++ Creat soft timer. soft timer totol count: 1
------------------------------------------------------------------------
[0]: addr = 001713E0, id = 0; priority = 4; mode = 4, timeout = 4, startTimer = 1653389188, next = 00000000
------------------------------------------------------------------------
[0]+++ Creat soft timer. soft timer totol count: 1
------------------------------------------------------------------------
[0]: addr = 00171410, id = 0; priority = 7; mode = 7, timeout = 7, startTimer = 1653389188, next = 00000000
------------------------------------------------------------------------
[2]+++ Creat soft timer. soft timer totol count: 2
------------------------------------------------------------------------
[2]: addr = 001713E0, id = 2; priority = 3; mode = 3, timeout = 3, startTimer = 1653389188, next = 00171410
[0]: addr = 00171410, id = 0; priority = 7; mode = 7, timeout = 7, startTimer = 1653389188, next = 00000000
------------------------------------------------------------------------
[3]+++ Creat soft timer. soft timer totol count: 3
------------------------------------------------------------------------
[2]: addr = 001713E0, id = 2; priority = 3; mode = 3, timeout = 3, startTimer = 1653389188, next = 00171410
[0]: addr = 00171410, id = 0; priority = 7; mode = 7, timeout = 7, startTimer = 1653389188, next = 00171440
[3]: addr = 00171440, id = 3; priority = 8; mode = 8, timeout = 8, startTimer = 1653389188, next = 00000000
------------------------------------------------------------------------
[4]+++ Creat soft timer. soft timer totol count: 4
------------------------------------------------------------------------
[2]: addr = 001713E0, id = 2; priority = 3; mode = 3, timeout = 3, startTimer = 1653389188, next = 00171410
[0]: addr = 00171410, id = 0; priority = 7; mode = 7, timeout = 7, startTimer = 1653389188, next = 00171470
[4]: addr = 00171470, id = 4; priority = 7; mode = 7, timeout = 7, startTimer = 1653389188, next = 00171440
[3]: addr = 00171440, id = 3; priority = 8; mode = 8, timeout = 8, startTimer = 1653389188, next = 00000000
------------------------------------------------------------------------
[5]+++ Creat soft timer. soft timer totol count: 5
------------------------------------------------------------------------
[5]: addr = 001714A0, id = 5; priority = 1; mode = 1, timeout = 1, startTimer = 1653389188, next = 001713E0
[2]: addr = 001713E0, id = 2; priority = 3; mode = 3, timeout = 3, startTimer = 1653389188, next = 00171410
[0]: addr = 00171410, id = 0; priority = 7; mode = 7, timeout = 7, startTimer = 1653389188, next = 00171470
[4]: addr = 00171470, id = 4; priority = 7; mode = 7, timeout = 7, startTimer = 1653389188, next = 00171440
[3]: addr = 00171440, id = 3; priority = 8; mode = 8, timeout = 8, startTimer = 1653389188, next = 00000000
------------------------------------------------------------------------
[6]+++ Creat soft timer. soft timer totol count: 6
------------------------------------------------------------------------
[5]: addr = 001714A0, id = 5; priority = 1; mode = 1, timeout = 1, startTimer = 1653389188, next = 001713E0
[2]: addr = 001713E0, id = 2; priority = 3; mode = 3, timeout = 3, startTimer = 1653389188, next = 00171410
[0]: addr = 00171410, id = 0; priority = 7; mode = 7, timeout = 7, startTimer = 1653389188, next = 00171470
[4]: addr = 00171470, id = 4; priority = 7; mode = 7, timeout = 7, startTimer = 1653389188, next = 00171440
[3]: addr = 00171440, id = 3; priority = 8; mode = 8, timeout = 8, startTimer = 1653389188, next = 001714D0
[6]: addr = 001714D0, id = 6; priority = 9; mode = 9, timeout = 9, startTimer = 1653389188, next = 00000000
------------------------------------------------------------------------
[7]+++ Creat soft timer. soft timer totol count: 7
------------------------------------------------------------------------
[5]: addr = 001714A0, id = 5; priority = 1; mode = 1, timeout = 1, startTimer = 1653389188, next = 001713E0
[2]: addr = 001713E0, id = 2; priority = 3; mode = 3, timeout = 3, startTimer = 1653389188, next = 00171500
[7]: addr = 00171500, id = 7; priority = 5; mode = 5, timeout = 5, startTimer = 1653389188, next = 00171410
[0]: addr = 00171410, id = 0; priority = 7; mode = 7, timeout = 7, startTimer = 1653389188, next = 00171470
[4]: addr = 00171470, id = 4; priority = 7; mode = 7, timeout = 7, startTimer = 1653389188, next = 00171440
[3]: addr = 00171440, id = 3; priority = 8; mode = 8, timeout = 8, startTimer = 1653389188, next = 001714D0
[6]: addr = 001714D0, id = 6; priority = 9; mode = 9, timeout = 9, startTimer = 1653389188, next = 00000000
------------------------------------------------------------------------
[7]--- Delete soft timer. soft timer totol count: 6
------------------------------------------------------------------------
[5]: addr = 001714A0, id = 5; priority = 1; mode = 1, timeout = 1, startTimer = 1653389188, next = 001713E0
[2]: addr = 001713E0, id = 2; priority = 3; mode = 3, timeout = 3, startTimer = 1653389188, next = 00171410
[0]: addr = 00171410, id = 0; priority = 7; mode = 7, timeout = 7, startTimer = 1653389188, next = 00171470
[4]: addr = 00171470, id = 4; priority = 7; mode = 7, timeout = 7, startTimer = 1653389188, next = 00171440
[3]: addr = 00171440, id = 3; priority = 8; mode = 8, timeout = 8, startTimer = 1653389188, next = 001714D0
[6]: addr = 001714D0, id = 6; priority = 9; mode = 9, timeout = 9, startTimer = 1653389188, next = 00000000
------------------------------------------------------------------------
[6]--- Delete soft timer. soft timer totol count: 5
------------------------------------------------------------------------
[5]: addr = 001714A0, id = 5; priority = 1; mode = 1, timeout = 1, startTimer = 1653389188, next = 001713E0
[2]: addr = 001713E0, id = 2; priority = 3; mode = 3, timeout = 3, startTimer = 1653389188, next = 00171410
[0]: addr = 00171410, id = 0; priority = 7; mode = 7, timeout = 7, startTimer = 1653389188, next = 00171470
[4]: addr = 00171470, id = 4; priority = 7; mode = 7, timeout = 7, startTimer = 1653389188, next = 00171440
[3]: addr = 00171440, id = 3; priority = 8; mode = 8, timeout = 8, startTimer = 1653389188, next = 00000000
------------------------------------------------------------------------
[5]--- Delete soft timer. soft timer totol count: 4
------------------------------------------------------------------------
[2]: addr = 001713E0, id = 2; priority = 3; mode = 3, timeout = 3, startTimer = 1653389188, next = 00171410
[0]: addr = 00171410, id = 0; priority = 7; mode = 7, timeout = 7, startTimer = 1653389188, next = 00171470
[4]: addr = 00171470, id = 4; priority = 7; mode = 7, timeout = 7, startTimer = 1653389188, next = 00171440
[3]: addr = 00171440, id = 3; priority = 8; mode = 8, timeout = 8, startTimer = 1653389188, next = 00000000
------------------------------------------------------------------------
[2]+++ soft timer remain reload 2, 1653389191 - 1653389188 = 3
[2]+++ soft timer remain reload 1, 1653389194 - 1653389191 = 3
[4]+++ soft timer remain reload 6, 1653389195 - 1653389188 = 7
[0]+++ soft timer remain reload 6, 1653389195 - 1653389188 = 7
[3]+++ soft timer remain reload 7, 1653389196 - 1653389188 = 8
[2]+++ soft timer remain reload 0, 1653389197 - 1653389194 = 3
[2]--- soft timer auto delete
[0]+++ soft timer remain reload 5, 1653389202 - 1653389195 = 7
[4]+++ soft timer remain reload 5, 1653389202 - 1653389195 = 7
[3]+++ soft timer remain reload 6, 1653389204 - 1653389196 = 8
[0]+++ soft timer remain reload 4, 1653389209 - 1653389202 = 7
[4]+++ soft timer remain reload 4, 1653389209 - 1653389202 = 7
[3]+++ soft timer remain reload 5, 1653389212 - 1653389204 = 8
[4]+++ soft timer remain reload 3, 1653389216 - 1653389209 = 7
[0]+++ soft timer remain reload 3, 1653389216 - 1653389209 = 7
[3]+++ soft timer remain reload 4, 1653389220 - 1653389212 = 8
[0]+++ soft timer remain reload 2, 1653389223 - 1653389216 = 7
[4]+++ soft timer remain reload 2, 1653389223 - 1653389216 = 7
[3]+++ soft timer remain reload 3, 1653389228 - 1653389220 = 8
[0]+++ soft timer remain reload 1, 1653389230 - 1653389223 = 7
[4]+++ soft timer remain reload 1, 1653389230 - 1653389223 = 7
[3]+++ soft timer remain reload 2, 1653389236 - 1653389228 = 8
[0]+++ soft timer remain reload 0, 1653389237 - 1653389230 = 7
[4]+++ soft timer remain reload 0, 1653389237 - 1653389230 = 7
[4]--- soft timer auto delete
[3]+++ soft timer remain reload 1, 1653389244 - 1653389236 = 8
[0]--- soft timer auto delete
[3]+++ soft timer remain reload 0, 1653389252 - 1653389244 = 8
[3]--- soft timer auto delete
All software timers are automatically released!
2022-05-24 李不清的烦恼,总结篇。