目录
- 1.输出比较OC
-
- [1.1 简介](#1.1 简介)
- [1.2 PWM简介](#1.2 PWM简介)
- [1.3 输出比较通道](#1.3 输出比较通道)
-
- [1.3.1 通用定时器](#1.3.1 通用定时器)
- [1.3.2 高级定时器](#1.3.2 高级定时器)
- [1.4 PWM基本结构](#1.4 PWM基本结构)
- [1.6 舵机](#1.6 舵机)
- [1.7 直流电机](#1.7 直流电机)
-
- [1.7.1 引入:MX1508 芯片](#1.7.1 引入:MX1508 芯片)
- [1.7.2 TB6612芯片](#1.7.2 TB6612芯片)
- [1.8 结构体和API](#1.8 结构体和API)
-
- [1.8.1 结构体](#1.8.1 结构体)
- [1.8.2 API](#1.8.2 API)
-
- [1. `TIM_OC1Init` / `TIM_OC2Init` / `TIM_OC3Init` / `TIM_OC4Init`](#1.
TIM_OC1Init
/TIM_OC2Init
/TIM_OC3Init
/TIM_OC4Init
) - [2. `TIM_OCStructInit`](#2.
TIM_OCStructInit
) - [3. `TIM_SetCompare1` / `TIM_SetCompare2` / `TIM_SetCompare3` / `TIM_SetCompare4`](#3.
TIM_SetCompare1
/TIM_SetCompare2
/TIM_SetCompare3
/TIM_SetCompare4
) - 4.示例
- [1. `TIM_OC1Init` / `TIM_OC2Init` / `TIM_OC3Init` / `TIM_OC4Init`](#1.
- [1.9 实验](#1.9 实验)
-
- [1.9.1 呼吸灯](#1.9.1 呼吸灯)
-
- [1. `PWM_Init` 函数](#1.
PWM_Init
函数) - [2. `PWM_SetCompare1` 函数](#2.
PWM_SetCompare1
函数) - 3.使用示例
- [1. `PWM_Init` 函数](#1.
- [1.9.2 舵机](#1.9.2 舵机)
- [1.9.3 直流电机](#1.9.3 直流电机)
- [1.10 混淆](#1.10 混淆)
-
- [1.10.1 中断频率](#1.10.1 中断频率)
- [1.10.2 PWM 输出频率](#1.10.2 PWM 输出频率)
- [1.10.3 占空比](#1.10.3 占空比)
- 1.10.4示例理解
- [1.10.5 PWM波形和时钟电平](#1.10.5 PWM波形和时钟电平)
- 2.输入捕获IC
-
- [2.1 简介](#2.1 简介)
- [2.2 输入的频率测量](#2.2 输入的频率测量)
- [2.3 输入比较通道](#2.3 输入比较通道)
- [2.4 基本结构](#2.4 基本结构)
- [2.5 相关api和结构体](#2.5 相关api和结构体)
-
- [2.5.1 结构体](#2.5.1 结构体)
- [2.5.2 api](#2.5.2 api)
- [2.6 实验](#2.6 实验)
-
- [2.6.1 输入捕获模式测频率](#2.6.1 输入捕获模式测频率)
- [2.6.2 PWMI模式测频率占空比](#2.6.2 PWMI模式测频率占空比)
- 3.TIM编码器接口
-
- [3.1 简介](#3.1 简介)
- [3.2 正交编码器](#3.2 正交编码器)
- [3.3 基本结构](#3.3 基本结构)
- [3.4 工作模式](#3.4 工作模式)
- [3.5 实例](#3.5 实例)
- [3.5 实验](#3.5 实验)

1.输出比较OC
1.1 简介
OC(Output Compare)输出比较
通用定时器的输出比较单元支持四个输出通道(OC1、OC2、OC3、OC4),用于比较 CNT 计数器和预设的比较值 (CCRx),主要用于产生 PWM 信号、输出定时事件等。
输出比较单元通过比较 CNT 计数器和指定的比较值 (CCRx) 来控制输出行为,支持多种模式:
- PWM 模式:通过设置比较值实现 PWM 输出,用于调节输出信号的占空比。
- 定时模式:在计数器值等于比较值时生成输出事件,适合用于定时控制。
- 单脉冲模式:产生单个脉冲信号,可以用于控制单一事件。
每个高级定时器和通用定时器都拥有4个输出比较通道,高级定时器的前3个通道额外拥有死区生成和互补输出的功能,这些在上一篇文章已经讲解过,下面是通用定时器中输出比较寄存器的位置:

1.2 PWM简介
PWM(Pulse Width Modulation)脉冲宽度调制。
PWM参数: 频率 = 1 / TS 占空比 = TON / TS 分辨率 = 占空比变化步距
PWM波形是一种数字信号 ,也是由高低电平组成。在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域。

按理说数字信号控制LED只有完全亮和完全灭两种状态,但是通过PWM可以实现呼吸灯的效果,也就是渐变的亮度,而PWM也是数字信号,这怎么实现的???其实就是通过改变数字信号高低电平波形的的时间比例(Ton和Toff)来实现LED呈现不同亮度的级别,只要我闪的够快,就察觉不到我是只有完全灭和亮。
其实说白了PWM就是通过对数字信号的高低电平时间占比的调制,来实现类似于模拟信号的效果:占空比越大,就越趋近于模拟电压的高电平

但需要注意的是有个前提:在具有惯性的系统中。比如LED由亮转灭,这个过程是有时间的,并不是一瞬间的事情;对于电机,转动的时候想要停下来,也是需要时间的。这些都是惯性。
1.3 输出比较通道
1.3.1 通用定时器

当CNT>CCR1或CNT=CCR1的时候,输出模式控制器就会该改变它输出的co1ref(Output Compare 1 reference 输出捕获参考信号1)。接下来这个信号可以通往主模式控制器,也就是映射到TRGO引脚输出出去。不过oc1ref主要是去往下面的一路,可以到了有个0和1的极性选择,就是CC1P写0的时候,信号不翻转,写1的时候信号翻转。
那么什么时候给oc1reg高电平,什么时候给oc1ref低电平,这就要去看输出模式控制器的输出比较模式了,通过配置TIMx_CCMR1寄存器来灵活地控制器ocref的输出
模式 | 描述 |
---|---|
冻结 | CNT=CCR时,REF保持为原状态 |
匹配时置有效电平 | CNT=CCR时,REF置有效电平 |
匹配时置无效电平 | CNT=CCR时,REF置无效电平 |
匹配时电平翻转 | CNT=CCR时,REF电平翻转 |
强制为无效电平 | CNT与CCR无效,REF强制为无效电平 |
强制为有效电平 | CNT与CCR无效,REF强制为有效电平 |
PWM模式1 | 向上计数:CNT<CCR时,REF置有效电平,CNT≥CCR时,REF置无效电平向下计数:CNT>CCR时,REF置无效电平,CNT≤CCR时,REF置有效电平 |
PWM模式2 | 向上计数:CNT<CCR时,REF置无效电平,CNT≥CCR时,REF置有效电平向下计数:CNT>CCR时,REF置有效电平,CNT≤CCR时,REF置无效电平 |
1.3.2 高级定时器

通常外部电路接的是两个MOS管(大功率电子开关),高电平导通,低电平断开,也就是最基本的推挽电路,中间是输出。上管导通,下管断开就是输出高电平;反之则输出低电平。因此上管和下管是互补的,因此内部中就设置了OC1和OC1N来和个字节的MOS管对应,分别控制MOS的导通和关闭。
如果上管导通的瞬间,下管立刻打开,可能会因为期间的不理想,上管还没有完全打开下管就已经导通了,出现了短暂的上下导通事件,会导致功功率损耗,引起器件发热。因为为了这种现象就有了死区生成电路,起着MOS关闭的时候延迟一小段时间。
1.4 PWM基本结构

对于图中的折线图,黄色线是ARR,也就是中断发生所需要达到的计数次数;蓝色折现则是CNT计数次数,当CNT==ARR就清0,申请中断。
在这个过程中添加一个CCR,也就是红色线,当CNT<CCR,REF就值为有效电平(高电平),也就是图中的绿色波形信号图。
这样PWM的占空比就受到CCR和ARR影响(无效电平占比,ARR越高,CCR越高,占空比就越大)
- PWM频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1)
- PWM占空比: Duty = CCR / (ARR + 1)
- PWM分辨率: Reso = 1 / (ARR + 1)

需要牢记的是PWM波形靠的是ARR、CCR、CNT来进行转换成电平的,因此得借助到时钟电平,因为CNT的计数是和时钟电平挂钩的,所以千万不要认识转换后的REF的PWM波形是和时钟电平是一回事。
c
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 设置时钟分频,不分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 设置计数模式为向上计数
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; // 计数周期 ARR,设置为 99
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; // 预分频 PSC,设置为 719
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重复计数器(仅高级定时器用),设置为 0
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); // 配置 TIM2 的时基单元
- TIM_ClockDivision:设为 TIM_CKD_DIV1,不分频。
- TIM_CounterMode:设为 TIM_CounterMode_Up,表示计数器采用向上计数模式。
- TIM_Period:设为 99 (100 - 1),ARR 决定计数的上限。ARR 的值加上预分频器值决定 PWM 的频率。
- TIM_Prescaler:设为 719,定时器时钟频率会被分频为 72MHz / 720 = 100KHz。
- TIM_RepetitionCounter:设为 0,仅在高级定时器(如 TIM1)中生效。
以上配置将时钟频率设置为 100KHz,计数器每达到 99 时溢出,所以 PWM 频率为 72MHz /720Hz/100Hz = 1KHz。(1kHz表示每秒有1000个周期。因此,1个周期的时间是1秒除以1000,即0.001秒,也就是一次PWM波形的输出占比的时间是1毫秒,其中又分为了高电平和低电平的占比。当输出了99次PWM波形后CNT就清0从从新开始)

注意是不要和中断混淆,在中断中(假设是向上计数),CNT每加1(上升沿+1)的周期时间是1/(CK_PSC/(PSC+1)),CNT达到ARR就申请中断,也就是申请中断的时间为1/(CK_PSC/(PSC+1)) ✖ ARR,这个时间内有多个完整的高低电平。
在PWM中,PWM频率则是指一次完整的PWM周期波形所所占的时间:1/Freq,Freq = CK_PSC / (PSC + 1) / (ARR + 1)。比如上图就是1ms;占空比的比例是指在PWM波形周期计数CCR**➗**重装载计数ARR+1,其实就可以等价于高电平在一个PWM波形中所占的时间比例。
1.6 舵机
舵机是一种根据输入PWM信号占空比来控制输出角度的装置输入
PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms


1.7 直流电机
1.7.1 引入:MX1508 芯片
GPIO 驱动能力有限,要在引脚和电机之间加入驱动电路,也就是驱动芯片。
MX1508 芯片采用 H 桥电路结构设计,采用高可靠性功率管工艺,特别适合驱动线圈、马达等感性负载,内置两路驱动电路,可同时驱动两个线圈马达,工作电压范围覆盖 2V 到 9.6V。电路设计有芯片级温度检测电路,实时监控芯片内部发热,当芯片内部温度超过设定值时(典型值 150℃),启动保护功能,芯片内置的温度迟滞电路,确保电路恢复到安全温度后,才允许重新对功率管进行控制。

待机模式
- 在待机模式下,INAx=INBx=L。包括驱动功率管在内的所有内部电路都处于关断状态。电路消耗极低极低的电流。此时马达输出端 OUTAx 和 OUTBx 都为高阻状态(记为 Z)。
正转模式
- 正转模式的定义为:INAx=H,INBx=L,此时马达驱动端 OUTAx 输出高电平,马达驱动端 OUTBx 输出低电平时,马达驱动电流从 OUTAx 流入马达,从OUTBx 流到地端,此时马达的转动定义为正转模式。
反转模式
- 反转模式的定义为:INAx=L,INBx=H,此时马达驱动端 OUTBx 输出高电平,马达驱动端 OUTAx 输出低电平时,马达驱动电流从 OUTBx 流入马达,从OUTAx 流到地端,此时马达的转动定义为反转模式。
刹车模式
- 刹车模式的定义为:INAx=H,INBx=H,此时马达驱动端 OUTAx 以及 OUTBx 都输出低电平,马达内存储的能量将通过 OUTAx 端 NMOS 管或者 OUTBx 端NMOS 快速释放,马达在短时间内就会停止转动。注意在刹车模式下电路将消耗静态功耗。
PWM 模式 A
- 当输入信号 INAx 为 PWM 信号,INBx=0 或者 INAx=0,INBx 为 PWM 信号时,马达的转动速度将受到 PWM 信号占空比的控制。在这个模式下,马达驱动电路是在导通和待机模式之间切换,在待机模式下,所有功率管都处于关断状态,马达内部储存的能量只能通过功率 MOSFET 的体二极管缓慢释放。
- 当输入信号 INAx 为 PWM 信号,INBx=1 或者 INAx=1,INBx 为 PWM 信号时,马达的转动速度将受到 PWM 信号占空比的控制。在这个模式下,马达驱动电路输出在导通和刹车模式之间,在刹车模式下马达存储的能量通过低边的NMOS 管快速释放。
1.7.2 TB6612芯片
直流电机是一种将电能转换为机械能的装置,有两个电极,当电极正接时,电机正转,当电极反接时,电机反转
直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作
TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向



1.8 结构体和API
1.8.1 结构体
这个 TIM_OCInitTypeDef
结构体用于配置定时器的输出比较(Output Compare)和 PWM 模式的参数,适用于 STM32F101 系列。以下是每个成员的详细说明,包括其可能的取值及含义:
TIM_OCMode
:指定定时器的输出比较模式或 PWM 模式。
-
- 可选值包括:
-
-
TIM_OCMode_Timing
:仅用于定时,不产生任何输出。TIM_OCMode_Active
:当计数器达到捕获比较寄存器(CCR)值时,将输出引脚置为高电平。TIM_OCMode_Inactive
:当计数器达到 CCR 值时,将输出引脚置为低电平。TIM_OCMode_Toggle
:当计数器达到 CCR 值时,输出引脚电平翻转。TIM_OCMode_PWM1
:标准 PWM 模式 1。TIM_OCMode_PWM2
:标准 PWM 模式 2。
-
-
TIM_OutputState
:配置输出比较状态。
-
- 可选值包括:
-
-
TIM_OutputState_Disable
:禁用输出比较。TIM_OutputState_Enable
:启用输出比较,使能输出信号。
-
TIM_OutputNState
:配置互补输出比较状态,仅适用于高级定时器 TIM1 和 TIM8。
-
- 可选值包括:
-
-
TIM_OutputNState_Disable
:禁用互补输出。TIM_OutputNState_Enable
:启用互补输出。
-
TIM_Pulse
:指定加载到捕获比较寄存器(CCR)中的脉冲值。
-
- 值范围为
0x0000
至0xFFFF
。在 PWM 模式下,该值用于控制 PWM 的占空比。
- 值范围为
TIM_OCPolarity
:配置输出比较信号的极性。
-
- 可选值包括:
-
-
TIM_OCPolarity_High
:输出高电平有效。TIM_OCPolarity_Low
:输出低电平有效。
-
TIM_OCNPolarity
:配置互补输出的极性,仅适用于 TIM1 和 TIM8。
-
- 可选值包括:
-
-
TIM_OCNPolarity_High
:互补输出高电平有效。TIM_OCNPolarity_Low
:互补输出低电平有效。
-
TIM_OCIdleState
:配置空闲状态下(即计数器停止时)输出比较引脚的状态,仅适用于 TIM1 和 TIM8。
-
- 可选值包括:
-
-
TIM_OCIdleState_Set
:空闲状态下输出引脚为高电平。TIM_OCIdleState_Reset
:空闲状态下输出引脚为低电平。
-
TIM_OCNIdleState
:配置空闲状态下互补输出引脚的状态,仅适用于 TIM1 和 TIM8。
-
- 可选值包括:
-
-
TIM_OCNIdleState_Set
:空闲状态下互补输出引脚为高电平。TIM_OCNIdleState_Reset
:空闲状态下互补输出引脚为低电平。
-
下面是一个使用 TIM_OCInitTypeDef
的示例代码,配置定时器通道 1 为 PWM 模式 1,输出高电平有效,空闲状态低电平:
c
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable; // 仅用于 TIM1 和 TIM8,其他定时器可忽略
TIM_OCInitStructure.TIM_Pulse = 1000; // 占空比设置,决定 PWM 高电平的时间
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; // 仅用于 TIM1 和 TIM8
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; // 仅用于 TIM1 和 TIM8
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset; // 仅用于 TIM1 和 TIM8
// 使用配置初始化定时器通道1的输出比较功能
TIM_OC1Init(TIMx, &TIM_OCInitStructure);
以上配置在计数器到达 CCR 值时切换输出状态,以产生 PWM 信号,适合用于控制占空比的应用。
1.8.2 API
与输出比较(Output Compare)功能相关的函数如下:
TIM_OC1Init
TIM_OC2Init
TIM_OC3Init
TIM_OC4Init
TIM_OCStructInit
TIM_SetCompare1
TIM_SetCompare2
TIM_SetCompare3
TIM_SetCompare4
其余的函数看固件库函数手册就行了。
1. TIM_OC1Init
/ TIM_OC2Init
/ TIM_OC3Init
/ TIM_OC4Init
这些函数用于初始化定时器的输出比较通道。不同的函数针对不同的输出比较通道(OC1、OC2、OC3 和 OC4)进行初始化。
- 函数原型:
c
void TIM_OCxInit(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
其中 x
可以是 1 到 4,分别对应 OC1 到 OC4 通道。
-
参数:
-
TIMx
:指定要配置的定时器,例如TIM1
、TIM2
等。TIM_OCInitStruct
:指向TIM_OCInitTypeDef
结构体的指针,该结构体包含输出比较相关的配置信息,如比较模式、极性、脉冲值等。
-
使用示例:
c
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 1000;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
// 初始化 TIM1 的 OC1 通道
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
2. TIM_OCStructInit
该函数用于初始化 TIM_OCInitTypeDef
结构体,使其成员变量设定为默认值。
- 函数原型:
c
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
- 使用示例:
c
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
3. TIM_SetCompare1
/ TIM_SetCompare2
/ TIM_SetCompare3
/ TIM_SetCompare4
这些函数用于设置定时器的比较寄存器值,即指定定时器计数器达到比较值时的输出状态。
- 函数原型:
c
void TIM_SetComparex(TIM_TypeDef* TIMx, uint16_t Comparex);
其中 x
可以是 1 到 4,分别对应 OC1 到 OC4 比较寄存器。
-
参数:
-
TIMx
:指定要配置的定时器,例如TIM1
、TIM2
等。Comparex
:指定比较值。当定时器计数器达到该值时,会触发输出比较事件。
-
使用示例:
c
// 设置 TIM1 的 OC1 通道的比较值为 2000
TIM_SetCompare1(TIM1, 2000);
4.示例
以下示例演示如何使用这些函数来配置一个定时器的输出比较通道,实现当计数器到达指定的比较值时,输出引脚的状态会发生切换(Toggle 模式)。
c
#include "stm32f10x.h"
// 初始化输出比较
void TIM_OC_ExampleInit(void) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 1. 启用定时器时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// 2. 配置定时器的基础时间
TIM_TimeBaseStructure.TIM_Period = 4000 - 1; // 自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler = 7200 - 1; // 预分频器
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 不分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// 3. 配置 OC1 通道为 Toggle 模式
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle; // 切换模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 使能输出
TIM_OCInitStructure.TIM_Pulse = 2000; // 设置比较值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 高电平极性
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
// 4. 使能定时器
TIM_Cmd(TIM2, ENABLE);
}
int main(void) {
// 初始化输出比较
TIM_OC_ExampleInit();
while (1) {
// 主循环
}
}
- 定时器配置 :使用
TIM_TimeBaseInit
配置定时器基础参数,设置自动重装载值为 4000 和预分频值为 7200,以调整定时器的计数频率。 - 输出比较配置:配置 OC1 通道为 Toggle 模式,当计数器达到设定的脉冲值(2000)时会切换输出状态。
- 开启定时器 :调用
TIM_Cmd(TIM2, ENABLE);
启动定时器。
在该示例中,定时器每当计数器达到 TIM_OCInitStructure.TIM_Pulse
的值(2000)时,输出状态(例如引脚电平)将发生切换,从而实现一个方波输出或其它周期性信号的生成。
1.9 实验
1.9.1 呼吸灯

注:这里是负极接地,正极接IO口,也就是说这个LED是高电平驱动亮的。
User:
Hardware:
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的值
}
1. PWM_Init
函数
PWM_Init
函数用于初始化 PWM 的配置,包括开启定时器的时钟、设置 GPIO 引脚、配置时基单元、输出比较模式等。
时钟与 GPIO 配置
c
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 开启 TIM2 的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 开启 GPIOA 的时钟
首先开启了 TIM2 和 GPIOA 的时钟,以确保后续可以对这两个外设进行配置。
c
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 设置引脚模式为复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 选择 PA0 引脚作为 PWM 输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 设置引脚的速度为 50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化 PA0 引脚
配置了 PA0 引脚为复用推挽输出模式,速度为 50MHz,用于输出 PWM 信号。使用定时器来控制引脚,需要使用复用功能输出,因为此时通过数据寄存器输出的引脚是断开的。

定时器内部时钟配置
c
TIM_InternalClockConfig(TIM2); // 选择 TIM2 为内部时钟
将 TIM2 的时钟源设置为内部时钟,默认为内部时钟,所以可以不写。
时基单元初始化
c
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 设置时钟分频,不分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 设置计数模式为向上计数
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; // 计数周期 ARR,设置为 99
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; // 预分频 PSC,设置为 719
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重复计数器(仅高级定时器用),设置为 0
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); // 配置 TIM2 的时基单元
- TIM_ClockDivision:设为 TIM_CKD_DIV1,不分频。
- TIM_CounterMode:设为 TIM_CounterMode_Up,表示计数器采用向上计数模式。
- TIM_Period:设为 99 (100 - 1),ARR 决定计数的上限。ARR 的值加上预分频器值决定 PWM 的频率。
- TIM_Prescaler:设为 719,定时器时钟频率会被分频为 72MHz / 720 = 100KHz。
- TIM_RepetitionCounter:设为 0,仅在高级定时器(如 TIM1)中生效。
以上配置将时钟频率设置为 100KHz,计数器每达到 99 时溢出,所以 PWM 频率为 72MHz /720Hz/100Hz = 1KHz。(1kHz表示每秒有1000个周期。因此,1个周期的时间是1秒除以1000,即0.001秒,也就是一次PWM波形的输出占比的时间是1毫秒,其中又分为了高电平和低电平的占比。)

输出比较初始化
c
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 设置 PWM 模式为 PWM1
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 设置输出极性为高
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 使能输出
TIM_OCInitStructure.TIM_Pulse = 0; // 初始的占空比为 0%
TIM_OC1Init(TIM2, &TIM_OCInitStructure); // 初始化 TIM2 通道 1
- TIM_OCMode :设置为 TIM_OCMode_PWM1,表示 PWM 模式 1,当计数器值小于
CCR
值时输出高电平,大于等于时输出低电平。 - TIM_OCPolarity:设置为高极性,即输出高电平。
- TIM_OutputState:使能输出。
- TIM_Pulse :设为 0,这里控制了 CCR 寄存器的初始值,占空比公式为
CCR / (ARR + 1)
,设为 0 表示初始占空比为 0。可以理解为1个周期内(1KHz的一个周期时间为1ms)高电平所占的时间
使能定时器
c
TIM_Cmd(TIM2, ENABLE); // 使能 TIM2
使能 TIM2,开始计数,输出 PWM 信号。
2. PWM_SetCompare1
函数
c
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare); // 设置 CCR1 的值
}
- Compare:占空比设定值,范围为 0 ~ 100。
- TIM_SetCompare1 :将
Compare
值写入 TIM2 的 CCR1 寄存器,控制输出占空比。
例如,Compare
设为 50 时,占空比为 50%。
3.使用示例
c
int main(void)
{
PWM_Init(); // 初始化 PWM
while (1)
{
for (uint16_t i = 0; i <= 100; i++)
{
PWM_SetCompare1(i); // 逐步增加占空比
Delay_ms(10); // 延时,形成呼吸效果
}
for (uint16_t i = 100; i > 0; i--)
{
PWM_SetCompare1(i); // 逐步减小占空比
Delay_ms(10);
}
}
}
上述代码在 main
函数中,通过循环逐步增加和减少占空比,实现 LED 灯的"呼吸"效果。占空比越大,LED越亮。
1.9.2 舵机

User:
Hardware:
舵机的PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms。也就是Freq = 50Hz


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初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA1引脚初始化为复用推挽输出
//受外设控制的引脚,均需要配置为复用模式
/*配置时钟源*/
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 = 20000 - 1; //计数周期,即ARR的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 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_OC2Init(TIM2, &TIM_OCInitStructure); //将结构体变量交给TIM_OC2Init,配置TIM2的输出比较通道2
/*TIM使能*/
TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行
}
/**
* 函 数:PWM设置CCR
* 参 数:Compare 要写入的CCR的值,范围:0~100
* 返 回 值:无
* 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
* 占空比Duty = CCR / (ARR + 1)
*/
void PWM_SetCompare2(uint16_t Compare)
{
TIM_SetCompare2(TIM2, Compare); //设置CCR2的值
}
/**
* 函 数:舵机设置角度
* 参 数:Angle 要设置的舵机角度,范围:0~180
* 返 回 值:无
*/
void Servo_SetAngle(float Angle)
{
PWM_SetCompare2(Angle / 180 * 2000 + 500); //设置占空比
//将角度线性变换,对应到舵机要求的占空比范围上
}
系统内部时钟CK_PSC为72MHz,那么PSC可以设置为72Hz,ARR设置为20K,Freq就为50Hz(一个周期0.02s = 20ms)这个是PWM一个波形输出的周期时间
那么占空比就可以设置为Angle / 180 * 2000 + 500,Angle是角度
舵机的控制脉宽范围为 0.5ms 到 2.5ms 对应 -90° 到 90°,也就是2ms的时间刚好丢应一个180°。
脉宽的最大增量 = (2.5ms - 0.5ms) = 2ms。而这个2ms需要转换为CNT的计数次数才能被识别。根据代码设置的PSC+1为72,那么分频后的系统时钟频率为:1,000,000,也就是1s(1000ms)内有1,000,000个电平周期(或者说是1s内CNT寄存器会计数1,000,000次),那么换算一下就可以知道1ms有1,000个周期,所以2ms是2000个周期。
而ARR设置为了20000个计数,那么就是说一个PWM脉冲波形持续20000/1000 = 20ms
也就是说走一个180°,CNT需要计数2000个,占空比为2000/20000=1/10 --- CNT/(ARR+1),也就是高电平的时间为1/10 ✖ 20ms = 2ms,这不就对上了。那1°的话,CNT就需要计数1/180° ✖2000 次。而加上这个舵机默认是从-90°开始的,未转动的起始脉冲就占了0.5ms,所以得加上偏移量500
1.9.3 直流电机

User:
Hardware:
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初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA2引脚初始化为复用推挽输出
//受外设控制的引脚,均需要配置为复用模式
/*配置时钟源*/
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 = 36 - 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_OC3Init(TIM2, &TIM_OCInitStructure); //将结构体变量交给TIM_OC3Init,配置TIM2的输出比较通道3
/*TIM使能*/
TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行
}
/**
* 函 数:PWM设置CCR
* 参 数:Compare 要写入的CCR的值,范围:0~100
* 返 回 值:无
* 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
* 占空比Duty = CCR / (ARR + 1)
*/
void PWM_SetCompare3(uint16_t Compare)
{
TIM_SetCompare3(TIM2, Compare); //设置CCR3的值
}
PWM脉冲一个周期的电平持续多长时间???和舵机的差不多:
设置了PSC为36,那么分频后的时钟频率为2MHz(72/32),也就是1s内时钟输出的电平周期有2,000,000个,即1s内CNT计数器能计数2,000,000,而设置了ARR+1为100,也就是计数到了100,CNT就清0。
那么这时候就可以知道CNT计数一次的时间了,也就是一个时钟电平周期的时间为0.0005ms
即一个PWM波形电平周期时间为0.0005ms ✖ (ARR+1) = 0.05ms,其高电平持续时间为CNT/(ARR+1) ✖0.05 ms。
1.10 混淆
需要牢记的是PWM波形靠的是ARR、CCR、CNT来进行转换成电平的,因此得借助到时钟电平,因为CNT的计数是和时钟电平挂钩的,所以千万不要认识转换后的REF的PWM波形是和时钟电平是一回事。
1.10.1 中断频率
在定时器中断模式下,可以设置定时器达到 ARR
值(即自动重装载值)时触发中断。假设定时器计数器是向上计数模式,那么每当 CNT
增加 1 时经过的时间为:

即每次计数的时间间隔是时钟频率 CK_PSC 经过预分频器 PSC
分频之后的结果(这是分频后的时钟,一次内部完整的高低电平周期时间)。计数器 CNT
达到 ARR
值时就会触发一次中断,因此中断触发周期(即中断的间隔时间)为:

在这个时间间隔内,计数器 CNT
从 0 增加到 ARR
,即会经过 ARR + 1
个计数周期。
1.10.2 PWM 输出频率
在 PWM 模式下,PWM 的频率是指一个完整的 PWM 周期,即一个完整的高电平和低电平所需的时间。PWM 频率可以通过预分频器 PSC
和自动重装载寄存器 ARR
来设置。PWM 的频率公式如下:

也就是说,PWM 的周期 T
为:

这个周期就是一个完整的 PWM 波形(一个高电平和一个低电平的组合)的时间。
1.10.3 占空比
在 PWM 中,占空比定义为在一个 PWM 周期内,高电平所占的时间比例。可以用 CCR
值与 ARR
值的比值来表示占空比:

例如,当 CCR
为 ARR
的一半时,占空比为 50%,表示高电平时间是低电平时间的 1 倍。如果 ARR
为 99,那么占空比为 50 / 100 = 50%
。
1.10.4示例理解
假设时钟 CK_PSC 为 72MHz,设置 PSC = 719
,ARR = 99
,那么:
- 中断触发频率 :每当
CNT
从 0 增加到ARR
时触发中断。
-
- 预分频后时钟频率为:
- 预分频后时钟频率为:
-
-
- 这表示分频后的时钟一次完整的高低电平的时间为0.01ms,也就是一次计数的时间为0.01ms
-
-
- 每次中断的周期为:
- 每次中断的周期为:
-
这表示每 1 毫秒触发一次计数清0。
-
PWM 输出频率:
-
- PWM 频率为:
- PWM 频率为:
-
表示 PWM 信号的频率是 1kHz,即每个 PWM 周期的时间为 1 毫秒。
-
占空比:
-
- 若
CCR = 50
,则占空比为
- 若
-
表示高电平持续时间为 0.5 毫秒,低电平持续时间也为 0.5 毫秒。
1.10.5 PWM波形和时钟电平
PWM 波形的生成确实依赖于 ARR
、CCR
和 CNT
,它们共同作用将定时器的计数(由时钟驱动)转换为我们所需要的 PWM 输出信号。但 PWM 波形本身和时钟电平并不是一回事,虽然 PWM 信号的产生需要依赖时钟信号的计数过程。
- 计数原理:
-
CNT
是一个计数器,它的计数是基于内部时钟(CK_PSC)驱动的。每当CNT
增加 1,实际是内部时钟经过了若干周期(取决于预分频器PSC
的配置)。CNT
从 0 开始计数到ARR
,在达到ARR
后重置为 0,形成一个周期。
- PWM 信号生成:
-
- 在每个 PWM 周期内,当
CNT
<CCR
时,输出为高电平;当CNT
≥CCR
时,输出为低电平。 - 通过设置
CCR
的值,可以改变在一个周期内高电平所占的时间(即占空比)。但是,CCR
的大小不会影响计数器CNT
的计数速度,也不会影响 PWM 的周期(周期由ARR
决定)。
- 在每个 PWM 周期内,当
- 时钟与 PWM 波形的关系:
-
- 时钟信号的频率和预分频器
PSC
设置决定了CNT
增加的速度,即定时器的计数速度。 - 然而,PWM 波形的频率和占空比是由
ARR
和CCR
控制的,是CNT
的计数结果被映射到输出引脚生成的波形。也就是说,PWM 波形只是利用了时钟信号计数的结果,并不是直接等同于时钟电平。
- 时钟信号的频率和预分频器
因此,时钟电平是定时器计数的基础,而 PWM 波形是基于 ARR
、 CCR
和 CNT
生成的输出结果。
2.输入捕获IC
2.1 简介
IC(Input Capture)输入捕获
输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
每个高级定时器和通用定时器都拥有4个输入捕获通道,可配置为PWMI模式,同时测量频率和占空比可配合主从触发模式,实现硬件全自动测量

-
通用定时器支持四路输入捕获通道(TI1、TI2、TI3、TI4),用于捕获外部输入信号的事件,主要用于频率测量、信号周期或占空比测量等。并且从图中可以可以看出,一个引脚的输入是可以有两种路线可以走的,也就是可以隐射到两个捕获单元,这是PWMI模式的经典结构
-
每个输入捕获通道具有以下功能组件:
-
- 输入滤波器:对输入信号进行去抖和滤波处理,以减少噪声的干扰。
- 边沿检测器:可以选择高电平触发还是低电平触发,当出现指定电平时,就会触发后续电路执行工作
- 预分频器 (ICxPS):可以将输入信号分频后再捕获,以支持不同频率信号的测量。
- 捕获比较寄存器 (CCRx):当发生输入事件时,捕获当前的 CNT 值并存储在对应的 CCR 寄存器中。
2.2 输入的频率测量

测频法:在闸门时间T内,对上升沿计次,得到N,则频率
- 就是指定时间内出现了多少个周期电平信号,符合频率的基本定义
- 这个适合测高频信号
测周法:两个上升沿内,以标准频率fc计次,得到N ,则频率
- 周期的倒数就是频率,如果能知道一个周期的时间,就能知道其频率。通过捕获一个周期电平信号的时间(两个电平的上升沿之间),获得其时间,就能知道输入的信号的频率。
- 但是一般我们没有那么精确的时间测试来对输入的信号频率进行测试,用的还是内部的时钟,而时钟本质还是计数,我们是可以知道时钟频率为fc,也就是计数一次的时间为1/fc,那么只要从输入的电平的第一个上升沿开始计次到第二个上升沿结束,得到其计次数N,就可以知道输入的信号周期时间为:1/fc ✖ N,取倒数就可以知道频率为fc/N
- 这个适合测低频信号
中界频率:测频法与测周法误差相等的频率点
- 这个主要是解决上面两种方法正负1误差的,根据情况决定选择上面的哪两种方法。
- 当我使用的是测频法,阀门时间T到了,而最后一个几次刚好就对应在了最后的电平的中间位置,就会产生误差,要么只能舍弃它要么就只能算成1个周期。同理测周法也一样会有这种类似的情况
- 所以一般计次N都需要尽量大一点,来减少些误差。
2.3 输入比较通道

上面也提到过,输入的信号经过处理后是可以进入不同的捕获通道的,也就是上面的TI1FP1和TI2FP1,前者来自自己的通道,后者是来自通道2的。通过数据选择寄存器CC1S来对TI1FP1和TI2FP1进行选择;而ICPS则是对分频器进行选择分频;CCE1寄存器则是使能或失能输出,如果使能那么输入的信号就可以转换为IC1PS,让CNT寄存器的值转运到CCR寄存器(捕获/比较寄存器)中。
另外每捕获一次CNT的值就要将CNT清0以便下次捕获,这里硬件电路就可以实现在捕获后自动实行清0工作。
- 意思就是比如测周法,获取一个输入信号周期的计数,当CNT达到CCR的值时就清0
- 对于TI1FP1和TI1F_ED信号都可以通向从模式控制器,比如TI1FP1通向捕获通道的同时还可以通向从模型中的电路,这些内部电路可以自动完成CNT的清0。

主模式可以将定时器内部的信号映射到TRGO引脚,用于触发别的外设。
从模式则用于接收其它外设或者自身外设的一些信号用于控制自身定时器的运行,也就是被别的信号控制。
触发源选择用于选择一个指定的信号,得到TRGI,去触发从模式,从模式可以在自身的列表中选择一个操作去执行。
比如刚才提到了CNT清0,就可以通过输入TI1D_ED,对于到从模式的Reset信号,去触发CNT的自动清0
具体的信号自行去看参考手册中TIMx寄存器的介绍。
2.4 基本结构
输入捕获基本结构:


PWMI基本结构:

使用了两个通道去同时捕获一个输入引脚。
TF1FP1信号去设置上升沿触发,去触发捕获周期和清0CNT;TF2FP1信号设置为下升沿触发,去触发通道2的捕获单元。
这样可以实现CCR1去获取一整个电平周期的计数值,而CCR2则可以去获取一个周期内高电平信号的计数值,这样不就可以计算出该输入信号的占空比。
2.5 相关api和结构体
2.5.1 结构体
c
typedef struct
{
uint16_t TIM_Channel; /*!< Specifies the TIM channel.
This parameter can be a value of @ref TIM_Channel */
uint16_t TIM_ICPolarity; /*!< Specifies the active edge of the input signal.
This parameter can be a value of @ref TIM_Input_Capture_Polarity */
uint16_t TIM_ICSelection; /*!< Specifies the input.
This parameter can be a value of @ref TIM_Input_Capture_Selection */
uint16_t TIM_ICPrescaler; /*!< Specifies the Input Capture Prescaler.
This parameter can be a value of @ref TIM_Input_Capture_Prescaler */
uint16_t TIM_ICFilter; /*!< Specifies the input capture filter.
This parameter can be a number between 0x0 and 0xF */
} TIM_ICInitTypeDef;
TIM_ICInitTypeDef
结构体用于配置 STM32 的定时器输入捕获功能中的各个参数。
\1. TIM_Channel
-
作用:指定输入捕获通道。
-
可能取值:
-
TIM_Channel_1
:定时器通道 1。TIM_Channel_2
:定时器通道 2。TIM_Channel_3
:定时器通道 3。TIM_Channel_4
:定时器通道 4。
-
含义:选择定时器的特定通道用于输入捕获功能。STM32 的每个通道可以被配置为输入捕获模式,以捕捉外部信号的边沿事件。
\2. TIM_ICPolarity
-
作用:指定输入信号的活动边沿(即上升沿或下降沿)。
-
可能取值:
-
TIM_ICPolarity_Rising
:捕获上升沿。TIM_ICPolarity_Falling
:捕获下降沿。TIM_ICPolarity_BothEdge
:捕获上升沿和下降沿(如果硬件支持)。
-
含义:定义捕获事件触发的条件,即在信号的哪个边沿发生时捕获定时器的计数值。这有助于检测输入信号的周期或占空比。
\3. TIM_ICSelection
-
作用:指定输入源。
-
可能取值:
-
TIM_ICSelection_DirectTI
:直接连接到定时器输入。TIM_ICSelection_IndirectTI
:连接到另一个通道的反相输入。TIM_ICSelection_TRC
:选择触发输入(由TIMx_TRC
信号生成)。
-
含义:设置输入捕获单元的信号源,可以将输入通道连接为直接输入、间接输入或选择触发输入。这对于某些应用场景(如 PWM 输入测量)是有用的。
\4. TIM_ICPrescaler
-
作用:指定输入捕获的预分频器(即对输入信号的频率进行分频)。
-
可能取值:
-
TIM_ICPSC_DIV1
:无分频,捕获每一个事件。TIM_ICPSC_DIV2
:2 倍分频,捕获每 2 个事件。TIM_ICPSC_DIV4
:4 倍分频,捕获每 4 个事件。TIM_ICPSC_DIV8
:8 倍分频,捕获每 8 个事件。
-
含义:对输入捕获事件进行分频,可用于减少输入信号频率较高时的处理次数,从而降低系统负荷。
\5. TIM_ICFilter
-
作用:指定输入捕获的数字滤波器,用于滤除噪声。
-
可能取值 :可以是
0x0
到0xF
(即 0 到 15 之间的数值)。 -
- 数值越高,滤波器的时间常数越长,抗干扰能力越强,但响应时间越慢。
-
含义:设置输入信号的采样滤波窗口,通过调整滤波器值可以忽略短暂的干扰信号,适用于捕捉带有噪声的信号。
2.5.2 api
以下几个函数主要用于输入捕获单元(Input Capture)的操作:
TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct)
-
- 作用:配置定时器的输入捕获功能。
- 参数:
-
-
TIMx
:选择目标定时器。TIM_ICInitStruct
:指向TIM_ICInitTypeDef
结构体的指针,用于配置输入捕获参数。
-
-
- 用法 :在使用输入捕获功能时,初始化结构体变量
TIM_ICInitTypeDef
,设置输入捕获通道、输入极性、预分频、滤波等参数,然后调用该函数将配置应用到目标定时器的指定输入捕获通道。
- 用法 :在使用输入捕获功能时,初始化结构体变量
TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct)
-
- 作用 :初始化
TIM_ICInitTypeDef
结构体的默认值。 - 参数:
- 作用 :初始化
-
-
TIM_ICInitStruct
:指向TIM_ICInitTypeDef
结构体的指针。
-
-
- 用法 :在对
TIM_ICInitTypeDef
结构体进行自定义配置前,可以使用该函数将结构体初始化为默认值,以避免未初始化的字段出现不确定值。
- 用法 :在对
TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct)
-
- 作用:配置 PWM 输入模式,用于测量输入信号的频率和占空比。
- 参数:
-
-
TIMx
:选择目标定时器。TIM_ICInitStruct
:指向TIM_ICInitTypeDef
结构体的指针,用于配置 PWM 输入模式参数。
-
-
- 用法:该函数用于配置 PWM 输入捕获,以便测量输入信号的周期和高电平时长。可以用于捕获 PWM 信号的频率和占空比,特别适用于需要分析 PWM 波形的应用。
TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC)
、TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC)
、TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC)
、TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC)
-
- 作用:设置输入捕获通道的分频器。
- 参数:
-
-
TIMx
:目标定时器。TIM_ICPSC
:指定输入捕获通道的分频比率。
-
-
- 用法:可以对输入信号的频率进行分频,以减缓输入频率较高时的计数速度,从而优化捕获效果。
TIM_GetCapture1(TIM_TypeDef* TIMx)
、TIM_GetCapture2(TIM_TypeDef* TIMx)
、TIM_GetCapture3(TIM_TypeDef* TIMx)
、TIM_GetCapture4(TIM_TypeDef* TIMx)
-
- 作用:获取捕获寄存器的值,即从指定输入捕获通道读取捕获的时间计数。
- 参数:
-
-
TIMx
:选择目标定时器。
-
-
- 用法:这些函数在捕获到输入信号的上升沿或下降沿后,读取捕获寄存器的值,用于计算时间差来确定信号的周期或脉宽。
TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource)
TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource)
TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode)
2.6 实验
2.6.1 输入捕获模式测频率

User:
Hardware:
2.6.2 PWMI模式测频率占空比

User:
Hardware:
注意:
- 需要注意,对于stm32的内部计数器CNT,最多只能计数到65535次。所以在测量输入的PWMI波形的一个周期电平的频率的时候,不能超过CK_PSC/(PSC+1)/65535。
- 以这个例子,分频后的时钟频率是1MHz,那么1MH/65535,也就是大概1Hz,也就是PWMI的输入的周期电平不能超过1Hz,即时间不能超过1s
3.TIM编码器接口
采用的是仍然是旋转编码器计次的实验,只不过这个代码是通过定时器的编码器接口,来自动计次。之前的实验是通过外部中断来触发中断,在中断函数中进行手动计次的,这样的坏处是会浪费软件资源。
通过编码器接口可以完成一些需要频繁计次而完成的只是简单的任务的情况下去使用。比如使用电机高速旋转需要知道其旋转圈数时,编码器不得产生非常多的脉冲,程序就得频繁中断,而中断只是简单的进行加1,造成严重的软件资源浪费,通过编码器接口就可以解决这种情况。
3.1 简介
Encoder Interface 编码器接口
编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度
每个高级定时器和通用定时器都拥有1个编码器接口
两个输入引脚借用了输入捕获的通道1和通道2
3.2 正交编码器


为什么不是使用单独定义一个引脚来表示正向和反向,而是使用这种提前或滞后的正交信号来表示方向???
- 正交信号的精度更高,因为A、B相都可以计次,就相当于计次频率提高了一倍。
- 正交信号可以抗噪声,因为两个信号的是交替跳变的,可以设计一个抗噪电路,如果一个信号不变,而另一个信号在连续跳变,也就是产生了噪声,CNT就可以选择不计次。
3.3 基本结构


3.4 工作模式

3.5 实例
均不反相:

TI1反相:

3.5 实验

User:
Hardware: