前言

          第六章 TIM 定时器主要包含以下四个部分:

  • 第一部分:定时器基本定时功能(已学习)
  • 第二部分:定时器输出比较功能(已学习)
  • 第三部分:定时器输入捕获功能(本篇内容)
  • 第四部分:定时器编码器接口功能

          在前两部分中,我们掌握了时基单元的定时逻辑与输出比较(OC)的波形产生技术。本章将深入探讨输入捕获功能。该功能不仅是测量外部信号频率与占空比的核心手段,更是理解定时器主从触发自动化机制的关键。


目录

前言

1 频率测量理论基础

1.1 测频法 (适合高频信号)

1.2 测周法 (适合低频信号)

1.3 中界频率

2 输入捕获原理概述

3 输入捕获硬件电路分析

3.1 信号链路与核心处理模块

3.1.1 输入滤波器

3.1.2 边缘检测器与极性选择

3.1.3 通道交叉映射

3.1.4 主从触发模式(硬件自动化)

4 PWM 输入模式(PWMI)原理

4.1 核心设计特性

4.2 硬件配置与执行逻辑(以引脚 TI1 为例)

4.2.1 通道 1(直连映射:测量周期)

4.2.2 通道 2(交叉映射:测量脉宽)

4.3 频率与占空比的量化计算

4.4 硬件资源约束与选型规范

5 输入捕获配置步骤(基于标准外设库)

5.1 使能时钟与 GPIO 初始化

5.2 初始化定时器时基单元(TimeBase)

5.3 初始化输入捕获通道(核心区分点)

5.4 配置从模式与硬件复位触发

5.5 使能定时器

5.6 数据读取与参数计算

6. 本章节实验

6.1 输入捕获模式测频率

6.1.1 实验目标

6.1.2 硬件设计

6.1.3 软件设计

6.1.4 实验现象

6.2 PWM 输入模式测频率与占空比

6.2.1 实验目标

6.2.2 硬件设计

6.2.3 软件设计

6.2.4 实验现象


1 频率测量理论基础

          针对数字信号(高低电平方波信号)的频率测量,工程中通常采用测频法(Direct Frequency Measurement)测周法(Period Measurement)两种算法。在讲解 STM32 输入捕获功能前建立这些理论储备,是因为STM32 的输入捕获本质上就是“测周法”在硬件层面的自动化实现,因此它在测量中低频信号时具有天然的精度优势。理解了算法底层逻辑,便能更透彻地掌握输入捕获的硬件配置与参数计算。

          在 STM32 的通用定时器架构中,标准输入捕获功能正是基于测周法来设计的。下图展示了这两种方法涉及到的关键参数:

1.1 测频法 (适合高频信号)

          测量原理:开启一个固定时长的时间窗口(即闸门时间 $T_{gate}$),在此窗口内利用计数器对输入信号的有效边沿(如上升沿)进行计次,最终得到计数值 N。

          计算公式:频率的物理本质是单位时间(1s)内信号周期的个数。若在 $T_{gate}$ 秒内监测到 N 个完整周期,则 1 秒内的周期数(频率)即为:

$f_x = \frac{N}{T_{gate}}$

          工程特性:

  • 自带均值滤波效果,输出数值较为平滑,但测量结果的更新率受限于设定的闸门时间(更新较慢)。
  • 误差分析:计数器在闸门开闭瞬间会产生不可避免的 $\pm1$ 量化误差。待测信号频率越高,在相同$T_{gate}$内累积的计数值 N 越大,$\pm1$ 误差对整体结果的相对误差(1/N)就越小。因此,该方法极度适合高频信号测量。

1.2 测周法 (适合低频信号)

          测量原理:利用一个已知且极高频率的标准时钟(频率为 fc)来驱动定时器计数。通过捕获待测信号两个相邻有效边沿(如连续两个上升沿),测量出一个完整信号周期内标准时钟的计步总数 N。

          计算公式: 频率与周期互为倒数(f = 1/T)。已知标准时钟计一步的时间为 1/fc,若待测信号一个周期内标准时钟计了 N 步,则待测信号的周期 Tx 为:

$T_x = N \times \frac{1}{f_c}$

          则待测信号频率 fx 为:

$f_x = \frac{1}{T_x} = \frac{f_c}{N}$

          工程特性:

  • 高实时性。每经历一个完整的待测信号周期,即可输出一次测量结果,更新速度动态跟随待测信号。
  • 误差分析:此时的 $\pm1$ 量化误差来源于内部标准时钟 fc 的计数跳变。待测信号频率越低,其单周期时间越长,所能容纳的标准时钟计数值 N 就越大,相对误差也就越小。因此,该方法极度适合低频信号测量。

1.3 中界频率

          中界频率(fm)是指测频法与测周法相对测量误差绝对相等时的临界频率点。从数学本质上看。

          计算公式:当这两种方法在同等条件下产生的的相对误差相等时,意味着它们的计数值 N 相同。

          由测频法得:$N = f_x \times T_{gate}$

          由测周法得:$N = \frac{f_c}{f_x}$

          令两者相等:

$f_x \times T_{gate} = \frac{f_c}{f_x} \Rightarrow f_x^2 = \frac{f_c}{T_{gate}}$

          解得中界频率:

