蜂鸣器播放超级马里奥(STM32HAL库)

硬件部分

1.无源蜂鸣器原理

蜂鸣器按驱动方式可分为有源蜂鸣器(内含驱动线路)和无源蜂鸣器(外部驱动)(“源”指的是激励源)。有源蜂鸣器虽加电源就可以发出声音但频率相对固定,这里使用的是无源蜂鸣器,无源蜂鸣器利用电磁感应现象,为音圈接入交变电流后形成的电磁铁与永磁铁相吸或相斥而推动振膜发声,接入直流电只能持续推动振膜而无法产生声音,只能在接通或断开时产生声音。

蜂鸣器部分原理图

2.频率音调对照表

所以只需要将无源蜂鸣器接入电路中并用单片机控制引脚给它一定频率的方波信号就可以产生不同音调,再用延时加以节奏就可以用蜂鸣器模拟曲调实现音乐效果了。

频率音调对照表

软件部分

1.播放单个音调

音调

函数第一个参数hz控制声音音调,本质是在循环中控制高低电平时间及占比即可控制音调,比如要播放中音1这个音调,该音调频率为523Hz也就是每个电平变化周期是1/523s==>1000000/523us,让其中高低电平各占一半,即输出低电平后延时500000/523us再输出低电平,后再延时500000/523us再输出高电平。

持续时间

函数第二个参数time控制音调时间,本质是控制循环的次数,如播放523Hz音调电平每秒需要切换523次,那100ms需要切换的次数就为523*100/1000次。

1
2
3
4
5
6
7
8
9
void Beep(uint16_t hz,uint16_t time){
uint16_t i;
for(i=0; i<hz*time/1000; i++){//循环次数决定单音的时长
HAL_GPIO_WritePin(BEEP1_GPIO_Port,BEEP1_Pin,GPIO_PIN_RESET); //蜂鸣器接口输出低电平
delay_us(500000/hz); //延时
HAL_GPIO_WritePin(BEEP1_GPIO_Port,BEEP1_Pin,GPIO_PIN_SET); //蜂鸣器接口输出高电平
delay_us(500000/hz); //延时
}
}

2.播放整首

有了控制蜂鸣器播放单个音调的函数之后,想要播放整首歌就很简单了,只需要找到该歌曲的简谱,根据音符填入对应的频率即可。这里我们把频率都写入一个数组中,方便程序遍历。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
uint16_t mario[] = {659, 659, 15, 659, 15, 523, 659, 15, 784, 30, 392, 45, 523,
30, 392, 30, 330, 30, 440, 30, 494, 15, 466, 440, 15, 392, 15,659, 784,
880, 15, 698, 784, 15, 659, 15, 523, 587, 494, 30, 523, 30, 392, 30,
330, 30, 440, 30, 494, 15, 466, 440, 15, 392, 15, 659, 784, 880, 15,
698, 784, 15, 659, 15, 523, 587, 494, 15, 784, 740, 622, 659, 523,
587, 392, 440, 523, 15, 440, 523, 587, 15, 784, 740, 622, 659, 523,
15, 1046, 15, 1046, 1046, 30, 784, 740, 622, 659, 523, 587, 392, 440,
523, 15, 440, 523, 587, 15, 622, 30, 587, 30, 523, 30, 392, 392, 15,
392, 30, 523, 523, 15, 523, 15, 523, 587, 15, 659, 523, 15, 440, 392,
15, 523, 523, 15, 523, 15, 523, 587, 659, 15, 392, 15, 523, 523, 15,
523, 15, 523, 587, 15, 659, 523, 15, 440, 392, 15, 659, 659, 15, 659,
15, 523, 659, 15, 784, 30, 523, 30, 392, 30, 330, 30, 440, 30, 494,
15, 466, 440, 15, 392, 15, 659, 784, 880, 15, 698, 784, 15, 659, 15,
523, 587, 494, 30, 523, 30, 392, 30, 330, 30, 440, 30, 494, 15, 466,
440, 15, 392, 15, 659, 784, 880, 15, 698, 784, 15, 659, 15, 523, 587,
494, 30, 659, 523, 15, 392, 15, 415, 15, 440, 698, 15, 698, 698, 30,
494, 880, 15, 880, 5, 880, 5, 784, 5, 698, 15, 659, 523, 15, 440, 392,
30, 659, 523, 15, 392, 15, 415, 15, 440, 698, 15, 698, 698, 30, 494,
698, 5, 698, 5, 698, 5, 698, 5, 659, 5, 587, 523, 30};

