虚拟定时器Virtual Timers
CMSIS-RTOS API里有几个向下计数的虚拟定时器,它们实现计数完成时用户的回调功能。每个定时器都可以配置成单次计数或重复计数模式,它们可以在定义定时器结构体的时候被创建:
osTimerDef(timer0, led_function);
在上面的代码中给定时器定义了一个名字timer0,并同时定义了回调函数led_function,紧接着必须对这个定时器进行实例化:
osTimerId timer0_handle = osTimerCreate(timer(timer0), osTimerPeriodic,(void*)0);
上面的代码创建了这个定时器,并把它定义成周期性定时器(还可以定义成单次定时器–osTimerOnce),最后一个参数0是传递给回调函数的实参,当定时器定时结束时会用的用到。
osTimerStart(timer0_handle, 0x100);
定时器可以在线程的任意位置启动,从上面的代码中可以看到,定时器启动函数使用的是timer0的句柄,并定义了0x100ms的周期定时。
练习:虚拟定时器
在这个练习里,我们将配置一些虚拟定时器以不同的频率来触发回调函数。
打开Pack Installer,并选择“Ex 6 Virtual Timers”,然后把它安装到自定义路径
代码依旧是原来的LED闪烁程序,在代码中添加四个虚拟定时器来触发回调函数。当某个定时器定时完成时,这个回调函数就会执行翻转LED的程序。
定时器在启动代码中定义:
osTimerDef(timer0_handle, callback);
osTimerDef(timer1_handle, callback);
osTimerDef(timer2_handle, callback);
osTimerDef(timer3_handle, callback);
在main函数中初始化:
osTimerId timer0 = osTimerCreate(osTimer(timer0_handle), osTimerPeriodic, (void*)0);
osTimerId timer0 = osTimerCreate(osTimer(timer0_handle), osTimerPeriodic, (void*)1);
osTimerId timer0 = osTimerCreate(osTimer(timer0_handle), osTimerPeriodic, (void*)2);
osTimerId timer0 = osTimerCreate(osTimer(timer0_handle), osTimerPeriodic, (void*)3);
每个定时器都有不同的句柄和ID号,并且给回调函数传递不同的参数:
void callback(void const *param){
switch((uint32_t)param) {
case 0: GPIOB->ODR ^=0x02; break;
case 1: GPIOB->ODR ^=0x04; break;
case 2: GPIOB->ODR ^=0x08; break;
case 3: GPIOB->ODR ^= 0x10;break;
}
}
当触发发生时,回调函数就会通过传递过来的参数来控制相应的LED翻转。
另外,在程序中配置虚拟定时器前,需要在RTX配置文件中使能定时器线程。
打开RTX_Conf_CM.c 文件,并切换到配置向导标签页
展开System Configuration,勾选User Timers。
编译工程,并打开debugger仿真。
全速运行!打开GPIOB仿真窗口。
从上图可以看到这几个管脚的设置是按照周期变化的。
打开System and Thread Viewer window
需要注意的是,osDelay()产生相对延时,而虚拟定时器产生绝对延时,利用这一点,你可以加入固定时间间隔的代码。
亚毫秒延时
前面介绍的各种CMSIS-RTOS时间管理函数时间精度都是以ms为单位的,如果想产生微秒级别的延时,可以使用系统节拍计数SysTick。这种延时并不产生任务的调度,它只是暂停执行一段时间。
首先,我们需要获取SysTick计数:
int32_t tick, delayPeriod;
tick = osKernelSysTick();//获取内核系统节拍起始值
然后,获取系统节拍微秒值的标定值:
delayPeriod = osKernelTickMicroSec(100);
最后,根据需要创建时间延时:
do{
//100us延时
}wile((osKernelSysTick())-tick < delayPeriod);