uCOS-II源码分析
ucOS-II范例分析_01

分析一下OS_CPU.H文件,它涉及特定CPU 的实现。一般都被替换为一条或者几条嵌入 式汇编代码。其实,它就是关中断。 只要任务不主动放弃CPU使用权,别的任务 就没有占用CPU的机会,这个任务就是独占 了。进入临界区后,这个应宏尽量少用,它 会破坏系统的一些服务,尤其是时间服务。 并使系统对外界响应性能降低。
不依赖于编译的数据类型
可移植型数据类型的程序 Typedef unsigned char BOOLEAN; Typedef unsigned char INT8U; Typedef signed char INT8S; Typedef unsigned int INT16U; Typedef signed int INT16S; Typedef unsigned long INT32U; Typedef signed long INT32S; Typedef float FP32; Typedef double FP64; #define BYTE INT8S -----------| #define UBYTE INT8U -----------| #define WORD INT16S ------------| ucos->ucosII #define UWORD INT16U -----------| #define LONG INT32S -----------| #define ULONG INT32U -----------|
嵌入式操作系统ucOS-II分析
ucOS-II应用程序基本结构
void task ( void* pdata ) { INT8U err; InitTimer(); // 可选 For( ;; ) { // 你的应用程序代码 …….. OSTimeDly(1); // 可选 } }
北航ARM9实验报告:实验3uCOS-II实验

北航ARM9实验报告:实验3uCOS-II实验北航 ARM9 实验报告:实验 3uCOSII 实验一、实验目的本次实验的主要目的是深入了解和掌握 uCOSII 实时操作系统在ARM9 平台上的移植和应用。
通过实际操作,熟悉 uCOSII 的任务管理、内存管理、中断处理等核心机制,提高对实时操作系统的理解和应用能力,为后续的嵌入式系统开发打下坚实的基础。
二、实验环境1、硬件环境:ARM9 开发板、PC 机。
2、软件环境:Keil MDK 集成开发环境、uCOSII 源代码。
三、实验原理uCOSII 是一个可裁剪、可剥夺型的多任务实时内核,具有执行效率高、占用空间小、实时性能优良和可扩展性强等特点。
其基本原理包括任务管理、任务调度、时间管理、内存管理和中断管理等。
任务管理:uCOSII 中的任务是一个独立的执行流,每个任务都有自己的堆栈空间和任务控制块(TCB)。
任务可以处于就绪、运行、等待、挂起等状态。
任务调度:采用基于优先级的抢占式调度算法,始终让优先级最高的就绪任务运行。
时间管理:通过系统时钟节拍来实现任务的延时和定时功能。
内存管理:提供了简单的内存分区管理和内存块管理机制。
中断管理:支持中断嵌套,在中断服务程序中可以进行任务切换。
四、实验步骤1、建立工程在 Keil MDK 中创建一个新的工程,选择对应的 ARM9 芯片型号,并配置相关的编译选项。
2、导入 uCOSII 源代码将 uCOSII 的源代码导入到工程中,并对相关的文件进行配置,如设置任务堆栈大小、系统时钟节拍频率等。
3、编写任务函数根据实验要求,编写多个任务函数,每个任务实现不同的功能。
4、创建任务在主函数中使用 uCOSII 提供的 API 函数创建任务,并设置任务的优先级。
5、启动操作系统调用 uCOSII 的启动函数,使操作系统开始运行,进行任务调度。
6、调试与测试通过单步调试、查看变量值和输出信息等方式,对系统的运行情况进行调试和测试,确保任务的执行符合预期。
uCOS-II源码分析

uC/OS-II源码分析(一)下载地址:/它的特点:1)开源,2)可移植性,绝大部分代码用C写,硬件相关部分用汇编写,3可固化,4)可剪裁,这通过条件编译实现,使用#define语句定义所需要的功能。
5)可剥夺性(总是运行就绪条件下优先级最高的任务),6)多任务(可以管理64个任务,其中保留8个给uC/OS-II,因此用户最多可有56个任务,每个任务优先级不同,也就意味着不支持时间片轮转调度法,因为这种方法适合于优先级平等的任务)。
7)可确定性。
函数调度和服务执行时间具有确定性,除了OSTimeTick()和某些事件标志服务,系统服务执行时间不依赖用户应用程序任务数目的多少。
8)任务栈。
允许每个任务自己单独的栈空间不同,可以使用栈空间检验函数确定所需要的栈空间大小。
9)系统服务。
提供信号量,互斥型信号量,事件标志,消息邮箱,消息队列,块大小固定的内存申请与释放,时间管理函数等服务。
10)中断管理。
中断嵌套层数最多可达到255层。
11)稳定性和可靠性。
OSInit()函数用来初始化内核,必须首先调用。
建立两个任务:空闲任务(其他任务都未就绪时运行),统计任务(计算CPU的利用率).****************************************************************** Description: This function is used to initialize the internals of uC/OS-II and MUST be called prior to creating any uC/OS-II object and, prior to calling OSStart().*****************************************************************void OSInit (void){OSInitHookBegin();/* 调用用户特定的初始化代码(通过一个接口函数实现用户要求的插件式进入系统中)*/OS_InitMisc();/* 初始化变量*/OS_InitRdyList();/* 初始化就绪列表*/OS_InitTCBList();/* 初始化OS_TCB空闲列表*/OS_InitEventList();/* 初始化OS_EVENT空闲列表*/#if(OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)//允许事件标志OS_FlagInit();/* 初始化事件标志结构*/#endif#if(OS_MEM_EN > 0) && (OS_MAX_MEM_PART > 0)//允许内存管理OS_MemInit();/* 初始化内存管理器*/#endif#if(OS_Q_EN > 0) && (OS_MAX_QS > 0)//允许消息队列OS_QInit();/* 初始化消息队列结构*/#endifOS_InitTaskIdle();/*创建空闲任务*/#if OS_TASK_STAT_EN > 0OS_InitTaskStat();/* 创建统计任务*/#endif#if OS_TMR_EN > 0//允许时间管理OSTmr_Init();/* 初始化时间管理器*/#endifOSInitHookEnd();/*调用用户特定的初始化代码(参考OSInitHookBegin())*/#if OS_DEBUG_EN > 0//允许DebugOSDebugInit();//初始化调试器#endif}******************************************************************************************** * Description: This function is called by OSInit() to initialize miscellaneous variables.********************************************************************************************static void OS_InitMisc (void){#if OS_TIME_GET_SET_EN > 0OSTime = 0L; /* 32位的系统时钟清零*/#endifOSIntNesting = 0; /* 中断嵌套层数计数器清零*/OSLockNesting = 0; /* 调度器锁的嵌套层数计数器清零*/ OSTaskCtr = 0; /* 任务数清零*/OSRunning = OS_FALSE; /*指明多任务未开始*/OSCtxSwCtr = 0; /* 任务切换次数计数器清零*/OSIdleCtr = 0L; /*32位空闲计数器清零*/#if OS_TASK_STAT_EN > 0 /*运行统计任务*/OSIdleCtrRun = 0L;OSIdleCtrMax = 0L;OSStatRdy = OS_FALSE; /* 统计任务未就绪*/#endif}空闲任务和统计任务建立的代码基本一样,只是统计任务的优先级比空闲任务大1,******************************************************************************************** * Description: This function creates the Idle Task.********************************************************************************************static void OS_InitTaskIdle (void){#if OS_TASK_NAME_SIZE > 7 //INT8U err;#endif#if OS_TASK_CREATE_EXT_EN > 0 //使用扩展的OSTaskCreateExt来创建#if OS_STK_GROWTH == 1 //任务堆栈从底部向顶部增长的方向有两种:表示从大到小,表示从小到大(void)OSTaskCreateExt(OS_TaskIdle,(void *)0, /* 没有参数传给OS_TaskIdle() */&OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1], /*设置堆栈顶*/OS_TASK_IDLE_PRIO, /* 优先级设置为最低*/OS_TASK_IDLE_ID, //设置ID&OSTaskIdleStk[0], /* 设置栈底*/OS_TASK_IDLE_STK_SIZE, //设置栈大小(void *)0, /* 没有TCB扩展数据结构OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* 允许堆栈检测和清空堆栈*/#else(void)OSTaskCreateExt(OS_TaskIdle,(void *)0, /* No arguments passed to OS_TaskIdle() */&OSTaskIdleStk[0], /* Set Top-Of-StackOS_TASK_IDLE_PRIO, /* Lowest priority levelOS_TASK_IDLE_ID,&OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1], /* Set Bottom-Of-Stack */OS_TASK_IDLE_STK_SIZE,(void *)0, /* No TCB extensionOS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack check ing + clear stack */#endif#else//使用不带扩展性的OSTaskCreate创建#if OS_STK_GROWTH == 1(void)OSTaskCreate(OS_TaskIdle,(void *)0,&OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1],OS_TASK_IDLE_PRIO);#else(void)OSTaskCreate(OS_TaskIdle,(void *)0,&OSTaskIdleStk[0],OS_TASK_IDLE_PRIO);#endif#endif//设置任务名称#if OS_TASK_NAME_SIZE > 14OSTaskNameSet(OS_TASK_IDLE_PRIO, (INT8U *)"uC/OS-II Idle", &err); #else#if OS_TASK_NAME_SIZE > 7OSTaskNameSet(OS_TASK_IDLE_PRIO, (INT8U *)"OS-Idle", &err);#endif#endif}uC/OS-II源码分析(二)在真正开始分析源代码前,先来看使用uC/OS-II的三个例子1)使用信号量#define TASK_STK_SIZE 512 /* 每个任务堆栈的大小(以字计算)*/ #define N_TASKS 10 /* 任务数*/OS_STK TaskStk[N_TASKS][TASK_STK_SIZE]; /*任务堆栈*/OS_STK TaskStartStk[TASK_STK_SIZE]; //开始任务的堆栈char TaskData[N_TASKS]; /*传给每个任务的数据*/OS_EVENT *RandomSem; //互斥型信号量void main (void){PC_DispClrScr(DISP_FGND_WHITE + DISP_BGND_BLACK); /*清空屏幕*/OSInit(); /* 初始化uC/OS-II*/PC_DOSSaveReturn(); /* 保存环境以便稍后可以返回DOS 环境*/PC_VectSet(uCOS, OSCtxSw); /*设置uC/OS-II的切换处理函数*/RandomSem = OSSemCreate(1); /* 建立一个信号量*/OSTaskCreate(TaskStart, (void *)0, &TaskStartStk[TASK_STK_SIZE - 1], 0);//创建第一个任务,优先级设置为最大值OSStart(); /* 开始多任务*/}void TaskStart (void *pdata){#if OS_CRITICAL_METHOD == 3 /* 为CPU的状态寄存器分配内存*/OS_CPU_SR cpu_sr;#endifchar s[100];INT16S key;pdata = pdata; /* 这步是为了防止编译错误*/TaskStartDispInit(); /* 初始化显示屏*/OS_ENTER_CRITICAL();PC_VectSet(0x08, OSTickISR); /*替换机器的时钟中断函数为uC/OS-II所需要的中断函数*/PC_SetTickRate(OS_TICKS_PER_SEC); /* 调整时钟频率*/OS_EXIT_CRITICAL();OSStatInit(); /* 初始化统计任务*/TaskStartCreateTasks(); /*创建其他任务*/ for (;;) {TaskStartDisp();if (PC_GetKey(&key) == TRUE) { /* 是否按键*/if (key == 0x1B) { /* ESCAPE按下了*/PC_DOSReturn(); /* 返回DOS*/}}OSCtxSwCtr = 0; /* 切换次数计数器清零*/OSTimeDlyHMSM(0, 0, 1, 0); /*挂起秒,让给其他任务运行*/ }}static void TaskStartCreateTasks (void){INT8U i;for (i = 0; i < N_TASKS; i++) { /* 创建N_TASKS个任务*/TaskData[i] = '0' + i; /* 每个任务显示其数据*/OSTaskCreate(Task, (void *)&TaskData[i], &TaskStk[i][TASK_STK_SIZE - 1 ], i + 1);}}void Task (void *pdata){INT8U x;INT8U y;INT8U err;for (;;) {OSSemPend(RandomSem, 0, &err); /* 获取信号量*/x = random(80); /* 计算X坐标*/y = random(16); /* 计算Y坐标*/OSSemPost(RandomSem); /* 释放信号量*//* Display the task number on the screen */PC_DispChar(x, y + 5, *(char *)pdata, DISP_FGND_BLACK + DISP_BGND_ LIGHT_GRAY);OSTimeDly(1); /* 挂起秒,让给其他任务运行*/}}2)使用消息邮箱#define TASK_STK_SIZE 512#define TASK_START_ID 0 /* 任务ID*/#define TASK_CLK_ID 1#define TASK_1_ID 2#define TASK_2_ID 3#define TASK_3_ID 4#define TASK_4_ID 5#define TASK_5_ID 6#define TASK_START_PRIO 10 /* 任务优先级*/#define TASK_CLK_PRIO 11#define TASK_1_PRIO 12#define TASK_2_PRIO 13#define TASK_3_PRIO 14#define TASK_4_PRIO 15#define TASK_5_PRIO 16OS_STK TaskStartStk[TASK_STK_SIZE];OS_STK TaskClkStk[TASK_STK_SIZE];OS_STK Task1Stk[TASK_STK_SIZE];OS_STK Task2Stk[TASK_STK_SIZE];OS_STK Task3Stk[TASK_STK_SIZE];OS_STK Task4Stk[TASK_STK_SIZE];OS_STK Task5Stk[TASK_STK_SIZE];OS_EVENT *AckMbox; /* 任务和使用的消息邮箱*/ OS_EVENT *TxMbox;void main (void){OS_STK *ptos;OS_STK *pbos;INT32U size;PC_DispClrScr(DISP_FGND_WHITE);OSInit();PC_DOSSaveReturn();PC_VectSet(uCOS, OSCtxSw);PC_ElapsedInit();ptos = &TaskStartStk[TASK_STK_SIZE - 1]; pbos = &TaskStartStk[0];size = TASK_STK_SIZE;OSTaskStkInit_FPE_x86(&ptos, &pbos, &size); OSTaskCreateExt(TaskStart,(void *)0,ptos,TASK_START_PRIO,TASK_START_ID,pbos,size,(void *)0,OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); OSStart();}void TaskStart (void *pdata){#if OS_CRITICAL_METHOD == 3OS_CPU_SR cpu_sr;#endifINT16S key;pdata = pdata;TaskStartDispInit();OS_ENTER_CRITICAL();PC_VectSet(0x08, OSTickISR);PC_SetTickRate(OS_TICKS_PER_SEC);OS_EXIT_CRITICAL();OSStatInit();AckMbox = OSMboxCreate((void *)0); /* 创建两个消息邮箱*/ TxMbox = OSMboxCreate((void *)0);TaskStartCreateTasks();for (;;) {TaskStartDisp();if (PC_GetKey(&key)) {if (key == 0x1B) {PC_DOSReturn();}}OSCtxSwCtr = 0;OSTimeDly(OS_TICKS_PER_SEC);}}void Task1 (void *pdata){INT8U err;OS_STK_DATA data; /* 任务堆栈数据*/INT16U time;INT8U i;char s[80];pdata = pdata;for (;;) {for (i = 0; i < 7; i++) {PC_ElapsedStart();err = OSTaskStkChk(TASK_START_PRIO + i, &data);//执行堆栈检查time = PC_ElapsedStop();if (err == OS_NO_ERR) {sprintf(s, "%4ld %4ld %4ld %6d",data.OSFree + data.OSUsed,data.OSFree,data.OSUsed,time);PC_DispStr(19, 12 + i, s, DISP_FGND_BLACK + DISP_BGND_LIGHT_G RAY);}}OSTimeDlyHMSM(0, 0, 0, 100); /* 挂起mS*/}}void Task4 (void *data){char txmsg;INT8U err;data = data;txmsg = 'A';for (;;) {OSMboxPost(TxMbox, (void *)&txmsg); /* 发消息给Task #5*/OSMboxPend(AckMbox, 0, &err); /* 等待Task #5的应答消息*/txmsg++; /*下一个要发的消息数据*/if (txmsg == 'Z') {txmsg = 'A'; /* 循环发送A-Z*/}}}void Task5 (void *data){char *rxmsg;INT8U err;data = data;for (;;) {rxmsg = (char *)OSMboxPend(TxMbox, 0, &err); /* 等待来自Task #4的数据*/PC_DispChar(70, 18, *rxmsg, DISP_FGND_YELLOW + DISP_BGND_BLUE); OSTimeDlyHMSM(0, 0, 1, 0); /* 挂起秒,让给其他任务运行*/OSMboxPost(AckMbox, (void *)1); /*发送接收到数据的应答消息*/}}运行结果:3)使用消息队列#define TASK_STK_SIZE 512#define TASK_START_ID 0#define TASK_CLK_ID 1#define TASK_1_ID 2#define TASK_2_ID 3#define TASK_3_ID 4#define TASK_4_ID 5#define TASK_5_ID 6#define TASK_START_PRIO 10#define TASK_CLK_PRIO 11#define TASK_1_PRIO 12#define TASK_2_PRIO 13#define TASK_3_PRIO 14#define TASK_4_PRIO 15#define TASK_5_PRIO 16#define MSG_QUEUE_SIZE 20 /* 消息队列大小*/ typedef struct {char TaskName[30];INT16U TaskCtr;INT16U TaskExecTime;INT32U TaskTotExecTime;} TASK_USER_DATA;OS_STK TaskStartStk[TASK_STK_SIZE];OS_STK TaskClkStk[TASK_STK_SIZE];OS_STK Task1Stk[TASK_STK_SIZE];OS_STK Task2Stk[TASK_STK_SIZE];OS_STK Task3Stk[TASK_STK_SIZE];OS_STK Task4Stk[TASK_STK_SIZE];OS_STK Task5Stk[TASK_STK_SIZE];TASK_USER_DATA TaskUserData[7];OS_EVENT *MsgQueue; /*消息队列指针*/ void *MsgQueueTbl[20]; /*消息存储*/void main (void){PC_DispClrScr(DISP_BGND_BLACK);OSInit();PC_DOSSaveReturn();PC_VectSet(uCOS, OSCtxSw);PC_ElapsedInit();strcpy(TaskUserData[TASK_START_ID].TaskName, "StartTask"); OSTaskCreateExt(TaskStart,(void *)0,&TaskStartStk[TASK_STK_SIZE - 1],TASK_START_PRIO,TASK_START_ID,&TaskStartStk[0],TASK_STK_SIZE,&TaskUserData[TASK_START_ID],0);OSStart();}void TaskStart (void *pdata){#if OS_CRITICAL_METHOD == 3OS_CPU_SR cpu_sr;#endifINT16S key;pdata = pdata;TaskStartDispInit();OS_ENTER_CRITICAL();PC_VectSet(0x08, OSTickISR);PC_SetTickRate(OS_TICKS_PER_SEC);OS_EXIT_CRITICAL();OSStatInit();MsgQueue = OSQCreate(&MsgQueueTbl[0], MSG_QUEUE_SIZE); /*创建消息队列,大小为*/TaskStartCreateTasks();for (;;) {TaskStartDisp();if (PC_GetKey(&key)) {if (key == 0x1B) {PC_DOSReturn();}}OSCtxSwCtr = 0;OSTimeDly(OS_TICKS_PER_SEC);}}void Task1 (void *pdata){char *msg;INT8U err;pdata = pdata;for (;;) {msg = (char *)OSQPend(MsgQueue, 0, &err);//从消息队列中取消息PC_DispStr(70, 13, msg, DISP_FGND_YELLOW + DISP_BGND_BLUE);OSTimeDlyHMSM(0, 0, 0, 100);}}void Task2 (void *pdata)char msg[20];pdata = pdata;strcpy(&msg[0], "Task 2");for (;;) {OSQPost(MsgQueue, (void *)&msg[0]);//发送消息到队列中 OSTimeDlyHMSM(0, 0, 0, 500);}}void Task3 (void *pdata){char msg[20];pdata = pdata;strcpy(&msg[0], "Task 3");for (;;) {OSQPost(MsgQueue, (void *)&msg[0]);//发送消息到队列中 OSTimeDlyHMSM(0, 0, 0, 500);}}void Task4 (void *pdata){char msg[20];pdata = pdata;strcpy(&msg[0], "Task 4");for (;;) {OSQPost(MsgQueue, (void *)&msg[0]);//发送消息到队列中 OSTimeDlyHMSM(0, 0, 0, 500);}}void OSTaskStatHook (void)char s[80];INT8U i;INT32U total;INT8U pct;total = 0L; /* Totalize TOT. EXEC. TIME for each t askfor (i = 0; i < 7; i++) {total += TaskUserData[i].TaskTotExecTime;DispTaskStat(i); /* Display task data }if (total > 0) {for (i = 0; i < 7; i++) { /* Derive percentage of each task */pct = 100 * TaskUserData[i].TaskTotExecTime / total;sprintf(s, "%3d %%", pct);PC_DispStr(62, i + 11, s, DISP_FGND_BLACK + DISP_BGND_LIGHT_GRA Y);}}if (total > 1000000000L) { /* Reset total time counters at 1 billionfor (i = 0; i < 7; i++) {TaskUserData[i].TaskTotExecTime = 0L;}}}void OSTaskSwHook (void){INT16U time;TASK_USER_DATA *puser;time = PC_ElapsedStop(); /* This task is donePC_ElapsedStart(); /* Start for next task puser = OSTCBCur->OSTCBExtPtr; /* Point to used dataif (puser != (TASK_USER_DATA *)0) {puser->TaskCtr++; /* Increment task counter puser->TaskExecTime = time; /* Update the task's execution timepuser->TaskTotExecTime += time; /* Update the task's total executi on time}}运行结果:uC/OS-II源码分析(三)首先来了解下实时系统的基本概念:1)临界区,共享资源,任务(类似于进程),任务切换,任务调度,可剥夺型内核,可重入函数,动态优先级调度,2)如何处理优先级反转问题。
uC OS II

调度工作的内容可以分为两部分:最高优先级任务的寻找和任务切换。其最高优先级任务的寻找是通过建立 就绪任务表来实现的。
中断机理
1
引言
2
组成部分
3
中断处理
4
函数调用
5
中断调用
在嵌入式操作系统领域,由Jean J. Labrosse开发的μC/OS,由于开放源代码和强大而稳定的功能,曾经 一度在嵌入式系统领域引起强烈反响。而其本人也早已成为了嵌入式系统会议(美国)的顾问委员会的成员。
(综合电子论坛)
优先级
优先翻转
运行机制
翻转解决
在嵌入式系统的应用中,实时性是一个重要的指标,而优先级翻转是影响系统实时性的重要问题。本文着重 分析优先级翻转问题的产生和影响,以及在uC/OS-II中的解决方案。
在ANSI C中是使用malloc和free两个函数来动态分配和释放内存。但在嵌入式实时系统中,多次这样的操作 会导致内存碎片,且由于内存管理算法的原因,malloc和free的执行时间也是不确定。
uC/OS-II中把连续的大块内存按分区管理。每个分区中包含整数个大小相同的内存块,但不同分区之间的内 存块大小可以不同。用户需要动态分配内存时,系统选择一个适当的分区,按块来分配内存。释放内存时将该块 放回它以前所属的分区,这样能有效解决碎片问题,同时执行时间也是固定的。
时钟部分(OSTime.c) μC/OS-II中的最小时钟单位是timetick(时钟节拍)。任务延时等操作是在这里完 成的。
uCOS-II中断服务程序代码分析

uC/OS-II中断服务程序代码分析在讲解uC/OS-II中断处理机制之前,大家要了解uC/OS-II中对于中断函数的处理机制。
我们在查阅《嵌入式实时操作系统μCOS-II(第二版)》书中P103页时,可以查看到在uC/OS-II中的中断处理机制如下描述:Void ISP_Function( void ){保存全部的CPU寄存器;调用OSIntEnter()或OSIntNesting++;If(OSIntNesting == 1 ){OSTCBCur->OSTCBStkPtr= SP ;}清中断源;重新打开中断;执行用户代码做中断服务;调用OSIntExit();恢复所有CPU寄存器;执行中断返回指令;}在uC/OS-II中,对于中断中的任务切换,是用两个函数实现的1、OSIntEnter()2、OSIntExit()在OSIntEnter()中将用于记录中断嵌套的计数器OSIntNesting自增1;其函数源代码如下:voidOSIntEnter (void){if (OSRunning == OS_TRUE){if (OSIntNesting < 255u){OSIntNesting++;}}这里我想提醒大家的是:uC/OS-II的程序设计思想是非常严谨的,OSIntEnter首先判断中断发生是否在uC/OS-II整个系统启动后,如果不是这样的话,那么所有的uC/OS-II的系统功能均不能提供给用户使用。
void OSIntExit(void)函数时用于在用户编写的中断服务代码执行完毕以后,调用该函数,从而实现uC/OS-II 始终保证“处于就绪状态下优先级最高的任务始终能得到系统的所有资源。
void OSIntExit(void)函数源代码如下:voidOSIntExit (void){#if OS_CRITICAL_METHOD == 3OS_CPU_SR cpu_sr = 0;#endifif (OSRunning == OS_TRUE){OS_ENTER_CRITICAL();if (OSIntNesting > 0){OSIntNesting--;if (OSIntNesting == 0){if (OSLockNesting == 0){OS_SchedNew();if (OSPrioHighRdy != OSPrioCur){OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];#if OS_TASK_PROFILE_EN > 0OSTCBHighRdy->OSTCBCtxSwCtr++;#endifOSCtxSwCtr++;OSIntCtxSw();}}}OS_EXIT_CRITICAL();}}这部分代码的意思,大家自己想一下了,这里我主要讲解一下在OS_CPU_A.a文件中的OSIntCtxSw()函数了。
Ucos_II2.52源码中文译注

作者:钟常慰Ucos_II2.52是一份非常完美的嵌入式开发系统,在学习ARM的基础上,嵌入ucos系统并增加自己的源码是一件不错的选择,目前在市面上已经有了大量的ucos嵌入案例,特别是在arm和dsp的应用当中,已经成为一种主流,虽然和其它的嵌入式系统相比,ucos不是很完善,如没有内存分配、任务级别不多;但却是一个代码简短、条理清晰、实时性及安全性能很高的嵌入式操作系统。
Ucos_II2.52对比2.8版的256个任务而言,任务数量相比过少,但却是目前应用量最大的一个版本,相对而言,能够满足我们的基本要求,而且增加了很多消息处理,特别是在优先级别方面,具有不可比拟的优势;我曾试图阅读ecos的源码,但还是失败了,还有挑战linux0.01版源码的想法,最终我不能不被屈服;对于Ucos而言,很多入门者是一个福音,因为它的代码非常的少,而且能够对应贝贝老师的书本直接参考,他的书本对结构方面讲解的极为xian详细。
在学习Ucos的整个过程中,E文的理解是一个致命的打击,原因是我的E文水平很差,不过Ucos还是给了我尝试的动力,在作者的原基础上增加中文译码,也许是一件非常不错的选择,相信在中国和我这种水平的人多不胜数,中文的注解对源码而言,能够具有极高的理解价值,可以在极短的时间内,能够充分了解ucos的真正含义。
整个翻译过程历时4个月,每每在寒冬腊月坐在计算机前面,不断的查阅贝贝老师的书来对整个Uco s进行理解,对每个源码进行逐条翻译,也是一件非常需要勇气的事情,但E文的翻译过程中很多变量是不能完全理解的,所以在翻译过程中不乏错误译文很多,于此带来的错误还请读者纠正,相信克服种种困难一定会有所了解的。
对于经济窘迫的我来说,曾试图希望卖一点资料来养家糊口,但这种做法根本不现实,很多的读者可能和我一样,习惯了拿不收费的资料,并对变相收费有一种深恶痛绝的感觉;想了很多决定还是把它贡献出来,让更多的人来(更容易)了解ucos,贡献自己的一点力量。
uCOS-II实时操作系统内核源代码注释

/*********************************************************************************************************** uC/OS-II* 实时操作内核* 内存管理** (c) 版权 1992-2002, Jean J. Labrosse, Weston, FL* All Rights Reserved** 文件名 : OS_MEM.C* 作者 : Jean J. Labrosse* 注释 : 彭森 2007/9/2**********************************************************************************************************/#ifndef OS_MASTER_FILE#include "includes.h"#endif#if (OS_MEM_EN > 0) && (OS_MAX_MEM_PART > 0)/*********************************************************************************************************** 创建一个内存分区** 说明 : 创建一个固定大小的内存分区,这个内存分区通过uC/OS-II管理。
** 参数 : addr 内存分区的起始地址** nblks 来自分区的内存块的数目.** blksize 每个内存分区中的内存块的大小.** err 指向一个变量包含错误的信息,这个信息通过这个函数或其他来设置:** OS_NO_ERR 内存分区创建正确返回值. * OS_MEM_INVALID_ADDR 指定的是无效地址作为分区的内存存储空间* OS_MEM_INVALID_PART 没有未使用的有用的分区* OS_MEM_INVALID_BLKS 使用者指定一个无效的内存块(必须 >= 2)* OS_MEM_INVALID_SIZE 使用者指定一个无效的内存空间值* (必须大于指针的空间大小) * 返回值 : != (OS_MEM *)0 分区被创建* == (OS_MEM *)0 分区没有被创建由于无效的参数,没有未使用的分区可用**********************************************************************************************************/OS_MEM *OSMemCreate (void *addr, INT32U nblks, INT32U blksize, INT8U *err){#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register 为CPU状态寄存器分配存储空间 */OS_CPU_SR cpu_sr;#endifOS_MEM *pmem;INT8U *pblk;void **plink;INT32U i;#if OS_ARG_CHK_EN > 0if (addr == (void *)0) { /* Must pass a valid address for the memory part. 必须是一个有效的内存部分*/*err = OS_MEM_INVALID_ADDR;return ((OS_MEM *)0);}if (nblks < 2) { /* Must have at least 2 blocks per partition 每个分区至少有两个分区*/*err = OS_MEM_INVALID_BLKS;return ((OS_MEM *)0);}if (blksize < sizeof(void *)) { /* Must contain space for at least a pointer 必须包含空间至少有一个指针*/*err = OS_MEM_INVALID_SIZE;return ((OS_MEM *)0);}#endifOS_ENTER_CRITICAL();pmem = OSMemFreeList; /* Get next free memory partition 得到下一个未使用的内存分区*/if (OSMemFreeList != (OS_MEM *)0) { /* See if pool offree partitions was empty 查看是否有未使用的分区空间是空的*/OSMemFreeList = (OS_MEM *)OSMemFreeList->OSMemFreeList;}OS_EXIT_CRITICAL();if (pmem == (OS_MEM *)0) { /* See if we have a memory partition 查看是否我们有一个内存分区*/*err = OS_MEM_INVALID_PART;return ((OS_MEM *)0);}plink = (void **)addr; /* Create linked list of free memory blocks 创建未使用的内存块的可连接的列表*/pblk = (INT8U *)addr + blksize;for (i = 0; i < (nblks - 1); i++) {*plink = (void *)pblk;plink = (void **)pblk;pblk = pblk + blksize;}*plink = (void *)0; /* Last memory block points to NULL 最后的内存块指针指向NULL*/pmem->OSMemAddr = addr; /* Store start address of memory partition 保存内存分区的起始地址*/pmem->OSMemFreeList = addr; /* Initialize pointer to pool of free blocks 为未使用的内存块初始化指针*/pmem->OSMemNFree = nblks; /* Store number of free blocks in MCB 在内存控制块中保存许多未使用的内存块*/pmem->OSMemNBlks = nblks;pmem->OSMemBlkSize = blksize; /* Store block size of each memory blocks 保存每个内存块的块大小*/*err = OS_NO_ERR;return (pmem);}/*$PAGE*//********************************************************************** ************************************* 得到一个内存控制块** 说明 : 从分区中得到一个内存块** 参数 : pmem 指针指向一个内存分区控制块** err 指向一个变量包含一个错误信息,这个错误信息通过这个函数或其他来设计** OS_NO_ERR 假如内存分区被正确的创建. * OS_MEM_NO_FREE_BLKS 假如没有更多的内存块被分配给调用者* OS_MEM_INVALID_PMEM 假如已经为'pmem'通过一个空指针** 返回值 : 一个指针指向一个内存控制块,假如没有察觉错误* 一个指针指向空指针,假如有错误被察觉到********************************************************************* *************************************/void *OSMemGet (OS_MEM *pmem, INT8U *err){#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register 为CPU状态寄存器分配存储空间 */OS_CPU_SR cpu_sr;#endifvoid *pblk;#if OS_ARG_CHK_EN > 0if (pmem == (OS_MEM *)0) { /* Must point to a valid memory partition 必须指向一个有效的内存分区 */*err = OS_MEM_INVALID_PMEM;return ((OS_MEM *)0);}#endifOS_ENTER_CRITICAL();if (pmem->OSMemNFree > 0) { /* See if there are any free memory blocks 查看是否有其他未使用的内存块*/pblk = pmem->OSMemFreeList; /* Yes, point to next free memory block 是的,指针指向下一个未使用的内存块*/ pmem->OSMemFreeList = *(void **)pblk; /* Adjust pointer to new free list 调整指针指向一个新的未使用的空间列表*/pmem->OSMemNFree--; /* Oneless memory block in this partition 在这个分区中减少一个内存块*/OS_EXIT_CRITICAL();*err = OS_NO_ERR; /* Noerror 没有错误*/return (pblk); /* Return memory block to caller 返回内存控制块给调用者*/}OS_EXIT_CRITICAL();*err = OS_MEM_NO_FREE_BLKS; /* No, Notify caller of empty memory partition 告知调用者是空的内存分区*/return ((void *)0); /* Return NULL pointer to caller 返回NULL个调用者*/}/*$PAGE*//********************************************************************** ************************************* 释放一个内存块** 说明 : 返回一个内存块给分区** 参数 : pmem 指针指向内存分区控制块** pblk 指针指向被保留的内存块.** 返回值 : OS_NO_ERR 假如内存块是被插入的分区* OS_MEM_FULL 假如返回一个已经全部内存分区的内存块* (你释放了更多你分配的内存块!)* OS_MEM_INVALID_PMEM 假如指针'pmem'指向NULL。
uCOSII的源代码

052 INT16U const OSFlagNameSize = OS_FLAG_NAME_SIZE; /* Size (in bytes) of flag names */
053
054 INT16U const OSLowestPrio = OS_LOWEST_PRIO;
069
070 INT16U const OSPtrSize = sizeof(void *); /* Size in Bytes of a pointer */
071
072 INT16U const OSQEn = OS_Q_EN;
022 */
023
024 INT16U const OSDebugEn = OS_DEBUG_EN; /* Debug constants are defined below */
025
026 #if OS_DEBUG_EN > 0
048 INT16U const OSFlagNodeSize = 0;
049 INT16U const OSFlagWidth = 0;
050 #endif
051 INT16U const OSFlagMax = OS_MAX_FLAGS;
083
084 INT16U const OSStkWidth = sizeof(OS_STK); /* Size in Bytes of a stack entry */
085
086 INT16U const OSTaskCreateEn = OS_TASK_CREATE_EN;
087 INT16U const OSTaskCreateExtEn = OS_TASK_CREATE_EXT_EN;
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
OS_ENTER_CRITICAL(); if (OSIntNesting > 0) { OS_EXIT_CRITICAL(); return (OS_ERR_TASK_CREATE_ISR); } if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { OSTCBPrioTbl[prio] = (OS_TCB *)1;
OSCtxSwCtr = 0; OSIdleCtr =0L; 其中包括:中断嵌套标志 OSIntNesting,调度锁定标志 OSLockNesting,OS 标志 OSRunning 等。OSRunning 在这里设置为 FALSE,在后面 OSStartHighRdy 中会被设 置为 TRUE 表示 OS 开始工作。 OS_InitRdyList()初始化就绪 Task 列表: static void OS_InitRdgt; 0) && (OS_MAX_MEM_PART > 0) OS_MemInit(); #endif #if (OS_Q_EN > 0) && (OS_MAX_QS > 0) OS_QInit(); #endif OS_InitTaskIdle(); #if OS_TASK_STAT_EN > 0 OS_InitTaskStat(); #endif #if OS_VERSION >= 204 OSInitHookEnd(); #endif #if OS_VERSION >= 270 && OS_DEBUG_EN > 0 OSDebugInit(); #endif } OS_InitMisc()完成的是一些其其他他的变量的初始化: OSIntNesting = 0; OSLockNesting = 0; OSTaskCtr = 0; OSRunning = FALSE;
for (i = 0; i < (OS_MAX_EVENTS - 1); i++) { pevent1->OSEventType = OS_EVENT_TYPE_UNUSED; pevent1->OSEventPtr = pevent2; #if OS_EVENT_NAME_SIZE > 1 pevent1->OSEventName[0] = '?'; pevent1->OSEventName[1] = OS_ASCII_NUL; #endif pevent1++; pevent2++; } pevent1->OSEventType = OS_EVENT_TYPE_UNUSED; pevent1->OSEventPtr = (OS_EVENT *)0; #if OS_EVENT_NAME_SIZE > 1 pevent1->OSEventName[0] = '?'; pevent1->OSEventName[1] = OS_ASCII_NUL; #endif OSEventFreeList = &OSEventTbl[0]; #else OSEventFreeList = &OSEventTbl[0]; OSEventFreeList->OSEventType = OS_EVENT_TYPE_UNUSED; OSEventFreeList->OSEventPtr = (OS_EVENT *)0; #if OS_EVENT_NAME_SIZE > 1
OSEventFreeList->OSEventName[0] = '?'; OSEventFreeList->OSEventName[1] = OS_ASCII_NUL; #endif #endif #endif } 同样将 EventTbl[]数组中的 OSEventType 都初始化为 OS_EVENT_TYPE_UNUSED。 OS_InitTaskIdle(),中间我们跳过其他的如 Mem 等的初始化,看看 Idle Task 的初始化。 (void)OSTaskCreateExt(OS_TaskIdle, (void *)0, &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1], OS_IDLE_PRIO, OS_TASK_IDLE_ID, &OSTaskIdleStk[0], OS_TASK_IDLE_STK_SIZE, (void *)0, OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); 其实 Idle Task 的初始化很简单就是调用 OSTaskCrete 系列的函数创建一个 Task, OSTaskCreate 我们后面再做进一步分析。 初始化 State Task 也是类似调用 OSTaskCreate 系列函数创建 Stat Task。这里只是创建了 该 Task 的各个结构还没有真正运行该 Task,直到 OSStart 中才依据优先级调度运行。 OK,到这里 OSInit 算高一个段落了,我们接着回到 main 往下看
INT8U i; INT8U *prdytbl; OSRdyGrp = 0x00; prdytbl = &OSRdyTbl[0]; for (i = 0; i < OS_RDY_TBL_SIZE; i++) { *prdytbl++ = 0x00; } OSPrioCur = 0; OSPrioHighRdy = 0; OSTCBHighRdy = (OS_TCB *)0; OSTCBCur = (OS_TCB *)0; }
uC/OS-II 源码分析(总体思路 二) OSTaskCreate 负责创建 Task 所需的数据结构,该函数原形如下所示: INT8U OSTaskCreate (void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT8U prio) 其中 task 是一个函数指针,指向该 Task 所开始的函数,当这个 Task 第一次被调度运行时 将会从 task 处开始运行。 p_arg 是传给 task 的参数指针; ptos 是堆栈指针,指向栈顶(堆栈从上往下)或栈底(堆栈从下往上); prio 是进程的优先级,uC/OS-II 共支持最大 64 个优先级,其中最低的两个优先级给 Idle 和 Stat 进程,并且各个 Task 的优先级必须不同。 接下来,我们看看这个函数的执行流程: #if OS_ARG_CHK_EN > 0 if (prio > OS_LOWEST_PRIO) {
ptcb1->OSTCBNext = (OS_TCB *)0; #if OS_TASK_NAME_SIZE > 1 ptcb1->OSTCBTaskName[0] = '?'; ptcb1->OSTCBTaskName[1] = OS_ASCII_NUL; #endif OSTCBList = (OS_TCB *)0; OSTCBFreeList = &OSTCBTbl[0]; } 这里完成的工作很简单,首先把整个数组使用 OSTCBNext 指针连接成链表链起来,然后将 OSTCBList 初始化为 0,也就是还没有 TCB,因为还没有 Task 产生,OSTCBFreeList 指 向 OSTCBTbl[]数组的第一个表示所有 TCB 都处于 Free 状态。 OS_InitEventList()初始化 Event 列表。 static void OS_InitEventList (void) { #if OS_EVENT_EN && (OS_MAX_EVENTS > 0) #if (OS_MAX_EVENTS > 1) INT16U i; OS_EVENT *pevent1; OS_EVENT *pevent2; OS_MemClr((INT8U *)&OSEventTbl[0], sizeof(OSEventTbl)); pevent1 = &OSEventTbl[0]; pevent2 = &OSEventTbl[1];
INT8U i; OS_TCB *ptcb1; OS_TCB *ptcb2; OS_MemClr((INT8U *)&OSTCBTbl[0], sizeof(OSTCBTbl)); OS_MemClr((INT8U *)&OSTCBPrioTbl[0], sizeof(OSTCBPrioTbl)); ptcb1 = &OSTCBTbl[0]; ptcb2 = &OSTCBTbl[1]; for (i = 0; i < (OS_MAX_TASKS + OS_N_SYS_TASKS - 1); i++) { ptcb1->OSTCBNext = ptcb2; #if OS_TASK_NAME_SIZE > 1 ptcb1->OSTCBTaskName[0] = '?'; ptcb1->OSTCBTaskName[1] = OS_ASCII_NUL; #endif ptcb1++; ptcb2++; }
uC/OS-II 源码分析(总体思路 一) 首先从 main 函数开始,下面是 uC/OS-II main 函数的大致流程: int main() {
OSInit(); TaskCreate(...); OSStart(); } 首先是调用 OSInit 进行初始化,然后使用 TaskCreate 创建几个进程/Task,最后调 用 OSStart,操作系统就开始运行了。 OSInit 最先看看 OSInit 完成哪些初始化: void OSInit (void) { #if OS_VERSION >= 204 OSInitHookBegin(); #endif OS_InitMisc(); OS_InitRdyList(); OS_InitTCBList(); OS_InitEventList(); #if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0) OS_FlagInit(); #endif