ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Arduino UNO蜂鸣器演奏音乐:从接线到代码实现

Arduino UNO蜂鸣器演奏音乐:从接线到代码实现 用 Arduino UNO 和蜂鸣器演奏《Aoharu》这类乐曲看起来只是把一段旋律喂给蜂鸣器但真正动手时有源蜂鸣器与无源蜂鸣器的区别、UNO 引脚能不能直接驱动、乐谱怎么转成频率和时值以及tone()函数的实际行为每个环节都会直接影响最终效果。这篇文章会按项目从零跑通的顺序先解释蜂鸣器发声的原理再给出接线方式和测试代码然后演示如何把任意简谱转成 Arduino 旋律数组包括如何替换成《Aoharu》这样的点歌曲目最后整理常见故障的排查路径。这个场景很适合做电子音乐小作品一块 UNO 板、一个蜂鸣器、几根杜邦线就能把一首歌的主旋律播放出来。评论区点歌的本质其实是“换数据”。真正发生变化的是音符频率数组和节拍数组播放引擎和硬件电路基本不需要修改。所以下面先把硬件和原理讲清楚再给出一套可以复用的播放框架。1. 蜂鸣器为什么能演奏音乐先理解发声原理1.1 从“响”到“唱”蜂鸣器需要什么蜂鸣器通常被设计用来发出提示音而不是播放音乐。常见提示音只需要一个固定频率因此很多蜂鸣器内部已经集成了震荡电路只要通电就会响。这种蜂鸣器适合做报警、开关机提示但不能演奏旋律因为它的音高被固定住了。能演奏旋律的蜂鸣器必须允许外部改变振动频率。无源蜂鸣器不包含震荡源它需要一个外部方波信号来驱动。方波频率高声音音调就高方波频率低声音音调就低。Arduino UNO 提供的tone()函数就是用来产生方波的tone(引脚号, 频率);例如tone(8, 440)会让 8 号引脚输出 440Hz 的方波驱动无源蜂鸣器发出 A4 这个音。停止发声时调用noTone(8);在标准 Arduino 实现中tone()的频率范围一般是 31Hz 到 65535Hz。但这不是说蜂鸣器在这个范围内都能有效发声实际效果还取决于蜂鸣器本身的频率响应。常见小蜂鸣器中频段表现比较好频率太高或太低时音量可能明显下降甚至人耳不易察觉。所以用 UNO 演奏音乐的核心工作就是把乐谱中的“音高”和“时值”转换成“频率”和“持续时间”两个数据然后用tone()和delay()把它们按顺序播放出来。1.2 有源蜂鸣器与无源蜂鸣器选错就放不出旋律很多新手第一次买蜂鸣器回来接上后要么一直长鸣要么完全无声原因往往就是分不清有源和无源。对比项有源蜂鸣器无源蜂鸣器内部结构自带震荡源无震荡源发声条件接上额定直流电压就响需要外部方波信号音高变化固定不能变调可随输入频率变化适合场景报警音、提示音播放旋律、音乐门铃Arduino 驱动用数字高电平控制通断用tone()输出方波判断手里蜂鸣器类型可以用最简单的方式把正极接 5V负极接 GND。如果直接长鸣说明是有源蜂鸣器如果只有上电瞬间“咔哒”一声或者完全不响大概率是无源蜂鸣器。这个测试要在蜂鸣器额定电压允许的情况下进行。也可以观察外观常见有源蜂鸣器底部会用黑胶封装震荡电路而无源蜂鸣器底部往往能看到线圈或压电陶瓷片。但不同厂家封装差异很大最可靠的方法是查规格书或者直接做上面的通电测试。如果把有源蜂鸣器接到 UNO 上播放旋律效果通常是固定频率的长鸣听不出音高变化。因为tone()输出的方波频率变化对有源蜂鸣器内部的震荡源没有意义。因此做音乐播放项目时建议直接选用无源蜂鸣器。1.3 频率、音名和时值乐谱如何变成程序数据一个音符由两个关键信息组成音高和时值。音高决定频率时值决定持续多久。以常见的 C 调为例简谱中的1如果对应 C4那么它的频率约 262Hz5对应 G4频率约 392Hz。时值方面如果歌曲速度是 120 BPM四分音符持续 0.5 秒八分音符持续 0.25 秒二分音符持续 1 秒。Arduino 程序里常见做法是把音符频率和节拍存成数组const int melody[] { 262, 294, 330, 392 }; const int beat[] { 4, 4, 4, 4 };melody数组保存频率beat数组保存每个音是几分音符。播放时逐个读取先用tone()发声再延时对应时长最后用noTone()关闭。理解了这层关系后面把《Aoharu》或其他歌曲填进去就只是一个数据转换过程而不是改电路。2. 环境与接线UNO、蜂鸣器和驱动电路怎么搭2.1 硬件清单和两脚蜂鸣器的标准接法准备以下硬件Arduino UNO 或兼容开发板无源蜂鸣器一只建议 5V 规格面包板一块杜邦线若干可选100Ω 到 220Ω 限流电阻可选ULN2003APG 驱动模块用于大功率蜂鸣器普通两脚无源蜂鸣器只有正负两个引脚。长脚通常是正极另一个是负极。标准接法如下蜂鸣器引脚接 UNO正极D8负极GND如果用面包板把蜂鸣器正极通过杜邦线连接到 D8负极连接到 GND。为了限制电流可以在 D8 和蜂鸣器正极之间串联一个 100Ω 到 220Ω 电阻。但要注意串电阻会降低音量如果蜂鸣器额定电压就是 5V 且工作电流较小也可以不串以规格书为准。这里有一个需要注意的地方UNO 的tone()函数在 AVR 平台上会使用定时器而部分 PWM 引脚也依赖定时器。在 Uno 上analogWrite()在 3 号和 11 号引脚上的输出会受tone()影响。如果项目里同时用这两个引脚做 LED 调光或电机调速要留意这个冲突。另外同一时刻只能在一个引脚上调用tone()。如果程序里对第二个引脚调用tone()新的方波不会正常输出。需要先对前一个引脚执行noTone()再切换。2.2 三脚压电陶瓷蜂鸣器的接法要单独确认三脚压电陶瓷蜂鸣器和普通两脚蜂鸣器不一样。它并不是简单地多了一个引脚常见的三脚器件至少有一个脚用于反馈或内部驱动具体功能需要看数据手册和外壳丝印。如果手里是三脚压电陶瓷蜂鸣器先不要猜测引脚按下述步骤处理观察外壳丝印查找标有、-和信号脚或反馈脚的标记。搜索型号对应的数据手册确认哪个引脚接电源哪个引脚接地哪个引脚是信号输入。用 1kΩ 电阻串联在信号线上做保护避免接错时损坏 UNO 引脚。在音乐播放场景中如果三脚器件属于“无源驱动型”可以通过tone()改变频率来变调。但如果它内部已经封装了自激振荡电路通电后只能发出固定频率无法用来演奏旋律。这种情况下它更适合做提示音不适合做点歌播放器。2.3 什么时候需要 ULN2003APG 驱动普通小蜂鸣器UNO 的 GPIO 引脚可以直接驱动。但有些电磁式蜂鸣器或大功率蜂鸣器工作电流可能接近甚至超过 40mA已经超过 UNO 引脚的绝对最大额定电流。此时直接接 GPIO轻则声音失真重则损坏引脚。ULN2003APG 是常用的达林顿管驱动芯片内部包含多路集电极开路输出适合驱动继电器、小电机、电磁阀等负载。用它驱动大电流蜂鸣器时UNO 引脚只需要提供小信号实际工作电流由外部电源提供这样更安全。接线示例如下ULN2003 模块引脚接法IN1接 UNO D8OUT1接蜂鸣器负极蜂鸣器正极接外部 5VGND接 UNO GND共地COM接外部 5V用于续流二极管这里要解释一下ULN2003 的输出是集电极开路形式。当输入为高电平时内部达林顿管导通OUT1 对地导通蜂鸣器形成电流回路输入为低电平时负载断开。tone()输出的方波本质上就是高低电平的交替因此蜂鸣器仍会按设定频率振动。变化的是驱动电流能力不是发声原理。如果蜂鸣器是感性负载比如电磁式蜂鸣器关断瞬间会产生反向电动势。ULN2003 内部已经集成了续流二极管COM 引脚接外部电源正极后可以吸收这部分能量减少对电路的冲击。使用大功率蜂鸣器时还要确保外部电源规格足够不要为了追求音量把蜂鸣器接到超过额定电压的电源上。2.4 Arduino IDE 配置和第一段发声测试在电脑上安装 Arduino IDE开发板选择Arduino Uno端口选择实际识别到的串口。先写一段最小测试代码验证硬件链路是否正常int buzzerPin 8; void setup() { pinMode(buzzerPin, OUTPUT); } void loop() { tone(buzzerPin, 440); delay(1000); noTone(buzzerPin); delay(500); }这段代码让蜂鸣器响 1 秒、停 0.5 秒。如果听到 440Hz 单音说明接线和tone()链路正常。如果无声先检查接线和引脚号。如果发出的是固定长鸣而不是由tone()控制的断续声可能是因为手里的蜂鸣器是有源蜂鸣器。此时可以把它换成正极接 5V、负极接 GND用 GPIO 控制通断来使用但这只能做开关提示无法演奏旋律。3. 把《Aoharu》的旋律转换成蜂鸣器数据3.1 用频率表把简谱转成数字要把简谱转换成 Arduino 可用的数据先要准备一张音符频率表。下面是常见中音区音符频率音名频率C4262 HzD4294 HzE4330 HzF4349 HzG4392 HzA4440 HzB4494 HzC5523 Hz在程序里可以用宏定义提高可读性#define NOTE_C4 262 #define NOTE_D4 294 #define NOTE_E4 330 #define NOTE_F4 349 #define NOTE_G4 392 #define NOTE_A4 440 #define NOTE_B4 494 #define NOTE_C5 523如果需要更高八度可以直接用NOTE_C4 * 2表示 C5但整数乘法要注意溢出。更稳妥的做法是直接查表写下需要的频率宏。转换简谱时要确认调号。如果简谱标的是1C那么1可以对应 C4 或 C5具体取决于歌曲音域。选太高蜂鸣器可能发音发尖选太低可能听不清。可以先选一个八度试听再整体上下调整。3.2 音频数据结构音符、八度和节拍怎么组织最简单的方式是两个数组一个保存频率一个保存节拍const int melody[] { NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4 }; const int beat[] { 4, 4, 4, 4 };beat数组里的4表示四分音符。如果 BPM 是 120四分音符时长是60000 / 120 500ms八分音符时长是 250ms二分音符时长是 1000ms。也可以使用结构体把频率和节拍绑定这样数组元素更清晰struct Note { int frequency; int duration; // 几分音符4 表示四分音符 }; Note song[] { { NOTE_C4, 4 }, { NOTE_D4, 4 }, { NOTE_E4, 4 }, { NOTE_F4, 4 } };结构体数组的优点是每个音符的“音高”和“时值”在一起不容易错位。缺点是稍后如果要放进 Flash 存储读取方式需要多写一点代码。关于节拍计算推荐写一个转换函数int noteToMs(int noteValue, int bpm) { return (int)(60000.0 / bpm * (4.0 / noteValue)); }例如noteToMs(4, 120)
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进