5-软件定时器

UCOSIII 软件定时器

 

  1. 软件定时器原理
  2. 软件定时器相关函数汇总
  3. 总结函数的参数和用法及注意事项
  4. 举个栗子

 

 

在不带操作系统的时候,如果想要定时一般会使用芯片上的硬件定时器,但是在UCOS中,我们可以使用软件定时器,而把硬件定时器留作他用,而且UCOS的软件定时器是没有数量限制的,当然前提是内存够大才行。虽然软件定时器不占用硬件定时器的资源,但是他也有个缺点,就是精度不高,因为软件定时器的频率来源于心跳频率的分频,所以频率不会大于心跳频率。所以如果精度要求特别高的话还是用硬件定时器吧,在一般时间精度要求不高的场合用软件定时器还是挺方便的。

软件定时器有个使能开关,如果要使用软降定时器的话得打开这个开关OS_CFG_TMR_EN。

软件定时器应该可以算是一个任务,所以他也有优先级,这个优先级是可以在更改的,在os_cfg_app.h文件中,更改OS_CFG_TMR_TASK_PRIO就可以重置软件定时器的优先级。

软件定时器的频率设置在os_cfg_app.h文件中,更改OS_CFG_TMR_TASK_RATE_HZ就可以设置软件定时器的频率。这个频率不能大于心跳频率,因为软件定时器时钟是又系统时钟分频而来,为了准确的分频,软件定时器的频率和心跳频率最好成倍数关系,这个频率最好不要设得太高,至于为什么我也不知道,可能会导致系统负担加重什么的吧。

软件定时器其实是一个到计数器OSTmrTickCtr,当软件定时器的脉冲到来时,OSTmrTickCtr就会自减,当OSTmrTickCtr减到0时就会触发回调函数,回调函数可以理解为中断处理函数吧。因为在执行回调函数时任务调度器是上锁的,所以回调函数应该简短,就像中断处理函数一样,千万不要在回调函数中调用会导致阻塞的函数,不然程序会卡在回调函数中很久。

软件定时器创建后要手动启动,不然是不会运行的。

 

软甲定时器有4中状态,可以说是一个简单的状态机吧。分别是

0;未使用,可以理解为未创建,又或者是被删除了

1;暂停,创建了未启动或者是中途暂停了

2;运行中

3;运行完毕后停止,这个应该是针对单次定时器的,周期定时器应该不存在停止的说法

 

软件定时器有两种模式单次模式和周期模式(包括有初始化延时周期和无初始化延时周期)。单次模式就是只运行一次就停止了,回调函数只会运行一次;而周期模式则是可以重复运行的定时器,回调函数也会周期性的执行。

单次定时器,在创建软件定时器的时候给定时器一个初始值dly,也就是OSTmrTickCtr的初值,当启动定时器的时候每来一个定时器时钟OSTmrTickCtr就会减1,当OSTmrTickCtr减到0的时候就会触发回调函数,回调函数执行完成后这个定时器就没什么用了。如果想要再次使用这个定时器的话直接启动就行,不需要重新创建一个定时器。但是如果在定时器运行过程中重新启动定时器,那么定时器会重新开始计数而不是继续往下计数。打个比方,我要定时100个定时器节拍,把OSTmrTickCtr初始化为100,在OSTmrTickCtr减到70的时候重新启动定时器,那么OSTmrTickCtr由会从100开始倒计数,而不会从70往下接着计数,当计数到0的时候实际上已经计数了130个定时器节拍。但是如果先暂停定时器然后再启动定时器的话,那么计数器会从原来的位置接着往下计数。在单次定时器模式下周期值period是没有用的,period组好设置为0吧。

周期定时器,周期定时器可以用硬件定时器的思维来理解,比如51单片机吧,在定时器初始化的时候会设置一个初始化计数变量,然后在中断中重装载计数值。UCOS中的软件定时器周期模式也是一样,在创建定时器的时候要设置一个初始值dly,这是第一次定时的时间,从第二次开始定时的时间就是周期值period定时的时间了。打个比方,dly=10,period=50,那么启动定时器的时候OSTmrTickCtr会从10开始倒计数,当OSTmrTickCtr倒计数到0的时候会第一次触发回调函数,然后给OSTmrTickCtr重置为50,从50计数到0的时候会第二次触发回调函数,然后OSTmrTickCtr又重置为50................也就是说只有第一次定时是10个定时器节拍的,从第二次开始之后的每一次都是定时器50个定时器节拍。这里有个需要注意的地方,在周期定时器模式下当dly初始化为0时会马上触发回调函数,因为第一次定时的时间为0.如果在定时器运行过程中启动定时器的话,定时器的计数值也是会重置的,当启动已经暂停了的定时器的话,定时器会接着原来位置继续运行,而不会重置计数值。

 

