3-(时间管理)延时函数

UCOSIII 时间管理(延时函数)

  1. 系统频率设置方法及延时函数原理
  2. 各个函数参数用法介绍,及注意事项
  3. 栗子功能+代码

 

 

延时函数会涉及到系统滴答时钟,所以有必要介绍下系统滴答时钟的原理。系统里有一个计数变量OSTickCtr。系统滴答时钟是由滴答定时器产生的,其实也就是通过一个定时器来产生特定时间间隔的中断,每产生一个系统时钟中断这个变量就自加1。而延时函数就是用这个OSTickCtr来产生延时的,比如OSTimeDly()函数在相对延时模式下延时20个时钟节拍,当前OSTickCtr=100,那么当OSTickCtr==120的时候延时结束。当然,通过与OSTickCtr挂钩来延时是不准确的,比如滴答定时器每5ms产生一个中断,然后OSTickCtr++。比如当前OSTickCtr==100开始延时,因为不知道过1毫秒还是4毫秒OSTickCtr会加到101。所以这个延时精度取决于系统心跳时钟的频率。

系统时钟是在os_cfg_app.h文件里面设置的,OS_CFG_TICK_RATE_HZ代表系统心跳的频率,注意这个是频率,不是周期。本工程用的系统心跳频率为200HZ,也就是5ms一个心跳。

当程序调用OSTimeDly()或者OSTimeDlyHMSM()这两个延时函数时,系统会把当前任务挂起来,然后去运行别的任务,等到延时结束后程序才会回到当前任务运行。这个和不带系统的软件延时有点区别,不带系统的软件延时程序会卡在当前位置一直等待延时结束,但是带系统的延时函数在延时期间系统会去运行别的任务,而不会让系统傻傻地等啥都不干。

 

 

与时间管理相关的函数不多,也就这么几个而已

1、OSTimeDly()

2、OSTimeDlyHMSM()

3、OSTimeDlyResume()

4、OSTimeGet()

5、OSTimeSet()

6、OSTimeTick()

7、OSTimeTickHook()

 

 

下面总结下各个函数的用法吧

1、OSTimeDly()函数,运行这个函数该任务会被挂起,原型如下

void  OSTimeDly (OS_TICK   dly,

                 OS_OPT    opt,

                 OS_ERR   *p_err)

参数解释

OS_TICK   dly

这个就是要延时的心跳节拍数量,心跳节拍的频率设置方法上面已经写了

OS_OPT    opt

这个函数有三个模式,这个选项就是用来选择模式的,三个模式分别是

 

OS_OPT_TIME_DLY 相对延时模式,

这个模式很好理解,比如当前系统心跳OSTickCtr=100,dly=20,也就是要延时20个系统节拍,那么OSTickCtr=120的时候程序就会继续往下执行,如果系统任务比较中的话,这个模式时间误差会比较大。

 

OS_OPT_TIME_PERIODIC 周期模式

还是举例子吧,比如当前系统心跳OSTickCtr=100,dly=20,也就是说周期是20个时钟节拍,当系统时钟计数变量OSTickCtr是dly的整数倍时,120、140、160、180、200........程序就会从延时函数处继续往下执行,这个模式在长时间延时时误差比相对延时模式要小,所以这个模式比较常用

 

OS_OPT_TIME_MATCH  绝对模式

这个模式有点意思,就是设定一个值dly,当OSTickCtr >= dly时就会执行延时函数后面的代码。比如心跳频率为200hz,也就是5ms,现在我要在上电5秒后打开一个灯,那么dly 初始值为dly = 5000ms / 5ms =1000,这样当OSTickCtr 到达1000时就会运行延时函数后面的代码从而开灯。但是这个模式有个坑,当OSTickCtr ==1000时会开灯,但是当OSTickCtr 等于1001、1002、1003时都会执行延时函数后面的代码,也就是说从上电第五秒起会频繁的开灯(虽然灯没关闭过)。所以这个绝对模式适用于只执行一次的任务,任务执行后就删除该任务,这样就不会一直执行这个任务了。如果这个任务本来就是要一直执行的话另当别论,就算是这个任务要一直执行那也要另外再加一个延时函数,不然这个任务会一直处于就绪太,会占用非常高的CPU资源

 

OS_ERR   *p_err

函数返回的错误码

 

  1. OSTimeDlyHMSM()函数,运行这个函数该任务会被挂起,原型如下

