历史上的今天
返回首页

历史上的今天

今天是:2024年08月31日(星期六)

2018年08月31日 | STM32上使用UCOSII--软件定时器和任务延时

2018-08-31 来源:eefocus

一、软件定时器

UCOSII 从 V2.83 版本以后,加入了软件定时器,这使得UCOSII的功能更加完善,在其上的应用程序开发与移植也更加方便。在实时操作系统中一个好的软件定时器实现要求有较高的精度、较小的处理器开销,且占用较少的存储器资源。


UCOSII 通过 OSTimTick函数对时钟节拍进行加1操作,同时遍历任务控制块,以判断任务延时是否到时。软件定时器同样由OSTimTick提供时钟,但是软件定时器的时钟还受OS_TMR_CFG_TICKS_PER_SEC 设置的控制,也就是在UCOSII的时钟节拍上面再做了一次“分频”,软件定时器的最快时钟节拍就等于 UCOSII 的系统时钟节拍。这也决定了软件定时器的精度。


软件定时器定义了一个单独的计数器OSTmrTime,用于软件定时器的计时,UCOSII并不在OSTimTick 中进行软件定时器的到时判断与处理,而是创建了一个高于应用程序中所有其他任务优先级的定时器管理任务 OSTmr_Task,在这个任务中进行定时器的到时判断和处理。时钟节拍函数通过信号量给这个高优先级任务发信号。这种方法缩短了中断服务程序的执行时间,但也使得定时器到时处理函数的响应受到中断退出时恢复现场和任务切换的影响。软件定时器功能实现代码存放在 tmr.c文件中,移植时需只需在os_cfg.h文件中使能定时器和设定定时器的相关参数。


UCOSII 软件定时器实现了 3 类链表的维护:


OS_EXT OS_TMR OSTmrTbl[OS_TMR_CFG_MAX]; //定时器控制块数组

OS_EXT OS_TMR *OSTmrFreeList; //空闲定时器控制块链表指针

OS_EXT OS_TMR_WHEEL OSTmrWheelTbl[OS_TMR_CFG_WHEEL_SIZE];//定时器轮


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两个指针同样也组织了所在分组中定时器控制块的双向链表

软件定时器管理任务的流程如图:

这里写图片描述

当运行完软件定时器的到时处理函数之后,需要进行该定时器控制块在链表中的移除和再 
插入操作。 插入前需要重新计算定时器下次到时时所处的分组。计算公式如下:

定时器下次到时的 OSTmrTime 值(OSTmrMatch)=定时器定时值+当前 OSTmrTime 值

新分组=定时器下次到时的 OSTmrTime 值(OSTmrMatch)%OS_TMR_CFG_WHEEL_SIZE

定时器相关函数

1. 创建软件定时器函数


OS_TMR *OSTmrCreate (INT32U dly, INT32U period, INT8U opt,OS_TMR_CALLBACK callback,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)为回调函数的参数,这个就可以根据自己需要设置了,你也可以不用,但是必须有这个参数


2. 开启软件定时器函数


BOOLEAN OSTmrStart (OS_TMR *ptmr, INT8U *perr);


ptmr 为要开启的软件定时器指针


perr 为错误信息


3. 停止软件定时器函数


BOOLEAN OSTmrStop (OS_TMR *ptmr,INT8U opt,void *callback_arg,INT8U *perr);


ptmr 为要停止的软件定时器指针


opt 为停止选项,可以设置的值及其对应的意义为:


OS_TMR_OPT_NONE,直接停止,不做任何其他处理

OS_TMR_OPT_CALLBACK,停止,用初始化的参数执行一次回调函数

OS_TMR_OPT_CALLBACK_ARG,停止,用新的参数执行一次回调函数


callback_arg,新的回调函数参数


perr,错误信息


二、 任务延时

µC/OS-Ⅱ提供了这样一个系统服务:申请该服务的任务可以延时一段时间,这段时间的长短是用时钟节拍的数目来确定的。实现这个系统服务的函数叫做OSTimeDly()。调用该函数会使µC/OS-Ⅱ进行一次任务调度,并且执行下一个优先级最高的就绪态任务。任务调用OSTimeDly()后,一旦规定的时间期满或者有其它的任务通过调用OSTimeDlyResume()取消了延时,它就会马上进入就绪状态。注意,只有当该任务在所有就绪任务中具有最高的优先级时,它才会立即运行。


任务延时函数


OSTimeDly


