一 本章简介
本章介绍如何使用 MicroPython 的 PWM 功能驱动筑基学习板上的无源蜂鸣器。与有源蜂鸣器只能发出固定音调不同,无源蜂鸣器可以通过改变 PWM 频率来发出不同音调的声音,甚至可以演奏简单的旋律。
IMPORTANT
筑基学习板上的有源蜂鸣器和无源蜂鸣器共用同一个引脚 PA6,通过 8 路拨码开关的 BIT4 来切换。使用无源蜂鸣器时,BIT4 必须拨到 ON 位置(向上拨)。如果 BIT4 处于 OFF 位置(默认状态),PA6 的信号会被路由到有源蜂鸣器,无源蜂鸣器将不会响。
1.1 学习目标
| 序号 | 学习目标 | 重要程度 |
|---|---|---|
| 1 | 理解无源蜂鸣器的驱动原理(PWM 频率决定音调) | ⭐⭐⭐⭐⭐ |
| 2 | 掌握使用 pyb.Timer 输出指定频率 PWM 驱动蜂鸣器的方法 | ⭐⭐⭐⭐⭐ |
| 3 | 理解拨码开关 BIT4 对蜂鸣器选择的作用 | ⭐⭐⭐⭐⭐ |
| 4 | 能够实现音阶播放和简单旋律演奏 | ⭐⭐⭐⭐ |
1.2 重点提示
- 无源蜂鸣器的控制引脚为 PA6,对应 TIM13_CH1(AF9)。
- 使用前必须确认拨码开关 BIT4 处于 ON 位置(向上拨)。BIT4 的默认状态是 OFF(有源蜂鸣器),需要手动切换。
- 无源蜂鸣器靠 PWM 的频率决定音调,靠占空比决定音量。通常占空比设为 50% 即可获得最大音量。
- 人耳可听频率范围约为 20Hz~20kHz,常用音符频率在 262Hz(中央 C)到 1047Hz(高音 C)之间。
1.3 基础概念与术语
- 无源蜂鸣器(Passive Buzzer):内部没有振荡电路,需要外部提供特定频率的方波信号才能发声。频率不同,音调不同。
- 音调(Pitch):声音的高低,由振动频率决定。频率越高,音调越高。
- 音符频率:每个音符对应一个固定频率,例如中央 C(C4)= 262Hz,A4(标准音)= 440Hz。
二 硬件说明
左边的是无源蜂鸣器,右边的是有源蜂鸣器。

筑基学习板上,PA6 的信号经过一个单刀双掷模拟开关后,分别连接到有源蜂鸣器和无源蜂鸣器:
2.1 无源蜂鸣器驱动原理
无源蜂鸣器内部是一个压电陶瓷片或电磁线圈,当施加交变信号时,陶瓷片/线圈振动产生声音。振动频率等于输入信号的频率,因此改变 PWM 频率就能改变音调。
PA6 (TIM13_CH1) → 模拟开关(BIT4=ON) → 无源蜂鸣器2.1.1 谐振频率与音量的关系
筑基学习板板载的无源蜂鸣器的谐振频率约为 2.7kHz,在这个频率下蜂鸣器的音量最大。
为什么会这样?这和蜂鸣器的物理结构有关。无源蜂鸣器内部的压电陶瓷片(或振膜)是一个弹性体,就像吉他弦一样,它有一个 天生 最容易振动的频率——这就是谐振频率。当外部驱动信号的频率恰好等于谐振频率时,振膜的振幅最大,推动空气的力度最强,声音自然也就最响亮。
偏离谐振频率越远,振膜的振动幅度越小,音量也就越低:
| 频率范围 | 相对音量 | 说明 |
|---|---|---|
| < 500Hz | 很小 | 远低于谐振频率,振膜几乎不动 |
| 1kHz~2kHz | 中等 | 接近谐振区,音量逐渐增大 |
| ~2.7kHz | 最大 | 谐振频率,振幅最大 |
| 3kHz~4kHz | 中等 | 偏离谐振区,音量逐渐减小 |
| > 5kHz | 较小 | 远高于谐振频率,响应变差 |
TIP
在实际应用中,如果你只是想让蜂鸣器发出最响亮的提示音(比如报警),直接用 2.7kHz 驱动即可。如果要演奏旋律,不同音符的音量会有差异——靠近 2.7kHz 的音符(如 E7=2637Hz)会比较响,远离的音符(如 C4=262Hz)会比较轻。这是无源蜂鸣器的物理特性决定的,无法通过软件完全消除。
2.2 无源蜂鸣器资源汇总
| 参数 | 说明 |
|---|---|
| 控制引脚 | PA6 |
| 定时器通道 | TIM13_CH1(AF9) |
| 驱动方式 | PWM(频率决定音调,占空比决定音量) |
| 谐振频率 | ~2.7kHz(此频率下音量最大) |
| 拨码开关 | BIT4 = ON(向上拨) |
| 推荐占空比 | 50%(最大音量) |
2.3 拨码开关 BIT4 设置
CAUTION
使用无源蜂鸣器前,请务必将 8 路拨码开关的 BIT4 拨到 ON 位置(向上拨)。出厂默认是 OFF(有源蜂鸣器),如果不切换,无源蜂鸣器不会发声。