剩下就只需要在播放函数中遍历此数组就好了,其中ARRAY_SIZE(song)是一个计算数组大小的函数宏,方便播放不同歌曲。

1
2
3
4
5
6
7
8
9
10
11
12
void MIDI_PLAY(void){
uint16_t i = 0;
uint16_t hz = 0;
for(i=0; i<ARRAY_SIZE(song); i++){
hz = song[i];
if(hz < 50)
HAL_Delay(hz*10);
else
Beep(hz, 160);
HAL_Delay(5);
}
}

这里在数组中加入了一些小于50的数作为节奏上的暂停,if语句判断如果如果小于50就延时该数值*10ms,还需要自己根据歌曲速度给Beep函数设定合适的音调时间。

完整驱动代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include "buzzer.h"
#define ARRAY_SIZE(a) ((sizeof a)/(sizeof a[0]))
#define song mario

void Beep(uint16_t hz,uint16_t time){
uint16_t i;
for(i=0; i<hz*time/1000; i++){//循环次数决定单音的时长
HAL_GPIO_WritePin(BEEP1_GPIO_Port,BEEP1_Pin,GPIO_PIN_RESET); //蜂鸣器接口输出低电平
delay_us(500000/hz); //延时
HAL_GPIO_WritePin(BEEP1_GPIO_Port,BEEP1_Pin,GPIO_PIN_SET); //蜂鸣器接口输出高电平
delay_us(500000/hz); //延时
}
}
/*超级马里奥*/
uint16_t mario[] = {659, 659, 15, 659, 15, 523, 659, 15, 784, 30, 392, 45, 523,
30, 392, 30, 330, 30, 440, 30, 494, 15, 466, 440, 15, 392, 15,659, 784,
880, 15, 698, 784, 15, 659, 15, 523, 587, 494, 30, 523, 30, 392, 30,
330, 30, 440, 30, 494, 15, 466, 440, 15, 392, 15, 659, 784, 880, 15,
698, 784, 15, 659, 15, 523, 587, 494, 15, 784, 740, 622, 659, 523,
587, 392, 440, 523, 15, 440, 523, 587, 15, 784, 740, 622, 659, 523,
15, 1046, 15, 1046, 1046, 30, 784, 740, 622, 659, 523, 587, 392, 440,
523, 15, 440, 523, 587, 15, 622, 30, 587, 30, 523, 30, 392, 392, 15,
392, 30, 523, 523, 15, 523, 15, 523, 587, 15, 659, 523, 15, 440, 392,
15, 523, 523, 15, 523, 15, 523, 587, 659, 15, 392, 15, 523, 523, 15,
523, 15, 523, 587, 15, 659, 523, 15, 440, 392, 15, 659, 659, 15, 659,
15, 523, 659, 15, 784, 30, 523, 30, 392, 30, 330, 30, 440, 30, 494,
15, 466, 440, 15, 392, 15, 659, 784, 880, 15, 698, 784, 15, 659, 15,
523, 587, 494, 30, 523, 30, 392, 30, 330, 30, 440, 30, 494, 15, 466,
440, 15, 392, 15, 659, 784, 880, 15, 698, 784, 15, 659, 15, 523, 587,
494, 30, 659, 523, 15, 392, 15, 415, 15, 440, 698, 15, 698, 698, 30,
494, 880, 15, 880, 5, 880, 5, 784, 5, 698, 15, 659, 523, 15, 440, 392,
30, 659, 523, 15, 392, 15, 415, 15, 440, 698, 15, 698, 698, 30, 494,
698, 5, 698, 5, 698, 5, 698, 5, 659, 5, 587, 523, 30};

/*小星星*/
uint16_t star[] = {523,523,784,784,880,880,784,15,698,698,659,659,587,587,523,15,784,784,698,698,659,659,587,15,784,784,698,698,659,659,587,15};

void MIDI_PLAY(void){
uint16_t i = 0;
uint16_t hz = 0;
for(i=0; i<ARRAY_SIZE(song); i++){
hz = song[i];
if(hz < 50)
HAL_Delay(hz*10);
else
Beep(hz, 160);
HAL_Delay(5);
}
}