本此任务的目标主要是通过简单的有源蜂鸣器输出层次分明的八音,这样就实现了我们基本的播放器的输出基础。在很多项目中,使用蜂鸣器发出音符是一种常见且简单的方式。而如果要获得层次感就要用到PWM,在本任务文章中,将使用PWM(脉冲宽度调制)控制有源蜂鸣器,生成基础八音阶(即"哆瑞米发嗦啦西哆"),主要是我们最小接触的音乐知识可能就是这个了。
"哆瑞米发嗦啦西哆" 这一连串的音符通常称为 C大调音阶,其中每个音符都有其特定的频率。这八个音符的名字及其对应的音高分别是:
哆(Do): C4
瑞(Re): D4
米(Mi): E4
发(Fa): F4
嗦(Sol): G4
啦(La): A4
西(Ti): B4
哆(Do): C5
这些音符广泛应用于全球的音乐教育和演奏中,尤其是在 C大调(自然音阶)中,是最基础且最常见的一组音符。无论是钢琴、吉他还是其他乐器,它们的音符与频率几乎是固定的,因此可以被视为一种普适性音阶。
在音乐中,音符对应的频率值是固定的。每个音符的频率是通过科学的音律计算得出的,并且通常以赫兹(Hz) 作为单位。C大调的八个音符与它们对应的频率如下:
C4(哆):261.63 Hz
D4(瑞):293.66 Hz
E4(米):329.63 Hz
F4(发):349.23 Hz
G4(嗦):392.00 Hz
A4(啦):440.00 Hz
B4(西):493.88 Hz
C5(哆):523.25 Hz
我们可以看到,C4 到 C5 之间的音符频率遵循一定的规律,每个音符的频率相差约为 1.059 倍(即音程为半音)。
通过有源蜂鸣器实现这些音符,PWM(脉冲宽度调制)是控制蜂鸣器频率的一种常见方法。通过改变 PWM 信号的频率,可以产生不同的音高。每个音符的频率都对应一个特定的 PWM 输出频率,从而控制蜂鸣器发出相应的声音。在CircuitPython中,我们可以通过 pulseio 或 simpleio来进行PWM控制,当然pwmio也可以。
使用PWM控制蜂鸣器时,我们需要设定正确的频率,使得每个音符的声音正确匹配。比如:
C4(261.63 Hz):用PWM频率为 261 Hz
D4(293.66 Hz):用PWM频率为 294 Hz
E4(329.63 Hz):用PWM频率为 330 Hz
F4(349.23 Hz):用PWM频率为 349 Hz
G4(392.00 Hz):用PWM频率为 392 Hz
A4(440.00 Hz):用PWM频率为 440 Hz
B4(493.88 Hz):用PWM频率为 494 Hz
C5(523.25 Hz):用PWM频率为 523 Hz
我们在上一章节的基础上增加一个有源蜂鸣器,控制引脚选用的是D13,硬件连接如下:

import pwmio
# 设置蜂鸣器连接的引脚(D13)
pwm = pwmio.PWMOut(board.D13, duty_cycle=0, frequency=440, variable_frequency=True)
# 8 音阶频率(C4, D4, E4, F4, G4, A4, B4, C5)
note_names = ['C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4', 'C5']
frequencies = [261, 293, 329, 349, 392, 440, 493, 523]
# 按键防抖延迟
DEBOUNCE_TIME = 0.2 # 防抖延时(秒)
# 音符播放时长(秒)
NOTE_DURATION = 0.5 # 每个音符的最大播放时长(秒)
# 音符播放控制函数
def play_note(note):
if note in range(len(note_names)):
names = note_names[note]
freq = frequencies[note]
print(f"Playing {names} at {freq} Hz")
pwm.frequency = int(freq)
pwm.duty_cycle = 2 ** 15
else:
print(f"Note {names} is not valid in the scale")
# 停止播放音符
def stop_playing():
pwm.duty_cycle = 0note_index = 0 # 当前音符的索引 last_button_state = False # 上一次的按键状态(上拉状态下,未按下为False) note_start_time = time.monotonic() # 记录音符播放开始的时间 # 示例调用 while True: # 检测按键是否按下(低电平表示按下) current_button_state = button2.value # 获取当前按键状态 if not current_button_state and last_button_state: # 按键刚被按下(由高电平变低电平) stop_playing() # 停止当前音符(如果正在播放) play_note(note_index) # 播放下一个音符 # 切换到下一个音符,若达到最后一个音符则从头开始 note_index = (note_index + 1) % len(note_names) # 重置音符开始播放的时间 note_start_time = time.monotonic() # 防抖处理 time.sleep(DEBOUNCE_TIME) # 自动停止音符播放 if time.monotonic() - note_start_time > NOTE_DURATION: stop_playing() # 超过最大播放时长后停止播放 note_start_time = time.monotonic() # 记录下一个音符的开始时间 last_button_state = current_button_state # 更新按键状态 time.sleep(0.01) # 稍微等一下,避免CPU占用过高
这里需要注意一下,pulseio 或 simpleio都是通过阻塞式的方法进行的蜂鸣器的控制,播放时间的长短会影响后面的颜色采集以及播放效果,使用pwmio模块必须开启variable_frequency=True,才能实现动态调整。
相关效果可以看一下教程视频内容。
我要赚赏金
