STM32F407使用pwm控制蜂鸣器播放音乐
1.PWM概述
脉冲宽度调制(PulseWidthModulation,PWM)简称脉宽调制。通俗讲,PWM是一种对模拟信号
电平进行数字编码的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信
号的电平进行编码。
在模拟电路中,模拟信号的值可以连续进行变化,在时间和值的幅度上都几乎没有限制,基本上可
以取任何实数值,输入与输出也呈线性变化。所以在模拟电路中,电压和电流可直接用来进行控制对
象,例如家用电器设备中的音量开关控制、采用卤素灯泡灯具的亮度控制等等。
但模拟电路有诸多的问题:例如控制信号容易随时间漂移,难以调节;功耗大;易受噪声和环境干
扰等。生成模拟信号对于数字电路而言往往需要额外的数字-模拟转换器。
与模拟电路不同,数字电路是在预先确定的范围内取值,在任何时刻,其输出只可能为ON和OFF
两种状态,所以电压或电流会通/断方式的重复脉冲序列加载到模拟负载。PWM技术是一种对模拟信号
电平的数字编码方法,通过使用高分辨率计数器(调制频率)调制方波的占空比,从而实现对一个模
拟信号的电平进行编码。其最大的优点是从处理器到被控对象之间的所有信号都是数字形式的,无需
再进行数模转换过程;而且对噪声的抗干扰能力也大大增强(噪声只有在强到足以将逻辑值改变时,
才可能对数字信号产生实质的影响),这也是PWM在通讯等信号传输行业得到大量应用的主要原因。
模拟信号能否使用PWM进行编码调制,仅依赖带宽,这即意味着只要有足够的带宽,任何模拟信
号值均可以采用PWM技术进行调制编码。
我们假定定时器工作在向上计数 PWM模式,且
当 CNT<CCRx时,输出 0
当 CNT>=CCRx时,输出 1。
那么就可以得到如上的 PWM示意图:
a. 0-t1段,定时器计数器TIMx_CNT值小于CCRx值,输出低电平。
b. t1-t2段,定时器计数器TIMx_CNT值大于CCRx值,输出高电平。
当TIMx_CNT值达到ARR时,定时器溢出,重新向上计数...循环此过程至此一个PWM周期完成。
改变 CCRx的值,就可以改变 PWM输出的占空比,改变 ARR的值,就可以改变 PWM输出的 频
率,这就是 PWM输出的原理。
占空比:指高电平占整个周期的比例。
(1) TIM_OCMode:比较输出模式选择,总共有八种,常用的为PWM1/PWM2。它设定CCMRx 寄
存器OCxM[2:0] 位的值。
(2) TIM_OutputState:比较输出使能,决定最终的输出比较信号OCx 是否通过外部引脚输出。它
设定TIMx_CCER 寄存器CCxE/CCxNE 位的值。
(3) TIM_OutputNState: 比较互补输出使能,决定OCx 的互补信号OCxN 是否通过外部引脚输出。
它设定CCER 寄存器CCxNE 位的值。
(4) TIM_Pulse:比较输出脉冲宽度,实际设定比较寄存器CCR 的值,决定脉冲宽度。可设置范围
为0 至65535。
(5) TIM_OCPolarity:比较输出极性,可选OCx 为高电平有效或低电平有效。它决定着定时器通
道有效电平。它设定CCER 寄存器的CCxP 位的值。
(6) TIM_OCNPolarity:比较互补输出极性,可选OCxN 为高电平有效或低电平有效。它设定
TIMx_CCER 寄存器的CCxNP 位的值。
(7) TIM_OCIdleState:空闲状态时通道输出电平设置,可选输出1 或输出0,即在空闲状态
(BDTR_MOE 位为0) 时,经过死区时间后定时器通道输出高电平或低电平。它设定CR2 寄存
器的OISx 位的值。
(8) TIM_OCNIdleState:空闲状态时互补通道输出电平设置,可选输出1 或输出0,即在空闲状态
(BDTR_MOE 位为0) 时,经过死区时间后定时器互补通道输出高电平或低电平,设定值必须与
TIM_OCIdleState 相反。它设定是CR2 寄存器的OISxN 位的值。
分频系数:84-1
arr值:2000-1
比较值:480-1
可知:
输送给定时器4的时钟频率为:APB1时钟频率2/84=482/84=1Mhz
PWM周期为:在1Mhz的时钟频率下。数2000个数 1/1Mhz*2000=2ms
占空比为:高电平时间/PWM周期=数480个数/数2000个数=480/2000=24%
pwm频率为:1/pwm周期 = 1/2ms=500Hz
以上均为PWM基础部分,我相信学会这些并不困难,那我们就来尝试用PWM来控制蜂鸣器播放音乐
1.首先要选择好定时器,一定要看原理图选择合适的通道,我这里选择的定时器比较通道刚好还有蜂鸣器功能
2.编写基本的代码,时基单元,以及PWM
`void TIMx_PWM_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM13, ENABLE);
TIM_TimeBaseStructure.TIM_Period =3816-1;//周期
TIM_TimeBaseStructure.TIM_Prescaler = 84-1;// 84000000/84 = 1Mhz(1s)
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM13, &TIM_TimeBaseStructure);
// TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
// TIM_OCInitStructure.TIM_Pulse = 3816/2 ;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM13, &TIM_OCInitStructure);
// TIM_OC1PreloadConfig(TIM13, TIM_OCPreload_Enable);//直接加载设定好的值
// TIM_ARRPreloadConfig(TIM13, ENABLE);
/* TIM3 enable counter */
TIM_Cmd(TIM13, ENABLE);
}
`
3.我们先来搞懂一些关系:频率决定音调,峰占比觉得音量,于是我们来写一个函数用来控制 时基单元的频率和比较值:
//此函数可以改变预装载的值 TIM_SetAutoreload(TIM13, period);//period:预装载的值
还有
`
//此函数可以改变pwm的比较值
TIM_SetCompare1(TIM13, pulse);
`
void set_pwm(uint16_t period, uint16_t pulse)
{
//此函数可以改变预装载的值 相当于ARR
TIM_SetAutoreload(TIM13, period);//period:预装载的值
//此函数可以改变pwm的比较值
TIM_SetCompare1(TIM13, pulse);
}
4.根据音乐简朴 低音,中音,高音,用数组储存
5.再看main函数
点击查看代码
const uint16_t tone[] ={3817,3401,3030,2865,2552,2272,2024,//do~xi 0-6 低音
// 0 1 2 3 4 5 6
1912,1703,1517,1432,1275,1136,1012,//7-13 中音
// 7 8 9 10 11 12 13
956, 851,758, 715, 637,568, 506};//14-20 高音
// 14 15 16 17 18 19 20
uint8_t music_tone[200] = {5,7,11,7,3,4,5,11,7, 0,4,11,7,0,11,6,11, 5,7,11,5,3,4,5,11,7, 0,4,11,7,0,11,6,7,11,
5,7,11,7,3,4,5,11,0, 0,4,11,7,0,11,6,11, 5,7,11,5,3,4,5,11,7, 0,4,11,7,0,11,6,7,11,
11,11,7,7,8,9, 11,11,7,7,8,9,8,7,4,11,11,7,7,8,9 ,9,8,9,10,9,8,11,9,8,7};
uint8_t music_time[200] = {2,2, 2,2, 2,1,1,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,1,1,2,
2,2, 2,2, 2,1,1,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,1,1,2,
2,2,2,2,4,2,2,2,2,2,2,1,1,1,1,2,2,2,2,4,2,2,4,1,1,1,1,1,1,1,1,2};
uint8_t i;
while(1)
{
for(i=0; i<101; i++)
{
set_pwm(tone[music_tone[i]],tone[music_tone[i]]/2);
Delay_ms(music_time[i]*62.5*4);
}
}
6.别忘了GPIO引脚要调成复用模式
点击查看代码
void BEEP_PWM(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* GPIOC clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
/* GPIOC Configuration: TIM3 CH1 (PC6), TIM3 CH2 (PC7), TIM3 CH3 (PC8) and TIM3 CH4 (PC9) */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_Init(GPIOF, &GPIO_InitStructure);
/* Connect TIM3 pins to AF2 */
GPIO_PinAFConfig(GPIOF, GPIO_PinSource8, GPIO_AF_TIM13);
}
搞定!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具