ucos期末复习整理2013版

更新时间:2024-06-13 04:59: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、理解上课示例和实验所有与同步、死锁、优先级反转有关代码。 三、任务之间的数据通信

1、在采用消息邮箱进行任务之间数据通信时,发送消息的任务一般是将消息指针(即消息的存放地址)发送到消息邮箱中。

2、通过消息邮箱进行任务之间数据通信时,一般发送消息任务(生产者)的执行频率较慢,接收消息任务(消费者)的执行频率较快,否则可能引起消息丢失。

3、如果平均生产速度小于平均消费速度,但可能某个时间段生产速度大于消费速度,这时怎么办?引入消息队列。消息队列起到仓库的作用,用于缓存临时生产过剩的消息。 4、如何在任务之间传递结构体消息(上课示例、实验四、实验六:发送汉字区位码)

OSMboxPost(mbox,&qw);

qw = (_qwh *)OSMboxPend(mbox,0,&err);

5、理解上课示例和实验中所有与任务之间通信有关的程序代码。 三、任务管理和任务调度的实现

1、什么是任务调度?分析任务调度的时机(在什么情况下会发生任务调度)

所谓任务调度,就是通过一个算法在多个任务中确定该运行的任务(一般是所有就绪任务中优先级最高的任务),然后进行任务切换。

任务调度时机:新任务被创建、有任务被删除、处于等待状态的任务被唤醒;

由于异步事件发生,在中断服务程序中激活一个或几个任务、正在运行的任务需要等待某个事件的发生而进入等待状态、正在运行的任务调用延时函数或挂起函数而自愿进入等待状态。

2、μC/OS-II初始化时,会创建若干个空闲任务控制块,这些空闲任务控制块通过一个指针链接成单链表。认真理解该单链表的结构。

3、 在μC/OS-II中,已创建任务的任务控制块通过两个指针链接成一个双向链表。认真理解该双向链表的结构。

4、如果要将优先级为29的任务置为就绪状态,请问如何设置任务就绪表OSRdyTbl[]和OSRdyGrp?请写出相关的代码

OSRdyGrp |= OSMapTbl[29>>3];

OSRdyTbl[29>>3] |= OSMapTbl[29&0x07];

5、μC/OS-II将已创建任务的任务控制块指针保存在数组OSTCBPrioTbl[]中,则判断优先级为prio的任务是否已经创建的条件是什么?

OSTCBPrioTbl[prio] != (OS_TCB *)0

6、查找表OSMapTbl[]的值如下所示,请给出将优先级别为prio的任务设置为就绪状态或非

就绪状态的示意代码。

设置为就绪状态:

OSRdyGrp |= OSMapTbl[prio>>3];

OSRdyTbl[prio>>3] |= OSMapTbl[prio&0x07];

设置为非就绪状态:

if((OSRdyTbl[prio>>3] &= ~OSMapTbl[prio&0x07]) == 0) {

OSRdyGrp &= ~OSMapTbl[prio>>3]; }

7、查找表OSUnMapTbl[]下标为n的元素的值代表二进制数n第一个为1的位的位置(从低位看起),请给出利用OSUnMapTbl[]从任务就绪表中计算优先级别最高的就绪任务的示意代码。

y = OSUnMapTbl[OSRdyGrp];

OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);

四、信号量和消息邮箱的实现

1、认真理解事件控制块的结构。在μC/OS-II中,信号量、互斥型信号量、消息邮箱等采用相同的数据结构——事件控制块来描述。

typedef struct {

INT8U OSEventType; //事件的类型 INT16U OSEventCnt; void *OSEventPtr; INT8U OSEventGrp;

//信号量计数器 //消息或消息队列的指针 //等待事件的任务组

INT8U OSEventTbl[OS_EVENT_TBL_SIZE]; //任务等待表

} OS_EVENT;

2、在一个中断服务程序ISR中可以调用发送信号量函数OSSemPost(),但不能调用请求信号量函数OSSemPend()。

能执行OSMboxPost()、但不能执行OSMboxPend()

3、通过信号量的事件控制块可以信号量是否有效(判断条件是pevent->OSEventCnt > 0),判定是否有任务在等待该信号量(判定条件是pevent->OSEventGrp != 0),还可以知道有哪些任务在等待该信号量。

4、通过消息邮箱的事件控制块可以知道消息邮箱中是否有消息(pevent->OSEventPtr!= (void *)0),是否有任务在等待该消息邮箱的消息(pevent->OSEventGrp != 0),还可以知道有哪些任务在等待该消息邮箱的消息。

