【UCOSIII】UCOSIII软件定时器

在学习STM32的时候会使用定时器来做很多定时任务,这个定时器是单片机自带的,也就是硬件定时器,在UCOSIII中提供了软件定时器,我们可以使用这些软件定时器完成一些功能,本文我们就讲解一下UCOSIII软件定时器。


UCOSIII软件定时器简介

定时器其实就是一个递减计数器,当计数器递减到0的时候就会触发一个动作,这个动作就是回调函数,当定时器计时完成时就会自动的调用这个回调函数。因此我们可以使用这个回调函数来完成一些设计。比如,定时10秒后打开某个外设等等,在回调函数中应避免任何可以阻塞或者删除定时任务的函数。

如果要使用定时器的话需要将宏OS_CFG_TMR_DEL_EN定义为1。

定时器的分辨率由我们定义的系统节拍频率OS_CFG_TICK_RATE_HZ决定,比如我们定义为200,系统时钟周期就是5ms,定时器的最小分辨率肯定就是5ms。但是定时器的实际分 辨 率 是 通 过 宏OS_CFG_TMR_TASK_RATE_HZ定 义 的 , 这 个 值 绝 对 不 能 大 于OS_CFG_TICK_RATE_HZ。比如我们定义OS_CFG_TMR_TASK_RATE_HZ为100,则定时器的时间分辨率为10ms。有关UCOSIII定时器的函数都在os_tmr.c文件中。

什么是回调函数呢?

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方法直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。


UCOSIII软件定时器API函数

UCOSIII软件定时器API函数
函数 含义
OSTmrCreate() 创建定时器并制定运行模式
OSTmrDel() 删除定时器
OSTmrRemainGet() 获取定时器的剩余时间
OSTmrStart() 启动定时器计数
OSTmrStateGet() 获取当前定时器状态
OSTmrStop() 停止计数器倒计时

创建一个定时器

如果我们要使用定时器,肯定需要先创建一个定时器,使用OSTmrCreate()函数来创建一个定时器,这个函数也用来确定定时器的运行模式,OSTmrCreate()函数原型如下:

