【uC/OS-II】笔记1----入门
前言
uC/OS-II操作系统有许多基本概念和机制。单独看这些基本知识,很枯燥,也不知道如何联系。就像打太极拳,那么多招式,只知道是什么,不知道为什么的,学起来也很慢。本文以问答的方式,环环相扣,把各个基本概念给串起来,便于理解概念、机制和ucosii设计的精妙之处。
问:为什么要使用uC/OS-II操作系统?
在编写单片机程序时,分为裸机程序和带操作系统的程序。裸机程序在运行时,从上而下顺序、串行地执行。
例如:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
void main(){ init(); while(1){ key();//按键操作 Display();//显示 GetsensorVal();//获取传感器输出 CtrlGpio();//输出 GetBeijingTime();//获取北京时间 Drivestepmotor();//控制步进电机 } }
按键:需要实时响应
显示:需要定时1s更新北京时间
步进电机:按键控制步进电机转动
传感器:获取传感器数值
其中,按键、步进电机需要实时响应,显示需要定时更新等。每个函数得到重复执行就必须等待一个完整的while(1)周期,这样,获取传感器数据、按键控制步进电机时,可能显示就不能定时更新,实时性不容易得到保证。当然,可以使用状态机和搭配外部中断、定时器中断设置一些flag,缩短while(1)执行的周期,来保证显示的实时性。但,如果程序再复杂一些,程序员不仅要写功能函数、逻辑函数、还要维护这套函数的调用逻辑,工作量就比较大了。
问:uC/OS-II操作系统是什么?
uC/OS-II操作系统是一套完善的任务(也就是函数)切换机制,可以保证程序运行的实时性。主要方法是划分时间片,在每个时间片去轮番运行各个函数(也可以叫任务、进程)。如果任务比较多,轮番执行起来的周期仍然很慢,所以,操作系统对各个任务划分5种状态(休眠、就绪、运行、阻塞、挂起等)。只有就绪态的任务,才可以得到执行。这样,就跳过了很多没有就绪好的任务,缩短了轮番执行的周期。
问:多个任务同时就绪怎么切换?
用户在创建任务时,需要对任务分配优先级,保证在多个任务都就绪时,操作系统可以按照优先级有序地执行。uC/OS-II还设计了就绪组合就绪表来标志就绪的任务。
任务调度程序(时钟中断服务程序)每隔一段时间查询一次。
问:如果任务数量比较多,查询慢,这样就影响了系统实时性,怎么解决?
问:uC/OS-II操作系统为什么有部分是汇编语言编写?
操作系统在按时间片切换各个任务时,并不能保证在时间片内此任务能够运行完毕,在下一次切换到此任务时,需要接着上次执行的状态继续执行。这就需要保护现场、恢复现场。编程时,给每个任务定义并分配一定的堆栈,对其数据进行保存。c语言不能操作寄存器,汇编可以,所以操作系统是c和汇编混写的。
问:裸机程序,可以使用if来确定各函数的执行逻辑,uC/OS-II下两个任务之间有逻辑限制怎么办?例如:A任务接收串口数据,写入buffer,B任务要读出buffer内容,进行指令解析。
任务之间有互相通信、数据交换的需求,因此,有邮箱、消息等用于通信的数据结构。
同步&互斥:A任务接收串口数据,写入buffer,B任务要读出buffer内容,进行指令解析,这样,两个任务就需要同步。任务之间这种共享资源和数据,为了排他避免冲突,因此,有互斥、死锁、信号量、临界区、消息等概念。
临界区:这种共享资源叫临界资源(Critical Resource)。每个任务中在访问共享资源的那段程序成为临界区(Critical Section),在临界区不允许任务切换,为了保证实时性,临界区的代码应该在限定的时间内运行完。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
int function(){ OS_ENTER_CRITICAL(); //处理临界资源的代码 OS_EXIT_CRITICAL(); //...其他代码 }
事件:A任务接收串口数据,写入buffer,B任务要读出buffer内容,进行指令解析。当B任务访问buffer前,应调用OSSemPend()来等待,当A任务完成buffer的写入后,需调用OSSemPost()来通知B任务,缓冲区有可用的资源。这就是A产生一个事件,B在等待一个事件。事件处理是操作系统处理这些信息的手段。
信号量:共享资源的访问钥匙。三种操作:建立Creat、请求Pend、释放Post。
互斥信号量:一种特殊的信号量,不仅用于互斥资源的访问,还在于使用互斥信号量管理需要解决的优先级反转问题。低优先级的在访问互斥资源时,高优先级不能对其进行抢占。
事件标志组:在信号量和互斥信号量的管理中。如果任务要等待多个事件的发生,或者多个中的一个事件发生,那么就应该采用事件标志组管理。
邮箱:邮箱传递的是消息的地址(指针),不是消息本身。
以上,均是为了让任务间能进行通信,相互协调、有序的运行,发明的一些基本概念。
6、有些功能性函数,可能多个任务均会调用,则此函数必须设计成可重入函数。因为,在任务切换的时候,保存现场仅是保存了局部变量的值。全局变量是公共的,可能在下一个任务被改变。恢复运行上一个任务时,使用全局变量新值和上一次的运行状态进行杂交运行,会得不到正确的数据。所以,可重入函数,必须使用局部变量,避免使用全局变量。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
int m,n; int sum(int a, int b){ int result; while(1){ m=a; n=b; OSTimeDly(OS_TICKS_PER_SEC); return (m+n); } } //------------------------------------ int sum2(int a, int b){ int p,q; while(1){ p=a; q=b; OSTimeDly(OS_TICKS_PER_SEC); return (p+q); } }
8、