1、 硬件定时器设备概述RT-Thread内置了已经内置了软件定时器,但是对于需求更高实时性或者定时精度的场合不太适用。RT-Thread提供了一种驱动设备框架提供硬件定时器驱动,源码位于drivers/hwtimer目录下。hwtimer本身不依赖于其他组件,直接添加到工程里面就能用。2、 硬件定时器驱动实现这里以stm32单片机内部的TIM6这个基本定时器为例,实现一个硬件定时器驱动。a) 至少要实现的接口初始化函数:这个函数用于实现对TIM6硬件的相关初始化。函数原型:void (init)(struct rt_hwtimer_device timer,rt_uint32_t state);
启动函数:这个函数用于启动TIM6硬件的定时功能函数原型:rt_err_t (start)(struct rt_hwtimer_device timer,rt_uint32_t cnt, rt_hwtimer_mode_t mode);
停止函数:这个函数用于停止TIM6硬件的定时功能函数原型:void (stop)(struct rt_hwtimer_device timer);
获取定时器计数值函数:这个函数用于获取TIM6的Counter值(似乎没啥用)函数原型:rt_uint32_t (count_get)(struct rt_hwtimer_devicetimer);
控制函数:用于对定时器进行控制,控制功能主要是设置定时器中断频率。函数原型:rt_err_t (control)(struct rt_hwtimer_device timer,rt_uint32_t cmd, void args);b) 初始化函数实现方法初始化函数参数中有一个state参数,这里一定要判定这个值是否等于1,其他情况不要进行硬件初始化。代码片段:if(state==1){//先开启TIM6的时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);//对TIM6硬件进行默认参数初始化TIM_TimeBaseStructure6.TIM_Period = 10000-1;TIM_TimeBaseStructure6.TIM_Prescaler = 7200-1;TIM_TimeBaseStructure6.TIM_ClockDivision = 0x0;TIM_TimeBaseStructure6.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseStructure6.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure6);//开启TIM6的溢出中断请求TIM_ITConfig(TIM6,TIM_IT_Update, ENABLE);//先不启动TIM6这个定时器TIM_Cmd(TIM6, DISABLE);//配置TIM6的NVIC 这里省略具体配置过程NVIC_Timer6_Config();}c) 启动函数实现方法这个函数中的cnt和mode两个参数分别用于设置定时器的计数值和运行模式,这个计数值其实就是定时器的溢出值,当定时器计数到这个值的时候产生一次溢出中断请求。模式设置就两种,单次模式和连续模式,这里可以不用理会这个参数直接把定时器设置成连续模式,因为驱动框架中会在单次模式定时时间到了以后把定时器停掉(具体执行过程见hwtimer.c的第303行函数rt_device_hwtimer_isr)。这里不理会这个参数。代码片段://设置定时器溢出值TIM_TimeBaseStructure6.TIM_Period = cnt;//配置定时器TIM_TimeBaseInit(TIM6, dev);//启动定时器TIM_Cmd(TIM6, ENABLE);d) 停止函数实现方法这个函数很简单,直接禁能掉硬件定时器就是。代码片段:TIM_Cmd(TIM6,DISABLE);e) 控制函数实现方法这里函数中的cmd和arg分别用于指定命令类型和命令参数。其中arg是void指针,这里需要根据不同命令进行强转成需要的数据类型。这里我们只需要实现一种命令的实现,HWTIMER_CTRL_FREQ_SET,这个控制指令用于设置定时器的定时频率,此命令传入的参数类型是rt_uint32_t,用于指定频率值。根据这个值进行定时器的频率设定。代码片段:uint16_t val; //临时变量 用于存储计算出的分频值rt_uint32_t freq;//用于获取参数中的频率设定值//判定命令是不是 设置频率命令if(cmd == HWTIMER_CTRL_FREQ_SET){freq = ((rt_uint32_t)arg);//从参数中获取频率设定值val = TIMER_CLK / freq;//算分频值TIM_TimeBaseStructure6. TIM_Prescaler=val-1;//设置硬件分频系数TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure6);//初始化TIM6硬件return RT_EOK;//返回正常值}f) 获取定时器计数值函数实现方法这个函数比较简单,直接返回定时器内部的计数值就行。return TIM_GetCounter(TIM6);g) 添加定时器到系统中 i. 根据rt_hwtimer_ops结构体定义实现一个实例,这个结构体将所有相关的硬件定时器操作函数集合在一起,根据结构体的成员把上面实现好的函数放在里面。代码片段:static struct rt_hwtimer_ops ops={rt_hwtimer_init,rt_hwtimer_start,rt_hwtimer_stop,rt_hwtimer_get,rt_hwtimer_control} ii. 根据rt_hwtimer_info 结构体定义实现一个实例,这个结构体用于界定定时器所能运行的频率范围和定时器最大计数值。static struct rt_hwtimer_info hw_timer6_info= {
100000,//最大频率2000,//最小频率0xFFFF,//最大计数值HWTIMER_MODE_PERIOD//周期模式}; iii. 使用rt_hwtimer_t结构体定义一个实例,这个结构体是硬件定时器设备结构体,把上面两步的两个结构体的地址赋值给这个实例的相应成员。然后使用rt_device_hwtimer_register函数注册这个设备到操作系统中。代码片段:timer6.info = &hw_timer6_info;timer6.ops = &ops;rt_device_hwtimer_register(&timer6, “timer6”,RT_NULL);h) 中断函数实现方法中断函数中需要调用rt_device_hwtimer_isr函数通知驱动框架某个硬件定时器设备发生了溢出中断,使得驱动框架正常计时。代码片段:rt_interrupt_enter();//先进入临界区if(TIM_GetITStatus(TIM6,TIM_IT_Update) != RESET)//判定是不是溢出中断{ TIM_ClearITPendingBit(TIM6,TIM_IT_Update);//清中断标志rt_device_hwtimer_isr(&timer6);//调用驱动框架的中断通知函数} rt_interrupt_leave();//离开临界区3、 硬件定时器使用a) 初始化定时器使用rt_device_find函数查找设备,判定是否为空然后再进行下面的初始化等工作。rt_device_t rt_d;//定义一个设备指针rt_d = rt_device_find(“timer6”);//从操作系统中查找timer6这个设备if(rt_d == RT_NULL) return RT_ERROR;//判空rt_d->init(rt_d);//初始化b) 设置定时器中断频率rt_uint32_t p = 10000;//设置频率rt_d->control(rt_d,HWTIMER_CTRL_FREQ_SET,(void)(&p));//调用控制函数进行频率设置,注意指针强转c) 打开定时器rt_d->open(rt_d,0);//最后一个参数没用d) 设置计时器定时时间rt_hwtimerval_t timeval;//这个结构体用于设置定时器计时时间timeval.sec = 2;//sec成员是多少秒timeval.usec = 0;//usec成员是多少微秒rt_d->write(rt_d,0,(void*)&timeval,sizeof(timeval));//使用写函数设置计时时间e) 挂回调函数rt_d->rx_indicate = &timer6_callback;//这个回调函数在计时时间到了以后会被自动调用
b) 设置定时器中断频率
d) 设置计时器定时时间
请参考官方文档:https://www.rt-thread.org/document/site/programming-manual/device/hwtimer/hwtimer/