菜鸟学uC/OS_II(7)<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
By <?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />Norman
2008-7-14
实验
书看了一遍了,借助例程的文件,在PC上的移植也基本上弄懂了,不过感觉编写上比较生疏,决定花一些时间来熟悉一下uCOS_II的编程,并且做一做任务管理、通信等等方面的功能试验,以期对uCOS_II更加熟悉,对其内核调度等功能有更深的感性认识。
实验一:
实验内容:创建一个任务,改任务以1秒为间隔,在DOS窗口下显示给定字符
源程序及分析:
这个程序实现比较简单,不用详细分析了,只是利用程序来熟悉一下如何开始一个任务。
#include "includes.h"
#define TASK_STK_SIZE 512
OS_STK M_TaskStk[TASK_STK_SIZE]; /*首先定义并初始化数据结构相关*/
INT16S key; /*按键缓冲区*/
INT16U x = 30,y = 6; /*显示位置*/
void M_Task(void* data); /*声明任务入口函数*/
void main()
{
char* s_M = "M"; /*赋值全局变量*/
OSInit(); /*@1@初始化内核*/
PC_DOSSaveReturn();
PC_VectSet(uCOS,OSCtxSw); /*@2@*/
OSTaskCreate(M_Task,s_M,&M_TaskStk[TASK_STK_SIZE - 1],0); /*@3@这里我当时没有给出“-1”,出现了乱码,要十分注意堆栈问题*/
OSStart(); /*@5@开始多任务之前必须创建至少一个任务*/
}
void M_Task(void* pdata)
{
#if OSCRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
pdata = pdata; /*这句话虽然可以不要,因为这里用到了pdata,但是作为习惯添上比较好吧,反正影响不大,但是多耗一个指令时间*/
/*初始化屏幕,一般将其写在另一个函数中比较好,这就是编程风格,尽量让主函数简洁,一目了然;只是这里程序比较简单,所以我没有另外写*/
PC_DispStr( 0, 0, " uC/OS-II, The Real-Time Kernel ", DISP_FGND_WHITE + DISP_BGND_BLUE + DISP_BLINK);
PC_DispStr( 0, 1, " Norman ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 2, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 3, " EXPERIMENT #1 ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 4, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 5, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 6, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 7, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 8, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 9, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 10, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 11, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 12, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 13, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 14, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 15, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 16, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 17, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 18, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 19, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 20, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 21, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 22, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 23, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 24, " <-PRESS 'ESC' TO QUIT-> ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY + DISP_BLINK);
OS_ENTER_CRITICAL(); /*@4@设置时钟中断和频率*/
PC_VectSet(0x08,OSTickISR);
PC_SetTickRate(OS_TICKS_PER_SEC);
OS_EXIT_CRITICAL();
OSStatInit(); /*一定要在这里初始化统计函数——第一个任务中*/
for(;;)
{
if(x > 50)
{
x = 30;
y += 2;
}
PC_DispChar(x,y,*(char*)pdata,DISP_BGND_BLACK + DISP_FGND_WHITE);
x += 1;
if(PC_GetKey(&key) == TRUE)
{
if(key == 0x1B)
{
PC_DOSReturn();
}
}
OSTimeDlyHMSM(0,0,0,200);
}
}
实验二:
实验内容:使用一个布尔变量来模拟信号量。
源程序及分析:
此程序使用了一个布尔量的值来作为任务间的通信标志:只有当flag = 1的时候,任务才能够对共享的资源进行访问,任务得以继续运行。
本程序设计了两个任务:任务1优先级为5,运行时间需要400ticks;任务2优先级为6,需要200ticks。他们共同访问一个资源signal。
我发现,优先级如果反过来,更能够体现这个保护机制。
#include "includes.h"
#define TASK_STK_SIZE 1024
OS_STK TaskStartStk[TASK_STK_SIZE];
OS_STK Task_1_Stk[TASK_STK_SIZE];
OS_STK Task_2_Stk[TASK_STK_SIZE];
char* signal = "Original Data";
INT16S key;
BOOLEAN flag;
INT16U y1 = 6,y2 = 6;
void taskStart(void* pdata);
void task_1(void* pdata);
void task_2(void* pdata);
static void TaskStartDispInit(void);
void main(void)
{
flag = 1;
OSInit();
PC_DOSSaveReturn();
PC_VectSet(uCOS,OSCtxSw);
OSTaskCreate(taskStart,signal,&TaskStartStk[TASK_STK_SIZE-1],0); /*一般也就是这个结构了比较简洁明了*/
OSStart();
}
void taskStart(void* pdata) /*在必须创建的任务中创建多任务并作一定初始化;当然并不一定都需要这样*/
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
pdata = pdata;
TaskStartDispInit(); /*初始化显示*/
OS_ENTER_CRITICAL();
PC_VectSet(0x08,OSTickISR);
PC_SetTickRate(OS_TICKS_PER_SEC);
OS_EXIT_CRITICAL();
OSStatInit(); /*统计任务初始化*/
OSTaskCreate(task_1,pdata,&Task_1_Stk[TASK_STK_SIZE-1],5);
OSTaskCreate(task_2,pdata,&Task_2_Stk[TASK_STK_SIZE-1],6);
for(;;)
{
OSTimeDly(100); /*第一个任务完成了使命,在本程序中,它再也不需要了,不过,这里没有删除它而是让它一直延迟*/
}
}
void task_1(void* pdata)
{
pdata = pdata;
for(;;)
{
if(flag) /*任务一要访问signal了,首先它要去判断是不是能够访问*/
{
flag = 0;
signal = "task_1 gets the resource!";
if(y2 >20)
{
y1 = 6;
y2 = 6;
TaskStartDispInit();
}
PC_DispStr(0,++y1,signal,DISP_BGND_LIGHT_GRAY+DISP_FGND_RED);
flag = 1; /*访问完成之后,将资源置为可用,别的任务就可以去访问了*/
}
if(PC_GetKey(&key) == TRUE)
{
if(key == 0x1B)
{
PC_DOSReturn();
}
}
OSTimeDly(400);
}
}
void task_2(void* pdata) /*同任务一*/
{
pdata = pdata;
for(;;)
{
if(flag)
{
flag = 0;
signal = "task_2 gets the resource!";
if(y2 >20)
{
y1 = 6;
y2 = 6;
TaskStartDispInit();
}
PC_DispStr(40,++y2,signal,DISP_BGND_LIGHT_GRAY+DISP_FGND_BLUE);
flag = 1;
}
if(PC_GetKey(&key) == TRUE)
{
if(key == 0x1B)
{
PC_DOSReturn();
}
}
OSTimeDly(200);
}
}
static void TaskStartDispInit(void) /*显示*/
{
PC_DispStr( 0, 0, " uC/OS-II, The Real-Time Kernel ", DISP_FGND_WHITE + DISP_BGND_BLUE + DISP_BLINK);
PC_DispStr( 0, 1, " Norman ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 2, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 3, " EXPERIMENT #2 ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 4, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 5, " Original Data ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 6, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 7, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 8, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 9, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 10, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 11, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 12, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 13, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 14, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 15, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 16, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 17, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 18, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 19, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 20, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 21, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 22, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 23, " ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY);
PC_DispStr( 0, 24, " <-PRESS 'ESC' TO QUIT-> ", DISP_FGND_BLACK + DISP_BGND_LIGHT_GRAY + DISP_BLINK);
}
实验结果:
两个任务按照自己的运行时间运行,似乎没有什么干扰,保护了共享资源。
可以修改优先级去试验前面的猜想<略>
实验三:
试验内容:二值信号量。使用二值信号量来实现实验一的功能,并作一些试探的试验,确定信号量的功能。
源程序及分析
前面实验的程序我发现了几个问题:
首先是,taskStart任务一直在做延时,似乎很浪费,后来才发现,它可以用来返回DOS环境,这样一来,其他两个任务也就不用劳神去干这个事情了。而且,如果两个任务被阻塞而无法运行,岂不是回不去了?
其次是,两个任务的时间正好两倍,是否有些情况不能验证?<待分析>
所以,我将程序修改如下:
#include "includes.h"
#define TASK_STK_SIZE 1024 /*按照一般的流程将所需要的数据结构定义、初始化等*/
OS_STK TaskStartStk[TASK_STK_SIZE];
OS_STK Task_1_Stk[TASK_STK_SIZE];
OS_STK Task_2_Stk[TASK_STK_SIZE];
char* signal = "Original Data";
INT16S key;
INT16U y1 = 6,y2 = 6;
OS_EVENT *flag;
void taskStart(void* pdata);
void task_1(void* pdata);
void task_2(void* pdata);
static void TaskStartDispInit(void);
void main(void) /*main函数的一般结构*/
{
OSInit();
PC_DOSSaveReturn();
PC_VectSet(uCOS,OSCtxSw);
flag = OSSemCreate(1); /*创建工作一般都放在这里*/
OSTaskCreate(taskStart,signal,&TaskStartStk[TASK_STK_SIZE-1],0);
OSStart(); /*开始多任务*/
}
void taskStart(void* pdata) /*第一个任务必须创建,做创建其他任务、初始化等工作*/
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr;
#endif
pdata = pdata;
TaskStartDispInit();
OS_ENTER_CRITICAL();
PC_VectSet(0x08,OSTickISR);
PC_SetTickRate(OS_TICKS_PER_SEC);
OS_EXIT_CRITICAL();
OSStatInit();
OSTaskCreate(task_1,pdata,&Task_1_Stk[TASK_STK_SIZE-1],5);
OSTaskCreate(task_2,pdata,&Task_2_Stk[TASK_STK_SIZE-1],6);
for(;;) /*作返回判断工作*/
{
if(PC_GetKey(&key) == TRUE)
{
if(key == 0x1B)
{
PC_DOSReturn();
}
}
OSTimeDly(10);
}
}
void task_1(void* pdata)
{
INT8U* err;
pdata = pdata;
for(;;)
{
OSSemPend(flag,0,err); /*等待信号量*/
signal = "task_1 gets the resource!";
if(y2 >20)
{
y1 = 6;
y2 = 6;
TaskStartDispInit();
}
PC_DispStr(0,++y1,signal,DISP_BGND_LIGHT_GRAY+DISP_FGND_RED);
OSSemPost(flag); /*释放信号量*/
OSTimeDly(400);
}
}
void task_2(void* pdata)
{
INT8U* err;
pdata = pdata;
for(;;)
{
OSSemPend(flag,0,err);
signal = "task_2 gets the resource!";
if(y2 >20)
{
y1 = 6;
y2 = 6;
TaskStartDispInit();
}
PC_DispStr(40,++y2,signal,DISP_BGND_LIGHT_GRAY+DISP_FGND_BLUE);
OSSemPost(flag);
OSTimeDly(200);
}
}
static void TaskStartDispInit(void) /*略*/
实验结果:
同实验二的结果是一致的。
为了更全面观测信号量,我做了以下改动的试验:
1) 屏蔽任务1中OSSemPost()
2) 屏蔽任务2中OSSemPost()
3) 将两个任务中OSSemPost()都屏蔽
4) 修改OSSemPend()有限等待时间,然后重复上面三个步骤
5) 修改优先级并重复上述四个步骤
6) 同步试验:
任务2运行时间比较短,为了让任务2运行与任务1同步,我们可以这样:
修改任务2等待时间为任务1运行时间;并且让任务1不调用OSSemPost()释放信号量。
这样的话,任务一虽然运行完成了,但是由于它没有释放信号量,信号量不可用,那么任务1和任务2下次运行都需要等待,而任务2运行时间比较短,它想去得到信号量,但是没有了,只好挂起——此时任务1是一直在等待任务2释放信号量——等到挂起时限到,任务2自动运行,处理自己的事情并释放信号量,此时,由于任务1得知信号量可用,优先级高的它就又立刻运行了。
实验结果显示这些分析是比较合理的。但是,我个人认为可能有些不太能够证明的地方:任务处理的是一个打印的事件,好像很快就处理完了,延时是让自己挂起,让其他任务运行,而不是自己占用了CPU。这一点我一时也想不怎么明白,放在这里看以后能够解决心中之惑否。
7)其他:实验中改了很多参数来看结果,不过很多都忘了记下。
(待续)
文章评论(0条评论)
登录后参与讨论