实验原理
输入捕获实验目的是为了获得脉宽和频率,首先是脉宽的获得。
在实际捕获实验中,有两个通道,一个负责捕获上升沿,一个负责下降沿,
从上图可以看出,t1-t2 时间就是我们需要测量的高电平时间,假如定时器工作在向上计数模式,测量方法是:首先设置定时器通道 x 为上升沿捕获,这样在 t1 时刻,就会捕获到当前的 CNT 值,然后立即清零 CNT,并设置通道 x 为下降沿捕获,这样到 t2 时刻,又会发生捕获事件,得到此时的 CNT 值,记为 CCRx2。根据定时器的计数频率,我们就可以算出 t1-t2 的时间,从而得到高电平脉宽。我们来看代码部分。
代码讲解
最关键的部分是输入捕获模式的初始化
static void TIM_PWMINPUT_Config(void)
{
TIM_IC_InitTypeDef TIM_ICInitStructure;
TIM_SlaveConfigTypeDef TIM_SlaveConfigStructure;
TIM_MasterConfigTypeDef TIM_MasterConfigStructure;
// 开启TIMx_CLK,x[1,8]
ADVANCE_TIM_CLK_ENABLE();
/* 定义定时器的句柄即确定定时器寄存器的基地址*/
TIM_PWMINPUT_Handle.Instance = ADVANCE_TIM;
// 这里设置为周期最大,为65535,也就是65535*1us,为什么老师也没讲,
个人理解是因为这里的输出的pwm波周期是500*1us,输入捕获周期设置为最大能避免自身更新产生中断,有正确的理解的老哥可以和我讲一讲
TIM_PWMINPUT_Handle.Init.Period = 0xFFFF;
// 高级控制定时器时钟源TIMxCLK = HCLK=168MHz
// 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=1MHz,每次计数时间是1us
TIM_PWMINPUT_Handle.Init.Prescaler = 168-1;
// 采样时钟分频,不分频
TIM_PWMINPUT_Handle.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
// 计数方式,向上计数
TIM_PWMINPUT_Handle.Init.CounterMode=TIM_COUNTERMODE_UP;
// 初始化定时器TIMx, x[1,8]
HAL_TIM_IC_Init(&TIM_PWMINPUT_Handle);
/* IC1捕获:上升沿触发 TI1FP1 */
TIM_ICInitStructure.ICPolarity = TIM_ICPOLARITY_RISING;
//在系统结构中IC1和IC2是两个通道,但是他们又彼此可以连接,我们设定IC1直接通道,IC2间接就使得两个对一个引脚进行采样
TIM_ICInitStructure.ICSelection = TIM_ICSELECTION_DIRECTTI;
//设定捕获几次发生中断,每次都发生中断就选1,这里分频的目的是进行数字滤波防止出实现抖动带来的误差,如果有需求再去手册了解
TIM_ICInitStructure.ICPrescaler = TIM_ICPSC_DIV1;
//配合上条指令,详细见手册
TIM_ICInitStructure.ICFilter = 0x0;
HAL_TIM_IC_ConfigChannel(&TIM_PWMINPUT_Handle,&TIM_ICInitStructure,ADVANCE_IC1PWM_CHANNEL);
/* IC2捕获:下降沿触发 TI1FP2 */
TIM_ICInitStructure.ICPolarity = TIM_ICPOLARITY_FALLING;
TIM_ICInitStructure.ICSelection = TIM_ICSELECTION_INDIRECTTI;
TIM_ICInitStructure.ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.ICFilter = 0x0;
HAL_TIM_IC_ConfigChannel(&TIM_PWMINPUT_Handle,&TIM_ICInitStructure,ADVANCE_IC2PWM_CHANNEL);
/* 选择从模式: 复位模式 */
复位模式的存在作用是让IC1和IC2的计数器在检测到上升沿时被寄存器捕获。之后清零重新计数,并且产生中断,中断再处理捕获的数值,这样我们就会得到单纯的一次周期的数值。
TIM_SlaveConfigStructure.SlaveMode = TIM_SLAVEMODE_RESET;
/* 选择定时器输入触发: TI1FP1 */选择IC1通道的进行触发
TIM_SlaveConfigStructure.InputTrigger = TIM_TS_TI1FP1;
HAL_TIM_SlaveConfigSynchronization(&TIM_PWMINPUT_Handle,&TIM_SlaveConfigStructure);
/* 使能捕获/比较2中断请求 */
HAL_TIM_IC_Start_IT(&TIM_PWMINPUT_Handle,TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&TIM_PWMINPUT_Handle,TIM_CHANNEL_2);
}
第二个比较重要的就是一个频率和占空比的计算
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
/* 获取输入捕获值 */
IC1Value = HAL_TIM_ReadCapturedValue(&TIM_PWMINPUT_Handle,ADVANCE_IC1PWM_CHANNEL);
IC2Value = HAL_TIM_ReadCapturedValue(&TIM_PWMINPUT_Handle,ADVANCE_IC2PWM_CHANNEL);
if (IC1Value != 0)
{
/* 占空比计算 ,这里乘以100是为了转换成百分比形式*/
DutyCycle = (float)((IC2Value+1) * 100) / (IC1Value+1);
/* 频率计算 ,频率等于1s内的次数,现IC1等于1us乘以n次,乘以10的6次方就是一秒多少次,就是频率*/
Frequency = 168000000/168/(float)(IC1Value+1);
}
else
{
DutyCycle = 0;
Frequency = 0;
}
}
}
其余就是之前讲过的输出pwm初始化,引脚初始化和中断初始化
//初始化引脚,中断,输出模式,输入模式
void TIMx_Configuration(void)
{
TIMx_GPIO_Config();
TIMx_NVIC_Configuration();
TIM_PWMOUTPUT_Config();
TIM_PWMINPUT_Config();
}
//引脚初始化
static void TIMx_GPIO_Config(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启定时器相关的GPIO外设时钟*/
GENERAL_OCPWM_GPIO_CLK_ENABLE();
ADVANCE_ICPWM_GPIO_CLK_ENABLE();
/* 定时器功能引脚初始化 */
/* 通用定时器PWM输出引脚 */
GPIO_InitStructure.Pin = GENERAL_OCPWM_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
GPIO_InitStructure.Alternate = GENERAL_OCPWM_AF;
HAL_GPIO_Init(GENERAL_OCPWM_GPIO_PORT, &GPIO_InitStructure);
/* 高级定时器输入捕获引脚 */
GPIO_InitStructure.Pin = ADVANCE_ICPWM_PIN;
GPIO_InitStructure.Alternate = ADVANCE_ICPWM_AF;
HAL_GPIO_Init(ADVANCE_ICPWM_GPIO_PORT, &GPIO_InitStructure);
}
/ /高级控制定时器 TIMx,x[1,8]中断优先级配置
static void TIMx_NVIC_Configuration(void)
{
//设置抢占优先级,子优先级
HAL_NVIC_SetPriority(ADVANCE_TIM_IRQn, 0, 3);
// 设置中断来源
HAL_NVIC_EnableIRQ(ADVANCE_TIM_IRQn);
}
static void TIM_PWMOUTPUT_Config(void)
{
TIM_OC_InitTypeDef TIM_OCInitStructure;
// 开启TIMx_CLK,x[2,3,4,5,12,13,14]
GENERAL_TIM_CLK_ENABLE();
/* 定义定时器的句柄即确定定时器寄存器的基地址*/
TIM_PWMOUTPUT_Handle.Instance = GENERAL_TIM;
/* 累计 TIM_Period个后产生一个更新或者中断*/
//当定时器从0计数到9999,即为10000次,为一个定时周期
TIM_PWMOUTPUT_Handle.Init.Period = 500-1;
// 高级控制定时器时钟源TIMxCLK = HCLK=84MHz
// 设定定时器频率为=TIMxCLK/(TIM_Prescaler+1)=1mHz
TIM_PWMOUTPUT_Handle.Init.Prescaler = 84-1;
// 采样时钟分频
TIM_PWMOUTPUT_Handle.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
// 计数方式
TIM_PWMOUTPUT_Handle.Init.CounterMode=TIM_COUNTERMODE_UP;
// 重复计数器
TIM_PWMOUTPUT_Handle.Init.RepetitionCounter=0;
// 初始化定时器TIMx, x[1,8]
HAL_TIM_PWM_Init(&TIM_PWMOUTPUT_Handle);
/*PWM模式配置*/
//配置为PWM模式1
TIM_OCInitStructure.OCMode = TIM_OCMODE_PWM1;
TIM_OCInitStructure.Pulse = 250;
TIM_OCInitStructure.OCPolarity = TIM_OCPOLARITY_HIGH;
TIM_OCInitStructure.OCNPolarity = TIM_OCNPOLARITY_HIGH;
TIM_OCInitStructure.OCIdleState = TIM_OCIDLESTATE_SET;
TIM_OCInitStructure.OCNIdleState = TIM_OCNIDLESTATE_RESET;
//初始化通道3输出PWM
HAL_TIM_PWM_ConfigChannel(&TIM_PWMOUTPUT_Handle,&TIM_OCInitStructure,TIM_CHANNEL_3);
/* 定时器通道3输出PWM */
HAL_TIM_PWM_Start(&TIM_PWMOUTPUT_Handle,TIM_CHANNEL_3);
}
定时器.h文件中各个引脚的定义。
#ifndef __ADVANCE_TIM_H
#define __ADVANCE_TIM_H
#include "stm32f4xx.h"
/* 通用定时器 */
#define GENERAL_TIM TIM4
#define GENERAL_TIM_CLK_ENABLE() __TIM4_CLK_ENABLE()
/* 通用定时器PWM输出 */
/* PWM输出引脚 */
#define GENERAL_OCPWM_PIN GPIO_PIN_14
#define GENERAL_OCPWM_GPIO_PORT GPIOD
#define GENERAL_OCPWM_GPIO_CLK_ENABLE() __GPIOD_CLK_ENABLE()
#define GENERAL_OCPWM_AF GPIO_AF2_TIM4
/* 高级控制定时器 */
#define ADVANCE_TIM TIM1
#define ADVANCE_TIM_CLK_ENABLE() __TIM1_CLK_ENABLE()
/* 捕获/比较中断 */
#define ADVANCE_TIM_IRQn TIM1_CC_IRQn
#define ADVANCE_TIM_IRQHandler TIM1_CC_IRQHandler
/* 高级控制定时器PWM输入捕获 */
/* PWM输入捕获引脚 */
#define ADVANCE_ICPWM_PIN GPIO_PIN_9
#define ADVANCE_ICPWM_GPIO_PORT GPIOE
#define ADVANCE_ICPWM_GPIO_CLK_ENABLE() __GPIOE_CLK_ENABLE()
#define ADVANCE_ICPWM_AF GPIO_AF1_TIM1
#define ADVANCE_IC1PWM_CHANNEL TIM_CHANNEL_1
#define ADVANCE_IC2PWM_CHANNEL TIM_CHANNEL_2
extern TIM_HandleTypeDef TIM_PWMOUTPUT_Handle;
extern TIM_HandleTypeDef TIM_PWMINPUT_Handle;
void TIMx_Configuration(void);
#endif /* __ADVANCE_TIM_H */
mian.c函数,记得初始化串口通信printf
int main(void)
{
/* 初始化系统时钟为168MHz */
SystemClock_Config();
/* 初始化串口 */
DEBUG_USART_Config();
/* 初始化基本定时器定时,1s产生一次中断 */
TIMx_Configuration();
while(1)
{
HAL_Delay(500);
printf("IC1Value = %d IC2Value = %d ", IC1Value, IC2Value);
printf("占空比:%0.2f%% 频率:%0.2fHz\n", DutyCycle, Frequency);
}
}