基础UCOSIII学习指南
1.OS中的中断服务函数注意:
OSIntEnter();//进入ISR
//用户定义的处理程序
OSIntExit();//退出ISR
2.UCOSIII任务优先级
数值越小,优先级越高
(最低优先级为OS_CFG_PRIO_MAX-1)
3.UCOSIII就绪表
就绪表:优先级位映射表+就绪任务列表
优先级位映射表:用于记录哪个优先级下有任务就绪(按数组元素,元素的位数表示优先级)
就绪任务列表:用于记录每个优先级下所有就绪的任务(按数组元素,元素的类型是结构体包含了数据链表指针,指向任务控制块)
4.UCOSIII任务调度(可剥夺型任务调度)
- 任务调度:中止当前正在运行的任务转而去执行其他任务
- 可剥夺型:当高优先级任务准备就绪,并且此时发生任务调度,那么高优先级任务获得CPU使用权
- 任务调度的实现:使用任务调度器(其实就是个函数)——任务调度器有2种=任务级调度器(OSSched())+中断级调度器(OSIntExit())
- 任务调度点:(任务调度的时间点)或者说(引发/导致任务调度)
1、 释放信号量或者发送消息,也可通过配置相应的参数不发生任务调度。
2、 使用延时函数OSTimeDly()或者OSTimeDlyHMSM()。
3、 任务等待的事情还没发生(等待信号量,消息队列等)。
4、 任务取消等待。
5、 创建任务。
6、 删除任务。
7、 删除一个内核对象。
8、 任务改变自身的优先级或者其他任务的优先级。
9、 任务通过调用OSTaskSuspend()将自身挂起。
10、 任务解挂某个挂起的任务。
11、 退出所有的嵌套中断。
12、 通过OSSchedUnlock()给调度器解锁。
13、 任务调用OSSchedRoundRobinYield()放弃其执行时间片。
14、 用户调用OSSched()。 - 任务调度器的上锁和解锁:
有一些代码的执行过程是不能被打断的。此时我们就可以使用函数OSSchedLock()对调度器加锁,当我们想要恢复任务调度的时候就可以使用函数OSSchedUnlock()给已经上锁的任务调度器解锁。 - 时间片轮转调度:(时间片轮转调度器(其实就是个函数)OS_SchedRoundRobin())
UCOSIII允许一个优先级下有多个任务,每个任务可以执行指定的时间(时间片),然后轮到下一个任务,这个过程就是时间片轮转调度,当一个任务不想在运行的时候就可以放弃其时间片。
- UCOSIII任务切换
当UCOSIII需要切换到另外一个任务时,它将保存当前任务的现场到当前任务的堆栈中,主要是CPU寄存器值,然后恢复新的现场并且执行新的任务,这个过程就是任务切换。
任务切换的2种方式:任务级切换(OSCtxSw())+中断级切换(OSIntCtxSw())
注意:这2个函数都是汇编写的
6.任务创建
void OSTaskCreate (OS_TCB *p_tcb,//实参为(要创建的任务控制块地址)
CPU_CHAR *p_name,//(一般为任务的函数名称)
OS_TASK_PTR p_task,//(任务函数)
void *p_arg,//(任务被创建后给任务的参数,一般不用)
OS_PRIO prio,//(任务的优先级)
CPU_STK *p_stk_base,//(任务堆栈的基地址,一般为数组地址)
CPU_STK_SIZE stk_limit,//(任务堆栈的深度,做一个堆栈溢出限制)
CPU_STK_SIZE stk_size,//(任务堆栈的大小,或者是元素个数)
OS_MSG_QTY q_size,//(允许发送个任务的最大消息数)
OS_TICK time_quanta,//(设定任务占几个时间轮转片)
void *p_ext,//(供用户拓展的存储区)
OS_OPT opt,//(任务选项)
OS_ERR *p_err)//(存放该函数错误时的返回值)
任务选项可选项:
OS_OPT_TASK_NONE 未选择任何选项
OS_OPT_TASK_STK_CHK 允许对任务进行堆栈检查
OS_OPT_TASK_STK_CLR 创建任务时清除堆栈
OS_OPT_TASK_SAVE_FP 如果CPU有浮点寄存器,请在上下文切换期间保存它们
OS_OPT_TASK_NO_TLS 如果调用方不希望或不需要任务的TLS(线程本地存储)支持。如果不包含此选项,则默认情况下将支持TLS。
任务当前状态为(就绪态)
7.任务删除
void OSTaskDel (OS_TCB *p_tcb,//(要删除任务的任务控制块)
OS_ERR *p_err)//(存放该函数错误时的返回值)
任务当前状态变为(休眠态)
8.任务挂起
void OSTaskSuspend (OS_TCB *p_tcb, //(要挂起任务的任务控制块)
OS_ERR *p_err) //(存放该函数错误时的返回值)
任务当前状态变为(等待态)
9.任务恢复
void OSTaskResume (OS_TCB *p_tcb, //(要恢复任务的任务控制块)
OS_ERR *p_err) //(存放该函数错误时的返回值)
任务当前状态变为(就绪态)
10.时间片轮转调度器配置
void OSSchedRoundRobinCfg (CPU_BOOLEAN en,//(确定是否启用循环或不启用)
OS_TICK dflt_time_quanta,//(配置时间片长度)
OS_ERR *p_err)// (存放该函数错误时的返回值)
注意:使用时间片轮转调度器要先将OS_CFG_SCHED_ROUND_ROBIN_EN=1;
后操作OSSchedRoundRobinCfg()配置好使能,并确定时间片时间长度
11.放弃该任务的本次时间片函数
void OSSchedRoundRobinYield (OS_ERR *p_err) // (存放该函数错误时的返回值)
12.UCOSIII的5个系统任务+钩子函数
-
空闲任务(ucosiii必备任务+ucosiii创建的第一个任务):
a) 优先级:OS_CFG_PRIO_MAX-1(优先级最低)
b) 注意:不能让空闲任务进入等待态 -
时钟节拍任务(ucosiii必备任务):时钟节拍任务用来跟踪任务延时和任务等待超时
a) 任务函数为:OS_TickTask()
b) 优先级:OS_CFG_TICK_TASK_PRIO=1(优先级很高) -
统计任务(ucosiii用户自选任务)
a) 统计CPU的使用率、各个任务的CPU使用率、各任务的堆栈使用情况
b) 在OSInit(&err);初始化中创建统计任务
c) 统计任务初始化函数:OS_StatTaskInit ()
d) 用户自己使能:OS_CFG_STAT_TASK_EN=1(预编译创建统计任务)
e) 优先级:OS_CFG_PRIO_MAX-2(优先级比空闲任务高一点) -
定时任务(ucosiii用户自选任务)
a) 提供软件定时器功能
b) 在OSInit(&err);初始化中创建定时任务
c) 定时任务初始化函数:OS_TmrInit()
d) 用户自己使能:OS_CFG_TMR_EN=1(预编译创建定时任务)
e) 优先级:OS_CFG_TMR_TASK_PRIO=2(优先级较高) -
中断服务管理任务(ucosiii用户自选任务)
a) 当ISR(中断服务函数)调用UCOSIII提供的“post(也就是“发送”)”函数时,要发送的数据和发送的目的地都会存入一个特别的缓冲队列中,当所有嵌套的ISR都执行完成以后UCOSIII会做任务切换,运行中断服务管理任务,该任务会把缓存队列中存放的信息重发给相应的任务。好处——(减少中断关闭的时间)
b) 否则,在ISR中还需要把任务从等待列表中删除,并把任务放入就绪表,以及做一些其他的耗时操作。
c) 在OSInit(&err);初始化中创建中断服务管理任务
d) 中断服务管理任务初始化函数:OS_IntQTaskInit()
e) 用户自己使能:OS_CFG_ISR_POST_DEFERRED_EN=1(预编译创建中断服务管理任务)
f) 优先级:0(优先级最高,不可更改) -
钩子函数:
1、OSIdleTaskHook(),空闲任务调用这个函数,可以用来让CPU进入低功耗模式
2、OSInitHook(),系统初始化函数OSInit()调用此函数。
3、OSStatTaskHook(),统计任务每秒中都会调用这个函数,此函数允许你向统计任务中添加自己的应用函数。
4、OSTaskCreateHook(),任务创建的钩子函数。
5、OSTaskDelHook(),任务删除的钩子函数。
6、OSTaskReturnHook(),任务意外返回时调用的钩子函数,比如删除某个任务
7、OSTaskSwHook(),任务切换时候调用的钩子函数。
8、OSTimeTickHook(),滴答定时器调用的钩子函数。
13.UCOSIII中断处理
UCOSIII支持中断嵌套(高优先级中断可打断低优先级中断)
注意:最大支持250级中断嵌套
使用OSIntNestingCtr来记录嵌套的次数
中断服务程序的函数:
OSIntEnter();//进入中断(主要记录中断嵌套次数)
OSIntExit();//退出中断(中断级调度器)
14.临界段代码保护
保护的目的:那些必须完整连续运行,不可被打断的代码段。就是临界段代码保护的对象与目的
保护的方式:2种方式:关中断OR调度器上锁(通过OS_CFG_ISR_POST_DEFERRED_EN选择方式)
OS_CFG_ISR_POST_DEFERRED_EN=0(采用关中断的方式保护临界段代码)
OS_CFG_ISR_POST_DEFERRED_EN=1(采用调度器上锁的方式保护临界段代码)
具体保护的形式:采用函数的方式,具体如下
OS_CRITICAL_ENTER(); //进入临界区
OS_CRITICAL_EXIT(); //退出临界区
CPU_INT_DIS();//(关闭中断)通过寄存器PRIMASK实现全局中断的开关(支持嵌套调用)
CPU_INT_EN();//(打开中断)通过寄存器PRIMASK实现全局中断的开关(支持嵌套调用)
CPU_CRITICAL_ENTER();//(进入临界段)
CPU_CRITICAL_EXIT();//(退出临界段)
15.时间管理
任务延时:
void OSTimeDly (OS_TICK dly,//(指定延时时间节拍数)
OS_OPT opt,//(延迟方式选项)
OS_ERR *p_err) // (存放该函数错误时的返回值)
方式选项:
OS_OPT_TIME_DLY 相对模式
OS_OPT_TIME_TIMEOUT 相对模式(和OS_OPT_TIME_DLY一样)
OS_OPT_TIME_MATCH 绝对模式
OS_OPT_TIME_PERIODIC 周期模式
void OSTimeDlyHMSM (CPU_INT16U hours, //(指定延时小时数)
CPU_INT16U minutes, //(指定延时分钟数)
CPU_INT16U seconds, //(指定延时秒数)
CPU_INT32U milli, //(指定延时毫秒数)
OS_OPT opt, //(延迟方式选项)
OS_ERR *p_err) //(存放该函数错误时的返回值)
方式选项:
OS_OPT_TIME_HMSM_STRICT 只允许
hours (0…99)
minutes (0…59)
seconds (0…59)
milliseconds (0…999)
OS_OPT_TIME_HMSM_NON_STRICT 只允许
hours (0…999)
minutes (0…9999)
seconds (0…65535)
milliseconds (0…4294967295)
取消任务延时:
void OSTimeDlyResume (OS_TCB *p_tcb,//(需要取消延时任务的任务控制块)
OS_ERR *p_err) //(存放该函数错误时的返回值)
获取和设置系统时间:
UCOSIII定义了一个CPU_INT32U类型的全局变量OSTickCtr来记录系统时钟节拍数,在调用OSInit()时被初始化为0,以后每发生1个时钟节拍,OSTickCtr加1。
OSTimeSet()允许用户改变当前时钟节拍计数器的值,慎用
OSTimeGet()用来获取当前时钟节拍计数器的值
16.软件定时器
定时器本质是递减计数器,当计数器减到零时可以触发某种动作的执行,这个动作通过回调函数来实现
通过OS_CFG_TMR_EN=1来使能软件定时器
通过OS_CFG_TMR_TASK_RATE_HZ来设置软件定时器的时间分辨率
(1/ OS_CFG_TMR_TASK_RATE_HZ)
注意!一定要避免在回调函数中使用阻塞调用或者可以阻塞或删除定时器任务的函数。
函数名 作用
OSTmrCreate() 创建定时器并制定运行模式
OSTmrDel() 删除定时器
OSTmrRemainGet() 获取定时器的剩余时间
OSTmrStart() 启动定时器计数
OSTmrStateGet() 获取当前定时器状态
OSTmrStop() 停止计数器倒计时
软件定时器可设置为:单次定时器+周期定时器
周期定时器分为:周期模式(无初始延时)+周期模式(有初始延迟)
17.信号量
信号量用于控制对共享资源的保护
但是现在基本用来做任务同步用
信号量类型:
二进制信号量(只有0和1)
计数型信号量(取值由OS_SEM_CTR定义)
函数名 作用
OSSemCreate() 建立一个信号量
OSSemDel() 删除一个信号量
OSSemPend() 等待一个信号量
OSSemPendAbrot() 取消等待
OSSemPost() 释放或者发出一个信号量
OSSemSet() 强制设置一个信号量的值
(1)创建信号量(可以是二进制信号量,也可以是计数型信号量)
void OSSemCreate (OS_SEM *p_sem,//(加入用户定义的信号量控制块)
CPU_CHAR *p_name,//(加入用户定义的信号量名称)
OS_SEM_CTR cnt,//(1为二进制信号量;n>1时为计数型信号量)
OS_ERR *p_err)//(存放该函数错误时的返回值)
注意:函数的第三个参数,cnt为信号量的个数。
cnt=1时创建的是二进制信号量。
cnt=n&&n>1时创建的时计数型信号量,n为计数型信号量的个数。
(2)等待信号量()
OS_SEM_CTR OSSemPend (OS_SEM *p_sem,//(要等待的信号量控制块)
OS_TICK timeout,//(等待超时时间,时间为节拍数)
OS_OPT opt,//(选择是否堵塞任务)
CPU_TS *p_ts,//(存储时间戳)
OS_ERR *p_err)//(存放该函数错误时的返回值)
函数的第二个参数:timeout
Timeout=0,代表无限时等待;(必须选择OS_OPT_PEND_BLOCKING)
Timeout=n&&n>0,那么n就为等待的超时时间;
函数的第三个参数:opt
Opt=OS_OPT_PEND_BLOCKING;不能立即获得信号量,就选择堵塞任务,继续等待信号量
Opt= OS_OPT_PEND_NON_BLOCKING;不能立即获得信号量,选择不堵塞任务,不继续等待信号量
返回值:信号量的当前计数值
(3)删除信号量
OS_OBJ_QTY OSSemDel ( OS_SEM *p_sem, //(要等待的信号量控制块)
OS_OPT opt,//(选择删除信号量的方式)
OS_ERR *p_err)//(存放该函数错误时的返回值)
函数的第二个函数:opt
Opt= OS_OPT_DEL_NO_PEND;//如果没有任务等待信号量,才删除信号量
Opt= OS_OPT_DEL_ALWAYS;//直接删除信号量
返回值:
返回值=0,没有任务等待信号量,或者有错误
返回值>0,信号量被删除前等待的任务数
(4)取消等待信号量(中止任务对信号量的等待)
OS_OBJ_QTY OSSemPendAbort (OS_SEM *p_sem, //(要等待的信号量控制块)
OS_OPT opt,//(中止信号量的选项)
OS_ERR *p_err)//(存放该函数错误时的返回值)
函数的第二个函数:opt
Opt= OS_OPT_PEND_ABORT_1;//只中止信号量等待列表中的最高优先级任务
Opt= OS_OPT_PEND_ABORT_ALL;//中止信号量等待列表中的所有
(5)发布信号量(释放信号量)
OS_SEM_CTR OSSemPost (OS_SEM *p_sem, //(要等待的信号量控制块)
OS_OPT opt,//(发布的方式)
OS_ERR *p_err) //(存放该函数错误时的返回值)
函数的第二个函数:opt
Opt= OS_OPT_POST_1;//发布等待该信号量中最高优先级的任务
Opt= OS_OPT_POST_ALL;//发布等待该信号量的所有任务
18.互斥信号量
互斥信号量的目的:为了防止在实时系统中出现优先级反转,所有采用互斥信号量。
函数名 作用
OSMutexCreate() 建立一个互斥信号量
OSMutexDel() 删除一个互斥信号量
OSMutexPend() 等待一个互斥信号量
OSMutexPendAbrot() 取消等待
OSMutexPost() 释放或者发布一个互斥信号量
19.任务内嵌信号量
在UCOSIII中每个任务都有自己的内嵌的信号量
函数名 作用
OSTaskSemPend() 等待一个任务信号量
OSTaskSemPendAbort() 取消等待任务信号量
OSTaskSemPost() 发布任务信号量
OSTaskSemSet() 强行设置任务信号量计数
20.UCOSIII消息传递
消息传递的目的:是为了实现一个任务和另一个任务或者(几个任务)进行交流(或者说消息传递)——称之为任务间通信
UCOSIII中任务间通信的2种途径:全局变量+发布消息
发布消息:消息通过消息队列作为中介发布给任务
UCOSIII种消息的组成部分:
指向数据的指针+数据的长度+记录消息发布的时间戳
注意:发送的消息需要保持可见性(最好是采用动态内存管理,或者全局变量、全局数组、全局数据结构、全局函数,或者在局部前定义成静态)
消息队列分类:外建消息队列+任务内建消息队列
21.UCOSIII事件标志组
事件标志组的作用:完成任务同步(信号量也能完成任务同步)
事件标志组能够解决一个任务和多个事件之间的同步
事件标志组与任务间的同步机制:
或同步:当任何一个事件发生,任务都被同步
与同步:要所有的事件都发生,任务才被同步
(主要简单来说就是看bit位,为1还是为0)
22.同时等待多个内核对象
单个对象如:信号量、互斥信号量、消息队列、事件标志组
UCOSIII允许任务同时等待多个信号量和多个消息队列(不支持同时等待多个事件标志组或互斥信号量)
注意:一个任务可等待任意数量的信号量和消息队列,第一个信号量或消息队列的发布会导致该任务进入就绪态
OS_OBJ_QTY OSPendMulti (OS_PEND_DATA *p_pend_data_tbl,
OS_OBJ_QTY tbl_size,
OS_TICK timeout,
OS_OPT opt,
OS_ERR *p_err)
23.内存管理
近日做了《例14-1 UCOSIII 内存管理》实验,结合之前做的几个实验,对正点原子及 UCOSIII的内存管理有了一点认识:
- 便利性:正如文档所述,UCOSIII的内存管理功能比较粗糙,一个内存分区包含若干个固定大小的存储块。原子哥的内存管理比较灵活,可以申请任意大小的存储块。
- 实时性:UCOSIII会把第一个空闲的存储块分配给应用程序,执行时间很快且时间固定。原子哥的内存管理执行时间,会根据申请内存数量的增大而延长。
比如在72M时钟下,OSMemGet()的执行时间为1.3us。 mymalloc()申请10个字节时为3.2us,申请500字节时为13us,申请1K字节时为23us。 - 互斥保护:UCOSIII的内存管理已经加入了互斥保护。原子哥的内存管理没有互斥保护。
- 具体应用:①.当在中断及任务中分配内存时,建议使用UCOSIII的内存管理,执行时间固定而迅速,不会影响到系统的实时性。
②.当只在任务中分配内存时,建议使用原子哥的内存管理,并加入互斥保护(锁定调度器,互斥信号量)