本帖最后由 zoey不种土豆 于 2024-12-31 16:50 编辑  
 
在之前的课程中,我们学会了如何控制蜂鸣器发出不同的声音。那么,我们是否可以通过调整声音的频率和节拍,来组合出一段完整的旋律呢?在这个项目中,我们将使用蜂鸣器演奏音乐,并学习如何创建头文件(.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调音台 |