void  OSTimeDlyHMSM (CPU_INT16U   hours,

                     CPU_INT16U   minutes,

                     CPU_INT16U   seconds,

                     CPU_INT32U   milli,

                     OS_OPT       opt,

                     OS_ERR      *p_err)

使用这个函数有个要注意的地方,也就是打开使能开关OS_CFG_TIME_DLY_HMSM_EN才能使用,这个使能开关在os_cfg.h文件里。

这个函数使用起来挺方便的,只要设置好延时的时间的时、分、秒、毫秒就行,而不需要去管系统心跳频率是多少。虽然不需要关心系统心跳频率,但是这个函数也是根据系统心跳来延时的,所以延时精度也由心跳频率决定。

参数解释

 

OS_OPT_TIME_HMSM_STRICT

OS_OPT_TIME_HMSM_NON_STRICT

CPU_INT16U   hours,

延时的时间,小时。参数范围 0-99

  范围 0-999

CPU_INT16U   minutes,

延时的时间,分钟。参数范围 0-59

范围 0-9999

CPU_INT16U   seconds,

延时的时间,秒。参数范围 0-59

  范围 0-65535

CPU_INT32U   milli,

延时的时间,毫秒。参数范围 0-999

  范围 0-4294967295

OS_OPT       opt,

选项,这里有两个选项,严格模式和非严格模式,这个名字是我根据单词直译的,至于这两个模式的区别在另外几个参数里有介绍,其实就是不同的模式下延时函数入口参数的可取范围不一样而已

OS_OPT_TIME_HMSM_STRICT 严格模式

OS_OPT_TIME_HMSM_NON_STRICT 非严格模式

OS_ERR      *p_err

老规矩,返回的错误码

这些参数看着挺蛋疼的,来两个栗子吧。都是延时70分钟。

严格模式

70分钟=1小时+10分钟;OSTimeDlyHMSM (1,10,0,0,OS_OPT_TIME_HMSM_STRICT,&myErr)

 

非严格模式,这个模式可以有不同的表示方法

70分钟表示为70分钟;OSTimeDlyHMSM (0,70,0,0,OS_OPT_TIME_HMSM_NON_STRICT,&myErr)

70分钟表示为70*60秒;OSTimeDlyHMSM (0,0,70*60,0,OS_OPT_TIME_HMSM_NON_STRICT,&myErr)

70分钟表示为70*60*1000毫秒;OSTimeDlyHMSM (0,0,0, 70*60*1000,OS_OPT_TIME_HMSM_NON_STRICT,&myErr)

 

至于用什么模式,还是看个人习惯吧。

 

3、OSTimeDlyResume()

这个函数是让被OSTimeDly()或者OSTimeDlyHMSM()挂起任务解挂,从而进入就绪太。但是这样解挂有个缺点,任务并不知道是延时时间到了还是被提前解挂了。比如任务要延时10分钟,但是在延时6分钟的时候OSTimeDlyResume() 把延时取消了,然后任务进入就绪太,这时任务会认为延时10分钟已经到了,并不知道实际上10分钟延时还没到。有个要注意的地方,不能在在本任务中给自己解挂,因为已经被挂起了,所以本任务根本不会运行,又何来解挂的说法,所以要在别的任务中给本任务解挂。

函数原型如下;

void  OSTimeDlyResume (OS_TCB  *p_tcb,

                       OS_ERR  *p_err)

这里只有连个参数,第一个参数是要解挂的任务的TCB块,第二个参数是返回的错误码。

 

4、OSTimeGet()

这个函数是获取系统心跳计数变量OSTickCtr 的。

 

5、OSTimeSet()

这个函数是设置系统心跳计数变量OSTickCtr 的,因为延时函数是根据这个OSTickCtr 来延时的,所以这个OSTickCtr 没事不要更改。比如当前OSTickCtr =100,我要延时20个心跳节拍,当OSTickCtr 到达110的时候我把OSTickCtr 重置为100,当OSTickCtr 到达120的时候实际上已经过了30个心跳节拍,延时的时间就不准了。除了影响延时函数还会影响软件定时器,所以最好不要调用这个函数。

6、OSTimeTick()

这个可以认为是系统内部函数,就是每次心跳节拍来的时候都会调用一次,在这个函数里更新OSTickCtr 和各个延时函数的变量等。用户一般用不到吧

  1. OSTimeTickHook()

