STM32外设学习--TIM定时器--输入捕获---测频方法(代码编写)
上次我们了解了stm32TIM定时器外设输入捕获的基本工作原理,接下来我们来进行代码的编写。
一.硬件接线图

目前我们输入信号的测试引脚时PA6,信号从PA6进来,待测的PWM信号也是由我们STM32单片机产生的,属于是自给自足了。输出引脚是PA0所以接线的话,用一根跳线,直接从PA0引到PA6就可以了。如果有信号发生器的话,可以直接设置为方波信号输出高电平3.3V低电平0V.,然后直接接到PA6,另外不要忘记共地。

完成我们的接线。
二.程序编写
1.函数了解
![]()
因为我们要用PWM生成待测信号。直接复制PWM驱动呼吸灯代码
![]()
起个名字,然后打开keil5。

我们需要对PWM.c这个文件,进行一下改进。
目前这个代码的逻辑是初始化TIM2的一个通道1,产生一个PWM波形输出引脚是PA0。

然后通过这个函数,可以修改CCR1的值。从而控制PWM的占空比。

但是PWM的频率,是在初始化里面写好了的,是固定的。运行时候调节不太方便。
所以我们加入一个函数,用来调节PWM的频率。
我们知道PWM的频率=更新频率=72M/(PSC+1)/(ARR+1),所以PSC和ARR都可以调节频率,但是占空比=CCR/(ARR+1)。所以通过ARR调节频率的话,还会影响我们的占空比,所以通过PSC调节频率就不会影响占空比。显然比较方便,所以我们的计划是固定ARR的值为100-1。通过调结PSC来改变我们的频率。因为ARR的值为100-1,CCR的值就直接是占空比,用起来非常方便。
所以我们设置频率的话,可以根据分辨率的要求,先设置好ARR,然后PSC决定频率就可以了。

我们需要用到一个单独写入PSC的函数。
![]()
我们看一下第三个参数的解释,指定定时器预分频器的重装模式。

这个参数可以是下面的两个值
1.uodate:预分频器在更新事件重装
2.immediately:预分频器立刻重装。
意思就是影子寄存器预装载的问题,就是你写入的这个值,是立刻生效,还是在更新事件生效
立刻生效可能会在值改变时,产生切断波形现象。
在更新事件生效就是会有一个缓存器,延迟参数的写入时间,等一个周期结束了,再更新事件时候,再统一改变参数。保证我们周期完整。
2.输出程序编写

因为我们要求精度不高,所立即生效就好。

放在.h文件里面声明一下。

这两个函数整体解释就是,通过setcompater改变频率,通过setprescal改变通道1的占空比。

然后编译一下,发现0错误0警告。

![]()
来到主函数调用我们的设置频率函数,因为我们ARR固定是100,然后我们PSC给了720,那么频率目前就是1KHz。

占空比给到50,因为ARR设置好了为100。
这样子之后PA0口就可以输出一个频率1KHz,占空比为50的PWM波形了。
3.输入捕获函数了解

我们添加一下输入捕获的模块。

添加.c和.h文件,输入捕获我们就起名字为IC。

快速写好.h文件。

写出.c文件的框架。

然后按照我们的结构图进行初始化TIM的输入捕获。
![]()
我们打开库函数,发现这个不用想,肯定是初始化输入捕获的函数,老朋友了。

需要注意的是,向输出比较OC有四个通道,一个通道占用一个函数,然而输入捕获是四个通道占用一个函数,但是再结构体里面,有一个额外的参数,可以用来选择配置的是哪个通道。因为可能有交叉通道的配置,所以函数合在一起比较方便。
![]()
这个函数也是用来初始化输入捕获的。跟上一个函数比较,上一个函数只是单一的配置一个通道,然而这个函数可以快速配置两个通道。
![]()
老朋友了给结构体赋一个初始值。
![]()
选择触发源的TRGI

对应我们这里,从模式的触发源选择。比如我们要用到的TI1FP1。
![]()
选择输出触发源TRGO

对应这里,也就是选择主模式输出的触发源
![]()

选择从模式,这个函数对应的就是从模式选择这部分,这下三兄弟就凑齐了,三个函数对应三个需要选择的部分,还是简单明了的。

分别设置通道1,2,3,4的分频器,这个参数结构体里也可以配置。

分别读取四个通道的CCR的值。

跟这四个函数是对应的,读写的都是CCR寄存器的值
输出比较模式下,CCR是只写的要用setcompare进行写入
输入捕获模式下,CCR是只读的要用getcapture进行读取
4.编写程序:1


因为前面经常写GPIO,TIM的配置,所以我们复制一下。然后逐条进行修改。
![]()
首先我们把开启时钟的定时器切换为TIM3,因为我们需要TIM2输出PWM波形,我们需要更换输入捕获的定时器,然后TIM3也是APB1的外设,

我们查一下引脚定义,TIM3的通道1和通道2,对应PA6和PA7,通道3和通道4,对应PB0和PB1。
我们计划用TIM3的通道1引脚,也就是PA6。

所以要将pin0改为pin6。
![]()
模式更改为上拉模式。
![]()
然后选择内部时钟,让然后选择我们的TIM3。
![]()
之后为了防止计数溢出,我们将ARR的值调整为最大,65536。
接着是PSC预分频器的值,这个值根据我们上次分析,它决定了测周法的基准频率fc,72M/预分频,就是计数器自增的频率,也是计数的标准频率
![]()
我们给72-1,这样子基准频率就是72MHz/72=1MHz。比较方便计算。