与软件定时器相关的函数不多,就那么几个

1、OSTmrCreate()

2、OSTmrDel()

3、OSTmrRemainGet()

4、OSTmrStart()

5、OSTmrStateGet()

6、OSTmrStop()

 

各个函数用法及参数解释

  1. OSTmrCreate(),定时器创建函数

函数原型

void  OSTmrCreate (OS_TMR               *p_tmr,

                   CPU_CHAR             *p_name,

                   OS_TICK               dly,

                   OS_TICK               period,

                   OS_OPT                opt,

                   OS_TMR_CALLBACK_PTR   p_callback,

                   void                 *p_callback_arg,

                   OS_ERR               *p_err)

使能开关

OS_CFG_TMR_EN

各个入口参数说明

OS_TMR               *p_tmr,

 定时器控制块,用法功能有点像任务控制块

CPU_CHAR             *p_name,

可以给定时器一个名字,一般用不到,只有用上位机调试的时候才会用到

 OS_TICK               dly,

定时器的初始值,单次模式下就算定时的定时器节拍数,周期模式下是第一次定时的定时器节拍数

OS_TICK               period,

定时器周期值,单次模式这个值没用,一般设置为0.周期模式下代表定时器的重装载值,也就是定时周期。

OS_OPT                opt,

定时器选项,这两个选项的区别上面已经说了

OS_OPT_TMR_ONE_SHOT 单次模式

OS_OPT_TMR_PERIODIC 周期模式

OS_TMR_CALLBACK_PTR   p_callback,

定时器的回到函数,回调函数是有格式要求的,统一格式如下

Void MyCallBack(OS_TMR * p_tmr,  void * p_arg)

实测不给任何入口参数也是可以用的

void                 *p_callback_arg,

可以给回调函数一个入口参数,

OS_ERR               *p_err

函数返回的错误码

 

 

  1. OSTmrDel()

删除定时器,这个删除并不是删除定时器的代码,而是让这个定时器不受UCOS的管理,减少UCOS的负担,删除定时器后该定时器就不能使用了。

函数原型

CPU_BOOLEAN  OSTmrDel (OS_TMR  *p_tmr,

                       OS_ERR  *p_err)

使能开关

OS_CFG_TMR_EN

各个入口参数说明

OS_TMR  *p_tmr,

软件定时器控制块

 OS_ERR  *p_err

返回的错误码

 

 

3、OSTmrRemainGet()

获取剩余时间,这个时间并不是真正的时间,而是软件定时器剩余的节拍数,看看还有多少个定时器时钟节拍就会执行回调函数。这个函数不能在中断中调用。

函数原型

OS_TICK  OSTmrRemainGet (OS_TMR  *p_tmr,

                         OS_ERR  *p_err)

使能开关

OS_CFG_TMR_EN

各个入口参数说明

OS_TMR  *p_tmr,

软件定时器控制块

 OS_ERR  *p_err

返回的错误码

 

 

4、OSTmrStart()

启动或重启定时器,要启动的定时器必须是已经创建了的。

函数原型

CPU_BOOLEAN  OSTmrStart (OS_TMR  *p_tmr,

                         OS_ERR  *p_err)

使能开关

OS_CFG_TMR_EN

各个入口参数说明

OS_TMR  *p_tmr,

软件定时器控制块

 OS_ERR  *p_err

返回的错误码

 

 

 

5、OSTmrStateGet()

获取当前定时器状态,没事谁会去管定时器的状态啊,

 

6、OSTmrStop()

暂停定时器,只是暂停而已并不是停止,暂停后只要启动还是可以继续运行的,在暂停定时器时还可以执行一次回调函数,也可以不执行。如果是暂停已经停止的定时器的话,无论是任何选项都不会触发回调函数。

函数原型

CPU_BOOLEAN  OSTmrStop (OS_TMR  *p_tmr,

                        OS_OPT   opt,

                        void    *p_callback_arg,

                        OS_ERR  *p_err)

使能开关

OS_CFG_TMR_EN

各个入口参数说明

OS_TMR  *p_tmr,

定时器的任务控制块

