第8章 基于ucos-ii的程序设计实例

更新时间:2023-11-29 09:21:02 阅读量: 教育文库 文档下载

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

第2章 可编程控制器的硬件结构

第八章 基于μCOS-II的程序设计实例

8.1 实例介绍

为了使读者对μCOS-II操作系统有更深的理解,本章将介绍一个在STM32F103 处理器平台上使用μCOS-II实时操作系统的程序设计实例。此实例使用英倍特公司提供的STM103V100评估板来实现一个简易温度计。该实例使用STM103V100评估板自带的高灵敏度数字温度传感器来传送温度数据,根据实际采样周期的需要,安排了四种不同的采样方式。采样的条件和周期可以通过键盘输入进行调节,采样得到的结果可以在评估板的液晶屏上同步显示,并通过串口将采样所得的结果送到上位机。关于STM103V100评估板的更多内容超出本书范围,请读者参阅其它相关资料。

8.2 实例分析

本节主要分析如何通过基于实时操作系统编程的方法实现整个系统的所有功能。下面的内容将从任务划分开始,详细说明任务分析的过程。

8.2.1 实例任务划分

为了更合理的将整个系统划分为不同任务,首先要明确一个好的实时系统应具备那些特点,即任务划分的基本原则是什么。一般说来,任务划分的基本原则有以下几点:

? 满足系统“实时性”:一般使用μCOS-II的嵌入式应用系统,对于响应时间要求很高,如果实时性得不到满足,系统会出现错误甚至导致难以挽回的故障。因此在任务划分时,保证系统实时性是首要原则。

? 较少资源需求:多个任务协同运转,依靠操作系统的调度策略。任务之间的同步,任务之间的通信,内存管理都需要消耗系统资源。所以在任务划分时,尽量将使用同类资源的应用归入同一任务中,以减少操作系统调度时所消耗的资源。

? 合理的任务数:同一系统,任务划分的数目越多,每个任务的功能越简单,实现越容易,但任务数目的增多,加大了操作系统的调度负担,资源开销也随之加大;相反,如果任务划分的数目太少,会增加每个任务的复杂性,使任务设计难度加大。最极端的情况,当系统任务数目减少到1时,也就失去了使用多任务操作系统的意义。

对一个具体的嵌入式应用系统进行任务划分时,可以有不同的任务划分方案。常用的任

·11·

可编程控制器应用技术与设计实例

务划分方法有:以硬件模块为对象划分任务、以实时性优先原则划分任务和切分耗时任务等。所谓“以硬件模块为对象划分任务”就是根据系统需求,以硬件模块相关驱动为基础,根据硬件驱动在系统中的关键性设定优先级的任务划分方法;“实现性优先原则划分任务”即,将对实时性要求较高的应用划分为单独任务,并赋予较高的优先级来保证整个系统实时性的要求;而“切分耗时任务”的任务划分方法,顾名思义就是将一些占用大量CPU处理时间的繁琐应用从系统中分离出来,作为一个优先级较低的任务在系统空闲时运行。

根据上述任务划分的原则和方法,本实例被划分为7个任务:包括4个采用任务,1个负责和用户交互的键盘任务,1个显示任务和1个向上位机传送数据的串口发送任务。其中,4个采样任务分别使用不同的采样条件:延时采样、使用系统时钟节拍采样、定时中断采样和使用高优先级中断的采样。键盘任务除负责接收用户输入并做出反馈外,还需要完成操作系统和系统资源的初始化,包括系统中用到的消息队列、邮箱和互斥信号量的创建等。因为按键任务是第一个启动的用户任务,所以目标板的初始化也由该任务完成。

8.2.2 实例任务设计与优先级分配

完成系统任务划分后,需要对任务的优先级进行设定。任务优先级分配是否合理,直接影响到系统的实时性和可靠性。对任务优先级的安排一般遵循以下原则:

? 外设相关任务安排高优先级:因为外设任务直接与中断服务程序相对应,如果外设任务优先级低,当需要中断处理时,系统资源可能被高优先级任务占用,而导致中断丢失;