$f_m = \sqrt{\frac{f_c}{T_{gate}}}$

          注:fc  为测周法标准频率,$T_{gate}$为测频法闸门时间)

          架构选型指南:

  • 当待测信号频率 fx < fm 时,测周法的相对误差更小,应选用测周法。
  • 当待测信号频率 fx > fm 时,测频法的相对误差更小,应选用测频法。

2 输入捕获原理概述

          输入捕获(Input Capture, IC)是 STM32 定时器的核心功能之一。与输出比较(OC)通过主动比对计数器 CNT 与捕获/比较寄存器 CCR 的值来驱动引脚电平逻辑不同,输入捕获的核心任务是:在外部信号发生预设的电平跳变时,通过硬件瞬间记录该时刻定时器的计数值。

          具体而言,当输入捕获通道对应的 GPIO 引脚(TIMx_CHx)检测到指定的有效边沿(上升沿、下降沿或双边沿,可通过软件寄存器配置)时,定时器内部的捕获电路会立即触发锁存动作,将 CNT 的当前数值自动转运并保存至对应的 CCR 中,完成一次捕获。同时还可以配置捕获时是否触发中断/DMA 等。

          以定时器通道 1(TIMx_CH1)为例,展示外部信号从引脚输入到最终触发捕获的完整硬件路径。下图展示了信号经过滤波、边沿检测、交叉映射到最终触发捕获的完整底层逻辑:

          该机制由硬件底层全自动执行,具备以下显著的技术特性:

  • 高度自动化:捕获触发动作由硬件逻辑直接完成,无需 CPU 实时参与运算。
  • 时序严谨性:计数值的锁存时刻与输入信号的边沿跳变保持严格同步。
  • 测量高精度:捕获精度直接取决于定时器的计数频率,即系统时钟的分辨率。

          正因为输入捕获能够精确记录物理时间节点,所以通过对连续两次或多次捕获值进行差值计算,可以精准提取信号的关键时域参数,包括:信号周期、信号频率、PWM 占空比、高电平持续时间以及低电平持续时间。

补充:输入捕获 vs. 外部中断(EXTI)

          虽然两者均具备检测电平跳变的能力,但在底层执行逻辑上存在本质差异。EXTI 的执行动作是触发 CPU 挂起当前任务并执行中断服务程序(ISR);而输入捕获的动作是将 CNT 的计数值原子性地存入 CCR。这种纯硬件级的时间记录方式,彻底消除了中断响应延迟对时间测量的影响,使得测量高频信号时精度更高,系统开销更小

          在明确了输入捕获的底层触发机制与应用场景后,需要进一步剖析其在 STM32 定时器内部的硬件架构与数据流向。


3 输入捕获硬件电路分析

          在硬件资源配置上,STM32 的通用定时器和高级定时器通常配备 4 个独立的输入捕获通道(CC1 ~ CC4)。为了直观理解其在定时器系统中的物理定位,可以参考下图所示的定时器结构框图:

          从上述框图中可以清晰观察到,输入捕获模块输出比较模块在底层共用了同一通道的硬件资源(即同一个 CCR 寄存器与物理引脚)。

          基于上述硬件共享机制,在工程配置中必须遵守以下约束:同一物理通道在同一时刻无法同时启用输入捕获与输出比较功能;但不同通道之间(例如 CH1 与 CH2)具备完全的独立性,可并行执行不同的工作模式。

3.1 信号链路与核心处理模块

          明确了模块间的宏观资源分配后,需要深入通道内部,剖析外部信号是如何一步步转化为寄存器中的数值的。

          以下就以定时器通道 1(TIMx_CH1)为例,展示其输入部分的底层硬件逻辑结构:

          为了确保测量结果的准确与自动化,输入捕获电路在信号触发捕获前,必须经过以下四个核心处理单元的逐级处理:

3.1.1 输入滤波器

          信号从 TIMx_CH1—>TI1 引脚进入定时器后,首先经过输入滤波器。该模块以特定的采样频率($f_{DTS}$)对输入信号连续采样 N 次,只有当连续 N 次采样电平均相同时,输出逻辑才发生跳变。此机制可有效滤除外部信号的高频噪声与物理毛刺。

          滤波器的参数(采样频率和 N 值)由寄存器 TIMx_CCMR1 中的 IC1F[3:0] 位配置,用户可根据外部信号质量灵活调整。数字滤波后的干净信号记为 TI1F,送往下一级处理。

3.1.2 边缘检测器与极性选择

          TI1F 随后进入边沿检测器。极性选择由寄存器 TIMx_CCER 中的 CC1P 位控制:

  • 当 CC1P = 0 时,选择上升沿作为有效触发边沿;
  • 当 CC1P = 1 时,选择下降沿。

          该选择的结果产生最终的触发信号 TI1FP1,用于后续捕获或触发。同时,边沿检测器还会输出一个 TI1F_ED 信号(任意双边沿触发),直接送往从模式控制器,用于特殊的双边沿触发场景。