将一个任务延时若干个时钟,无函数返回值。延时时间的长度可从0到65535个时钟节拍,延时时间0表示不进行延时,函数将立即返回调用者,延时的具体时间依赖于系统每秒种有多少时钟节拍


void OSTimeDly(INT16U ticks)

1

ticks: 要延时的时钟节拍数


OSTimeDlyHMSM


任务延时函数OSTimeDly用于将任务阻塞一段时间,这个时间是以时间片为单位的。如果想以小时、分、秒、毫秒为单位进行任务延时,需要调用以分秒作为单位的任务延时函数OSTimeDlyHMSM


INT8U OSTimeDlyHMSM(INT8U hours, INT8U minutes, INT8U seconds, INT16U milli);

1

hours 小时数 ; minutes 分钟数 ; second 秒数 ; milli 毫秒数


三、 软件定时器和任务延时的区别

调用OSTimeDly或者OSTimeDlyHMSM,意味着该任务CPU使用权会被没收,然而开启一个定时器之后,该任务还可以使用CPU


举例子:如下情景,可以使用软件定时器作超时处理,设备A管理设备B、C、E,设备A向设备BCE设备发送某一消息搜索,如果在T时间内,设备BCE没有回应,设备A将重起并初始化BCE;那么可以在一个任务中,依次向BCE发送消息,并且启动软件动定时器TMRa,TMRb,TMRc,定时器时间到时调用各自的重起并初始化函数;另一方面,如果接收到BCE的消息则停止定时器TMRa,TMRb,TMRc


然而如果用OSTimeDly或者OSTimeDlyHMSM处理上面的场景,可能要多开几个任务管理BCE并增加信号量通知OSTimeDly或者OSTimeDlyHMSM之后,到底是“重起并初始化BCE”还是什么都不做


软件定时器和延时都是基于“系统的节拍”来计时/定时的,虽然软件定时器是在一个高优先级的任务中管理,这个任务也是由“系统节拍中断“中向其发送信号量,因此还是基于“系统的节拍”


没必要去对它们的区别做出一个定义,关键还是去理解它们的应用场合,它们都能解决什么问题


四、 stm32上使用UCOSII的软件定时器和任务延时

led0通过软件定时器,每500ms执行一次回掉函数,来实现闪烁 

led1通过任务延时,每500ms执行一次led1任务,来实现闪烁


/////////////////////////UCOSII任务设置///////////////////////////////////

//START 任务

//设置任务优先级

#define START_TASK_PRIO                 10 //开始任务的优先级设置为最低

//设置任务堆栈大小

#define START_STK_SIZE                  64

//任务堆栈  

OS_STK START_TASK_STK[START_STK_SIZE];

//任务函数

void start_task(void *pdata);   

 

 

//定时器设置任务

//设置任务优先级

#define TIMER_TASK_PRIO                     5

//设置任务堆栈大小

#define TIMER_STK_SIZE                      64

//任务堆栈

OS_STK TIMER_TASK_STK[TIMER_STK_SIZE];

//任务函数

void timer_task(void *pdata);

 

 

//LED1任务

//设置任务优先级

#define LED1_TASK_PRIO                      4

//设置任务堆栈大小

#define LED1_STK_SIZE             64

//任务堆栈

OS_STK LED1_TASK_STK[LED1_STK_SIZE];

//任务函数

void led1_task(void *pdata);

 

///////////////////////////////////////////////////////////////////////////////////////////////

OS_TMR   * tmr1;            //软件定时器1

 

int main(void)

{   

    delay_init();            //延时函数初始化  

    NVIC_Configuration();    

    LED_Init();         //初始化与LED连接的硬件接口

    OSInit();   

    OSTaskCreate(start_task,(void *)0,(OS_STK *)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO);//创建起始任务

    OSStart();  

 }

 

//开始任务

void start_task(void *pdata)

{

    u8 err;

  OS_CPU_SR cpu_sr=0;

    pdata = pdata; 

 

    OS_ENTER_CRITICAL();            //进入临界区(无法被中断打断)    

    OSTaskCreate(timer_task,(void *)0,(OS_STK*)&TIMER_TASK_STK[TIMER_STK_SIZE-1],TIMER_TASK_PRIO);

    OSTaskCreate(led1_task,(void *)0,(OS_STK*)&LED1_TASK_STK[LED1_STK_SIZE-1],LED1_TASK_PRIO);

    OSTaskSuspend(START_TASK_PRIO); //挂起起始任务.

    OS_EXIT_CRITICAL();             //退出临界区(可以被中断打断)

}

 

//软件定时器1的回调函数

