前段时间看了看任哲老师的UCOSII书籍,有些感悟便想着写点东西要不过两天就全忘了
关于UCOSII的代码详解和使用网络上有很多资料和书籍在这就不说了
(比如UCOSII源码详解,
代码中文注释
各函数使用方法解释
UCOSII参考手册,
介绍UCOSII的书籍都有pdf或其他电子档
(原著,任哲的,邵贝贝的http://wenku.baidu.com/view/1949346f011ca300a6c39087.html)
百度文库和搜索引擎能找到各种单片机和ARM上的移植博文,课件和源代码
另外芯片提供商和开发板提供商都会提供移植好的demo,很多人也是在别人demo基础上写的博客
官网http://micrium.com/page/products
)
这里主要用自己最简单的话来理解UCOSII的常用功能,不去探讨内核实现
主要分为以下部分
1,UCOSII概述
2,UCOSII移植和配置(概述)
3,UCOSII使用
4,UCOSII剪裁和优化
一,ucosii概述
主要功能有:
1, 多任务管理
2, 任务间通信
3, 中断和时间管理
4, 内存管理
特点:
最多支持56个任务,8个系统占用的
属于可剥夺型内核,总是执行优先级最高的任务。
必须使用空闲任务
版本:2.52 2.80 2.86等
附带功能µC/OS-II(Kernel) µC/OS-III (Kernel)
µC/TimeSpaceOS RTOS Add-ons
µC/TCP-IP µC/GUI
µC/File System µC/USB
µC/USB Host µC/USB Device
µC/CAN µC/Modbus
µC/FL µC/Building Blocks
二,ucosii移植和配置(简单描述)
操作系统移植概念
所谓操作系统的移植,是指使一个实时操作系统能够在某个特定的微处理器平台上运行。
mCOS-II的主要代码都是由标准的C语言写成的,移植方便。但仍需要用汇编语言写一些与处理器相关的代码,这是因为µC/OS-Ⅱ在读写处理器寄存器时只能通过汇编语言来实现。
移植的主要工作是修改部分与处理器硬件相关的代码。
处理器需要满足的条件
处理器的C编译器能产生可重入代码。
在程序中可以打开或者关闭中断。
处理器支持中断,并且能产生定时中断(通常在10-100Hz之间)。
处理器支持能够容纳一定量数据的硬件堆栈(可能达几KB)
处理器有将堆栈指针和其他CPU寄存器存储和读出到堆栈(或者内存)的指令
了解处理器的特点
大小端
增长方向
定时器使用和中断的管理
移植主要是三个和cpu相关文件的修改
uC/OS-II的全部源代码量大约是6000-7000行,共15个文件。将 uC/OS-II 移植到ARM处理器上,需要修改三个与ARM体系结构相关的文件,代码量大约是500行。
OS_CPU.H OS_CPU_A.ASM OS_CPU_C.C
配置
文件OS_CFG.H是与应用程序有关的配置文件,主要是对操作系统进行设置。包括:
设置系统的最多任务数OS_MAX_TASKS;
最多事件控制块设置 OS_MAX_EVENTS;
堆栈方向的设置OS_STK_GROWTH(1为递减、0为递增);
是否支持堆栈检验OS_TASK_CREATE_EXT;
是否支持任务统计OS_ASK_STAT_EN;
是否支持事件标志组OS_FLG_EN…
三,ucosii使用(具体的函数使用可参考
UCOSII 常用函数查询 http://wenku.baidu.com/view/02f11f6c011ca300a6c390c2.html
函数参考手册 http://www.doc88.com/p-361144437527.html
UCOSII API函数 http://blog.chinaunix.net/uid-20766895-id-219986.html
UCOSII功能函数大全 http://wenku.baidu.com/view/1b56d219fad6195f312ba6a3.html)
获取版本号:
应用程序调用OSVersion()可以得到当前μC/OS-Ⅱ的版本号。 OSVersion()
函数返回版本号值乘以 100。换言之,200 表示版本号 2.00。
任务模型
void YourTask (void *pdata)
{
for (;;) {
/* 用户代码 */
调用µC/OS-Ⅱ的服务例程之一:
OSMboxPend();
OSQPend();
OSSemPend();
OSTaskDel(OS_PRIO_SELF);
OSTaskSuspend(OS_PRIO_SELF);
OSTimeDly();
OSTimeDlyHMSM();
/* 用户代码 */
}
}
先上一个在pc上测试的demo程序
/******************************Test*******************************/ #include "includes.h" #define TASK_STK_SIZE 512 //任务堆栈长度 OS_STK MyTaskStk[TASK_STK_SIZE]; //定义任务堆栈区 OS_STK YouTaskStk[TASK_STK_SIZE]; //定义任务堆栈区 INT16S key; //用于退出uCOS_II的键 INT8U x=0,y=0,time=0; //字符显示位置 void MyTask(void *data); //声明任务 void YouTask(void *data); //声明任务 /************************主函数*********************************/ void main (void) { char* s_M="M"; //定义要显示的字符 OSInit( ); //初始化uCOS_II PC_DOSSaveReturn( ); //保存Dos环境 PC_VectSet(uCOS, OSCtxSw); //安装uCOS_II中断 OSTaskCreate( MyTask, //创建任务MyTask s_M, //给任务传递参数 &MyTaskStk[TASK_STK_SIZE - 1], //设置任务堆栈栈顶指针 0 //任务的优先级别为0 ); OSStart( ); //启动多任务管理 } /*******************任务MyTask**********************************/ void MyTask (void *pdata) { char* s_Y="Y"; //定义要显示的字符 #if OS_CRITICAL_METHOD == 3 OS_CPU_SR cpu_sr; #endif pdata = pdata; OS_ENTER_CRITICAL( ); PC_VectSet(0x08, OSTickISR); //安装时钟中断向量 PC_SetTickRate(OS_TICKS_PER_SEC); //设置时钟频率 OS_EXIT_CRITICAL( ); OSStatInit( ); //初始化统计任务 OSTaskCreate( YouTask, //创建任务MyTask s_Y, //给任务传递参数 &YouTaskStk[TASK_STK_SIZE - 1], //设置任务堆栈栈顶指针 2 // MyTask的优先级别为2 ); for (;;) { if (x>50) { x=0; y+=2; } PC_DispChar(x, y, //字符的显示位置 *(char*)pdata, DISP_BGND_BLACK+DISP_FGND_WHITE ); x += 1; //如果按下Esc键则退出uCOS_II if (PC_GetKey(&key) == TRUE) { if (key == 0x1B) { PC_DOSReturn( ); //恢复Dos环境 } } OSTimeDlyHMSM(0, 0, 3, 0); //等待3秒 } } /************************任务YouTask******************************/ void YouTask (void *pdata) { #if OS_CRITICAL_METHOD == 3 OS_CPU_SR cpu_sr; #endif pdata = pdata; for (;;) { if(time==20) OSTaskSuspend(0); if(time==40) OSTaskResume(0); time++; if (x>50) { x=0; y+=2; } PC_DispChar( x, y, //字符的显示位置 *(char*)pdata, DISP_BGND_BLACK+DISP_FGND_WHITE ); x += 1; OSTimeDlyHMSM(0, 0, 1, 0); //等待1秒 } } /************************End************************************/
1, 多任务管理
首先每个任务都需要创建任务堆栈,来保存任务切换时的数据
#define TASK_STK_SIZE 512 //任务堆栈长度任务函数主要包括:
创建任务两个函数后者更灵活但会增加开销
INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio) //任务函数指针,参数,任务堆栈栈顶指针,优先级
INT8U OSTaskCreateExt (void (*task)(void *pd),
void *pdata,
OS_STK *ptos,
INT8U prio,
INT16U id,
OS_STK *pbos,
INT32U stk_size,
void *pext,
INT16U opt)
{
删除任务
INT8U OSTaskDel (INT8U prio)
void RequestorTask (void *pdata) //删除别的任务
void TaskToBeDeleted (void *pdata) //删除自己
改变优先级 INT8U OSTaskChangePrio (INT8U oldprio, INT8U newprio)
挂起任务INT8U OSTaskSuspend (INT8U prio)
恢复任务INT8U OSTaskResume(INT8U prio)
查询任务
OS_TCB MyTaskData;
void MyTask (void *pdata)
{
pdata = pdata;
for (;;) {
/* 用户代码 */
err = OSTaskQuery(10, &MyTaskData);
/* Examine error code .. */
/* 用户代码 */
}
}
使用流程
创建任务堆栈
OSinit()
创建任务
OSStart()
2, 任务间通信
OS_EventWaitListInit:事件控制块初始化
使一个任务进入就绪状态OS_EventTaskRdy:
使一个正在等待的任务进入就绪状态OS_EventTaskWait:
使一个等待超时的任务进入就绪状态OS_EventTO:
信号量(其实就是一个全局变量,添加了管理机制)
就是一个标志来标示公共资源现在是否可用,不可用就排队等待一定时限或者直接返回
应用场合:多个任务需要访问同一个资源,为了避免各任务竞争资源导致数据资源中的混乱,就需要信号量来管理
各任务对公共资源的访问
相关函数:创建,请求(等待和不等待),释放,删除,查询
使用流程:
声明一个信号量 OS_Event *Fun_semp;
创建信号量(注册刚才声明的那个信号量)
请求/释放信号量
互斥信号量(添加了优先级功能的信号量)
比信号量多了个功能为了避免优先级的翻转现象。
应用场合:多个任务需要访问公共资源,且对任务优先级要求很严格,不允许出现优先级反转现象的场合。
相关函数:创建,请求,释放,删除,查询
使用流程:
同信号量的操作流程
信号量集(含有状态组的信号量)
应用场合:假如任务4需要任务1,2,3同时执行完后才能执行,那么就用信号量集来完成这个功能
任务一完成后改变信号量集的第一位
任务二完成后改变信号量集的第二位
任务三完成后改变信号量集的第三位
那么任务四就看这个信号量集的1,,2,3位是否都改变了,如果是就执行
相关函数:创建信号量集,请求/查询信号量集,发送/修改信号量集,删除信号量集,查询信号量集
使用流程:
声明一个信号量集 OS_FLAG_GRP *sem_f;
创建信号量集(注册刚才声明的那个信号量集)
发送/修改信号量集
请求/查询信号量集
消息邮箱(其实就是一个全局指针,不同的任务来通过这个指针传递数据)
功能是提供任务间的数据传递,但是是单向的传递两个任务间要互相传递数据,就需要连个邮箱。
不同任务间的通信需要定义多邮箱来实现才能互不干扰
应用场合:两个或多个任务间需要传递一条数据的场合。
相关函数:创建,收,发,查询,删除
使用流程:
定义一个消息邮箱OS_EVENT *str_box;
创建
收/发
(删除)
消息队列(每次可传递多条消息的机制)
和邮箱类似但是每次传递的数据较多,不同任务间的通信需要定义多个队列来实现才能互不干扰
应用场合:两个或多个任务间需要传递多条数据的场合。
相关函数:创建,收,发,查询,删除
使用流程:
定义消息指针数组 void *MsgGrp[消息队列长度];
定义事件控制块 OS_EVENT *str_Q;
创建
收/发
(删除)
3, 中断和时间管理
OSTimeDly() 延时以时钟节拍算
OSTimeDlyHMSM() 延时按时分秒毫秒算
OSTimeDlyResu() 取消延时
OSTimeGet() 系统时间获取
OSTimeSet() 系统时间设置
4, 内存管理
相关函数
注册内存区
申请内存块
释放内存块
使用流程:
开辟内存区 就是定义两位数组INT8U Partition[100][32];
注册内存区
申请内存块
使用内存块
释放内存块
四,ucosii剪裁和优化