3.1.3 通道交叉映射

          STM32 定时器的硬件架构提供了灵活的信号路由机制。经过滤波及极性选择后的 TI1FP1 信号,将与另外两路参考信号共同输入至多路选择器(Data Selector)。该选择器的输出信号 IC1 将作为通道 1 捕获单元的最终触发源。

          参与多路选择的信号源主要包括:

  • TI2FP1(交叉映射信号):源自通道 2(CH2)的输入信号。通过内部硬件链路跨越至通道 1,使通道 1 能够获取相邻通道的频率或占空比特征。
  • TRC(Trigger Controller):内部触发总线信号。该信号源于从模式控制器的触发输入,通常用于定时器级联同步或与芯片内部其他外设(如 ADC 触发、DMA 请求)进行硬件级联动。

          最终的路由路径由寄存器 TIMx_CCMR1 中的 CC1S[1:0] 位进行配置:

  • CC1S[1:0] = 00直连模式 (DirectTI)。选择 TI1FP1 作为输入源,即通道 1 捕获其自身引脚的输入信号。
  • CC1S[1:0] = 01交叉模式 (IndirectTI)。选择 TI2FP1 作为输入源,即通道 1 捕获来自通道 2 引脚的信号。
  • CC1S[1:0] = 10TRC 映射模式。选择内部触发信号 TRC 作为输入源,用于实现定时器间的复杂同步逻辑。

          这种硬件路由机制是实现 PWM 输入模式 (PWMI) 的底层物理基础。通过将单一物理引脚(如 TI1)的信号同时路由至直连通道(IC1)与交叉通道(IC2),系统可以在不增加额外外部连线的前提下,利用两个独立的捕获/比较寄存器(CCR1 和 CCR2)并行锁存信号的周期与脉宽数据。这种设计显著降低了 CPU 的运算开销,并确保了测量参数在时域上的严格同步。

名词解释:硬件路由(Signal Routing)

          在 STM32 的定时器架构中,“路由”特指信号的路径选择与定向输送

          这一过程本质上是通过配置寄存器(如 TIMx_CCMR1 中的 CC1S[1:0] 位)来控制内部多路选择器的导通状态。它决定了外部引脚采集到的信号(如 TI1)在芯片内部的流向——是定向输送至本通道的捕获单元(直连路径),还是跨越至相邻通道的捕获单元(交叉路径)。这种灵活的路由机制允许开发者在不改动外部硬件电路的前提下,通过软件协议栈自由重组信号链路。

3.1.4 主从触发模式(硬件自动化)

          为了解决测周法要求每次测量必须“从零开始计时”的需求,必须引入从模式控制器以实现“零 CPU 干预”的全自动化测量。

          在此机制中,信号在处理链路上被分流为两个并行的动作分支:

  • 捕获动作:信号 IC1 先送入输入捕获分频器(由 ICPS[1:0] 控制),分频后的信号最终触发捕获事件,将当前 CNT 值锁存至 CCR1 中。
  • 触发动作:与此同时,信号 TI1FP1 作为 触发输入信号(Trigger Input, TRGI)被路由至从模式控制器。

          作为从模式控制器的标准输入源,TRGI 在硬件链路上起到了同步触发源的作用。其本质是一个与捕获边沿严格同步的逻辑脉冲。当从模式被配置为 复位模式(Reset Mode)时,该脉冲信号会直接驱动从模式控制器的硬件组合逻辑,瞬间完成计数器 CNT 的清零操作。

          配置为复位模式后的具体执行逻辑如下:

  • 捕获:有效边沿到达瞬间,硬件电路首先执行锁存操作,将 CNT 的值原子性地转运至 CCR。
  • 复位:在完成捕获的同时,TRGI 信号驱动从模式控制器将 CNT 清零,为下一个周期的计数重置基准。

          由于底层硬件逻辑在设计上确保了捕获动作早于清零动作(或采用原子级同步逻辑),从而规避了由于先清零而导致捕获值为 0 的逻辑冲突。这种由 TRGI 驱动的硬件同步机制,确保了 CCR 中存储的值永远是上一个周期的完整计数值 N,为后续频率与占空比的精确计算提供了可靠的数据支撑。

          将上述的“通道交叉映射”与“主从自动复位”机制有机结合,便构成了 STM32 用于同时测量信号频率与占空比的最强应用架构——PWM 输入模式。


4 PWM 输入模式(PWMI)原理

          PWM 输入模式(PWM Input Mode, PWMI)是 STM32 定时器输入捕获功能的一种特殊硬件工作模式。该模式利用定时器内部的通道交叉映射机制从模式控制器(Slave Mode Controller)的硬件同步复位功能,实现了仅需占用一个物理输入引脚,即可同步测量外部 PWM 信号的周期(Period)脉宽(Pulse Width)

4.1 核心设计特性

          在信号链路结构上,PWMI 模式建立在标准输入捕获单元的基础之上,但通过特定的寄存器组合配置,实现了测量过程的完全硬件自动化。其核心特性包括:

  • 同源映射(Signal Multiplexing):两个独立的捕获通道(如 IC1 与 IC2)在内部同时映射至同一个物理输入信号源。例如,输入引脚 TI1 的信号被分为两路:一路直连映射(TI1 —> IC1),另一路交叉映射(TI1 —> IC2)。因此,单一外部信号可同时驱动两个捕获单元。
  • 互补极性检测(Complementary Edge Detection):两个捕获通道被配置为相反的有效触发边沿。一个通道检测信号周期的起始边沿(如上升沿),另一个通道检测高/低电平的结束边沿(如下降沿),从而在时域上截取周期长度与脉冲宽度。
  • 硬件同步复位(Hardware Synchronized Reset):选定的输入触发信号(TI1FP1 或 TI2FP2)被路由至从模式控制器的触发输入端(TRGI)。当有效边沿到达时,从模式控制器自动执行计数器(CNT)清零操作,以此实现周期测量的无缝自动同步。

