输入捕获可以对输入的信号的上升沿、下降沿或者双边沿进行捕获,常用的有测量输入信号的脉宽和测量PWM 输入信号的频率和占空比这两种。
输入捕获的原理就是,当捕获到信号的跳变沿的时候,把计数器CNT 的值锁存到捕获寄存器CCR 中,把前后两次捕获到的CCR 寄存器中的值相减,就可以算出脉宽或者频率。
如果捕获的脉宽的时间长度超过你的捕获定时器的周期,就会发生溢出,这个就需要做额外的处理。
需要被测量的信号从定时器的外部引脚TIMx_CH1/2/3/4 进入,通常叫TI1/2/3/4,在后面的捕获讲解中对于要被测量的信号我们都以TIx 为标准叫法。
当输入的信号存在高频干扰的时候,我们需要对输入信号进行滤波,即进行重新采样;
根据采样定律,采样的频率必须大于等于两倍的输入信号。比如输入的信号为1M,又存在高频的信号干扰,那么此时就很有必要进行滤波,我们可以设置采样频率为2M,这样可以在保证采样到有效信号的基础上把高于2M 的高频干扰信号过滤掉。
滤波器的配置由CR1 寄存器的位CKD[1:0] 和CCMR1/2 的位ICxF[3:0] 控制。从ICxF 位的描述可知,采样频率fSAMPLE 可以由fCK_INT 和fDTS 分频后的时钟提供,其中是fCK_INT 内部时钟,fDTS是fCK_INT 经过分频后得到的频率,分频因子由CKD[1:0] 决定,可以是不分频,2 分频或者是4分频。
边沿检测器用来设置信号捕获模式:上升沿,下降沿,或双边沿,具体的由CCER 寄存器的位CCxP 和CCxNP 决定。
捕获通道就是图中的IC1/2/3/4,每个捕获通道都有相对应的捕获寄存器CCR1/2/3/4,当发生捕获的时候,计数器CNT 的值就会被锁存到捕获寄存器中。
输入通道是用来输入信号的,捕获通道是用来捕获输入信号的通道;
ICx 的输出信号会经过一个预分频器,用于决定发生多少个事件时进行一次捕获。具体的由寄存器CCMRx 的位ICxPSC 配置,如果希望捕获信号的每一个边沿,则不分频。
例如:
每2个事件触发一次捕获,如上升沿捕获时,连续获取到两个上升沿后才会触发计数
每4个事件触发一次捕获
每8个事件触发一次捕获
经过预分频器的信号ICxPS 是最终被捕获的信号,当发生捕获时(第一次),计数器CNT 的值会被锁存到捕获寄存器CCR 中,还会产生CCxI 中断,相应的中断位CCxIF(在SR 寄存器中)会被置位,通过软件或者读取CCR 中的值可以将CCxIF 清0。
如果发生第二次捕获(即重复捕获:CCR 寄存器中已捕获到计数器值且CCxIF 标志已置1),则捕获溢出标志位CCxOF(在SR 寄存器中)会被置位,CCxOF 只能通过软件清零。
基础STM32Cube MX 的配置,参考这篇博客:STM32 CubeMx教程 – 基础知识及配置使用教程
配置RCC,选择使用外部晶振模式
配置SYS,debug模式选择使用Serial Wire
配置一个串口,用于输出调试信息
配置定时器,配置TIM,
将时钟源配置为内部时钟源;
配置通道1为输入捕获模式;
配置TIM里面 GPIO模式;
配置为输入模式(input mode);
配置为上拉模式(Pull - up),上拉模式模式初始电平为高电平;
按键另一端接地,初始化按键另一边为高电平,当按键按下的时候,就可以检测到低电平;
配置打开定时器中断
配置TIM详细参数;
预分频系数为7199,重装载值 99;
检测周期 Tout = (7199+1)*(99+1) / 72 000 000 = 10ms
GPIO初始为高电平,按键按下成为低电平,所以触发模式设置为下降沿模式
配置时钟树
涉及的HAL库函数
HAL_TIM_Base_Start_IT(&htim2); //开启定时器2溢出中断
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2); //开启输入捕获中断
HAL_TIM_IC_Stop_IT(&htim2, TIM_CHANNEL_2) //关闭输入捕获中断
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim); //定时器溢出回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim); //捕获中断回调函数
测量低电平的持续时间,先下降沿后上升沿,记录计数值,最终输出us单位。
代码示例:
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
//变量存储
typedef struct
{
uint8_t flg; //0为未开始,1已经开始,2为结束
uint16_t num; //计数值
uint16_t num_period; //溢出次数
}COUNT_TEMP;
COUNT_TEMP count_temp={0};
/* USER CODE END PD */
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM2_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim2); //开启定时器2溢出中断
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1); //开启输入捕获中断
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(count_temp.flg == 2 ) //等待测量
{
//计数计数值,0xFFFF为最大计数
uint32_t ulTime = (uint32_t)count_temp .num_period * 0xFFFF + count_temp .num;
//输出测量的值
printf ( "low time :%d us\n",ulTime);
count_temp .flg = 0; //复位测量
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/* USER CODE BEGIN 4 */
/* 每隔10ms进入一次定时器中断函数,每按下一次按键都会进入一次捕获中断函数 */
/* 捕获中断设置为下降沿捕获,因为初始电平设置的高电平 */
/* 没按下一次按键,也就是经过一个下降沿,就进入一次捕获中断函数 */
/* 进入捕获中断函数以后,开始计时,并且设置为上升沿触发下一次捕获中断 */
/* 当按键拿起,重回高电平,也就是捕获一次上升沿高电平,再次进入捕获中断函数 */
/* 进入捕获中断函数,获取记录的时间,并设置为下降沿触发中断,如此往复 */
//捕获中断回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
//判断定时器2
if(TIM2 == htim->Instance){
if ( count_temp.flg == 0 )
{
// 清零定时器计数
__HAL_TIM_SET_COUNTER(htim,0);
//设置上升沿触发
__HAL_TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING);
count_temp .flg = 1; //设置已经开始
count_temp .num_period = 0; //溢出计数清零
count_temp .num = 0; //计数清零
}
else
{
// 获取定时器计数值
count_temp .num = HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1);
//设置下降沿触发
__HAL_TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING);
count_temp .flg = 2;
}
}
}
//定时器溢出回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(TIM2 == htim->Instance){
//每次溢出时间为65536us
if(count_temp.flg==1)//还未成功捕获
{
if(count_temp.num_period==0XFFFF)//电平太长了
{
count_temp.flg=2; //标记成功捕获了一次
count_temp .num=0XFFFF;
}else count_temp .num_period ++;
}
}
}
/* USER CODE END 4 */
本文涉及到的代码:STM32 HAL库 定时器输入捕获