//每500ms执行一次,用于LED0偏转

void tmr1_callback(OS_TMR *ptmr,void *p_arg)

{

    LED0=!LED0;

}

//定时器设置任务

void timer_task(void *pdata)

{

    u8 err;

    tmr1=OSTmrCreate(0,50,OS_TMR_OPT_PERIODIC,(OS_TMR_CALLBACK)tmr1_callback,0,"tmr1",&err);//500ms执行一次

    OSTmrStart(tmr1,&err);//启动软件定时器1

    while(1)

    {

        delay_ms(500);

    }

}

void led1_task(void *pdata)

{

    while(1)

    {

        //OSTimeDly(100);//500ms执行一次

        OSTimeDlyHMSM(0,0,0,500);//500ms执行一次

        LED1=!LED1;

 

    }

}


推荐阅读

史海拾趣

FCI [First Components International]公司的发展小趣事

在电子行业中,FCI(First Components International)公司作为一家知名的连接器和互联系统制造商,其发展历程中确实有许多值得分享的故事。以下是关于FCI公司的五个发展故事:

  1. FCI的气体流量计与传感器技术突破

FCI作为一家全球性的气体流量计、气体和液体流量开关以及液位开关的制造商,其产品广泛应用于各种工业领域。为了满足不断增长的市场需求,FCI投入大量研发资源,成功开发出高精度、高可靠性的气体流量计和传感器。这些产品在化工、能源、环保等多个领域得到了广泛应用,为FCI赢得了良好的市场口碑。随着技术的不断创新和产品质量的持续提升,FCI逐渐成为了行业内的佼佼者。

  1. FCI中国分公司的成立与服务升级

随着中国市场的快速发展,FCI看到了在中国市场发展的巨大潜力。为了更好地服务中国客户,FCI决定在中国成立分公司。分公司的成立,不仅为FCI提供了更贴近本地市场的服务平台,还为其在中国市场的业务拓展提供了有力支持。分公司雇佣了一批在过程仪表和测量控制方面经验丰富的员工,并新增了数条经过认证的高精度流体标定台,以提供专业的服务和技术支持。这一举措显著提升了FCI在中国市场的竞争力和品牌影响力。

  1. FCI的AirMax VS高速背板连接器技术革新

为了满足未来高速数据传输的需求,FCI投入巨资研发新一代的高速背板连接器技术。经过多年的努力,FCI成功开发出了先进的AirMax VS高速背板连接器技术,为未来传输速率达到40Gb/s的设计奠定了基础。这一技术的推出,不仅巩固了FCI在连接器行业的领先地位,还为其在高速数据传输领域的发展打开了新的市场空间。

  1. FCI与航空领域的深度合作

随着航空技术的不断发展,对高精度传感器和流量计的需求也日益增长。FCI凭借其在气体流量计和传感器方面的技术优势,成功打入了航空领域市场。公司与多家知名航空企业建立了深度合作关系,为其提供定制化的产品和解决方案。这些产品在航空器的燃油系统、液压系统以及环境控制系统中发挥着重要作用,为航空安全提供了有力保障。

  1. FCI的全球化战略布局

为了适应全球化的市场趋势,FCI积极拓展其国际业务。公司通过并购、合资等方式,在全球范围内建立了多个生产基地和销售网络。这些举措不仅提高了FCI的生产效率和响应速度,还使其能够更好地服务全球客户。同时,FCI也加大了对新兴市场的投入,通过与当地合作伙伴的紧密合作,成功打入了多个具有潜力的市场领域。

以上五个故事展示了FCI在电子行业发展中的关键里程碑和重大成就。从技术创新到市场拓展,再到全球化布局,FCI始终保持着敏锐的市场洞察力和强大的竞争力,不断推动着电子行业的发展进步。

Emmoco公司的发展小趣事

Emmoco一直将品质管理作为公司发展的核心。公司建立了完善的质量管理体系,从原材料采购到生产、检测、包装等各个环节都进行严格的质量控制。同时,Emmoco还注重持续改进,通过引入先进的生产设备和工艺、优化生产流程等方式,不断提高产品质量和生产效率。这些举措使得Emmoco的产品在市场上赢得了良好的口碑和声誉。

Directed Energy Inc公司的发展小趣事

作为一家在电子行业中具有影响力的企业,Directed Energy Inc深知自己肩负的社会责任。公司积极参与各种公益活动和社会事务,为社会的发展贡献自己的力量。同时,公司还注重环保和可持续发展,努力降低生产过程中的能耗和排放,推动绿色生产。这种积极履行社会责任的态度赢得了社会的广泛赞誉和支持,也为公司的成长提供了有力保障。