OS_OPT   opt,

选项

OS_OPT_TMR_NONE 无选项,不执行回调函数

OS_OPT_TMR_CALLBACK 执行回调函数,使用初始化的参数

OS_OPT_TMR_CALLBACK_ARG 执行回调函数,使用最新传递的参数

void    *p_callback_arg,

给回调函数传递一个参数

OS_ERR  *p_err

返回的错误码

 

 

 

举个栗子吧;本工程软件定时器频率为100hz,也就是10ms一个脉冲。

功能;在main函数中创建第一个任务star task,然后在这个任务中创建一个LED灯闪烁任务led task和创建两个定时器timer 1、timer 2,并启动定时器。定时器也可以理解为是任务吧,所以在创建的时候和创建任务并列。老规矩,创建完定时器和LED任何后就把第一个任务star tas删除了。其中LED任务提示系统正在运行,定时器1是一个单次定时器,只会在上电后运行一次,在第5秒执行回调函数,在串口发送自己运行的次数。而定时器2是一个周期定时器,每隔1秒向串口输出执行回调函数的次数。代码如下;

Main.c文件
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "includes.h"

#include "task.h"
/************************************************
 ALIENTEK战舰STM32开发板UCOS实验
 技术支持:www.openedv.com
 淘宝店铺:http://eboard.taobao.com 
 关注微信公众平台微信号:"正点原子",免费获取STM32资料。
 广州市星翼电子科技有限公司  
 作者:正点原子 @ALIENTEK
************************************************/

//UCOSIII中以下优先级用户程序不能使用,ALIENTEK
//将这些优先级分配给了UCOSIII的5个系统内部任务
//优先级0:中断服务服务管理任务 OS_IntQTask()
//优先级1:时钟节拍任务 OS_TickTask()
//优先级2:定时任务 OS_TmrTask()
//优先级OS_CFG_PRIO_MAX-2:统计任务 OS_StatTask()
//优先级OS_CFG_PRIO_MAX-1:空闲任务 OS_IdleTask()
//技术支持:www.openedv.com
//淘宝店铺:http://eboard.taobao.com  
//广州市星翼电子科技有限公司  
//作者:正点原子 @ALIENTEK


//*****************************************************************
//开始任务
OS_TCB  starTaskTCB;	//任务控制块
#define STAR_TASK_PRIO      3		//任务优先级
#define STAR_TASK_STK_SIZE  128	//任务堆栈总大小
CPU_STK STAR_TASK_STK[ STAR_TASK_STK_SIZE ];	//任务堆栈数组


int main()
{
	OS_ERR myErr;			//os的错误码
	CPU_SR_ALLOC();	//使用临街保护就得添加这玩意,不然会报错
	
	delay_init();  //时钟初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组配置
	uart_init(115200);   //串口初始化
	LED_Init();        	 //LED初始化	
	
	OSInit(&myErr);				 //os初始化
	
	//创建任务的时候进入临街保护比较好,但是这个函数非用户的API函数,用户使用是否合适以后再讨论
	OS_CRITICAL_ENTER(); 
	
	//创建开始任务,在开始任务里创建其他任务,为每个函数入口参数都强制转换类型吧,以防万一
	OSTaskCreate (		(OS_TCB       *)&starTaskTCB,						//任务控制块,
                    (CPU_CHAR     *)"star task",						//任务名字
                    (OS_TASK_PTR   )starTaskFunc,						//任务函数
                    (void         *)0,											//任务函数入口参数
                    (OS_PRIO       )STAR_TASK_PRIO,					//任务优先级
                    (CPU_STK      *)&STAR_TASK_STK[0],			//堆栈数组基地址
                    (CPU_STK_SIZE  ) STAR_TASK_STK_SIZE/10,	//堆栈溢出限位
                    (CPU_STK_SIZE  ) STAR_TASK_STK_SIZE,		//堆栈大小
                    (OS_MSG_QTY    ) 0,											//本任务消息长度				
                    (OS_TICK       ) 0,											//时间片个数
                    (void         *) 0,											//任务块补充参数
                    (OS_OPT        ) OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,  //选项
                    (OS_ERR       *)&myErr);								//错误码
	
 OS_CRITICAL_EXIT();	//任务创建完了就该退出临界保护了									
 OSStart(&myErr);				//任务创建完成后就该运行了
 while(1);						//运行系统后就一直在系统里面跑了,应该运行不到这里
}



