STM32 input capture experiment example detailed

table of Contents

STM32 input capture experiment

Register part explanation (take TIM5_CH1 as an example)

The role of TIMx_CCMR1.ICF[3:0]

The role of TIMx_CCER.CC1P

The role of TIMx_CCMR1.CC1S

The role of TIMx_CCMR1.ICPS

The role of TIMx_CCER.CC1E

Programming skills

How to judge whether it is a rising edge or a falling edge?

How to perform pulse width measurement?

What should I do if the timer overflows when the high level lasts too long?

What should I do if the timer overflows too many times?

Library function configuration principle

Overall block diagram

Configuration of multiplexed pins and GPIO

Counter configuration

Configuration of input capture channel properties

Interrupt priority configuration

TIM5_CH1 interrupt type selection

Timer enable

Interrupt service function for measuring effective pulse

Focus: The logic of writing interrupt service functions

Difficulty: What should I do when the high level lasts for too long?

Code example

Main.c

Timer.c

Timer.h

Key.c

Key.h

operation result


STM32 input capture experiment

Register part explanation (take TIM5_CH1 as an example)

 

The role of TIMx_CCMR1.ICF[3:0]

The role of the filter is to "collect samples in order to determine the accurate level state." Let's take ICIF[3:0] = 0010 as an example:

 

 

 

Example application: Assuming that the input signal jitters within a maximum of 5 internal clock cycles, we must configure the filter bandwidth to be longer than 5 clock cycles. Therefore, we can sample 8 times continuously (at fDTS frequency) to confirm the last real edge transition at TI1, that is, write IC1F=0011 in the TIMx_CCMR1 register.

The role of TIMx_CCER.CC1P

This register is very important, it determines the "rising edge/falling edge trigger input capture", and most importantly, it is the only register used to configure the polarity, which means that the input polarity and output polarity must pass through it. Configuration, so when we call the function below, you will find that whether it is PWM output or input capture, changing the polarity is the same function of configuration.

 

The role of TIMx_CCMR1.CC1S

 

The above is the channel configuration. The existence of CC1S[1:0] is to configure the "signal type that triggers the event/interrupt". For example, CC1S[1:0] = 01, then the signal that triggers the interrupt/event in TIM5_CH1 is from TI1FP1 .

 

The role of TIMx_CCMR1.ICPS

The function of the prescaler is to configure "How many valid level changes do we need to trigger an interrupt?".

 

The role of TIMx_CCER.CC1E

Enable the corresponding input capture channel, so that when the IC1PS port outputs a valid level change, it can be captured immediately to trigger the corresponding event/interrupt.

Programming skills

How to judge whether it is a rising edge or a falling edge?

Pulse edge + level state at the previous moment = level change. For example: the level at the previous moment was high and the edge of the pulse was captured, then we can know "the level at this time is low and the edge of the pulse is a falling edge pulse".

How to perform pulse width measurement?

We know that "the principle of input capture is: when a valid pulse change is captured, then the value of the counter is automatically captured by the capture/compare register", we can do this: if we want to measure the duration of the high level, we can When the rising edge occurs, we set the initial count value of the counter to 0, and when the falling edge occurs, we load the value of the counter at this time into the capture/comparison counter to capture and read. Finally "(captured value-counter initial value) * unit count duration of counter = high level duration".

What should I do if the timer overflows when the high level lasts too long?

We know that each timer has a corresponding interrupt service function, we can enable two interrupts: counter overflow interrupt + input capture interrupt. We can record the number of times the timer overflows when the input level is high. In the final calculation of time, "high level duration = (overflow times * (counter MAX value-counter initial value) + (capture value-counter initial value)) * counter unit counting time".

What should I do if the timer overflows too many times?

When the high level duration is too long and the counter overflows too many times, we do not need to wait for the "falling edge pulse", directly set the "capture completion flag", and set the capture value to what the data type can represent MAX value.

Library function configuration principle

Overall block diagram

 

Configuration of multiplexed pins and GPIO

Enable the corresponding bus clock and GPIO initialization:

I multiplex the PA0 pin as TIM5_CH1 for input capture.

We can see from the status of KEY_UP that GPIOA.0 must be a pull-down input. Only in this way can the button be pressed to trigger the input level change.

GPIO_InitTypeDef GPIO_InitStructure;  
      
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); // 使能GPIOA与TIM5的时钟进行引脚的复用
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  
      
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;  
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;  
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置PA0为下拉输入

 

Counter configuration