钩子函数,以后会用专门的章节来总结钩子函数的。

最后一部分,来个栗子吧。创建几个任务,就是把上面的两个延时函数的各个不同选项都用一遍,具体在代码里有注释。
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_TCB  uartTask_1_TCB;
#define UART_TASK_1_PRIO 5
#define UART_TASK_1_STK_SIZE 128
CPU_STK UART_TASK_1_STK[ UART_TASK_1_STK_SIZE ];


//串口输出任务2
OS_TCB  uartTask_2_TCB;
#define UART_TASK_2_PRIO 6
#define UART_TASK_2_STK_SIZE 128
CPU_STK UART_TASK_2_STK[ UART_TASK_2_STK_SIZE ];


//串口输出任务3
OS_TCB  uartTask_3_TCB;
#define UART_TASK_3_PRIO 7
#define UART_TASK_3_STK_SIZE 128
CPU_STK UART_TASK_3_STK[ UART_TASK_3_STK_SIZE ];

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								
	OSTaskCreate (	  (OS_TCB       *)&uartTask_1_TCB,
                    (CPU_CHAR     *)"uart task 1 ",
                    (OS_TASK_PTR   )uartTask_1_Func,
                    (void         *)0,
                    (OS_PRIO       )UART_TASK_1_PRIO,
                    (CPU_STK      *)&UART_TASK_1_STK[0],
                    (CPU_STK_SIZE  ) UART_TASK_1_STK_SIZE/10,
                    (CPU_STK_SIZE  ) UART_TASK_1_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);

	//创建串口输出任务2									
	OSTaskCreate (	  (OS_TCB       *)&uartTask_2_TCB,
                    (CPU_CHAR     *)"uart task 2 ",
                    (OS_TASK_PTR   )uartTask_2_Func,
                    (void         *)0,
                    (OS_PRIO       )UART_TASK_2_PRIO,
                    (CPU_STK      *)&UART_TASK_2_STK[0],
                    (CPU_STK_SIZE  ) UART_TASK_2_STK_SIZE/10,
                    (CPU_STK_SIZE  ) UART_TASK_2_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);
										
	//创建串口输出任务3
	OSTaskCreate (	  (OS_TCB       *)&uartTask_3_TCB,
                    (CPU_CHAR     *)"uart task 3 ",
                    (OS_TASK_PTR   )uartTask_3_Func,
                    (void         *)0,
                    (OS_PRIO       )UART_TASK_3_PRIO,
                    (CPU_STK      *)&UART_TASK_3_STK[0],
                    (CPU_STK_SIZE  ) UART_TASK_3_STK_SIZE/10,
                    (CPU_STK_SIZE  ) UART_TASK_3_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);
										
	//这个任务只是用来创建其他任务的,其他任务创建完成了这个任务也就没用了,删除掉		
	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的任务函数
//功能;每隔0.5秒向电脑串口助手发送一次数据
void uartTask_1_Func()
{
	OS_ERR myErr;
	while(1)
	{
		OSTimeDly(100, OS_OPT_TIME_DLY, &myErr );// 相对延时模式,延时100*5ms=500ms
		printf("Im uart task 1 \r\n");
	}
}

//串口任务2的任务函数
//功能;每隔1秒向电脑串口助手发送一次数据
void uartTask_2_Func()
{
	OS_ERR myErr;
	while(1)
	{
		OSTimeDly(200, OS_OPT_TIME_PERIODIC, &myErr );// 周期模式,200*5ms=1000ms
		printf("Im uart task 2 \r\n");
	}
}


//串口任务3的任务函数
//功能;上电5秒后通过串口向电脑发送数据,只发送一次
void uartTask_3_Func()
{
	OS_ERR myErr;
	while(1)
	{
		OSTimeDly(1000, OS_OPT_TIME_MATCH, &myErr );// 绝对模式,1000*5ms=5000ms
		printf("Im uart task 3 ,上电时间到达5秒\r\n");
		OSTaskDel((OS_TCB*)0,&myErr);//这个任务只在上电第5秒运行一次,然后就把自己给删除掉
	}
}



Task.h文件
#ifndef __TASK_H
#define __TASK_H	 

	 				    
void starTaskFunc();	
void ledTaskFunc();
void uartTask_1_Func();
void uartTask_2_Func();
void uartTask_3_Func();
							
#endif

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值