一、硬件定时器
- 高级控制定时器 Advanced Control Timers (TIM1/TIM8)
- 通用定时器 General Purpose Timers (TIM2/TIM3/TIM4/TIM5)
- 通用定时器 General Purpose Timers (TIM15/TIM16/TIM17)
- 基本定时器 Basic Timers (TIM6/TIM7)
![]()
表 1 定时器种类
二、TIM 中 PWM 概念
PWM 的基本原理就是通过 ++ / -- 计数
2.1 PWM 概念
PWM:Pulse width modulation - 脉冲宽度调制 (定时器)
脉冲:方波信号
方波信号:一个高电平和一个低电平组成的波形
脉冲宽度调制:调制一个方波信号中高电平或者低电平的占比
方波周期:一个方波信号持续的时间
方波频率:单位时间可以生成的方波个数 周期和频率成反比
周期的单位:s ms μs ns频率的单位:Hz(bps) KHz(Kbps) MHz(Mbps) GHz(Gbps)
占空比:高电平信号在一个方波周期内的占比
![]()
图 1 不同占空比 图1(1)占空比为 50%,(2)占空比为 70%.
1)PWM 的作用
通过 PWM 调节周期内高低电平的占比,对基于频率参数控制的外设(蜂鸣器、马达、直流电机等)可以进行更加有效的控制。
![]()
图 2 PWM 信号
三、分析电路图
底板上的蜂鸣器引脚:PA15 - TIM2_CH1风扇引脚:PC6 - TIM3_CH1震动马达引脚:PC7 - TIM3_CH2
![]()
图 3 查找蜂鸣器引脚 ![]()
图 4 查找风扇引脚 按图 3 - 图 5 所示查找到三个外设的引脚。![]()
图 5 查找震动马达引脚
四、分析芯片手册
4.1 定时器介绍
1)介绍
![]()
图 6 通用定时器 TIM2 - TIM5 介绍 TIM2/TIM3/TIM4/TIM5 都是通用可编程的定时器。
通用定时器由一个可编程预分频器驱动的 16 位或 32位自动重载计数器组成。
它们可用于多种用途:测量输入信号的脉冲长度(输入捕获)或生成输出波形(输出比较和脉冲宽度调制(PWM))。
利用定时器预分频器和 RCC(复位和时钟控制)时钟控制器预分频器,脉冲长度和波形周期可在几微秒到几毫秒的范围内进行调制。
这些定时器之间完全相互独立,不共享任何资源。
2)主要特性
![]()
图 7 通用定时器 TIM2 - TIM5 主要特性
- 16 位或 32 位向上、向下、向上/向下自动重载计数器。
- 16 位可编程预分频器,可将计数器时钟频率从 1 到 65535 之间的任意系数分频(也可动态分频)。
- 最多 4 个独立通道,用于
- 输入捕获
- 输出比较
- 脉冲宽度调制(PWM)生成(边沿对齐和中心对齐模式)
- 单脉冲模式输出
3)功能描述
![]()
图 8 通用定时器 TIM2 - TIM5 功能描述 定时器基本单元。
可编程定时器的主要模块是一个带有相关自动重载寄存器的 16 位 / 32 位计数器。计数器可以向上计数、向下计数,或者既向上又向下计数。计数器时钟可由预分频器分频。
定时器基本单元包括:
- 计数寄存器 - TIMx_CNT
- 分频寄存器 - TIMx_PSC
- 自动重载寄存器 - TIMx_ARR
4)预分频系数发生变化波形图
![]()
图 9 预分频系数发生变化波形图
4.2 框
![]()
图 10 TIMx 定时器内部框图 分频器的分频范围: 16 位(分频系数的使用范围: 1 - 65535);
计数器的计数范围: 16 位(计数数值范围: 0 - 65535)32 位的(计数范围: 0 - (2^32) - 1);
计数器 / 定时器的计数方式:边沿对齐方式(递增计数方式、递减计数方式 / 向下计数方式)、中心对齐计数方式(先递增再递减计数方式)。递增计数方式:
CNT 计数器由分频后的时钟 CK_CNT 驱动工作,每来一个分频后的时钟,CNT 计数器中的值从 0 开始 + 1;
首先,需要设置自动重载寄存器中的值(ARR = 1000)和计数器的计数方式(递增计数方式);
当计数器开关开始工作时,自动重载寄存器中的值会被加载到计数器,每来 1 个时钟信号,计数器会从 0 开始 + 1;
直到计数器中的值加到和自动重载寄存器中的值相等时,此时就代表一个 PWM 方波产生了
当产生一个 PWM 方波后,会产生一个溢出事件(标志位),CNT 计数器又会回到 0,重新开始 + 1,循环操作。递减计数方式:
首先,需要设置自动重载寄存器中的值(ARR = 1000)和计数器的计数方式(递减计数方式);
当计数器开关工作时,自动重载寄存器中的值(ARR = 1000)会被加载到计数器中,每来 1 个时钟信号,计数器中的值会从 1000 开始 - 1;
直到计数器中的值递减到 0 时,此时就代表一个 PWM 方波产生了;
当产生一个 PWM 方波后,会产生一个溢出事件(标志位),CNT 计数器中的值又会回到 1000,重新开始 - 1,循环操作。先递增再递减计数方式:
首先,需要设置自动重载寄存器中的值(ARR = 1000)和计数器的计数方式(先递增再递减计数方式);
当计数器开关开始工作时,自动重载寄存器中的值会被加载到计数器,每来 1 个时钟信号,计数器会从 0 开始 + 1;
当计数器中的值加到和自动重载寄存器中的值相等时,开始递减,也就是每来 1 个时钟信号,计数器会从 1000 开始 - 1;
直到计数器中的值递减到 0 时,此时就代表一个 PWM 方波产生了;
当产生一个 PWM 方波后,会产生一个溢出事件(标志位),循环上述操作。目的:为了生成一个自定义脉宽的 PWM 波信号(可以人为改变高低电平占比的方波信号,也就是可以人为改变占空比的方波信号)
生成一个自定义脉宽的方波信号的流程:
- 准备工作:(默认以初始给出的频率值为频率甲)
1.1 由 RCC 产生并分配给 TIM 一个分频前的时钟频率 CK_PSC = 160MHz
1.2 设置分频寄存器中的分频系数 PSC = 160,分频后的时钟频率 CK_CNT = 100MHz,分频后的时钟可以驱动计数工作(递增 / 递减计数)
1.3 设置自动重载寄存器的值(ARR = 1000),设置计数器的计数方式为递增计数方式,设置捕获比较存储的值 CCR1 = 700- 自动重载寄存器中的值(ARR)会被加载到计数器(CNT)中
- 每来 1 个时钟信号,计数器中的值从 0 开始 + 1
- 当计数器中的值(CNT)加到和比较 / 捕获寄存器中的值(CCR1)相等时,此时输出的电平发生翻转
- 接着,计数器中的值继续 + 1,直到加到和自动重载寄存器中的值(ARR)相等时,此时就代表一个自定义脉宽的 PWM 方波产生了
![]()
图 11 默认初始输出电平不同时的占空比
分频前时钟 CK_PSC = 160 MHz,分频系数 PSC = 160,自动重载寄存器 ARR = 1000,计数器计数方式为递增计数,捕获/比较寄存器 CCR1 = 700;
默认输出初始电平为高电平。
则:
分频后的时钟频率 CK_CNT = CK_PSC / PSC = 160MHz / 160 = 1MHz(1s内产生1M个时钟信号) 分频后的时钟周期 = 1 / 分频后的时钟频率 = 1 / 1MHz = 1us(产生一个时钟信号需要1us)
生成的PWM方波的频率 = 分频后的时钟频率CK_PSC / 自动重载寄存器的值ARR = 1MHz / 1000 = 1000Hz = 1KHz 生成的PWM方波的周期 = 1 / 生成的PWM方波的频率 = 1 / 1000Hz = 0.001s = 1ms
生成的PWM方波的周期 = 分频后的时钟周期 * +1次数 = 分频后的时钟周期 * ARR = 1us * 1000 = 1ms 生成的PWM方波的频率 = 1 / 生成的PWM方波的周期 = 1 / 1ms = 1KHz
生成的PWM方波的占空比 = 700 / 1000 * 100% = 捕获/比较寄存器的值CCR1 / 自动重载寄存器的值ARR * 100% = 70%
- PWM方波的频率、周期 与 分频寄存器的分频系数 PSC、自动重载寄存器的值 ARR 有关;
- PWM方波的占空比 与 自动重载寄存器的值 ARR、捕获/比较寄存器的值 CCR1、输出的初始电平(有效电平)、PWM 的工作模式 有关。
4.3 边沿对齐方式 - 递增计数

