最近在学习嵌入式系统,听说ucos-ii是使用最广,也是最经典的嵌入式系统,也就跟随大众从这个系统入手了,用的stm32F407开发板。
借用百度的一段介绍:
μC/OS-II由Micrium公司提供,是一个可移植、可固化的、可裁剪的、占先式多任务实时内核,它适用于多种微处理器,微控制器和数字处理芯片(已经移植到超过100种以上的微处理器应用中)。同时,该系统源代码开放、整洁、一致,注释详尽,适合系统开发。 μC/OS-II已经通过联邦航空局(FAA)商用航行器认证,符合航空无线电技术委员会(RTCA)DO-178B标准。
在没学操作系统之前,平时写的程序都是在单片机上裸奔,就是一个大循环,一堆函数怼进去,然后循环着调用,现在正在努力脱离裸奔。
ucos ii操作系统的基础是要有一个时钟节拍,这个时钟节拍来自滴答定时器,时间一般为几毫秒,而且不可以被打断。我使用的时钟节拍选择了HCLK的8分频,因为外设为8MHz,然后倍频到168MHz,也就是说SYSTick的时钟为21MHz。
ucos ii是依靠任务优先级和通过调度任务来执行函数的,每一个任务都相当于以前程序的那个大循环,只要没有释放CPU就会一直执行下去。释放CPU的直接代码就是把当前任务挂起或者调用ucos ii里面的延时函数(强调:不要自己写for延时什么的,这样虽然也是延时,但是这是占用CPU的延时,不会切换任务的)。
接下来介绍ucos ii第一批函数:
1.建立任务函数OSTaskCreate(void (* task)(void *pd),void *pada,OS_STK * ptos,INTU prio);
这个函数有4个参数,
task是指向任务的指针,说白了就是任务的名字,后面的那个pd不用理,干嘛用的暂时没搞清楚;
pdata是任务开始的时候传递给任务的参数的指针,就是一个参数,一般都是0;
ptos是分配给任务的堆栈的栈顶指针,也就是这个任务拥有的存储空间大小,任务越复杂,这个空间需要的越大;
prio是任务的优先级,优先级的数值越低,任务的优先级越高(优先级是唯一的,在ii中一个优先级只能对应一个任务)。
2.任务删除函数OSTaskDel(INT8U prio);
这个函数不用怎么说了,prio就是要删除的任务的优先级,但是,任务不能随便删,在删除之前要先释放这个任务占用的资源。
3.请求任务删除函数OSTaskDelReq(INT8U prio);
前面说到要删除任务就必须保证资源释放完毕,这个函数的作用就是通过向 被删除任务 发送请求来实现任务释放自身占用资源后再删除。
4.改变任务优先级任务OSTaskChangPrio(INT8U oldprio,INT8U newprio);
这函数一看就知道用来改任务的优先级的,不多说。
5.任务挂起函数OSTaskSuspend(INT8U prio);
这个函数用来挂起当前任务,让这个任务不再参与调度直到被恢复。挂起和删除不同,挂起并不需要先释放资源,而且挂起的认为可以恢复,删 除的任务无法恢复。这个挂起函数一般用在开始任务中,学了几天ucos ii后发现套路都是先初始化硬件,然后初始化ucos ii,然后就创建一个 开始任务,再在这个任务里面创建其它任务,并不是一口气都在main()里面都把任务创建好,main里面只创建一个任务,然后在开始任务执行完后就可以把开始任务挂起了,不需要再参与任务调度了。
6.任务恢复函数OSTaskResume(INT8U prio);
这个函数就是可以把挂起的任务恢复,让调度器可以重新调度该任务。
7.任务查询信息OSTaskQuery(INT8I prio.OS_TCB *pdata);
这个用来了解任务信息的,不太懂,书上说是获得对应任务的OS_TCB中的内容拷贝。
接下来上几个例子,也就是套路(省略了头文件什么的)
先定义任务的优先级、堆栈大小、任务名称等
//START 任务
#define START_TASK_PRIO 10 //开始任务的优先级设置为最低
#define START_STK_SIZE 64
OS_STK START_TASK_STK[START_STK_SIZE];
void start_task(void *pdata);
//LED任务
#define LED_TASK_PRIO 8
#define LED_STK_SIZE 64
OS_STK LED_TASK_STK[LED_STK_SIZE];
void led_task(void *pdata);
//主任务
#define MAIN_TASK_PRIO 4
#define MAIN_STK_SIZE 128
OS_STK MAIN_TASK_STK[MAIN_STK_SIZE];
void main_task(void *pdata);
//按键任务
#define KEY_TASK_PRIO 3
#define KEY_STK_SIZE 64
OS_STK KEY_TASK_STK[KEY_STK_SIZE];
void key_task(void *pdata);
如上,这就创建了4个任务,优先级从低到高,然后再在main里面创建开始任务
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
.......//初始化各种模块
OSInit(); //初始化UCOSII
OSTaskCreate(start_task,(void *)0,(OS_STK *)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO );//创建起始任务
OSStart();
}
起始任务就是开始任务,,一个意思一个意思,OSStart()是用来启动任务调度了。然后就进入开始任务了
void start_task(void *pdata)
{
OS_CPU_SR cpu_sr=0;
pdata = pdata;
OSStatInit(); //初始化统计任务.这里会延时1秒钟左右
OS_ENTER_CRITICAL(); //进入临界区(无法被中断打断)
OSTaskCreate(led_task,(void *)0,(OS_STK*)&LED_TASK_STK[LED_STK_SIZE-1],LED_TASK_PRIO);
OSTaskCreate(key_task,(void *)0,(OS_STK*)&KEY_TASK_STK[KEY_STK_SIZE-1],KEY_TASK_PRIO);
OSTaskCreate(main_task,(void *)0,(OS_STK*)&MAIN_TASK_STK[MAIN_STK_SIZE-1],MAIN_TASK_PRIO);
OSTaskSuspend(START_TASK_PRIO); //挂起起始任务.
OS_EXIT_CRITICAL(); //退出临界区(可以被中断打断)
}
这个OSStaInit();据说是用来统计运行时间的,原文在这:
如果用户将系统定义常数OS_TASK_STAT_EN(见文件OS_CFG.H)设为1,这个任务就会建立。一旦得到了允许,OS_TaskStat()每秒钟运行一次(见文件OS_CORE.C),计算当前的CPU利用率。换句话说,OS_TaskStat()告诉用户应用程序使用了多少CPU时间,用百分比表示,这个值放在一个有符号8位整数OSCPUsage中,精读度是1个百分点。
如果用户应用程序打算使用统计任务,用户必须在初始化时建立一个唯一的任务,在这个任务中调用OSStatInit()。换句话说,在调用系统启动函数OSStart()之前,用户初始代码必须先建立一个任务,在这个任务中调用系统统计初始化函数OSStatInit(),然后再建立应用程序中的其它任务。
临界区什么的就是为了一些不能受干扰的代码段设置的,必须连续运行,不能被中断的代码段,敞开了说临界区就是把中断全都关了,一条线的执行程序。
最后就是各个任务的内容了void led_task(void *pdata)
{
while(1)
{
}
}
void main_task(void *pdata)
{
while(1)
{
}
}
void key_task(void *pdata)
{
while(1)
{
}
}
这里需要注意的是每个任务的while循环里都要有个延时,前面也说过ucos ii是通过延时来释放CPU占用的,没有延时的话当前任务会一直占着CPU不放,其它任务没法执行。
第一次的笔记就这样了,虽然本意上这是写来自己看的,但有错误欢迎指出。