The counter is the soul of the timer. Whether we configure the PWM output or input capture, we must first configure the timer:

The above picture is the function of "IM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // AHB->APB1 no frequency division".

TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;    
IM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // AHB->APB1不分频  
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式  
TIM_TimeBaseInitStructure.TIM_Period = ARR;  
TIM_TimeBaseInitStructure.TIM_Prescaler = PR;  
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); // 初始化计数器  

Configuration of input capture channel properties

Further configure TIM5_CH1 according to two different input/output modes:

TIM_ICInitTypeDef TIM_ICInitStructure;    
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;   // 使用TIM5_CH1进行输入捕获实验
TIM_ICInitStructure.TIM_ICFilter = 0;   // 不进行采集滤波
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;  // 上升沿触发
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;  // 一个有效上升沿触发一次输入捕获
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;  // 选择通道1的信号作为触发输入捕获的信号
TIM_ICInit(TIM5, &TIM_ICInitStructure); // 配置TIM5_CH1的输入捕获属性  

 

Interrupt priority configuration

Configure NVIC interrupt vector priority:

NVIC_InitTypeDef NVIC_InitStructure;      
VIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;   // TIM5中断使能
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;   // 抢占优先级 = 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  // 响应优先级 = 1
NVIC_Init(&NVIC_InitStructure); // 配置NVIC嵌入式中断向量优先级  

 

TIM5_CH1 interrupt type selection

Further configure the TIM5_CH1 interrupt trigger type:

TIM_ITConfig(TIM5, TIM_IT_CC1|TIM_IT_Update, ENABLE); // 使能TIM5_CH1的中断  

 

Timer enable

Timer TIM5 enable:

TIM_Cmd(TIM5, ENABLE); // TIM5定时器使能  

 

Interrupt service function for measuring effective pulse

Here we want to measure the length of time the button is pressed, that is, to calculate the "high level duration":

u8 TIM_CAP_STA = 0;  
u16 TIM_CAP_VAL = 0;  
  
void TIM5_IRQHandler()  
{  
    if((TIM_CAP_STA&0x80) == 0) // 捕获未完成  
    {  
        if(TIM_GetITStatus(TIM5, TIM_IT_CC1) == SET)  
        {  
            if((TIM_CAP_STA&0x40) == 0) // 上升沿  
            {  
                TIM_SetCounter(TIM5, 0); // 开始计数  
                TIM_CAP_VAL = 0;  
                TIM_CAP_STA = 0;  
                TIM_CAP_STA |= 0x40; // 高电平  
                TIM_OC1PolarityConfig(TIM5, TIM_OCPolarity_Low); // 捕获下降沿  
            }  
            else // 下降沿  
            {  
                TIM_CAP_VAL = TIM_GetCapture1(TIM5); // 捕获当前值  
                TIM_CAP_STA |= 0x80; // 完成捕获  
                TIM_OC1PolarityConfig(TIM5, TIM_OCPolarity_High); // 捕获上升沿  
            }  
        }  
        if(TIM_GetITStatus(TIM5, TIM_IT_Update) == SET)  
        {  
            if(TIM_CAP_STA&0x40)  
            {  
                if((TIM_CAP_STA&0x3F) == 0x3F)  
                {  
                    TIM_CAP_VAL = 0xFFFF;  
                    TIM_CAP_STA |= 0x80;  
                }  
                else  
                {  
                    TIM_CAP_STA++;  
                }  
            }  
        }  
    }  
    TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update);  
}  

 

Focus: The logic of writing interrupt service functions

 

 

u8 TIM_CAP_STA = 0;  
u16 TIM_CAP_VAL = 0;  
  
