ucos期末复习整理2013版

更新时间:2024-04-09 09:58:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

复习题

一、概述

1、μC/OS-II嵌入式操作系统的主要组成部分。

2、μC/OS-II嵌入式操作系统的主要特点。

1).资源占用最小化 微内核结构

模块化(系统可裁减 ) 2).实时性 3).事件驱动

优先级任务调度和抢占式调度 切换时间和中断延迟时间确定 4).非通用性,不易垄断

3、μC/OS-II的裁剪:修改OS_CFG.h文件。

OS_CFG.H是配置文件, μC/OS-II是依靠编译时的条件编译来实现可裁剪性的。即把用户可以裁剪的代码段放在#if 和 #endif预编译指令之间,在编译时根据#if 后面的常数来决定该段代码是否编译到目标代码中。而#if 后面的常数一般就是在OS_CFG.H中定义的。

二、基本任务管理

1、任务的基本构成:任务控制块、任务堆栈、任务函数

2、任务控制块(Task Control Block,TCB)就相当于是一个任务的身份证,任何已经创建的任务都有唯一的任务控制块。了解任务控制块TCB的主要字段的含义和作用。

任务控制块用来记录任务的堆栈指针、任务状态、优先级等一些与任务管理有关的属性 3、任务堆栈主要用于在任务切换是保存现场和恢复现场,所以每个任务都必须有它的专用任务堆栈。即使是基于同一任务函数创建的多个任务也应当有它们各自专用的任务堆栈。

注意:掌握任务创建函数的使用方法。

