写在前面:
由于时间的不足与学习的碎片化,写博客变得有些奢侈。
但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈。
既然如此
不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录,记录笔者认为最通俗、最有帮助的资料,并尽量总结几句话指明本质,以便于日后搜索起来更加容易。
标题的结构如下:"类型":"知识点"------"简短的解释"
部分内容由于保密协议无法上传。
2024.03.13
- 二十九、UCOSIII:基于时基列表的时延操作
-
- 1、配置时钟中断时间
- 2、创建任务
- 3、任务放置到就绪列表中,并优先级排队
- 4、将任务插入时基列表
-
- [1. 确认时延](#1. 确认时延)
- 2.对任务进行排序
- [3. 确认插入时基列表哪个成员](#3. 确认插入时基列表哪个成员)
- [4. 对就绪列表的操作](#4. 对就绪列表的操作)
二十九、UCOSIII:基于时基列表的时延操作
1、配置时钟中断时间
c
/* 配置SysTick 10ms 中断一次 */
OS_CPU_SysTickInit (10);
在中断触发时运行OSTimeTick()
函数
c
/* SysTick 中断服务函数 */
void SysTick_Handler(void)
{
OSTimeTick();
}
OSTimeTick()
函数定义如下:
c
void OSTimeTick (void)
{
/* 更新时基列表 */
OS_TickListUpdate();
/* 任务调度 */
OSSched();
}
很明显,系统需要10ms
一个时钟周期,每一个时钟周期更新一次时基列表
2、创建任务
创建任务需要使用 OSTaskCreate()
函数,这部分和之前相同,不再概述。
任务指针格式如下:
c
struct os_tcb {
CPU_STK *StkPtr;
CPU_STK_SIZE StkSize;
/* 任务延时周期个数 */
OS_TICK TaskDelayTicks;
/* 任务优先级 */
OS_PRIO Prio;
/* 就绪列表双向链表的下一个指针 */
OS_TCB *NextPtr;
/* 就绪列表双向链表的前一个指针 */
OS_TCB *PrevPtr;
/* 时基列表相关字段 */
OS_TCB *TickNextPtr;
OS_TCB *TickPrevPtr;
OS_TICK_SPOKE *TickSpokePtr;
OS_TICK TickCtrMatch;
OS_TICK TickRemain;
};
3、任务放置到就绪列表中,并优先级排队
任务创建好之后,会放到就绪列表中,并在优先级列表对应值中设为1
。
上述在之前章节已整理,本次不再概述。
4、将任务插入时基列表
当任务需要延时时,使用OS_TickListInsert()
函数将任务插入时基列表。
c
/* 将一个任务插入时基列表,根据延时时间的大小升序排列 */
void OS_TickListInsert (OS_TCB *p_tcb,OS_TICK time)
时基列表OSCfg_TickWheel[]
有OS_CFG_TICK_WHEEL_SIZE
个成员。
OS_CFG_TICK_WHEEL_SIZE
的推 荐值为任务数/4
,不推荐使用偶数。如果算出来是偶数,则加1变成质数,实际上质数是一个很好的选择。
时基列表OSCfg_TickWheel[]
每个成员有三个值。
c
typedefstruct os_tick_spoke OS_TICK_SPOKE;
//在μC/OS-III中,内核对象的数据类型都会用大写字母重新定义。
struct os_tick_spoke {
OS_TCB *FirstPtr;
//每个成员都包含一条单向链表, 被插入该条链表的TCB会按照延时时间做升序排列。
//FirstPtr用于指向这条单向链表的第一个节点。
OS_OBJ_QTY NbrEntries;
//NbrEntries表示该条单向链表当前有多少个节点。
OS_OBJ_QTY NbrEntriesMax;
//NbrEntriesMax记录该条单向链表最多的时候有多少个节点, 在增加节点的时候会刷新,在删除节点的时候不刷新。
};
1. 确认时延
当任务需要插入到时基列表中时,首先需要确认需要时延几个周期,即TickRemain
的值。
然后确认OSTickCtr
的值,将TickRemain
和OSTickCtr
相加得到TickCtrMatch
。
OSTickCtr
是一个全局变量, 记录的是系统自启动以来或者自上次复位以来经过了多少个SysTick
周期。
OSTickCtr
的值每经过一个SysTick
周期其值就加一
2.对任务进行排序
众所周知,任务随时都有可能加入到时基列表中,那么怎么能高效的将各个任务按时延长短进行排序,并快速取用呢?
答案就在TickCtrMatch
这个变量上,TickCtrMatch
变量是系统设计时的一个巧思妙想。
当一个任务需要插入时基列表中时,我们先获得TickCtrMatch
值。
因为TickCtrMatch
值是TickRemain
和OSTickCtr
的和,即当前时间+时延时间
,得到的值就是时延结束的绝对时间
。
这样就能将所有任务按照 实验结束的绝对时间
进行排序。
3. 确认插入时基列表哪个成员
用TickCtrMatch
对OS_CFG_TICK_WHEEL_SIZE
进行求余,即对成员总数进行求余,得到的数就是存放任务的下标。
这样做是为了对任务进行分类。
假如成员总数为10
(但实际上应该是个质数),当前系统时间为502
。
502
对10
求余得2
,那么在任务时延结束绝对时间TickCtrMatch
为502,513,515,522
的几个任务里只有502,522
余数为2
。我们不需要跟其他数比,只需要在余数为2
的任务里找就行,这样就可以极大的减少寻找时间
同时将余数相同的任务按顺序排列,那么当系统时间为502
,任务时延结束的绝对时间为522
,那么之后的任务就不需要再找了,因为一定会比522
大。
4. 对就绪列表的操作
当任务加入到时基列表中后,就需要从就绪列表中删除。
时基列表OSCfg_TickWheel[]
该成员的NbrEntries
加1。
当任务任务时延结束绝对时间TickCtrMatch
等于系统时间OSTickCtr
时就把任务加入到就绪列表,并从时基列表中删除。
时基列表OSCfg_TickWheel[]
该成员的NbrEntries
减1。
每个时间循环都要确定NbrEntriesMax
大于等于NbrEntries