本文参考自:
https://shumeipai.nxez.com/2020/11/18/raspberry-pi-controls-the-buzzer-to-play-music.html
驱动蜂鸣器
蜂鸣器是一种一体化结构的电子讯响器,采用直流电压供电。
有源蜂鸣器和无源蜂鸣器
有源蜂鸣器是给一个高电平就会响,响的频率不会改变,内部带震荡电路,一通电就鸣叫,所以可以跟前面LED一样,给个高电平就能响,编程比无源的更方便。
无源蜂鸣器内部没有震荡源,直流信号无法让它鸣叫。必须用去震荡的电流驱动它,给一系列脉冲才会响,声音频率可控,可以做出不同的音效。这里我们以无源蜂鸣器为例子。
蜂鸣器的外观
PWM
PWM(Pulse Width Modulation)即脉冲宽度调制,是一种利用微处理器的数字输出来控制模拟电路的控制技术。可以用下面的一幅图来形象地说明PWM:
图中tpwm就是一个周期的时间长度。对于2KHz频率来说,那么周期就是1s/2K=500us。图中的D叫做占空比,指的是高电平的时间占用整个周期时间的百分比。第一个周期D=50%,那么就是高电平低电平的时间各占一半。接下来的D为33%,那就是通电时间为33%,剩余的不通电时间占用67%。
所有引脚都能够进行软件PWM,而GPIO12、GPIO13、GPIO18、GPIO19(对应引脚12 、32、33 、35)则能够进行硬件脉冲宽度调制。具体引脚可以参考上面开头介绍GPIO的图。
需要注意的是BCM2711芯片只支持两路PWM输出,所以以上12 Pin脚和32 Pin脚对应的都是channel 1的PWM输出,即如果这两个Pin的功能都选择的是PWM输出,则它们输出的PWM是完全相同的,同理33 Pin脚和35 Pin脚对应芯片channel 2的PWM输出。
硬件连接图
红色杜邦线一头连接树莓派的32 Pin脚(PWM0),一头连接蜂鸣器正极。黑色杜邦线一头连接树莓派的30Pin脚(ground),一头连接蜂鸣器负极。
此程序的是将pwm频率从1000hz每次递增10,一直到2000hz,再从2000hz每次递减10到1000hz,如此反复。
软件编写
# -*- coding: utf-8 -*-
#coding=utf-8
import RPi.GPIO as GPIO
import time
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM) #设置GPIO模式
GPIO.setup(12,GPIO.OUT) #将BCM12号引脚设置为输出模式
pwm = GPIO.PWM(12,1000) #设置BCM12号引脚, 设置pwm频率为1000HZ
pwm.start(50) #设置初始占空比(范围:0.0 <= dc <= 100.0)
#pwm.ChangeFrequency(freq) # freq 为设置的新频率,单位为 Hz
#pwm.ChangeDutyCycle(dc) # dc 为设置的新的占空比 范围:0.0-100.0
i=1000 #频率
dirs = 1 #频率递增方向,1为正,-1为负
try:
while i:
pwm.ChangeFrequency(i) #为设置的新频率,单位为 Hz
i=i+10*dirs #当前频率加上要增加的频率(10)乘以方向
time.sleep(0.05) #延时0.05秒
print('pwm当前频率为: %d '% i)#控制台打印当前的频率
if i>=2000: #如果当前频率大于2000hz,方向改为负
dirs = -1
elif i<=1000: #如果当前频率小于1000hz,方向改为正
dirs = 1
finally:
print("clean up all GPIO..." )
扩展延伸
通过pwm播放音乐。每个音符由音调和演奏时间组成。不同的音调在物理上就对应不同频率的音波。所以我们只要控制输出的频率和时长就能输出一首音乐了。每个音符都会播放一定的时间,这样就能构成一首歌曲。在音乐上,音符节奏分为1拍、1/2拍、1/4拍、1/8拍,假设一拍音符的时间为1;半拍为0.5;1/4拍为0.25;1/8拍为0.125……,所以我们可以为每个音符赋予这样的拍子播放出来,音乐就成了。
程序
# -*- coding: utf-8 -*-
#coding=utf-8
import RPi.GPIO as GPIO
import time
#定义不同音调的频率
NOTE_B0=31
NOTE_C1=33
NOTE_CS1=35
NOTE_D1=37
NOTE_DS1=39
NOTE_E1=41
NOTE_F1=44
NOTE_FS1=46
NOTE_G1=49
NOTE_GS1=52
NOTE_A1=55
NOTE_AS1=58
NOTE_B1=62
NOTE_C2=65
NOTE_CS2=69
NOTE_D2=73
NOTE_DS2=78
NOTE_E2=82
NOTE_F2=87
NOTE_FS2=93
NOTE_G2=98
NOTE_GS2=104
NOTE_A2=110
NOTE_AS2=117
NOTE_B2=123
NOTE_C3=131
NOTE_CS3=139
NOTE_D3=147
NOTE_DS3=156
NOTE_E3=165
NOTE_F3=175
NOTE_FS3=185
NOTE_G3=196
NOTE_GS3=208
NOTE_A3=220
NOTE_AS3=233
NOTE_B3=247
NOTE_C4=262
NOTE_CS4=277
NOTE_D4=294
NOTE_DS4=311
NOTE_E4=330
NOTE_F4=349
NOTE_FS4=370
NOTE_G4=392
NOTE_GS4=415
NOTE_A4=440
NOTE_AS4=466
NOTE_B4=494
NOTE_C5=523
NOTE_CS5=554
NOTE_D5=587
NOTE_DS5=622
NOTE_E5=659
NOTE_F5=698
NOTE_FS5=740
NOTE_G5=784
NOTE_GS5=831
NOTE_A5=880
NOTE_AS5=932
NOTE_B5=988
NOTE_C6=1047
NOTE_CS6=1109
NOTE_D6=1175
NOTE_DS6=1245
NOTE_E6=1319
NOTE_F6=1397
NOTE_FS6=1480
NOTE_G6=1568
NOTE_GS6=1661
NOTE_A6=1760
NOTE_AS6=1865
NOTE_B6=1976
NOTE_C7=2093
NOTE_CS7=2217
NOTE_D7=2349
NOTE_DS7=2489
NOTE_E7=2637
NOTE_F7=2794
NOTE_FS7=2960
NOTE_G7=3136
NOTE_GS7=3322
NOTE_A7=3520
NOTE_AS7=3729
NOTE_B7=3951
NOTE_C8=4186
NOTE_CS8=4435
NOTE_D8=4699
NOTE_DS8=4978
#歌曲谱子,[音调,时间]
melody =[
# 1
[NOTE_A4, 1.5], # 6
[NOTE_G4, 0.5], # 5
[NOTE_A4, 1], # 6
[NOTE_G4, 0.5], # 5
[NOTE_E4, 0.5], # 3
# 2
[NOTE_G4, 1], # 5
[NOTE_D4, 3], # 2
# 3
[NOTE_C4, 1.5], # 1
[NOTE_A3, 0.5], # .6
[NOTE_D4, 0.5], # 2
[NOTE_E4, 0.5], # 3
[NOTE_G4, 0.5], # 5
[NOTE_F4, 0.5], # 4
# 4
[NOTE_E4, 3], # 3
[NOTE_E4, 0.5], # 3
[NOTE_G4, 0.5], # 5
# 5
[NOTE_A4, 1.5], # 6
[NOTE_G4, 0.5], # 5
[NOTE_A4, 1], # 6
[NOTE_G4, 0.5], # 5
[NOTE_E4, 0.5], # 5
# 6
[NOTE_G4, 1], # 5
[NOTE_D4, 3], # 2
# 7
[NOTE_C4, 1.5], # 1
[NOTE_A3, 0.5], # .6
[NOTE_D4, 0.5], # 2
[NOTE_E4, 0.5], # 3
[NOTE_G3, 0.5], # .5
[NOTE_B3, 0.5], # .7
# 8
[NOTE_A3, 4], # .6
[0, 1], # 0
[NOTE_E4, 0.5], # 3
[NOTE_D4, 0.5], # 2
[NOTE_C4, 1.5], # 1
[NOTE_B3, 0.5], # .7
#
[NOTE_A3, 1.5], # .6
[NOTE_E3, 0.5], # .3
[NOTE_A3, 2], # .6
#
[NOTE_A3, 1], # .6
[NOTE_A4, 0.5], # 6
[NOTE_G4, 0.5], # 5
[NOTE_E4, 1], # 3
[NOTE_G4, 0.5], # 5
[NOTE_D4, 0.5], # 2
[NOTE_E4, 3], # 3
[NOTE_E4, 0.5], # 3
[NOTE_D4, 0.5], # 2
[NOTE_C4, 1.5], # 1
[NOTE_B3, 0.5], # .7
[NOTE_A3, 1.5], # .6
[NOTE_E3, 0.5], # .6
[NOTE_A3, 2], # .6
[0, 1], # 0
[NOTE_D4, 0.5], # 2
[NOTE_C4, 0.5], # 1
[NOTE_A3, 1], # .6
[NOTE_C4, 0.5], # 1
[NOTE_D4, 0.5], # 1
[NOTE_E4, 3], # 3
[NOTE_E4, 1], # 3
[NOTE_A4, 1.5], # 6
[NOTE_G4, 0.5], # 5
[NOTE_A4, 1], # 6
[NOTE_G4, 0.5], # 5
[NOTE_E4, 0.5], # 3
[NOTE_G4, 1], # 5
[NOTE_D4, 3], # 2
[NOTE_C4, 1.5], # 1
[NOTE_A3, 0.5], # .6
[NOTE_D4, 0.5], # 2
[NOTE_E4, 0.5], # 3
[NOTE_G4, 0.5], # 5
[NOTE_FS4, 0.5], # #4
[NOTE_E4, 3], # 3
[NOTE_E4, 0.5], # 3
[NOTE_G4, 0.5], # 5
[NOTE_A4, 1.5], # 6
[NOTE_G4, 0.5], # 5
[NOTE_A4, 1.0], # 6
[NOTE_G4, 0.5], # 5
[NOTE_E4, 0.5], # 3
[NOTE_G4, 1.0], # 5
[NOTE_D4, 3], # 2
[NOTE_C4, 1.5], # 1
[NOTE_A3, 0.5], # .6
[NOTE_D4, 0.5], # 2
[NOTE_E4, 0.5], # 3
[NOTE_G3, 0.5], # .5
[NOTE_B3, 0.5], # .7
[NOTE_A3, 3], # .6
[0, 1], # 0
[NOTE_E4, 0.5], # 3
[NOTE_D4, 0.5], # 2
[NOTE_C4, 1.0], # 1
[NOTE_C4, 0.5], # 1
[NOTE_B3, 0.5], # .7
[NOTE_A3, 1.5], # .6
[NOTE_E3, 0.5], # .3
[NOTE_A3, 2.0], # .6
[0, 1], # 0
[NOTE_A3, 0.5], # .6
[NOTE_G3, 0.5], # .5
[NOTE_E3, 1.0], # .3
[NOTE_G3, 0.5], # .5
[NOTE_D3, 0.5], # .2
[NOTE_E3, 3.0], # .3
[0, 1], # 0
[NOTE_E4, 0.5], # 3
[NOTE_D4, 0.5], # 2
[NOTE_C4, 1.0], #
[NOTE_C4, 0.5], # 1
[NOTE_B3, 0.5], # .7
[NOTE_A3, 1.5], # .6
[NOTE_E4, 0.5], # 3
[NOTE_D4, 2.0], # 2
[0, 1], # 0
[NOTE_D4, 0.5], # 2
[NOTE_C4, 0.5], # 1
[NOTE_A3, 1.0], # .6
[NOTE_B3, 0.5], # .7
[NOTE_G3, 0.5], # .5
[NOTE_A3, 3.0], # .6
]
#播放一组音调,(频率,时间,pwm对象)
def beep(freq, t_ms ,pwm):
if freq == 0:
ranges=1
else :
ranges=freq
print("will call change freq: %d range: %d\n" %(freq, ranges))
pwm.ChangeFrequency(ranges) # 设置的新频率,单位为 Hz
pwm.ChangeDutyCycle(95) # 改变占空比范围:0.0 <= dc <= 100.0
if t_ms>0:
time.sleep(t_ms) #持续输出此频率t_ms秒
return
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM) #设置GPIO模式
GPIO.setup(12,GPIO.OUT) #设置BCM12引脚为输出模式
pwm = GPIO.PWM(12,1000) #设置BCM12引脚为PWM模式 , 设置频率为1000HZ
pwm.start(50) #设置初始占空比为50%(范围:0.0 <= dc <= 100.0)
index=0 #索引,为了遍历出melody列表
try:
while 1:
noteDuration = melody[index][1] #获取当前索引melody列表中的时间
beep(melody[index][0], noteDuration,pwm) #设置当前索引的频率和时间给beep函数
index = index + 1 #索引加一,向下索引
if index>=121: #列表一共121条,到了120跳出循环
break
finally:
print("clean up all GPIO..." )
pwm.ChangeDutyCycle(0) #将占空比设置为0停止播放
pwm.ChangeDutyCycle(0) #将占空比设置为0停止播放