INT8U OSTaskCreate (

void (*task)(void *pd),//指向任务的指针 void *pdata, //传递给任务的参数

OS_STK *ptos, //指向任务堆栈栈顶的指针 INT8U prio //任务的优先级 )

例如:根据函数函数MyTask和其任务堆栈MyTaskStk定义一个优先级为5的任务:

OSTaskCreate(MyTask, 0, &MyTaskStk[TaskStkSize-1], 5);

3、注意任务函数的框架。如何向任务函数传递参数(实验二)。

void TaskSendByUart(void *pdata) { Baud = *(INT16U*) pdata; while(1) {} }

4、任务状态及其状态转换:哪些转换是可能的,哪些转换是不可能的?

5、在μC/OS-II中,不同的任务具有不同优先级。注意理解优先级含义。

6、基于μC/OS-II应用程序在调用其他系统函数之前,必需先调用OSInit()对包括全局变量和数据结构的运行环境进行初始化。

7、在基于μC/OS-II的应用程序设计中,创建用户任务后,必需调用OSStart() 启动μC/OS-II操作系统,把控制权交给操作系统内核,开始任务调度。

8、一个正在运行任务如要挂起任务自身,则调用OSTaskSuspend(OS_PRIO_SELF) 9、在μC/OS-II中,恢复被挂起任务函数的函数原型是OSTaskResume(INT8U prio) 请问能否通过任务的挂起和恢复实现两个任务的交替执行?(参照相关示例)

void MyTask(void *pdata) {

while(1) { printf(\ Beep(1950, 20); n++; //通过空循环进行延时 if(n>=10) {

printf(\

n = 0; OSTaskSuspend(OS_PRIO_SELF); } } }

OSTaskResume(5) 恢复优先级为5的任务MyTask,MyTask进入就绪状态,由于其优先级高过 当前任务YourTask,所以通过任务调度进入运行状态 void YourTask(void *pdata) { while(1) {

printf(\ Beep(4000, 20); m++;

//通过空循环进行延时 if(m>=10) {

printf(\ m = 0;

OSTaskResume(5); } }

10、正在运行的任务如果需要删除自身,则需要调用OSTaskDel(OS_PRIO_SELF)。 OS_PRIO_SELF为常数,用这个参数就是对本任务进行操作。

11、在单处理器的多任务系统中,任务什么时候占用处理器和能占用多长时间,取决于:任务自身状态、优先级和任务调度策略 二、任务之间的同步

1、在任务同步时,生产者(控制任务)和消费者(被控任务)的优先级一般如何设置?谁负责发送信号量,谁申请信号量?信号量计数器的初始值如何设置?通过示例总结理解。

在通过信号量实现两个任务单向同步时,为了达到较好的同步效果,一般将发送信号量任务设置较低优先级,将接收信号量任务设置较高优先级。

2、如果希望实现多个任务同步一个任务,一般采用信号量集(事件标志组)。通过示例了解。 3、如果希望一个任务同步多个任务,则一般采用消息广播(或者多个信号量)。通过示例理解。

4、当利用信号量用于实现两个任务对一个共享资源共享访问时,则创建该信号量时的信号量计数器OSEventCnt的初始值应设置为1。

Sem = OSSemCreate(1);

写出各个访问共享资源任务的任务函数框架。

void MyTask(void *pdata) { while(1) {

OSSemPend(sem,0,&err); OSSemPost(sem); } }

void YourTask(void *pdata) { while(1) {

OSSemPend(sem,0,&err); OSSemPost(sem); } }

5、分析产生死锁的原因以及解决死锁的策略。

原因:死锁是指两个任务以上无限期地互相等待其他任务控制着的资源。

解决策略:每个必须先得到全部需要的资源再做下一步的工作,而且用同样的顺序去申请多个资源;资源利用完后使用相反的顺序释放资源;在申请信号量时定义等待超时,如果在给定的时限内没有申请成功,则释放原来占用的信号量(资源)。:

每个必须先得到全部需要的资源再做下一步的工作,而且用同样的顺序去申请多个资源;资源利用完后使用相反的顺序释放资源;

在申请信号量时定义等待超时,如果在给定的时限内没有申请成功,则释放原来占用的信号量(资源)。

6、任何任务创建后,在它的生命周期中,其优先级一定不会发生改变吗?

占用互斥信号量的任务在执行过程中优先级可能发生改变。(请问在什么时候可能发生改变),通过实验示例加以理解。 当一个优先级较低的任务已经申请到信号量,而另一个优先级较高的任务也申请该信号量时,优先级较低的任务的优先级将发生改变。

7、请说明信号量的应用情形,分析在不同的应用情况下(行为同步、资源互斥访问),创建信号量时设置的信号量计数器初始值有什么不同。

理解实验中所有与任务之间同步(行为同步、资源访问同步)有关的代码。 行为同步:信号量计数器初始值为0

资源互斥访问:信号量计数器初始值为可访问的资源数量。

8、什么是优先级反转,并说明解决优先级反转的解决方法。(上课示例、实验五)

在可剥夺型内核中,当任务以独占方式使用共享资源时,会出现低优先级任务先于高优先级任务而被运行的现象,这种现象叫做任务优先级反转。

解决问题的办法之一,是使获得信号量任务的优先级在使用共享资源期间暂时提升它的优先级,以使该任务不被其他的任务所打断,从而能尽快使用完共享资源并释放资源使用权(信号量),然后在释放了资源使用权(信号量)之后再恢复该任务原来的优先级别。

9、设有三个任务TaskA、TaskB、TaskC,它们共享一个缓冲区S。TaskA负责从输入设备读信息,每读一纪录后,把它存放在缓冲区S;TaskB负责对缓冲区中的纪录进行加工;TaskC把加工后的纪录打印输出。读入的纪录加工输出后,缓冲区中只可存放下一个纪录。请给出解决上述问题的应用程序的示意性代码(除与任务之间同步、通信有关的代码外可以用伪码)。

sem1 = OSSemCreate(1); sem2 = OSSemCreate(0); sem3 = OSSemCreate(0); void TaskA(void *pdata) {

while(1) {

OSSemPend(sem1,0,&err); //从输入设备读信息 OSSemPost(sem2); } }

void TaskB(void *pdata) {

while(1) {

OSSemPend(sem2,0,&err); //对缓冲区的中的记录进行加工 OSSemPost(sem3); } }

void TaskC(void *pdata) {

while(1) {

OSSemPend(sem3,0,&err); //把加工后的记录打印输出 OSSemPost(sem1); } }

10、理解上课示例和实验所有与同步、死锁、优先级反转有关代码。 三、任务之间的数据通信

Beep(1950, 20); n++;

for(i=0;i<2000;i++) { //通过空循环进行延时 for(j=0;j<50000;j++); }

OSTimeDly(OS_TICKS_PER_SEC*5); } }

void YourTask(void *pdata) {

while(1) {

printf(\ Beep(4000, 20); m++;

for(i=0;i<3000;i++) { //通过空循环进行延时 for(j=0;j<50000;j++); }

if(m>=3) {

printf(\取消任务MyTask的延时!\\n\ m = 0;

OSTimeDlyResume(5); } } }

3、任务之间的单向同步(通过信号量):按键控制音频播放(PC)、按键控制LED(Proteus) 设计两个任务,第一个任务是按键检测任务(TaskKey),第二个任务是串口发送任务(TaskSendByUART),这两个任务要求通过信号量进行同步,即按键任务检测到按键后,串口发送任务将按键次数信息发送到串口终端上。注意:要求创建串口发送任务时能够通过任务函数的参数pdata实现波特率的设置。

检测到按键后,串口发送按键次数信息,8个Led再以二进制数的形式显示按键次数。如何改造程序达到以上要求的实验效果,请画出通过信号量实现三个任务同步的示意图。

void TaskLed(void *pdata) { while(1) {

OSTaskSuspend(OS_PRIO_SELF); n = (n+1) % 255; IO1SET = LEDCON; IO1CLR = n<<16; } }

void TaskKey(void *pdata) {

while(1) { if((IO0PIN & KEYCON) == 0)//检测到按键按下 { OSTimeDly(OS_TICKS_PER_SEC/50); //消抖 if((IO0PIN & KEYCON) == 0)//确认按键按下 { while((IO0PIN & KEYCON) == 0);//等待按键释放 OSTaskResume(5); } } } }

4、任务之间的双向同步(通过两个信号量):上课示例

void MyTask(void *pdata)

{

while(1) {

OSSemPend(sem1,0,&err); if(err == OS_NO_ERR) {

printf(\任务MyTask:我制造快乐!\\n\ Beep(1950, 20);

for(i=0;i<3000;i++) { for(j=0;j<50000;j++); } }

OSTimeDly(OS_TICKS_PER_SEC/10); OSSemPost(sem2); } }

void YourTask(void *pdata) {

while(1) {

OSSemPend(sem2,0,&err); if(err == OS_NO_ERR) {

printf(\任务YourTask: 我消费快乐!\\n\

for(i=0;i<=14;i++) Beep(music_book[i], 20); }

OSTimeDly(OS_TICKS_PER_SEC*2); OSSemPost(sem1); } }

5、多个任务之间的同步:上课示例、实验七 实验七:

编写三个任务,一个任务负责图形按钮点击检测,一个任务负责音频播放(如生日快乐、两只老虎等),一个任务负责通过进度条显示音频播放进度,要求通过信号量或消息邮箱实现任务之间的同步和通信。

void MyTask(void * pdata) { while(1) {

while (GUI_WaitKey() != GUI_ID_OK); OSSemPost(beepsem); } }

void YourTask(void * pdata) { while(1) { OSSemPend(beepsem,0,&err); for (i=0;i<32;i++) { Beep(FREQUENCY[i],DELAY[i]); OSSemPost(progsem); } } }

void HerTask(void * pdata) { while(1) { for (i=0;i<32;i++) { OSSemPend(progsem,0,&err); //改变进程条的进度 PROGBAR_SetValue(hProgBar,i); WM_Exec(); } } }

6、多个任务同步一个任务(事件标识组):上课示例

void MyTask(void *pdata) {

while(1) {

printf(\任务MyTask:等我来按键!\\n\ ch = getch();

if(ch==13) OSFlagPost(FLAGW,0x01,OS_FLAG_SET,&err); } }

void YourTask(void *pdata) {

while(1) {

printf(\任务YourTask:等你来说话!\\n\

OSFlagPost(FLAGW,0x02,OS_FLAG_SET,&err); OSTimeDly(OS_TICKS_PER_SEC*3); } }

void HisTask(void *pdata) {

while(1) {

OSFlagPend(FLAGW,0x03,OS_FLAG_WAIT_SET_ALL+OS_FLAG_CONSUME,0,&err); if(err == OS_NO_ERR) {

printf(\任务HisTask: 万事具备,该他来唱首歌啦!\\n\ for(i=0;i<=14;i++) Beep(music_book[i], 20); } } }

7、一个任务同步多个任务(消息广播、多个信号量):上课示例、实验二

实验二:检测到按键后,串口发送按键次数信息,8个Led再以二进制数的形式显示按键次数。如何改造程序达到以上要求的实验效果,请画出通过信号量实现三个任务同步的示意图。

TaskKey 键盘任务

void TaskKey(void *pdata) { INT16U i;

pdata = pdata; while(1) {

for(i=0;i<1000;i++); //延时,去毛刺

if( (IO0PIN&KEY)!=0 ) continue;//未按键,继续等待延时 OSSemPost(sem1);//发送一个信号量函数; while((IO0PIN & KEY) == 0) { //按键未释放 IO0SET = KEY; } } }

TasSendByUart 串口发送任务(低优先级) void TaskSendByUart(void *pdata) {

INT16U Baud,times=0; INT8U err; char s[30];

pdata = pdata;

Baud = *(INT16U*) pdata; //baud用来设置波特率; UART0_Init(Baud);

UART0SendStr(SendString); while(1) {

OSSemPend(sem1,0,&err);//等待一个信号量函数; times++;

sprintf(s,\ UART0SendStr(s);

OSSemPost(sem2); //发送一个信号量函数; } }

TaskLed Led控制任务

void TaskLed(void *pdata) { INT8U err;

pdata = pdata; /* 避免编译警告 */ IO0DIR |= LEDCON; / 设置LED输出 IO0SET = LEDCON; while(1) {

OSSemPend(sem2,0,&err); //等待一个信号量函数; led++;

IO0SET = LEDCON;

IO0CLR =(led<<8); } }

8、共享资源的互斥访问:信号量、互斥型信号量(上课示例、实验三) 实验三:

1、以下为两个串口发送任务,第一个串口发送任务将字符串”I am Task A!”发送到串口终端,第二个串口发送任务将字符串”I am Task B!”发送到串口终端,请问输出结果有什么问题?为什么会出现该问题?如何通过信号量或互斥型信号量解决上述问题。画出两个任务对串口终端实现互斥访问的示意图,并改造上述程序。

OS_EVENT *mutex;

mutex = OSMutexCreate(2,&err); void TaskA(void *pdata) { INT16U Baud,i=0; INT8U err; pdata = pdata;

Baud = *(INT16U*) pdata; UART0_Init(Baud); while(1) {

OSMutexPend(mutex,0,&err);//申请互斥型信号量(互斥型信号量指针、等待时限、错误信息); i = 0;

while(StrA[i]!='\\0') {

UART0SendByte(StrA[i]); // 发送数据 i++;

OSTimeDly(10); }

OSMutexPost(mutex); } }

void TaskB(void *pdata) { INT16U i=0; INT8U err; pdata = pdata; while(1) {

OSMutexPend(mutex,0,&err); i = 0;

while(StrB[i]!='\\0') {

UART0SendByte(StrB[i]); // 发送数据 i++;

OSTimeDly(20); }

OSMutexPost(mutex); } }

9、任务之间的数据通信(上课示例、实验四、实验七) 实验四:

(1) 建立两个μC/OS-II任务,第一个键盘扫描任务(TaskKeyScan)作为数据发送任务,第二个是串口发送任务(TaskUartSend),按键扫描任务负责对按键进行检测,检测到按键后,将按键的行、列号扫描码作为消息发送到消息邮箱。串口发送任务等待消息邮箱的消息(按键的行列号),获得消息后将相关信息发送到串口终端。

void TaskSendByUart(void *pdata) {

INT16U Baud,times=0; INT8U err; _key *pkey; char s[30]; pdata = pdata;

Baud = *(INT16U*) pdata; UART0_Init(Baud); while(1) {

pkey = (_key *)OSMboxPend(mbox,0,&err);

sprintf(s,\ UART0SendStr(s); } }

void TaskKeyScan(void *pdata) { while(1) {

while((IO1PIN&(0xf0<<16)) != (0xf0<<16)); //等待按键释放

if(flag) OSMboxPost(mbox,&key); } }

(2)如果希望实验效果为:键盘扫描任务检测到按键后,如果需要将按键信息发送给多个任务(如串口发送任务、数码管显示任务),请问如何改造程序达到以上要求的实验效果,请写出三个任务的任务函数框架,说明三个任务的数据通信的实现。

void TaskSendByUart(void *pdata) {

while(1) {

pkey = (_key *)OSMboxPend(mbox,0,&err);

sprintf(s,\ UART0SendStr(s); } }

void TaskLed(void *pdata) { while(1) {

pkey = (_key *)OSMboxPend(mbox,0,&err); i = pkey->row*4 + pkey->col;

HC595_SendDat(DISP_TAB[i]); //输出LED显示数据 DelayNS(100); //延时 } }

void TaskKeyScan(void *pdata) { while(1) {

while((IO1PIN&(0xf0<<16)) != (0xf0<<16)); //等待按键释放

if(flag) OSMboxPostOpt(mbox,&key,OS_POST_OPT_BROADCAST); } }

实验六:

编写两个任务,一个任务获取汉字区位码,并将汉字区位码发送到消息邮箱,另外一个任务从消息邮箱接收汉字区位码,并从汉字字库中获取汉字点阵信息并显示。请写出根据汉字内码获取汉字点阵信息并显示的基本步骤。

1.根据汉子机内码求出区位码:qh=*p++ - 160;wh = *p - 160 2.求出汉子在字库中的位置:loc=(94*(qh - 1) + (wh - 1)*hzsize); 3.读入汉字字库点阵信息:fseek(fp, loc, 0);fread(&hz, sizeof(hz), 1, fp); 4.显示汉字:Load_HZ(hz, 24, 24, x, 100);

void MyTask(void * pdata) {

char s[]=\电子科技大学中山学院\ p = s;

while(*p) { //根据汉字机内码求出区位码 qw.qh=*p++-160; qw.wh=*p++-160; OSMboxPost(mbox,&qw); }

while(1) { } }

void YourTask(void * pdata)

{

fp = fopen(\楷\ while(1) { qw = (_qwh *)OSMboxPend(mbox,0,&err); GUI_DispDecAt(qw->qh,x+3,60,2); GUI_DispDecAt(qw->wh,x+3,80,2); loc=(94*(qw->qh-1)+(qw->wh-1))*HZSize; //求出汉字在字库中的位置 //读入汉字字库点阵信息 fseek(fp,loc,0); fread(&hz,sizeof(hz),1,fp); Load_HZ(hz,24,24,x,100); x += 30; }

fclose(fp);

while(1) { } }

//汉字显示完整代码 : #include #include #include #include \#include \

#define TaskStkLengh 2048 //定义用户任务的堆栈长度 OS_STK TaskSendStk[TaskStkLengh]; //定义发送汉字点阵任务堆栈 OS_STK TaskDispStk[TaskStkLengh]; //定义显示汉字点阵任务堆栈 void TaskSend(void * pdata) ; void TaskDisp(void * pdata) ; OS_EVENT * mbox; typedef struct QWM { INT8U qh; INT8U wh; }QWCode;

//显示汉字点阵函数

void Load_HZ( const INT8U *hz,INT16U Len,INT16U Height,INT16U xPos,INT16U yPos) {

INT16U x,y,i; INT8U temp;

for(y=yPos;y<=(yPos+Height-1);y++) { for(x=xPos;x<=(xPos+Len-1);) { temp = *hz++;

for(i=128; i>=1; i=i/2) { GUI_SetColor(GUI_RED);

if(temp&i) GUI_DrawPixel(x,y); x++; } } } }

int main() {

GUI_Init(); OSInit();

OSTaskCreate(TaskSend, 0, &TaskSendStk[TaskStkLengh-1], 5); OSTaskCreate(TaskDisp, 0, &TaskDispStk[TaskStkLengh-1], 4); mbox = OSMboxCreate((void *)0); OSStart(); return 0; }

void TaskSend(void * pdata) { QWCode qw;

char s[]=\电子科大计算机学院\

//实际应用中可能是通过通信链路获得要显示的字符串 p = s;

while(*p) { //根据汉字机内码求出区位码 qw.qh=*p++-160; qw.wh=*p++-160; OSMboxPost(mbox,&qw); } while(1) {} }

void TaskDisp(void * pdata) { FILE *fp; INT8U err; QWCode *p; INT8U hz[72]; INT32U loc; INT16U x = 10;

fp = fopen(\ //打开汉字库文件

if(!fp) { GUI_DispStringAt(\ exit(0); }

else GUI_DispStringAt(\ while(1) { p =(QWCode*)OSMboxPend(mbox,0,&err); loc=(94*(p->qh-1)+(p->wh-1))*72; //求出汉字在字库中的位置 //读入汉字字库点阵信息 fseek(fp,loc,0); fread(&hz,72,1,fp);

Load_HZ(hz,24,24,x,100); x += 30; } }

//BMP图片显示

//#include \#define TaskStkLengh 2048 //定义用户任务的堆栈长度 OS_STK TaskSendStk[TaskStkLengh]; //定义用户任务堆栈 OS_STK TaskDispStk[TaskStkLengh]; //定义用户任务堆栈 void TaskSend(void * pdata) ; void TaskDisp(void * pdata) ; OS_EVENT *mbox;

INT16U Width,Height,w; //定义BMP文件信息区 typedef struct BMP_file { INT16U bfType; //文件类型

INT32U bfSize; //bmp文件长度 INT16U Reserved1; INT16U Reserved2;

INT32U bfOffset; //文件描述区长度,16色为118,256色为1078 }bitmapfile;

//定义图象信息区

typedef struct BMP_info { INT32U biSize;

INT32U biWidth; INT32U biHeight; INT16U biPlanes;

INT16U biBitCount; //每个像素的颜色位 INT32U biCompression; INT32U biSizeImage;

INT32U biXplosPerMeter; INT32U biYplosPerMeter; INT32U biClrUsed;

INT32U biClrImportant; }bitmapinfo;

int main() {

GUI_Init(); OSInit();

OSTaskCreate(TaskSend, 0, &TaskSendStk[TaskStkLengh-1], 5); OSTaskCreate(TaskDisp, 0, &TaskDispStk[TaskStkLengh-1], 4); mbox = OSMboxCreate((void *)0); OSStart(); return 0; }

void TaskSend(void * pdata) { FILE *fp; bitmapfile file; bitmapinfo info;

INT8U mybuf[3*320]; int y; //打开文件

fp = fopen(\

if(!fp) GUI_DispStringAt(\ else GUI_DispStringAt(\

//读入文件信息内容

fread(&file,sizeof(bitmapfile),1,fp);

//读入图像有关信息内容 fseek(fp,14,0);

fread(&info,sizeof(bitmapinfo),1,fp); Width = info.biWidth; Height = info.biHeight;

GUI_DispDecAt(info.biWidth,100,120,6); GUI_DispDecAt(info.biHeight,100,140,6); GUI_DispDecAt(file.bfOffset,100,160,6);

GUI_Delay(5000);

for(y=Height-1;y>=0;y--) { //文件指针定位 w = 3*Width; if(w%4>0) w = (w/4+1)*4; fseek(fp,54+y*w,0); //读取一个行像素数据放入缓冲区 fread(mybuf,1,3*Width,fp); OSMboxPost(mbox,mybuf); }

fclose(fp);

while(1) { } }

void TaskDisp(void * pdata) { INT8U *p; INT8U err;

INT32U lcdColor; INT32U r,g,b; INT16U x,y=0; while(1) { p = (INT8U *)OSMboxPend(mbox,0,&err); for(x=0;x

#define TaskStkLength 200 //定义用户任务的堆栈长度 OS_STK TaskLedStk[TaskStkLength]; // 定义按键任务的堆栈 void TaskLed(void *pdata); //声明 sem1 = OSSemCreate(0);

OSTaskCreate (TaskLed,(void *)0, &TaskLedStk[TaskStkLength - 1],3);

OSSemPend(sem1,0,&err);//等待一个信号量函数

OSSemPost(sem1);

本文来源:https://www.bwwdw.com/article/3i7r.html

Top