| BIT4 状态 | 效果 |
|---|---|
| OFF(默认,向下) | PA6 连接到有源蜂鸣器 |
| ON(向上) | PA6 连接到无源蜂鸣器 |
三 软件设计
3.1 基础部分
3.1.1 发出固定频率的声音
先让蜂鸣器发出一个 1kHz 的声音,验证硬件配置是否正确:
# 无源蜂鸣器基本测试
from pyb import Pin, Timer
import time
# PA6 -> TIM13_CH1, AF9
pin = Pin('PA6', Pin.AF_PP, af=9) # AF9 = TIM13
tim = Timer(13, freq=1000) # 1kHz
ch = tim.channel(1, Timer.PWM, pin=pin)
# 50% 占空比发声
ch.pulse_width_percent(50)
time.sleep(1)
# 停止发声
ch.pulse_width_percent(0)
tim.deinit()
print("测试完成")2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
TIP
如果没有声音,请检查:
- 拨码开关 BIT4 是否拨到了 ON(向上)
- 筑基学习板是否正常供电
- AF 编号是否正确(AF9 对应 TIM13)
3.1.2 播放不同音调
通过改变定时器频率来发出不同音调:
# 播放不同音调
from pyb import Pin, Timer
import time
pin = Pin('PA6', Pin.AF_PP, af=9)
tim = Timer(13, freq=1000)
ch = tim.channel(1, Timer.PWM, pin=pin)
def play_tone(freq, duration_ms):
"""播放指定频率和时长的音调"""
if freq == 0:
ch.pulse_width_percent(0)
else:
tim.freq(freq)
ch.pulse_width_percent(50)
time.sleep_ms(duration_ms)
ch.pulse_width_percent(0)
time.sleep_ms(50) # 音符间隔
# 从低到高播放几个音
for freq in [262, 330, 392, 523, 659, 784, 1047]:
print("频率: {}Hz".format(freq))
play_tone(freq, 300)
tim.deinit()
print("播放完成")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
3.1.3 音符频率表与音阶播放
# 音阶播放(C4 到 C5)
from pyb import Pin, Timer
import time
pin = Pin('PA6', Pin.AF_PP, af=9)
tim = Timer(13, freq=1000)
ch = tim.channel(1, Timer.PWM, pin=pin)
# 音符频率表(Hz)
NOTES = {
'C4': 262, 'D4': 294, 'E4': 330, 'F4': 349,
'G4': 392, 'A4': 440, 'B4': 494, 'C5': 523,
'D5': 587, 'E5': 659, 'F5': 698, 'G5': 784,
'A5': 880, 'B5': 988, 'C6': 1047,
'REST': 0,
}
def play_tone(freq, duration_ms):
if freq == 0:
ch.pulse_width_percent(0)
else:
tim.freq(freq)
ch.pulse_width_percent(50)
time.sleep_ms(duration_ms)
ch.pulse_width_percent(0)
time.sleep_ms(50)
# 播放 C 大调音阶(上行+下行)
scale_up = ['C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4', 'C5']
scale_down = ['B4', 'A4', 'G4', 'F4', 'E4', 'D4', 'C4']
print("C 大调音阶上行")
for note in scale_up:
print(" {} {}Hz".format(note, NOTES[note]))
play_tone(NOTES[note], 300)
time.sleep_ms(300)
print("C 大调音阶下行")
for note in scale_down:
play_tone(NOTES[note], 300)
ch.pulse_width_percent(0)
tim.deinit()
print("播放完成")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
3.2 进阶部分
3.2.1 演奏《小星星》
# 演奏《小星星》
from pyb import Pin, Timer
import time
pin = Pin('PA6', Pin.AF_PP, af=9)
tim = Timer(13, freq=1000)
ch = tim.channel(1, Timer.PWM, pin=pin)
NOTES = {
'C4': 262, 'D4': 294, 'E4': 330, 'F4': 349,
'G4': 392, 'A4': 440, 'B4': 494, 'C5': 523,
'REST': 0,
}
def play_tone(freq, duration_ms):
if freq == 0:
ch.pulse_width_percent(0)
else:
tim.freq(freq)
ch.pulse_width_percent(50)
time.sleep_ms(duration_ms)
ch.pulse_width_percent(0)
time.sleep_ms(50)
def play_melody(melody):
"""播放旋律,格式: [(音符名, 时长ms), ...]"""
for note, duration in melody:
freq = NOTES.get(note, 0)
play_tone(freq, duration)
# 《小星星》旋律
twinkle = [
('C4', 400), ('C4', 400), ('G4', 400), ('G4', 400),
('A4', 400), ('A4', 400), ('G4', 800),
('F4', 400), ('F4', 400), ('E4', 400), ('E4', 400),
('D4', 400), ('D4', 400), ('C4', 800),
('G4', 400), ('G4', 400), ('F4', 400), ('F4', 400),
('E4', 400), ('E4', 400), ('D4', 800),
('G4', 400), ('G4', 400), ('F4', 400), ('F4', 400),
('E4', 400), ('E4', 400), ('D4', 800),
('C4', 400), ('C4', 400), ('G4', 400), ('G4', 400),
('A4', 400), ('A4', 400), ('G4', 800),
('F4', 400), ('F4', 400), ('E4', 400), ('E4', 400),
('D4', 400), ('D4', 400), ('C4', 800),
]
print("演奏《小星星》")
play_melody(twinkle)
ch.pulse_width_percent(0)
tim.deinit()
print("演奏完成")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
50
51
52
3.2.2 通用旋律播放器类
# 通用旋律播放器类
from pyb import Pin, Timer
import time
class MelodyPlayer:
"""
无源蜂鸣器旋律播放器
pin_name: 蜂鸣器引脚
tim_id: 定时器编号
af: 复用功能编号
"""
# 标准音符频率表
NOTE_FREQ = {
'C4': 262, 'C#4': 277, 'D4': 294, 'D#4': 311,
'E4': 330, 'F4': 349, 'F#4': 370, 'G4': 392,
'G#4': 415, 'A4': 440, 'A#4': 466, 'B4': 494,
'C5': 523, 'C#5': 554, 'D5': 587, 'D#5': 622,
'E5': 659, 'F5': 698, 'F#5': 740, 'G5': 784,
'G#5': 831, 'A5': 880, 'A#5': 932, 'B5': 988,
'C6': 1047,
'REST': 0, '_': 0,
}
def __init__(self, pin_name='PA6', tim_id=13, af=9):
self.pin = Pin(pin_name, Pin.AF_PP, af=af)
self.tim = Timer(tim_id, freq=1000)
self.ch = self.tim.channel(1, Timer.PWM, pin=self.pin)
self.ch.pulse_width_percent(0)
def tone(self, freq, duration_ms):
"""播放单个音调"""
if freq == 0:
self.ch.pulse_width_percent(0)
else:
self.tim.freq(freq)
self.ch.pulse_width_percent(50)
time.sleep_ms(duration_ms)
self.ch.pulse_width_percent(0)
time.sleep_ms(30)
def play(self, melody, tempo=1.0):
"""
播放旋律
melody: [(音符名, 时长ms), ...]
tempo: 速度倍率,>1 加快,<1 减慢
"""
for note, duration in melody:
freq = self.NOTE_FREQ.get(note, 0)
self.tone(freq, int(duration / tempo))
def stop(self):
"""停止播放"""
self.ch.pulse_width_percent(0)
def deinit(self):
"""释放资源"""
self.ch.pulse_width_percent(0)
self.tim.deinit()
# 使用示例
player = MelodyPlayer('PA6', 13, 9)
# 《欢乐颂》片段
ode_to_joy = [
('E4', 400), ('E4', 400), ('F4', 400), ('G4', 400),
('G4', 400), ('F4', 400), ('E4', 400), ('D4', 400),
('C4', 400), ('C4', 400), ('D4', 400), ('E4', 400),
('E4', 600), ('D4', 200), ('D4', 800),
]
print("演奏《欢乐颂》片段")
player.play(ode_to_joy)
# 加速播放
print("1.5 倍速播放")
player.play(ode_to_joy, tempo=1.5)
player.deinit()
print("完成")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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
四 常见问题
Q: 代码运行了,但蜂鸣器没有声音或者声音完全不对?
第一优先级:检查拨码开关 BIT4 是否拨到了 ON(向上)。这是最常见的原因。BIT4 默认是 OFF(有源蜂鸣器),使用无源蜂鸣器必须手动切换。
Q: 声音很小或者有杂音?
- 占空比设为 50% 可获得最大音量
- 频率过低(<100Hz)或过高(>5kHz)时,蜂鸣器的响应会变差
- 确认供电正常,蜂鸣器使用 5V 驱动
Q: 有源蜂鸣器和无源蜂鸣器能同时使用吗?
不能。它们共用 PA6 引脚,通过模拟开关二选一。同一时间只能使用其中一个。
Q: 做完无源蜂鸣器实验后,想切回有源蜂鸣器怎么办?
把拨码开关 BIT4 拨回 OFF(向下)即可。同时代码也要改回普通 GPIO 输出方式(Pin.OUT_PP),不再需要 PWM。
五 本节参考文档
- MicroPython pyb.Timer 文档:
- MicroPython pyb.Pin 文档:
- 天空星硬件资料(原理图):