1. 输入捕获简介

输入捕获模式可以用来测量脉冲宽度或者测量频率,下图以测量脉宽为例来说明输入捕获的原理

假定定时器工作在向上计数模式,图中t1-t2的时间就是我们需要测量的低电平时间。测量方法为:首先设置定时器通道x为下降沿捕获,在t1时刻就会捕获到当前的CNT值,然后立即清零CNT,并设置通道x为上升沿捕获,到t2时刻又会发送捕获事件,得到此时的CNT值(记为CCRx2)。在t1-t2之间可能产生N次定时器溢出,因此需要对定时器溢出做处理,防止低电平太长导致数据不准确。 t1-t2之间计数的次数为:N * ARR + CCRx2,再乘以CNT计数周期即可得到低电平持续时间

 2. 硬件设计

本实验通过TIM5的通道1输入捕获功能捕获S1按键的高电平持续时间,并通过printf函数打印捕获到的高电平时间,用LED灯提示系统正常运行

3、 STM32CubeMX设置

  • RCC设置外接HSE,时钟设置为72M
  • PE5设置为GPIO推挽输出模式、上拉、高速、默认输出电平为高电平
  • USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位
  • 选择TIM5,定时器时钟源已经默认为内部时钟源、设置通道1为输入捕获模式(PA0自动被选中),预分频系数设置为72-1,向上计数,自动重装载值设为0x65535(最大值),则计时器时钟频率为1MHz,计时器周期为1us,定时器溢出周期为 65535 * 1 = 65535us。CH1选择上升沿触发,默认不分频,滤波8位。

NVIC设置中激活定时器中断。

在GPIO设置里将PA0下拉,保证没有信号输入的时候电平稳定。

输入工程名,选择工程路径(不要有中文),选择MDK-ARM V5;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP ;点击GENERATE CODE,生成工程代码

4、程序编译

由于定时器最大的重装载值是65535,溢出一次约65ms,考虑到按下按键的时间过长,因此添加定时器溢出中断,用来计算计数器溢出的次数。

/* TIM5CH1_CAP_STA 各数据位说明
** bit7   捕获完成标志
** bit6   捕获到高电平标志
** bit5~0 捕获高电平后定时器溢出的次数*/
uint8_t TIM5CH1_CAP_STA = 0;
uint16_t TIM5CH1_CAP_VAL;

//定时器溢出回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
    if((TIM5CH1_CAP_STA & 0X80) == 0){  //还未成功捕获
        if(TIM5CH1_CAP_STA & 0X40){     //已经捕获到高电平
            if((TIM5CH1_CAP_STA & 0X3F) == 0X3F)
			  {   //高电平时间太长了
                TIM5CH1_CAP_STA |= 0X80;            //标记为完成一次捕获
                TIM5CH1_CAP_VAL = 0XFFFF;           //计数器值
               }
            else
                TIM5CH1_CAP_STA++;      //溢出次数加1            
        }   
    }
}

添加输入捕获中断回调函数

//输入捕获中断回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){
    if((TIM5CH1_CAP_STA & 0X80) == 0){  //还未成功捕获
        if(TIM5CH1_CAP_STA & 0X40){     //捕获到上升沿后条件为真
            TIM5CH1_CAP_STA |= 0X80;    //标记为完成一次高电平捕获
            
			TIM5CH1_CAP_VAL = HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_1);  //获取当前的计数器值
            TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1);    //清除原来的设置       
            TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);    //设置上升沿捕获
        }
        else{
            TIM5CH1_CAP_STA = 0;
            TIM5CH1_CAP_VAL = 0;
            TIM5CH1_CAP_STA |= 0X40;    //标记捕获到上升沿
            __HAL_TIM_DISABLE(&htim5);  //关闭定时器
            __HAL_TIM_SET_COUNTER(&htim5,0);    //计数器值清零
            TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1);    //清除原来的设置               
            TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);   //设置下降沿捕获
            __HAL_TIM_ENABLE(&htim5);   //使能定时器     
        }   
    }
}
 

程序流程:

        初始化时设置为上升沿触发,当按下按键时触发输入捕获中断,程序进入输入捕获中断回调函数,将TIM5CH1_CAP_STA设置为0X40,定时器计数器清零并重新计数,同时将触发模式改成下降沿触发,然后等待按键松开。

        在等待按键松开的过程中,按键长按超过65ms,计数器溢出触发定时器中断,在定时器中断中将TIM5CH1_CAP_STA的值加1,标志一次溢出。如果时间过长导致(TIM5CH1_CAP_STA & 0X3F)>= 0X3F,这时强制完成输入捕获,

        按键松开时,触发下降沿输入捕获中断,进入回调函数将TIM5CH1_CAP_STA的最高位设置为1,标志完成一次输入捕获,并将此时的计数器的值给到TIM5CH1_CAP_VAL,同时将触发模式重新改回上升沿触发。

        当完成输入捕获时,在main函数中计算TIM5CH1_CAP_STA的溢出次数*65535 再加上结束时的计数器值,即可计算出输入捕获高电平的总时间。

        //注意:在更改触发模式前要先清除原来的设置!!!

extern uint8_t TIM5CH1_CAP_STA ;
extern uint16_t TIM5CH1_CAP_VAL;
int main(void)
{
 //***省略***//
 long long temp = 0;
 HAL_TIM_IC_Start_IT(&htim5,TIM_CHANNEL_1);    //一定要开启TIM5通道1的捕获中断
  __HAL_TIM_ENABLE_IT(&htim5,TIM_IT_UPDATE);    //一定要开启TIM5的更新中断
 while (1)
  {
		 HAL_Delay(50);
		 if(TIM5CH1_CAP_STA & 0X80){     //完成一次高电平捕获
        temp = TIM5CH1_CAP_STA & 0X3F;
        temp *= 65536;              //溢出总时间
        temp += TIM5CH1_CAP_VAL;    //总的高电平时间
        printf1("High level duration:%lld us\r\n",temp);
        TIM5CH1_CAP_STA = 0;        //准备下一次捕获
    }
    HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);
  }
}

   5、下载验证

将程序编译无误后下载到板子上,打开串口助手,按下按键,即可看到按下按键的时间了。

 6、参考文献

【STM32】HAL库 STM32CubeMX教程八---定时器输入捕获_hal_tim_readcapturedvalue-CSDN博客

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