本帖最后由 zoey不种土豆 于 2024-11-19 17:36 编辑
在之前的课程中,我们学会了如何控制蜂鸣器发出不同的声音。那么,我们是否可以通过调整声音的频率和节拍,来组合出一段完整的旋律呢?在这个项目中,我们将使用蜂鸣器演奏音乐,并学习如何创建头文件(.h文件),以导入别人已经编写好的“曲谱”。 在使用Arduino IDE编程时,每创建一个项目文件被称为一个“Sketch”。一个Sketch中可以包含多个代码文件。为了避免程序代码过长且不易阅读,可以在主文件中编写程序的主要逻辑部分,而将程序的独立功能模块存放在其他文件中,以便于管理和维护。这种做法不仅提高了代码的可读性,还便于功能的复用和扩展。
元件清单
硬件连接按如下连线图连接,注意蜂鸣器引脚正(+),负(-)。标有+的引脚接到数字口8,另一个接GND。蜂鸣器的正负极通过盖子上的+符号查看。 图 1 音乐蜂鸣器连线图
示例代码样例代码 - Main Sketch:
- //项目 - 音乐蜂鸣器
- #include "pitches.h" // 包含一个头文件
-
- int melody[] = {
- NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3, 0, NOTE_B3, NOTE_C4
- }; // 旋律中的音符序列
-
- int noteTypes[] = {
- 4, 8, 8, 4, 4, 4, 4, 4
- }; // 每个音符的类型:4 = 四分音符,8 = 八分音符
-
- void setup() {
-
- for (int thisNote = 0; thisNote < 8; thisNote++) {
- int noteDuration = 1000 / noteTypes[thisNote];
- // 计算音符的持续时间:1秒 / 音符类型
- tone(8, melody[thisNote], noteDuration);
- // 在引脚8上播放音符
- int pauseBetweenNotes = noteDuration * 1.30;
- // 根据音符持续时间计算暂停时间,使旋律听起来更自然
- delay(pauseBetweenNotes); // 音符之间的暂停
- noTone(8); // 停止在引脚8上播放的音调
- }
- }
-
- void loop() {
- // 不需要重复播放旋律
- }
复制代码
要创建一个新的头文件,点击串口监视器下方的按钮,选择新建标签页,输入文件名pitches.h Pitches.h: - #define NOTE_B0 31
- #define NOTE_C1 33
- #define NOTE_CS1 35
- #define NOTE_D1 37
- #define NOTE_DS1 39
- #define NOTE_E1 41
- #define NOTE_F1 44
- #define NOTE_FS1 46
- #define NOTE_G1 49
- #define NOTE_GS1 52
- #define NOTE_A1 55
- #define NOTE_AS1 58
- #define NOTE_B1 62
- #define NOTE_C2 65
- #define NOTE_CS2 69
- #define NOTE_D2 73
- #define NOTE_DS2 78
- #define NOTE_E2 82
- #define NOTE_F2 87
- #define NOTE_FS2 93
- #define NOTE_G2 98
- #define NOTE_GS2 104
- #define NOTE_A2 110
- #define NOTE_AS2 117
- #define NOTE_B2 123
- #define NOTE_C3 131
- #define NOTE_CS3 139
- #define NOTE_D3 147
- #define NOTE_DS3 156
- #define NOTE_E3 165
- #define NOTE_F3 175
- #define NOTE_FS3 185
- #define NOTE_G3 196
- #define NOTE_GS3 208
- #define NOTE_A3 220
- #define NOTE_AS3 233
- #define NOTE_B3 247
- #define NOTE_C4 262
- #define NOTE_CS4 277
- #define NOTE_D4 294
- #define NOTE_DS4 311
- #define NOTE_E4 330
- #define NOTE_F4 349
- #define NOTE_FS4 370
- #define NOTE_G4 392
- #define NOTE_GS4 415
- #define NOTE_A4 440
- #define NOTE_AS4 466
- #define NOTE_B4 494
- #define NOTE_C5 523
- #define NOTE_CS5 554
- #define NOTE_D5 587
- #define NOTE_DS5 622
- #define NOTE_E5 659
- #define NOTE_F5 698
- #define NOTE_FS5 740
- #define NOTE_G5 784
- #define NOTE_GS5 831
- #define NOTE_A5 880
- #define NOTE_AS5 932
- #define NOTE_B5 988
- #define NOTE_C6 1047
- #define NOTE_CS6 1109
- #define NOTE_D6 1175
- #define NOTE_DS6 1245
- #define NOTE_E6 1319
- #define NOTE_F6 1397
- #define NOTE_FS6 1480
- #define NOTE_G6 1568
- #define NOTE_GS6 1661
- #define NOTE_A6 1760
- #define NOTE_AS6 1865
- #define NOTE_B6 1976
- #define NOTE_C7 2093
- #define NOTE_CS7 2217
- #define NOTE_D7 2349
- #define NOTE_DS7 2489
- #define NOTE_E7 2637
- #define NOTE_F7 2794
- #define NOTE_FS7 2960
- #define NOTE_G7 3136
- #define NOTE_GS7 3322
- #define NOTE_A7 3520
- #define NOTE_AS7 3729
- #define NOTE_B7 3951
- #define NOTE_C8 4186
- #define NOTE_CS8 4435
- #define NOTE_D8 4699
- #define NOTE_DS8 4978
复制代码
代码回顾在这个项目中,我们新建了pitches.h文件,并复制了一段代码。从项目【温度报警器】中我们了解到,不同的频率会产生不同的音调。而我们复制的这段代码是最初由 Brett Hagman 编写的音符表,包含了所有典型音符所对应的频率。比如,NOTE_C4 是中央C音,对应的频率是262。有了这张音符表,我们就可以通过输入音符的名称来编写旋律了。 在Main Sketch中使用了#include语法来导入了新建的文件或外部库,将不同的文件关联起来。 在程序中,我们首先创建了两个数组来按顺序储存音调与节奏。 - int melody[] = {
- NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3, 0, NOTE_B3, NOTE_C4
- };
- int noteTypes[] = {
- 4, 8, 8, 4, 4, 4, 4, 4
- };
复制代码
上面这个数组noteTypes[] 定义了每个音符的持续时间。这里使用的是一种简化的表示方法,其中数字代表音符的“类型”,而不是确切的毫秒数。例如,4代表四分音符,8代表八分音符。实际的持续时间将在setup()函数中计算得出。
数组是一个变量的集合,可以通过索引号来找到数组中的元素。可以看出我们创建的数组中分别有8个元素。数组是从0开始索引的,因此数组中的8个元素的索引号是0~7。
在本程序中旋律只会播放一次,因此我们在setup里编写播放音符的程序。
通过一个for循环,来遍历数组中的每一个元素,通过变量thisNote表示目前的元素索引号。
- for (int thisNote = 0; thisNote < 8; thisNote++) {
- int noteDuration = 1000 / noteTypes[thisNote];
- // 计算音符的持续时间:1秒 / 音符类型
- tone(8, melody[thisNote], noteDuration);
- // 在引脚8上播放音符
- int pauseBetweenNotes = noteDuration * 1.30;
- // 根据音符持续时间计算暂停时间,使旋律听起来更自然
-
- delay(pauseBetweenNotes); // 音符之间的暂停
- noTone(8); // 停止在引脚8上播放的音调
- }
复制代码
在循环中,首先计算了音符的持续时间。在音乐的定义中,四分音符占一个小节的1/4,因此可以通过小节时间(这里设置的是1秒)除以4来计算每个音符的持续时间。
然后使用tone()函数设置引脚,frequency和duration,来播放音符。
每个音符中间需要有一定的间隔才能够让我们方便区分,因此需要在音符之间设置延迟。间隔时间根据音符持续时间调整会让旋律更加流畅,用noteDuration * 1.30会得到不错的效果,你也可以试试设置delay(200) 的固定时间,来看看效果会有什么不一样。
在音符播放完,使用noTone让声音停止。
硬件回顾
Arduino开发板通常都配备了一个复位键(Reset Button),这个按键在多个方面都非常有用,特别是在编程、调试和重新启动Arduino时。复位键的主要功能是使Arduino板上的微控制器(如ATmega系列)重置其状态,类似于计算机上的重启操作,但针对的是微控制器本身。
Arduino UNO的复位键通常位于开发板的左上角(如图2),靠近USB接口或其他I/O接口。它是一个小按钮,旁边标有“RESET”字样。同样,你也可以使用扩展板上的复位按键来重启Arduino里面的程序。
图 2 Arduino UNO 复位键位置
图 3 扩展板复位键位置
示例代码
课后练习
你有没有熟悉的旋律想要通过蜂鸣器来播放呢?比如小星星或者玛丽有只小羊羔?来试试改变数组中的音符和节奏吧。别忘了改变for循环中的数量哦。
下一课:Arduino入门教程17--DJ调音台 |