【STM32】定时器基础整理:PSC、ARR、CCR 与 PWM 输出

【STM32】定时器基础整理:PSC、ARR、CCR 与 PWM 输出

文章目录

  • [【STM32】定时器基础整理:PSC、ARR、CCR 与 PWM 输出](#【STM32】定时器基础整理:PSC、ARR、CCR 与 PWM 输出)
    • 一、前言
    • 二、定时器整体认识
      • [2.1. PSC:预分频器](#2.1. PSC:预分频器)
      • [2.2. CNT:计数器](#2.2. CNT:计数器)
      • [2.3. ARR:自动重装载寄存器](#2.3. ARR:自动重装载寄存器)
      • [2.4. CCR:捕获/比较寄存器](#2.4. CCR:捕获/比较寄存器)
    • [三、更改 预分频系数 & 自动重装载寄存器](#三、更改 预分频系数 & 自动重装载寄存器)
      • [3.1 更改 预分频系数 PSC](#3.1 更改 预分频系数 PSC)
      • [3.2 更改 自动重装载寄存器 ARR](#3.2 更改 自动重装载寄存器 ARR)
    • [四、`cubeMX` 配置 `systick` & `timer`](#四、cubeMX 配置 systick & timer)
      • [3.1 `cubeMX` 配置`systick`](#3.1 cubeMX 配置systick)
      • [3.2 `cubeMX` 配置 `timer`](#3.2 cubeMX 配置 timer)
    • 五、定时器的输出功能
    • 六、使用PWM控制三色灯
      • [5.1 硬件电路](#5.1 硬件电路)
      • 5.2`cubeMX`配置
      • [5.3 PWM HAL 函数](#5.3 PWM HAL 函数)

本文基于 DshanMCU-103 开发板,学习韦东山 STM32 HAL 课程中定时器相关内容后整理。

一、前言

STM32 定时器是比较重要的外设之一,后面很多功能都会用到它,比如定时中断、PWM 输出、LED 调光、电机调速、舵机控制、输入捕获、编码器测速等。

刚开始学习定时器时,不建议一上来就死磕寄存器。更好的方式是先理解它的整体工作流程:

text 复制代码
定时器时钟
   ↓
PSC 预分频
   ↓
CNT 计数
   ↓
数到 ARR 后溢出/重装载
   ↓
产生更新事件 / 输出 PWM / 触发中断

本文主要整理 STM32F103 中定时器的基础知识,包括定时器的基本原理、PSC/CNT/ARR/CCR 的关系、时基中断、PWM 输出、HAL 常用函数,以及关键寄存器的简单对应关系,方便后续项目开发时快速查阅。


二、定时器整体认识

建议先看下面这张总览图:

总览图
复制代码
     |

STM32 定时器本质上可以理解为一个"自动计数器"。

2.1. PSC:预分频器

PSC 的作用是把定时器输入时钟分频,让 CNT 计数变慢。

计算公式:

text 复制代码
计数频率 = 定时器时钟频率 / (PSC + 1)

注意这里是 PSC + 1,不是直接除以 PSC。

例如定时器时钟是 72 MHz:

text 复制代码
PSC = 71

计数频率 = 72 MHz / (71 + 1)
         = 1 MHz

这表示 CNT 每 1 us 加 1。


2.2. CNT:计数器

CNT 表示当前计数值。

在向上计数模式下,CNT 会从 0 开始不断加 1:

text 复制代码
0 → 1 → 2 → 3 → ... → ARR

当 CNT 数到 ARR 后,会重新回到 0。


2.3. ARR:自动重装载寄存器

ARR 决定 CNT 数到多少重新开始。

定时周期可以这样计算:

text 复制代码
定时周期 = (ARR + 1) / 计数频率

例如:

text 复制代码
计数频率 = 1 MHz
ARR = 999

那么:

text 复制代码
定时周期 = (999 + 1) / 1 MHz
         = 1000 us
         = 1 ms
         
定时周期 = (PSC + 1) * (ARR + 1) / 定时器输入时钟频率

也就是说,每 1 ms 会产生一次更新事件。


2.4. CCR:捕获/比较寄存器

CCR 常用于 PWM 输出。

在 PWM 模式 1、向上计数时,可以简单理解为:

text 复制代码
当 CNT < CCR 时,输出高电平
当 CNT >= CCR 时,输出低电平

所以:

text 复制代码
CCR 越大,高电平持续时间越长
CCR 越小,高电平持续时间越短

注意:CCR 不是控制电压高度,而是控制比较点的位置,也就是高电平持续多久。

三、更改 预分频系数 & 自动重装载寄存器

3.1 更改 预分频系数 PSC

更改 预分频系数 PSC

3.2 更改 自动重装载寄存器 ARR

简易计数器 影子寄存器

更新 ARR 后,什么时候起作用,取决于影子寄存器(真正起作用的寄存器) ,通过设置ARPE寄存器,可调整 ARR 才写入影子寄存器的时机

ARPE=0 ARPE=1
ARR 立马写入影子寄存器; 马上起作用 CNT计数器 溢出后,自动重装载寄存器 ARR 才写入影子寄存器 溢出后起作用

四、cubeMX 配置 systick & timer

3.1 cubeMX 配置systick

systick
cubeMX 配置 systick
c 复制代码
main > HAL_Init > HAL_InitTick(TICK_INT_PRIORITY); >HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)
HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);     //1ms 产生一次中断

SysTick_Handler > HAL_IncTick(); > uwTick += uwTickFreq;  // 中断服务函数,每产生一次中断,+1

3.2 cubeMX 配置 timer

cubeMX 配置 timer

五、定时器的输出功能

4个CCR 一个CCR ==> PWM 的输出有 2 种:正常输出 & 互补输出。所谓互补输出就是引脚信号跟正常输出情况下的电平是相反的。

高级定时器原理图 PWM输出
设置 AutoReload (ARR ) 设置 CCR PSC
决定 PWM 周期 决定高电平时间(占空比) 影响计数速度
PWM

六、使用PWM控制三色灯

5.1 硬件电路

三色灯 三色灯硬件原理图

全彩 LED 由三个 LED 组成,它们的颜色分别是红、 绿、蓝。它的接口电路如下:当PA2、 PA15、 PB3 为低电平时, 全彩 LED 里的红、绿、蓝就会发光。调整这些引脚的占空比,就可以调配出不同的颜色。

5.2cubeMX配置

cubeMX配置 TIM2 的 三个通道CH1/CH2/CH3

5.3 PWM HAL 函数

c 复制代码
/* 启动PWM:
* htim: 哪个定时器?
* Channel:哪个通道,取值有TIM_CHANNEL_1到TIM_CHANNEL_4, TIM_CHANNEL_ALL*/
HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel);

/* 停止PWM:
* htim: 哪个定时器?
* Channel:哪个通道,取值有TIM_CHANNEL_1到TIM_CHANNEL_4, TIM_CHANNEL_ALL*/
HAL_StatusTypeDef HAL_TIM_PWM_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
c 复制代码
/* 配置PWM:
* htim: 哪个定时器?
* Channel:哪个通道,取值有TIM_CHANNEL_1到TIM_CHANNEL_4, TIM_CHANNEL_ALL*/
HAL_StatusTypeDef HAL_TIM_PWM_ConfigChannel(TIM_HandleTypeDef *htim, const TIM_OC_InitTypeDef *sConfig, uint32_t Channel);

/* 配置 PWM 时,配置参数为"TIM_OC_InitTypeDef" 类型,它的定义如下 */
typedef struct
{
	uint32_t OCMode; /* 模式,通常设置为 TIM_OCMODE_PWM1 */
    uint32_t Pulse; /* 有效电平的宽度,单位为时钟个数 */
    uint32_t OCPolarity; /* 输出信号有效电平是高还是低, TIM_OCPOLARITY_HIGH 或 TIM_OCPOLARITY_LOW */
    uint32_t OCNPolarity; /* 互补输出信号有效电平是高还是低, TIM_OCPOLARITY_HIGH 或 TIM_OCPOLARITY_LOW */
    uint32_t OCFastMode; /* 是否使用快速模式,通常设置为 TIM_OCFAST_DISABLE */
    uint32_t OCIdleState; /* 空闲是引脚的输出电平 */
    uint32_t OCNIdleState; /* 空闲时互补引脚的输出电平 */
} TIM_OC_InitTypeDef;`