实验四 UCOSII消息队信号量和软件定时器Word文档下载推荐.docx
- 文档编号:4817146
- 上传时间:2023-05-04
- 格式:DOCX
- 页数:30
- 大小:197.83KB
实验四 UCOSII消息队信号量和软件定时器Word文档下载推荐.docx
《实验四 UCOSII消息队信号量和软件定时器Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《实验四 UCOSII消息队信号量和软件定时器Word文档下载推荐.docx(30页珍藏版)》请在冰点文库上搜索。
INT16UOSQEntries;
}OS_Q;
其中,可以移动的指针为OSQIn和OSQOut,而指针OSQStart和OSQEnd只是一个标志
(常指针)。
当可移动的指针OSQIn或OSQOut移动到数组末尾,也就是与OSQEnd相等时,可移动的指针将会被调整到数组的起始位置OSQStart。
于是,这个由消息指针构成的数组就头尾衔接起来形成了一个如图所示的循环队列。
在UCOSII初始化时,系统将按文件os_cfg.h中的配置常数OS_MAX_QS定义OS_MAX_QS个队列控制块,并用队列控制块中的指针OSQPtr将所有队列控制块链接为链表。
由于这时还没有使用它们,故这个链表叫做空队列控制块链表。
在UCOSII中,与消息队列相关的几个函数(未全部列出,下同)。
创建消息队列函数
创建一个消息队列首先需要定义一指针数组,然后把各个消息数据缓冲区的首地址存入这个数组中,然后再调用函数OSQCreate来创建消息队列。
创建消息队列函数OSQCreate的原型为:
OS_EVENT*OSQCreate(void**start,INT16Usize)
其中,start为存放消息缓冲区指针数组的地址,size为该数组大小。
该函数的返回值为消息队列指针。
请求消息队列函数
请求消息队列的目的是为了从消息队列中获取消息。
任务请求消息队列需要调用函数OSQPend,该函数原型为:
void*OSQPend(OS_EVENT*pevent,INT16Utimeout,INT8U*err)其中,pevent为所请求的消息队列的指针,timeout为任务等待时限,err为错误信息。
向消息队列发送消息函数
任务可以通过调用函数OSQPost或OSQPostFront两个函数来向消息队列发送消息。
函数OSQPost以FIFO的方式组织消息队列,函数OSQPostFront以LIFO的方式组织消息队列。
这两个函数的原型分别为:
INT8UOSQPost(OS_EVENT*pevent,void*msg)、INT8UOSQPost(OS_EVENT*pevent,void*msg)。
其中,pevent为消息队列的指针,msg为待发消息的指针。
2)信号量集
在实际应用中,任务常常需要与多个事件同步,即要根据多个信号量组合作用的结果来决定任务的运行方式。
UCOSII为了实现多个信号量组合的功能定义了一种特殊的数据结构——信号量集。
信号量集所能管理的信号量都是一些二值信号,所有信号量集实质上是一种可以对多个输入的逻辑信号进行基本逻辑运算的组合逻辑。
成员OSFlagWaitList是一个指针,当一个信号量集被创建后,这个指针指向了这个信号量集的等待任务链表。
与其他前面介绍过的事件不同,信号量集用一个双向链表来组织等待任务,每一个等待任务都是该链表中的一个节点(Node)。
标志组OS_FLAG_GRP的成员OSFlagWaitList就指向了信号量集的这个等待任务链表。
等待任务链表节点OS_FLAG_NODE的结构如下:
typedefstruct
void*OSFlagNodeNext;
//指向下一个节点的指针
void*OSFlagNodePrev;
//指向前一个节点的指针
void*OSFlagNodeTCB;
//指向对应任务控制块的指针
void*OSFlagNodeFlagGrp;
//反向指向信号量集的指针
OS_FLAGSOSFlagNodeFlags;
//信号过滤器
INT8UOSFlagNodeWaitType;
//定义逻辑运算关系的数据
}OS_FLAG_NODE;
其中OSFlagNodeWaitType是定义逻辑运算关系的一个常数(根据需要设置),其可选值和对应的逻辑关系如表所示:
OSFlagFlags、OSFlagNodeFlags、OSFlagNodeWaitType三者的关系如图所示:
图中将OSFlagFlags定义为8位,但是UCOSII支持8位/16位/32位定义,这个通过修改OS_FLAGS的类型来确定(UCOSII默认设置OS_FLAGS为16位)。
上图清楚的表达了信号量集各成员的关系:
OSFlagFlags为信号量表,通过发送信号量集的任务设置;
OSFlagNodeFlags为信号滤波器,由请求信号量集的任务设置,用于选择性的挑选OSFlagFlags中的部分(或全部)位作为有效信号;
OSFlagNodeWaitType定义有效信号的逻辑运算关系,也是由请求信号量集的任务设置,用于选择有效信号的组合方式。
在UCOSII中,与信号量集相关的几个函数。
创建信号量集函数
任务可以通过调用函数OSFlagCreate来创建一个信号量集。
函数OSFlagCreate的原型为:
OS_FLAG_GRP*OSFlagCreate(OS_FLAGSflags,INT8U*err)。
其中,flags为信号量的初始值(即OSFlagFlags的值),err为错误信息,返回值为该信号量集的标志组的指针,应用程序根据这个指针对信号量集进行相应的操作。
请求信号量集函数
任务可以通过调用函数OSFlagPend请求一个信号量集,函数OSFlagPend的原型为:
OS_FLAGSOSFlagPend(OS_FLAG_GRP*pgrp,OS_FLAGSflags,INT8Uwait_type,INT16Utimeout,INT8U*err)。
其中,pgrp为所请求的信号量集指针,flags为滤波器(即OSFlagNodeFlags的值),wait_type为逻辑运算类型(即OSFlagNodeWaitType的值),timeout为等待时限,err为错误信息。
向信号量集发送信号函数
任务可以通过调用函数OSFlagPost向信号量集发信号,函数OSFlagPost的原型为:
OS_FLAGSOSFlagPost(OS_FLAG_GRP*pgrp,OS_FLAGSflags,INT8Uopt,INT8U*err)。
其中,pgrp为所请求的信号量集指针,flags为选择所要发送的信号,opt为信号有效选项,err为错误信息。
所谓任务向信号量集发信号,就是对信号量集标志组中的信号进行置“1”(置位)或置“0”(复位)的操作。
至于对信号量集中的哪些信号进行操作,用函数中的参数flags来指定;
对指定的信号是置“1”还是置“0”,用函数中的参数opt来指定(opt=OS_FLAG_SET为置“1”操作;
opt=OS_FLAG_CLR为置“0”操作)。
3)软件定时器
软件定时器使UCOSII的功能更加完善,在其上的应用程序开发与移植也更加方便。
在实时操作系统中一个好的软件定时器实现要求有较高的精度、较小的处理器开销,且占用较少的存储器资源。
通过前面的实验,知道UCOSII通过OSTimTick函数对时钟节拍进行加1操作,同时遍历任务控制块,以判断任务延时是否到时。
软件定时器同样由OSTimTick提供时钟,但
是软件定时器的时钟还受OS_TMR_CFG_TICKS_PER_SEC设置的控制,也就是在UCOSII的时钟节拍上面再做了一次“分频”,软件定时器的最快时钟节拍就等于UCOSII的系统时钟
节拍。
这也决定了软件定时器的精度。
软件定时器定义了一个单独的计数器OSTmrTime,
用于软件定时器的计时,UCOSII并不在OSTimTick中进行软件定时器的到时判断与处理,
而是创建了一个高于应用程序中所有其他任务优先级的定时器管理任务OSTmr_Task,在这个任务中进行定时器的到时判断和处理。
时钟节拍函数通过信号量给这个高优先级任务发信号。
这种方法缩短了中断服务程序的执行时间,但也使得定时器到时处理函数的响应受到中断退出时恢复现场和任务切换的影响。
软件定时器功能实现代码存放在tmr.c文件中,移植时需只需在os_cfg.h文件中使能定时器和设定定时器的相关参数。
UCOSII中软件定时器的实现方法是,将定时器按定时时间分组,使得每次时钟节拍到来时只对部分定时器进行比较操作,缩短了每次处理的时间。
但这就需要动态地维护一个定时器组。
定时器组的维护只是在每次定时器到时时才发生,而且定时器从组中移除和再插入操作不需要排序。
这是一种比较高效的算法,减少了维护所需的操作时间。
UCOSII软件定时器实现了3类链表的维护:
OS_EXTOS_TMROSTmrTbl[OS_TMR_CFG_MAX];
//定时器控制块数组
OS_EXTOS_TMR*OSTmrFreeList;
//空闲定时器控制块链表指针
其中OS_TMR为定时器控制块,定时器控制块是软件定时器管理的基本单元,包含软
件定时器的名称、定时时间、在链表中的位置、使用状态、使用方式,以及到时回调函数及其参数等基本信息。
OSTmrTbl[OS_TMR_CFG_MAX];
:
以数组的形式静态分配定时器控制块所需的RAM空间,并存储所有已建立的定时器控制块,OS_TMR_CFG_MAX为最大软件定时器的个数。
OSTmrFreeLiSt:
为空闲定时器控制块链表头指针。
空闲态的定时器控制块(OS_TMR)中,OSTmrnext和OSTmrPrev两个指针分别指向空闲控制块的前一个和后一个,组织了空闲控制块双向链表。
建立定时器时,从这个链表中搜索空闲定时器控制块。
OSTmrWheelTbl[OS_TMR_CFG_WHEEL_SIZE]:
该数组的每个元素都是已开启定时器的一个分组,元素中记录了指向该分组中第一个定时器控制块的指针,以及定时器控制块的个数。
运行态的定时器控制块(OS_TMR)中,OSTmrnext和OSTmrPrev两个指针同样也组织了所在分组中定时器控制块的双向链表。
软件定时器管理所需的数据结构示意图如
图所示:
OS_EXTOS_TMR_WHEELOSTmrWheelTbl[OS_TMR_CFG_WHEEL_SIZE];
OS_TMR_CFG_WHEEL_SIZE定义了OSTmrWheelTbl的大小,同时这个值也是定时器分组的依据。
按照定时器到时值与OS_TMR_CFG_WHEEL_SIZE相除的余数进行分组:
不同余数的定时器放在不同分组中;
相同余数的定时器处在同一组中,由双向链表连接。
这样,余数值为0~OS_TMR_CFG_WHEEL_SIZE-1的不同定时器控制块,正好分别对应了数组元素OSTmr-WheelTbl[0]~OSTmrWheelTbl[OS_TMR_CFGWHEEL_SIZE-1]的不同分组。
每次时钟节拍到来时,时钟数OSTmrTime值加1,然后也进行求余操作,只有余数相同的那组定时器才有可能到时,所以只对该组定时器进行判断。
这种方法比循环判断所有定时器更高效。
随着时钟数的累加,处理的分组也由0~OS_TMR_CFG_WHEEL_SIZE-1循环。
这里,推荐OS_TMR_CFG_WHEEL_SIZE的取值为2的N次方,以便采用移位操作计算余数,缩短处理时间。
信号量唤醒定时器管理任务,计算出当前所要处理的分组后,程序遍历该分组中的所有控制块,将当前OSTmrTime值与定时器控制块中的到时值(OSTmrMatch)相比较。
若相等(即到时),则调用该定时器到时回调函数;
若不相等,则判断该组中下一个定时器控制块。
如此操作,直到该分组链表的结尾。
当运行完软件定时器的到时处理函数之后,需要进行该定时器控制块在链表中的移除和再插入操作。
插入前需要重新计算定时器下次到时时所处的分组。
计算公式如下:
定时器下次到时的OSTmrTime值(OSTmrMatch)=定时器定时值+当前OSTmrTime值
新分组=定时器下次到时的OSTmrTime(OSTmrMatch)%OS_TMR_CFG_WHEEL_SIZE
接下来我们看看在UCOSII中,与软件定时器相关的几个函数。
创建软件定时器函数
创建软件定时器通过函数OSTmrCreate实现,该函数原型为:
OS_TMR*OSTmrCreate(INT32Udly,
INT32Uperiod,
INT8Uopt,
OS_TMR_CALLBACKcallback,
void*callback_arg,
INT8U*pname,INT8U*perr)
其中dly,用于初始化定时时间,对单次定时(ONE-SHOT模式)的软件定时器来说,这就是该定时器的定时时间,而对于周期定时(PERIODIC模式)的软件定时器来说,这是该定时器第一次定时的时间,从第二次开始定时时间变为period。
period,在周期定时(PERIODIC模式),该值为软件定时器的周期溢出时间。
opt,用于设置软件定时器工作模式。
可以设置的值为:
OS_TMR_OPT_ONE_SHOT或OS_TMR_OPT_PERIODIC,如果设置为前者,说明是一个单次定时器;
设置为后者则表示是周期定时器。
callback,为软件定时器的回调函数,当软件定时器的定时时间到达时,会调用该函数。
callback_arg,回调函数的参数。
pname,为软件定时器的名字。
perr,为错误信息。
软件定时器的回调函数有固定的格式,我们必须按照这个格式编写,软件定时器的回调函数格式为:
void(*OS_TMR_CALLBACK)(void*ptmr,void*parg)。
其中,函数名可随意设置,而ptmr这个参数,软件定时器用来传递当前定时器的控制块指针,
所以一般设置其类型为OS_TMR*类型,第二个参数(parg)为回调函数的参数,这个就可以根据自己需要设置了,你也可以不用,但是必须有这个参数。
开启软件定时器函数
任务可以通过调用函数OSTmrStart开启某个软件定时器,该函数的原型为:
BOOLEANOSTmrStart(OS_TMR*ptmr,INT8U*perr)。
其中ptmr为要开启的软件定时器指针,perr为错误信息。
停止软件定时器函数
任务可以通过调用函数OSTmrStop停止某个软件定时器,该函数的原型为:
BOOLEANOSTmrStop(OS_TMR*ptmr,INT8Uopt,void*callback_arg,INT8U*perr)。
其中ptmr为要停止的软件定时器指针。
opt为停止选项,可以设置的值及其对应的意义为:
OS_TMR_OPT_NONE,直接停止,不做任何其他处理
OS_TMR_OPT_CALLBACK,停止,用初始化的参数执行一次回调函数
OS_TMR_OPT_CALLBACK_ARG,停止,用新的参数执行一次回调函数
callback_arg,新的回调函数参数。
perr,错误信息。
三、实验内容
1)硬件设计
在UCOSII里面创建7个任务:
开始任务、LED任务、触摸屏任务、队列消息显示任务、信号量集任务、按键扫描任务和主任务,开始任务用于创建邮箱、消息队列、信号量集以及其他任务,之后挂起;
触摸屏任务用于在屏幕上画图,测试CPU使用率;
队列消息显示任务请求消息队列,在得到消息后显示收到的消息数据;
信号量集任务用于测试信号量集,采用OS_FLAG_WAIT_SET_ANY的方法,任何按键按下(包括TPAD),该任务都会控制蜂鸣器发出“滴”的一声;
按键扫描任务用于按键扫描,优先级最高,将得到的键值通过消息邮箱发送出去;
主任务创建3个软件定时器(定时器1,100ms溢出一次,显示CPU和内存使用率;
定时2,200ms溢出一次,在固定区域不停的显示不同颜色;
定时3,100ms溢出一次,用于自动发送消息到消息队列),并通过查询消息邮箱获得键值,根据键值执行DS1控制、控制软件定时器3的开关、触摸区域清屏、触摸屏校和软件定时器2的开关控制等。
所要用到的硬件资源如下:
指示灯DS0、DS1
4个机械按键(KEY0/KEY1/KEY2/WK_UP)
TPAD触摸按键
蜂鸣器
TFTLCD模块
2)软件参数设置
本章OS_TICKS_PER_SEC的设置还是为500,即UCOSII的时钟节拍为2ms。
另外由于创建了7个任务,加上统计任务、空闲任务和软件定时器任务,总共10个任务,如果想添加其他任务,请把OS_MAX_TASKS的值适当改大。
另外,我们还需要在os_cfg.h里面修改软件定时器管理部分的宏定义,修改如下:
#defineOS_TMR_EN1u//使能软件定时器功能
#defineOS_TMR_CFG_MAX16u//最大软件定时器个数
#defineOS_TMR_CFG_NAME_EN1u//使能软件定时器命名
#defineOS_TMR_CFG_WHEEL_SIZE8u//软件定时器轮大小
#defineOS_TMR_CFG_TICKS_PER_SEC100u//软件定时器的时钟节拍(10ms)
#defineOS_TASK_TMR_PRIO0u//软件定时器的优先级,设置为最高
这样就使能UCOSII的软件定时器功能了,并且设置最大软件定时器个数为16,定时器轮大小为8,软件定时器时钟节拍为10ms(即定时器的最少溢出时间为10ms)
打开前面实验做的模板,在主程序中输入下列程序:
#include"
sys.h"
usart.h"
delay.h"
led.h"
beep.h"
key.h"
exti.h"
wdg.h"
timer.h"
tpad.h"
oled.h"
lcd.h"
usmart.h"
rtc.h"
wkup.h"
adc.h"
dac.h"
dma.h"
24cxx.h"
flash.h"
can.h"
touch.h"
remote.h"
joypad.h"
adxl345.h"
ds18b20.h"
dht11.h"
24l01.h"
mouse.h"
stmflash.h"
rda5820.h"
audiosel.h"
ov7670.h"
sram.h"
malloc.h"
string.h"
includes.h"
rs485.h"
/////////////////////////////////UCOSII任务设置///////////////////////////////////////
//START任务
//设置任务优先级
#defineSTART_TASK_PRIO10
//开始任务的优先级设置为最低
//设置任务堆栈大小
#defineSTART_STK_SIZE64
//任务堆栈
OS_STKSTART_TASK_STK[START_STK_SIZE];
//任务函数
voidstart_task(void*pdata);
//LED任务
#defineLED_TASK_PRIO7
#defineLED_STK_SIZE64
OS_STKLED_TASK_STK[LED_STK_SIZE];
//触摸屏任务
#defineTOUCH_TASK_PRIO6
#defineTOUCH_STK_SIZE64
OS_STKTOUCH_TASK_STK[TOUCH_STK_SIZE];
voidtouch_task(void*pdata);
//队列消息显示任务
#defineQMSGSHOW_TASK_PRIO5
#defineQMSGSHOW_STK_SIZE64
OS_STKQMSGSHOW_TASK_STK[QMSGSHOW_STK_SIZE];
voidqmsgshow_task(void*pdata);
//主任务
voidled_task(void*pdata);
#defineMAIN_TASK_PRIO4
#defineMAIN_STK_SIZE128
OS_STKMAIN_TASK_STK[MAIN_STK_SIZE];
voidmain_task(void*pdata);
//信号量集任务
#defineFLAGS_TASK_PRIO3
#defineFLAGS_STK_SIZE64
OS_STKFLAGS_TA
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 实验四 UCOSII消息队信号量和软件定时器 实验 UCOSII 消息 信号量 软件 定时器