STM32-输入捕获模式测频率PWMI模式测频率占空比(十五)

发布时间:2026/6/4 22:19:54

STM32-输入捕获模式测频率PWMI模式测频率占空比(十五) 输入捕获模式测频率目前我们这个测量信号的输入引脚啊是 PA6信号从 PA6 进来待测的 PWM 信号也是 STM32 自己生成的输出引脚是 PA0。所以接线这里直接用一根线把 PA0 引到 PA6 就行了。如果你有信号发生器的话也可以设置成方波信号输出高电平 3.3 伏低电平 0 伏然后直接接到 PA6。另外也别忘了共地这样接线也行。那这就是接线图。目前我们这两个代码的接线都是一样的哈都是接一根线从 PA0 到 PA6。来看一下面包板硬件电路这里直接找一根面包板飞线一端接在 PA6另一端接在 PA0这样接线就完成了。然后回到工程文件夹。之前的这些代码先删掉。然后 PWM 模块这里我们还要再进行一些改进。目前这个代码的逻辑是初始化 TIM2 的通道 1产生一个 PWM 波形输出引脚是 PA0 哈。然后通过下面这个 SetCompare1 的函数可以调节 CCR1 寄存器的值从而控制 PWM 的占空比。但是目前 PWM 的频率啊是在初始化里写好了的是固定的运行的时候调节不太方便。所以我们在最后再加一个函数用来便捷的调节 PWM 频率哈。如何调节 PWM 频率呢通过公式我们知道 PWM 频率等于更新频率等于 72 兆除以 PSC 加一除 AR 加一。所以 PSC 和 AR 都可以调节频率但是占空比等于 CCR 除以 AR 加一。所以通过调节 AR R调节频率还同时会影响到占空比。而通过 PSC 调节频率不会影响占空比啊显然比较方便。所以我们的计划是固定 AR R为 100-1通过调节 PSC 来改变 PWM 频率。另外 ARR 为 100-1 CCR 的数值直接就是占空比啊用起来比较直观。所以可以看出这个 PWM 模块还是比较复杂的啊频率和占空比都需要计算并不是我们想象的那样初始化好了一个寄存器控制频率另一个寄存器控制占空比。其实读多少就是多少没那么方便啊。当然实际使用也是有技巧的一般我们可以根据分辨率的要求先确定好 ARR 比如分辨率 1% 就足够了那 ARR 给 100-1这样 PSC 决定频率 CCR 决定占空比这就好算了是吧如果我们想要更高的分辨率比如 0.1%那 ARR 就先固定 1000-1这样频率就是 72 兆除以分频除 1000占空比就是 CCR 除 1000这样也好算啊。好在这里目前 ARR 我们固定给 100-1 PSC 初始化这里的哈就先不管我们后面再写一个函数在初始化之后单独修改 PSC。void PWM_ Set_ Prescaler uint16_ t Prescaler。 在这里面我们就要调用库函数里单独写入 PSC 的函数了。在 TIM 点 H 里找一下。这里这个 TIM_ PSC_ Config 就是单独写入 PSC 的函数。因为这个函数还有一个重装模式的参数啊所以它并不叫 Set_ PSC而叫 PSC_ Config。这是这个库的命名规范啊就是写入 PSC。我们复制这个函数然后放到这里。参数第一个 TIMX我们使用的是定时器 2所以给 TIM2。第二个 PSC 就是要写入 PSC 的值我们直接把外层函数的这个参数传进去。第三个 reload mode 就是重装模式我们转到定义看一下解释哈。这里的参数解释是指定定时器预分频器的重装模式这个参数可以是下面的其中一个值。第一个 update 预分频器在更新事件重装第二个 immediate 预分频器立即重装。说白了就还是影子寄存器预装载这个问题啊就是你写入的值是立刻生效还是在更新事件生效。立刻生效可能会在值改变时产生切断波形的现象比如你 PWM 一个周期刚过去一半立刻生效了那就立刻切断当前波形开始新的一个周期。在频率变化时哈这里会出现一个不完整的周期。那在更新事件生效呢就是会有个缓存器延迟参数的写入时间等一个周期结束了。在更新事件时再统一改变参数保证每个周期的完整啊。那目前我们这个程序使用哪个参数呢其实无所谓我们要求不高哪个都行啊。这里就选择立刻生效吧复制然后放在第三个参数的位置。这样我们这个 SetPSC 的函数就写好了我们把它放到头文件声明一下啊。这样这个模块就改造好了通过 SetPrescale 改变频率通过 Set Compare1 改变通道一的占空比编译一下。使用一下先按 PWM 一例的初始化再调用 PWM Set Prescaler 参数给个 720-1。计算频率的公式放在右边啊频率 FREQ 等于 72 兆除以 PSC 加一再除 ARR 加一。目前 ARR 加一是 100所以目前频率经过计算是 1000 赫兹。然后调用 PWM Set Compare1参数给 50。计算占空比的公式也放到右边啊。占空比 DUTY 等于 CCR 除以 AR 加一 ARR 加一是 100所以目前占空比是 50%。这样调用之后 PA0 引脚就能输出一个频率 1000 赫兹、占空比 50% 的带磁信号了。编一下。没问题啊下载看一下。当然现在直接看是看不出什么现象的哈如果你有示波器的话可以先验证一下 PA0 的波形看一下是不是符合要求。那接下来我们回到程序开始写输入捕获的代码哈自己测自己看一下 PA0 口的频率和占空比是多少。那还是先建一个模块初始化的步骤呢也是对照着 PPT 的这个基本结构来。目前我们需要配置电路连接成图四的这个样子啊所以步骤就是第一步 RCC 开启时钟把 GPIO 和 TIM 的时钟打开。第二步 GPIO 初始化把 GPIO 配置成输入模式一般选择上拉输入或者浮空输入模式啊。第三步配置时基单元让 CNT 计数器在内部时钟的驱动下自增运行这一步和之前的代码是一样的。第四步配置输入捕获单元包括滤波器啊、极性啊。直连通道还是交叉通道分频器这些参数用一个结构体就可以统一进行配置了哈。第五步选择同模式的触发源触发源选择为 TI1FP1 啊这里调用一个库函数给个参数就行了。第六步选择触发之后执行的操作执行 Reset 操作这里也是调用一个库函数就行了最后当这些电路都配置好之后调用 TIM cmd 函数开启定时器这样所有的电路就能配合起来按照我们的要求工作了。当我们需要读取最新一个周期的频率时直接读取 CCR 寄存器然后按照 FC 除 N 计算一下就行了。这就是整个程序的思路。那思路清楚了我们回到 Keil 来看一下库函数。打开 TIM 点 h 的文件看一下。首先 TIM_ IC_ Init 这个根据以往的经验一看就知道肯定就是用结构体配置输入捕获单元的函数。第一个参数选择哪个定时器第二个参数就是包含各个配置的结构体。另外注意啊输入捕获和输出比较都有四个通道 OC_ IT 四个通道像这样每个通道单独占一个函数。而 IC_ IT 呢四个通道是共用一个函数的在结构体里会额外有个参数可以用来选择具体是配置的哪个通道。因为可能有交叉通道的配置哈所以函数合在一起比较方便。然后继续下面还有一个输入捕获的初始化函数 TIM_ InputCapture_ 初始化。这个函数和上一个函数类似哈都是用于初始化输入捕获单元的。但是上一个函数只是单一的配置一个通道而这个函数可以快速配置两个通道把外侧电路结构配置成我们 PPT 这里展示的 PWM I 模式这个等会我们写第二个代码的时候会用到啊。然后往下看 TIM ICStruct_ IT 可以给输入捕获结构体赋一个初始值。然后继续往下看 TIM SelectInputTrigger 选择输入触发源 TRGI这个函数就对应 PPT 的这里重模式的触发源选择调用这函数就能选择重模式的触发源了比如我们本节要用的 TR1FP1然后继续看这个 TIM_ Select_ Output_ Trigger 选择输出触发源 TRGO 这个应该好理解吧它对应的就是 PPT 的这里选择主模式输出的触发源。然后继续 TIM_ Select_ Slave_ Mode 选择从模式好这下三兄弟就凑齐了这个函数对应的就是 PPT 这里的从模式选择的部分三个函数对应这三个需要选择的部分使用还是比较简单明了的哈然后继续往下看。这里 TIM_ SetIC1234波形发生器。分别单独配置通道 1234 的分频器哈这个参数结构体里也可以配置是一样的效果。最后就是 TIM_ GetCapture1234分别读取四个通道的 CCR。这四个函数和上面的 SET_ Compare1234 是对应的读写的都是 CCR 寄存器。输出比较模式下 CCR 是只写的要用 SET_ Compare 写入。输入捕获模式下 CCR 是只读的要用 GET_ Capture 读出。这就是这函数。好到这里函数介绍就完成了。现在思路有了函数也有了我们就来开始写程序。首先前面几步开启时钟啊配置 GPIO 啊配置时基单元啊我们之前已经写过很多次了哈所以就直接到之前的代码复制一下。我们打开这个 PWM 点 C 把这前面的三部分代码复制一下哈。之后回到 IC 一的这里粘贴。这三行删掉哈。那这些就也是我们本次代码的前三步开启时钟配置 GPIO 配置时基单元。接着我们来一一修改一下第一个开启时钟这里开启的是 TIM2我们这个代码还需要 TIM2 输出 PWM 哈所以输入捕获的定时器要换一个我们就换到 TIM3 TIM3 也是 APB1 的外设所以函数还是 APB1 的这个。之后 GPIO 的时钟啊这个就要查一下引脚定义表了我们看一下引脚定义。在这里可以看到 TIM3 的通道一和通道二对应 PA6 和 PA7通道三和通道四对应 PB0 和 PB1。所以引脚需要根据实际需求来哈我们本次代码计划用 TIM3 的通道一引脚所以引脚就是 PA6。如果你选择其他通道或者其他定时器那这个引脚就需要变了哈。回到代码我们计划用 PA6 的通道一所以开启 GPIOA 的时钟没问题啊。然后 GPIO 初始化我们要用 PA6 引脚所以 GPIO pin 0 改成 pin 6下面初始化 GPIOA 没问题啊。之后是 GPIO 模式这里需要 GPIO mode IPU 上拉输入那这样 GPIO 配置就完成了。接下来就是时基单元选择内部时钟没问题但是这里定时器要换成 TIM3 啊别忘了。然后是时基单元的参数 ARR 自动重装载值根据上一小节的分析啊我们最好要设置大一些防止计数溢出。这里我们就给最大值65536-1也就是 16 位的寄存器可以满量程计数哈。之后是 PSC 预分频器根据上一小节的分析啊这个值决定了测周法的标准频率 FC。 72 兆除预分频就是计数器自身的频率就是计数标准频率。这个呢需要根据你信号频率的分布范围来调整哈我们暂时先给 72-1这样标准频率就是 72 兆除 72 等于 1 兆赫兹比较方便计算哈。然后计数器还是采用向上计数的模式。下面时基单元初始化改成 TIM3把这些参数配置到 TIM3 的时基单元哈这样时基单元就配置好了。那接下来按照流程我们进入第四步初始化输入捕获单元。那接下来按照流程我们进入第四步初始化输入 5 个单元到 TIM 点去里看一下。我们要用这个 TIMIC _Init函数复制然后放到这里。第一个参数给 TIM3第二个参数是结构体。我们跳转到函数的定义哈在上面这里有结构体的类型名复制。在这上面粘贴起个变量名叫 TIM_ IC_ Init_ Structure复制一下。然后把结构体成员都引出来。最后把这个结构体的地址放到 IC_ Init 函数里面。好我们来看一下参数第一个 TIM_ Channel这个就是我们刚才说的选择通道的那个参数哈因为 IC_ Init 函数只有一个所以要靠结构体的这个参数来指定是配置哪个通道。转到定义看一下这里干啥我们 CTRL F 搜索下这个参数列表这里通道一二三四四个参数需要配置哪个通道就选哪个参数。目前我们计划使用的是 TIM3 的通道一所以选择 TIM_ Channel1 复制放到这里。接着继续看下一个TIM_ IC_ Filter 这个参数用来选择输入捕获的滤波器哈也就是 PPT 这里这个滤波器。如果你信号有毛刺和噪声哈就可以增大滤波器参数可以有效避免干扰。那在程序这里我们转到定义。右边的解释是这个参数可以是 0X0 到 0XFF 之间的一个数数越大滤波效果越好。每个数值对应的采样频率和采样次数啊在参考手册里有我们上小节讲过哈。那这里呢我们就给点滤波吧就给 0XFF 放到这里。注意滤波器和分频器的区别哈虽然它俩都是计数的东西但是滤波器计数并不会改变信号的原有频率。一般滤波器的采样频率都会远高于信号频率所以它只会滤除高频噪声使信号更平滑。1 千赫兹滤波之后仍然是 1000 赫兹信号频率不会变化。而分频器就是对信号本身进行计数会改变频率。1000 赫兹二分频之后就是 500 赫兹四分频就是 250 赫兹。这个注意下。然后继续下一个参数 TIM IC polarity。 极性这个对应的就是图里的这个边沿检测极性选择的部分了。选择是上升沿触发还是下降沿触发。转到定义看一下。这里参数列表搜索看一下这里有三个参数第一个 rising 上升沿触发第二个 falling 下降沿触发第三个 both edge 上升沿下降沿都触发。这里我们需要上升沿触发啊所以复制第一个。放到这里。接着继续 TIM IC 分频器这个参数配置的是我们结构里的这个啊触发信号分频器。不分频就是每次触发都有效二分频就是每隔一次有效一次以此类推啊。那我们转到定义看一下这里搜索一下参数列表。可以看到有这些参数啊 DIV1 就是不分频 DIV2 二分频之后四分频八分频。这个分频值并不能任意指定啊只能选择这四种。那我们目前需要每次触发都有效所以选择 DIV1 不分频复制放到这里。接着最后一个参数 TIMICSELECTION选择触发信号从哪个引脚输入。看一下 PPT 啊显然这个参数是配置这个数据选择器的可以选择直连通道或者是交叉通道我们看一下。转到定义。然后右边搜索一下参数列表下面这里可以看到 Direct 的 TI 就是直连通道的输入 Indirect 的 TI 就是交叉通道的输入。当然还有个 TRC 引脚哈这个我们暂时不用。那对于这个代码来说我们需要选择直连通道所以复制第一个参数放到这里。好到这里我们输入捕获的通道就配置完了。对应这个图来看是不是每一部分都对应一个结构体参数啊不多不少参数配置完了这个通道的电路就连接好了。然后呢继续按照流程来把主从模式的这两个东西配置好。第五步配置 T R G I 的触发源为 T I E M P 一。那回到代码找一下函数啊。触发源选择要用这个 TIM_ Select_ InputTrigger 复制放在这下面。第一个参数给 TIM3第二个参数就是要选择哪个触发源。转到定义看一下可以看到这里给出了 8 个可选的触发源这 8 个触发源就对应我们 PPT 的这里啊具体的解释可以看看手册。那我们要选的很显然是这个 TIMTS_ TI1FP1复制放到这第二个参数的位置。这样触发源就选择好了。之后继续第六步配置从模式为 reset。找一下函数啊使用这个 TIM_ SelectSlaveMode 选择从模式复制。放在这里参数还是类似的第一个给 TIM3第二个看下定义这里给出了这四种从模式就对应 PPT 这里的下面四个从模式上面这三个 input 的从模式是给编码器接口用的哈还会另外有函数进配置所以这个函数的参数就没有给出。那在这里我们需要选择 reset 这个参数复制。放到这里这样从模式就配置好了。接下来最后一步我们调用 TIMCmd 参数给 TIM3 ENABLE 启动定时器这样整个电路的配置就完成了。电路的配置就完成了。当我们启动定时器之后 CNT 就会在内部时钟的驱动下不断自增即使信号没有过来哈它也会不断自增。不过这也没关系因为有信号来的时候它就会在从模式的作用下自动清 0并不会影响测量。好那初始化之后整个电路就能全自动测量了。当我们想要查看频率时需要读取 CCR 进行计算所以我们在下面再写个函数 UINT32 杠 7 IC_ Get_ Freq void。 在里面我们需要执行 PPT 这里的公式 FX 等于 FC 除 N。FC 呢之前说过等于 72 兆除 PSC 加一目前 PSC 是 72 减一所以 FC 等于一兆赫兹。所以下面这里直接 return 一零零零零零零这是一兆。然后除以 NN 就是读取 CCR 的值我们到 TIM 点去找一下函数啊需要用这个 TIM Get Capture 1 复制放到这里参数给 TIM3 这样就行了。目前我们这个函数返回的是最新一个周期的频率值啊单位是赫兹。那我们来测试一下先把这两个函数放到头文件声明一下编译看一下。PWM 模块已经将待测信号输出到 PA0 了 PA0 又通过导线输入到 PA6 PA6 是 TIM3 的通道一通道一通过输入捕获模块测量得到频率。然后在主循环里我们不断刷新显示频率 OLED Show Number 一行 6 列显示 IC_ Get_ Freq。 长度为 5 啊这样就行了。我们测试一下下载看一下。可以看到目前测量得到的频率是 1001但是我们输出的是 1000 赫兹啊多了一点点。按理说自己输出自己测应该不会有误差呀但是这确实很稳定的多了一点啊。据我个人推测吧可能是计数刚到 1000 赫兹的那个数时信号也刚好跳变因为电路结构啊或者其他什么原因导致这一个数刚好没记到才会有一点误差哈。不过这个也属于正负一误差的范畴。目前这个误差也是符合要求的但是总是差那么一点逼死强迫症啊。所以我决定还是在程序上给它补回来。那么回到程序在这里加个括号给计数值加一给它补一个数啊。这样再试一下下载看一下现在测量 1000 赫兹没有问题啊凑个整现在就舒服多了啊。好到这里第一个程序就完成了。PWMI测频率占空比我们来看一下啊。首先开启时钟 GPIO 和实际单元都不需要更改。然后这里输入捕获初始化的部分需要进行一下升级配置成两个通道同时捕获同一个引脚的模式。怎么配置呢一个简单的想法就是把这个通道初始化的部分复制一份。这个结构体定义的不要复制了哈。然后呢通道一是直连输入上升沿触发这个还沿用这个配置。接着下面通道一改成通道二直连输入改成这个交叉输入上升沿触发改成下降沿触发。这样看一下是不是就对应我们 PPT 的这个结构了通道一直连输入上升沿触发通道二交叉输入下降沿触发。这样就完事了。回到代码哈这样配置是可行的没问题。但是呢还是建议公司他们给麻烦还专门给我们封装了一个函数来快捷的完成这个配置。到 TIM 点 h 里看一下这个函数就是我们之前说过的 TIM PWMConfig我们复制一下。然后在这里下面这一大段都不需要了直接调用 PWMConfig第一个参数 TIM3第二个参数举例子把结构体变量放过来。这样就行了。目前演示的这个代码就和刚才那个代码的效果是一样的哈。使用这个函数你只需要传入一个通道的参数就行了。在函数里会自动把剩下的一个通道初始化成相反的配置。比如我这里传入通道一直连上升沿那函数里面就会顺带配置通道二交叉下降沿。如果我传入通道二直连上升沿呢函数就会顺带配置通道一交叉下降沿。这就是这个函数的操作可以快捷的把电路配置成 PWMI 模式的标准结构。那我是咋知道的呢转到一。分析下源码就知道了。这里可以看到它会进行判断哈如果我传入的是 Rising 则对象就是 falling 否则我传入 falling 对象就是 rising。 然后如果我选择的是直连对象就是交叉否则我选择交叉对象就是直连。最后如果我选择配置通道一则结构体参数配置到 TIM1对象参数配置到 TIM2。如果我选择配置通道二则结构体参数配置到 TIM2对象参数配置到 TIM1。总之就是一堆 if 进行判断以传入任意一个通道的参数都会顺带把另一个通道配置成相反的参数这就是这个函数的操作逻辑哈。当然这里也可以看出这个函数只支持通道一和通道二的配置不要传入通道三和通道四哈。那返回到这里执行完这个函数我们的 PWM I电路就配置好了。接着主从模式啊启动定时器这些都不需要更改。最后获取频率的函数不用改我们再写一个获取占空比的函数。高电平的计数值存在 CCR2 里整个周期的计数值存在 CCR1 里我们用 CCR2 除 C CR 1就能得到占空比了。所以这里直接 return T M get capture 2 T M 3除以 T M get capture 1 T M 3。这样就得到占空比。显然这个数的范围是 0~1 啊我们要显示整数的话可以在这里给它乘个 100扩大 100 倍。这样返回值的范围就是 0~100对应占空比 0%~100%。另外还是那个问题啊经过实测这个 CC R 总会少一数所以我们给它各加一个数补回来哈看着舒服些。好我们来测试一下把这个函数放到头文件声明一下。编译没问题。然后 main 点 c 这里先显示一下固定的字符串复制一下二行一列显示 D U T Y 冒号 00 百分号。之后 show number 也复制一下。二行六列显示 IC get duty长度为二。这样程序就完成了我们测试一下编译。我们修改一下看看。比如预分频改成 7200频率就是 100 赫兹 CCR1 改成 80占空比就是 80%。再试一下下载看看也没问题啊。大家可以自行更改参数尝试一下。那到这里我们第二个代码就写完了。最后我们来研究一下这个测频率的性能哈。首先是测频率的范围目前我们给的标准频率是 1 兆赫兹计数器最大只能记到 655·35所以所测量的最低频率是一兆除 65535 这个值算下大概是 15 赫兹。如果信号频率再低计数器就要溢出了所以最低频率就是 15 赫兹左右。那如果想再降低一些最低频率的限制呢我们可以把这个预分频再加大点这样标准频率就更低所支持测量的最低频率也就更低。这是测量频率的下限然后是测量频率的上限就是支持的最大频率。这个最大频率哈并没有一个明显的界限因为随着待测频率的增大误差也会逐渐增大。如果非要找一个频率上限那应该就是标准频率一兆赫兹。超过一兆赫兹信号频率比标准频率还高那肯定测不了了。但是这个一兆赫兹的上限并没有意义啊因为信号频率接近一兆赫兹时误差已经非常大了。所以最大频率要看你对误差的要求。上一小节我们说到了正负一误差记 100 个数误差 1 个相对误差就是百分之一。记 1000 个数误差 1 个相对误差就是 千分之一。所以正负一误差可以认为是 1 除计数值。在这里如果你要求误差等于千分之一时频率为上限那这个上限就是 1 兆/ 1000 等于 1000 赫兹。如果要求误差可以到 百分之一那频率上限就是 1 兆除/100 等于 10 千赫兹。这就是频率的上限。如果想提高频率的上限那我们在这里就要把 PSC 给降低一些提高标准频率上限就会提高。除此之外如果频率还要更高呢那我们就要考虑一下测频法了。测频法适合高频测周法适合低频。我们这里是测周法所以对于非常高的频率还是交给测频法来解决吧。然后呢还有一个就是误差分析除了我们之前说的正负一误差外在实际测量的时候还会有晶振误差比如我们 STM32 的晶振不是那么准在计数几百几万次之后误差积累起来也会造成一些影响。当然目前我们这个现象哈是自己测量自己不存在晶振误差所以数值还是非常稳定的。如果你要测量别的信号那数值可能就会有些抖动了哈后期可以再做一些滤波处理。好那这些就是对这个数模转换和测频率的性能评估哈其他更深入的内容就需要大家在实践中不断研究总结了。

相关新闻