RTOS全程为:Real Time OS,就是实时操作系统。实时操作系统分为硬实时和软实时。硬实时要求在规定时间内必须完成操作,硬件实时系统有不允许超时。
RTOS操作系统:UCOS,FreeRTOS,RTX,RT-Thread,DJYOS等
UCOS的内核是可剥夺型的,可剥夺内核顾名思义就是可以剥夺其他任务的CPU使用权,它总是运行就绪任务中的优先级最高的那个任务。
UCOSIII中的任务主要有三部分组成:1)任务堆栈;2)任务控制块;3)任务函数。
任务堆栈实际上就是一个数组,是任务的重要部分,堆栈是在RAM中按照“FIFO”的原则组织的一块连续的存储空间。用于保存任务切换和响应中断时保存CPU寄存器中的内容及任务调用其他函数时的需要。
任务控制块是用来记录与任务相关的信息的数据结构
UCOSIII中任务优先级数由宏OS_CFG_PRIO_MAX定义来配置,数值越小优先级越高,最低可用优先级就是OS_CFG_PRIO_MAX-1。
UCOSIII中就绪表由2部分组成:
1)优先级位映射表OSPrioTbl[],用来记录那个优先级下有任务就绪;
2)就绪任务列表OSRdyList[],用来记录每一个优先级下所有就绪的任务。
优先级位映射表主要就是存储不同优先级的任务,而就绪任务列表是记录每一个优先级下有没有已经就绪的任务。
一个是记录不同优先级下的就绪任务,另外一个是记录同一个优先级下的就绪任务
任务调度就是中止当前正在运行的任务转而去执行其他的任务
UCOSIII是可剥夺型内核,高优先级任务准备就绪后,发送任务调度,高优先级的任务就会获得CPU的使用权!
1)任务级调度器为函数OSSched();
2)中断级调度器为函数OSIntExit(),当退出外部中断服务函数的时候使用中断级任务调度。
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntEnter();
#endif
//串口接收中断函数主体(省略)
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntExit();
#endif
}
定义:在什么时间和什么位置发生了任务的调度
1)释放信号消息或发送消息,也可通过相应的参数不发生任务调度。
2)使用延时函数OSTimeDly()或者OSTimeDlyHMSM();
3)任务等待的事情还没发送(等待信号量,消息队列等);
4)任务取消等待
5)创建任务
6)删除任务
7)删除一个内核任务
8)任务改变自身的优先级或者其他任务的优先级
9)任务通过调用OSTaskSuspend()将自身挂起
10)任务解挂某个任务
11)退出所有的嵌套中断
12)调度器解锁
13)放弃其执行时间片
14)用户调用OSSched()(任务级调度器函数)
不希望发生任务调度的时候,例如一些代码的执行过程是不能被打断的。此时我们就可以使用函数OSSchedLock()对调度器加锁,当需要回复任务调度的时候就可以使用函数OSSchedUnlock()给已经上锁的任务调度器解锁。
UCOSIII允许一个优先级下有多个任务,每个任务可以执行指定的时间(时间片),然后轮到下一个任务。这个过程就是时间片轮转调度,当一个任务不想在运行的时候就可以放弃其时间片。
时间片轮转调度为:OS_SchedRoundRobin()。
将保存当前任务的现场到当前任务的堆栈中,主要是CPU寄存器值,然后恢复新的现场并且执行新的任务,这个过程就是任务切换。
从用户的角度讲任务一共有五种状态:
1)休眠态:任务已经在CPU的flash中了,但是还不受UCOSIII管理;
2)就绪态:系统为任务分配了任务控制块,并且任务已经在就绪表中登记,这时这个任务就具有了运行的条件,此时的任务就处于就绪的状态
3)运行态:任务获得CPU使用权
4)等待态:正在运行的任务需要等待一段时间,或者等待某个事件,这个任务就进入了等待态,此时系统就会把CPU使用权转给别的任务
5)中断服务态:当发送中断,当前正在运行的任务会被挂起,CPU转而去执行中断服务函数,此时任务的状态叫中断服务态。
使用系统之前需要将系统初始化,函数OSInit()用来初始化UCOSIII的初始化,初始化是初始化其中的一些控制系统的变量,初始化函数必须先于其他UCOSIII函数调用(包括OSStart())。例:
int main(){
OS_ERR err;
CPU_SR_ALLOC();
delay_Init(168); //时钟初始化
NVIC_PriorityGroupConfig(NVIC_PriortyGroup_2); //中断分组配置
uart_init(115200); //串口初始化
LED_Init(); //LED初始化
OSInit(&err); //初始化UCOSIII 初始化5个系统任务函数,最基本的需要初始化2个任务
OS_CRITICAL_ENTER(); //进入临界区
//创建开始任务
OSTaskCreate((OS_TCB * )&StartTaskTCB,//任务控制块
(CPU_CHAR * )"start task", //任务名字
(OS_TASK_PTR )start_task, //任务函数
(void * )0, //传递给任务函数的参数
(OS_PRIO )START_TASK_PRIO, //任务优先级
(CPU_STK * )&START_TASK_STK[0],//任务堆栈基地址
(CPU_STK_SIZE)START_STK_SIZE/10,//任务堆栈深度限位
(CPU_STK_SIZE)START_STK_SIZE,//任务堆栈大小
(OS_MSG_QTY )0,//任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
(OS_TICK )0,//当使能时间片轮转时的时间片长度,为0时为默认长度,
(void * )0,//用户补充的存储区
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
(OS_ERR * )&err);//存放该函数错误时的返回值
OS_CRITICAL_EXIT(); //退出临界区
OSStart(&err); //开启UCOSIII
}
UCOSIII中使用函数OSTaskCreate()创建任务,
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 opt有四个不同的选项,OS_OPT_TASK_NONE(表示没有任何选项)、OS_OPT_TASK_STK_CHK(指定是否允许监测该任务的堆栈)、OS_OPT_TASK_STK_CLR(指定是否清除该任务的堆栈)、OS_OPT_TASK_SAVE_FP(指定是否存储浮点寄存器,CPU需要有浮点运算硬件并且有专用代码保存浮点寄存器)。
错误码有解释调试的时候可以使用串口打印,然后对比寻找问题的原因。
使用函数OSTaskDel(),函数原型如下:void OSTaskDel(OS_TCB *p_tch OS_ERR *p_err)
1、不要在运行中删除某个任务,防止一个任务影响另外一个任务的执行。
当需要该任务暂停,但是又不需要删除这个任务的时候就可以使用函数OSTaskSuspend()来将这个任务挂起,函数原型如下:
void OSTaskSuspend(OS_TCB *p_tcb,OS_ERR *p_err);
恢复某个被挂起的任务的时候可以调用函数OSTaskResume(),函数原型如下:
void OSTaskResume(OS_TCB *p_tcb,OS_ERR *p_err);
时间片轮转调度主要是用于同一优先级下的任务,执行时间的控制。要是不同优先级的任务时间片轮转调度没有实际的意义,只会影响后面低优先级任务的执行而已。
#if OS_CFG_SCHED_ROUND_ROBIN_EN //当使用时间片轮转的时候,修改宏置1
//使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);
#endif
1)空闲任务是UCOSIII创建的第一个任务
2)空闲任务是UCOSIII必须创建的
3)空闲任务优先级总是OS_CFG_PRIO_MAX-1
4)空闲任务中不能调用任何可使空闲任务进入等待态的函数
void OS_IdleTask (void *p_arg)
{
CPU_SR_ALLOC();
p_arg = p_arg; /* Prevent compiler warning for not using 'p_arg' */
while (DEF_ON) {
CPU_CRITICAL_ENTER();
OSIdleTaskCtr++;//一个变量,该变量增加的越快表示系统处于空闲的时间越多,也就表示系统越‘闲’
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCtr++;
#endif
CPU_CRITICAL_EXIT();
OSIdleTaskHook(); /* Call user definable HOOK */
}
}
用来跟踪任务延时和任务等待超时
可用来统计CPU的使用率、各个任务的CPU使用率和各任务的堆栈使用情况。开启步骤
1)将宏OS_CFG_STAT_TASK_EN置1
2)在唯一的一个应用任务里面调用函数OSStatTaskCPUUsageInit()。
系统提供软件定时器功能,将宏OS_CFG_TMR_EN设置为1就会使能定时任务。要使用软件定时器一定需要使能定时任务。
减少中断关闭的时间
钩子函数一般主要是用来扩展其他函数(任务)功能的,钩子函数有如下几个:
1)OSIdleTaskHook(),空闲任务调用这个函数,可以用来让CPU进入低功耗模式
2)OSInitHook(),初始化函数OSInit()调用此函数
3)OSStatTaskHook(),统计任务每秒中都会调用的函数
4)OSTaskCreateHook(),任务创建的钩子函数
5)OSTaskDelHook(),任务删除的钩子函数
6)OSTaskReturnHook(),任务意外返回时调用的钩子函数
7)OSTaskSwHook(),任务切换时候调用的钩子函数
8)OSTimeTickHook(),滴答定时器调用的钩子函数
钩子函数中可以添加自己的应用函数,但是不可以直接在这些函数中添加需要到APP函数中添加,例如空闲任务就需要在os_app_hooks.c文件中的App_OS_IdleTaskHook()函数添加。不可以在空闲任务函数中调用任何使该任务变为等待态的代码
#if OS_CFG_APP_HOOKS_EN //使用钩子函数,使用前需要将该宏修改为1
App_OS_SetAllHooks();
#endif
在UCOSIII系统初始化的时候会初始化几个基本的任务,这几个基本的任务使用了五个不同的优先级,后面创建的任务不可以使用这五个优先级。
OS_IdleTaskInit(p_err); //初始化空闲任务 优先级OS_CFG_PRIO_MAX-1
OS_TickTaskInit(p_err); //初始化时钟节拍任务 优先级1
OS_StatTaskInit(p_err); //初始化统计任务 优先级OS_CFG_PRIO_MAX-2
OS_IntQTaskInit(p_err); //初始化中断队列处理程序任务 优先级0
OS_TmrInit(p_err); //初始化定时器任务 优先级2
系统裁剪的文件是os_cfg.h和os_cfg_app.h,其中的宏定义就是控制一些功能的开或者关闭。