时基单元初始化,改为TIM3。
![]()
初始化输入捕获的函数

找到函数的结构体,起个新名字,然后把变量都印出来。
![]()
选择输入捕获的通道,因为ICInit的函数只有一个,,所以需要用这个参数来选择通道。

我们转到这个参数的定义,可以看到有四个通道,需要哪个通道,就写入哪个函数就可以了。
![]()
因为我们计划的是使用TIM3的通道1,所以我们选择这个参数来选择通道1。
![]()
这个参数是用来选择输入捕获的滤波器。

对应这里,如果你的信号有毛刺和噪声,可以选择增大滤波器参数,来减小干扰。

转到定义,可以看到解释是可以选择0x0到0xF之间的任意一个数。每个数值对应的采样频率和采样次数手册中有定义。
![]()
我们给点滤波。


这个就是对应我们的极性选择。选择上升沿触发还是下降沿触发。

跳转到定义,有三个参数,分别是上升沿触发,下降沿触发,边沿触发。
![]()
我们选择上升沿触发。
![]()
配置我们信号的分频器

对应这个。不分频是每次触发都有效,而分频就是每隔一次有效一次。

跳转到定义,DIV1就是不分频,DIV2就是2分频以此类推,然后这个值只能是系统给定的,不能我们自己随意写入。
![]()
我们选择DIV1也就是不分频。
![]()
选择触发信号从哪个引脚输入

是用来配置数据选择器的,可以选择直连通道或者交叉通道。

转到定义我们可以看到,directti就是我们的直连通道的输入,indirectti就是交叉通道的输入,TRC引脚我们暂时不用,上次原理图也提起过。
![]()
我们选择直链通道。
![]()
触发源选择函数。
转到定义

这里面给了我们八个触发源

和这几个主从模式一一对应。
![]()
我们选择这个。
![]()
编写程序。
![]()
设置从模式。

转到定义,这里给出了四个从模式。

对应我们下面四个从模式。

上面这三个从模式,是给编码器接口用的。
![]()
设置从模式为清零。

最后我们启动TIM3的定时器。

写一个频率计算的函数,需要执行我们的公式。

fc=72MHz/(PSC+1),目前PSC+1为72-1,所以我们的fc就是1MHz。N就是读取CCR的值。
![]()
读取CCR值的函数。

这样子就可以了。

放在.h文件中声明。

来到主程序编写,因为我们以及把PWM给到了PA0所以,只需要让PA6进行捕获就可以了。

可以看到目前,待测的频率是1001Hz。这符合我们±1的概念,但是看着别扭,我们给补回来
![]()

然后就可以正常显示了。
5.程序编写:2

复制我们上一个程序。

我们需要配置为两个通道同时捕获一个引脚的模式。
![]()
需要用到PWMI这个模式。

我们只需要调用这个函数,然后把地址传过去就可以了,不用再去选择第二个通道,然后下降沿触发,然后交叉连接等等。
这个函数会,自动把剩下的通道,设置为相反的配置。比如我们传入的是通道1,上升沿,直链,那么通道二就是下降沿,交叉连接。
我们知道高电平的计数值存在CCR2里整个周期的计数值存在CCR1里

然后我们编写获取占空比的函数。用CCR2/CCR1就可以得到占空比了,得出的结过在0~1之间,我们给这个结过×100,加个%就是我们的占空比了。实测会少一个数,我们来给他加一,把这个数变正常。
我感觉是因为cnt是在0,开始计数的,所以记1000个数的话实际上是记录到999,加1给补园一下。

放入.h文件引用一下。

主函数稍微复制,粘贴修改一下。这样子程序就完成了。

编译下载到单片机。

测量结果,频率1K,占空比50。这样子我们的程序就完成了。

修改我们的占空比以及频率,在进行测试。此时频率应为100Hz,占空比为80%。

看到我们的输出结果,没有问题。
6.测频率的性能
最后我们来研究一下,测频率的范围

我们现在给出的是,标准频率为1MHz,计数器最高可以记到65535。所以所测得的最低频率因该为1MHz/65535,大概等于15Hz,如果信号频率再低的话,计数器就记录满了,就会溢出。如果想要更低的频率我们可以把预分频改大一点,这样子所支持的最低频率就会更低。
最大可测量周期 = 65535 + 1 = 65536 个时钟周期
最大周期时间 = 65536 / 1000000Hz = 0.065536秒
最低频率 = 1 / 0.065536 ≈ 15.26Hz
例1:预分频系数 = 720
TIM_Prescaler = 720 - 1;
计数频率 = 72MHz / 720 = 100kHz
最大测量周期 = 65536 / 100000 = 0.65536秒
最低频率 ≈ 1.5Hz
例2:预分频系数 = 7200
TIM_Prescaler = 7200 - 1;
计数频率 = 72MHz / 7200 = 10kHz
最大测量周期 = 65536 / 10000 = 6.5536秒
最低频率 ≈ 0.15Hz
正负1误差,就是记100个数,误差一个相对误差就是1%,
记1000个数误差一个,相对误差就是千分之一
所以正负1误差可以认为是1/计数值。如果要求误差为千分之一频率为上限,那么这个频率的上线应该为1M/1K=1KHz,如果误差要求为1%,那么频率上线就为1M/100=10KHz。
如果向提高频率的上限,就要把PSC降低一些。如果要求更高的频率,我们就要考虑测频法了。