? 如果调用时消息邮箱中有消息(.OSEventPtr不为NULL),则将该消息指针返回给

调用者,并从消息邮箱中清除该消息。同时*err的返回值为OS_NO_ERR;如果调用时消息邮箱中没有消息(.OSEventPtr是NULL),则使任务进入等待状态,并引发一次任务调度。

? 如果在指定的时钟节拍内没有收到消息,则函数返回空指针,同时*err的值为

OS_TIMEOUT。

? 如果有多个任务等待同一消息,则将优先级最该的任务将获得该消息。

5、理解OSSemPend()、OSSemPost()、OSMboxPend()、OSMboxPost()、OSMutexPend()、

OSMutexPost()等函数的实现思想和关键代码。

6、如果一个任务调用OSSemPost(Sem)系统服务函数,则信号量Sem的计数器OSEventCnt应如何变化?注意区分是否有其他任务在等待该信号量这两种情况(结合P/V操作理解)

OSSemPost ( )函数在对信号量的计数器操作之前,首先要检查是否还有等待该信号量的任务。如果没有,就把信号量计数器OSEventCnt加1;如果有,则调用调度器OS_Sched( )去运行等待任务中优先级别最高的任务。

7、如果一个任务调用OSSemPend(Sem,0, &err),则信号量Sem的计数器值OSEventCnt应如何变化?

如果信号量有效,则对信号量计数器OSEventCnt减1,否则任务进入等待状态,信号量计数器OSEventCnt不变。

8、当信号量用于实现两个任务单向同步时,它代表某个事件是否发生,这时创建该信号量

时应当将信号量计数器OSEventCnt初始值应设置为什么值?

Sem = OSSemCreate(0);

9、如果一个信号量的事件控制块指针为pevent,则判断信号量是否有效(即信号量计数器值是否大于0)的条件是什么?

pevent->OSEventCnt > 0

10、如果一个信号量的事件控制块指针为pevent,则判断是否有任务在等待这个信号量的判定条件是什么?

pevent->OSEventGrp != 0

11、如果一个消息邮箱的事件控制块指针为pevent,则判断消息邮箱中是否有消息的条件是什么?

pevent->OSEventPtr!= (void *)0

12、查找表OSUnMapTbl[]下标为n的元素的值代表二进制数n第一个为1的位的位置(从低位看起),请给出利用OSUnMapTbl[]从pevent指针指向的事件控制块的任务等待表中计算优先级别最高的等待任务的示意代码。

y = OSUnMapTbl[OSRdyGrp];

OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);

四、uC/OS 的初始化和启动

1、理解初始化函数OSInit( )的主要作用及其实现方法

OSInit( )将对μC/OS-II的所有全局变量和数据结构进行初始化,同时创建空闲任务OSTaskIdle,并赋之以最低优先级和永远的就绪状态。

2、理解启动函数OSStart( )的主要作用及其相关代码 把控制权交给操作系统,开始任务调度。 五、中断处理和时间管理

1、理解uc/OS中与时间处理有关的基础:时钟中断、时钟节拍、时钟中断处理程序OSTickISR( )以及时钟节拍处理程序OSTimeTick( )的关键代码。

2、理解OSTimeDly()和OSTimeDlyResume实现的主要思想和关键代码。 六、ucGUI

1、文本显示

2、图形绘制(含汉字和图像显示) 3、控件和控件处理。

4、相关示例代码的理解(包括涉及到任务以及任务之间的通信) 七、上课示例和实验示例

1、任务的挂起和恢复(上课示例、实验一)

实验一:定义两个任务:一个是LED显示控制任务,一个是按键检测任务,要求通过任务的挂起和回复实现每按键一次,8个LED以二进制的形式显示按键次数。写出两个任务的任务函数代码

void TaskLed(void *pdata) { pdata = pdata; IO1DIR = LEDCON; IO1SET = LEDCON; while(1) {

IO1SET = LEDCON; IO1CLR = (led<<16);

OSTaskResume(4);//恢复按键的任务 } }

void TaskKey(void *pdata) { pdata = pdata; while(1) {

if((IO0PIN&KEYCON)==0) }

{

while((IO0PIN&KEYCON) == 0); led++;

OSTaskSuspend(OS_PRIO_SELF); }}

2、任务的延时和取消延时

void MyTask(void *pdata) {

while(1) {

printf(\

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/xr76.html

Top