目录
一、输入捕获原理
二、输入捕获过程
1.设置输出捕获滤波器(通道1为例)
2.设置输入捕获极性
3.设置输入捕获映射通道
4.设置输入捕获分频器
5.捕获到有效信号可以开启中断
6.工作工程
三、输入捕获编程流程(HAL库)
四、程序要求
五、代码实现
1.timer.h
2.timer.c
3.main.c
4.实验结果
图中 t1~t2 时间,就是我们需要测量的高电平时间。测量方法如下:首先设置定时器通道 x 为
上升沿捕获,这样,t1 时刻,就会捕获到当前的 CNT 值,记为 CCRx1,然后立即清零 CNT,并设置通道 x为下降沿捕获,这样到 t2 时刻,又会发生捕获事件,得到此时的 CNT 值,记为 CCRx2。这样,根据定时器的计数频率,我们就可以算出 t1~t2 的时间,从而得到高电平脉宽。
滤波器IC1F[3:0]是用来设置输入采样频率和数字滤波器长度。是根据TIMx_CR1的CKD[1:0]来设置的,如果CKD[1:0]设置为00,则。fCK_INT是内部时钟,等于72MHz。假如为上升沿触发,那么在捕获上升沿的时候,再以fCK_INT的频率连续采样8次通道1的电平,如果都是高电平,则是一个有效的触发。这样就可以滤掉低于8个采样周期的脉冲信号(IC1F[3:0]=0011,滤波长度N=8),实现滤波效果。IC1F[3:0]=0000,只用采集上升沿,就触发捕获。
边沿检测器检测信号,比如设置为上升沿捕获,则捕获上升沿,下降沿同理;CC1输出设置高电平有效还是低电平有效;CC1输入则是选择捕获上升沿或下降沿。
每两个事件(设置为上升沿捕获):每隔2次上升沿执行一次捕获
如果开启了捕获中断,就是在捕获上升沿或下降沿 的时候开启中断。
通过检测TIMx CHx上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的捕获/比较寄存器 (TIMx CCRx)里面,完成一次捕获。
1.定时器输入捕获时基参数初始化
HAL_TIM_IC_Init(TIM_HandleTypeDef *htim); // IC输入捕获初始化
2.初始化通道参数配置
HAL_TIM_IC_ConfigChannel();
3.初始化回调函数(一般情况,回调函数里编写时钟使能,IO口配置以及优先级设置等)
__HAL_RCC_TIM5_CLK_ENABLE();
4.使能定时器并使能输入捕获通道
HAL_TIM_IC_Start(); // 使能
HAL_TIM_IC_Start_IT(); // 捕获的同时开启中断
5.捕获中断服务函数
HAL_TIM_IRQHandler();
6.读取捕获值
uint32_t HAL_TIM_ReadCapturedValue();
tip:因为要检测上图高电平信号,所以我们用PA0按键,因为PA0是高电平有效,低电平无效。因为是用的是PA0,所以用的是TIM5_CH1。
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
extern TIM_HandleTypeDef TIM5_IC_Handle;
void TIM5_IC_Init(void);
#endif
#include "timer.h"
TIM_HandleTypeDef TIM5_IC_Handle; // TIM句柄
TIM_IC_InitTypeDef TIM5_IC_InitStruct; // TIM_IC句柄
void TIM5_IC_Init(void)
{
TIM5_IC_Handle.Instance = TIM5;
TIM5_IC_Handle.Init.CounterMode = TIM_COUNTERMODE_UP; // 设置TIM5的计数模式,向上计数
TIM5_IC_Handle.Init.Period = 0xFFFFFFFF; // 设置自动装载值
TIM5_IC_Handle.Init.Prescaler = 90 - 1; // 设置分频系数
HAL_TIM_IC_Init(&TIM5_IC_Handle);
TIM5_IC_InitStruct.ICPolarity = TIM_ICPOLARITY_RISING; // 捕获极性:上升沿捕获
TIM5_IC_InitStruct.ICPrescaler = TIM_ICPSC_DIV1; // 输入分频:配置输入分频,选择不分频
TIM5_IC_InitStruct.ICSelection = TIM_ICSELECTION_DIRECTTI; // 输入映射:映射到TI1上
TIM5_IC_InitStruct.ICFilter = 0; // 配置滤波器,不滤波
HAL_TIM_IC_ConfigChannel(&TIM5_IC_Handle, &TIM5_IC_InitStruct, TIM_CHANNEL_1); // 配置IC通道
HAL_TIM_IC_Start_IT(&TIM5_IC_Handle, TIM_CHANNEL_1); // 开启TIM5捕获通道,并且开启捕获中断
__HAL_TIM_ENABLE_IT(&TIM5_IC_Handle, TIM_IT_UPDATE); // 当按键按得过长或许会溢出ARR(设定的装载值),需要记录溢出多少次,所以要开启更新中断
}
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM5) // 判断为TIM5
{
__HAL_RCC_TIM5_CLK_ENABLE(); // 使能TIM5
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能PA
GPIO_InitTypeDef GPIO_InitStruct; // GPIO句柄
GPIO_InitStruct.Pin = GPIO_PIN_0; // PA0
GPIO_InitStruct.Pull = GPIO_PULLDOWN; // 下拉
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 推挽输出
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH; // 高速
GPIO_InitStruct.Alternate = GPIO_AF2_TIM5; // 复用为TIM5
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 初始化GPIOA
HAL_NVIC_EnableIRQ(TIM5_IRQn); // 使能TIM5
HAL_NVIC_SetPriority(TIM5_IRQn, 2, 1); // 设置TIM5优先级
}
}
/*
捕获状态:
[7]:0:没有捕获成功;1:捕获成功
[6]:0:没有捕获高电平;1:捕获高电平
[5-0]:捕获低电平后溢出的次数
*/
u8 TIM5CH1_CAPTURE_STA=0; //输入捕获状态
u32 TIM5CH1_CAPTURE_VAL; //输入捕获值(TIM2/TIM5是32位)
void TIM5_IRQHandler(void) // TIM5中断服务函数
{
HAL_TIM_IRQHandler(&TIM5_IC_Handle); // 中断处理入口函数(所有的TIM中断都用函数)
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) // 定时器更新中断(溢出)处理回调函数,在HAL_TIM_IRQHandler调用
{
if ((TIM5CH1_CAPTURE_STA&0x80) == 0) // 还未捕获成功
{
if (TIM5CH1_CAPTURE_STA&0x40) // 捕获成功
{
if ((TIM5CH1_CAPTURE_STA&0x3F) == 0x3F) // 高电平太长
{
TIM5CH1_CAPTURE_STA |= 0x80; // 标记捕获成功一次
TIM5CH1_CAPTURE_VAL = 0XFFFFFFFF;
}
else
{
TIM5CH1_CAPTURE_STA++; // 自加1次
}
}
}
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) // 定时器输入捕获回调函数,在HAL_TIM_IRQHandler调用
{
if ((TIM5CH1_CAPTURE_STA&0x80) == 0) // 未捕获成功
{
if (TIM5CH1_CAPTURE_STA&0x40) // 捕获到下降沿
{
TIM5CH1_CAPTURE_STA |= 0x80; // 标志成功捕获到一次高电平脉宽
TIM5CH1_CAPTURE_VAL = HAL_TIM_ReadCapturedValue(&TIM5_IC_Handle,TIM_CHANNEL_1); // 获取当前的捕获值
TIM_RESET_CAPTUREPOLARITY(&TIM5_IC_Handle, TIM_CHANNEL_1); // 清除之前的设置
TIM_SET_CAPTUREPOLARITY(&TIM5_IC_Handle, TIM_CHANNEL_1, TIM_ICPOLARITY_RISING); // 配置TIM5通道1上升沿捕获
}
else
{
TIM5CH1_CAPTURE_STA = 0; // 清零
TIM5CH1_CAPTURE_VAL = 0;
TIM5CH1_CAPTURE_STA |= 0x40; // 捕获到上升沿
__HAL_TIM_DISABLE(&TIM5_IC_Handle); // 关闭定时器TIM5
__HAL_TIM_SET_COUNTER(&TIM5_IC_Handle, 0);
TIM_RESET_CAPTUREPOLARITY(&TIM5_IC_Handle, TIM_CHANNEL_1); // 清除之前的设置
TIM_SET_CAPTUREPOLARITY(&TIM5_IC_Handle, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); // 配置TIM5通道1下降沿捕获
__HAL_TIM_ENABLE(&TIM5_IC_Handle); // 使能TIM5
}
}
}
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "timer.h"
extern u8 TIM5CH1_CAPTURE_STA; //输入捕获状态
extern u32 TIM5CH1_CAPTURE_VAL; //输入捕获值(TIM2/TIM5是32位)
int main()
{
long long temp = 0;
HAL_Init();
Stm32_Clock_Init(360, 25, 2, 8);
delay_init(180);
uart_init(115200);
TIM5_IC_Init();
while (1)
{
delay_ms(10);
if (TIM5CH1_CAPTURE_STA&0x80) // 成功捕获了一次高电平
{
temp = TIM5CH1_CAPTURE_STA&0x3F; // 先把最大的值赋值给temp
temp *= 0XFFFFFFFF; // 再和TIM最大值相乘可以得到溢出时间的总和
temp += TIM5CH1_CAPTURE_VAL; // 得到高电平时间
printf("高电平时间:%lldus \r\n", temp); // 输出打印高电平时间
TIM5CH1_CAPTURE_STA = 0; // 清零,进行下一次捕捉
}
}
}