void TIM5_IRQHandler()  
{  
    if((TIM_CAP_STA&0x80) == 0) // 捕获未完成  
    {  
        if(TIM_GetITStatus(TIM5, TIM_IT_CC1) == SET)  // TIM5_CH1捕获有效的脉冲沿
        {  
            if((TIM_CAP_STA&0x40) == 0) // 前一刻为低电平——脉冲沿为上升沿  
            {  
                TIM_SetCounter(TIM5, 0); // 开始计数  
                TIM_CAP_VAL = 0;  
                TIM_CAP_STA = 0;  
                TIM_CAP_STA |= 0x40; // 更新电平状态  
                TIM_OC1PolarityConfig(TIM5, TIM_OCPolarity_Low); // 捕获下降沿  
            }  
            else // 前一刻为高电平——脉冲沿为下降沿  
            {  
                TIM_CAP_VAL = TIM_GetCapture1(TIM5); // 捕获当前值  
                TIM_CAP_STA |= 0x80; // 完成捕获  
                TIM_OC1PolarityConfig(TIM5, TIM_OCPolarity_High); // 捕获上升沿  
            }  
        }  
        if(TIM_GetITStatus(TIM5, TIM_IT_Update) == SET)  // 计数器溢出
        {  
            if(TIM_CAP_STA&0x40)  // 如果为高电平,计数器溢出则会被记录
            {  
                if((TIM_CAP_STA&0x3F) == 0x3F)  // 如果计数器溢出次数过多
                {  
                    TIM_CAP_VAL = 0xFFFF;  // 此时的捕获值设为MAX
                    TIM_CAP_STA |= 0x80;  // 已经完成高电平的捕获
                }  
                else  
                {  
                    TIM_CAP_STA++;  // 计数器溢出次数+1
                }  
            }  
        }  
    }  
    TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update);  // 清除中断标志
}  

After the following procedures, we can already judge: Is the level at this time valid?

if(TIM_GetITStatus(TIM5, TIM_IT_CC1) == SET)  // TIM5_CH1捕获有效的脉冲沿
        {  
            if((TIM_CAP_STA&0x40) == 0) // 前一刻为低电平——脉冲沿为上升沿  
            {  
                TIM_SetCounter(TIM5, 0); // 开始计数  
                TIM_CAP_VAL = 0;  
                TIM_CAP_STA = 0;  
                TIM_CAP_STA |= 0x40; // 更新电平状态  
                TIM_OC1PolarityConfig(TIM5, TIM_OCPolarity_Low); // 捕获下降沿  
            }  
            else // 前一刻为高电平——脉冲沿为下降沿  
            {  
                TIM_CAP_VAL = TIM_GetCapture1(TIM5); // 捕获当前值  
                TIM_CAP_STA |= 0x80; // 完成捕获  
                TIM_OC1PolarityConfig(TIM5, TIM_OCPolarity_High); // 捕获上升沿  
            }  
        } 

Then, we record the number of overflows according to whether it is a high level: when it is at a high level, that is, an effective level, the counter overflow is considered valid and recorded.

if(TIM_GetITStatus(TIM5, TIM_IT_Update) == SET)  // 计数器溢出
        {  
            if(TIM_CAP_STA&0x40)  // 如果为高电平,计数器溢出则会被记录
            {  
                if((TIM_CAP_STA&0x3F) == 0x3F)  // 如果计数器溢出次数过多
                {  
                    TIM_CAP_VAL = 0xFFFF;  // 此时的捕获值设为MAX
                    TIM_CAP_STA |= 0x80;  // 已经完成高电平的捕获
                }  
                else  
                {  
                    TIM_CAP_STA++;  // 计数器溢出次数+1
                }  
            }  
        } 

Difficulty: What should I do when the high level lasts for too long?

if(TIM_GetITStatus(TIM5, TIM_IT_Update) == SET)  // 计数器是否溢出
{  
    if(TIM_CAP_STA&0x40)  // 是否为有效电平(高电平)
    {  
        if((TIM_CAP_STA&0x3F) == 0x3F)  // 捕获次数已经为MAX
        {  
            TIM_CAP_VAL = 0xFFFF;  // 捕获值为MAX
            TIM_CAP_STA |= 0x80;  // 直接标记:已经捕获完成
        }  
        else  
        {  
            TIM_CAP_STA++;  
        }  
    }  
} 

 

When the high level duration is too long and the counter overflows too many times, we do not need to wait for the "falling edge pulse", directly set the "capture completion flag", and set the capture value to what the data type can represent MAX value.

Code example

Main.c

#include "key.h"  
#include "timer.h"  
#include "usart.h"  
#include "led.h"  
#include "delay.h"  
  
int main()  
{  
    extern u8 TIM_CAP_STA;  
    extern u16 TIM_CAP_VAL;  
    u32 temp = 0;  
      
    delay_init(); // 初始化systick时钟  
    KEY_InitConfig(); // 初始化KEY_WAKEUP  
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置中断优先级分组  
    TIM_CAPTURE_InitConfig(0xFFFF, 72-1); // 周期为65.535s  
    uart_init(115200); // 初始化串口  
      
    while(1)  
    {  
        delay_ms(10);  
        if((TIM_CAP_STA&0x80) == 0x80)  // 完成有效电平的捕获
        {  
            temp = TIM_CAP_VAL + (TIM_CAP_STA&0x3F) * 0xFFFF; // 计算脉冲持续时间  
            printf("%d\r\n", temp); // 串口打印  
            TIM_CAP_STA = 0; // 清空TIM_CAP_STA内的数据以便再次捕获
        }  
    }     
}

  

