基于51单片机的UCOSII例程以及内核分析(揭开UCOSII“神秘”面纱)
以下只代表个人观点,理解有错误的地方还请大家指出,谢谢先上传文件:
注:内核文件是我买的书《uC/OS-II内核分析、移植与驱动程序开发》光盘里的
内核文件ourdev_309642.rar(文件大小:75K) (原文件名:source.rar)
工程文件ourdev_309643.rar(文件大小:260K) (原文件名:all.rar)
学习板原理图ourdev_309644.pdf(文件大小:30K) (原文件名:Protel Schematic.pdf)
编绎软件:keil uv2 九级优化
Program Size: data=84.0 xdata=635 code=5486
程序功能:时钟IC RX8025显示时间 按键扫描(未做按键处理)
MCU:STC89C54RD+
晶振:18.432M 6T/双倍速(相当于普通AT89C51的36.864M)
接下来还会对UCOSII内核进行分析,以加深自己对它的理解
UCOSII里面用了大量的结构,指针,链表等知识,结构用来封装数据块,指针用来把数据块链接起来
比如里面有一个很重要的数据块OS_TCB的定义如下:
typedef struct os_tcb {
OS_STK *OSTCBStkPtr; /* 指向当前数据块的起始地址*/
#if OS_TASK_CREATE_EXT_EN > 0
void *OSTCBExtPtr; /* Pointer to user definable data for TCB extension */
OS_STK *OSTCBStkBottom; /* Pointer to bottom of stack */
INT32U OSTCBStkSize; /* Size of task stack (in number of stack elements) */
INT16U OSTCBOpt; /* Task options as passed by OSTaskCreateExt() */
INT16U OSTCBId; /* Task ID (0..65535) */
#endif
struct os_tcb *OSTCBNext; /* 指向下一个数据块*/
struct os_tcb *OSTCBPrev; /* 指向前一个数据块*/
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0) || (OS_SEM_EN > 0) || (OS_MUTEX_EN > 0)
OS_EVENT *OSTCBEventPtr; /* Pointer to event control block */
#endif
#if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0)
void *OSTCBMsg; /* Message received from OSMboxPost() or OSQPost() */
#endif
#if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
#if OS_TASK_DEL_EN > 0
OS_FLAG_NODE*OSTCBFlagNode; /* Pointer to event flag node */
#endif
OS_FLAGS OSTCBFlagsRdy; /* Event flags that made task ready to run */
#endif
INT16U OSTCBDly; /* 任务延时节拍数*/
INT8U OSTCBStat; /* 任务状态:挂起,就绪,运行等*/
INT8U OSTCBPrio; /* 任务优先级*/
INT8U OSTCBX; /* Bit position in groupcorresponding to task priority (0..7) */
INT8U OSTCBY; /* Index into ready table corresponding to task priority */
INT8U OSTCBBitX; /* Bit mask to access bit position in ready table */
INT8U OSTCBBitY; /* Bit mask to access bit position in ready group */
#if OS_TASK_DEL_EN > 0
BOOLEAN OSTCBDelReq; /* Indicates whether a task needs to delete itself */
#endif
} OS_TCB;
在创建一个任务的时候时会创建一个对应的 OS_TCB 数据块,这个数据块涉及到系统对任务的大部分操作
当没有创建任务时,main函数里面就只有二个函数调用
void main(void)
{
OSInit();
OSStart();
}
OSInit();完成初始化工作
OSStart();函数启运优先级最高的任务运行,如果没有创建任务,就是空闲任务和统计任务(如果允许) 下面先分析OSInit();函数
OSInit()函数在OS_CORE.C文件里面
定义如下:
voidOSInit (void) reentrant
{
#if OS_VERSION >= 204 && OS_CPU_HOOKS_EN > 0
OSInitHookBegin(); /* Call port specific initialization code */
#endif
OS_InitMisc(); /* Initialize miscellaneous variables */
OS_InitRdyList(); /* Initialize the Ready List */
OS_InitTCBList(); /* Initialize the free list of OS_TCBs */
OS_InitEventList(); /* Initialize the free list of OS_EVENTs */
#if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
OS_FlagInit(); /* Initialize the event flag structures */
#endif
#if (OS_MEM_EN > 0) && (OS_MAX_MEM_PART > 0)
OS_MemInit(); /* Initialize the memory manager */
#endif
#if (OS_Q_EN > 0) && (OS_MAX_QS > 0)
OS_QInit(); /* Initialize the message queue structures*/
#endif
OS_InitTaskIdle(); /* Create the Idle Task */
#if OS_TASK_STAT_EN > 0
OS_InitTaskStat(); /* Create the Statistic Task */
#endif
#if OS_VERSION >= 204 && OS_CPU_HOOKS_EN > 0
OSInitHookEnd(); /* Call port specific init. code */
#endif
}
下面进行分析:
配置文件中,我把OS_CPU_HOOKS_EN置成0,所以所有的钩子函数都不会产生,即函数名中有HOOK的
所以第一个执行的函数是:OS_InitMisc()
这个函数是初始化变量的函数,在OS_CORE.C里面,定义如下:
staticvoidOS_InitMisc (void)
{
#if OS_TIME_GET_SET_EN > 0
OSTime = 0L; /* Clear the 32-bit system clock */
#endif
OSIntNesting= 0; /* 中断层数Clear the interrupt nesting counter */
OSLockNesting = 0; /* Clear the scheduling lock counter */
OSTaskCtr = 0; /* 任务数目Clear the number of tasks */
OSRunning = FALSE; /* 运行状态Indicate that multitasking not started */
OSCtxSwCtr = 0; /* 任务切换次数Clear the context switch counter */
OSIdleCtr = 0L; /* 空闲计数器Clear the 32-bit idle counter */
#if (OS_TASK_STAT_EN > 0) && (OS_TASK_CREATE_EXT_EN > 0)
OSIdleCtrRun= 0L;
OSIdleCtrMax= 0L;
OSStatRdy = FALSE; /* Statistic task is not ready */
#endif
} 接下来运行的函数是OS_InitRdyList();
这是初始化任务优先级链表的函数,也在OS_CORE.C文件中,定义如下:
staticvoidOS_InitRdyList (void)
{
INT16U i;
INT8U *prdytbl;
OSRdyGrp = 0x00; /* Clear the ready list */
prdytbl = &OSRdyTbl;
for (i = 0; i < OS_RDY_TBL_SIZE; i++) {
*prdytbl++ = 0x00;
}
OSPrioCur = 0;
OSPrioHighRdy = 0;
OSTCBHighRdy= (OS_TCB *)0;
OSTCBCur = (OS_TCB *)0;
}
这是一个把任务优先级数据块全部清零的函数,这里需要讲一下UCOSII任务优先级的处理方法
任务优先级数据块应该叫就绪表
就绪表中有二个变量OSRdyGrp和OSRdyTbl[],定义如下:
#define OS_RDY_TBL_SIZE ((OS_LOWEST_PRIO)/8 +1)
OS_EXT INT8U OSRdyGrp;
OS_EXT INT8U OSRdyTbl;
它们分别是一个8位的变量和一个字符数组
在OSRdyGrp中,任务按优先级分组,8个任务一组,OSRdyTbl[]数组中的每一字节表示8个任务的就绪状态,即任务中每一组中是否有进入就绪状态的任务。
而OSRdyGrp中的每一位,对应了每一组中是否有任意一个进入就绪状态,如果有任意一个进入了就绪态,则OSRdyGrp中对应的位置1,OSRdyTbl对应其最低位,OSRdyTbl对应其最高位,低位的优先级高于高位的优先级,由此也可以算出目前支持的任务数目为8*8即64个任务,而系统在切换任务的时候都是基于就绪表来的,即找出就绪表中优先级最高的任务,然后运行。
任务的优先级在OS_TCB里面的定义是INT8U OSTCBPrio
这是一个字节的变量,受任务数的限制,它的范围为0--63,用二进制表示的话高二位永远为0,按照优先级分组分成8组,分别为0--7,8--15。。。。56--63
0--7和8--15,一一比较的话低三位全部相同(比如0和8比较,1和9比较)只是次低三位不一样,按照这样计算,次低三位对应着OSRdyTbl的下标,而低三位对应于对应下标的数组数据,比如任务优先级8的任务就绪,可以算出为OSRdyTbl的最低位为1;
这里也说不是太清楚。后面再讲这方面。 标记 标记一下 mark 接下来是运行 OS_InitEventList(); 初始化事件数据链表
也是在OS_CORE.C文件中,定义如下:
staticvoidOS_InitEventList (void)
{
#if (OS_EVENT_EN > 0) && (OS_MAX_EVENTS > 0)
#if (OS_MAX_EVENTS > 1)
INT16U i;
OS_EVENT*pevent1;
OS_EVENT*pevent2;
pevent1 = &OSEventTbl;
pevent2 = &OSEventTbl;
for (i = 0; i < (OS_MAX_EVENTS - 1); i++) { /* 把事件控制块链接起来*/
pevent1->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent1->OSEventPtr= pevent2;
pevent1++;
pevent2++;
}
pevent1->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent1->OSEventPtr= (OS_EVENT *)0; //最后一个控制块指向空
OSEventFreeList = &OSEventTbl; //指向第一个控制块
#else
OSEventFreeList = &OSEventTbl; /* Only have ONE event control block */
OSEventFreeList->OSEventType = OS_EVENT_TYPE_UNUSED;
OSEventFreeList->OSEventPtr= (OS_EVENT *)0;
#endif
#endif
}
其中OS_EVENT是一个结构体,定义如下:(在UCOS_II.H文件中)
typedef struct {
INT8U OSEventType; /* 事件控制块的类型*/
INT8U OSEventGrp; /* Group corresponding to tasks waiting for event to occur*/
INT16UOSEventCnt; /* 计数器*/
void *OSEventPtr; /* Pointer to message or queue structure */
INT8U OSEventTbl; /* 等待链表*/
} OS_EVENT;
事件控制块在创建使用事件的时候会用到,比如信号量。
系统会事先创建“一串”空的控制块空间,系统有一个指针,指向第一个空的控制块
当需要创建事件控制块的时候,会使用第一个空的控制块,系统的那个指针往后移一个位置
所以在使用控制块的时候,一定要在配置文件里配置好,比如上面的对应的配置文件(OS_CFG.H)中的配置为:
#define OS_MAX_EVENTS
这里定义了会使用到的控制块的最大数量。而初使化的作用就是把这些控制块链接起来 接下来是OS_InitTaskIdle();即初使化空闲任务
它的定义如下:
staticvoidOS_InitTaskIdle (void)
{
#if OS_TASK_CREATE_EXT_EN > 0
#if OS_STK_GROWTH == 1
(void)OSTaskCreateExt(OS_TaskIdle,
(void *)0, /* No arguments passed to OS_TaskIdle() */
&OSTaskIdleStk, /* Set Top-Of-Stack */
OS_IDLE_PRIO, /* Lowest priority level */
OS_TASK_IDLE_ID,
&OSTaskIdleStk, /* Set Bottom-Of-Stack */
OS_TASK_IDLE_STK_SIZE,
(void *)0, /* No TCB extension */
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack checking + clear stack*/
#else
(void)OSTaskCreateExt(OS_TaskIdle,
(void *)0, /* No arguments passed to OS_TaskIdle() */
&OSTaskIdleStk, /* Set Top-Of-Stack */
OS_IDLE_PRIO, /* Lowest priority level */
OS_TASK_IDLE_ID,
&OSTaskIdleStk, /* Set Bottom-Of-Stack */
OS_TASK_IDLE_STK_SIZE,
(void *)0, /* No TCB extension */
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack checking + clear stack*/
#endif
#else
#if OS_STK_GROWTH == 1
(void)OSTaskCreate(OS_TaskIdle,
(void *)0,
&OSTaskIdleStk,
OS_IDLE_PRIO);
#else
(void)OSTaskCreate(OS_TaskIdle,
(void *)0,
&OSTaskIdleStk,
OS_IDLE_PRIO);
#endif
#endif
}
其实就是创建空闲任务,空闲任务优先级为最低 之后有个统计任务,定义如下:
#if OS_TASK_STAT_EN > 0
staticvoidOS_InitTaskStat (void)
{
#if OS_TASK_CREATE_EXT_EN > 0
#if OS_STK_GROWTH == 1
(void)OSTaskCreateExt(OS_TaskStat,
(void *)0, /* No args passed to OS_TaskStat()*/
&OSTaskStatStk, /* Set Top-Of-Stack */
OS_STAT_PRIO, /* One higher than the idle task*/
OS_TASK_STAT_ID,
&OSTaskStatStk, /* Set Bottom-Of-Stack */
OS_TASK_STAT_STK_SIZE,
(void *)0, /* No TCB extension */
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack checking + clear*/
#else
(void)OSTaskCreateExt(OS_TaskStat,
(void *)0, /* No args passed to OS_TaskStat()*/
&OSTaskStatStk, /* Set Top-Of-Stack */
OS_STAT_PRIO, /* One higher than the idle task*/
OS_TASK_STAT_ID,
&OSTaskStatStk, /* Set Bottom-Of-Stack */
OS_TASK_STAT_STK_SIZE,
(void *)0, /* No TCB extension */
OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack checking + clear*/
#endif
#else
#if OS_STK_GROWTH == 1
(void)OSTaskCreate(OS_TaskStat,
(void *)0, /* No args passed to OS_TaskStat()*/
&OSTaskStatStk, /* Set Top-Of-Stack */
OS_STAT_PRIO); /* One higher than the idle task*/
#else
(void)OSTaskCreate(OS_TaskStat,
(void *)0, /* No args passed to OS_TaskStat()*/
&OSTaskStatStk, /* Set Top-Of-Stack */
OS_STAT_PRIO); /* One higher than the idle task*/
#endif
#endif
}
#endif
也就是创建统计任务,统计任务的优先级为比空闲任务低一级,统计任务每秒钟运行一次(如果使能)
到这里就完成了UCOSII的初始化工作,接下来主要讲述运行原理。 MARK 以下是OSStart的定义
voidOSStart (void) reentrant
{
INT8U y;
INT8U x;
if (OSRunning == FALSE) {
y = OSUnMapTbl; /* Find highest priority's task priority number */
x = OSUnMapTbl];
OSPrioHighRdy = (INT8U)((y << 3) + x);
OSPrioCur = OSPrioHighRdy;
OSTCBHighRdy= OSTCBPrioTbl; /* Point to highest priority task ready to run */
OSTCBCur = OSTCBHighRdy;
OSStartHighRdy(); /* Execute target specific code to start task */
}
}
它做的事情就是启动优先级最高的任务。启运之后将永远不会返回。 结束了吗? mark 顶 作记号先,但在移植自己开书就很多不懂啊,能移植部分以51为例作一个解释吗? 好 学习学习哈 学习 mark
结构体真是太强悍了,所有高级应用基本都是从这里产生的
为啥不加一个指向函数的指针呢,更加方便 OSStartHighRdy();
这个函数在哪里呀? 逐渐地逐渐地,硬件变成了软件。 good 不懂,标记一下。 我来跟着做一下 顶一个 顶 mark q r 能不把光盘的内容也上了 mark!!! MARK哈哈 MARK哈哈 ddddddddddddd mark MARK哈哈 mark 牛人 mark 牛人 呵呵,学习了 mark 不太理解。
不过对于ds18b20那种精确到十几个us延时的器件,用OS都不太合适吧? 下面几个连接是:《uC/OS-II内核分析、移植与驱动程序开发》的pdf版可惜不像amo这里可以自在下载,谁有注册的帮忙下一下再传上来看看,还有 光盘 lz能否传上来看看?
http://www.elecm.com/a/read-htm-tid-106304.html
http://d.download.csdn.net/down/1882091/gabrial_1
http://download.chinaprj.cn/download/iDTEDbrq 虽然有评论“STC上跑ucosii ram略小些”,但是新版本的s t c 90c58AD系列,有4Kram 足够跑小一点的ucos2了吧?? 点击此处下载 ourdev_547012.pdf(文件大小:19.22M) (原文件名:uCOS-II内核分析、移植与驱动程序开发.pdf)
s可以了,总算找到个下载的, mark mark mark M K mark mark~~~ 学习。 没有serial.c mark Mark! Mark!&thanks mark 学习下 mark mark mark 讲得非常详细,收藏了~ mark! 收藏,谢谢楼主 mark 回复【楼主位】lusson
-----------------------------------------------------------------------
你好版主,我想请教你一个问题,最近我做关于ds12c887的程序,发现程序怎样写显示出来的数据都不对,显示秒的时候总是逢10跳6,一下从9跳到16,然后在过十秒继续跳6,电路连接没有问题,程序是按照时序图写的,2进制与BCD码都换过了,还是不好使,我研究了有一周多了,还是没有进展,希望您能帮帮我,在此我先谢谢了!! 非常感谢你的教悔 标记 学习了 mark mark mark mark 这个好~ mark 收下!!! mark!! mark!! mark,下载,学习 MARK xuexi a mark,非常棒 ding MARK MARK Mark:UCOS+STC 学习学习谢谢楼主 mark 3Q,MARK! mark 好东东,记号一下. 有这本书的电子版,看了半天,代码也不知道怎么输了,能把整个光盘发一份给我吗?非常的感谢,邮箱:1138816567@qq.com 必须mark MACK mark一下 uC/OS-II没有神秘过......... mark mark mark 好好 学习
! gooooooood 先标记再慢慢啃辛苦楼主
页:
[1]
2