[分享] 【填坑】【RT-Thread驱动开发】硬件定时器
2869 查看
12 回复
 楼主 | 发布于 2018-11-08 | 只看楼主
分享到:

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);

 

获取定时器计数值函数:这个函数用于获取TIM6Counter值(似乎没啥用)

函数原型:rt_uint32_t (*count_get)(struct rt_hwtimer_device *timer);

 

控制函数:用于对定时器进行控制,控制功能主要是设置定时器中断频率。

函数原型: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);

//配置TIM6NVIC 这里省略具体配置过程

NVIC_Timer6_Config();}

c)      启动函数实现方法

这个函数中的cntmode两个参数分别用于设置定时器的计数值和运行模式,这个计数值其实就是定时器的溢出值,当定时器计数到这个值的时候产生一次溢出中断请求。模式设置就两种,单次模式和连续模式,这里可以不用理会这个参数直接把定时器设置成连续模式,因为驱动框架中会在单次模式定时时间到了以后把定时器停掉(具体执行过程见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)      控制函数实现方法

这里函数中的cmdarg分别用于指定命令类型和命令参数。其中argvoid指针,这里需要根据不同命令进行强转成需要的数据类型。

这里我们只需要实现一种命令的实现,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;//这个回调函数在计时时间到了以后会被自动调用

本帖有更多资源,需 登录 才可以下载,没有帐号?立即 注册

(1 ) (1 )
回复 举报

回复于 2018-11-09 沙发

感谢分享;

(0 )
评论 (0) 举报

回复于 2018-11-09 2#

感谢分享,有需要机械臂,机器人,AGV小车的请联系我哦,均可定制。微信:Y816151

(0 )
评论 (0) 举报

回复于 2018-11-10 3#

支持下,谢谢分享~
(0 )
评论 (0) 举报

回复于 2018-11-10 4#

感谢分享
(0 )
评论 (0) 举报

回复于 2018-11-10 5#

感谢分享
(0 )
评论 (0) 举报

回复于 2018-11-14 6#

感谢分享!
(0 )
评论 (0) 举报

回复于 2018-11-17 7#

支持下,谢谢分享!
(0 )
评论 (0) 举报

回复于 2018-11-17 8#

感谢分享;
(0 )
评论 (0) 举报

回复于 2018-11-17 9#

感谢分享;
(0 )
评论 (0) 举报

回复于 2018-11-18 10#

不错,感谢分享;
(0 )
评论 (0) 举报
发表回复
0/3000





举报

请选择举报类别

  • 广告垃圾
  • 违规内容
  • 恶意灌水
  • 重复发帖

全部板块

返回顶部