μC/OS:微控制器操作系统。其性能特点:
(1)开源
(2)可移植
(3)可固化
(4)可裁剪
(5)…
创建任务: O S T a s k C r e a t e ( ) 、 O S T a s k C r e a t e E x t ( ) OSTaskCreate()、OSTaskCreateExt() OSTaskCreate()、OSTaskCreateExt()
主函数是无限循环。
删除任务: O S T a s k D e l ( ) OSTaskDel() OSTaskDel(),只是内核看不到了而已
创建空闲任务: O S T a s k I d l e ( ) OSTaskIdle() OSTaskIdle(),任务优先级最低
64个任务,优先级从高到低: [ 0 , 1 , . . . , O S _ L O W E S T _ P R I O ] [0,1,...,OS\_LOWEST\_PRIO] [0,1,...,OS_LOWEST_PRIO],也是任务的标识号,其中 O S _ L O W E S T _ P R I O OS\_LOWEST\_PRIO OS_LOWEST_PRIO的值为63。用户可用的只有56个任务。
任务控制块(TCB),用来存放各种管理信息。
TCB链表:空闲链表、使用链表(双向)。
任务状态:
(1)休眠:内核不可见
(2)就绪
(3)运行
(4)等待
(5)中断:中断服务程序ISR接管了CPU
状态转换图:
任务就绪表
任务的优先级数值可以写成8位二进制的形式,因为不会超过63,所以最高的两位用不到。
⊠ ⊠ { □ □ □ } { □ □ □ } \boxtimes\boxtimes\{\Box\Box\Box\}\{\Box\Box\Box\} ⊠⊠{□□□}{□□□}
任务就绪表由8位的组号 O s R d y G r p OsRdyGrp OsRdyGrp和就绪表数组 O s R d y T b l [ ] OsRdyTbl[\ ] OsRdyTbl[ ]组成。其中,最低的三优先级数值的低三位表示组内编号,中间三位表示组号。
很容易得出如下的推导,
{ p r i o / 8 = 组 号 p r i o % 8 = 组 内 编 号 组 号 × 8 + 组 内 编 号 = p r i o \begin{cases} prio/8=组号 \\[2ex] prio\%8=组内编号 \\[2ex] 组号\times 8+组内编号=prio \end{cases} ⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧prio/8=组号prio%8=组内编号组号×8+组内编号=prio
(1)任务进入就绪态的操作
O s R d y G r p ∣ = O s M a p T b l [ p r i o ≫ 3 ] O s R d y T b l [ p r i o ≫ 3 ] ∣ = O s M a p T b l [ p r i o & 0 x 07 ] OsRdyGrp\ |=OsMapTbl[prio\gg3] \\[2ex] OsRdyTbl[prio\gg3]\ |=OsMapTbl[prio\ \&\ 0x07] OsRdyGrp ∣=OsMapTbl[prio≫3]OsRdyTbl[prio≫3] ∣=OsMapTbl[prio & 0x07]
(2)任务脱离就绪态的操作
r e t = ( O s R d y T b l [ p r i o ≫ 3 ] & = ! O s M a p T b l [ p r i o & 0 x 07 ] ) i f ( r e t = = 0 ) { O s R d y G r p & = ! O s M a p T b l [ p r i o ≫ 3 ] } ret=(OsRdyTbl[prio\gg3]\ \&=!OsMapTbl[prio\ \&\ 0x07]) \\[2ex] if(ret==0)\ \\[1ex] \ \ \ \ \{OsRdyGrp\ \&=!OsMapTbl[prio\gg3] \} ret=(OsRdyTbl[prio≫3] &=!OsMapTbl[prio & 0x07])if(ret==0) {OsRdyGrp &=!OsMapTbl[prio≫3]}
任务的调度
可抢占实施多任务操作系统,每次都运行当前就绪队列中优先级最高的任务,一般使用 O S S c h e d ( ) OSSched() OSSched()函数。
y = O s U n M a p T b l [ O s R d y G r p ] x = O s U n M a p T b l [ O s R d y T b l [ y ] ] p r i o = ( y ≪ 3 ) + x y=OsUnMapTbl[OsRdyGrp]\\[2ex] x=OsUnMapTbl[OsRdyTbl[y]]\\[2ex] prio=(y\ll3)+x y=OsUnMapTbl[OsRdyGrp]x=OsUnMapTbl[OsRdyTbl[y]]prio=(y≪3)+x
任务切换: O S _ T A S K _ S W ( ) OS\_TASK\_SW() OS_TASK_SW()
(1)保护现场(当前)
(2)恢复新任务的现场
(3)执行中断返回指令
(4)执行新任务
操作 | 函数 |
---|---|
创建任务 | O S T a s k C r e a t e ( ) OSTaskCreate() OSTaskCreate() |
申请堆栈空间 | O S _ S T K x x x [ ] OS\_STK\ \ xxx[\ ] OS_STK xxx[ ]或 m a l l o c ( ) malloc() malloc() |
删除任务 | O S T a s k D e l ( ) OSTaskDel() OSTaskDel() |
改变优先级 | O S T a s k C h a n g e P r i o ( ) OSTaskChangePrio() OSTaskChangePrio() |
任务查询 | O S T a s k Q u e r y ( ) OSTaskQuery() OSTaskQuery() |
挂起 | O S T a s k S u s p e n d ( ) OSTaskSuspend() OSTaskSuspend() |
恢复 | O S T a s k R e s u m e ( ) OSTaskResume() OSTaskResume() |
CPU响应中断的条件:
(1)至少有一个中断源
(2)系统允许中断,且未屏蔽此信号
进入中断服务程序(ISR)的过程:
(1)保存CPU寄存器的值
(2) O s I n t E n t e r ( ) OsIntEnter() OsIntEnter()
(3)中断服务
(4) O s I n t E x i t ( ) OsIntExit() OsIntExit()
(5)恢复CPU寄存器的值
(6)执行中断返回指令
时钟节拍也是一种特殊的中断。
在 O S S t a r t ( ) OSStart() OSStart()之后再开启时钟节拍器。
void main(void)
{
OSInit(); /* 初始化uC/OS-II*/
/* 应用程序初始化代码... */
/* 调用OSTaskCreate()创建至少一个任务*/
OSStart(); /* 开始多任务调度 */
允许时钟节拍中断;
}
通过控制事件控制块(ECB),控制任务的通信和同步。
等待任务列表,操作同任务就绪表。
同步与互斥,通过开关中断来实现:
3种开关中断的方法:
(1)执行 E N T E R ( ) ENTER() ENTER()关中断,执行 E X I T ( ) EXIT() EXIT()开中断
(2) E N T E R ( ) ENTER() ENTER()先在栈中保存当前中断状态,然后关中断, E X I T ( ) EXIT() EXIT()从栈中弹出
(3)在局部变量中保存中断状态
操作 | 函数 |
---|---|
创建信号量 | O S S e m C r e a t e ( ) OSSemCreate() OSSemCreate() |
P操作 | O S S e m P e n d ( ) OSSemPend() OSSemPend() |
V操作 | O S S e m P o s t ( ) OSSemPost() OSSemPost() |
查询 | O S S e m Q u e r y ( ) OSSemQuery() OSSemQuery() |
任务的通信:
内存管理是对堆进行管理。
采用(可变大小)固定分区的方式 ⇒ \Rightarrow ⇒解决了内存碎片。
操作 | 函数 |
---|---|
创建分区 | O S M e m C r e a t e ( ) OSMemCreate() OSMemCreate() |
分配内存块 | O S M e m G e t ( ) OSMemGet() OSMemGet(),为了防止将同一个块分配给多个任务,必须使用信号量PV操作进行加锁 |
释放内存块 | O S M e m P u t ( ) OSMemPut() OSMemPut() |