第五章 实时操作系统μC/OS-II
1、μC/OS-II概述
μC/OS:微控制器操作系统。其性能特点:
(1)开源
(2)可移植
(3)可固化
(4)可裁剪
(5)…
2、任务管理
创建任务:
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() |
3、中断和时间管理
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(); /* 开始多任务调度 */
允许时钟节拍中断;
}
4、通信与同步
通过控制事件控制块(ECB),控制任务的通信和同步。
等待任务列表,操作同任务就绪表。
同步与互斥,通过开关中断来实现:
- 临界区
- 信号量
- P操作: O S _ E N T E R _ C R I T I C A L ( ) → OS\_ENTER\_CRITICAL()\rightarrow OS_ENTER_CRITICAL()→关中断
- V操作: O S _ E X I T _ C R I T I C A L ( ) → OS\_EXIT\_CRITICAL()\rightarrow OS_EXIT_CRITICAL()→开中断
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() |
任务的通信:
- 低级通信方式
- 信号量
- 高级通信方式
- 共享内存
- 邮箱(Mbox)
- 消息队列(Q)
5、存储管理
内存管理是对堆进行管理。
采用(可变大小)固定分区的方式
⇒
\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() |