? 根据任务实现功能的重要性安排优先级:一般情况下,任务越重要优先级越高; ? 占用关键资源的任务优先级尽量高:只有保证占用关键资源的任务优先运行,才能使其尽早释放资源,以便其它任务运行;

? 对于周期性任务,执行周期越短的任务,优先级应越高,以保证其得到及时运行; ? 当以上条件相近时,耗时越短的任务优先级应越高。这样可以缩短其它就绪任务的延时时间。

根据以上优先级安排原则,系统中存在的4个采样任务实现同样功能且均为一次性任务。所以,4个任务的优先级应一致。但在μCOS-II操作系统中,同一优先级不允许存在多个任务,所以将4个采样任务的优先级依次排列,因为它们不可能同时发生,则对于应用来说,4个任务的优先级是一致的。

键盘任务是系统运行的第一个任务,负责与用户交互。根据优先级安排原则,将其优先级定为7个任务中的中等级别,设为15。

显示任务负责将采集的数据显示在LED屏上,它由采样任务触发。根据优先级安排原则,将其优先级定为7个任务中的最低优先级,设为17。

系统中任务的优先级安排如下程序清单所示,

void Task_FastSamp(void *pdata); //使用高优先级中断的采样,优先级5 void Task_HookSamp(void *pdata); //使用钩子函数的采样任务,优先级6 void Task_TimerSamp(void *pdata); //使用定时中断的采用任务,优先级7 void Task_DelaySamp(void *pdata); //使用延时函数的采用任务,优先级8 void Task_Send(void *pdata); //串口发送任务,优先级13 ·12·

第2章 可编程控制器的硬件结构

void Task_Key (void *pdata); //键盘任务,优先级15 void Task_Disply(void *pdata); //显示任务,优先级17

程序运行过程中,当系统检测到键盘输入后,根据采样周期的设定值而创建一个采样任务和串口发送任务Task_Send()。4个采样任务Task_FastSamp(),Task_HookSamp(),Task_TimerSamp(),Task_DelaySamp()和串口发送任务Task_Send()均为一次性任务,且在任务完成后自动删除。Task_Disply()为消息队列驱动的任务,根据消息队列传送的内容进行不同的显示操作。Task_Key()是周期性执行的任务,不断检测键盘输入,并根据检测结果,做出不同的响应。

键盘任务是系统运行的第一个任务,负责系统初始化和其它任务的创建。该任务运行后,接收用户输入并根据接收到的输入值启动不同的采样任务。同时,将接收到的输入值通过消息队列的形式发送到显示任务。

显示任务负责显示用户输入的参数值和采样任务采样得到的数据。

串口发送任务在键盘任务检测到发送操作按键确定时创建。该任务负责从全局采样数组中读取数据并使用中断方式发送到上位机。全局采样数组是系统初始化时建立的FIFO缓存,通过互斥信号量保护。

4个采样任务均在键盘接收到用户输入后创建,不同的采样任务使用不同的采样方式进行采样,并将采样所得数据保存在全局FIFO缓存中。所有采样任务在采样过程中的工作就是检测什么时候采样结束,并且负责将采样所得的数据进行处理。处理完毕后,发送消息通知显示任务,最后删除自己本身。

8.3 任务实现详解

开始程序设计之前,首先了解一下主函数。在主函数中,进行了操作系统的初始化,创建了一个键盘任务,然后,启动多任务操作系统。主程序代码如下:

int main(void) {

OSTaskCreateExt(Task_Key, //任务指针

·13·

Tmr_TickInit(); // 操作系统Tick初始化 OSInit();

/* 初始化OS */

Bsp_Init(); //系统外设初始化

PWM_IoConfiguration(); //初始化系统时钟 #if (OS_TASK_NAME_SIZE >= 16)

INT8U err; #endif

可编程控制器应用技术与设计实例

(void *)0,

(OS_STK *)&InitTaskStk[OS_INIT_TASK_STACK_SIZE - 1],

}

OSStart(); return(0);

/* 启动多任务环境 */

OS_TASK_KEY_PRIO, //任务优先级 OS_TASK_KEY_PRIO, (OS_STK *)&InitTaskStk[0], OS_INIT_TASK_STACK_SIZE, (void *)0,

OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);