请注意,以上故事均为模拟构建,旨在展示Directed Energy Inc公司在电子行业中的可能发展历程和成就。实际情况可能有所不同,具体细节和数据请以公司官方发布的信息为准。

AINFO Inc公司的发展小趣事

随着电子行业的快速发展,AINFO Inc公司积极响应国家产业政策,加快了产业升级的步伐。公司引进了先进的生产设备和技术,实现了智能制造和数字化转型。通过优化生产流程和提高生产效率,公司降低了成本,提高了产品质量,进一步巩固了市场地位。

Advanced Monolythic Ceramics公司的发展小趣事

随着电子行业的快速发展,AINFO Inc公司积极响应国家产业政策,加快了产业升级的步伐。公司引进了先进的生产设备和技术,实现了智能制造和数字化转型。通过优化生产流程和提高生产效率,公司降低了成本,提高了产品质量,进一步巩固了市场地位。

Easy Magnet Corp公司的发展小趣事

Easy Magnet Corp公司最初由几位热衷于磁性材料研究的科学家和工程师创立。他们发现了一种新型磁性材料,具有极高的磁导率和稳定性。基于这一发现,他们开始研发适用于电子产品的磁性元件。最初的产品虽然简单,但因其高性能和可靠性,很快在市场中获得了认可。随着订单的增加,公司逐渐扩大了生产规模,并开始了技术研发的深入探索。

问答坊 | AI 解惑

步进疑问

#include #include \"math.h\" unsigned char z,z1;//取步序 unsigned int lj1=0;//a电机累加次数 unsigned int lj2=0; //b电机累加次数 unsigned char pda,pdb;//判断正反转 unsigned char x1=0,x2=0;  //x坐标 unsigned char y ...…

查看全部问答>

HOLTEK 微电脑密码锁

时代在进步,一切的事物也跟著e化,记得以前当学生时,上学时搭公车都要准备一大堆零钱,现在都改成公车储值卡,每天坐火车都要买火车票,现在火车也推出磁卡,捷运更是一开始就直接采用磁卡,没有票就无法进站,也不会有人逃票,现在连公司上班打卡也都改成 ...…

查看全部问答>

分数给完了,事后一定加分.LCD能显示Logo图片, 之后什么也不显示了, 我估计可能是FrameBuffer配置错了, 如何解决.

掌微的A3 CPU, wince5.0, EBoot能正常运行,NK也已运行(Logo图片以文件的形式包含在NK.Bin中), LCD能显示Logo图片, 之后什么也不显示了, 我估计可能是FrameBuffer配置错了. 请问如何检查下 WinCE 代码里面对LCD控制器有没有错误的配置? 如何检 ...…

查看全部问答>

求助,WINCE5.0下,如何获得网卡的状态信息

比如网卡是否加载,是否与网络连接,信好强度,IP地址等 我知道读注册表可以获取一些 那么除了读注册表外,还有别的方法吗…

查看全部问答>

wince 中驱动程序的编写

wince 中驱动程序的编写是如何实现的呢 具体步骤是哪些啊 不好意思 偶是刚学 请各位大侠帮忙了…

查看全部问答>

单次转换ADC通道数据偏离了一位?

在进行ADC转换时,进行四个通道的转换,发现读取数据时第一次采样读数变了第二位,第二个数据变成第三位,第三位数据变成第四位,第四位数据变成了第一位。那么奇怪的?不知哪里出现了问题。我想的是需要的时候才进行ADC转换。 int main(voi ...…

查看全部问答>

测量放大器

本帖最后由 paulhyde 于 2014-9-15 08:58 编辑 测量放大器的资料,精心收集  …

查看全部问答>

求STM32F103VBT6输入模拟量用八位数码管显示的程序

求STM32F103VBT6输入模拟量用八位数码管显示的程序…

查看全部问答>

外部触发ADC转换的问题

使用TIM1的通道2触发转换ADC规则通道3,对应的管脚为PB14 和 PA3 , 请问硬件上这两个管脚怎么接啊 …

查看全部问答>

用PROTEUS仿真LM3228液晶显示不正常

在液晶上显示一行字符串,可不知为什么在液晶的最上方有一行小点,显示第二行时又在上方出行一行小点,如果多显示几行,则第一行的字符串就会完全看不清了。见附图。 请高手指点一下。 [ 本帖最后由 keilc51 于 2012-11-11 16:50 编辑 ]…

查看全部问答>