STM32定时器PWM(脉冲宽度调制)输出原理,在使用固件库时,主要涉及定时器的配置以及PWM信号的生成。以下是对该原理的详细解释:
一、PWM基本概念
PWM(Pulse Width Modulation)是一种通过改变脉冲宽度来控制模拟信号平均值的技术。在STM32中,PWM信号由定时器生成,其频率和占空比分别由定时器的自动重装载寄存器(ARR)和比较寄存器(CCR)决定。
所谓脉宽调制信号(PWM波),就是一个TIMx_ARR自动重装载寄存器确定频率(由它决定PWM周期),TIM_CCRx寄存器确定占空比信号。
以TIM3为例,STM32的通用定时器氛围TIM2,TIM3,TIM4,TIM5,每个定时器都有独立的四个通道可以用来作为: 输入捕获,输出比较,PWM输出,单脉冲模式输出等。
STM32的定时器除了TIM6和TIM7(基本定时器)之外,其他的定时器都可以产生PWM波输出,高级定时器TIM1,TIM8可以同时产生7路PWM输出,而通用定时器可以同时产生4路PWM输出,这样STM32可以最多同时输出30路PWM输出!
以向上计数为例,讲述PWM原理:
①在PWM输出模式下除了CNT(计数器当前值),ARR(自动重装载值),CCRx(捕获/比较寄存器值)。
②当CNT小于CCRx时,TIMx_CHx通道输出低电平
③当CNT等于或大于CCRx时,TIMx_CHx通道输出高电平
CCR1:设置捕获比较寄存器,设置比较值。
CCMR1寄存区:设置PWM模式1 或者PWM模式2。
CCER: P位:输出/捕获 :设置极性: 0 高电平有效,1 低电平有效
E位:输出/捕获 : 使能端口
二、PWM的模式
模式一:边沿对齐模式
向上计数时: 当TIMx_CNT<TIMx_CCRx时通道1为有效电平,否则为无效电平;
向下计数时: 一旦TIMx_CNT>TIMx_CCRx,CCR1通道1为无效电平,否则为有效电平。
模式二:中央对齐模式
向上计数时: 当TIMx_CNT<TIMx_CCRx时通道1为无效电平,否则为无效电平;
向下计数时: 一旦TIMx_CNT>TIMx_CCRx,CCR1通道1为有效电平,否则为无效电平。
自动加载的预加载寄存器
简单的说:
APER =1 ,ARR立即生效
APER =0,ARR下个周期生效
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
三、PWM输出原理
- 定时器配置:
- 时钟使能:首先,需要使能定时器和相关GPIO端口的时钟。这通常通过RCC(Reset and Clock Control)模块实现。
- GPIO配置:将用于PWM输出的GPIO端口配置为复用推挽输出模式(GPIO_Mode_AF_PP),并指定其连接到哪个定时器通道。
- 定时器初始化:配置定时器的预分频器(Prescaler)、自动重装载值(ARR)以及计数模式(如向上计数、向下计数或中心对齐模式)。这些参数决定了PWM信号的频率。
- PWM通道配置:
- 输出比较模式:设置定时器的输出比较模式为PWM模式(TIM_OCMode_PWM1或TIM_OCMode_PWM2)。这两种模式的主要区别在于输出电平的极性。
- 输出极性:配置PWM信号的输出极性(高电平有效或低电平有效)。
- 占空比调节:通过修改比较寄存器(CCR)的值来调节PWM信号的占空比。占空比是指PWM信号中高电平所占的比例。
- PWM信号生成:
- 当定时器开始计数时,计数器的值与比较寄存器的值进行比较。根据输出比较模式和输出极性的设置,当计数器的值小于(或大于)比较寄存器的值时,PWM输出为高电平(或低电平);否则为低电平(或高电平)。
- 当计数器的值达到自动重装载值时,定时器会重新从0开始计数,并重复上述过程,从而生成连续的PWM信号。
四、固件库配置步骤(以STM32F1系列为例)
- 使能定时器和相关IO口时钟:
- 使用
RCC_APB1PeriphClockCmd
和RCC_APB2PeriphClockCmd
函数使能定时器和GPIO端口的时钟。
- 使用
- 初始化GPIO口:
- 使用
GPIO_Init
函数配置GPIO端口为复用推挽输出模式,并指定其连接到哪个定时器通道。
- 使用
- 定时器初始化:
- 使用
TIM_TimeBaseInit
函数配置定时器的预分频器、自动重装载值和计数模式。
- 使用
- 初始化输出比较参数:
- 使用
TIM_OCxInit
函数(x为通道号)配置PWM输出比较模式、输出极性和占空比(通过比较寄存器的值间接设置)。
- 使用
- 使能预装载寄存器和定时器:
- 使用
TIM_OCxPreloadConfig
函数使能预装载寄存器,并使用TIM_Cmd
函数使能定时器。
- 使用
五、注意事项
- 在配置PWM输出时,需要注意定时器的复用功能重映像问题,特别是当需要改变PWM信号的输出引脚时。
- PWM信号的频率和占空比可以通过修改定时器的自动重装载值和比较寄存器的值来调节。
- 在实际应用中,还需要考虑PWM信号的稳定性、噪声抑制以及输出电流的能力等因素。
相关结构体
这个结构体TIM_OCInitTypeDef
是用于STM32微控制器中定时器输出比较(Output Compare, OC)功能配置的。它封装了配置PWM(脉冲宽度调制)或输出比较模式所需的所有关键参数。
typedef struct
{
uint16_t TIM_OCMode; // 配置PWM模式1、模式2或其他输出比较模式
// 此成员用于选择定时器输出比较的模式。例如,可以是PWM模式1(TIM_OCMode_PWM1)、PWM模式2(TIM_OCMode_PWM2)
// 或其他如输出比较活动(TIM_OCMode_Active)、输出比较空闲(TIM_OCMode_Inactive)等模式。
uint16_t TIM_OutputState; // 配置输出使能/失能
// 控制定时器输出通道的状态,可以是使能(TIM_OutputState_Enable)或失能(TIM_OutputState_Disable)。
// 当设置为使能时,输出比较通道才会根据配置产生输出信号。
uint16_t TIM_OutputNState;
// 对于高级定时器(如TIM1和TIM8),这个成员控制互补输出通道N的状态。
// 它也有使能和失能两种状态,用于控制互补输出通道N的输出。
// 对于基本定时器,这个成员可能不会被使用。
uint16_t TIM_Pulse; // 配置比较值,CCRx
// 这个值被加载到捕获/比较寄存器(CCRx)中,用于与定时器计数器进行比较。
// 在PWM模式下,它决定了PWM信号的占空比(即高电平持续的时间与周期的比例)。
uint16_t TIM_OCPolarity; // 比较输出极性
// 配置输出比较通道的比较输出极性。
// 可以是高电平有效(TIM_OCPolarity_High)或低电平有效(TIM_OCPolarity_Low)。
// 这决定了当输出比较匹配发生时,输出信号是高电平还是低电平。
uint16_t TIM_OCNPolarity;
// 对于高级定时器,这个成员控制互补输出通道N的比较输出极性。
// 它同样有高电平有效和低电平有效两种配置。
// 对于基本定时器,这个成员可能不会被使用。
uint16_t TIM_OCIdleState;
// 配置定时器输出通道在空闲(Idle)状态下的电平。
// 可以是高电平(TIM_OCIdleState_Set)或低电平(TIM_OCIdleState_Reset)。
// 这决定了当定时器处于空闲模式时,输出通道的电平状态。
uint16_t TIM_OCNIdleState;
// 对于高级定时器,这个成员控制互补输出通道N在空闲状态下的电平。
// 它同样有高电平和低电平两种配置。
// 对于基本定时器,这个成员可能不会被使用。
} TIM_OCInitTypeDef;
相关函数
1. TIM_OCxInit
函数
void TIM_OCxInit(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct); |
功能描述:
该函数用于初始化定时器的输出比较通道x(x通常为1, 2, 3, 或4,取决于定时器型号和通道数量)。它使用TIM_OCInitTypeDef
结构体中的参数来配置输出比较模式、输出状态、比较值、输出极性等。
参数说明:
TIM_TypeDef* TIMx
:指向定时器寄存器的指针,表示要配置的定时器。TIM_OCInitTypeDef* TIM_OCInitStruct
:指向包含输出比较配置信息的结构体的指针。
注释:
这个函数是配置输出比较通道的核心,它允许用户以结构体的形式一次性设置多个配置参数,而不是单独调用多个函数。
2. TIM_SetCompare1
函数
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1); |
功能描述:
该函数用于设置定时器输出比较通道1的比较值(CCRx,其中x=1)。这个值决定了输出比较匹配的条件。
参数说明:
TIM_TypeDef* TIMx
:指向定时器寄存器的指针。uint16_t Compare1
:要设置的新比较值。
注释:
这个函数在需要动态改变PWM占空比或输出比较匹配条件时非常有用。
3. TIM_OC1PreloadConfig
函数
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload); |
功能描述:
该函数用于使能或失能定时器输出比较通道1的预装载功能。预装载允许在更新事件(如定时器溢出)发生时,将预装载寄存器中的值加载到实际的比较寄存器中。
参数说明:
TIM_TypeDef* TIMx
:指向定时器寄存器的指针。uint16_t TIM_OCPreload
:预装载配置。TIM_OCPreload_Enable
使能预装载,TIM_OCPreload_Disable
失能预装载。
注释:
预装载功能对于在更新事件发生时平滑地改变PWM占空比或输出比较值非常有用。
4. TIM_Cmd
函数
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState); |
功能描述:
该函数用于开启或关闭指定的定时器。
参数说明:
TIM_TypeDef* TIMx
:指向定时器寄存器的指针。FunctionalState NewState
:新的定时器状态。ENABLE
开启定时器,DISABLE
关闭定时器。
注释:
这个函数是控制定时器是否运行的基本函数。
5. TIM_ARRPreloadConfig
函数
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState); |
功能描述:
该函数用于使能或失能定时器的自动重装载预装载寄存器。这允许在更新事件(如定时器溢出)发生时,将预装载寄存器中的值加载到自动重装载寄存器中。
参数说明:
TIM_TypeDef* TIMx
:指向定时器寄存器的指针。FunctionalState NewState
:新的预装载配置状态。ENABLE
使能预装载,DISABLE
失能预装载。
注释:
自动重装载预装载功能对于在更新事件发生时平滑地改变定时器周期非常有用。
6. TIM_OC1PolarityConfig
函数
void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity); |
功能描述:
该函数用于配置定时器输出比较通道1的比较输出极性。
参数说明:
TIM_TypeDef* TIMx
:指向定时器寄存器的指针。uint16_t TIM_OCPolarity
:输出比较极性配置。TIM_OCPolarity_High
表示高电平有效,TIM_OCPolarity_Low
表示低电平有效。
注释:
这个函数允许用户根据应用需求配置输出比较信号的极性。
使用PWM驱动SG90电机:
调用函数:
#include "pwm.h"
// PWM 初始化函数
void PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure; // 定义 GPIO 结构体,用于配置 GPIO 引脚
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; // 定义 TIMx 定时器结构体,用于配置定时器基础参数
TIM_OCInitTypeDef TIM_OCInitStructure; // 定义定时器脉宽调制结构体,用于配置脉宽调制参数
// 使能 TIM3 时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
// 使能 GPIOB 和 AFIO(复用功能时钟)时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE);
// TIM3 部分重映射,将 TIM3_CH2 映射到 PB5
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);
// 配置 GPIOB 的 PB5 引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; // 使用 PB5(TIM_CH2)引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; // 配置输出速度为 10MHz
GPIO_Init(GPIOB,&GPIO_InitStructure); // 初始化 GPIOB
// 配置 TIM3 定时器基础参数
TIM_TimeBaseStructure.TIM_Period = arr; // 设置自动重装载寄存器周期值,arr = value - 1
TIM_TimeBaseStructure.TIM_Prescaler = psc; // 设置预分频值,psc = value - 1
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // 设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // TIM 向上计数模式
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure); // 初始化 TIM3 定时器时间基数
// 初始化 TIM3 Channel2 PWM 模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 选择定时器模式为脉宽调制模式 1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 使能比较输出
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 输出极性为高电平有效
TIM_OC2Init(TIM3,&TIM_OCInitStructure); // 根据给定参数初始化外设 TIM3 OC2
// 使能 TIM3 在 CCR2 上的预装载寄存器
TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);
// 使能 TIM3
TIM_Cmd(TIM3, ENABLE);
}
主函数:
int main(){
//初始化PWM,设置自动重装载值为199,预分频器值为7199
PWM_Init(199,7199);
delay(1000);
while(1)
{
delay(1000);
//设置TIM3通道2的比较值,即占空比为5%
TIM_SetCompare2(TIM3,5);
delay(1000);
//设置TIM3通道2的比较值,即占空比为10%
TIM_SetCompare2(TIM3,10);
delay(1000);
//设置TIM3通道2的比较值,即占空比为15%
TIM_SetCompare2(TIM3,15);
delay(1000);
//设置TIM3通道2的比较值,即占空比为20%
TIM_SetCompare2(TIM3,20);
delay(1000);
//设置TIM3通道2的比较值,即占空比为25%
TIM_SetCompare2(TIM3,25);
}
}
-
比较值与占空比
- 比较值的作用:在PWM模式中,定时器的比较值(CCR)定义了输出信号在何时从高电平切换到低电平。当定时器的计数器值达到CCR时,输出信号变为低电平。
- 占空比的计算:占空比可以通过公式占空比=(CCR/ARR)×100%占空比=(CCR/ARR)×100%来计算。因此,主函数中的比较值直接决定了PWM信号的占空比。
-
定时器与PWM关系
- 定时器计数器:在STM32等微控制器中,定时器内部有一个计数器,该计数器根据时钟源和预分频器的设置进行计数。
- 自动重装载值:自动重装载值(ARR)定义了定时器计数器的上限值,当计数器达到这个值时会重置为0,从而形成一个周期。
-
PWM信号生成
- 配置定时器:通过配置定时器的ARR和CCR值,可以设置PWM信号的频率和占空比。
- 信号输出:在STM32等微控制器中,定时器的通道可以配置为PWM输出模式,通过调整CCR值来改变占空比。
相关概念的解释:
一、计数器相关
CNT
(Counter Value,计数器当前值):- 就像一个不断跳动的秒表数字,它随着定时器时钟的脉冲不断增加或减少。这个值会从 0 开始,根据定时器的计数模式(向上计数、向下计数或向上 / 向下计数)不断变化,直到达到某个设定的值(比如自动重装载值
ARR
)后,又重新开始计数。例如,在向上计数模式下,CNT
从 0 开始逐步增加,当达到ARR
时,又回到 0 重新开始计数。
- 就像一个不断跳动的秒表数字,它随着定时器时钟的脉冲不断增加或减少。这个值会从 0 开始,根据定时器的计数模式(向上计数、向下计数或向上 / 向下计数)不断变化,直到达到某个设定的值(比如自动重装载值
二、自动重装载相关
ARR
(Auto-Reload Register,自动重装载值):- 可以想象成一个目标数字。定时器的计数器
CNT
不断变化,当CNT
达到这个目标数字时,就会触发一些特定的事件,比如产生更新中断或者让计数器重新从 0 开始计数。例如,设定ARR
为 100,那么计数器CNT
从 0 开始计数,一直到 100 后,就会根据设定的规则进行相应的操作。
- 可以想象成一个目标数字。定时器的计数器
三、捕获 / 比较相关
CCRx
(Capture/Compare Register x,捕获 / 比较寄存器值):- 这是一个用来和计数器
CNT
的值进行比较的参考值。在 PWM 输出中,根据CNT
与CCRx
的大小关系来决定输出电平的高低。例如,在 PWM1 模式下,当CNT
小于CCRx
时,输出高电平;当CNT
大于等于CCRx
时,输出低电平。所以CCRx
的值决定了 PWM 信号高电平的持续时间。
- 这是一个用来和计数器
四、预分频相关
PSCR
(Prescaler Register,预分频寄存器值):- 它的作用是对定时器的输入时钟进行分频。可以把它理解为一个 “减速器”。比如,如果输入时钟是 100MHz,设定
PSCR
为 10,那么经过预分频后,定时器实际使用的时钟频率就变成了 100MHz/10 = 10MHz。这样可以调整定时器的计数速度,从而控制 PWM 信号的频率和占空比。
- 它的作用是对定时器的输入时钟进行分频。可以把它理解为一个 “减速器”。比如,如果输入时钟是 100MHz,设定
五、重复计数器相关
RCR
(Repeat Counter Register,重复计数器值):- 在一些高级定时器中,这个值决定了定时器在产生更新事件之前需要重复计数的次数。可以想象成一个 “计数器的计数器”。例如,设定
RCR
为 5,那么定时器的计数器CNT
需要从 0 计数到ARR
一共 5 次后,才会产生更新事件。这可以用于实现更复杂的定时控制或者在需要长时间定时的情况下减少处理器的干预。
- 在一些高级定时器中,这个值决定了定时器在产生更新事件之前需要重复计数的次数。可以想象成一个 “计数器的计数器”。例如,设定
六、PWM 模式相关
PWM1
模式和PWM2
模式:PWM1
模式:在计数器向上计数时,当CNT
小于CCRx
时输出高电平,当CNT
大于等于CCRx
时输出低电平;在计数器向下计数时,当CNT
大于CCRx
时输出低电平,当CNT
小于等于CCRx
时输出高电平。可以把它理解为一种 “先高后低” 的模式。PWM2
模式:与PWM1
模式相反。在计数器向上计数时,当CNT
小于CCRx
时输出低电平,当CNT
大于等于CCRx
时输出高电平;在计数器向下计数时,当CNT
大于CCRx
时输出高电平,当CNT
小于等于CCRx
时输出低电平。可以看作是 “先低后高” 的模式。
七、极性相关
- 高极性和低极性:
- 高极性时,PWM 输出在有效状态为高电平。可以理解为,当我们选择高极性时,我们希望的输出信号在 “起作用” 的时候是高电平状态。比如在控制一个电机时,高电平可能表示电机正转。
- 低极性时,PWM 输出在有效状态为低电平。同理,低极性意味着输出信号在 “起作用” 的时候是低电平状态。比如在某些情况下,低电平可能表示某个设备开启。
八、中断相关
-
更新中断:
- 当定时器发生更新事件时(如计数器达到自动重装载值、软件触发更新等),就会产生更新中断。可以把它想象成一个 “警报器”,当定时器完成了一个完整的计数周期或者因为其他特定情况需要通知处理器时,这个中断就会响起,让处理器知道该进行一些处理了,比如更新某些变量或者执行特定的任务。
-
捕获 / 比较中断:
- 当捕获 / 比较事件发生时(如计数器值与捕获 / 比较寄存器值相等),会产生捕获 / 比较中断。这就像是一个 “触发器”,当定时器的计数器
CNT
的值与我们设定的CCRx
相等时,这个中断就会触发,告诉处理器可以进行一些特定的操作,比如记录时间、触发其他事件等。
- 当捕获 / 比较事件发生时(如计数器值与捕获 / 比较寄存器值相等),会产生捕获 / 比较中断。这就像是一个 “触发器”,当定时器的计数器