注:捕获锁存与计数器复位由不同的硬件单元执行,但由同一外部事件同步触发,硬件设计保证了先捕获、后复位此时序逻辑。

4.2 硬件配置与执行逻辑(以引脚 TI1 为例)

          当待测 PWM 信号接入 TI1 引脚时,定时器通过内部两个捕获通道的协同工作,完成周期与脉宽的提取。

4.2.1 通道 1(直连映射:测量周期)

          通道 1 负责捕获信号的完整周期,同时充当从模式控制器的复位触发源。

  • 输入源选择:设置 TIMx_CCMR1 寄存器中的 CC1S[1:0] = 01,将 IC_1 通道配置为直连输入模式,即物理引脚 TI_1 的信号直接作为 IC_1 的输入源。
  • 极性配置:设置 TIMx_CCER 寄存器中的 CC1P = 0,设定为上升沿检测。当检测到上升沿时,输入级生成内部触发信号 TI1FP1。
  • 从模式配置:设置 TIMx_SMCR 寄存器中的 TS[2:0] = 101(选择 TI1FP1 为TRGI触发源),并设置 SMS[2:0] = 100(配置从模式为复位模式)。
  • 底层执行逻辑:当 TI1 出现上升沿并触发 TI1FP1 时,硬件产生原子性同步动作:首先将当前 CNT 的值锁存至 CCR1 寄存器中,随后瞬间触发从模式控制器将 CNT 寄存器复位清零,开启新一轮周期的计数。

4.2.2 通道 2(交叉映射:测量脉宽)

          通道 2 负责捕获电平翻转点(如高电平结束时刻),记录与占空比相关的计数值。

  • 输入源选择:设置 TIMx_CCMR1 寄存器中的 CC2S[1:0] = 10,将 IC_2 通道设定为交叉输入模式。此时,TI1 引脚的信号经过经过滤波与极性选择后,生成的 TI1FP2 信号被路由至通道 2 的数据选择器,最终映射至 IC_2。如图所示:

  • 极性配置:设置 TIMx_CCER 寄存器中的 CC2P = 1,设定为下降沿检测。当检测到下降沿时,生成内部信号 TI1FP2。

  • 底层执行逻辑:当输入信号出现下降沿时,触发通道 2 的捕获电路,将当前 CNT 的值锁存至 CCR2 寄存器中。由于 CNT 已经在上一个上升沿被硬件清零,因此此时 CCR2 中锁存的值,即为精确的 PWM 高电平持续时间。注:TI1FP2 并未被配置为 TRGI 触发源,因此该边沿不会引发 CNT 复位。

          通过上述配置,定时器内部形成了基于同一输入信号的双通道协同测量结构。下图展示了 TI1 输入信号如何同时驱动捕获单元与从模式控制器,实现周期测量与脉宽测量的时序同步:

说明图中首个上升沿出现 IC2 捕获的底层原因
          在 PWMI 模式的初始阶段,当第一个上升沿到来触发 TRGI 复位动作时,由于 IC2 通道已被使能(CC2E = 1),根据 STM32 的内部硬件逻辑,触发控制器会同步向所有已使能的捕获通道发送采样脉冲。这意味着在 CNT 被清零前的瞬间,硬件会强制 IC2 执行一次捕获锁存操作。因此,IC2 在此处的捕获是由主从复位动作连带触发的硬件级同步行为,而非单纯的下降沿触发。

4.3 频率与占空比的量化计算

          得益于由 TI1FP1 驱动的“先捕获、后复位”硬件机制,CCR1 寄存器始终存储着上一个完整周期的计数值,而 CCR2 寄存器则存储着高电平阶段的计数值。基于此,可通过以下公式直接量化物理参数:

  • 周期测量:外部信号的周期 T 由 CCR1 决定。

$T = \frac{CCR_1 + 1}{f_{DTS}}$

          其中 $f_{DTS}$ 为定时器驱动时钟频率;+1 用于补偿计数器从 0 开始计数的硬件偏移量)。

  • 频率计算:外部信号的频率  $f_{PWM}$为周期T的倒数。

$f_{PWM} = \frac{f_{DTS}}{CCR1 + 1}$

  • 占空比测量:外部信号的占空比 Duty 由两个寄存器的比值决定:

    $Duty = \frac{CCR_2 + 1}{CCR_1 + 1} \times 100\%$

          这种基于纯硬件寄存器的数据获取方式,彻底避免了高频中断对 CPU 资源的挤占,确保了系统在面对高频信号时依然具备极高的测量精度与实时性。

4.4 硬件资源约束与选型规范

          依据 STM32 定时器的内部布线架构,在物理层面上,仅有 TI1FP1TI2FP2 这两路信号被直接硬连线至从模式控制器的触发输入总线(TRGI)。如图所示:

          因此,在使用硬件级 PWMI 自动复位机制时,必须严格遵循以下硬件通道分配约束:

  • TI1 输入源限制:若待测信号接入 TI1 引脚,必须由通道 1(利用 TI1FP1)触发从模式复位。
  • TI2 输入源限制:若待测信号接入 TI2 引脚,必须由通道 2(利用 TI2FP2)触发从模式复位。
  • CH3/CH4 不适用:定时器的通道 3(CH3)与通道 4(CH4)在硬件设计上不具备指向从模式控制器 TRGI 的直连触发链路,故无法原生支持标准的 PWMI 自动复位功能。若项目强行需要使用 CH3 或 CH4 测量 PWM,则必须退而求其次,依赖软件中断进行清零,或通过复杂的内部触发(ITR)级联来实现等效功能。

