首页 关于
树枝想去撕裂天空 / 却只戳了几个微小的窟窿 / 它透出天外的光亮 / 人们把它叫做月亮和星星
目录

计时器之输入捕获

上一篇文章中, 我们利用STM32的输出比较通道产生了PWM信号,用于控制LED灯的亮度。在本节中,我们将利用输入捕获通道, 来测量一个PWM信号的频率和占空比。

1. 输入通道结构

下图1描述了用作输入的通道1的结构图。外部信号通过引脚TI1输入,STM32的计时器模块通过一个向下计数的滤波器对输入信号进行必要检测,去除干扰后, 得到TI1F信号。这个滤波器本质上就是一个向下计数器,我们通过CCMR的ICF位描述计数的初值。当它检测到输入信号变化后,开始向下计数, 如果计数到0输入的信号没有再改变,它就认为检测到了一个稳定的信号变化。滤波器的工作需要由一个频率在\(f_{DTS}\)的时钟驱动, \(f_{DTS}\)是从计时器时钟CK_INT分频出来的,我们可以通过控制寄存器CR1的CKD位指定分频系数。

图1 计时器输入通道结构图
经过滤波的信号TI1F将被输入到一个边缘检测器中,在其中判定捕获到了上升沿还是下降沿。我们通过寄存器CCER中的CCxP和CCxNP位指定计时器捕获的边沿类型。 进而我们就得到了捕获信号TI1FP1。

通过查看手册我们知道,输入通道IC1的捕获信号可以映射到TI1和TI2两个引脚中的一个。所以在这个图中,我们也看到了来自TI2的边沿信号TI2FP1。 最后输入通道IC1具体使用的是哪个引脚上的边沿信号,我们需要通过寄存器CCMR的CC1S位描述清楚。

边沿信号IC1还需要经过一个分频器分频后,产生IC1PS信号。分频系数可以通过CCMR的ICPS位定义。当我们通过寄存器CCER的CC1E位使能通道1后, IC1PS信号就会作为捕获的信号输入到时基单元。如果我们通过寄存器EGR的CC1G(这点没有在图上体现),那么每次产生IC1PS信号后, 都将产生一个输入捕获事件。此时计时器将把时基单元中当前计数值(CNT寄存器)写到CCR1寄存器中。 我们通过读取CCR1的数据就可以获知连续两次捕获事件之间的时间间隔。合理的配置捕获事件,我们就可以测量输入PWM信号的频率和占空比。

2. 测量PWM信号周期

上一篇文章中, 我们用TIM14的通道1产生了PWM信号来控制LED灯的亮度。通过查看原理图发现, 探索者开发板把PC6和PA8两个IO引脚用排针引出来了,这两个引脚正对应着TIM8的通道1和TIM1的通道1。 用杜邦线直接把PC6和PA8两个引脚连接起来,让PC6输出PWM信号,PA8用作TIM1的通道1捕获和测量PC6的PWM信号周期。 相关例程可以在这里下载

首先,我们配置TIM1的时基单元,令其向上计数。向预分频寄存器PSC中写入数据167,以产生一个1MHz的计数频率。因为是要测量PWM的信号周期, 所以需要在每次捕获到信号的上升沿时清零计数。考虑到这一点我们给装载寄存器ARR中写入了65535,它是16位数中最大的那个。 配置EGR寄存器的CC1G位,使得捕获到边沿信号后就产生一个捕获事件。因为我们用TIM8产生的PWM信号频率还是比较快的,TIM1的计数是不可能溢出的, 所以这里做了一些简化,没有开启UG位产生更新事件。如果检测的信号频率很小,TIM1就可能计数溢出,此时我们还需要开启UG和计数溢出中断, 处理这一事件。

    TIM1->CR1.bits.DIR = TIM_COUNT_DIR_UP;
    TIM1->PSC = 167;
    TIM1->ARR = 65535;
    TIM1->EGR.bits.CC1G = 1;
    TIM1->CR1.bits.ARPE = 1;

接着,我们配置TIM1的通道1为输入捕获通道。宏定义TIM_Channel_Mode_Input1的值为1,表示通道1的捕获信号IC1被映射到了引脚TI1上, 也就是我们这里的PA8引脚。向ICxPSC位写0(宏定义TIM_ICMode_PSC_0)表示不对边沿信号进行分频处理。最后选择了一个最苛刻的滤波方式。 通过函数timer_set_ccmr将这些配置信息写入到CCMR寄存器中,通道1将工作在输入捕获模式下。

    union timer_chanel_mode cfg;
    cfg.ic.CCxS = TIM_Channel_Mode_Input1;
    cfg.ic.ICxPSC = TIM_ICMode_PSC_0;
    cfg.ic.ICxF = 0xF;
    timer_set_ccmr(TIM1, 1, cfg);

