一、 OS_TICKS_PER_SEC
OS_TICKS_PER_SEC 是系统节拍数的参数,代表着在1s的时间内系统进几次滴答中断进行任务调度。
如下例代码所示,
1.CPU主频为168M,滴答定时器时钟配置为8分频,即滴答定时器时钟为21M,代表着一秒钟计数21M次。
2.SysTick->LOAD为重装载寄存器,当其计数到0后进入滴答中断。
3.此时当重装载寄存器的值为21 * 1000000时,代表着1S进入1次滴答中断,将其与OS_TICKS_PER_SEC建立联系,即21 * 1000000 / OS_TICKS_PER_SEC .若OS_TICKS_PER_SEC = 200,则进入滴答中断的时间缩小200倍,代表着在1S时间内进入200次滴答定时器中断,所以滴答定时器的中断时间(任务调度周期)就为1/200 = 0.005S = 5 ms,频率F = 1/t = 200hz。
#define delay_ostickspersec OS_TICKS_PER_SEC //OS时钟节拍,即每秒调度次数//STM32F407的系统运行时钟配置为168M,函数delay_init()传参168void delay_init(u8 SYSCLK){u32 reload;SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); reload=SYSCLK/8; //每秒钟的计数次数 单位为M reload*=1000000/delay_ostickspersec; //根据delay_ostickspersec设定溢出时间//reload为24位寄存器,最大值:16777216,在168M下,约合0.7989s左右 SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断SysTick->LOAD=reload; //每1/delay_ostickspersec秒中断一次 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK }
二、OS_TMR_CFG_TICKS_PER_SEC
OS_TMR_CFG_TICKS_PER_SEC用于定时器分频,如图所示,定时器模块的时钟节拍实际上是Hook在操作系统的时钟节拍上再分频的,这也决定了其精度不会超过系统时钟节拍(好像超过了也没什么意义),跟Hook有关的代码(os_cpu_c.c中)如下:
……
#if (OS_TMR_EN > 0)
static INT16U OSTmrCtr;
#endif
……
void OSTimeTickHook (void)
{
……
#if OS_TMR_EN > 0OSTmrCtr++;if (OSTmrCtr >= (OS_TICKS_PER_SEC / OS_TMR_CFG_TICKS_PER_SEC)) {OSTmrCtr = 0;OSTmrSignal();}
#endif
}
……
可以看出,实际上定时器模块是通过计数的方式对系统节拍进行了分频,系统每Tick了OS_TICKS_PER_SEC / OS_TMR_CFG_TICKS_PER_SEC下就通知(OSTmrSignal)一次定时器模块,要是OS_TMR_CFG_TICKS_PER_SEC大于OS_TICKS_PER_SEC 的话,那相除的结果就是0,也就是每次系统Tick都会通知定时器模块。再通过定时器的创建函数OSTmrCreate()中dly、period的周期来确认定时器的定时时间。
如下例所示,此时系统节拍为5MS进一次滴答中断,定时器则是在200/100次中断后通知定时器模块,period分别为 10 、 20,实现了100ms、200ms的定时,当定时时间到后调用相对应的回调函数进行操作。
#define OS_TICKS_PER_SEC 200u /* Set the number of ticks in one second */
#define OS_TMR_CFG_TICKS_PER_SEC 100u
/*Rate at which timer management task runs (Hz) */tmr1=OSTmrCreate(10,10,OS_TMR_OPT_PERIODIC,(OS_TMR_CALLBACK)tmr1_callback,0,"tmr1",&err); //100ms执行一次
tmr2=OSTmrCreate(10,20,OS_TMR_OPT_PERIODIC,(OS_TMR_CALLBACK)tmr2_callback,0,"tmr2",&err); //200ms执行一次
三、延时函数:以系统节拍数为单位
OSTimeDly()是以系统节拍为处理单位的,实际的时间与希望的时间是有误差的,最坏的情况下误差接近一个系统节拍。因此时间管理服务函数只能用在对时间精度要求不高的场合,或者时间间隔较长的场合。
通过一个实例来说明下:我们设计一个任务,让一个LED以50个时钟节拍为单位
void TaskLED (void *pdata){…… //初始化代码while (1) //循坏控制LED以固定频率闪烁{IO0CLR = LED1;//点亮LEDOSTimeDly(25);//延时25个节拍IO0SET = LED1;//熄灭LEDOSTimeDly(25);//延时25个节拍}}
注意:上面的设计是OSTimeDly() 控制任务的周期性执行,还可以用它来控制任务的运行节拍。
OSTimeDlyHMSM(hours, minutes,seconds, ms)是一个以时、分、秒、毫秒为参数的延时函数。函数在内部把这些参数转换为时钟节拍,再通过单次或多次调用OSTimeDly()进行延时和任务调度,所以延时原理和调用延时函数OSTimeDly()是一样的。
相关宏定义:
1)OS_TIME_DLY_HMSM_EN //使能
2)OS_ARG_CHK_EN //参数合法检测
对于延时的计算如下:
((INT32U)hours * 3600L + (INT32U)minutes * 60L + (INT32U)seconds) * OS_TICKS_PER_SEC对于时,分,秒的TICK计算,这部分问题不大。前面都是算总共有多少秒,后面再一起乘每秒的滴答中断次数OS_TICKS_PER_SEC就能算出总共有多少个TICK。
关键是后面毫秒部分的计算,采用四舍五入的计算方式,
OS_TICKS_PER_SEC * ((INT32U)ms + 500L / OS_TICKS_PER_SEC) / 1000L;
找了一下,发现函数前面的注释做出来解释:
* Note(s) : The resolution on the milliseconds depends on the tick rate. For example, you can't do
* a 10 mS delay if the ticker interrupts every 100 mS. In this case, the delay would be
* set to 0. The actual delay is rounded to the nearest tick.
毫秒分辨率取决于TICK的速度。例如:如果ticker设置的时间中断为100ms,则不能进行10ms的延时,精度单位为100ms。这种情况下,延迟会被四舍五入掉。
比如我现在ticker是为10ms进中断,进行1ms的延时只会是四舍五入。但是延时又不能全部是0,就默认为1。1个TICK的延时。