创建基于F411的芯片空工程。
启用hwtimer:
拷贝以下三个文件到drivers目录,路径分别为
C:\RT-ThreadStudio\download\rt-thread-sdk\rt-thread-src\v4.0.2\bsp\stm32\libraries\HAL_Drivers\config\f4
和 C:\RT-ThreadStudio\download\rt-thread-sdk\rt-thread-src\v4.0.2\bsp\stm32\libraries\HAL_Drivers
。
注释掉drv_hwtimer.c
中的#include "drv_config.h"
。
取消注释stm32f4xx_hal_conf.h
中的#define HAL_TIM_MODULE_ENABLED
。
向tim_config.h
添加#include
。
向drv_hwtimer.c
添加#include
。
向drv_hwtimer.c
添加#include
。
在board.h中定义需要使用的定时器:
根据要使用的定时器,修改tim_config.h
:
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-12-11 zylx first version
*/
#ifndef __TIM_CONFIG_H__
#define __TIM_CONFIG_H__
#include
#include
#ifdef __cplusplus
extern "C" {
#endif
#ifndef TIM_DEV_INFO_CONFIG
#define TIM_DEV_INFO_CONFIG \
{ \
.maxfreq = 1000000, \
.minfreq = 3000, \
.maxcnt = 0xFFFF, \
.cntmode = HWTIMER_CNTMODE_UP, \
}
#endif /* TIM_DEV_INFO_CONFIG */
#ifdef BSP_USING_TIM1
#ifndef TIM1_CONFIG
#define TIM1_CONFIG \
{ \
.tim_handle.Instance = TIM1, \
.tim_irqn = TIM1_UP_TIM10_IRQn, \
.name = "timer1", \
}
#endif /* TIM11_CONFIG */
#endif /* BSP_USING_TIM11 */
#ifdef BSP_USING_TIM2
#ifndef TIM2_CONFIG
#define TIM2_CONFIG \
{ \
.tim_handle.Instance = TIM2, \
.tim_irqn = TIM2_IRQn, \
.name = "timer2", \
}
#endif /* TIM11_CONFIG */
#endif /* BSP_USING_TIM11 */
#ifdef BSP_USING_TIM3
#ifndef TIM3_CONFIG
#define TIM3_CONFIG \
{ \
.tim_handle.Instance = TIM3, \
.tim_irqn = TIM3_IRQn, \
.name = "timer3", \
}
#endif /* TIM11_CONFIG */
#endif /* BSP_USING_TIM11 */
#ifdef BSP_USING_TIM4
#ifndef TIM4_CONFIG
#define TIM4_CONFIG \
{ \
.tim_handle.Instance = TIM4, \
.tim_irqn = TIM4_IRQn, \
.name = "timer4", \
}
#endif /* TIM11_CONFIG */
#endif /* BSP_USING_TIM11 */
#ifdef BSP_USING_TIM5
#ifndef TIM5_CONFIG
#define TIM5_CONFIG \
{ \
.tim_handle.Instance = TIM5, \
.tim_irqn = TIM5_IRQn, \
.name = "timer5", \
}
#endif /* TIM11_CONFIG */
#endif /* BSP_USING_TIM11 */
#ifdef BSP_USING_TIM9
#ifndef TIM9_CONFIG
#define TIM9_CONFIG \
{ \
.tim_handle.Instance = TIM9, \
.tim_irqn = TIM1_BRK_TIM9_IRQn, \
.name = "timer9", \
}
#endif /* TIM11_CONFIG */
#endif /* BSP_USING_TIM11 */
#ifdef BSP_USING_TIM10
#ifndef TIM10_CONFIG
#define TIM10_CONFIG \
{ \
.tim_handle.Instance = TIM10, \
.tim_irqn = TIM1_UP_TIM10_IRQn, \
.name = "timer10", \
}
#endif /* TIM11_CONFIG */
#endif /* BSP_USING_TIM11 */
#ifdef BSP_USING_TIM11
#ifndef TIM11_CONFIG
#define TIM11_CONFIG \
{ \
.tim_handle.Instance = TIM11, \
.tim_irqn = TIM1_TRG_COM_TIM11_IRQn, \
.name = "timer11", \
}
#endif /* TIM11_CONFIG */
#endif /* BSP_USING_TIM11 */
#ifdef __cplusplus
}
#endif
#endif /* __TIM_CONFIG_H__ */
在drv_hwtimer.h
中增加CubeMX生成的底层代码:
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
if (htim_base->Instance == TIM1)
{
__HAL_RCC_TIM1_CLK_ENABLE();
}
else if (htim_base->Instance == TIM2)
{
__HAL_RCC_TIM2_CLK_ENABLE();
}
else if (htim_base->Instance == TIM3)
{
__HAL_RCC_TIM3_CLK_ENABLE();
}
else if (htim_base->Instance == TIM4)
{
__HAL_RCC_TIM4_CLK_ENABLE();
}
else if (htim_base->Instance == TIM5)
{
__HAL_RCC_TIM5_CLK_ENABLE();
}
else if (htim_base->Instance == TIM9)
{
__HAL_RCC_TIM9_CLK_ENABLE();
}
else if (htim_base->Instance == TIM10)
{
__HAL_RCC_TIM10_CLK_ENABLE();
}
else if (htim_base->Instance == TIM11)
{
__HAL_RCC_TIM11_CLK_ENABLE();
}
}
修改drv_hwtimer.h
中HAL_TIM_PeriodElapsedCallback
,为使用的定时器添加回调函数:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
#ifdef BSP_USING_TIM1
if (htim->Instance == TIM1)
{
rt_device_hwtimer_isr(&stm32_hwtimer_obj[TIM1_INDEX].time_device);
}
#endif
#ifdef BSP_USING_TIM2
if (htim->Instance == TIM2)
{
rt_device_hwtimer_isr(&stm32_hwtimer_obj[TIM2_INDEX].time_device);
}
#endif
#ifdef BSP_USING_TIM3
if (htim->Instance == TIM3)
{
rt_device_hwtimer_isr(&stm32_hwtimer_obj[TIM3_INDEX].time_device);
}
#endif
#ifdef BSP_USING_TIM4
if (htim->Instance == TIM4)
{
rt_device_hwtimer_isr(&stm32_hwtimer_obj[TIM4_INDEX].time_device);
}
#endif
#ifdef BSP_USING_TIM5
if (htim->Instance == TIM5)
{
rt_device_hwtimer_isr(&stm32_hwtimer_obj[TIM5_INDEX].time_device);
}
#endif
#ifdef BSP_USING_TIM9
if (htim->Instance == TIM9)
{
rt_device_hwtimer_isr(&stm32_hwtimer_obj[TIM9_INDEX].time_device);
}
#endif
#ifdef BSP_USING_TIM10
if (htim->Instance == TIM10)
{
rt_device_hwtimer_isr(&stm32_hwtimer_obj[TIM10_INDEX].time_device);
}
#endif
#ifdef BSP_USING_TIM11
if (htim->Instance == TIM11)
{
rt_device_hwtimer_isr(&stm32_hwtimer_obj[TIM11_INDEX].time_device);
}
#endif
}
添加中断函数入口:
#ifdef BSP_USING_TIM2
void TIM2_IRQHandler(void)
{
/* enter interrupt */
rt_interrupt_enter();
HAL_TIM_IRQHandler(&stm32_hwtimer_obj[TIM2_INDEX].tim_handle);
/* leave interrupt */
rt_interrupt_leave();
}
#endif
#ifdef BSP_USING_TIM3
void TIM3_IRQHandler(void)
{
/* enter interrupt */
rt_interrupt_enter();
HAL_TIM_IRQHandler(&stm32_hwtimer_obj[TIM3_INDEX].tim_handle);
/* leave interrupt */
rt_interrupt_leave();
}
#endif
#ifdef BSP_USING_TIM4
void TIM4_IRQHandler(void)
{
/* enter interrupt */
rt_interrupt_enter();
HAL_TIM_IRQHandler(&stm32_hwtimer_obj[TIM4_INDEX].tim_handle);
/* leave interrupt */
rt_interrupt_leave();
}
#endif
#ifdef BSP_USING_TIM5
void TIM5_IRQHandler(void)
{
/* enter interrupt */
rt_interrupt_enter();
HAL_TIM_IRQHandler(&stm32_hwtimer_obj[TIM5_INDEX].tim_handle);
/* leave interrupt */
rt_interrupt_leave();
}
#endif
#ifdef BSP_USING_TIM11
void TIM1_BRK_TIM9_IRQHandler(void)
{
/* enter interrupt */
rt_interrupt_enter();
HAL_TIM_IRQHandler(&stm32_hwtimer_obj[TIM9_INDEX].tim_handle);
/* leave interrupt */
rt_interrupt_leave();
}
#endif
#if defined( BSP_USING_TIM1) ||defined(BSP_USING_TIM10)
void TIM1_UP_TIM10_IRQHandler(void)
{
/* enter interrupt */
rt_interrupt_enter();
#ifdef BSP_USING_TIM1
if (__HAL_TIM_GET_FLAG(&stm32_hwtimer_obj[TIM1_INDEX].tim_handle,TIM_IT_UPDATE) != RESET)
HAL_TIM_IRQHandler(&stm32_hwtimer_obj[TIM1_INDEX].tim_handle);
#endif
#ifdef BSP_USING_TIM10
if (__HAL_TIM_GET_FLAG(&stm32_hwtimer_obj[TIM10_INDEX].tim_handle,TIM_IT_UPDATE) != RESET)
HAL_TIM_IRQHandler(&stm32_hwtimer_obj[TIM10_INDEX].tim_handle);
#endif
/* leave interrupt */
rt_interrupt_leave();
}
#endif
#ifdef BSP_USING_TIM11
void TIM1_TRG_COM_TIM11_IRQHandler(void)
{
/* enter interrupt */
rt_interrupt_enter();
HAL_TIM_IRQHandler(&stm32_hwtimer_obj[TIM11_INDEX].tim_handle);
/* leave interrupt */
rt_interrupt_leave();
}
#endif
新建一个usertimer.c
,添加示例代码,通过msh调用即可看到效果:
/*
* Copyright (c) 2006-2020, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2020-03-23 ShineRoyal the first version
*/
#include
#include
#include
#define DBG_TAG "user"
#define DBG_LVL DBG_LOG
#include
#define LED0_PIN GET_PIN(C, 0)
#define LED1_PIN GET_PIN(C, 1)
#define LED2_PIN GET_PIN(C, 2)
#define LED3_PIN GET_PIN(C, 3)
#define LED4_PIN GET_PIN(C, 4)
#define LED5_PIN GET_PIN(C, 5)
#define LED6_PIN GET_PIN(C, 6)
#define LED7_PIN GET_PIN(C, 7)
static int rt_hw_pin_init()
{
rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);
rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT);
rt_pin_mode(LED2_PIN, PIN_MODE_OUTPUT);
rt_pin_mode(LED3_PIN, PIN_MODE_OUTPUT);
rt_pin_mode(LED4_PIN, PIN_MODE_OUTPUT);
rt_pin_mode(LED5_PIN, PIN_MODE_OUTPUT);
rt_pin_mode(LED6_PIN, PIN_MODE_OUTPUT);
rt_pin_mode(LED7_PIN, PIN_MODE_OUTPUT);
return 0;
}
INIT_BOARD_EXPORT(rt_hw_pin_init);
/* 定时器超时回调函数 */
static rt_err_t timeout_cb1(rt_device_t dev, rt_size_t size)
{
static int i = 0;
i++;
if (i % 2)
rt_pin_write(LED0_PIN, PIN_LOW);
else
rt_pin_write(LED0_PIN, PIN_HIGH);
LOG_D("%d",i);
return 0;
}
/* 定时器超时回调函数 */
static rt_err_t timeout_cb2(rt_device_t dev, rt_size_t size)
{
static int i = 0;
i++;
if (i % 2)
rt_pin_write(LED1_PIN, PIN_LOW);
else
rt_pin_write(LED1_PIN, PIN_HIGH);
return 0;
}
/* 定时器超时回调函数 */
static rt_err_t timeout_cb3(rt_device_t dev, rt_size_t size)
{
static int i = 0;
i++;
if (i % 2)
rt_pin_write(LED2_PIN, PIN_LOW);
else
rt_pin_write(LED2_PIN, PIN_HIGH);
return 0;
}
/* 定时器超时回调函数 */
static rt_err_t timeout_cb4(rt_device_t dev, rt_size_t size)
{
static int i = 0;
i++;
if (i % 2)
rt_pin_write(LED3_PIN, PIN_LOW);
else
rt_pin_write(LED3_PIN, PIN_HIGH);
return 0;
}
/* 定时器超时回调函数 */
static rt_err_t timeout_cb5(rt_device_t dev, rt_size_t size)
{
static int i = 0;
i++;
if (i % 2)
rt_pin_write(LED4_PIN, PIN_LOW);
else
rt_pin_write(LED4_PIN, PIN_HIGH);
return 0;
}
/* 定时器超时回调函数 */
static rt_err_t timeout_cb9(rt_device_t dev, rt_size_t size)
{
static int i = 0;
i++;
if (i % 2)
rt_pin_write(LED5_PIN, PIN_LOW);
else
rt_pin_write(LED5_PIN, PIN_HIGH);
return 0;
}
/* 定时器超时回调函数 */
static rt_err_t timeout_cb10(rt_device_t dev, rt_size_t size)
{
static int i = 0;
i++;
if (i % 2)
rt_pin_write(LED6_PIN, PIN_LOW);
else
rt_pin_write(LED6_PIN, PIN_HIGH);
return 0;
}
/* 定时器超时回调函数 */
static rt_err_t timeout_cb11(rt_device_t dev, rt_size_t size)
{
static int i = 0;
i++;
if (i % 2)
rt_pin_write(LED7_PIN, PIN_LOW);
else
rt_pin_write(LED7_PIN, PIN_HIGH);
return 0;
}
static int hw1timer_sample(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
rt_hwtimerval_t timeout_s; /* 定时器超时值 */
rt_device_t hw_dev = RT_NULL; /* 定时器设备句柄 */
rt_hwtimer_mode_t mode; /* 定时器模式 */
/* 查找定时器设备 */
hw_dev = rt_device_find("timer1");
/* 以读写方式打开设备 */
ret = rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
/* 设置超时回调函数 */
rt_device_set_rx_indicate(hw_dev, timeout_cb1);
/* 设置模式为周期性定时器 */
mode = HWTIMER_MODE_PERIOD;
ret = rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
/* 设置定时器超时值为5s并启动定时器 */
timeout_s.sec = 1; /* 秒 */
timeout_s.usec = 0; /* 微秒 */
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;
}
return ret;
}
MSH_CMD_EXPORT(hw1timer_sample, hw1timer_sample);
static int hw2timer_sample(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
rt_hwtimerval_t timeout_s; /* 定时器超时值 */
rt_device_t hw_dev = RT_NULL; /* 定时器设备句柄 */
rt_hwtimer_mode_t mode; /* 定时器模式 */
/* 查找定时器设备 */
hw_dev = rt_device_find("timer2");
/* 以读写方式打开设备 */
ret = rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
/* 设置超时回调函数 */
rt_device_set_rx_indicate(hw_dev, timeout_cb2);
/* 设置模式为周期性定时器 */
mode = HWTIMER_MODE_PERIOD;
ret = rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
/* 设置定时器超时值为5s并启动定时器 */
timeout_s.sec = 1; /* 秒 */
timeout_s.usec = 0; /* 微秒 */
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;
}
return ret;
}
MSH_CMD_EXPORT(hw2timer_sample, hw2timer_sample);
static int hw3timer_sample(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
rt_hwtimerval_t timeout_s; /* 定时器超时值 */
rt_device_t hw_dev = RT_NULL; /* 定时器设备句柄 */
rt_hwtimer_mode_t mode; /* 定时器模式 */
/* 查找定时器设备 */
hw_dev = rt_device_find("timer3");
/* 以读写方式打开设备 */
ret = rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
/* 设置超时回调函数 */
rt_device_set_rx_indicate(hw_dev, timeout_cb3);
/* 设置模式为周期性定时器 */
mode = HWTIMER_MODE_PERIOD;
ret = rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
/* 设置定时器超时值为5s并启动定时器 */
timeout_s.sec = 1; /* 秒 */
timeout_s.usec = 0; /* 微秒 */
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;
}
return ret;
}
MSH_CMD_EXPORT(hw3timer_sample, hw3timer_sample);
static int hw4timer_sample(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
rt_hwtimerval_t timeout_s; /* 定时器超时值 */
rt_device_t hw_dev = RT_NULL; /* 定时器设备句柄 */
rt_hwtimer_mode_t mode; /* 定时器模式 */
/* 查找定时器设备 */
hw_dev = rt_device_find("timer4");
/* 以读写方式打开设备 */
ret = rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
/* 设置超时回调函数 */
rt_device_set_rx_indicate(hw_dev, timeout_cb4);
/* 设置模式为周期性定时器 */
mode = HWTIMER_MODE_PERIOD;
ret = rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
/* 设置定时器超时值为5s并启动定时器 */
timeout_s.sec = 1; /* 秒 */
timeout_s.usec = 0; /* 微秒 */
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;
}
return ret;
}
MSH_CMD_EXPORT(hw4timer_sample, hw4timer_sample);
static int hw5timer_sample(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
rt_hwtimerval_t timeout_s; /* 定时器超时值 */
rt_device_t hw_dev = RT_NULL; /* 定时器设备句柄 */
rt_hwtimer_mode_t mode; /* 定时器模式 */
/* 查找定时器设备 */
hw_dev = rt_device_find("timer5");
/* 以读写方式打开设备 */
ret = rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
/* 设置超时回调函数 */
rt_device_set_rx_indicate(hw_dev, timeout_cb5);
/* 设置模式为周期性定时器 */
mode = HWTIMER_MODE_PERIOD;
ret = rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
/* 设置定时器超时值为5s并启动定时器 */
timeout_s.sec = 1; /* 秒 */
timeout_s.usec = 0; /* 微秒 */
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;
}
return ret;
}
MSH_CMD_EXPORT(hw5timer_sample, hw5timer_sample);
static int hw9timer_sample(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
rt_hwtimerval_t timeout_s; /* 定时器超时值 */
rt_device_t hw_dev = RT_NULL; /* 定时器设备句柄 */
rt_hwtimer_mode_t mode; /* 定时器模式 */
/* 查找定时器设备 */
hw_dev = rt_device_find("timer9");
/* 以读写方式打开设备 */
ret = rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
/* 设置超时回调函数 */
rt_device_set_rx_indicate(hw_dev, timeout_cb9);
/* 设置模式为周期性定时器 */
mode = HWTIMER_MODE_PERIOD;
ret = rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
/* 设置定时器超时值为5s并启动定时器 */
timeout_s.sec = 1; /* 秒 */
timeout_s.usec = 0; /* 微秒 */
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;
}
return ret;
}
MSH_CMD_EXPORT(hw9timer_sample, hw9timer_sample);
static int hw10timer_sample(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
rt_hwtimerval_t timeout_s; /* 定时器超时值 */
rt_device_t hw_dev = RT_NULL; /* 定时器设备句柄 */
rt_hwtimer_mode_t mode; /* 定时器模式 */
/* 查找定时器设备 */
hw_dev = rt_device_find("timer10");
/* 以读写方式打开设备 */
ret = rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
/* 设置超时回调函数 */
rt_device_set_rx_indicate(hw_dev, timeout_cb10);
/* 设置模式为周期性定时器 */
mode = HWTIMER_MODE_PERIOD;
ret = rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
/* 设置定时器超时值为5s并启动定时器 */
timeout_s.sec = 1; /* 秒 */
timeout_s.usec = 0; /* 微秒 */
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;
}
return ret;
}
MSH_CMD_EXPORT(hw10timer_sample, hw10timer_sample);
static int hw11timer_sample(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
rt_hwtimerval_t timeout_s; /* 定时器超时值 */
rt_device_t hw_dev = RT_NULL; /* 定时器设备句柄 */
rt_hwtimer_mode_t mode; /* 定时器模式 */
/* 查找定时器设备 */
hw_dev = rt_device_find("timer11");
/* 以读写方式打开设备 */
ret = rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR);
/* 设置超时回调函数 */
rt_device_set_rx_indicate(hw_dev, timeout_cb11);
/* 设置模式为周期性定时器 */
mode = HWTIMER_MODE_PERIOD;
ret = rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode);
/* 设置定时器超时值为5s并启动定时器 */
timeout_s.sec = 1; /* 秒 */
timeout_s.usec = 0; /* 微秒 */
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;
}
return ret;
}
MSH_CMD_EXPORT(hw11timer_sample, hw11timer_sample);
注意,定时器1的定时会快一倍(这里是1s的间隔,但定时器1会500ms进一次中断),尚不清楚是rt-thread的库造成的还是硬件本身时钟相关的原因,后续在研究。
完整工程下载
第二天找到了具体原因,查看RT-Thread Studio部分定时器时钟不正确的解决方案