然后,配置CCER寄存器。配置其中的CCxP和CCxNP位,使得边沿检测器只捕获上升沿。向CCxE写1开启捕获功能。

    union timer_chanel_en cen;
    cen.bits.CCxE = 1;
    cen.bits.CCxP = 0;
    cen.bits.CCxNP = 0;
    timer_set_ccer(TIM1, 1, cen);

最后,我们配置开启捕获中断,打开计时器。

    TIM1->DIER.bits.CC1IE = 1;
    TIM1->CR1.bits.CEN = 1;

完成对计时器TIM1的配置之后,我们还需要在它的中断服务函数中把记录在CCR1中的数值取出来,并清零计数器以开始下一个测量周期。 这样gDuring中将记录测量的PWM周期,我们用\(10^6\)除以gDuring中的数值就可以换算出PWM的频率。

    uint32 gDuring = 0;
    void TIM1_CC_IRQHandler(void) {
        if (1 == TIM1->SR.bits.CC1IF) {
            gDuring = TIM1->CCR1;
            TIM1->CNT = 0;
        }
        TIM1->SR.bits.CC1IF = 0;
    }

3. 测量PWM信号的占空比

现在我们来考虑如何测量一个PWM信号的占空比。我们可以配置输入通道捕获上升沿还是下降沿,甚至是两种沿都捕获。但是无论哪种捕获方式, 我们都只能记录连续两次捕获事件发生的时间间隔。也就是说只是用一个通道,我们只能采集到信号的周期、高电平时间或者低电平时间。 如果我们有两个寄存器,用其中一个记录连续两次上升沿的时间,就可以获得PWM信号的周期;再用另一个寄存器记录从上升沿到下降沿的时间, 我们就可以测得一个周期中高电平的时间。这样就可以计算出PWM信号的占空比了。

为了实现测量占空比的功能,我们将使用第二个输入通道来测量从上升沿到下降沿的时间间隔。在前面的输入通道结构的框图中我们可以看到, 通道IC1可以映射到引脚TI1也可以映射到TI2。 通过查询参考手册,可以看到IC2同样可以映射到TI1或者TI2上。 IC3和IC4则可以分别映射到TI3或者TI4上。可以通过CCMR寄存器的CCxS位指定具体的映射关系。

这样我们的工作就很简单了,再配置一个输入通道IC2,将其映射到TI1上,控制该通道检测下降沿。捕获到下降沿信号后, 计时器将把时基单元中当前计数值写到CCR2中。我们不需要打开IC2检测到的下降沿信号的中断了,只在IC1的中断服务函数中, 把CCR2中记录的高电平时间和CCR1中记录的周期信息读取出来,同时清零计时器就可以计算占空比了。 下面的代码片段,从左往右分别是通道2的配置和中断服务函数。 相关例程可以在这里下载

        cfg.byte = 0;
        cfg.ic.CCxS = TIM_Channel_Mode_Input2;
        cfg.ic.ICxPSC = TIM_ICMode_PSC_0;
        cfg.ic.ICxF = 0xF;
        cen.all = 0;
        cen.bits.CCxE = 1;
        cen.bits.CCxP = 1;
        cen.bits.CCxNP = 0;
        timer_set_ccmr(TIM1, 2, cfg);
        timer_set_ccer(TIM1, 2, cen);
        uint32 gDuring = 0;
        uint32 gDuty = 0;
        void TIM1_CC_IRQHandler(void) {
            if (1 == TIM1->SR.bits.CC1IF) {
                gDuty = TIM1->CCR2;
                gDuring = TIM1->CCR1;
                TIM1->CNT = 0;
            }
            TIM1->SR.bits.CC1IF = 0;
        }

4. 总结

本节我们先讨论了输入捕获通道的结构,在根据这个结构分别测量了PWM的频率和周期。本质上就是配置计时器的CCMR和CCER两个寄存器, 配置输入通道的滤波特性、检测信号(上升沿还是下降沿)、通道的信号源(IC1是映射到了TI1上还是TI2上)。完成了这些配置并在捕获到信号后, 把保存在CCRx中的计数值取出,就可以计算出我们需要的频率、占空比等数据了。

其实我在使用计时器的输入捕获功能时,主要是为了完成各种电机测速。STM32也提供了很多针对这方面需求的方法,我们将在下一节中进行介绍。




Copyright @ 高乙超. All Rights Reserved. 京ICP备16033081号-1