Presentation is loading. Please wait.

Presentation is loading. Please wait.

6. 面向任务程序设计(TOP).

Similar presentations


Presentation on theme: "6. 面向任务程序设计(TOP)."— Presentation transcript:

1 6. 面向任务程序设计(TOP)

2 6. 面向任务程序设计(TOP) 程序设计方法 任务与函数 任务构造方法 任务优先级与堆栈 任务调度与切换 任务间同步与通信 任务挂起与恢复

3 任务种类与关系图

4 6.1 程序设计方法 程序设计方法最重要的指导思想是模块化设计的思想。在早期面向过程的程序设计中,模块化体现为将要实现的工作编写为一个个的函数,供应用程序调用;在面向对象的程序设计中,模块化体现为将要实现的工作封装为一个个集成了数据和方法的对象(即类定义的变量),程序的执行体现为每个对象的方法执行。

5 6.1p01 任务与对象的属性对比 属性 任务 对象 数据结构
具有专属于任务的变量,当任务运行时,这些变量位于内存中;当任务处于非执行态时,这些变量处于堆栈中 对象具有私有、保护和公有三种类型的变量,对像存活期间这些变量均有效;对象被删除后,这些变量消失 函数(或方法) 任务中可以调用任何函数,对于不可重入型函数,应使用信号量保护;任务本身对应着一个函数,任务变量就是这个函数的局部变量 对象只能使用定义它的类中的函数、友元函数、继承函数或公有函数,属于对象的函数称为对象的实现方法,简称方法;对象包含很多函数

6 6.1p02 心电测试仪功能分解

7 6.2 任务与函数 函数是完成特定功能的一组代码集合,包括函数声明和函数体,具有一个返回值和参数列表,典型的C函数样式为: 返回类型 函数名(参数列表){ 语句组;} 其中,函数名首字符必须为字母或下划线,可返回任意数据类型,如果返回类型为void,则表示无返回值;参数列表中的参数可为任意个或任意类型,如果没有参数列表,默认为void型,即空参数类型。语句组为函数执行的功能,即函数体,需要用花括号括起来。

8 6.2p01 函数示例(1) 1 void LEDoff(void) 2 { 3 unsigned int val; 4 val=GPCDAT; 5 GPCDAT = (val | 0xE0); 6 }

9 6.2p02 函数示例(2) 8 INT8U LED_Flash(INT8U u) //LED Flash 9 { 10 INT32U val; 11 val=GPCDAT; 12 switch(u) 13 { 14 case 1: 15 GPCDAT = val ^ 0x0020; // LED1 Flash 16 break; 17 case 2: 18 GPCDAT = val ^ 0x0040; // LED2 Flash 19 break; 20 case 3: 21 GPCDAT = val ^ 0x0080; // LED3 Flash 22 break; 23 default: 24 GPCDAT = val ^ 0x00E0; // LED1,2,3 Flash 25 break; 26 } 27 return(1); 28 }

10 6.2p03 任务对应的函数必须具有的特点 (1) 函数返回值为空,即返回类型为void,或无返回值。 (2) 函数参数为void *类型,且只能有一个该类型参数。 (3) 函数中有一个无限循环,即死循环。 (4) 死循环中必需出现延时等待、请求事件(或删除任务自己)的语句。

11 6.2p04 任务举例 1 void AppTask_1(void *pdata) 2 { 3 INT8U err; 4 5 OSTaskNameSet(OS_PRIO_SELF,"AppTask_1",&err); 6 for(;;) 7 { 8 LED_Flash(1); 9 10 OSTimeDlyHMSM(0,0,1,0); 11 } 12 }

