打印
[应用相关]

STM32单片机 - 输入捕获

[复制链接]
1185|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
wowu|  楼主 | 2024-9-7 09:21 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
输入捕获
作用
输入捕获模式可以用来测量脉冲宽度或者测量频率。STM32的定时器,除了TIM6、TIM7,其他的定时器都有输入捕获的功能。



int a = value1;

int b = value2 - value1;

a = value3 - value1



1.测量频率(周期)(上升沿触发或者下降沿触发或者双边沿触发都可)

当捕获通道TIx 上出现上升沿时,发生第一次捕获,计数器CNT 的值会被锁存到捕获寄存器CCR 中,而且还会进入捕获中断,在中断服务程序中记录一次捕获(可以用一个标志变量来记录),并把捕获寄存器中的值读取到value1 中。

当出现第二次上升沿时,发生第二次捕获,计数器CNT 的值会再次被锁存到捕获寄存器CCR 中,并再次进入捕获中断,在捕获中断中,把捕获寄存器 的值读取到value3 中,并清除捕获记录标志。利用value3 和value1 的差值我们就可以算出信号的 周期(频率)。

2.测量脉宽(双边沿触发)

当捕获通道TIx 上出现上升沿时,发生第一次捕获,计数器CNT 的值会被锁存到捕获寄存器CCR 中,而且还会进入捕获中断,在中断服务程序中记录一次捕获(可以用一个标志变量来记录),并把捕获寄存器中的值读取到value1 中。

然后把捕获边沿改变为下降沿捕获,目的是捕获后面的 下降沿。当下降沿到来的时候,发生第二次捕获,计数器CNT 的值会再次被锁存到捕获寄存器 CCR 中,并再次进入捕获中断,在捕获中断中,把捕获寄存器的值读取到value2,利用value2-value1的差值即可算出脉冲宽度。

同时测量脉宽和频率需要占用两个捕获寄存器(单独测量脉宽和频率只需要一个)



具体过程:

信号由输入通道TI1 进入,信号会被分为两路,一路是 TI1FP1,另外一路是TI2FP2。其中一路是周期,另一路是占空比。(作为触发输入的那一路信号对应的就是 周期,另一路就是对应占空比。作为触发输入的那一路信号还需要设置极性,是上升沿还是下降 沿捕获,一旦设置好触发输入的极性,另外一路硬件就会自动配置为相反的极性捕获,无需软件 配置。一句话概括就是:选定输入通道,确定触发信号,然后设置触发信号的极性即可)

将从模式控制器配置为复位模式(配置寄存器SMCR 的位 SMS[2:0] 来实现),即当我们启动触发信号开始进行捕获的时候,同时把计数器CNT 复位清零。

以下图为例:



PWM 信号由输入通道TI1 进入,配置TI1FP1 为触发信号,上升沿捕获。当上升沿的时候IC1 和 IC2 同时捕获,计数器CNT 清零。

到了下降沿的时候,IC2 捕获,此时计数器CNT 的值被锁存 到捕获寄存器CCR2 中,到了下一个上升沿的时候,IC1 捕获,计数器CNT 的值被锁存到捕获寄 存器CCR1 中。

可知CCR2 测量的是脉宽,CCR1 测量的是周期。

代码编写
任务:利用输入捕获功能测量按键按下期间的脉宽、频率和占空比等,通过usart串口或者oled显示屏进行显示。

步骤
看原理图,确定GPIO和定时器

​ 通过原理图,我们可以看到,开发板上4个按键中,只有WK_UP(PA0)按键连接了定时器,这里我们可以选择定时器TIM5,进行后续的开发。



配置GPIO,时钟使能 我们要使用开发板的引脚,就需要对它进行配置,这里与之前的配置都是一样的,但是有两点需要注意,一个是因为我们要使用定时器的输入捕获功能,所以在GPIO的模式选择时,我们要选择复用模式,使用复用模式时别忘了复用引脚的映射呀,使用GPIO_PinAFConfig函数即可,这个函数用的很多,可以记住!还有一点是,WK_UP按键,和其他按键不同,通过观察原理图我们可以知道,它的另一端是接高电平,而其他3个按键都是接低电平。所以这里我们要选择下拉输入,这样当按键按下时,电路导通,按键生效。

GPIO_InitTypeDef GPIO_InitStructure;//声明结构体
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//时钟使能
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//初始化0号引脚
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF;//复用模式
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_DOWN;//下拉出入模式
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;//快速响应
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource0, GPIO_AF_TIM5);//引脚复用
3.配置定时器

我们要使用定时器的输入捕获功能,所以配置定时器当然是必不可少的啦。这里的定时器配置和之前都是差不多的啦,不同的是,在设置自动重装载值ARR时,我们可以直接设置为最大值,通过查手册中的表可知,TIM5的计数器分辨率是32位的,所以最大值为0xFFFFFFFF,一共8个F,因为2^32,十六进制是4个二进制为一位,因此一共有32/4=8位。

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//定时器结构体
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);//定时器时钟使能
//定时器配置
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//计数方式,向上计数
TIM_TimeBaseStructure.TIM_Period = 0xFFFFFFFF;//自动重装载寄存器,计数器溢出值,ARR计数最大值
TIM_TimeBaseStructure.TIM_Prescaler = 84-1;//PSC预分频系数
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);
4.配置输入通道

