0、 前言
PWM技术在开关电源、电机控制等领域具有广泛的应用。STM32集成了多个定时器,可以配置输出PWM,掌握STM32配置PWM的方法,具有重要的应用意义。
1、 PWM基础概念
PWM(Pulse Width Modulation)是一种通过调节脉冲宽度来控制模拟信号的技术。其核心是通过改变占空比(高电平时间与周期的比值)来等效输出不同电压或功率。
-
频率(Frequency):每秒脉冲周期数,单位为Hz。高频PWM适用于电机控制(如20kHz),低频PWM适用于LED调光(如500Hz)。
-
占空比(Duty Cycle):高电平时间Ton占整个周期的比例,范围0%~100%。占空比越大,等效输出电压越高。
下图展示了利用PWM不同占空比等效不同电压,
2、配置PWM底层代码
步骤1:使能时钟源
使能时钟源为外部晶振,并配置调试接口。
步骤2:配置时钟树
系统配置最大系统时钟72MHz,各个总线时钟按照最大时钟配置。
步骤3:配置PWM输出引脚
STM32的高级定时器的框图如下所示[1],高级定时器具有4个通道TIM1_CH1~TIM1_CH4,每个通道都可以独立配置输出不同形式的PWM,具有输出互补PWM的功能,可以用于无刷直流电机控制。本文仅使用TIM1的通道1,输出单路的PWM。STM32高级定时器的框图如下所示,
选择高级定时器TIM1,时钟选择内部时钟72MHz,配置通道1输出单路PWM,
步骤4:配置PWM频率、占空比
- 配置PWM频率的方法:计数器计数模式为向上计数(波形为锯齿波),通过配置计数器的计数器峰值决定PWM的频率,计数器峰值 = 定时器时钟频率/分频系数/PWM频率
- 配置PWM占空比的方法:计数器计数值小于比较值,PWM输出高,大于比较值,PWM输出高,通过配置比较值决定PWM的占空比,改变比较值可以改变PWM的占空比,,比较值 = 计数器峰值*占空比。
芯片一般会有专门的寄存器去存储计数器峰值、比较值,更改寄存器中的值,就可以改变PWM的频率与占空比,对于STM32芯片,计数峰值存放在TIMx_ARR寄存器,,比较值存放在TIMx_CCRx寄存器。
如下图配置TIM1的计数器峰值、比较值在PA8引脚上输出频率为4KHz,占空为50%的PWM,
步骤5:生成底层配置代码
配置工程名称,选择对应的IDE,自动生成代码。STM32CubuMX使用细节可以参考: STM32入门_GPIO_STM32CubuMX配置点亮LED
3、蜂鸣器播放音乐
无源蜂鸣器是一种需要外部驱动信号才能发声的电子元件,内部不含振荡电路,必须通过方波、正弦波等周期性电信号驱动。STM32的PWM引脚输出不同频率、占空比的PWM信号可以驱动无源蜂鸣器发出不同的声音,频率决定音调,占空比决定强度。
步骤1:驱动蜂鸣器发声
生成底层配置代码后,添加开启定时器的代码,即可输出PWM信号驱动蜂鸣器发声,代码如下所示,
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1); //开启定时器
/* USER CODE END 2 */
步骤2:基础音符实现
无源蜂鸣器频率与音符的对应关系如下表所示,
让蜂鸣器输出不同音符的关键点在于更改PWM频率,编写用于更新PWM频率的函数,代码如下,
/**
* @brief 更新定时器PWM输出频率
* @param htim:定时器
* @param channel:PWM输出通道
* @param pwm_freq:频率,单位Hz
* @retval None
*/
void Tim_Update_PwmFreq(TIM_HandleTypeDef *htim, uint32_t channel, uint32_t pwm_freq)
{
uint32_t new_arr = 0;
new_arr = 72000000/7200/pwm_freq //计数器峰值= 定时器时钟频率/分频系数/PWM频率
HAL_TIM_PWM_Stop(htim, channel);
__HAL_TIM_SET_AUTORELOAD(htim, new_arr);
__HAL_TIM_SET_COMPARE(htim, channel, new_arr/2); //保持50%占空比
HAL_TIM_PWM_Start(htim, channel);
}
C调对应的频率数组如下,结合PWM频率更新函数即可演奏基础音阶。
uint16_t Note[21] ={131,147,165,175,196,221,248,
262,294,330,350,393,441,495,
525,589,661,700,786,882,990 };
步骤3:根据乐谱生成数组 歌曲的本质就是音调组合+节拍,节拍可以通过延时函数来实现,如四分音符是延时500ms。下面是小星星对应的频率、节拍数组,
// 小星星简谱对应频率(单位:Hz)
uint16_t tone[] = {
262, 262, 392, 392, 440, 440, 392, // 前4小节
349, 349, 330, 330, 294, 294, 262, // 后4小节
392, 392, 349, 349, 330, 330, 294,
392, 392, 349, 349, 330, 330, 294,
};
// 每个音符持续时间(单位:节拍)
uint16_t beat[] = {
1,1,1,1,1,1,2,
1,1,1,1,1,1,2,
1,1,1,1,1,1,2,
1,1,1,1,1,1,2,
};
//音乐播放
for(i = 0; i < 28; i++)
{
Tim_Update_PwmFreq(&htim1,TIM_CHANNEL_1,tone[i]);
HAL_Delay(beat[i]*500);
HAL_TIM_PWM_Stop(&htim1,TIM_CHANNEL_1); //音符间隔
HAL_Delay(50);
}
4、测试
实际测试效果视频如下,
5、参考
[1].RM0008 STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx advanced Arm-based 32-bit MCUs (version 20)