12 6.2p05 创建任务示例(1) 1 OSTaskCreateExt(AppTaskStart, 2 (void *)0, 3 &AppTaskStartStk[TASK_START_STK_SIZE-1], 4 AppTaskStartPrio, 5 AppTaskStartID, 6 &AppTaskStartStk[0], 7 TASK_START_STK_SIZE, 8 (void *)0, 9 OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

13 6.2p06 创建任务示例(2) 11 OSTaskCreate(AppTask_1, 12 (void *)0, 13 &AppTask_1_Stk[TASK_STK_SIZE-1], 14 AppTask_1_Prio);

14 6.2p07 µC/OS-II系统中的任务“注册”信息

15 6.2p08 任务与函数的关系(1) 属性 任务(或任务函数) 普通函数 基本要素 任务函数、优先级、堆栈 函数声明、函数体 函数返回值
任务函数返回值必须为空 返回值可为任意类型 函数参数 任务函数参数必须为void *类型,且只有一个该类型参数 形式参数可以为任意类型,且可以有任意多个 函数中有无死循环 任务函数中必须有死循环 不能有死循环 函数中能否调用系统服务 任务函数可以调用系统服务 当普通函数被任务函数调用时,才能使用系统服务函数 有无独立堆栈 具有独立的堆栈 使用公共堆栈

16 6.2p09 任务与函数的关系(2) 函数个数有无限制 用户任务函数最多253个 可以为任意多个 函数创建方法
使用OSTaskCreate或OSTaskCreateExt系统函数创建任务 指定函数原型和语句组创建 函数间数据交互 任务函数间使用事件进行数据通信 普通函数间使用指针或全局变量进行数据交互 函数工作状态 任务有五种工作状态,即休眠态、就绪态、等待态、运行态和中断态 普通函数只有休眠态、运行态和中断态三种 是否被系统调度 具有优先级,参与系统调度,总使得最高优先级就绪任务得到执行,其他状态的任务对系统内核可见 被调用时执行,调用完成后对应用程序不可见

17 6.3 任务构造方法 针对博创UP-CPU2410实验平台上的三个LED灯,要求实现这三个LED灯循环闪烁的功能。基于µC/OS-II系统可以有三种实现方法:其一为创建一个指示层任务;其二为创建三个输入输出层任务;其三为创建计算层和输入输出层任务共同实现。

18 6.3p01 指示层任务设计过程(1) 首先,为该任务指定优先级。由于系统中指示层任务属于完成附加功能的任务,所以其优先级往往较低,但必须比系统的统计任务和空闲任务要高,因此,可根据需要指定其为接近于空闲任务优先级号的数值。如果空闲任务在系统中的优先级为63,则可指定该指示层任务优先级为小于59但接近59的数值。

19 6.3p02 指示层任务设计过程(2) 然后,为该任务创建堆栈。堆栈是使用内存的主要单位,一般地,堆栈的大小应为根据程序推算出来的堆栈大小的1.2~1.3倍,甚至更大,这样可有效地解决不可预测的临时中间变量造成的堆栈消耗。对于指示层任务,由于其处理的工作简单,中间变量少或没有,堆栈不应指定过大,以避免浪费。

20 6.3p03 指示层任务设计过程(3) 最后,为该指示层任务创建函数。该任务函数的功能为每1秒依次循环点亮各个LED灯,根据实现的功能要求,函数体应包括点亮LED灯的语句和系统延时函数。

21 6.3p04 任务函数 AppTask_1 1 void AppTask_1(void *pdata) 2 { 3 INT8U err; 4 INT8U i; 5 i=1; 6 OSTaskNameSet(OS_PRIO_SELF,"AppTask_1",&err); 7 for(;;) 8 { 9 LEDon(i); 10 i++; 11 if(i>3) 12 i=1; 13 OSTimeDlyHMSM(0,0,1,0); 14 } 15 }

22 6.3p05 和创建任务AppTask_1相关的操作(1)
(1) 创建该任务的堆栈。 #define TASK_STK_SIZE OS_STK AppTask_1_Stk[TASK_STK_SIZE]; (2) 定义该任务的优先级和识别号。 #define AppTask_1_Prio #define AppTask_1_ID

23 6.3p06 和创建任务AppTask_1相关的操作(2)
(3) 定义任务扩展数据结构(这一步根据需要设定)。 #define N_TASKS typedef struct { INT8S TaskName[30]; //Task's name INT32U TaskCtr; //Run times INT32U TaskExecTime; //Need a Timer, describe later }TASK_USER_DATA; TASK_USER_DATA TaskUserData[N_TASKS];

24 6.3p07 和创建任务AppTask_1相关的操作(3)
(4) 调用OSTaskCreateExt函数创建任务。 strcpy((char *)TaskUserData[AppTask_1_ID].TaskName,"Task_1"); OSTaskCreateExt(AppTask_1, (void *)0, &AppTask_1_Stk[TASK_STK_SIZE-1], AppTask_1_Prio, AppTask_1_ID, &AppTask_1_Stk[0], TASK_STK_SIZE, &TaskUserData[AppTask_1_ID], OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR); (5) 创建任务函数体,如程序段S7-4所示的函数AppTask_1。

25 6.3q01 输入输出层任务设计(1) 实现三个LED灯循环闪烁的第二种任务设计方法,是把每个LED灯的亮灭视为一个任务,这样三个LED灯的工作状态变化需要三个独立的任务完成,这三个任务的功能在于控制外设(这里是LED灯的亮灭),因此,可称为输入输出层任务。

26 6.3q02 任务函数 AppTask_1、AppTask_2和AppTask_3(1)
1 void AppTask_1(void *pdata) 2 { 3 INT8U err; 4 5 OSTaskNameSet(OS_PRIO_SELF,"AppTask_1",&err); 6 for(;;) 7 { 8 LEDon(1); 9 OSTimeDlyHMSM(0,0,3,0); 10 } 11 }

27 6.3q03 任务函数 AppTask_1、AppTask_2和AppTask_3(2)
13 void AppTask_2(void *pdata) 14 { 15 INT8U err; OSTaskNameSet(OS_PRIO_SELF,"AppTask_2",&err); 18 for(;;) 19 { 20 OSTimeDlyHMSM(0,0,1,0); 21 LEDon(2); 22 OSTimeDlyHMSM(0,0,2,0); 23 } 24 }

28 6.3q04 任务函数 AppTask_1、AppTask_2和AppTask_3(3)
26 void AppTask_3(void *pdata) 27 { 28 INT8U err; OSTaskNameSet(OS_PRIO_SELF,"AppTask_3",&err); 31 for(;;) 32 { 33 OSTimeDlyHMSM(0,0,2,0); 34 LEDon(3); 35 OSTimeDlyHMSM(0,0,1,0); 36 } 37 }

29 6.3r01 计算层和输入输出层任务联合设计(1) 把控制三个LED灯的亮灭的任务视为输入输出层任务,而把三个LED灯亮灭的工作方式视为计算层任务,这时需要创建二个任务,且这二个任务间要进行数据同步和通信。这里采用的方法为:计算层任务每隔1秒执行一次,向输入输出层任务释放消息邮箱;输入输出层任务请求到消息邮箱后得到执行,然后提取消息值,根据得到的消息选择控制方式,使三个LED灯循环闪亮。

30 6.3r02 任务AppTask_1和AppTask_2(1)
1 OS_EVENT *LedShineMbox; 2 void AppTask_1(void *pdata) 3 { 4 INT8U err; 5 INT8U iStyle; 6 iStyle = 1; 7 LedShineMbox=OSMboxCreate(NULL); 8 OSEventNameSet(LedShineMbox,"Led Shine",&err); 9 10 OSTaskNameSet(OS_PRIO_SELF,"AppTask_1",&err); 11 for(;;) 12 { 13 switch(iStyle) 14 {

31 6.3r03 任务AppTask_1和AppTask_2(2)
15 case 1: 16 OSMboxPost(LedShineMbox,(void *)1); 17 break; 18 case 2: 19 OSMboxPost(LedShineMbox,(void *)2); 20 break; 21 case 3: 22 OSMboxPost(LedShineMbox,(void *)3); 23 break; 24 } 25 iStyle++; 26 if(iStyle>3) 27 iStyle=1; 28 OSTimeDlyHMSM(0,0,1,0); 29 } 30 }

32 6.3r04 任务AppTask_1和AppTask_2(3)
32 void AppTask_2(void *pdata) 33 { 34 INT8U err; 35 void *pmsg; OSTaskNameSet(OS_PRIO_SELF,"AppTask_2",&err); 38 for(;;) 39 { 40 pmsg=OSMboxPend(LedShineMbox,0,&err); 41 LEDon((INT32U)((void *)pmsg)); 42 } 43 }

33 6.4 任务优先级与堆栈 任务有三个要素,即任务先级、任务堆栈和任务函数,设计任务只需要设计任务的三个要素。 任务优先级号是任务的标识符,用于唯一地标识一个任务;任务识别号(ID号)没有实际意义。 每个任务必须具有自己独立的堆栈,不同任务的堆栈大小可以不同。

34 6.4p01 优先级号的取值范围 优先级号的数值不能随意指定,os_cfg.h文件中的宏常量OS_LOWEST_PRIO是最大的优先级号数值,如果 #define OS_LOWEST_PRIO 254 则优先级号最大取值为254,由于µC/OS-II系统内部的统计任务和空闲任务要占用二个优先级号,所以,用户任务的最大优先级号数值为OS_LOWEST_PRIO-2。最小优先级号数值为0。优先级号数值越小,任务的优先级越高,因此,优先级号为0的任务优先级最高。

35 6.4p02 任务个数与优先级号的关系 在os_cfg.h中还有另外一个宏常量OS_MAX_TASKS,该宏常量定义用户应用程序中最大任务数。 如果宏常量OS_MAX_TASKS的值定义为20,即 #define OS_MAX_TASKS 20 除去三个系统任务(空闲任务、统计任务和定时器任务)外,用户最多创建的用户任务为17个,这17个任务的优先级号必须为0~OS_LOWEST_PRIO-2中的17个不同数值。

36 6.4p03 任务优先级号的分配原则 任务的重要性与其实现的功能密切相关 一般地,那些指示层的任务,由于提供系统运行状态的信息,不需要严格的实时性,故其优先级应设为所有用户任务中最低的。 计算层任务对数据进行处理,对于大数据量的处理,计算层任务会占用较多的CPU时间;输入输出层任务一般仅对内存地址进行操作,必须有严格的时序关系,占用CPU时间较少,并且,输入输出层任务常配合中断服务程序工作。因此,计算层和输入输出层任务的优先级的指定采用规律:输入层任务的优先级高于计算层任务的优先级,计算层任务的优先级高于输出层任务的优先级。

37 6.4p04 任务及其应用程序抽象描述

38 6.5 任务调度与切换 任务调度是指从就绪的所有任务中找出优先级最高的任务,使其获得CPU使用权而执行;任务切换是指CPU从一个任务转到为另一个任务服务的过程。当中断服务程序获得CPU使用权,而后中断服务程序执行完后,将发生任务调度,如果被中断的任务的优先级低于就绪的最高优先级任务,则发生任务切换;如果被中断的任务的优先级高于就绪的最高优先级任务,则不发生任务切换,仍然恢复被中断的任务执行。因此,任务调度不一定会发生任务切换,但是任务切换一定会伴随着任务调度。

39 6.5p01 什么情况下发生任务调度?(1) (1) 当开启多任务时,即调用OSStart函数时。 (2) 当新建一个新任务时,即调用OSTaskCreate或OSTaskCreateExt函数时(必须开启多任务功能)。 (3) 当中断返回时,即调用OSIntExit函数时。 (4) 当任务执行过程中遇到延时函数时,即调用OSTimeDly或OSTimeDlyHMSM时。 (5) 当任务请求事件而得不到时,即调用OSSemPend、OSMboxPend、OSFlagPend、OSMutexPend、OSQPend、OSEventPendMulti等函数而请求不到事件时。

40 6.5p02 什么情况下发生任务调度?(2) (6) 当任务释放事件且有某个任务正在请求该事件时(µC/OS-II似乎不支持抢占式调度,即如果某个优先级高的任务在请求该事件,也要等释放该事件的任务执行完后再调度),即调用OSSemPost、OSMboxPost、OSMboxPostOpt、OSMutexPost、OSQPost、OSQPostFront、OSQPostOpt和OSFlagPost函数时。 (7) 当任务被挂起或恢复时,即调用OSTaskSuspend或OSTaskResume函数时。 (8) 当任务被删除时,即调用OSTaskDel函数时。 (9) 当任务延时被取消时,即调用OSTimeDlyResume函数时。 (10) 当调用系统时钟节拍函数时,即调用OSTimeTick函数时。

41 6.5p03 任务调度器的CPU时间 无论操作系统本身设计得多么出色,任务调度和切换都是与应用程序无关的代码,这部分代码越少,操作系统直接使用CPU的时间就越少,从而应用程序得到尽可能多的CPU时间。此外,任务调用和切换时间应该是固定的,不应随任务数量的多少而变化,这样才能准确计算各个用户任务的CPU使用时间。

42 6.5p04 任务函数中必需包含调度因素 作为任务的要素的任务函数中,必须包含至少一个能使任务发生调度的函数,即上述10种情况下的函数之一,例如延时函数和请求与释放事件函数等,否则调度器无法调度任务。

43 6.6 任务间同步与通信 如果某个任务按另一个任务的执行节拍同步执行,则称该任务与另一个任务同步;如果某个任务接收来自另一个任务的信息,称该任务与另一个任务通信。在µC/OS-II中,任务间不但可以单向同步与通信,还可以双向同步与通信,同步与通信的手段是事件。

44 6.6p01 任务间的事件同步与通信

45 6.6p02 任务对事件的操作? 任务对事件的主要操作有二种,即请求事件和释放事件。当任务请求事件时,任务从事件容器中找出所需要的事件,如果请求成功,则在任务中解析事件,从事件中分析出有用的信息。当任务释放事件时,首先把要发出的信息打包到事件中,然后,将该事件释放到事件容器中(如果有任务正在请求该事件,则事件直接转移到请求该事件的符合条件的任务中)。

46 6.7 任务挂起与恢复 任务有一种特殊的等待状态,即挂起状态。通过调用函数OSTaskSuspend可以将正在执行的任务自己或处于就绪态和等待态的其他任务挂起。任务一旦被挂起后,只能由其他的任务调用函数OSTaskResume使被挂起的任务恢复其原来的状态,如果原来处于等待态,则恢复后的任务仍然为等待态,并且继续等待延时时间(注意:挂起状态的任务的延时值在任务被挂起期间会随时钟节拍发生变化;如果某时刻延时值变为0了,任务仍然处于挂起状态,不会就绪);如果原来的任务状态为就绪态或执行态,则恢复后的任务处于就绪态,能否立即执行要视其优先级是否比就绪的其他任务的优先级都要高。

47 6.7p01 任务挂起的主要用途? 由于任务创建后将永不停息地工作下去,如果想使得某个任务工作一段时间后,暂停一段较长的时间,等到一个新的情况发生后,再使用运行。诸如此类的情况,将使用到任务挂起和恢复功能。 任务挂起后,任务仍然存在,其优先级号不能被新创建的任务使用。

48 需要掌握的知识点 任务的三要素是什么?任务与函数有何本质区别? 任务可分为哪几种类型,各类任务都有什么特点? 任务在什么时候发生调度?任务调度和任务切换是什么关系?


Download ppt "6. 面向任务程序设计(TOP)."

Similar presentations


Ads by Google