Github
整个RTT学习过程中对工程的完善和开发都记录在Github上的一个项目中,项目还会随着学习的深入进行更新
项目URL:https://github.com/HITLIVING/-RT-Thread-.git
读者可以通过Pull下来整个项目进一步了解
对内部定时器驱动的实现需要格外关注drv_timer.cdrv_timer.h
两个文件
外设配置
STM32F1RCT6芯片
Cube
不需要外部引脚,这里只使用普通计时器,cube中仅开启定时器的时钟即可
STM32F1RCT6的TIM6和TIM7为普通计数器
Kconfig
设置定时器的驱动选项
menuconfig BSP_USING_TIMbool "Enable timer"default nselect RT_USING_HWTIMERif BSP_USING_TIMconfig BSP_USING_TIM6bool "Enable TIM6"default nconfig BSP_USING_TIM7bool "Enable TIM7"default nendif
Env
打开对TIM6和TIM7的驱动



使用 Scons 指令生成MDK5工程
MDK

即驱动配置成功
此时编译会报错TIM6和TIM7的初始化未定义
需要在 tim_config.h 文件中添加代码段
``
#ifdef BSP_USING_TIM6#ifndef TIM6_CONFIG#define TIM6_CONFIG \{ \.tim_handle.Instance = TIM6, \.tim_irqn = TIM6_IRQn, \.name = "timer6", \}#endif /* TIM6_CONFIG */#endif /* BSP_USING_TIM6 */#ifdef BSP_USING_TIM7#ifndef TIM7_CONFIG#define TIM7_CONFIG \{ \.tim_handle.Instance = TIM7, \.tim_irqn = TIM7_IRQn, \.name = "timer7", \}#endif /* TIM7_CONFIG */#endif /* BSP_USING_TIM7 */
设置定时器设备名称和中断等
同时需要检查是否有对应的中断服务函数,如果没有需要手动添加drv_hwtimer.c
#ifdef BSP_USING_TIM6void TIM6_IRQHandler(void){/* enter interrupt */rt_interrupt_enter();HAL_TIM_IRQHandler(&stm32_hwtimer_obj[TIM6_INDEX].tim_handle);/* leave interrupt */rt_interrupt_leave();}#endif#ifdef BSP_USING_TIM7void TIM7_IRQHandler(void){/* enter interrupt */rt_interrupt_enter();HAL_TIM_IRQHandler(&stm32_hwtimer_obj[TIM7_INDEX].tim_handle);/* leave interrupt */rt_interrupt_leave();}#endif
周期中断服务函数
#ifdef BSP_USING_TIM6if (htim->Instance == TIM6){rt_device_hwtimer_isr(&stm32_hwtimer_obj[TIM6_INDEX].time_device);}#endif#ifdef BSP_USING_TIM7if (htim->Instance == TIM7){rt_device_hwtimer_isr(&stm32_hwtimer_obj[TIM7_INDEX].time_device);}#endif
再编译即可通过,完成对Timer的驱动
接口函数
查找定时器设备
应用程序根据硬件定时器设备名称获取设备句柄,进而可以操作硬件定时器设备,查找设备函数如下所示:
rt_device_t rt_device_find(const char* name);
| 参数 | 描述 |
|---|---|
| name | 硬件定时器设备名称 |
| 返回 | —— |
| 定时器设备句柄 | 查找到对应设备将返回相应的设备句柄 |
| RT_NULL | 没有找到设备 |
打开定时器设备
通过设备句柄,应用程序可以打开设备。打开设备时,会检测设备是否已经初始化,没有初始化则会默认调用初始化接口初始化设备。通过如下函数打开设备:
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
| 参数 | 描述 |
|---|---|
| dev | 硬件定时器设备句柄 |
| oflags | 设备打开模式,一般以读写方式打开,即取值:RT_DEVICE_OFLAG_RDWR |
| 返回 | —— |
| RT_EOK | 设备打开成功 |
| 其他错误码 | 设备打开失败 |
设置超时回调函数
通过如下函数设置定时器超时回调函数,当定时器超时将会调用此回调函数:
rt_err_t rt_device_set_rx_indicate(rt_device_t dev,rt_err_t (*rx_ind)(rt_device_t dev,rt_size_t size))
| 参数 | 描述 |
|---|---|
| dev | 设备句柄 |
| rx_ind | 超时回调函数,由调用者提供 |
| 返回 | —— |
| RT_EOK | 成功 |
控制定时器设备
通过命令控制字,应用程序可以对硬件定时器设备进行配置,通过如下函数完成:
rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
| 参数 | 描述 |
|---|---|
| dev | 设备句柄 |
| cmd | 命令控制字 |
| arg | 控制的参数 |
| 返回 | —— |
| RT_EOK | 函数执行成功 |
| -RT_ENOSYS | 执行失败,dev 为空 |
| 其他错误码 | 执行失败 |
硬件定时器设备支持的命令控制字如下所示:
| 控制字 | 描述 |
|---|---|
| HWTIMER_CTRL_FREQ_SET | 设置计数频率 |
| HWTIMER_CTRL_STOP | 停止定时器 |
| HWTIMER_CTRL_INFO_GET | 获取定时器特征信息 |
| HWTIMER_CTRL_MODE_SET | 设置定时器模式 |
当使用第一个控制字HWTIMER_CTRL_FREQ_SET设置计数频率时:
定时器硬件及驱动支持设置计数频率的情况下设置频率才有效,一般使用驱动设置的默认频率即可。
当使用第三个控制字HWTIMER_CTRL_INFO_GET时:
获取定时器特征信息参数 arg 为指向结构体 struct rt_hwtimer_info 的指针,作为一个输出参数保存获取的信息。
当使用第四个关键字设置定时器模式时
参数 arg 可取如下值:
HWTIMER_MODE_ONESHOT 单次定时HWTIMER_MODE_PERIOD 周期性定时
设置定时器超时值
通过如下函数可以设置定时器的超时值:
rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);
| 参数 | 描述 |
|---|---|
| dev | 设备句柄 |
| pos | 写入数据偏移量,未使用,可取 0 值 |
| buffer | 指向定时器超时时间结构体的指针 |
| size | 超时时间结构体的大小 |
| 返回 | —— |
| 写入数据的实际大小 | |
| 0 | 失败 |
超时时间结构体原型如下所示:
typedef struct rt_hwtimerval{rt_int32_t sec; /* 秒 s */rt_int32_t usec; /* 微秒 us */} rt_hwtimerval_t;
获取定时器当前值
通过如下函数可以获取定时器当前值:
rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
| 参数 | 描述 |
|---|---|
| dev | 定时器设备句柄 |
| pos | 写入数据偏移量,未使用,可取 0 值 |
| buffer | 输出参数,指向定时器超时时间结构体的指针 |
| size | 超时时间结构体的大小 |
| 返回 | —— |
| 超时时间结构体的大小 | 成功 |
| 0 | 失败 |
关闭定时器设备
通过如下函数可以关闭定时器设备:
rt_err_t rt_device_close(rt_device_t dev);
| 参数 | 描述 |
|---|---|
| dev | 定时器设备句柄 |
| 返回 | —— |
| RT_EOK | 关闭设备成功 |
| -RT_ERROR | 设备已经完全关闭,不能重复关闭设备 |
| 其他错误码 | 关闭设备失败 |
关闭设备接口和打开设备接口需配对使用,
打开一次设备对应要关闭一次设备,这样设备才会被完全关闭,否则设备仍处于未关闭状态。
上机实验
#include <rtthread.h>#include <rtdevice.h>#include "drv_timer.h"/* 定时器超时回调函数 */static rt_err_t timer6_handle(rt_device_t dev, rt_size_t size){rt_kprintf("this is hwtimer timeout callback fucntion!\n");rt_kprintf("tick is :%d !\n", rt_tick_get());return 0;}rt_err_t timer0_init(void){rt_err_t ret = RT_EOK;rt_device_t timer6_dev = RT_NULL; /* 定时器设备句柄 *//* 查找定时器设备 */timer6_dev = rt_device_find("timer6");if (timer6_dev == RT_NULL){rt_kprintf("hwtimer sample run failed! can't find timer6 device!\n");return ret;}/* 以读写方式打开设备 */ret = rt_device_open(timer6_dev, RT_DEVICE_OFLAG_RDWR);if (ret != RT_EOK){rt_kprintf("open timer6 device failed!\n");return ret;}/* 设置超时回调函数 */rt_device_set_rx_indicate(timer6_dev, timer6_handle);/* 不修改定时器计时频率 *//* 设置模式为周期性定时器 */rt_hwtimer_mode_t mode = HWTIMER_MODE_PERIOD;ret = rt_device_control(timer6_dev, HWTIMER_CTRL_MODE_SET, &mode);if (ret != RT_EOK){rt_kprintf("timer6 set mode failed! ret is :%d\n", ret);return ret;}/* 设置定时器超时值*/rt_hwtimerval_t timeout_s;timeout_s.sec = 5; /* 秒 */timeout_s.usec = 0; /* 微秒 */if (rt_device_write(timer6_dev, 0, &timeout_s, sizeof(timeout_s)) != sizeof(timeout_s)){rt_kprintf("timer6 set timeout value failed\n");return RT_ERROR;}return ret;}