4.4 边沿对齐方式 - 递减计数

4.5 中心对齐计数

4.6 TIMx_CR1 寄存器

1)Bit 6:5
位 6:5 CMS[1:0]:中心对齐模式选择
00:边沿对齐模式。计数器根据方向位(DIR)进行递增或递减计数。
01:中心对齐模式 1。计数器交替进行递增和递减计数。配置为输出的通道(TIMx_CCMRx 寄存器中 CCxS=00)的输出比较中断标志仅在计数器递减计数时置位。
10:中心对齐模式 2。计数器交替进行递增和递减计数。配置为输出的通道(TIMx_CCMRx 寄存器中 CCxS=00)的输出比较中断标志仅在计数器递增计数时置位。
11:中心对齐模式 3。计数器交替进行递增和递减计数。配置为输出的通道(TIMx_CCMRx 寄存器中 CCxS=00)的输出比较中断标志在计数器递增或递减计数时都会置位。
注意:只要计数器使能(CEN=1),就不允许从边沿对齐模式切换到中心对齐模式。
2)Bit 4
位 4 DIR:计数方向位
0:计数器用作向上计数器,递增计数
1:计数器用作向下计数器,递减计数
3)Bit 0
位 0 CEN:计数器使能
0:计数器不使能
1:计数器使能
4.7 TIM_CCMR1 寄存器