void  OSTmrCreate (OS_TMR               *p_tmr,                //指向定时器的指针,宏OS_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)                    //调用此函数以后返回的错误码
{
    CPU_SR_ALLOC();

    OS_CRITICAL_ENTER();
    p_tmr->State          = (OS_STATE           )OS_TMR_STATE_STOPPED;     /* Initialize the timer fields             */
    p_tmr->Type           = (OS_OBJ_TYPE        )OS_OBJ_TYPE_TMR;
    p_tmr->NamePtr        = (CPU_CHAR          *)p_name;
    p_tmr->Dly            = (OS_TICK            )dly;
    p_tmr->Match          = (OS_TICK            )0;
    p_tmr->Remain         = (OS_TICK            )0;
    p_tmr->Period         = (OS_TICK            )period;
    p_tmr->Opt            = (OS_OPT             )opt;
    p_tmr->CallbackPtr    = (OS_TMR_CALLBACK_PTR)p_callback;
    p_tmr->CallbackPtrArg = (void              *)p_callback_arg;
    p_tmr->NextPtr        = (OS_TMR            *)0;
    p_tmr->PrevPtr        = (OS_TMR            *)0;

    OSTmrQty++;                                             /* Keep track of the number of timers created             */


    OS_CRITICAL_EXIT_NO_SCHED();
   *p_err = OS_ERR_NONE;
}

opt参数:定时器运行选项,这里有两个模式可以选择。OS_OPT_TMR_ONE_SHOT单次定时器,OS_OPT_TMR_PERIODIC周期定时器。

软件定时器工作模式

单次定时器

使用OSTmrCreate()函数创建定时器时把参数opt设置为OS_OPT_TMR_ONE_SHOT,就是创建的单次定时器。创建一个单次定时器以后,我们一旦调用OSTmrStart()函数定时器就会从创建时定义的dly开始倒计数,直到减为0调用回调函数并停止。单次定时器的定时器只执行一次。


上图展示了单次定时器在调用OSTmrStart()函数后开始倒计数,将dly减为0后调用回调函数的过程,到这里定时器就停止运行,不再做任何事情了,我们可以调用OSTmrDel()函数来删除这个运行完成的定时器。其实我们也可以重新调用OSTmrStart()函数来开启一个已经运行完成的定时器,通过调用OSTmrStart()函数来重新触发单次定时器,如下图所示。


周期模式(无初始延迟)

使用OSTmrCreate()函数创建定时器时把参数opt设置为OS_OPT_TMR_PERIODIC,就是创建的周期定时器。当定时器倒计数完成后,定时器就会调用回调函数,并且重置计数器开始下一轮的定时,就这样一直循环下去。如果使用OSTmrCreate()函数创建定时器的时候,参数dly为0的话,那么定时器在每个周期开始时计数器的初值就为period,如下图所示。


周期定时器(有初始化延迟)

在创建定时器的时候也可以创建带有初始化延时的,初始化延时就是OSTmrCreate()函数中的参数dly就是初始化延迟,定时器的第一个周期就是dly。当第一个周期完成后就是用参数period作为周期值,调用OSTmrStart()函数开启有初始化延时的定时器,如下图所示。



UCOSIII实际例程

例程要求:本例程新建两个任务:任务A和任务B,任务A用于创建两个定时器:定时器1和定时器2,任务A还创建了另外一个任务B。其中定时器1为周期定时器,初始延时为200ms,以后的定时器周期为1000ms,定时器2为单次定时器,延时为2000ms。

任务B作为按键检测任务,当KEY_UP键按下的时候,打开定时器1;当KEY0按下的时候打开定时器2;当KEY1按下的时候,同时关闭定时器1和2;任务B还用来控制LED0,使其闪烁,提示系统正在运行。

定时器1定时完成以后调用回调函数刷新其工作区域的背景,并且在LCD上显示定时器1运行的次数。定时器2定时完成后也调用其回调函数来刷新其工作区域的背景,并且显示运行次数,由于定时器2是单次定时器,我们通过串口打印来观察单次定时器的运行情况。

例子:

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
#include "includes.h"

//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()

//任务优先级
#define START_TASK_PRIO		3
//任务堆栈大小	
#define START_STK_SIZE 		128
//任务控制块
OS_TCB StartTaskTCB;
//任务堆栈	
CPU_STK START_TASK_STK[START_STK_SIZE];
//任务函数
void start_task(void *p_arg);

//任务优先级
#define TASK1_TASK_PRIO		4
//任务堆栈大小	
#define TASK1_STK_SIZE 		128
//任务控制块
OS_TCB Task1_TaskTCB;
//任务堆栈	
CPU_STK TASK1_TASK_STK[TASK1_STK_SIZE];
void task1_task(void *p_arg);

OS_TMR 	tmr1;		//定时器1
OS_TMR	tmr2;		//定时器2
void tmr1_callback(void *p_tmr, void *p_arg); 	//定时器1回调函数
void tmr2_callback(void *p_tmr, void *p_arg);	//定时器2回调函数

int lcd_discolor[14]={	WHITE, RED,   BLUE,  BRED,                          //LCD刷屏时使用的颜色
						GRED,  GBLUE, BLACK,   MAGENTA,       	 
						GREEN, CYAN,  YELLOW,BROWN, 			
						BRRED, GRAY };

int main(void)                                    //主函数
{
	OS_ERR err;
	CPU_SR_ALLOC();
	
	delay_init();  //时钟初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组配置
	uart_init(115200);   //串口初始化
	LED_Init();         //LED初始化	
	LCD_Init();			//LCD初始化	
	KEY_Init();			//按键初始化
	
	POINT_COLOR = RED;
	LCD_ShowString(30,10,200,16,16,"ALIENTEK STM32F1");	
	LCD_ShowString(30,30,200,16,16,"UCOSIII Examp 9-1");
	LCD_ShowString(30,50,200,16,16,"KEY_UP:Start Tmr1");
	LCD_ShowString(30,70,200,16,16,"KEY0:Start Tmr2");
	LCD_ShowString(30,90,200,16,16,"KEY1:Stop Tmr1 and Tmr2");
	
	LCD_DrawLine(0,108,239,108);		//画线
	LCD_DrawLine(119,108,119,319);		//画线
	
	POINT_COLOR = BLACK;
	LCD_DrawRectangle(5,110,115,314); 	//画一个矩形	
	LCD_DrawLine(5,130,115,130);		//画线
	
	LCD_DrawRectangle(125,110,234,314);     //画一个矩形	
	LCD_DrawLine(125,130,234,130);		//画线
	POINT_COLOR = BLUE;
	LCD_ShowString(6,111,110,16,16,	 "TIMER1:000");
	LCD_ShowString(126,111,110,16,16,"TIMER2:000");
	
	OSInit(&err);		    	//初始化UCOSIII
	OS_CRITICAL_ENTER();	//进入临界区			 
	//创建开始任务
	OSTaskCreate((OS_TCB 	* )&StartTaskTCB,		//任务控制块
				 (CPU_CHAR	* )"start task", //任务名字
                 (OS_TASK_PTR )start_task, 			//任务函数
                 (void		* )0,				//传递给任务函数的参数
                 (OS_PRIO	  )START_TASK_PRIO,     //任务优先级
                 (CPU_STK   * )&START_TASK_STK[0],	//任务堆栈基地址
                 (CPU_STK_SIZE)START_STK_SIZE/10,	//任务堆栈深度限位
                 (CPU_STK_SIZE)START_STK_SIZE,		//任务堆栈大小
                 (OS_MSG_QTY  )0,			//任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
                 (OS_TICK	  )0,			//当使能时间片轮转时的时间片长度,为0时为默认长度,
                 (void   	* )0,			//用户补充的存储区
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
                 (OS_ERR 	* )&err);		//存放该函数错误时的返回值
	OS_CRITICAL_EXIT();	            //退出临界区	 
	OSStart(&err);                      //开启UCOSIII
}

void start_task(void *p_arg)                //开始任务函数
{
	OS_ERR err;
	CPU_SR_ALLOC();
	p_arg = p_arg;
	
	CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0u
   OSStatTaskCPUUsageInit(&err);  	    //统计任务                
#endif
	
#ifdef CPU_CFG_INT_DIS_MEAS_EN		    //如果使能了测量中断关闭时间
    CPU_IntDisMeasMaxCurReset();	
#endif
	
#if	OS_CFG_SCHED_ROUND_ROBIN_EN      //当使用时间片轮转的时候
	                     //使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
	OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);  
#endif	
	
	//创建定时器1
	OSTmrCreate((OS_TMR	*)&tmr1,		//定时器1
                (CPU_CHAR	*)"tmr1",		//定时器名字
                (OS_TICK	 )20,			//20*10=200ms
                (OS_TICK	 )100,          //100*10=1000ms
                (OS_OPT		 )OS_OPT_TMR_PERIODIC, //周期模式
                (OS_TMR_CALLBACK_PTR)tmr1_callback,//定时器1回调函数
                (void	    *)0,			//参数为0
                (OS_ERR	    *)&err);		//返回的错误码
				
	//创建定时器2
	OSTmrCreate((OS_TMR	*)&tmr2,		
                (CPU_CHAR	*)"tmr2",		
                (OS_TICK	 )200,			//200*10=2000ms	
                (OS_TICK	 )0,   					
                (OS_OPT		 )OS_OPT_TMR_ONE_SHOT, 	//单次定时器
                (OS_TMR_CALLBACK_PTR)tmr2_callback,	//定时器2回调函数
                (void	    *)0,			
                (OS_ERR	    *)&err);	
				
	OS_CRITICAL_ENTER();	//进入临界区
	//创建TASK1任务
	OSTaskCreate((OS_TCB 	* )&Task1_TaskTCB,		
		 (CPU_CHAR	* )"Task1 task", 		
                 (OS_TASK_PTR )task1_task, 			
                 (void		* )0,					
                 (OS_PRIO	  )TASK1_TASK_PRIO,     
                 (CPU_STK   * )&TASK1_TASK_STK[0],	
                 (CPU_STK_SIZE)TASK1_STK_SIZE/10,	
                 (CPU_STK_SIZE)TASK1_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 	* )&err);				 
	OS_CRITICAL_EXIT();	//退出临界区
	OSTaskDel((OS_TCB*)0,&err);	//删除start_task任务自身
}

void task1_task(void *p_arg)            //任务1的任务函数
{
	u8 key,num;
	OS_ERR err;
	while(1)
	{
		key = KEY_Scan(0);
		switch(key)
		{
			case WKUP_PRES:     //当key_up按下的话打开定时器1
				OSTmrStart(&tmr1,&err);	//开启定时器1
				printf("开启定时器1\r\n");
				break;
			case KEY0_PRES:		//当key0按下的话打开定时器2
				OSTmrStart(&tmr2,&err);	//开启定时器2
				printf("开启定时器2\r\n");
				break;
			case KEY1_PRES:		//当key1按下话就关闭定时器
				OSTmrStop(&tmr1,OS_OPT_TMR_NONE,0,&err);	//关闭定时器1
				OSTmrStop(&tmr2,OS_OPT_TMR_NONE,0,&err);	//关闭定时器2
				printf("关闭定时器1和2\r\n");
				break;	
		}
		num++;
		if(num==50) //每500msLED0闪烁一次
		{
			num = 0;
			LED0 = ~LED0;	
		}
		OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err);   //延时10ms
	}
}

void tmr1_callback(void *p_tmr, void *p_arg)                    //定时器1的回调函数
{
	static u8 tmr1_num=0;
	LCD_ShowxNum(62,111,tmr1_num,3,16,0x80); //显示定时器1的执行次数
	LCD_Fill(6,131,114,313,lcd_discolor[tmr1_num%14]);//填充区域
	tmr1_num++;		//定时器1执行次数加1
}

void tmr2_callback(void *p_tmr,void *p_arg)                    //定时器2的回调函数
{
	static u8 tmr2_num = 0;
	tmr2_num++;		//定时器2执行次数加1
	LCD_ShowxNum(182,111,tmr2_num,3,16,0x80);  //显示定时器1执行次数
	LCD_Fill(126,131,233,313,lcd_discolor[tmr2_num%14]); //填充区域
	LED1 = ~LED1;
	printf("定时器2运行结束\r\n");
}


猜你喜欢

转载自blog.csdn.net/qq_38410730/article/details/80821152