与之前设置PWM输出比较类似的,配置输入捕获,与输出比较的结构体都是极其相似的,只需要将 TIM_OCInitTypeDef改为IC即可。特别注意的是,这里结构体初始化使用的不再是与输出比较类似的 TIM_OC1Init 这种初始化函数,而是 TIM_PWMIConfig 。当我们在使用 PWM 输入模式时,需要使用 TIM_ICInitTypeDef 结构体来定义捕获模式的参数,并通过 TIM_PWMIConfig 函数应用这些参数。 TIM_PWMIConfig 函数用于配置定时器的 PWM 输入模式。这种模式允许定时器通过其输入引脚捕获外部 PWM 信号,并测量该信号的频率和占空比。

TIM_ICInitTypeDef  TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICFilter = 0x0; //滤波、消除前面不稳定的电平
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_BothEdge; //双边沿检测,测量脉宽
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //1分频、不分频,跳过n个周期
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //直连模式,直接捕获
TIM_PWMIConfig(TIM5, &TIM_ICInitStructure);
TIM_Channel:配置的输入捕获通道。STMF407通常有4个输入捕获和输出比较通道。这里选择哪一个通 道是由我们之前在原理图上所用的那个引脚上标注的,我们使用的是TIM5_CH1所以就是通道1.

TIM_ICFilter:输入捕获滤波器,用于滤除高频噪声,确保捕获到的信号更加稳定可靠。可以选择滤除几个 波形,没有就写0x0即可。这里我们只是简单观察一下实验现象,所以就不进行滤波了。一般来说,按键按 下和松开一般会有抖动,容易造成一些误差,这时我们可以选择滤波功能来减小误差的影响。

TIM_ICPolarity:设置输入捕获的极性。可以配置为捕获上升沿、下降沿,或者两者都捕获。

TIM_ICPrescaler:输入捕获预分频器,用于降低输入信号的频率。这对于高频信号非常有用,可以避免定 时器溢出。相当于再进行一次分频,这里我们先不设置了。

TIM_ICSelection:选择输入捕获的源。有些定时器支持直接映射到外部引脚,也支持通过内部触发(如其 他定时器的输出)进行捕获。这里我们是捕获按键的电平,所以是直接捕获。

5.中断配置

NVIC_InitTypeDef   NVIC_InitStructure;  //配置NVIC的结构体
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);       
6.定时器使能,,清除中断标志位,中断使能

TIM_Cmd(TIM5, ENABLE);

TIM_ClearITPendingBit(TIM5, TIM_IT_CC1); //清除中断

TIM_ITConfig(TIM5, TIM_IT_CC1,ENABLE); //使能中断
7.重写中断服务函数

中断服务函数的名称仍然需要在 .s 的启动文件中查找,这里我们找到是TIM5_IRQHandler,然后在 it.c 中重写。 ​ 我们需要定义两个全局变量time1、time2,用来接收捕获寄存器的值,可以通过 TIM_GetCapture1 查询。还需要定义一个标志位flag,用来记录捕获的次数。 ​ 与之前的中断服务函数一样,我们也需要判断中断标志位,确定是否进入中断;然后再判断标志位,判断捕获了几次,因为我们设置的是双边沿捕获,所以如果第一次是下降沿,那么第二次就是上升沿,二者的差值就是脉宽的计数次数。然后再乘以计数周期,就是真正的脉宽了。因为 TIM2 是在APB1总线的定时器,所以它的时钟周期为84MHz,在之前的定时器配置中,我们的预分频器 PSC 的值设置的为84,所以它的时钟频率就是1MHz,时钟周期就是1us 。意思是,计数一次用时1us。所以我们可以直接用这里的差值来表示脉宽,单位为 us。

void TIM5_IRQHandler(void)
{
    static u8 key=1;
    static uint32_t press1=0;
    if(TIM_GetITStatus(TIM5,TIM_IT_CC1)==SET)
    {
        if(key)
        {
            press1=TIM_GetCapture1(TIM5);
            printf("%f ",TIM_GetCapture2(TIM5)*1.0);
            printf("%f ",press1*1.0);
            key=0;
        }
        else
        {
            printf("%f ",TIM_GetCapture2(TIM5)*1.0);
            printf("%f\n",(TIM_GetCapture1(TIM5)-press1)*1.0/1000000);
            key=1;
        }
    }
TIM_ClearITPendingBit(TIM5,TIM_IT_CC1);
}

8.主函数编写

int main(void)
{
    pwm();
    Nvic_Init();
    Usart_Init();
    uint32_t cmp=0;
    while(1)
    {

    }
}
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/m0_61256689/article/details/141818002

使用特权

评论回复
沙发
dongnanxibei| | 2024-9-7 15:02 | 只看该作者
昨天21群里还有人问捕获的问题,问PWM如何测量占空比和周期。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

99

主题

4122

帖子

1

粉丝