《第8章-基于ucos-ii的程序设计实例(共23页).doc》由会员分享,可在线阅读,更多相关《第8章-基于ucos-ii的程序设计实例(共23页).doc(23页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、精选优质文档-倾情为你奉上第八章 基于COS-II的程序设计实例8.1 实例介绍为了使读者对COS-II操作系统有更深的理解,本章将介绍一个在STM32F103 处理器平台上使用COS-II实时操作系统的程序设计实例。此实例使用英倍特公司提供的STM103V100评估板来实现一个简易温度计。该实例使用STM103V100评估板自带的高灵敏度数字温度传感器来传送温度数据,根据实际采样周期的需要,安排了四种不同的采样方式。采样的条件和周期可以通过键盘输入进行调节,采样得到的结果可以在评估板的液晶屏上同步显示,并通过串口将采样所得的结果送到上位机。关于STM103V100评估板的更多内容超出本书范围
2、,请读者参阅其它相关资料。8.2 实例分析本节主要分析如何通过基于实时操作系统编程的方法实现整个系统的所有功能。下面的内容将从任务划分开始,详细说明任务分析的过程。8.2.1 实例任务划分为了更合理的将整个系统划分为不同任务,首先要明确一个好的实时系统应具备那些特点,即任务划分的基本原则是什么。一般说来,任务划分的基本原则有以下几点: 满足系统“实时性”:一般使用COS-II的嵌入式应用系统,对于响应时间要求很高,如果实时性得不到满足,系统会出现错误甚至导致难以挽回的故障。因此在任务划分时,保证系统实时性是首要原则。 较少资源需求:多个任务协同运转,依靠操作系统的调度策略。任务之间的同步,任务
3、之间的通信,内存管理都需要消耗系统资源。所以在任务划分时,尽量将使用同类资源的应用归入同一任务中,以减少操作系统调度时所消耗的资源。 合理的任务数:同一系统,任务划分的数目越多,每个任务的功能越简单,实现越容易,但任务数目的增多,加大了操作系统的调度负担,资源开销也随之加大;相反,如果任务划分的数目太少,会增加每个任务的复杂性,使任务设计难度加大。最极端的情况,当系统任务数目减少到1时,也就失去了使用多任务操作系统的意义。对一个具体的嵌入式应用系统进行任务划分时,可以有不同的任务划分方案。常用的任务划分方法有:以硬件模块为对象划分任务、以实时性优先原则划分任务和切分耗时任务等。所谓“以硬件模块
4、为对象划分任务”就是根据系统需求,以硬件模块相关驱动为基础,根据硬件驱动在系统中的关键性设定优先级的任务划分方法;“实现性优先原则划分任务”即,将对实时性要求较高的应用划分为单独任务,并赋予较高的优先级来保证整个系统实时性的要求;而“切分耗时任务”的任务划分方法,顾名思义就是将一些占用大量CPU处理时间的繁琐应用从系统中分离出来,作为一个优先级较低的任务在系统空闲时运行。根据上述任务划分的原则和方法,本实例被划分为7个任务:包括4个采用任务,1个负责和用户交互的键盘任务,1个显示任务和1个向上位机传送数据的串口发送任务。其中,4个采样任务分别使用不同的采样条件:延时采样、使用系统时钟节拍采样、
5、定时中断采样和使用高优先级中断的采样。键盘任务除负责接收用户输入并做出反馈外,还需要完成操作系统和系统资源的初始化,包括系统中用到的消息队列、邮箱和互斥信号量的创建等。因为按键任务是第一个启动的用户任务,所以目标板的初始化也由该任务完成。8.2.2 实例任务设计与优先级分配完成系统任务划分后,需要对任务的优先级进行设定。任务优先级分配是否合理,直接影响到系统的实时性和可靠性。对任务优先级的安排一般遵循以下原则: 外设相关任务安排高优先级:因为外设任务直接与中断服务程序相对应,如果外设任务优先级低,当需要中断处理时,系统资源可能被高优先级任务占用,而导致中断丢失; 根据任务实现功能的重要性安排优
6、先级:一般情况下,任务越重要优先级越高; 占用关键资源的任务优先级尽量高:只有保证占用关键资源的任务优先运行,才能使其尽早释放资源,以便其它任务运行; 对于周期性任务,执行周期越短的任务,优先级应越高,以保证其得到及时运行; 当以上条件相近时,耗时越短的任务优先级应越高。这样可以缩短其它就绪任务的延时时间。根据以上优先级安排原则,系统中存在的4个采样任务实现同样功能且均为一次性任务。所以,4个任务的优先级应一致。但在COS-II操作系统中,同一优先级不允许存在多个任务,所以将4个采样任务的优先级依次排列,因为它们不可能同时发生,则对于应用来说,4个任务的优先级是一致的。键盘任务是系统运行的第一
7、个任务,负责与用户交互。根据优先级安排原则,将其优先级定为7个任务中的中等级别,设为15。显示任务负责将采集的数据显示在LED屏上,它由采样任务触发。根据优先级安排原则,将其优先级定为7个任务中的最低优先级,设为17。系统中任务的优先级安排如下程序清单所示,void Task_FastSamp(void *pdata); /使用高优先级中断的采样,优先级5void Task_HookSamp(void *pdata); /使用钩子函数的采样任务,优先级6void Task_TimerSamp(void *pdata); /使用定时中断的采用任务,优先级7void Task_DelaySamp(
8、void *pdata); /使用延时函数的采用任务,优先级8void Task_Send(void *pdata); /串口发送任务,优先级13void Task_Key (void *pdata); /键盘任务,优先级15void Task_Disply(void *pdata); /显示任务,优先级17程序运行过程中,当系统检测到键盘输入后,根据采样周期的设定值而创建一个采样任务和串口发送任务Task_Send()。4个采样任务Task_FastSamp(),Task_HookSamp(),Task_TimerSamp(),Task_DelaySamp()和串口发送任务Task_Send
9、()均为一次性任务,且在任务完成后自动删除。Task_Disply()为消息队列驱动的任务,根据消息队列传送的内容进行不同的显示操作。Task_Key()是周期性执行的任务,不断检测键盘输入,并根据检测结果,做出不同的响应。键盘任务是系统运行的第一个任务,负责系统初始化和其它任务的创建。该任务运行后,接收用户输入并根据接收到的输入值启动不同的采样任务。同时,将接收到的输入值通过消息队列的形式发送到显示任务。显示任务负责显示用户输入的参数值和采样任务采样得到的数据。串口发送任务在键盘任务检测到发送操作按键确定时创建。该任务负责从全局采样数组中读取数据并使用中断方式发送到上位机。全局采样数组是系统
10、初始化时建立的FIFO缓存,通过互斥信号量保护。4个采样任务均在键盘接收到用户输入后创建,不同的采样任务使用不同的采样方式进行采样,并将采样所得数据保存在全局FIFO缓存中。所有采样任务在采样过程中的工作就是检测什么时候采样结束,并且负责将采样所得的数据进行处理。处理完毕后,发送消息通知显示任务,最后删除自己本身。8.3 任务实现详解开始程序设计之前,首先了解一下主函数。在主函数中,进行了操作系统的初始化,创建了一个键盘任务,然后,启动多任务操作系统。主程序代码如下:int main(void)#if (OS_TASK_NAME_SIZE = 16) INT8U err;#endif Bsp_
11、Init(); /系统外设初始化 PWM_IoConfiguration(); /初始化系统时钟 OSInit();/* 初始化OS */ Tmr_TickInit(); / 操作系统Tick初始化 OSTaskCreateExt(Task_Key, /任务指针(void *)0, (OS_STK *)&InitTaskStkOS_INIT_TASK_STACK_SIZE - 1,OS_TASK_KEY_PRIO, /任务优先级OS_TASK_KEY_PRIO,(OS_STK *)&InitTaskStk0,OS_INIT_TASK_STACK_SIZE,(void *)0,OS_TASK_O
12、PT_STK_CHK | OS_TASK_OPT_STK_CLR);#if (OS_TASK_NAME_SIZE = 16)OSTaskNameSet(OS_TASK_IDLE_PRIO, (INT8U *)Idle task, &err);OSTaskNameSet(OS_TASK_INIT_PRIO, (INT8U *)Init task, &err);#endifOSStart();/* 启动多任务环境 */ return(0);从程序中可以看到,当执行OSStart()后,主函数返回,但了解了ucos-II操作系统后,可以知道这个“return 0”的语句是不可能执行的。在主函数中创建
13、了一个键盘任务Task_Key(),其优先级为15,负责初始化目标板和根据用户输入创建其他任务。8.3.1 键盘任务键盘任务的主要工作就是周期性扫描键盘,优先级为15。其程序流程图如图8.1所示。图8.1 键盘任务程序流程图程序中,首先进行目标板初始化,目标板初始化过程请参见STM32F103 处理器内部资源C编程与实例一章讲解。之所以将目标的初始化放在第一个任务里,是为了保证该初始化在OSStart()执行后完成。值得注意的是,与采样任务相关的A/D转换初始化也放在了这里,这是因为4个采样任务都用到了A/D转换,放在键盘任务中一次完成,避免了在每个采样任务中单独初始化而带来的代码冗余。接下来
14、创建显示任务Task_Disp(),屏幕初始化工作放在 该任务中完成。最后创建了任务轮转不可或缺的一些操作系统资源,包括一个互斥信号量Sem,一个邮箱MyMbox和一个消息队列ReMsgQeue,其中消息队列中消息数目定义为10。按键处理是这个键盘任务的关键,这个键盘任务中状态转移情况如表8.1所示。表8.1 键盘状态转移表初始状态操作键动作状态1:状态选定“确定”键flag状态翻转,进入周期修改状态或退出修改状态“数字”键如果为数字键并且flag=1,则设定数字有效并显示;否则不响应“清零”键如果flag=1,则将保存的设定值清零并显示,否则不响应“移动”键如果flag=0,则进入启动选定状
15、态并显示,否则不响应状态2:启动采样“确定”键如果clflagp=1,则清零clflagp。如果采样周期为20ms,则创建钩子函数采样任务如果采样周期为20ms的整倍数,但不为20ms,则创建延时函数采样任务。“移动”键进入显示选定状态状态3:清屏操作“确定”键清除上一次显示的采样数据“移动”键创建发送任务,将采样所得数据送上位机状态4:发送命令状态“确定”键创建发生任务,将采样所得的数据发送到上位机“移动”键进入周期选定状态状态5:采样周期设定状态“确定”键flag状态翻转,进入周期修改状态或退出修改状态“数字”键如果为数字键并且flag=1,则设定数字有效并显示;否则不响应“清零”键如果f
16、lag=1,则将保存的设定值清零并显示,否则不响应“高优先级采样”键如果flag=1,则创建高优先级采样标志clflg=1,否则不响应“移动”键如果flag=1,则进入条件选定状态并显示,否则不响应从表8.1中可以清晰看出系统被划分成的5种状态,且在5种状态下,不同的按键动作产生的不同后果: 状态1:条件选定状态,处理采样条件设定相关内容; 状态2:启动采样状态,即创建采样任务。使用一个UINT16型的数组保存采样周期和采样条件,并将数组指针通过创建任务函数参数(void *)指针传递给采样任务; 状态3:清屏操作,即清除本次采样数据。 状态4:发送命令状态,将本次采样得到的数据通过串口发送到
17、上位机。 状态5:采样周期设定状态,用户可以在该状态下根据自己的采样需要通过不同的按键设置系统采样周期。这个键盘任务的代码清单如下,void TaskKey(void *pdata) INT8U err; INT16U Fdiv; INT16U cl=100; INT16U cfg=320; INT16U cond2; INT8U Key,Key0, clflg=0; INT8U locad=0,locas=0; INT8U Keyarr4,clrs=0,flag=0; pdata=pdata; TargetInit(); /硬件初始化 UOFCR = 05; U0IER= 0x02; U0L
18、CR=0x83; Fdiv= (Fpclk/16)/; /设置波特率 U0DLM = Fdiv/256; U0DLL = Fdiv%256; U0LCR = 0x03; PINSEL1= 0x; IO2DIR = 0xff16; /设置LED为输入 IO2SET = 0xff16)|(Key != Key0) /未按键或与上次不同 Key0=Key; /记录本次按键continue; if(Key11)Key0=0; /数字键09,键码值分别为110 else Key0=Key%10; /4个命令键 swich(locas) case0: switch(Key0) case0: if(flag
19、) cl=cl*10+Key-1; /周期显示信息更改 break; case1: if(flag) clflg=1; cl=50; /采样周期和条件保存 breake; case2: if(!flag)locad = (locas+1) %5; break; case3: flag= !flag; break; case4: if(flag) cl=0 ; /清零周期设置 breake; break;case1: swich(key0) case0: if(flag) cl=cl*10+Key-1; /显示信息更改 break; case2: if(!flag)locad = (locas+
20、1) %5; break; case3: flag= !flag; break; case4: if(flag) cfg=0 ; /清零条件设置 breake; break; case2: if(Key0=2) locad = (locas +1)%5; else if (Key0 =3) cond0=cl; cond1=cfg; if(clflg) clflg =0; /清除高优先级采样标志 /* 创建高优先级采样任务,将采样条件传说给采样任务*/ OSTaskCreate(TaskFiqSamp,(void*)cond, &TaskHookSampStkTASK_STK_SIZE-1,5)
21、; else if (cl=20) /采样周期为系统节拍时钟时间 /*创建节拍钩子函数采样任务*/ OSTaskCreate(TaskhookSamp, (void*)cond, &TaskHookSampStkTASK_STK_SIZE-1,7); else if(cl%20)!=0) /*创建中断采样任务,将采样条件传送给采样任务*/ OSTaskCreate(TaskIntSamp, (void*)cond, &TaskHookSampStkTASK_STK_SIZE-1,9); else OSTaskCreate(TaskDelaySamp, (void*)cond, &TaskHoo
22、kSampStkTASK_STK_SIZE-1,11); break; case3: /清屏条件下 if(Key0=2) locad=(locas +1)%5;else if (Key0=3) clrs=1;Keyarr0 = 0; /清屏命令标志break; case3: if(Key0=2) locad=(locas +1)%5;else if (Key0=3)/创建串口发生任务,优先级13OSTaskCreate(TaskSend, (void*)cond, &TaskHookSampStkTASK_STK_SIZE-1,13);break; if(!clrs)Keyarr0= ox0f
23、; else clrs=0; if(locad !=locas) Keyarr1= 0xff;Keyarr2= locas;locas = locad; else if Keyarr1=flag; Keyarr2 = cl/256; Keyarr3= cl%256;else Keyarr1= (116)|(Key=0) break; OSTimeDly(1);8.3.2 显示任务显示任务的主要作用是初始化屏幕,显示按键的操作结果和系统采样结果,其任务优先级为17。显示任务流程图如图8.2所示。图8.2 显示任务程序流程图显示任务代码清单如下,void Task_Disp(void *pdata
24、) INT8U err, *pt, k=10; INT16U x=0,y ; INT8U(*p)63 = a; / pdata = pdata; GUI_Init(); /初始化LCM GUI_HLine(0,160,320,WHITE); /水平线 for(err=1;err8;err+) GUI_HLine(0,err* 20,320,BLUE); /绘制表格 for(err=1;err16;err+) GUI_RLine(20*err, 0,160,BLUE); GUI_SetColor(BLUE,WIHITE); / For(err=0; err2; err+) GUI_LoadPic
25、(24*err+k, 165, *p,24, 21); p+ / /显示周期默认信息; /显示条件默认信息; while(1) pt= (INT8U *)OSQPend(ReMsgQeue, 0, &err); /等待消息队列 if(pt0=0) OSMutexPend(ReMsgQeue, 0, &err); /等待消息队列 if(pt0=0) OSMutexPend(Sem, 0 , &err); for(x=0;xSamp0;x+) y=160-Sampx+1*160/3000; if(x!=0)&(x%20 = 20)|(y % 20 =0) GUI_Point(x, y, BLUE)
26、; else GUI_Point(x,y, BLACK); OSMutexPost(Sem); else if(pt0 = 0x0f) if(pt1= 0xff) Dispm(pt2,(pt2+1)%5); pt2= pt3; else if (ptpt1& 0xf0)=0 /显示采样周期数据 else /显示采样条件数据 else if(pt0=0xf0) if(pt1=0xff) x=0; /新的采样开始 else( y= 160-pt1; GUI_Point(x,y,RED); x+; ) else OSMutexPend(Sem, 0, &err); /获取互斥信号量 for(x=0;
27、 xSamp0;x+) y= 160-(INT8U)(Sampx+1*160/3000); GUI_Point(x,y,RED); / OSMutexPost(sem); 程序开始阶段,标号处定义了一个指向包含63个元素的一维数组的指针,并且将这个指针赋值为a。该数组包含的内容为汉字库。限于篇幅,关于汉字库的内容请参见相关资料。任务开始运行后进行显示初始化,如标号处所示。显示的具体实现在标号之间完成,该部分内容可结合STM32F103 处理器内部资源C编程与实例一章加深理解。标号出显示了采样周期值和采样条件值。代码处的无限循环等待显示的驱动消息,消息格式定义如表8.2所示。表8.2 消息队列内
28、容解析第一字节第二字节第三/四字节命令解析0x00-清除上次显示内容0xff0xff原位置坐标光标移到0x0x周期数据显示最新周期数据,“x”的值决定显示颜色0x1x条件数据显示最新条件数据,“x”值决定显示颜色0xf00xff-新的采样任务开始采样值-显示采样值0xff-高优先级采样显示8.3.3 使用延时函数的采样任务使用延时函数的采样任务流程如图8.3所示。图8.3 延时函数的采样任务流程图此任务有键盘任务创建,采样任务结束后删除任务本身。保存采样周期和条件就是保存发送给显示任务的任务参数,在键盘任务中已经提到过。延时任务的程序清单如下,void Task_DelaySamp(void
29、*pdata) INT8U err,Samparr4; INT16U c1,cfg,i,*pt; INT32U Temp; pt=(INT16U)pdata; /获取采样周期 cl=pt0; cfg = pt1; Samp0=cfg; Temp = ADDR; Samparr0=0xf0; Samparr1=0xff; OSQPost(ReMsgQeue,(void *)Samparr); OSTimeDly(1); OSMutexPend(Sem,0,&err); /获取互斥信号量 IO2CLR = 0xff16 ; /指示采样开始 for(i=1 ; i cfg+1; i+) /设置通道,
30、并开始第一次采样 /等待采样结束 /再次启动转换 /等待采样转换结束 /保存采样结果 Samparr1=INT8U(160*Sampi/3000); OSQPost(ReMsgQeue,(void*)Samparr); SOTimeDlyHMS(0,0,0,cl); OSMutexPost(Sem); IO2SET = 0xff16; OSTaskDel(OS_PRIO_SELF); 此实例中,采样结束后,采样任务通过消息队列将采样结束消息发送给显示任务。值得一提的是,存放采样信息的全局数组要用互斥信号量来保护,以避免发送和读取任务同时操作该数组而带来的数据错误。8.3.4 使用时钟节拍钩子函
31、数的采样任务对于采样周期是1个时钟节拍的采样任务,为了保证延时的准确性,不用操作系统提供延时函数,而用时钟节拍钩子函数来完成。所谓钩子函数,是由操作系统本身提供的空函数,可以由用户在需要的时候实现,以方便用户编程。本实例中所实现的钩子函数源代码程序清单如下,void TaskHookSamp(void *pdata) INT8U err, Samparr4; INT16U *pt,cl,cfg,i; pt = (INT16U*)pdata; cl = pt0; cfg = pt1; Samparr0= 0xf0; Samparr1= 0xff; OSMutexPend(Sem, 0, &err
32、); /获取互斥信号量 OSQPost(ReMsgQeue,(void*)Samparr); /向显示任务发送开始采样信息 OSTimeDly(1); Samp0 = cfg; /保存采样条件 cfgal = cfg; IO2CLR = 0xff 16 ; /指示采样开始 for(i=0;icfg;i+) Sampi+1= *(INT16U)OSMboxPend(MyMbox,0,&err); /获取并保存转换结果 samparr1=(INT16U)(Sampi+1*160/3000); OSQPost(ReMsgQeue,(void)Samparr); /向显示任务发送消息 OSMutexP
33、ost(Sem); IO2SET = 0xff16; / 指示采样结束 OSTaskDel(OS_PRIO_SELF); /删除自己 void OSTimeTickHook(void) static INT32U Temp; /静态变量 if(cfgal !=0) Temp =ADDR; /进行第一次采样 /等待采样结束 /再次启动采样 /等待采样结束 OSMboxPost(MyMbox, (void *)&Temp); cfgal-; /完成一次采样 从程序中可以看到,时钟节拍钩子函数在每个系统时钟中断时都会执行,但只有在采样任务发生信号后才会进行采样操作。该函数负责接收采样得到的数据,并处
34、理和保存,并通过消息队列发生给显示任务。8.3.5 使用定时中断的采样任务该实例中采样周期设定的最小值是1ms,可以利用定时器T1的ISR完成。在定时采样任务执行过程中将采样的条件与完成次数比较,如果达到了采样条件中预设的采样次数,就关掉采样中断。中断服务函数的流程如图8.4所示。图8.3 定时中断服务流程图注意,申请互斥信号函数OSMutexPend(Sem,0,&err)必须在打开中断之前进行,否则可能会丢失采样数据。这个定时中断服务函数关联采样任务的原理,从参见STM32F103 处理器内部资源C编程与实例一章。中断关联采样任务程序清单如下,void TaskIntSamp(void *
35、pdata) INT8U err,Samparr4; /局部变量短消息数组 INT16U cl,cfg,i,*pt; /采样条件和周期 INT32U Temp; /临时变量,保存A/D转换的临时结果 pt = (INT16U *) pdata; cl = pt0; cfg = pt1; OSMutexPend(Sem,0,&err); Samp0; Samparr0 = 0xf0; Samparr1 = 0xff; OSQPost(ReMsgQeue,(void*)Samparr); /向显示任务发生开始采样消息 OSTimeDly(1); /复位中断源 T1IR = 0x00; T1PR =
36、 0x00; I1TCR = 0x01; T1MCR = cl*(Fpclk/1000); VICIntSelect =0; VICIntEnable = 15; Temp = ADDR; IO2CLR = 0xff16; /指示开始采样 for(i=1;icfg+1;i+) Smapi= *(INT16U)OSMboxPend(MyMbox,0,&err); /获取并保存转换结果 Samparr1 = (INT8U)(160*Sampi/3000); OSQPost(REMsgQeue,(void*)Samparr); /向显示任务发生消息 OSMutexPost(Sem); /释放互斥信号量 /禁止定时器产生中断 IO2SET = oxff16; /指示采样结束 OSTaskDel(OS_PRIO_SELF); void Timer1_Exception(void) static INT32U Temp; OS_ENTER_CRITICAL(); T1IR = 0x01; /清除中断源 /通知中断控制器中断结束 Temp= ADDR; /进行第一次转换 /等待转换结束 /再次启动转换 OS_EXIT_CRITICAL(); /开中断 OSMbo