5 输入捕获配置步骤(基于标准外设库)

          前面几章我们彻底剖析了输入捕获与 PWMI 模式的底层硬件路由和主从触发机制。接下来,我们将这些纯硬件的理论映射到具体的软件代码中。

          在实际工程中,利用 STM32 标准库配置输入捕获(IC)与 PWM 输入模式(PWMI)的流程高度一致。结合硬件的主从触发复位机制,我们可以彻底免去传统的中断繁琐操作。具体配置步骤如下:

5.1 使能时钟与 GPIO 初始化

          输入捕获信号必须通过特定的物理引脚进入定时器。以本实验使用 TIM3 的通道 1(对应引脚为 PA6) 为例,首先需要开启定时器与对应 GPIO 的时钟,并将引脚配置为合适的输入模式。

          通常,为了防止外部信号悬空导致的误触发,建议将其配置为上拉输入(IPU)或下拉输入(IPD)。

/* 1. 开启 TIM3 与 GPIOA 时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); 
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

/* 2. 配置 PA6 为上拉输入模式 */
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入,提升抗干扰能力
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

5.2 初始化定时器时基单元(TimeBase)

          时基单元决定了测周法的“标尺精度”和“测量量程”,因此必须配置合理的预分频器(PSC)和自动重装载值(ARR):

  • 预分频系数(PSC):决定了标准频率 fc。如 PSC = 72-1,则 fc = 72MHz / 72 = 1MHz。这意味着计数器每 1us 计一个数。
  • 自动重装值(ARR):在测周法中,ARR 应设为最大值(65535)。这是为了保证在待测信号的一个周期内,计数器不会因为溢出而提前归零,从而导致周期测量错误。
TIM_InternalClockConfig(TIM3); // 选择内部时钟驱动 TIM3

TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     // 滤波器采样时钟不分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数
TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;               // 设为最大值,防止提前溢出
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;               // 预分频为72,标准时钟频率 Fc = 1MHz
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);

5.3 初始化输入捕获通道(核心区分点)

          这一步是配置信号的极性、滤波器、分频与路由映射。标准库提供了一个结构体 TIM_ICInitTypeDef 来完成这些参数的集合配置:

  • TIM_Channel:指定要配置的通道(如 TIM_Channel_1)。
  • TIM_ICPolarity:定义以何种电平跳变形式触发捕获(上升沿 Rising 或下降沿 Falling)。
  • TIM_ICSelection:配置映射关系:
  • 直连(DirectTI):TI1 —> IC1,信号从通道 1 入,交给通道 1 处理。
  • 交叉(IndirectTI):TI1 —> IC2,信号从通道 1 入,跨界交给通道 2 处理,PWMI 模式的关键。
  • TIM_ICPrescaler:捕获预分频。设为 TIM_ICPSC_DIV1 即不分频,每次边沿均触发。
  • TIM_ICFilter:硬件数字滤波器。设为 0xF 可最大程度滤除高频噪声毛刺。

          【模式分支配置】:根据需求选择不同的初始化函数

  • 普通输入捕获模式(仅测频率):调用 TIM_ICInit(),仅初始化单一目标通道。
  • PWMI 模式(测频率与占空比):调用 TIM_PWMIConfig()。这是一个强大的宏观库函数,传入通道 1 的参数后,它会在底层自动将通道 2 配置为相反的极性与交叉映射,一步到位完成 PWMI 的底层路由构建。
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; 
TIM_ICInitStructure.TIM_ICFilter = 0xF; 
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 周期起点:上升沿
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; 
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; 

/* 模式选择:二选一 */
// 仅测频率使用:
// TIM_ICInit(TIM3, &TIM_ICInitStructure); 

// PWMI 测频率与占空比使用:
TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);

5.4 配置从模式与硬件复位触发

          为了让计数器在捕获发生的瞬间自动清零,必须配置主从同步触发

  • 触发源选择:TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1),将通道 1 信号(TI1FP1)指定为触发输入(TRGI)。
  • 从模式选择:TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset),设定触发后的动作为“复位计数器”。

          如此,当上升沿到来触发从模式复位时,硬件会先执行锁存操作,将当前计数器 CNT 的实时值复制到捕获寄存器 CCR1 中。在捕获动作完成后(极短的硬件时钟周期延迟内,通常仅 1~2 个时钟周期),从模式控制器的复位信号生效,将 CNT 清零为,使定时器立即开始下一周期的计数,这一过程完全由硬件自动完成。

TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);    // 选择 TI1FP1 为触发源 TRGI
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); // 从模式配置为复位动作

5.5 使能定时器

          所有参数就绪后,启动定时器,硬件闭环机制即刻开始自动化运作。

TIM_Cmd(TIM3, ENABLE); // 使能 TIM3,计数与捕获逻辑开始运行

5.6 数据读取与参数计算

          配置完成后,CPU 无需再干预捕获过程,随时在主循环中读取捕获寄存器(CCR)的值即可获取结果:

  • 读取周期计数值(N):调用 TIM_GetCapture1(TIM3)。
  • 读取脉宽计数值(W):调用 TIM_GetCapture2(TIM3)(仅 PWMI 模式有效)。

          最终计算逻辑封装:

