通用定时器输入捕获实验

输入捕获模式可以用来测量脉冲宽度或者测量频率。STM32 的定时器,除了 TIM6 和 TIM7,其他定时器都有输入捕获功能。 STM32 的输入捕获,简单的说就是通过检测 TIMx_CHx 上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值( TIMx_CNT)存放到对应的通道的捕获/比较寄存器( TIMx_CCRx)里面,完成一次捕获。同时还可以配置捕获时是否触发中断/DMA 等。

通用定时器的输入捕获计时原理如下。以捕获测量高电平脉宽、递增计数模式为例,当我们捕捉到高电平脉冲的上升沿触发信号,则将时间数据计入CRRx1寄存器,将电平触发模式修改为下降沿触发,并且重置CNT计数器使之从0重新计数。随后,我们记录有N次溢出(CNT计数器达到了ARR自动重装载寄存器数据系数的数值)。当输入接收到下降沿输入信号后,将时间计入CRRx2,结束捕获部分。最后计算时间为Time=(Arr+1)*N+CRRx2,其中ARR=Tout/(PSC+1)。

通用定时器输入捕获实验_第1张图片

 接下来介绍实验代码。

实验要求:通过TIM5_Channel1捕获按键高电平脉宽时间,并通过串口打印或LED状态转变。以1MHz为例,Tout=(ARR+1)*(PSC+1)/Ft,可以计算得PSC=71,ARR=65535。

在我们之前的gtim.h的基础上进行编写。

首先编写函数头文件代码gtim.h:

#ifndef __GTIM_H
#define __GTIM_H
 
#include "./SYSTEM/sys/sys.h"
 
extern TIM_HandleTypeDef g_timx_pwm_chy_handle;
extern uint8_t g_timxchy_cap_sta = 0;
extern uint16_t g_timxchy_cap_val = 0;

 
void gtim_timx_cap_chy_init(uint16_t arr, uint16_t psc);
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim);
void TIM5_IRQHandler(void);
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);
void HAL_TIM_PeriodElapseCallback(TIM_HandleTypeDef *htim);
 
#endif

再编写函数文件gtim.h:

#include "./BSP/GTIM/gtim.h"
#include "./BSP/LED/led.h"
 
TIM_HandleTypeDef g_timx_cap_chy_handle;
 
void gtim_timx_cap_chy_init(uint16_t arr, uint16_t psc){
 
    TIM_OC_InitTypeDef timx_ic_cap_chy = {0};
 
    g_timx_cap_chy_handle.Instance = TIM5;
    g_timx_cap_chy_handle.Init.Prescaler = psc;
    g_timx_cap_chy_handle.Init.Period = arr;
    g_timx_cap_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;//向上计数模式
    HAL_TIM_PWM_Init(&g_timx_cap_chy_handle);
 
    timx_ic_cap_chy.ICPolarity = TIM_ICPOLARITY_RISING;//选择上升沿捕获。
    timx_ic_cap_chy.ICSelection = TIM_SELECTION_DIRECTTI;//映射到TI1上
    timx_ic_cap_chy.ICPrescaler = TIM_ICPSC_DIV1;//配置输入分频,不分频
    timx_ic_cap_chy.ICFilter = 0;//配置输入分频器
    HAL_TIM_cap_ConfigChannel(&g_timx_cap_chy_handle, &timx_ic_cap_chy, TIM_CHANNEL_1);
 
    __HAL_TIM_ENABLE_IT(&g_timx_cap_chy_handle, TIM_IT_UPDATE);//使能更新中断
    HAL_TIM_IC_Start_IT(&g_timx_cap_chy_handle, TIM_CHANNEL_1);//开始捕获
}
 
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim){
 
    if(htim->Instance == TIM5){
        GPIO_InitTypeDef gpio_init_struct;
        __HAL_RCC_GPIOB_CLK_ENABLE();
        __HAL_RCC_TIM5_CLK_ENABLE();
        __HAL_RCC_GPIOA_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);

        gpio_init_struct.Pin = GPIO_PIN_0;
        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(GPIOA, &gpio_init_struct);
 
        __HAL_RCC_AFIO_CLK_ENABLE();
        __HAL_AFIO_REMAP_TIM5_PARTIAL();
        HAL_NVIC_SetPriority(TIM5_IQRn, 1, 3);
        HAL_NVIC_EnableIRQ(TIM5_IRQn);
    }
}

