NUCLEO-F411RE RT-Thread 体验 (8) - GCC环境 TIM定时器驱动移植与基本使用
驱动移植
定时器驱动文件位于drv_hwtimer.c中,对应components层的文件位于rt-thread/components/drivers/hwtimer/hwtimer.c中。
修改Makefile,将其编译进去。
在rtconfig.h中增加TIM的配置
在RT-Thread-basic/Core/Inc/stm32f4xx_hal_conf.h使能HAL_TIM_MODULE_ENABLED
修改RT-Thread-basic/Core/Src/stm32f4xx_hal_msp.c
在HAL_TIM_Base_MspInit增加Tim 时钟的初始化。这个函数是个WEAK函数,在HAL_TIM_Base_Init中被调用。而HAL_TIM_Base_Init又在drv_hwtimer.c中的timer_init被调用,所以这个函数一定要添加。
修改RT-Thread-basic/libraries/HAL_Drivers/config/f4/tim_config.h
查看是否有TIM3 的相关配置。如果没有,将其添加。
编译查看设备是否生成
测试APP的编写
用的是官方的demo修改而来。
hwtimer_sample on 定时器开启, LED快速闪烁
hwtimer_sample off 定时器关闭,LED 熄灭
其中freq = 10000,其实设置timer的prescaler = 100M/10000= 10000-1。
也就是计时1s需要计数10000次,一次0.1ms。单次计时在0.1ms-1ms之间
,但是hrtimer.c timeout_calc函数里做了一个转换,可以计时多余1s。
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-11-30 misonyo first implementation.
*/
/*
* 程序清单:这是一个 hwtimer 设备使用例程
* 例程导出了 hwtimer_sample 命令到控制终端
* 命令调用格式:hwtimer_sample
* 程序功能:硬件定时器超时回调函数周期性10ms去改变LED的状态。
*/
#include <rtthread.h>
#include <rtdevice.h>
#include "led.h"
#ifdef BSP_USING_TIM3
#define HWTIMER_DEV_NAME "timer3" /* 定时器名称 */
/* 定时器超时回调函数 */
static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
{
led_toggle();
return 0;
}
rt_device_t hw_dev = RT_NULL; /* 定时器设备句柄 */
static int hwtimer_sample(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
rt_hwtimerval_t timeout_s; /* 定时器超时值 */
rt_hwtimer_mode_t mode; /* 定时器模式 */
rt_uint32_t freq = 10000; /* 计数频率 */
if (argc == 2)
{
if (!rt_strncmp(argv[1],"on",2))
{
/* 查找定时器设备 */
hw_dev = rt_device_find(HWTIMER_DEV_NAME);
if (hw_dev == RT_NULL)
{
rt_kprintf("hwtimer sample run failed! can't find %s device!\n", HWTIMER_DEV_NAME);
return RT_ERROR;
}
/* 以读写方式打开设备 */
ret = rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
if (ret != RT_EOK)
{
rt_kprintf("open %s device failed!\n", HWTIMER_DEV_NAME);
return ret;
}
/* 设置超时回调函数 */
rt_device_set_rx_indicate(hw_dev, timeout_cb);
/* 设置计数频率(默认1Mhz或支持的最小计数频率) */
ret = rt_device_control(hw_dev, HWTIMER_CTRL_FREQ_SET, &freq);
if (ret != RT_EOK)
{
rt_kprintf("set frequency failed! ret is :%d\n", ret);
return ret;
}
/* 设置模式为周期性定时器 */
mode = HWTIMER_MODE_PERIOD;
ret = rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
if (ret != RT_EOK)
{
rt_kprintf("set mode failed! ret is :%d\n", ret);
return ret;
}
/* 设置定时器超时值为5s并启动定时器 */
timeout_s.sec = 0; /* 秒 */
timeout_s.usec = 20000; /* 微秒 */
if (rt_device_write(hw_dev, 0, &timeout_s, sizeof(timeout_s)) != sizeof(timeout_s))
{
rt_kprintf("set timeout value failed\n");
return RT_ERROR;
}
}
else if (!rt_strncmp(argv[1],"off",3))
{
mode = HWTIMER_MODE_PERIOD;
ret = rt_device_control(hw_dev, HWTIMER_CTRL_STOP, &mode);
rt_device_close(hw_dev);
}
else
{
rt_kprintf("Usage: hwtimer_sample on /off\n");
}
}
else
{
rt_kprintf("Usage: hwtimer_sample on /off\n");
}
return ret;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(hwtimer_sample, hwtimer sample);
#endif