- List item
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
stm32高级定时器PWM输入模式实验,原子教程笔记
一、工作原理
芯片手册上,高级定时器PWM输入模式下,只能采用ch1和ch2口子。当上升沿来的时候进入中断将进入到IC1中,同时看到TI1FP1触发从模式选择:复位模式,将计数器清零,这样随后下降沿来的时候,进入中断,将CCR2+1即得到一个周期高电平时间(CCR2 为输入捕获 2 事件 (IC2) 发生时的计数器值),总之上升沿复位比直接CCR2-CCR1简单一些,下一次上升沿时CCR1的计数也就是一个周期的时间。看图片右下角。
**
1. 时序图
**
可以看到第一次上升沿复位清零,IC1和IC2分别在不同边沿被捕获,后续上升沿不会复位,第二个上升沿CCR1+1就是周期,当然这里讲的是向上计数。
二、参数配置
1.HAL库函数
第一个结构体只用前2个,第二个结构体用前3个
其中输入触发源选择是选蓝色部分,极性选择是紫色不是是上升沿触发还是下降沿,这里选上升沿
三、实验代码
以下是代码
函数1:atim_timx_pwmin_chy_init
用来完成输入捕获到初始化配置
函数2:HAL_TIM_IC_MspInit
一般是gpio口,以及NVIC中断的配置
函数3:TIM8_CC_IRQHandler
单纯添加定时器的句柄作为参数
函数4:atim_timx_pwmin_chy_restart
重启输入捕获
函数5:HAL_TIM_IC_CaptureCallback
中断回调函数,进入中断后会调用读函数读寄存器的值
#include "./BSP/TIMER/atim.h"
TIM_HandleTypeDef g_timx_pwmin_chy_handle; /* 定时器x句柄 */
/* PWM输入状态(g_timxchy_cap_sta)
* 0,没有成功捕获.
* 1,已经成功捕获了
*/
uint8_t g_timxchy_pwmin_sta = 0; /* PWM输入状态 */
uint16_t g_timxchy_pwmin_psc = 0; /* PWM输入分频系数 */
uint32_t g_timxchy_pwmin_hval = 0; /* PWM的高电平脉宽 */
uint32_t g_timxchy_pwmin_cval = 0; /* PWM的周期宽度 */
/* PWM输入模式 初始化函数,采样时钟频率为72Mhz,精度约13.8ns */
void atim_timx_pwmin_chy_init(void)
{
TIM_SlaveConfigTypeDef slave_config = {0};
TIM_IC_InitTypeDef tim_ic_pwmin_chy = {0};
g_timx_pwmin_chy_handle.Instance = TIM8; /* 定时器8 */
g_timx_pwmin_chy_handle.Init.Prescaler = 0; /* 定时器预分频系数 */
g_timx_pwmin_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
g_timx_pwmin_chy_handle.Init.Period = 65535; /* 自动重装载值 */
HAL_TIM_IC_Init(&g_timx_pwmin_chy_handle);
/* 从模式配置,IT1触发更新 */
slave_config.SlaveMode = TIM_SLAVEMODE_RESET; /* 从模式:复位模式 */
slave_config.InputTrigger = TIM_TS_TI1FP1; /* 定时器输入触发源:TI1FP1 */
slave_config.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING; /* 上升沿检测 */
slave_config.TriggerFilter = 0; /* 不滤波 */
HAL_TIM_SlaveConfigSynchro(&g_timx_pwmin_chy_handle, &slave_config);
/* IC1捕获:上升沿触发TI1FP1 */
tim_ic_pwmin_chy.ICPolarity = TIM_ICPOLARITY_RISING; /* 上升沿检测 */
tim_ic_pwmin_chy.ICSelection = TIM_ICSELECTION_DIRECTTI; /* 选择输入端IC1映射到TI1 */
tim_ic_pwmin_chy.ICPrescaler = TIM_ICPSC_DIV1; /* 不分频 */
tim_ic_pwmin_chy.ICFilter = 0; /* 不滤波 */
HAL_TIM_IC_ConfigChannel(&g_timx_pwmin_chy_handle, &tim_ic_pwmin_chy, TIM_CHANNEL_1);
/* IC2捕获:下降沿触发TI1FP2 */
tim_ic_pwmin_chy.ICPolarity = TIM_ICPOLARITY_FALLING; /* 下降沿检测 */
tim_ic_pwmin_chy.ICSelection = TIM_ICSELECTION_INDIRECTTI; /* 选择输入端IC2映射到TI1 */
HAL_TIM_IC_ConfigChannel(&g_timx_pwmin_chy_handle, &tim_ic_pwmin_chy, TIM_CHANNEL_2);
HAL_TIM_IC_Start_IT(&g_timx_pwmin_chy_handle, TIM_CHANNEL_1);
HAL_TIM_IC_Start(&g_timx_pwmin_chy_handle, TIM_CHANNEL_2);
/*
在STM32的HAL(硬件抽象层)库中,HAL_TIM_IC_Start_IT 和 HAL_TIM_IC_Start 是与TIM(定时器)模块的输入捕获(Input Capture, IC)功能相关的函数。
HAL_TIM_IC_Start_IT:
功能:启动TIM输入捕获的中断。
当定时器计数器的值与预设的捕获值匹配时,这个函数会启动一个中断请求。这通常用于测量脉冲的宽度或频率。
当你希望使用中断来处理输入捕获事件时,你会使用这个函数。
注意:在调用这个函数之前,你需要配置好TIM输入捕获的相关参数,如预分频器、捕获通道、捕获极性等。
HAL_TIM_IC_Start:
功能:启动TIM输入捕获功能,但不启动中断。
与HAL_TIM_IC_Start_IT类似,这个函数也会启动输入捕获功能,但不会生成中断请求。
你可能需要在主循环或其他非中断上下文中定期检查TIM的输入捕获状态。
注意:同样,在调用这个函数之前,你需要配置好TIM输入捕获的相关参数。
简单来说,HAL_TIM_IC_Start_IT和HAL_TIM_IC_Start的主要区别在于是否启动中断。
如果你想要实时响应输入捕获事件,你应该使用HAL_TIM_IC_Start_IT;如果你希望在主循环中检查输入捕获状态,你可以使用HAL_TIM_IC_Start。
*/
}
/* 定时器 输入捕获 MSP初始化函数,输入捕获GPIO引脚配置 */
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM8)
{
GPIO_InitTypeDef gpio_init_struct = {0};
__HAL_RCC_TIM8_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
gpio_init_struct.Pin = GPIO_PIN_6;
gpio_init_struct.Mode = GPIO_MODE_AF_PP; //输入,推挽式复用
gpio_init_struct.Pull = GPIO_PULLDOWN; //下拉,也可以不用
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; //高速,输入不用设置
HAL_GPIO_Init(GPIOC, &gpio_init_struct);
/* TIM1/TIM8有独立的输入捕获中断服务函数 */
HAL_NVIC_SetPriority(TIM8_CC_IRQn, 1, 3);//开启了输入捕获中断
HAL_NVIC_EnableIRQ(TIM8_CC_IRQn);
}
}
/* 定时器8 输入捕获 中断服务函数,仅TIM1/TIM8有这个函数,其他普通定时器没有这个中断服务函数! */
void TIM8_CC_IRQHandler(void)
{
HAL_TIM_IRQHandler(&g_timx_pwmin_chy_handle); /* 定时器共用处理函数 */
}
/* PWM输入模式 重新启动捕获 */
void atim_timx_pwmin_chy_restart(void)
{
sys_intx_disable(); /* 关闭中断 */
g_timxchy_pwmin_sta = 0; /* 清零状态,重新开始检测 */
g_timxchy_pwmin_hval=0;
g_timxchy_pwmin_cval=0;
sys_intx_enable(); /* 打开中断 */
}
/* 定时器输入捕获中断处理回调函数 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM8)
{
if(g_timxchy_pwmin_sta == 0)
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
g_timxchy_pwmin_hval = HAL_TIM_ReadCapturedValue(&g_timx_pwmin_chy_handle, TIM_CHANNEL_2) + 1 + 1;
g_timxchy_pwmin_cval = HAL_TIM_ReadCapturedValue(&g_timx_pwmin_chy_handle, TIM_CHANNEL_1) + 1 + 1;
g_timxchy_pwmin_sta = 1;
}
}
}
}
以下代码是PWM输出比较代码,用于输出pwm
#include "./BSP/TIMER/gtim.h"
TIM_HandleTypeDef g_timx_pwm_chy_handle;
/* 通用定时器PWM输出初始化函数 */
void gtim_timx_pwm_chy_init(uint16_t arr, uint16_t psc)
{
TIM_OC_InitTypeDef timx_oc_pwm_chy;
g_timx_pwm_chy_handle.Instance = TIM3; /*选择pwm的定时器x*/
g_timx_pwm_chy_handle.Init.Prescaler = psc; /*这个定时器的分频系数*/
g_timx_pwm_chy_handle.Init.Period = arr; /*自动重装载值*/
g_timx_pwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /*up计数*/
HAL_TIM_PWM_Init(&g_timx_pwm_chy_handle);
timx_oc_pwm_chy.OCMode = TIM_OCMODE_PWM1; /*oc是输出,这里选择输出pwm1*/
timx_oc_pwm_chy.Pulse = arr / 2; /*ccr比较值*/
timx_oc_pwm_chy.OCPolarity = TIM_OCPOLARITY_LOW; /*输出极性*/
HAL_TIM_PWM_ConfigChannel(&g_timx_pwm_chy_handle, &timx_oc_pwm_chy, TIM_CHANNEL_2);/*两个句柄加一个定时器pwm输出通道选择*/
HAL_TIM_PWM_Start(&g_timx_pwm_chy_handle, TIM_CHANNEL_2);/*开启pwm以及通道输出pwm*/
}
/* 定时器输出PWM MSP初始化函数 */
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM3)
{
GPIO_InitTypeDef gpio_init_struct;
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_TIM3_CLK_ENABLE();
gpio_init_struct.Pin = GPIO_PIN_5;
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 推挽复用 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(GPIOB, &gpio_init_struct);
__HAL_RCC_AFIO_CLK_ENABLE();/*使能AFIO时钟,从而允许对GPIO引脚进行复用配置和重映射操作。*/
__HAL_AFIO_REMAP_TIM3_PARTIAL();/*将TIM3引脚复用*/
}
}
接着是main函数
/**
****************************************************************************************************
* @file main.c
* @author 正点原子团队(ALIENTEK)
* @version V1.0
* @date 2020-04-20
* @brief 跑马灯 实验
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 STM32F103开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
****************************************************************************************************
*/
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/TIMER/atim.h"
#include "./BSP/TIMER/gtim.h"
extern uint16_t g_timxchy_pwmin_sta; /* PWM输入状态 */
extern uint16_t g_timxchy_pwmin_psc; /* PWM输入分频系数 */
extern uint32_t g_timxchy_pwmin_hval; /* PWM的高电平脉宽 */
extern uint32_t g_timxchy_pwmin_cval; /* PWM的周期宽度 */
int main(void)
{
uint8_t t = 0;
double ht, ct, f, tpsc;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
led_init(); /* 初始化LED */
gtim_timx_pwm_chy_init(10 - 1, 72 - 1); //配置pwm输出
TIM3->CCR2 = 3; /*设置输出pwm的占空比*/
atim_timx_pwmin_chy_init();
while (1)
{
delay_ms(10);
t++;
if (t >= 20) /* 每200ms输出一次结果,并闪烁LED0,提示程序运行 */
{
if (g_timxchy_pwmin_sta) /* 捕获了一次数据 */
{
printf("\r\n"); /* 输出空,另起一行 */
printf("PWM PSC :%d\r\n", g_timxchy_pwmin_psc); /* 打印分频系数 */
printf("PWM Hight:%d\r\n", g_timxchy_pwmin_hval); /* 打印高电平脉宽 */
printf("PWM Cycle:%d\r\n", g_timxchy_pwmin_cval); /* 打印周期 */
tpsc = ((double)g_timxchy_pwmin_psc + 1) / 72; /* 得到PWM采样时钟周期时间 */
ht = g_timxchy_pwmin_hval * tpsc; /* 计算高电平时间 */
ct = g_timxchy_pwmin_cval * tpsc; /* 计算周期长度 */
f = (1 / ct) * 1000000; /* 计算频率 */
printf("PWM Hight time:%.3fus\r\n", ht); /* 打印高电平脉宽长度 */
printf("PWM Cycle time:%.3fus\r\n", ct); /* 打印周期时间长度 */
printf("PWM Frequency :%.3fHz\r\n", f); /* 打印频率 */
atim_timx_pwmin_chy_restart(); /* 重启PWM输入检测 */
}
LED1_TOGGLE(); /* LED1(GREEN)闪烁 */
t = 0;
}
}
}