目录
PWM(脉冲宽度调制)是定时器输出比较功能的一个特例。一般用于驱动电机,无源蜂鸣器,呼吸灯等。下面将从它的原理,应用,代码实现,以及思路等方面全面细致地进行讲解。代码基于STM32标准库。
1.什么是PWM?
PWM是一种用于控制功率、信号或信息传输的技术。它通过改变一系列固定频率的数字信号(即方波信号)中高电平持续时间的比例来实现模拟信号效果。PWM技术广泛应用于电机控制、LED亮度调节、音频信号处理等领域。
关键参数:
- 周期(Period):一个完整的PWM波形从开始到重复所需的时间。
- 频率(Frequency):单位时间内发生的完整周期数,通常以赫兹(Hz)为单位表示。对于大多数应用,频率保持恒定。
频率 = 1 / 周期 周期 = 1 / 频率
- 占空比(Duty Cycle):在一个周期内,高电平所占的时间比例。占空比用百分比表示,范围从0%(始终为低电平)到100%(始终为高电平)。例如,50%的占空比意味着高电平和低电平各占一半时间。
2.PWM原理
PWM的基本思想是利用二进制逻辑电平(通常是0V和电源电压,例如5V或3.3V),通过调整高电平(ON状态)与低电平(OFF状态)的时间比例,从而在负载上产生平均电压的效果。这种方式可以高效地控制输出功率,同时保持较高的效率,因为开关元件(如MOSFET晶体管)要么完全导通(接近0电阻),要么完全截止(接近无限大电阻),在这两种状态下功耗都很小。
3.PWM的应用
-
电机速度控制
通过改变PWM信号的占空比,可以有效地控制直流电机的速度或步进电机的步距角。当占空比增加时,电机接收到更多的能量,转速加快;反之则减速。
-
LED呼吸灯
LED本质上是一个数字设备,只能全亮或全灭。但是通过快速切换LED的开闭,并调整“开”的时间相对于“关”的时间的比例(即占空比),可以让眼睛感觉到亮度的变化。这种现象基于人眼的视觉暂留效应。
-
功率放大器和音频信号处理
PWM也常用于D类放大器中,它可以将模拟音频信号转换成高频PWM信号,然后通过低通滤波器还原成模拟信号。这种方法能够提供高效的音频放大,减少热量损失。
-
电源管理
PWM技术还被应用于开关模式电源(SMPS),如AC-DC适配器、电池充电器等。通过精确控制开关器件的工作周期,可以实现高效的电压转换和稳压功能。
4.代码实现
4.1 引脚选择
要使用PWM功能,要先选择对应的时钟及输出通道。在网上搜索C8T6的引脚功能图,选择一个即可,这里选择了通用定时器2的通道2,即TIM2_CH2,它对应的输出引脚是PA1。
4.2 PWM配置方法
接着就是对相关引脚进行配置了。如果你在标准库中寻找stm32f10x_pwm.c的库,会发现根本找不到,因为PWM是属于定时器输出比较的一个特例。因此PWM 的相关函数也是和输出比较的函数放在一起的。 我们打开stm32f10x_tim.c,stm32f10x_tim.h两文件。
在stm32f10x_tim.h大概50和80行的地方分别找到TIM_TimeBaseInitTypeDef,TIM_OCInitTypeDef结构体,分别对定时器和PWM进行配置。其中定时器部分已经在“定时器定时篇”中讲解过。这里只讲解PWM部分。
TIM_OCInitTypeDef有8个成员,每个后面有注释,这里以TIM_OCMode为例,注释中说了相关参数在TIM_Output_Compare_and_PWM_modes中,我们选中它,Ctrl+F,点击Find Next。
跳转到图中的地方,会发现有6个参数,上面4个是输出比较功能的,下面2个是PWM的两个参数。如果你需要PWM波形在每个周期的起始阶段为高电平,那么PWM模式1可能是更好的选择;反之,如果需要波形在周期结束阶段为低电平,则可以选择PWM模式2。对于一般性的应用,在开始、结束阶段输出高或低都可以,随便选择一个模式就行。
特征 | TIM_OCMode_PWM1 | TIM_OCMode_PWM2 |
---|---|---|
触发条件 | 计数器 < CCR时输出高电平 | 计数器 >= CCR时输出高电平 |
波形起点 | 每个周期开始时输出高电平 | 每个周期开始时输出低电平 |
波形相位 | 高电平持续时间从周期开始到CCR设定值 | 高电平持续时间从CCR设定值到周期结束 |
应用场景 | 适合需要PWM波形在周期起始阶段为高电平 | 适合需要PWM波形在周期结束阶段为低电平 |
如果要查看和使用其他结构体成员的参数,用同样的方法即可。
下面对8个成员进行详细说明:
- TIM_OCMode:配置定时器输出比较(OC,Output Compare)通道工作模式。前4个是输出比较功能的,后2个是PWM的。
- TIM_OutputState:主输出状态设置。如果你的应用只需要控制一个方向的PWM输出或简单的输出比较功能,比如单向电机控制、LED亮度调节等,那么你只需使用
TIM_OutputState
来管理普通输出引脚的状态。参数TIM_OutputState_Enable为开启,TIM_OutputState_Disable为关闭。- TIM_OutputNState:TIM_OutputState的互补通道,互补输出状态设置。当你需要实现更复杂的控制逻辑,如双向电机控制、H桥驱动电路中的互补PWM输出,或者在某些应用中为了安全原因必须确保两个引脚之间有死区时间时,就需要同时使用
TIM_OutputState
和TIM_OutputNState
来分别管理主输出引脚和其互补引脚的状态。参数TIM_OutputNState_Enable为开启,TIM_OutputNState_Disable为关闭。C8T6中仅用于TIM1 和TIM8。
TIM_Pulse:定时器计数器的目标值。直接决定了高电平持续的时间长度,即占空比。通过调整
TIM_Pulse
的值,可以改变PWM波形的形状和平均输出电压,从而实现对电机速度、LED亮度等参数的控制。PWM模式1:占空比 =
TIM_Pulse
/ (ARR
+ 1)PWM模式2:占空比 = (ARR -
TIM_Pulse
) / (ARR
+ 1)ARR即定时器的TIM_Period对应的值。如果你想生成占空比50%的PWM信号,且
ARR
为999(TIM_Period=999),则在PWM模式1下应将TIM_Pulse
设置为500。50% = 500/(999+1)。在PWM模式2下,设置TIM_Pulse
为499。50% =(999-499)/(999+1)。
TIM_OCPolarity:配置定时器输出比较(OC,Output Compare)通道极性,为主输出。
参数
TIM_OCPolarity_High为输出高电平,
TIM_OCPolarity_Low为输出低电平。
默认为高。TIM_OCNPolarity:配置定时器互补输出比较(Complementary Output Compare,OCN)通道极性,为互补输出。参数TIM_OCNPolarity_High
为输出高电平,
TIM_OCNPolarity_Low为输出低电平。
默认为高。C8T6中仅用于TIM1 和TIM8。这两个成员的参数会影响PWM模式1和2的电平输出。选择HIGH时,和模式1、2的原输出一致,当选择LOW时,和模式1、2的原输出相反。具体如下:
高电平有效 (TIM_OCPolarity_High或TIM_OCNPolarity_High):
在PWM模式1下,当计数器小于捕获/比较寄存器(CCR)中的值时,主输出引脚为高电平,互补输出引脚为低电平;反之亦然。
在PWM模式2下,当计数器大于等于CCR中的值时,主输出引脚为高电平,互补输出引脚为低电平;反之亦然。
低电平有效 (TIM_OCPolarity_Low或TIM_OCNPolarity_Low):
在PWM模式1下,当计数器小于CCR中的值时,主输出引脚为低电平,互补输出引脚为高电平;反之亦然。
在PWM模式2下,当计数器大于等于CCR中的值时,主输出引脚为低电平,互补输出引脚为高电平;反之亦然。
TIM_OCIdleState:配置定时器输出比较通道在空闲状态(即非激活状态或停止模式)下的引脚电平,为主输出。参数TIM_OCIdleState_Set空闲状态下输出引脚将被强制拉高至高电平 TIM_OCIdleState_Reset 空闲状态下输出引脚将被强制拉低至低电平 C8T6中仅用于TIM1 和TIM8。
TIM_OCNIdleState:配置定时器互补输出比较通道(Complementary Output Compare,OCN)在空闲状态(即非激活状态或停止模式)下的引脚电平,为互补输出。参数TIM_OCNIdleState_Set 互补输出引脚将被强制拉高至高电平 。TIM_OCNIdleState_Reset 互补输出引脚将被强制拉低至低电平。C8T6中仅用于TIM1 和TIM8。
以上两个成员主要用于低功耗模式 ,或安全考虑 ,或外部电路需求。
熟悉了8个成员的意义和用途,就可以根据自己需求进行配置。
4.3 代码实现
pwm.c
void PWM_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM2_InitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//5ms
TIM2_InitStructure.TIM_Period = 4999;
TIM2_InitStructure.TIM_Prescaler = 71;
TIM2_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM2_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM2_InitStructure);
//PWM输出引脚配置 TIM2_CH2 PA1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //不是常规引脚功能,因此配置为复用推挽
GPIO_InitStructure.GPIO_Pin = PWM_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(PWM_PORT, &GPIO_InitStructure);
//PWM配置 TIM2_CH2 PA1 占空比:2500/(4999+1) = 50%
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PWM模式1
TIM_OCInitStructure.TIM_Pulse = 2500; //初始值2500个高电平
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //主通道使能
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable; //TIM2无此功能 随便写或不写
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //开始时输出高
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low; //TIM2无此功能 随便写或不写
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;//TIM2无此功能 随便写或不写
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set;//TIM2无此功能 随便写或不写
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
//中断分组配置
NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig(TIM2,TIM_IT_CC2,ENABLE);
TIM_Cmd(TIM2, ENABLE);
}
//TIM2中断服务程序
void TIM2_IRQHandler(void)
{
u8 i=0;
if(TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET) //TIM2捕获比较中断
{
i++;
if(i==255) //产生一次PWM后做的事情 根据自己需求修改
{
i=0;
}
TIM_ClearITPendingBit(TIM2, TIM_IT_CC2); //清除中断标志位
}
}
main.c
main()
{
SystemTinerInit(7200-1, 1000-1);
Delay_Init();
USART1_Init(115200);
LED_Init();
PWM_Init();
while(1)
{
TIM_SetCompare2(TIM2, 300); //输出占空为300/5000=6%的方波。
}
}
通过修改TIM_SetCompare2(TIM2, 300)的第二个参数即可修改占空比。
以上只是对PWM的基础使用,讲的比较多的是其配置方法。掌握了基础方法才能随自己心意进行配置,而不是只会CtrlC别人代码,需求改一下,自己就不会了。由于篇幅问题,PWM的具体应用会在下篇推出。
制作不易,如果对你有帮助可否点个赞再走~