#if (OS_TASK_NAME_SIZE >= 16)

OSTaskNameSet(OS_TASK_IDLE_PRIO, (INT8U *)\OSTaskNameSet(OS_TASK_INIT_PRIO, (INT8U *)\

#endif

从程序中可以看到,当执行OSStart()后,主函数返回,但了解了ucos-II操作系统后,可以知道这个“return 0”的语句是不可能执行的。在主函数中创建了一个键盘任务Task_Key(),其优先级为15,负责初始化目标板和根据用户输入创建其他任务。

8.3.1 键盘任务

键盘任务的主要工作就是周期性扫描键盘,优先级为15。其程序流程图如图8.1所示。

·14·

第2章 可编程控制器的硬件结构

开始目标板初始化串口A/D转换初始化创建显示任务TaskDisp创建互斥信号量创建消息邮箱创建消息队列按键处理

图8.1 键盘任务程序流程图

程序中,首先进行目标板初始化,目标板初始化过程请参见《STM32F103 处理器内部资源C编程与实例》一章讲解。之所以将目标的初始化放在第一个任务里,是为了保证该初始化在OSStart()执行后完成。值得注意的是,与采样任务相关的A/D转换初始化也放在了这里,这是因为4个采样任务都用到了A/D转换,放在键盘任务中一次完成,避免了在每个采样任务中单独初始化而带来的代码冗余。

接下来创建显示任务Task_Disp(),屏幕初始化工作放在 该任务中完成。

最后创建了任务轮转不可或缺的一些操作系统资源,包括一个互斥信号量Sem,一个邮箱MyMbox和一个消息队列ReMsgQeue,其中消息队列中消息数目定义为10。

按键处理是这个键盘任务的关键,这个键盘任务中状态转移情况如表8.1所示。

表8.1 键盘状态转移表

·15·

可编程控制器应用技术与设计实例

初始状态 状态1:状态选定 操作键 “确定”键 “数字”键 “清零”键 “移动”键 状态2:启动采样 “确定”键 动作 flag状态翻转,进入周期修改状态或退出修改状态 如果为数字键并且flag==1,则设定数字有效并显示;否则不响应 如果flag==1,则将保存的设定值清零并显示,否则不响应 如果flag==0,则进入启动选定状态并显示,否则不响应 如果clflagp==1,则清零clflagp。 如果采样周期为20ms,则创建钩子函数采样任务 如果采样周期为20ms的整倍数,但不为20ms,则创建延时函数采样任务。 “移动”键 状态3:清屏操作 状态4:发送命令状态 状态5:采样周期设定状态 “确定”键 “移动”键 “确定”键 “移动”键 “确定”键 “数字”键 “清零”键 “高优先级采样”键 “移动”键 进入显示选定状态 清除上一次显示的采样数据 创建发送任务,将采样所得数据送上位机 创建发生任务,将采样所得的数据发送到上位机 进入周期选定状态 flag状态翻转,进入周期修改状态或退出修改状态 如果为数字键并且flag==1,则设定数字有效并显示;否则不响应 如果flag==1,则将保存的设定值清零并显示,否则不响应 如果flag==1,则创建高优先级采样标志clflg=1,否则不响应 如果flag==1,则进入条件选定状态并显示,否则不响应 从表8.1中可以清晰看出系统被划分成的5种状态,且在5种状态下,不同的按键动作产生的不同后果:

? 状态1:条件选定状态,处理采样条件设定相关内容;

? 状态2:启动采样状态,即创建采样任务。使用一个UINT16型的数组保存采样周期和采样条件,并将数组指针通过创建任务函数参数(void *)指针传递给采样任务;

? 状态3:清屏操作,即清除本次采样数据。

? 状态4:发送命令状态,将本次采样得到的数据通过串口发送到上位机。

? 状态5:采样周期设定状态,用户可以在该状态下根据自己的采样需要通过不同的按键设置系统采样周期。

这个键盘任务的代码清单如下,

void TaskKey(void *pdata) {

INT8U err; INT16U Fdiv; INT16U cl=100; INT16U cfg=320; INT16U cond[2]; ·16·

第2章 可编程控制器的硬件结构

INT8U Key,Key0, clflg=0; INT8U locad=0,locas=0; INT8U Keyarr[4],clrs=0,flag=0;

pdata=pdata;

TargetInit(); //硬件初始化

UOFCR = 05; U0IER= 0x02; U0LCR=0x83;

Fdiv= (Fpclk/16)/115200; //设置波特率 U0DLM = Fdiv/256; U0DLL = Fdiv%6; U0LCR = 0x03; PINSEL1= 0x00400000;

IO2DIR = 0xff<<16; //设置LED为输入 IO2SET = 0xff<<16;

/*进行AD转换设置*/ ADconfig();

TimeDly(OS_TICKS_PER_SEC/10); //延时0.1s

/*创建显示任务TaskDisp, 优先级17*/

OSTaskCreate(TaskDisp, (void*)0,&TaskDispStk[TASK_STK_SIZE-1],17); Sem = OSMutexCreate(4,&err); //创建互斥信号,优先级继承值为4 MyMbox = OSMboxCreate((void×)0); //创建消息邮箱

//创建接受消息队列RegMsgQeue接收所有任务发生的消息

ReMsgQeue = OSQCreate(&MsgQeueTb[0],10); //最大可以存放10条消息 while() {

OSTimeDly(1); // 延时

Key= GetKey(); //获取按键操作的消息

if((key==0)|| (Key>16)||(Key != Key0)) //未按键或与上次不同 {

Key0=Key; //记录本次按键 }

·17·

continue;

可编程控制器应用技术与设计实例

swich(locas) ·18·

{ case0:

switch(Key0) { case0:

if(flag)

cl=cl*10+Key-1; //周期显示信息更改 break;

if(Key<11)Key0=0; //数字键0~9,键码值分别为1~10 else Key0=Key; //4个命令键

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;

第2章 可编程控制器的硬件结构

OSTaskCreate(TaskDelaySamp, (void*)cond, &TaskHookSampStk[TASK_STK_SIZE-1],11);

·19·

/*创建中断采样任务,将采样条件传送给采样任务*/

OSTaskCreate(TaskIntSamp, (void*)cond, &TaskHookSampStk[TASK_STK_SIZE-1],9); else

/*创建节拍钩子函数采样任务*/

OSTaskCreate(TaskhookSamp, (void*)cond, &TaskHookSampStk[TASK_STK_SIZE-1],7); else if((cl )!=0)

else if (cl==20) //采样周期为系统节拍时钟时间

case2:

if(!flag)locad = (locas+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) {

cond[0]=cl; }

cond[1]=cfg; if(clflg) {

clflg =0; //清除高优先级采样标志 }

/* 创建高优先级采样任务,将采样条件传说给采样任务*/

OSTaskCreate(TaskFiqSamp,(void*)cond, &TaskHookSampStk[TASK_STK_SIZE-1],5); }

可编程控制器应用技术与设计实例

}

if(!clrs)Keyarr[0]= ox0f; else clrs=0; if(locad !=locas) { ·20·

} } else {

Keyarr[1]= (1<<4)|flag;

Keyarr[2]= cfg/256; //保存采样条件 Keyarr[3]= cfg%6; Keyarr[1]= 0xff; } else if {

Keyarr[1]=flag; Keyarr[2] = cl/256; Keyarr[3]= cl%6;

Keyarr[2]= locas; locas = locad; case3:

if(Key0==2) locad=(locas +1)%5;

else if (Key0==3)

//创建串口发生任务,优先级13

OSTaskCreate(TaskSend, (void*)cond, &TaskHookSampStk[TASK_STK_SIZE-1],13); break;

case3: //清屏条件下

if(Key0==2) locad=(locas +1)%5;

else if (Key0==3) clrs=1; Keyarr[0] = 0; //清屏命令标志 break;

break;

本文来源:https://www.bwwdw.com/article/11jt.html

Top