1)Bit 7
位 7 OC1CE:输出比较 1 清除使能(通道 1 的输出比较寄存器清除使能位)
0:OC1Ref 不受 ETRF 输入的影响
1:一旦在 ETRF 输入上检测到高电平,OC1Ref 就会被清除
2)Bits 16, 6:4
![]()
图 17 TIM_CCMR1 的 Bits 16, 6:4 ![]()
图 18 TIM_CCMR1 的 Bits 16, 6:4 设置为 0110(PWM 模式 1) ![]()
图 19 TIM_CCMR1 的 Bits 16, 6:4 设置为 0111(PWM 模式 2)
4.8 TIM_CCER 寄存器
![]()
图 20 TIM_CCER 位 3 CC1NP:捕获 / 比较 1 输出极性。
CC1 通道配置为输出:在这种情况下,CC1NP 必须保持清零。位 1 CC1P:捕获 / 比较 1 输出极性。
CC1 通道配置为输出:
0:OC1 高电平有效 - 通道1的活跃电平(有效电平)为高电平信号,不活跃电平(无效电平)为低电平信号
1:OC1 低电平有效 - 通道1的活跃电平(有效电平)为低电平信号,不活跃电平(无效电平)为高电平信号位 0 CC1E:捕获 / 比较 1 输出使能 - 通道 1 的捕获 / 比较功能使能
CC1 通道配置为输出:
0:关闭 - OC1 未激活
1:开启 - OC1 信号在相应的输出引脚上输出 - 使能比较输出的功能
4.9 TIM_CNT 寄存器

计数器中的值可以人为手动写入;
一般需要手动写入时,应用在对计数器清0 / 设置阀值;
一般计数器中的值从自动重载寄存器(ARR)中来。==========================================================
位 31 CNT [31]:计数器值的最高有效位(在 TIM2 和 TIM5 上)
在其他定时器上为保留位。位 30:16 CNT [30:16]:计数器值的最高有效部分(在 TIM2 和 TIM5 上)
位 15:0 CNT [15:0]:计数器值的最低有效部分
4.10 TIM_PSC 寄存器

分频后的时钟频率 = 分频前的时钟频率 / (PSC寄存器中的值 + 1)
4.11 TIM_ARR 寄存器

4.12 TIM_CCR1 寄存器

五、CubeMX 配置
以震动马达为例:
将 PC7 引脚配置为 TIM3_CH2 模式,设置 TIM3 的 时钟源和通道 2 的模式![]()
图 25 配置 PC7 引脚
![]()
图 26 TIM3 参数含义 ![]()
图 27 TIM3 参数设置 按照如图所示设定 TIM3 的参数。
之后和之前一样设置 Debug、时钟频率 160Mhz、工程设置。直接生成代码。
六、API 接口
6.1 HAL_TIM_PWM_Start 函数
HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
1)功能
HAL库提供的用于开启某个定时器下某个通道的PWM功能的函数
2)参数
htim:可以代表TIM3外设控制器的句柄
Channel:需要开启PWM功能的通道
3)返回值
成功,返回 HAL_OK(0)
失败,返回错误码
6.2 __HAL_TIM_SET_COMPARE 宏函数
#define __HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__) \(((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCR1 = (__COMPARE__)) :\((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCR2 = (__COMPARE__)) :\((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCR3 = (__COMPARE__)) :\((__CHANNEL__) == TIM_CHANNEL_4) ? ((__HANDLE__)->Instance->CCR4 = (__COMPARE__)))
1)功能
HAL库提供的用于设置捕获/比较寄存器中数值的函数(向捕获/比较寄存器中写入值)
2)参数
__HANDLE__:TIM3外设控制器的句柄对象
__CHANNEL__:需要写入数值的通道(1/2/3/4)
__COMPARE__:需要写入到捕获/比较寄存器中的值
七、代码
int main(void)
{/* Initialize all configured peripherals */MX_GPIO_Init();MX_TIM1_Init();MX_TIM3_Init();/* USER CODE BEGIN 2 */HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){for(int i=100;i<=900;i+=100){__HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_2,700);HAL_Delay(2000);}/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}