/* 获取频率 (Fc = 1000000Hz) */
uint32_t IC_GetFreq(void)
{
    // 测周法公式:fx = fc / N
    // 注意:这里的 1000000 (1MHz) 是由时基单元配置中的 PSC 决定的 (72MHz / 72 = 1MHz)
    // 若修改了预分频值,此常数需同步调整
    return 1000000 / (TIM_GetCapture1(TIM3) + 1); 
}

/* 获取占空比 (PWMI模式) */
uint32_t IC_GetDuty(void)
{
    // 公式:Duty = (W / N) * 100%
    return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);
}

提示:

          在工程中,代码里的 1000000 是测周法的“标准频率” fc。它严格取决于你在 TIM_TimeBaseInit 中设置的预分频系数(PSC)。

          例如,若你将 PSC 改为 720-1,则 fc 变为 100kHz,此时代码中的常数也必须同步修改为 100000,否则计算结果将出现数量级偏差。


6. 本章节实验

6.1 输入捕获模式测频率

6.1.1 实验目标

  • 掌握外设协同与系统闭环设计:利用芯片内部的两个独立定时器资源,实现“信号发生”与“信号测量”的硬件级闭环测试系统。
  • 掌握测试信号源的构建:学习配置定时器(TIM2)的输出比较(PWM)功能,生成频率与占空比可软件动态调节的待测方波信号。
  • 深化输入捕获与主从触发应用:掌握将定时器(TIM3)配置为输入捕获模式,并结合硬件触发源(TRGI)与从模式控制器,实现无需 CPU 干预的计数器(CNT)自动复位。
  • 验证测周法理论:将测周法理论的数学公式转化为实际代码,完成物理信号向频率参数的量化计算。

6.1.2 硬件设计

6.1.3 软件设计

          本实验在软件架构上脱离了单向的测量模式,而是将系统划分为信号发生信号测量 和 结果展示三大核心模块。

1. 信号发生模块(基于 TIM2)

  • 引脚配置:将指定的 GPIO 引脚配置为复用推挽输出模式,将其控制权交接给 TIM2 外设。
  • 波形生成:利用 TIM2 的时基单元与输出比较通道(PWM 模式 1),持续输出高低电平交替的方波信号 。
  • 动态调节接口:封装底层的寄存器操作,提供独立的函数用于动态修改预分频器(PSC,决定频率)与捕获比较寄存器(CCR,决定占空比),模拟真实多变的外部被测信号。

2. 信号测量模块(基于 TIM3)

  • 信号接入:配置另一组 GPIO 引脚为上拉输入模式,用于物理接收 TIM2 产生的方波信号。
  • 配置时基单元:为 TIM3 设定合理的预分频系数,确立内部测周法的标准时钟频率(fc)。同时将自动重装载值(ARR)设定为最大值,以提供最大的单周期测量量程,防止低频测量时发生溢出。
  • 捕获配置:将输入通道配置为直连映射(Direct TI),设定为上升沿触发,并开启硬件数字滤波器以消除潜在的物理抖动。
  • 主从复位自动化:指定通道的有效跳变沿(TI1FP1)为硬件触发输入源(TRGI),并将从模式控制器配置为“复位模式”。这使得定时器在每次捕获到上升沿时,不仅会锁存计数值,还会通过纯硬件逻辑瞬间清零计数器。

3. 运行与数据处理逻辑(主程序)

  • 硬件自治:系统启动后,TIM2 持续发送脉冲,TIM3 在后台通过纯硬件机制连续执行:“捕获 —> 清零 —> 重新计数” 的死循环,全程无需触发任何软件中断 。
  • 数据换算与显示:在 while(1) 主循环中,主控只需按需调用读取函数,获取 TIM3 捕获寄存器(CCR1)中存储的上一个完整周期计数值 N。随后,利用测周法公式:fx = fc / (N+1) 换算出待测信号的频率,并将其刷新至 OLED 屏幕。

          具体代码如下:

          main.c文件:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"

int main(void)
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	PWM_Init();			//PWM初始化
	IC_Init();			//输入捕获初始化
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "Freq:00000Hz");		//1行1列显示字符串Freq:00000Hz
	
	/*使用PWM模块提供输入捕获的测试信号*/
	PWM_SetPrescaler(720 - 1);					//PWM频率Freq = 72M / (PSC + 1) / 100
	PWM_SetCompare1(50);						//PWM占空比Duty = CCR / 100
	
	while (1)
	{
		OLED_ShowNum(1, 6, IC_GetFreq(), 5);	//不断刷新显示输入捕获测得的频率
	}
}

          IC.c文件:

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:输入捕获初始化
  * 参    数:无
  * 返 回 值:无
  */
void IC_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);			//开启TIM3的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							//将PA6引脚初始化为上拉输入
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM3);		//选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;               //计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;               //预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM3的时基单元
	
	/*输入捕获初始化*/
	TIM_ICInitTypeDef TIM_ICInitStructure;							//定义结构体变量
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;				//选择配置定时器通道1
	TIM_ICInitStructure.TIM_ICFilter = 0xF;							//输入滤波器参数,可以过滤信号抖动
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;		//极性,选择为上升沿触发捕获
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;			//捕获预分频,选择不分频,每次信号都触发捕获
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;	//输入信号交叉,选择直通,不交叉
	TIM_ICInit(TIM3, &TIM_ICInitStructure);							//将结构体变量交给TIM_ICInit,配置TIM3的输入捕获通道
	
	/*选择触发源及从模式*/
	TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);					//触发源选择TI1FP1
	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);					//从模式选择复位
																	//即TI1产生上升沿时,会触发CNT归零
	
	/*TIM使能*/
	TIM_Cmd(TIM3, ENABLE);			//使能TIM3,定时器开始运行
}

/**
  * 函    数:获取输入捕获的频率
  * 参    数:无
  * 返 回 值:捕获得到的频率
  */
uint32_t IC_GetFreq(void)
{
	return 1000000 / (TIM_GetCapture1(TIM3) + 1);		//测周法得到频率fx = fc / N,这里不执行+1的操作也可
}

          PWM.c文件:

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:PWM初始化
  * 参    数:无
  * 返 回 值:无
  */
void PWM_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	/*GPIO重映射*/
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);			//开启AFIO的时钟,重映射必须先开启AFIO的时钟
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);			//将TIM2的引脚部分重映射,具体的映射方案需查看参考手册
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);		//将JTAG引脚失能,作为普通GPIO引脚使用
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;		//GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							//将PA0引脚初始化为复用推挽输出	
																	//受外设控制的引脚,均需要配置为复用模式		
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;					//计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;				//预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
	
	/*输出比较初始化*/
	TIM_OCInitTypeDef TIM_OCInitStructure;							//定义结构体变量
	TIM_OCStructInit(&TIM_OCInitStructure);							//结构体初始化,若结构体没有完整赋值
																	//则最好执行此函数,给结构体所有成员都赋一个默认值
																	//避免结构体初值不确定的问题
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;				//输出比较模式,选择PWM模式1
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;		//输出极性,选择为高,若选择极性为低,则输出高低电平取反
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	//输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;								//初始的CCR值
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);						//将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1
	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

/**
  * 函    数:PWM设置CCR
  * 参    数:Compare 要写入的CCR的值,范围:0~100
  * 返 回 值:无
  * 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
  *           占空比Duty = CCR / (ARR + 1)
  */
void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);		//设置CCR1的值
}

/**
  * 函    数:PWM设置PSC
  * 参    数:Prescaler 要写入的PSC的值,范围:0~65535
  * 返 回 值:无
  * 注意事项:PSC和ARR共同决定频率,此函数仅设置PSC的值,并不直接是频率
  *           频率Freq = CK_PSC / (PSC + 1) / (ARR + 1)
  */
void PWM_SetPrescaler(uint16_t Prescaler)
{
	TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate);		//设置PSC的值
}

6.1.4 实验现象

          使用板载 TIM2 输出的 1000Hz PWM 信号接入 PA6 进行自我测试,OLED 屏幕上实时显示当前测量得出的频率数值稳定为 1000Hz。


6.2 PWM 输入模式测频率与占空比

6.2.1 实验目标

  • 深入理解通道交叉映射机制:掌握 STM32 定时器内部输入通道的硬件路由分配原理,理解单根物理引脚如何同时驱动两个独立的捕获寄存器。
  • 掌握 PWMI 专属配置接口:学习使用标准库提供的高效宏观函数(TIM_PWMIConfig),快速搭建互补极性的双通道协同捕获架构,替代繁琐的单通道独立配置。
  • 实现多维信号参数同步测量:在上一节闭环测试系统的基础上进一步升级,实现对高频 PWM 信号的**频率(周期)与占空比(脉宽)**两个维度的全硬件、无干预同步解析。

6.2.2 硬件设计

6.2.3 软件设计

          本实验的软件架构在上一节频率测量的基础上进行了维度扩展,核心在于利用 PWMI 模式的硬件自动化特性,在同一输入信号上提取双重特征参数。

1. 信号发生模块(基于 TIM2)

  • 全参数可调信号源:继续沿用 TIM2 作为测试信号发生器。除了通过调节预分频器(PSC)来改变信号频率外,本实验中输出比较寄存器(CCR)的数值也作为关键变量,用于动态改变信号的高电平持续时间,从而生成频率与占空比均可独立控制的标准 PWM 测试波形。

2. PWMI 捕获架构(基于 TIM3)

  • 单线双路信号接入:外部 PWM 信号仅接入单一物理引脚(PA6)。在芯片内部,该信号被硬件级一分为二,同时送往通道 1(直连)与通道 2(交叉映射)。
  • 自动互补配置(TIM_PWMIConfig):在软件层面抛弃了对两个通道分别初始化的繁琐流程。主程序仅需向库函数传入基准通道(如通道 1:直连映射、上升沿捕获)的配置结构体,底层硬件会自动推导并配置关联通道(通道 2:交叉映射、下降沿捕获),从而形成严密的互补检测逻辑。
  • 从模式联动:保留上一个实验的主从复位机制,利用基准通道的有效边沿(上升沿)触发从模式控制器,实现定时器计数器(CNT)的周期性自动清零。

3. 运行逻辑与双维参数提取(主程序)

  • 硬件级测量时序:系统运行时,纯硬件电路将按以下时序自动流转 :

    • 阶段一(周期起点):信号产生上升沿 —> 通道 1 触发,CCR1 锁存上一个周期的总计数值 —> 从模式触发,瞬间清零 CNT。

    • 阶段二(脉宽终点):信号产生下降沿 —> 通道 2 触发,CCR2 独立锁存当前 CNT 的值(此时的值即为高电平持续时间)。

    • 阶段三(周期终点):下一个上升沿到来,重复阶段一,CNT 重新开始计数。如下图所示:

  • 数据换算展示:主程序彻底摆脱了时序追踪的负担,只需从两个专职寄存器中直接提取结果:

    • 频率计算:沿用 $f_x = \frac{f_c}{CCR1 + 1}$ 获取频率。

    • 占空比计算:利用公式 $Duty = \frac{CCR2 + 1}{CCR1 + 1} \times 100\%$直接求得占空比百分比,并将其格式化后与频率同步刷新至 OLED 屏幕。

          具体代码如下:

          main.c文件:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"