Task.c文件
#include "task.h"
#include "sys.h"
#include "led.h"
#include "includes.h" //想要使用UCOS系统,得包含这个头文件



//LED秒闪任务,提示系统正在运行
OS_TCB  ledTaskTCB;
#define LED_TASK_PRIO 4
#define LED_TASK_STK_SIZE 128
CPU_STK LED_TASK_STK[LED_TASK_STK_SIZE];

//定时器1 
OS_TMR timer1;

//定时器2 
OS_TMR timer2;



void starTaskFunc()
{	
	OS_ERR myErr;
	
	//创建LED闪烁任务
	OSTaskCreate (	  (OS_TCB       *)&ledTaskTCB,
                    (CPU_CHAR     *)"led task",
                    (OS_TASK_PTR   )ledTaskFunc,
                    (void         *)0,
                    (OS_PRIO       )LED_TASK_PRIO,
                    (CPU_STK      *)&LED_TASK_STK[0],
                    (CPU_STK_SIZE  ) LED_TASK_STK_SIZE/10,
                    (CPU_STK_SIZE  ) LED_TASK_STK_SIZE,
                    (OS_MSG_QTY    ) 0,
                    (OS_TICK       ) 0,
                    (void         *) 0,
                    (OS_OPT        ) OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                    (OS_ERR       *)&myErr);

		
	//创建定时器任务1
	OSTmrCreate (    (OS_TMR               *)&timer1,							//定时器控制块
                   (CPU_CHAR             *)"timer 1",					  //定时器名字
                   (OS_TICK               )500,    							//初值 500*10ms=5000ms
                   (OS_TICK               )0,										//周期值,单次DJ讲恍枰芷谥?
                   (OS_OPT                )OS_OPT_TMR_ONE_SHOT, //单次模式
                   (OS_TMR_CALLBACK_PTR   )timer1_CallBackFunc, //回调函数
                   (void                 *)0,									  //回调函数入口参数
                   (OS_ERR               *)&myErr);							//返回的错误码
										
	//创建定时器任务2
	OSTmrCreate (    (OS_TMR               *)&timer2,
                   (CPU_CHAR             *)"timer 2",
                   (OS_TICK               )0,										 //初值为0,启动后马上执行一次回调函数
                   (OS_TICK               )100,									 //周期值 100*10ms=1000ms
                   (OS_OPT                )OS_OPT_TMR_PERIODIC,	 //周期模式
                   (OS_TMR_CALLBACK_PTR   )timer2_CallBackFunc,
                   (void                 *)0,
                   (OS_ERR               *)&myErr);
									 
	//既然任务和定时器都创建完成了那就启动定时器吧
	OSTmrStart((OS_TMR*)&timer1 ,&myErr);
	OSTmrStart((OS_TMR*)&timer2 ,&myErr);
									 
	//这个任务只是用来创建其他任务的,其他任务创建完成了这个任务也就没用了,删除掉		
	OSTaskDel((OS_TCB*)0,&myErr);
}	

//LED任务的任务函数
//LED每隔500ms翻转一次,提示系统正在运行
void ledTaskFunc()
{
	OS_ERR myErr;
	while(1)		//注意,每个任务都是一个死循环
	{
		OSTimeDlyHMSM (		 (CPU_INT16U  ) 0,		//时
											 (CPU_INT16U  ) 0,		//分
											 (CPU_INT16U  ) 0,		//秒
											 (CPU_INT32U  ) 100,	//毫秒
											 (OS_OPT      ) OS_OPT_TIME_HMSM_STRICT,	//选项
											 (OS_ERR      *)&myErr);	//错误码
		LED = !LED;	//LED状态翻转
	}	
}

//定时器1回调函数
//功能;通过串口向电脑发送一条信息
void timer1_CallBackFunc(void *p_tmr, void *p_arg)
{
	static u16 callBackCount=0;
	callBackCount++;
	printf("定时器1 回调函数执行的次数= %d \r\n",callBackCount);
}


//定时器2回调函数
//功能;通过串口向电脑发送一条信息
void timer2_CallBackFunc()
{
	static u16 callBackCount=0;
	callBackCount++;
	printf("定时器2 回调函数执行的次数= %d \r\n",callBackCount);
}


Task.h文件
#ifndef __TASK_H
#define __TASK_H	 

	 				    
void starTaskFunc();	
void ledTaskFunc();
void timer1_CallBackFunc();
void timer2_CallBackFunc();
							
#endif

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值