uint8_t g_timxchy_cap_sta = 0;
uint16_t g_timxchy_cap_val = 0;

void TIM5_IRQHandler(void){

    HAL_TIM_HIRQHandler(&g_timx_cap_chy_handle);
}

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){

    if(htim->Instance == TIM5){

        if((g_timxchy_cap_sta & 0x80) == 0){//尚未捕获成功

            if(g_timxchy_cap_sta & 0x40){//捕获到一个下降沿
                g_timxchy_cap_sta |= 0x80;//标记捕获到高电平脉宽
                g_timxchy_cap_sta = HAL_ReadCaptureValue(&g_timx_cap_chy_handle, TIM——CHANNEL_1);//获取当前的捕获值
                TIM_RESET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, TIM_CHANNEL_1);//一定要清空之前的设置!
                TIM_SET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, TIM_CHANNEL_1, TIM_ICPOLARITY_RISING);//配置为上升沿捕获
            else{//还未开始,第一次捕获上升沿
                g_timxchy_cap_sta = 0;//清空
                g_timxchy_cap_val = 0;

                g_timxchy_cap_sta |= 0x40;//标记捕捉到上升沿信号
                __HAL_TIM_DISABLE(&g_timx_cap_chy_handle);//关闭定时器5
                __HAL_TIM_SET_COUNTER(&g_timx_cap_chy_handle, 0);//定时器5计数器清零
                TIM_RESET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, TIM_CHANNEL_1);//一定要清空之前的设置!
                TIM_SET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING);//修改为下降沿捕获
                __HAL_TIM_ENABLE(&g_timx_cap_chy_handle) ;//使能定时器5
            }
        }
    }

}

void HAL_TIM_PeriodElapseCallback(TIM_HandleTypeDef *htim){

    if(htim->Instance == TIM5){

        if((g_timxchy_cap_sta & 0x80) == 0){还未成功捕获
            
            if(g_timxchy_cap_sta & 0x40){已经捕获到高电平

                if((g_timxchy_cap_sta & 0x3F) ==0x3F){//高电平太长了

                    TIM_RESET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, TIM_CHANNEL_1);
                    TIM_SET_CAPTUREPOLARITY(&g_timx_cap_chy_handle, TIM_CHANNEL_1, TIM_ICPOLARITY_);
                    g_timxchy_cap_sta |= 0x80; //标记已经成功捕获一次
                    g_timxchy_cap_val = 0xFFFF;
                }
                else{//累计定时器溢出次数
                    g_timxchy_cap_val++;
                }
            }
        }
    }
}

最后编写我们的主函数文件main.c:

#include "./SYSTEM/delay/delay.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"
#include "./BSP/GTIM/gtim.h"
 
int main(void){
 
    uint8_t temp = 0;
 
    HAL_Init();
    sys_stm32_clock_init(RCC_PLL_MUL9);
    delay_init(72);
    led_init();
    gtim_timx_cap_chy_init(0xFFFF - 1, 72 - 1);
 
    while(1){
        
        if(g_timxchy_cap_sta & 0x80){
            temp = g_timxchy_cap_sta & 0x3F;
            temp *= 65536;
            temp += g_timxchy_cap_val;
            printf("High: %d us \n",temp);
            g_timxchy_cap_sta = 0;
        }

        t++;

        if(t > 20){

            t = 0;
            LED0_TOGGLE();
        }
        delay_ms(10);
    }
}

到此我们的实验代码便写完了。

你可能感兴趣的:(stm32,单片机,stm32,嵌入式硬件,c++,c语言)