Timer.c

#include "timer.h"  
#include "stm32f10x.h"  
#include "sys.h"  
#include "led.h"  
  
void TIM_CAPTURE_InitConfig(u16 ARR, u16 PSC)  
{  
    GPIO_InitTypeDef GPIO_InitStructure;  
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;  
    TIM_ICInitTypeDef TIM_ICInitStructure;  
    NVIC_InitTypeDef NVIC_InitStructure;  
      
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); // 使能APB1总线的TIM5时钟  
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能APB2总线的GPIOA时钟  
      
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
    GPIO_Init(GPIOA, &GPIO_InitStructure); // PA0初始化为下拉输入  
      
    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // AHB = APB1  
    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式  
    TIM_TimeBaseInitStructure.TIM_Period = ARR;  
    TIM_TimeBaseInitStructure.TIM_Prescaler = PSC;  
    TIM_TimeBaseInit(TIM5, &TIM_TimeBaseInitStructure); // 配置计数器的属性  
      
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;  
    TIM_ICInitStructure.TIM_ICFilter = 0;  
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;  
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;  
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;  
    TIM_ICInit(TIM5, &TIM_ICInitStructure); // 配置TIM5的输入属性  
      
    NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;  
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  
    NVIC_Init(&NVIC_InitStructure); // 配置TIM5的中断优先级  
      
    TIM_ITConfig(TIM5, TIM_IT_CC1|TIM_IT_Update, ENABLE); // 使能TIM5_CH1的中断  
      
    TIM_Cmd(TIM5, ENABLE); // 使能TIM5  
}  
  
u8 TIM_CAP_STA = 0;  
u16 TIM_CAP_VAL = 0;  
  
void TIM5_IRQHandler()  
{  
    if((TIM_CAP_STA&0x80) == 0)  
    {  
        if(TIM_GetITStatus(TIM5, TIM_IT_CC1) == SET)  
        {  
            if((TIM_CAP_STA&0x40) == 0x00) // 上升沿  
            {  
                TIM_CAP_STA = 0; // 初始化  
                TIM_CAP_VAL = 0; // 初始化  
                TIM_SetCounter(TIM5, 0); // 计数器开始计数  
                TIM_CAP_STA |= 0x40;  // 高电平  
                TIM_OC1PolarityConfig(TIM5, TIM_OCPolarity_Low); // 改变极性/改变有效电平——捕获上升沿  
            }  
            else // 下降沿  
            {  
                TIM_CAP_VAL = TIM_GetCapture1(TIM5); // 捕获此时计数器的值  
                TIM_CAP_STA |= 0x80; // 完成捕获  
                TIM_OC1PolarityConfig(TIM5, TIM_OCPolarity_High); // 改变极性/改变有效电平——捕获下降沿  
            }  
        }  
        if(TIM_GetITStatus(TIM5, TIM_IT_Update) == SET) //  计数器溢出  
        {  
            if((TIM_CAP_STA&0x40) == 0x40) // 上升沿  
            {  
                if((TIM_CAP_STA&0x3F) == 0x3F) // 当溢出次数过多,就默认"捕获完成并且此时计数器的值为MAX"  
                {  
                    TIM_CAP_VAL = 0xFFFF;  
                    TIM_CAP_STA |= 0x80; // 完成捕获  
                }  
                else  
                {  
                    TIM_CAP_VAL++; // 溢出次数递增  
                }  
            }  
        }  
    }  
    TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); // 清楚中断标志位  
}  

 

Timer.h

#ifndef _TIMER_H  
#define _TIMER_H  
  
#include "sys.h"  
  
void TIM_CAPTURE_InitConfig(u16 ARR, u16 PR);  
  
#endif  

 

Key.c

#include "key.h"  
#include "stm32f10x.h"  
  
void KEY_InitConfig()  
{  
    GPIO_InitTypeDef GPIO_InitStructure;  
      
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能总线时钟  
      
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
    GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化KEY_WAKRUP为下拉输入  
}

  

Key.h

#ifndef _KEY_H  
#define _KEY_H  
  
void KEY_InitConfig();  
  
#endif  

 

operation result

 

Guess you like

Origin blog.csdn.net/weixin_45590473/article/details/108164766