一、TIMER概述
定时器分类:基本定时器、通用定时器(输出比较、输入捕获)、高级定时器(PWM互补输出、死区及刹车功能);
时基单元:
计数器:16位计数器,从0计数累加到自动重装载计数值产生溢出,从0重新开始计数;
预分频器:对时钟源进行分频,降低计数频率;
自动重装载寄存器:定时器计数周期。
输出比较:
强制输出模式:通用定时器输出直接由软件将输出比较信号强制设置为有效电平、无效电平或输出不变;
比较输出模式:根据捕获/比较寄存器和计数器比较结果输出为有效电平、无效电平或者电平翻转;
PWM输出模式:根据捕获/比较寄存器和计数器比较结果输出为有效电平、无效电平。
输入捕获:设置捕获寄存器,通过边沿中断判断状态值;
二、HWTIMER定时回调
1、CubeMX驱动生成
根据使用定时器选择TIMX:
2、使用HWTIMER设备驱动程序
注意:支持哪些定时器需要查看drivers/include/config/tim_config.h,查找设备时根据.name匹配:
确保HAL库hal_conf.h功能模块使能(CubeMX会自动解除注释):
#define HAL_TIM_MODULE_ENABLED
添加tim.c硬件驱动到drivers/board.c:
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM3)
{
/* USER CODE BEGIN TIM3_MspInit 0 */
/* USER CODE END TIM3_MspInit 0 */
/* TIM3 clock enable */
__HAL_RCC_TIM3_CLK_ENABLE();
/* USER CODE BEGIN TIM3_MspInit 1 */
/* USER CODE END TIM3_MspInit 1 */
}
}
修改drivers/board.h:
#define BSP_USING_TIM
#ifdef BSP_USING_TIM
#define BSP_USING_TIM3
/*#define BSP_USING_TIM15*/
/*#define BSP_USING_TIM16*/
/*#define BSP_USING_TIM17*/
#endif
3、使用示例
#include <rtthread.h>
#include <rtdevice.h>
static rt_err_t timeoutCallBack(rt_device_t dev, rt_size_t size);
bool timerInit(char *devName, uint32_t sec, uint32_t usec)
{
// 查找定时器设备
rt_device_t devTimer = rt_device_find(devName);
if (devTimer == RT_NULL) {
rt_kprintf("rt_device_find[%s] failed!\n", devName);
return false;
}
// 打开定时器设备
rt_err_t ret = rt_device_open(devTimer, RT_DEVICE_OFLAG_RDWR);
if (ret != RT_EOK) {
rt_kprintf("rt_device_open[%s] failed!\n", devName);
return false;
}
// 设置超时回调函数
rt_device_set_rx_indicate(devTimer, timeoutCallBack);
// 设置计数频率(默认1Mhz或支持的最小计数频率)
rt_uint32_t freq = 10000;
ret = rt_device_control(devTimer, HWTIMER_CTRL_FREQ_SET, &freq);
if (ret != RT_EOK) {
rt_kprintf("rt_device_control[%s] freq failed!\n", devName);
return false;
}
// 设置模式为周期性定时器
rt_hwtimer_mode_t mode = HWTIMER_MODE_PERIOD;
ret = rt_device_control(devTimer, HWTIMER_CTRL_MODE_SET, &mode);
if (ret != RT_EOK) {
rt_kprintf("rt_device_control[%s] mode failed!\n", devName);
return false;
}
// 设置定时器超时值并启动定时器
rt_hwtimerval_t timeout_s;
timeout_s.sec = sec;
timeout_s.usec = usec;
if (rt_device_write(devTimer, 0, &timeout_s, sizeof(timeout_s)) != sizeof(timeout_s)) {
rt_kprintf("rt_device_write[%s] failed!\n", devName);
return false;
}
rt_kprintf("timer init ok!\n");
return true;
}
static rt_err_t timeoutCallBack(rt_device_t dev, rt_size_t size)
{
return RT_EOK;
}
三、HWTIMER输出比较
1、输出比较种类
强制输出模式:
强制输出模式是指通用定时器的输出直接由软件将输出比较信号(OxRef和OCx)强制设置为有效电平、无效电平或者输出不变。无需考虑捕获/比较寄存器(CCRx)和计数器(CNT)之间的任何比较结果;
比较输出模式:
比较输出模式的输出结果和捕获/比较寄存器(CCRx)和计数器(CNT)之间比较结果相关,当这两个值相等的时候,输出结果为有效电平、无效电平或者电平翻转;
PWM输出模式:
PWM输出模式是输出比较的一个特例,输出结果和捕获/比较寄存器(CCRx)和计数器(CNT)之间的比较值影响,输出结果为有效电平、无效电平。
注:有效电平和无效电平都可以是高电平也可以是低电平,但有效电平和无效电平两者一定是相反值。
2、输出比较框图
比较输出模式下,捕获/比较寄存器(CCRx)的值由用户自行设置,如果计数器(CNT)的值大于或等于捕获/比较寄存器(CCRx)中的值时,会产生对应的标志或者中断。
3、PWM输出模式概述
PWM是脉冲宽度调制的简称,在输出频率不变的情况下,通过调节其占空比(高电平占整个脉冲周期的比值),从而达到调节输出的目的。
通用定时器PWM模式可以产生一个信号,这个信号的频率周期是由自动重装载寄存器中的值决定,信号的占空比则由捕获/比较寄存器中的值决定。
PWM输出模式应用:
控制灯光:七彩灯(RGB灯)、灯光亮度;
控制电机:直流电机、步进电机、无刷电机;
红外遥控:红外发射;
电池充电:控制充电电流。
4、CubeMX驱动生成
查找手册查看GPIO支持复用定时器输出比较通道:
or:
注意:支持哪些定时器需要查看drivers/include/config/pwm_config.h,查找设备时根据.name匹配,如果默认没有则自定义添加:
自定义添加示例:
#ifdef BSP_USING_PWM14
#ifndef PWM14_CONFIG
#define PWM14_CONFIG \
{ \
.tim_handle.Instance = TIM14, \
.name = "pwm14", \
.channel = 0 \
}
#endif /* PWM14_CONFIG */
#endif /* BSP_USING_PWM14 */
如果.channel不指定且drivers/drv_pwm.c中未预定义通道,则不起作用(注意位移不要搞错):
5、使用PWM设备驱动程序
确保HAL库hal_conf.h功能模块使能(CubeMX会自动解除注释):
#define HAL_TIM_MODULE_ENABLED
添加tim.c硬件驱动到drivers/board.c:
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM3)
{
/* USER CODE BEGIN TIM3_MspInit 0 */
/* USER CODE END TIM3_MspInit 0 */
/* TIM3 clock enable */
__HAL_RCC_TIM3_CLK_ENABLE();
/* USER CODE BEGIN TIM3_MspInit 1 */
/* USER CODE END TIM3_MspInit 1 */
}
else if(tim_baseHandle->Instance==TIM14)
{
/* USER CODE BEGIN TIM14_MspInit 0 */
/* USER CODE END TIM14_MspInit 0 */
/* TIM14 clock enable */
__HAL_RCC_TIM14_CLK_ENABLE();
/* USER CODE BEGIN TIM14_MspInit 1 */
/* USER CODE END TIM14_MspInit 1 */
}
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(timHandle->Instance==TIM3)
{
/* USER CODE BEGIN TIM3_MspPostInit 0 */
/* USER CODE END TIM3_MspPostInit 0 */
__HAL_RCC_GPIOB_CLK_ENABLE();
/**TIM3 GPIO Configuration
PB0 ------> TIM3_CH3
*/
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USER CODE BEGIN TIM3_MspPostInit 1 */
/* USER CODE END TIM3_MspPostInit 1 */
}
... ...
}
修改drivers/board.h:
/*#define BSP_USING_PWM1*/
/*#define BSP_USING_PWM2*/
/*#define BSP_USING_PWM3*/
#define BSP_USING_PWM14
#define BSP_USING_PWM14_CH1
6、使用示例
#include <rtthread.h>
#include <rtdevice.h>
struct rt_device_pwm *pwmdev;
bool pwmInit(char *devName, int channel, rt_uint32_t period, rt_uint32_t pulse)
{
pwmdev = (struct rt_device_pwm *)rt_device_find(devName);
if (pwmdev == RT_NULL) {
rt_kprintf("rt_device_find[%s] failed!\n", devName);
return false;
}
rt_pwm_set(pwmdev, channel, period, pulse);
rt_pwm_enable(pwmdev, channel);
return true;
}
四、HWTIMER输入捕获
1、输入捕获概述
通用定时器输入捕获用于对输入信号脉冲宽度测量(测量时钟脉冲信号的周期以及时钟脉冲信号的占空比)。
输入捕获框图:
总结:通过检测TIMx_CHx边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)时,将当前定时器的计数值(TIMx_CNT)保存到对应通道的捕获/比较寄存器,完成一次捕获。
2、CubeMX驱动生成
定时器通道配置:
中断使能:
3、 使用示例
#include <rtthread.h>
#include <rtdevice.h>
#include "tim.h"
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
// 中断服务函数
// 默认生成到cubemx/Src/stm32f4xx_it.c,由于该文件未添加到SConscript,所以单独摘出来
void TIM3_IRQHandler(void)
{
/* USER CODE BEGIN TIM3_IRQn 0 */
/* USER CODE END TIM3_IRQn 0 */
HAL_TIM_IRQHandler(&htim3);
/* USER CODE BEGIN TIM3_IRQn 1 */
/* USER CODE END TIM3_IRQn 1 */
}
// 输入捕获中断回调函数
uint16_t last_capture = 0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
// 周期计算 - 100Hz的方波意味着每个周期的时间应该是10ms(10000微秒)
if (htim->Instance == TIM3 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
uint16_t capture = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
uint16_t diff = (capture >= last_capture) ? (capture - last_capture) : ((0xFFFF - last_capture) + capture + 1);
last_capture = capture;
rt_kprintf("Capture: %d, Diff: %d\n", capture, diff);
}
}
int main(void)
{
// TIM3初始化
MX_TIM3_Init();
// TIM3_CH1中断使能
if (HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1) != HAL_OK) {
rt_kprintf("HAL_TIM_IC_Start_IT error!\n");
// 启动错误处理
Error_Handler();
}
while (1) {
rt_kprintf("status: %d\n", 1);
rt_thread_mdelay(1000);
}
return RT_EOK;
}
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/dxf1971738522/article/details/146058720
|