int main(void)
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	PWM_Init();			//PWM初始化
	IC_Init();			//输入捕获初始化
	
	/*显示静态字符串*/
	OLED_ShowString(1, 1, "Freq:00000Hz");		//1行1列显示字符串Freq:00000Hz
	OLED_ShowString(2, 1, "Duty:00%");			//2行1列显示字符串Duty:00%
	
	/*使用PWM模块提供输入捕获的测试信号*/
	PWM_SetPrescaler(720 - 1);					//PWM频率Freq = 72M / (PSC + 1) / 100
	PWM_SetCompare1(50);						//PWM占空比Duty = CCR / 100
	
	while (1)
	{
		OLED_ShowNum(1, 6, IC_GetFreq(), 5);	//不断刷新显示输入捕获测得的频率
		OLED_ShowNum(2, 6, IC_GetDuty(), 2);	//不断刷新显示输入捕获测得的占空比
	}
}

          IC.c文件:

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:输入捕获初始化
  * 参    数:无
  * 返 回 值:无
  */
void IC_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);			//开启TIM3的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							//将PA6引脚初始化为上拉输入
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM3);		//选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;               //计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;               //预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM3的时基单元
	
	/*PWMI模式初始化*/
	TIM_ICInitTypeDef TIM_ICInitStructure;							//定义结构体变量
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;				//选择配置定时器通道1
	TIM_ICInitStructure.TIM_ICFilter = 0xF;							//输入滤波器参数,可以过滤信号抖动
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;		//极性,选择为上升沿触发捕获
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;			//捕获预分频,选择不分频,每次信号都触发捕获
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;	//输入信号交叉,选择直通,不交叉
	TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);						//将结构体变量交给TIM_PWMIConfig,配置TIM3的输入捕获通道
																	//此函数同时会把另一个通道配置为相反的配置,实现PWMI模式

	/*选择触发源及从模式*/
	TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);					//触发源选择TI1FP1
	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);					//从模式选择复位
																	//即TI1产生上升沿时,会触发CNT归零
	
	/*TIM使能*/
	TIM_Cmd(TIM3, ENABLE);			//使能TIM3,定时器开始运行
}

/**
  * 函    数:获取输入捕获的频率
  * 参    数:无
  * 返 回 值:捕获得到的频率
  */
uint32_t IC_GetFreq(void)
{
	return 1000000 / (TIM_GetCapture1(TIM3) + 1);		//测周法得到频率fx = fc / N,这里不执行+1的操作也可
}

/**
  * 函    数:获取输入捕获的占空比
  * 参    数:无
  * 返 回 值:捕获得到的占空比
  */
uint32_t IC_GetDuty(void)
{
	return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);	//占空比Duty = CCR2 / CCR1 * 100,这里不执行+1的操作也可
}

          PWM.c文件:

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:PWM初始化
  * 参    数:无
  * 返 回 值:无
  */
void PWM_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	/*GPIO重映射*/
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);			//开启AFIO的时钟,重映射必须先开启AFIO的时钟
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);			//将TIM2的引脚部分重映射,具体的映射方案需查看参考手册
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);		//将JTAG引脚失能,作为普通GPIO引脚使用
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;		//GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							//将PA0引脚初始化为复用推挽输出	
																	//受外设控制的引脚,均需要配置为复用模式		
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;					//计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;				//预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
	
	/*输出比较初始化*/
	TIM_OCInitTypeDef TIM_OCInitStructure;							//定义结构体变量
	TIM_OCStructInit(&TIM_OCInitStructure);							//结构体初始化,若结构体没有完整赋值
																	//则最好执行此函数,给结构体所有成员都赋一个默认值
																	//避免结构体初值不确定的问题
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;				//输出比较模式,选择PWM模式1
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;		//输出极性,选择为高,若选择极性为低,则输出高低电平取反
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	//输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;								//初始的CCR值
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);						//将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1
	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

/**
  * 函    数:PWM设置CCR
  * 参    数:Compare 要写入的CCR的值,范围:0~100
  * 返 回 值:无
  * 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
  *           占空比Duty = CCR / (ARR + 1)
  */
void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);		//设置CCR1的值
}

/**
  * 函    数:PWM设置PSC
  * 参    数:Prescaler 要写入的PSC的值,范围:0~65535
  * 返 回 值:无
  * 注意事项:PSC和ARR共同决定频率,此函数仅设置PSC的值,并不直接是频率
  *           频率Freq = CK_PSC / (PSC + 1) / (ARR + 1)
  */
void PWM_SetPrescaler(uint16_t Prescaler)
{
	TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate);		//设置PSC的值
}

6.2.4 实验现象

          OLED 屏幕同步显示两行数据:第一行为测得的信号频率(如 10000Hz),第二行为测得的信号占空比(如 50%)。动态修改发生器(或 TIM2)的预分频或捕获比较寄存器值,屏幕显示的测量参数会即刻跟随发生线性变化。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