一、 任务管理
任务控制块OS_TCB中包含了任务栈顶,栈深,下个任务控制块,任务名,任务栈基地址,时间戳,时间片,信号量数目等信息。
1.任务创建:定义任务控制块--定义任务优先级--定义任务栈大小--定义任务栈数组--定义任务函数
OSTaskCreate()函数创建任务。
2. 任务删除:OSTaskDel();
3. 任务调度:OSSched();延时函数中包含任务调度,运行任务调度函数时,自动切换至当前就绪的最高优先级的任务。
3. 任务挂起:OS_TaskSuspend();
4. 任务恢复:OS_TaskResume();
二、临界区
临界区保护的代码不会被中断或任务调度打断,临界区通过关中断或者调度器加锁的方式实现,此处使用调度器加锁。
使用临界区前必须调用 CPU_SR_ALLOC();
1. 进入临界区 : OS_CRITICAL_ENTER();
2. 退出临界区: OS_CRITICAL_EXIT();
三、时间片轮转调度
同优先级任务可以在创建时设定时间片长度,时间片执行完自动切换至另一任务,任务切换时会自动保护现场。
1. 时间片计算:
cpu_clk_freq = BSP_CPU_ClkFreq(); /* 获得HCLK时钟频率, cpu_clk_freq = 72MHz */
cnts = cpu_clk_freq / (CPU_INT32U)OSCfg_TickRate_Hz; /* cnts = 72KHz */
OS_CPU_SysTickInit(cnts); /* 重装载寄存器值为72K */
系统一个时钟周期为:1/72M
重装载寄存器值递至0所用时间:72K * (1/72M) = 1ms
2. 时间片轮转调度配置:OSSchedRoundRobinCfg();
配置为默认值时,一个时间片的长度为:(1000/10) * 1ms = 100ms;
四、钩子函数
1. 主要应用空闲任务钩子函数让系统进入低功耗
OSIdleTaskHook();
五、软件定时器
定时器数据类型:OS_TMR
主要包含定时器名、回调函数名、传递给函数的数据指针、定时器值、剩余量值、开启前是否延时、重复周期等
1. 定时器创建:
OSTmrCreate ( (OS_TMR *)&tmr1 ,
(CPU_CHAR *)"tmr1",
(OS_TICK )20, //第一个定时器周期,20*100ms
(OS_TICK )10, //第二个周期开始后的每次定时周期,若参数为0 ,则至定时1次,否则为周期性定时
(OS_OPT )OS_OPT_TMR_PERIODIC,
(OS_TMR_CALLBACK_PTR )tmr1_callback, //回调函数,每个周期定时结束,都会调用一次回调函数
(void *)0,
(OS_ERR *)&err);
2. 定时器开启:
定时器创建完成必须开启,否则不会定时,OSTmrStart();
2. 定时器停止:
OSTmrStop();
六、信号量
信号量定义: OS_SEM
1. 信号量创建
OSSemCreate (OS_SEM *p_sem, // 指向定义的信号量
CPU_CHAR *p_name, // 信号量名称
OS_SEM_CTR cnt, // 信号量初始值,以此定义二值信号量或者计数信号量,当用于任务同步,应初始化为0,为计数信号量,当表示共享资源应该初始化为共享资源数量,共享一个存储区的数组,应该初始化为1
OS_ERR *p_err // 返回错误值
);
2. 信号量请求
OSSemPend();
(1)当为计数信号量,cnt -1 ,cnt 减到0时,阻塞任务。
(2)当为二值信号量,cnt -1 ,为0表示共享资源正被占用,信号量还未被释放。
3. 信号量释放
OS_SemPost();
(1)当为计数信号量,cnt +1
(2)当为二值信号量, cnt +1 ,信号量可用,共享资源可用
七、优先级反转
(1) 任务 H 和任务 M 处于挂起状态,等待某一事件的发生,任务 L 正在运行。
(2) 某一时刻任务 L 想要访问共享资源,在此之前它必须先获得对应该资源的信号量。
(3) 任务 L 获得信号量并开始使用该共享资源。
(4) 由于任务 H 优先级高,它等待的事件发生后便剥夺了任务 L 的 CPU 使用权。
(5) 任务 H 开始运行。
(6) 任务 H 运行过程中也要使用任务 L 正在使用着的资源,由于该资源的信号量还被任务
L 占用着,任务 H 只能进入挂起状态,等待任务 L 释放该信号量。
(7) 任务 L 继续运行。
(8) 由于任务 M 的优先级高于任务 L,当任务 M 等待的事件发生后,任务 M 剥夺了任务 L
的 CPU 使用权。
(9) 任务 M 处理该处理的事。
(10) 任务 M 执行完毕后,将 CPU 使用权归还给任务 L。
(11) 任务 L 继续运行。
(12) 最终任务 L 完成所有的工作并释放了信号量,到此为止,由于实时内核知道有个高优
先级的任务在等待这个信号量,故内核做任务切换。
(13) 任务 H 得到该信号量并接着运行。
在这种情况下,任务 H 的优先级实际上降到了任务 L 的优先级水平。因为任务 H 要一直
等待直到任务 L 释放其占用的那个共享资源。由于任务 M 剥夺了任务 L 的 CPU 使用权,使得
任务 H 的情况更加恶化, 这样就相当于任务 M 的优先级高于任务 H,导致优先级反转。
八、互斥信号量
为了避免优先级反转的出现,使用互斥信号量
当低优先级的任务获得信号量,ucosiii会将占用信号量的低优先级任务提高到当前最高优先级,当任务结束,释放信号量可直接被高优先级的任务请求到,以此避免优先级反转为题。
定义:OS_MUTEX
1. 创建互斥信号量
OSMutexCreate();
2. 请求信号量
OSMutexPend();
3. 释放信号量
OSMutexPost();
九、消息队列
消息队列是任务与任务之间交流的媒介
注意点:中断服务程序只能使用 OSQPost()函数
1. 消息队列定义:OS_Q
struct os_q { /* Message Queue */
/* ------------------ GENERIC MEMBERS ------------------ */
OS_OBJ_TYPE Type; /* Should be set to OS_OBJ_TYPE_Q */
CPU_CHAR *NamePtr; /* Pointer to Message Queue Name (NUL terminated ASCII) */
OS_PEND_LIST PendList; /* 正在等待消息队列的任务列表 */
#if OS_CFG_DBG_EN > 0u
OS_Q *DbgPrevPtr;
OS_Q *DbgNextPtr;
CPU_CHAR *DbgNamePtr;
#endif
/* ------------------ SPECIFIC MEMBERS ------------------ */
OS_MSG_Q MsgQ; /* 消息列表 */
};
struct os_msg_q { /* OS_MSG_Q */
OS_MSG *InPtr; /* 指向下一个将被插人队列的消息控制块 */
OS_MSG *OutPtr; /* 指向将被从队列中提取的控制块 */
OS_MSG_QTY NbrEntriesSize; /*队列中最大消息数 */
OS_MSG_QTY NbrEntries; /* 对列中当前消息数 */
OS_MSG_QTY NbrEntriesMax; /* 对别中消息数的峰值 */
};
消息控制块OS_MSG :
struct os_msg { /* MESSAGE CONTROL BLOCK */
OS_MSG *NextPtr; /* Pointer to next message */
void *MsgPtr; /* Actual message */
OS_MSG_SIZE MsgSize; /* Size of the message (in # bytes) */
CPU_TS MsgTS; /* Time stamp of when message was sent */
};
2. 创建消息队列
OSQCreate (OS_Q *p_q, // 指向定义的消息队列
CPU_CHAR *p_name,
OS_MSG_QTY max_qty, //消息池中的最大消息数
OS_ERR *p_err)
3. 发送消息到消息队列
OSQPost (OS_Q *p_q,
void *p_void, //指向将要被发送的消息数据(只需要是一个地址,消息内容是多大是什么不用管)
OS_MSG_SIZE msg_size, //消息数据的大小(字节数)
OS_OPT opt, //发送方式,当发送紧急消息时,可用LIFO模式,OS_OPT_POST_LIFO
OS_ERR *p_err)
4. 请求消息队列
/***********函数返回值为消息内容**********/
void *OSQPend (OS_Q *p_q,
OS_TICK timeout,
OS_OPT opt, //请求不到是否阻塞任务
OS_MSG_SIZE *p_msg_size, //返回消息大小
CPU_TS *p_ts, //时间戳
OS_ERR *p_err)
十、事件标志组
事件标志组用于一个任务与多个事件同步,有两种同步模式,多个事件中有一个发生,就进行任务同步,是“或”模式。
多个事件同时发生才进行任务同步,是“与”模式。
事件标志组定义:OS_FLAG_GRP
1. 创建事件标志组
OSFlagCreate (OS_FLAG_GRP *p_grp, //指向定义的事件标志组变量
CPU_CHAR *p_name, // 事件标志组名字
OS_FLAGS flags, // 事件标志组的初始值 (可以是8/16/32位)
OS_ERR *p_err) //返回错误值
2. 发送事件标志
/**********返回值为事件标志组的当前值***********/
OS_FLAGS OSFlagPost (OS_FLAG_GRP *p_grp,
OS_FLAGS flags, // 需要更改的标志位
OS_OPT opt, //对flags位清0或者置1
OS_ERR *p_err)
3. 请求事件标志
OS_FLAGS OSFlagPend (OS_FLAG_GRP *p_grp,
OS_FLAGS flags, // 需要请求的标志位
OS_TICK timeout, // 请求等待时间,为0 则一直阻塞任务等待
OS_OPT opt,
/*
* OS_OPT_PEND_FLAG_CLR_ALL 等待所有的标志位被清0 (与)
* OS_OPT_PEND_FLAG_CLR_ANY 等待任意一个标志位被清0(或)
* OS_OPT_PEND_FLAG_SET_ALL 等待所有的标志位被置1 (与)
* OS_OPT_PEND_FLAG_SET_ANY 等待任意一个标志位被置1(或)
*/
CPU_TS *p_ts, //返回时间戳
OS_ERR *p_err)