操作STM32单⽚机蜂鸣器模块演奏歌曲《北京欢迎你》
本篇主要讲述在STM32单⽚机开发板上通过STM32CubeIDE配置相关管脚和参数,来操作⽆源蜂鸣器播放歌曲《北京欢迎你》的步骤和原理。
软硬件需求
硬件:STM32系列单⽚机开发板⼀块(附带开发板的原理图)、串⼝下载线、⽆源蜂鸣器模块⼀个;
软件:STM32CubeIDE、STM32CubeProgrammer。
蜂鸣器的发声原理
以下是蜂鸣器模块的原理图:
图中的PG便是蜂鸣器,⼀般来说,3.3V直流产⽣的电信号功率很⼩,不⾜以驱动蜂鸣器发出可听见的响声,所以整个模块中有⼀个电流放⼤模块,这便是图中的晶体三极管。图中的放⼤电路是典型的共发射极接法,蜂鸣器接在集电极上,就实现了电流的放⼤(详细的晶体管放⼤电路原理请见《模拟电⼦技术》)
蜂鸣器分为有源和⽆源两种。所谓的源,指的是其中内部的振荡源,有源蜂鸣器中的振荡器⼀般是多谐振荡器,其原理就是模拟电路中RC 振荡器的⼀般原理(放⼤电路、正反馈、相位差90°、稳压电路),有源蜂鸣器内部的振荡源频率是固定的,所以使⽤时不可调频,且输⼊的电信号必须是直流。
⽆源蜂鸣器没有内部振荡源,发声的基本原理是电磁感应,其内部的基本结构是⼀匝匝线圈以及⼀个微型扬声器。由于没有振荡源,且内部的主要结构是线圈,所以其⼯作时输⼊的电信号必须是交流,⽽不能是频率很低的直流(直流不能通过⽆负载的线圈,负责线圈会因为短路⽽被烧坏)。与有源蜂鸣器⼀样,⽆源蜂鸣器也必须使⽤放⼤电路来增⼤输⼊信号的功率。
脉冲宽度调制信号(PWM)
信号在某段时间内的能量等于其瞬时值的平⽅在该段时间上的时间累积(信号能量的微积分定义)。因此,我们可以⽤矩形波来进⾏能量等效,使得等效矩形波通过负载时获得的能量与原信号通过负载时获得的能量相等。这种等效的矩形波信号便是脉冲宽度调制信号,简称PWM信号。
PWM信号的主要参数有两个,⼀个是占空⽐,⼀个是频率。PWM信号⼀个周期内⾼电平作⽤时间同整个周期时间的⽐值叫占空⽐,占空⽐是衡量PWM信号功率的主要参数,PWM信号的功率与其占空⽐成正相关。
PWM信号的运⽤
PWM波控制灯亮度:在⼀定电压范围内,灯泡的亮度与其两端所加电压⼤⼩成正⽐,因⽽我们可以通过与灯泡串联滑动变阻器来实现灯泡亮度的控制,这是中学时学到的欧姆定律的基本原理。如果我们通过在LED灯两端输⼊PWM波的话,由于PWM波周期很短,⼈会因为视觉暂留效应察觉不到灯的闪烁,⽽是灯的亮度变⼩了,这便是PWM波调节LED等亮度的原理。采⽤PWM信号驱动LED灯,可以实现在不改变电压峰值的情况下控制LED等亮度,也减少了电路的硬件设计成本。
PWM波控制电机转速:通电导体在磁场中会受到⼒的作⽤,这是电机转动的基本原理,但⼜由电磁感应原理可知,当电机转⼀段时间后断开电源,电机会因为在线圈中产⽣感抗电流和惯性的作⽤,逐渐减速⽽停下。如果我们可以对电机⼀段时间通电⼀段时间断电,就可以实现电机转速的控制了,这就是PWM波在电机上⼯作的原理。
PWM波控制蜂鸣器:与电磁感应原理类似,调解PWM波的占空⽐,便可以控制⽆源蜂鸣器的响度;调解PWM波的频率,便可以调解⽆源蜂鸣器的发⽣频率,也就可以发出不同⾳调的声⾳,这同样也是
蜂鸣器演奏歌曲的原理。
PWM信号的产⽣
STM32单⽚机中的PWM信号可以通过定时器产⽣。通过对定时器产⽣的⽅波信号进⾏分频和设置脉冲宽度,便可以实现调频和调占空⽐。STM32定时器中有两种重要的寄存器:
1. ARR,⾃动重载寄存器,装载着计数器能计数的最⼤值,使能定时器中断之后,当计数值⼤于ARR的值时,会产⽣溢出中断。由数字
电⼦技术中时序逻辑电路定时器的基本原理可以知道,ARR的值与所产⽣的PWM信号的频率相关,F == BaseFrq /
ARR(BaseFrq指的是预分频后定时器的基频);
2. CCR,捕获/⽐较寄存器,定时器当前计数值与CCR的值进⾏⽐较,如果⼤则输出⾼电平,否则输出低电平(可以实现此功能的硬件
模块是滞回⽐较器,有兴趣的读者可以深⼊去理解)CCR的值是可调的,因此我们可以通过设置CCR的值实现PWM信号占空⽐的控制。
配置STM32定时器的步骤如下:
设置系统时钟
上图展⽰了STM32时钟树的部分。在配置系统时钟时,⾸先要使能其中的⾼速外部时钟(HSE),即选择时钟树中HSE中的选项;顺着HSE的时钟线⾛,可以发现⼀个**PLLCLK(锁相环频率)**选项,选择它;其次,我们可以看到⼀个HCLK频率的设置框,把这⾥的频率设置为80MHz(最⼤⾼速时钟频率),此时便可以看到Timer Clock(定时器时钟)的基频是80MHz,后⾯我们就会在这80MHz的频率上进⾏分频和最⼤计数设置,使其产⽣不同频率的PWM信号。
设置管脚参数
打开CubeIDE中的Pinout & Configuration,对照⾃⼰的开发板原理图,配置时钟管脚和蜂鸣器管脚
这⾥的HSE选择“Crystal Resonator”(晶体共振),其他的设置默认。
本⼈蜂鸣器所接管脚是PA1,其对应的定时器是TIM2,这⾥我们可以先点击TIM2进⾏设置,图中设置定时器时直接把定时器对应的通道(channel 2)进⾏使能即可。然后点击图中PA1的管脚,点击其中的TIM2_CH2。定时器的参数在播放⾳乐时是时刻会改变的,这⾥我们只把Prescaler(预分频器)设置为80,此时,定时器TIM2默认输出的PWM信号的频率为1MHz**(80MHz/80 ==
1MHz)**其他参数将在播放歌曲的函数⾥⾯进⾏动态配置。
⽣成⽬标代码
点击Project Manager此时按住CTL+S键,在Code Generator中勾选Generated files中的第⼀项,即⾃动⽣成代码。
之后按CTL+S保存设置,然后选择“⽣成⽬标代码”。
编程环节
软件编程是此⼯程的核⼼,核⼼的代码如下:
main.c
#define ARRAY_LEN(a)(sizeof(a)/sizeof(a[0]))
const uint32_t tone[][2][3]={
{{261,523,1046},{277,554,1109}},
{{294,587,1175},{311,622,1245}},
{{330,659,1318},{0,0,0}},
{{349,698,1397},{370,740,1480}},
{{392,784,1568},{415,831,1661}},
{{440,880,1760},{466,932,1865}},
{{494,988,1976},{0,0,0}}
};
uint8_t BeiJing1[][3]=
{
{3,0,1},{5,0,1},{3,0,1},{2,0,1},{3,0,1},{2,0,1},{3,0,1},{0,0,0},
{3,0,1},{2,0,1},{6,0,0},{1,0,1},{3,0,1},{2,0,1},{0,0,0},{0,0,0},
{2,0,1},{1,0,1},{6,0,0},{1,0,1},{2,0,1},{3,0,1},{5,0,1},{2,0,1},
{3,0,1},{6,0,1},{5,0,1},{6,0,0},{2,0,1},{1,0,1},{0,0,0},
{3,0,1},{6,0,1},{5,0,1},{6,0,0},{2,0,1},{1,0,1},{0,0,0},
{3,0,1},{5,0,1},{3,0,1},{2,0,1},{3,0,1},{2,0,1},{3,0,1},{0,0,0},
{3,0,1},{2,0,1},{6,0,0},{1,0,1},{3,0,1},{2,0,1},{0,0,0},{0,0,0},
{2,0,1},{1,0,1},{6,0,0},{1,0,1},{2,0,1},{3,0,1},{5,0,1},{2,0,1},
{3,0,1},{6,0,1},{5,0,1},{6,0,0},{2,0,1},{1,0,1},{0,0,0},
{2,0,1},{1,0,1},{6,0,0},{1,0,1},{2,0,1},{3,0,1},{5,0,1},{2,0,1},
{3,0,1},{6,0,1},{5,0,1},{5,0,1},{3,0,1},{0,0,0},
{2,0,1},{3,0,1},{2,0,1},{1,0,1},{5,0,1},{6,0,1},{2,0,1},{0,0,0},
{5,0,0},{3,0,1},{3,0,1},{2,0,1},{3,0,1},{0,0,0},
{3,0,1},{5,0,1},{3,0,1},{2,0,1},{3,0,1},{2,0,1},{3,0,1},{0,0,0},
{3,0,1},{2,0,1},{6,0,0},{1,0,1},{3,0,1},{2,0,1},{0,0,0},{0,0,0},
{2,0,1},{1,0,1},{6,0,0},{1,0,1},{2,0,1},{3,0,1},{5,0,1},{2,0,1},
{3,0,1},{6,0,1},{5,0,1},{6,0,0},{2,0,1},{1,0,1},{0,0,0},
{2,0,1},{1,0,1},{6,0,0},{1,0,1},{2,0,1},{3,0,1},{5,0,1},{2,0,1},
{3,0,1},{6,0,1},{5,0,1},{5,0,1},{3,0,1},{0,0,0},
{2,0,1},{3,0,1},{2,0,1},{1,0,1},{5,0,1},{6,0,1},{2,0,1},{0,0,0},
{6,0,0},{3,0,1},{2,0,1},{2,0,1},{1,0,1},{1,0,1},
{0,0,0},{0,0,0},{3,0,1},{5,0,1},{0,0,0},
{1,0,2},{5,0,1},{6,0,1},{0,0,0},{6,0,1},{5,0,1},{3,0,1},{3,0,1},{5,0,1},{5,0,1},{0,0,0},
{3,0,1},{5,0,1},{6,0,1},{1,0,2},{2,0,2},{1,0,2},{5,0,1},{3,0,1},{2,0,1},{5,0,1},{3,0,1},{0,0,0},
{3,0,1},{5,0,1},{1,0,2},{5,0,1},{6,0,1},{0,0,0},
{1,0,2},{2,0,2},{1,0,2},{5,0,1},{3,0,1},{5,0,1},{7,0,1},{6,0,1},{0,0,0},
{3,0,1},{2,0,1},{3,0,1},{5,0,1},{3,0,2},{2,0,2},{1,0,2},{1,0,2},{0,0,0},{0,0,0},{0,0,0}
};
uint8_t BeiJing2[][3]=
{
{3,0,1},{5,0,1},{3,0,1},{2,0,1},{3,0,1},{2,0,1},{3,0,1},{0,0,0},
{3,0,1},{2,0,1},{6,0,0},{1,0,1},{3,0,1},{2,0,1},{0,0,0},{0,0,0},
{2,0,1},{1,0,1},{6,0,0},{1,0,1},{2,0,1},{3,0,1},{5,0,1},{2,0,1},
{3,0,1},{6,0,1},{5,0,1},{6,0,0},{2,0,1},{1,0,1},{0,0,0},
{2,0,1},{1,0,1},{6,0,0},{1,0,1},{2,0,1},{3,0,1},{5,0,1},{2,0,1},
{3,0,1},{6,0,1},{5,0,1},{5,0,1},{3,0,1},{0,0,0},
{2,0,1},{3,0,1},{2,0,1},{1,0,1},{5,0,1},{6,0,1},{2,0,1},{0,0,0},
{6,0,0},{3,0,1},{2,0,1},{2,0,1},{1,0,1},{1,0,1},{0,0,0},
html播放音乐代码{3,0,1},{5,0,1},{1,0,2},{5,0,1},{6,0,1},{0,0,0},{6,0,1},{5,0,1},{3,0,1},{3,0,1},{5,0,1},{5,0,1},{0,0,0}, {3,0,1},{5,0,1},{6,0,1},{1,0,2},{2,0,2},{1,0,2},{5,0,1},{3,0,1},{2,0,1},{5,0,1},{3,0,1},{0,0,0},
{3,0,1},{5,0,1},{1,0,2},{5,0,1},{6,0,1},{0,0,0},
{1,0,2},{2,0,2},{1,0,2},{5,0,1},{3,0,1},{5,0,1},{7,0,1},{6,0,1},{0,0,0},
{3,0,1},{2,0,1},{3,0,1},{5,0,1},{3,0,2},{2,0,2},{1,0,2},{1,0,2},{0,0,0},
{3,0,1},{5,0,1},{1,0,2},{5,0,1},{6,0,1},{0,0,0},
{1,0,2},{2,0,2},{1,0,2},{5,0,1},{3,0,1},{5,0,1},{7,0,1},{6,0,1},{0,0,0},
{3,0,1},{2,0,1},{3,0,1},{5,0,1},{3,0,2},{2,0,2},{1,0,2},{1,0,2},{0,0,0},{0,0,0},
{3,0,1},{5,0,1},{1,0,2},{5,0,1},{6,0,1},{0,0,0},
{1,0,2},{2,0,2},{1,0,2},{5,0,1},{3,0,1},{5,0,1},{7,0,1},{6,0,1},{0,0,0},
{3,0,1},{2,0,1},{3,0,1},{5,0,1},{3,0,2},{2,0,2},{0,0,0},{0,0,0},{0,0,0},{1,0,2},
};
int main(void)
{
...
while(1)
{
for(i=0; i<ARRAY_LEN(BeiJing1); i++){
if(BeiJing1[i][0]!=0){
beep_set( tone[BeiJing1[i][0]-1][BeiJing1[i][1]][BeiJing1[i][2]]);
beep_cry(365);
}
else{
HAL_Delay(250);
HAL_Delay(250);
}
}
for(i=0; i<ARRAY_LEN(BeiJing2); i++){
if(BeiJing2[i][0]!=0){
beep_set( tone[BeiJing2[i][0]-1][BeiJing2[i][1]][BeiJing2[i][2]]);
beep_cry(365);
}
else{
HAL_Delay(250);
}
}
beep_set(1046);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
HAL_Delay(3000);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_2);
HAL_Delay(4000);
}
tim.c
void beep_set(uint32_t frq){
htim2.Init.Period = BASE_FRQ / frq;
if(HAL_TIM_PWM_Init(&htim2)!= HAL_OK)
{
Error_Handler();
}
if(HAL_TIMEx_MasterConfigSynchronization(&htim2,&sMasterConfig)!= HAL_OK)
{
Error_Handler();
}
sConfigOC.Pulse = htim2.Init.Period /2;
if(HAL_TIM_PWM_ConfigChannel(&htim2,&sConfigOC, TIM_CHANNEL_2)!= HAL_OK)
{
Error_Handler();
}
HAL_TIM_MspPostInit(&htim2);
}
void beep_cry(uint32_t delay){
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
HAL_Delay(delay);
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_2);
}
说⼀下通过软件调节PWM信号频率和占空⽐的⽅法。
代码在管脚配置完成之后会⾃动⽣成,⽣成的代码包含配置定时器参数的相关部分,此部分在⼯程⽂件夹Src⾥的tim.c中,其初始化函数是void MX_TIM2_Init(void),我们将这个函数内部的部分代码截取过来再进⾏相关改写,便成为了设置蜂鸣器⼯作频率的函数void
beep_set(uint32_t frq),htim2是⼀个抽象为定时器的结构体,其成员htim2.Init.Period本质上也就是ARR的值,只要已知定时器基频以及所调频率,便可以确定Period的值(Period = BaseFrq / F);sConfigOC.Pulse应该设置为(Period / q)其中Period即htim2中的Period,q为所调占空⽐,这个变量决定了PWM信号的占空⽐⼤⼩。
简述⼀下乐谱中的基本概念:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。