如何使用Arduino构建一架小而有趣的钢琴

描述

Arduino对于不是电子背景的人来说是一个福音,可以轻松构建东西。这是一个很棒的原型工具,或者尝试一些很酷的东西,在这个项目中,我们将使用 Arduino 构建一架小而有趣的钢琴。这架钢琴非常简单,只有8个按钮和蜂鸣器。它使用Arduino的tone()函数在扬声器上创建各种类型的钢琴音符。为了给它增添趣味,我们在项目中添加了录音功能,这使我们能够播放一首曲子,并在需要时重复播放。听起来很有趣吧!!所以让我们开始建设吧。

所需材料:

Arduino Uno

16 * 2液晶显示器

蜂鸣器

修剪器 10k

单刀双掷开关

按钮 (8 否)

电阻器 (10k, 560R, 1.5k, 2.6k, 3.9, 5.6k, 6.8k, 8.2k, 10k)

面包板

连接线

电路图:

完整的Arduino钢琴项目可以构建在带有一些连接线的面包板上。使用弗里茨制作的电路图显示了项目的试验板视图,如下所示

Arduino

只需按照电路图并相应地连接电线,按钮和蜂鸣器与PCB模块一起使用,但在实际硬件中,我们只使用了开关和蜂鸣器,它不应该让您感到困惑,因为它们具有相同类型的引脚输出。您还可以参考下面的硬件图像进行连接。

Arduino

左起电阻值按以下顺序排列,10k、560R、1.5k、2.6k、3.9、5.6k、6.8k、8.2k 和 10k。如果您没有相同的DPST开关,则可以使用普通的拨动开关,如上面的电路图所示。现在让我们看看项目的原理图,以了解为什么我们进行了以下连接。

原理图和解释:

上面显示的电路图原理图如下,也是使用Fritzing制作的。

Arduino

我们必须了解的一个主要连接是我们如何通过模拟A8引脚将8个按钮连接到Arduino。基本上我们需要 8 个输入引脚,可以连接到 8 个输入按钮,但对于这样的项目,我们不能将微控制器的 8 个引脚仅用于按钮,因为我们可能需要它们供以后使用。在我们的例子中,我们有要接口的LCD显示器。

因此,我们使用Arduino的模拟引脚,并形成具有不同电阻值的电位分压器来完成电路。这样,当按下每个按钮时,将向模拟引脚提供不同的模拟电压。下面显示了仅具有两个电阻和两个按钮的示例电路。

Arduino

在这种情况下,当按钮未按下时,ADC引脚将接收+5V,如果按下第一个按钮,则电位分压器通过560R电阻完成,如果按下第二个按钮,则使用1.5k电阻竞争电位分压器。这样,ADC引脚接收到的电压将根据分压器的公式而变化。如果您想了解更多关于分压器的工作原理以及如何计算ADC引脚接收到的电压值,则可以使用此电位分压器计算器页面。

除此之外,所有连接都是直截了当的,LCD 连接到引脚 8、9、10、11 和 12。蜂鸣器连接到引脚 7,SPDT 开关连接到 Arduino 的引脚 6。整个项目通过笔记本电脑的USB端口供电。您还可以通过直流插孔将Arduino连接到9V或12V电源,项目仍将正常工作。

了解 Arduino 的Tone()函数:

Arduino有一个方便的tone()函数,可用于生成不同的频率信号,这些信号可用于使用蜂鸣器产生不同的声音。因此,让我们了解该功能的工作原理以及如何将其与Arduino一起使用。

在此之前,我们应该知道压电蜂鸣器的工作原理.我们可能在我们学校了解过压电晶体, 它只不过是一种将机械振动转化为电能的晶体,反之亦然.在这里,我们应用一个可变的电流(频率),晶体振动从而产生声音。因此,为了使压电蜂鸣器产生一些噪音,我们必须使压电晶体振动,噪声的音调和音调取决于晶体振动的速度.因此,可以通过改变电流频率来控制音调和音高。

好的,那么我们如何从Arduino获得可变频率呢?这就是音调()函数的用武之地。音调()可以在特定引脚上生成特定频率。如果需要,也可以提及持续时间。音调 () 的语法是

Syntax

tone(pin, frequency) tone(pin, frequency, duration)

Parameters

pin: the pin on which to generate the tone frequency: the frequency of the tone in hertz – unsigned int duration: the duration of the tone in milliseconds (optional1) – unsigned long

引脚的值可以是您的任何数字引脚。我在这里使用了引脚 8。可以生成的频率取决于Arduino板中计时器的大小。对于UNO和大多数其他常见板,可以产生的最小频率为31Hz,可以产生的最大频率为65535Hz。然而,我们人类只能听到2000Hz到5000Hz之间的频率。

在Arduino上弹奏钢琴音调:

好的,在我开始这个话题之前,让我明确一点,我是一个音符或钢琴的新手,所以如果这个标题下提到的任何内容都是胡言乱语,请原谅我。

我们现在知道我们可以使用 Arduino 中的音调功能来产生一些声音,但是我们如何使用相同的音调来播放特定音符的音调。幸运的是,有一个名为“pitches.h”的图书馆,由Brett Hagman编写。该库包含有关哪个频率等同于钢琴上哪个音符的所有信息。我很惊讶这个图书馆实际上可以很好地工作并演奏钢琴上的几乎每个音符,我用同样的东西来弹奏加勒比海盗、疯狂青蛙、马里奥甚至泰坦尼克号的钢琴音符,它们听起来很棒。哎呀!我们在这里有点偏离主题,所以如果你对此感兴趣,请查看使用Arduino项目播放旋律。您还可以在该项目中找到有关pitches.h库的更多解释。

我们的项目只有 8 个按钮,因此每个按钮只能播放一个特定的音符,因此我们总共只能播放 8 个音符。我选择了钢琴上最常用的音符,但是您可以选择任何8个,甚至可以使用更多按钮扩展项目并添加更多音符。

此项目中选择的音符是音符 C4、D4、E4、F4、G4、A4、B4 和 C5,可以分别使用按钮 1 到 8 演奏。

对 Arduino 进行编程:

足够的理论让我们进入Arduino编程的有趣部分。完整的Arduino程序在本页末尾给出,如果您渴望或进一步阅读以了解代码的工作原理,您可以跳下来。

在我们的Arduino程序中,我们必须从引脚A0读取模拟电压,然后预测按下了哪个按钮并播放该按钮的相应音调。在执行此操作时,我们还应该记录用户按下了哪个按钮以及他/她按下了多长时间,以便我们可以重新创建用户稍后播放的音调。

在进入逻辑部分之前,我们必须声明我们将演奏哪 8 个音符。然后从 pitches.h 库中获取音符的相应频率,然后形成如下所示的数组。这里演奏音符C4的频率是262,依此类推。

int notes[] = {262, 294, 330, 349, 392, 440, 494, 523}; // Set frequency for C4, D4, E4, F4, G4, A4, B4,

接下来我们必须提到LCD显示器连接到哪些引脚。如果您遵循上面给出的完全相同的原理图,则无需在此处更改任何内容。

const int rs = 8, en = 9, d4 = 10, d5 = 11, d6 = 12, d7 = 13; //Pins to which LCD is connected

LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

接下来,在我们的设置功能中,我们只需初始化LCD模块和串行监视器以进行调试。我们还会显示介绍消息,以确保事情按计划进行。接下来,在主循环函数中,我们有两个 while 循环。

只要将SPDT开关放置在录制更多内容中,就会执行一个while循环。在录制模式下,用户可以支付所需的音调,同时正在播放的音调也将保存。所以 while 循环如下所示

while (digitalRead(6) == 0) //If the toggle switch is set in recording mode  {    lcd.setCursor(0, 0); lcd.print("Recording..");    lcd.setCursor(0, 1);    Detect_button();    Play_tone();  }

您可能已经注意到,我们在 while 循环中有两个函数。第一个函数Detect_button() 用于查找用户按下的按钮,第二个函数Play_tone()用于播放相应的音调。除了这个函数之外,Detect_button() 函数还记录按下了哪个按钮,Play_tone() 函数记录按下了多长时间。

在Detect_button()函数中,我们从引脚 A0 读取模拟电压,并将其与一些预定义的值进行比较,以找出按下了哪个按钮。该值可以通过使用上面的分压器计算器或使用串行监视器检查每个按钮读取的模拟值来确定。

void Detect_button()

{

analogVal = analogRead(A0); //read the analog voltag on pin A0

pev_button = button; //remember the previous button pressed by the user

if (analogVal < 550)

button = 8;

if (analogVal < 500)

button = 7;

if (analogVal < 450)

button = 6;

if (analogVal < 400)

button = 5;

if (analogVal < 300)

button = 4;

if (analogVal < 250)

button = 3;

if (analogVal < 150)

button = 2;

if (analogVal < 100)

button = 1;

if (analogVal > 1000)

button = 0;

 

/****Rcord the pressed buttons in a array***/

if (button != pev_button && pev_button != 0)

{

recorded_button[button_index] = pev_button;

button_index++;

recorded_button[button_index] = 0;

button_index++;

}

/**End of Recording program**/

如前所述,在此功能中,我们还记录按下按钮的顺序。记录的值存储在名为 recorded_button[] 的数组中。我们首先检查是否按下了新按钮,如果按下,我们还检查它是否不是按钮 0。其中按钮 0 什么都没有,但没有按下按钮。在 if 循环中,我们将值存储在变量 button_index 给出的索引位置上,然后我们还增加此索引值,这样我们就不会在同一位置过度写入。

/****Rcord the pressed buttons in a array***/

if (button != pev_button && pev_button != 0)

{

recorded_button[button_index] = pev_button;

button_index++;

recorded_button[button_index] = 0;

button_index++;

}

/**End of Recording program**/

在Play_tone()函数中,我们将使用多个 if 条件播放按下按钮的相应音调。我们还将使用一个名为 recorded_time[] 的数组,在其中我们将保存按下按钮的持续时间。该操作类似于记录按钮序列,我们使用 millis() 函数来确定每个按钮被按下了多长时间,也是为了减小变量的大小,我们将值除以 10。对于按钮 0,这意味着用户没有按下任何内容,我们在相同的持续时间内没有播放任何音调。函数内的完整代码如下所示。

void Play_tone()

{

/****Rcord the time delay between each button press in a array***/

if (button != pev_button)

{

lcd.clear(); //Then clean it

note_time = (millis() - start_time) / 10;

recorded_time[time_index] = note_time;

time_index++;

start_time = millis();

}

/**End of Recording program**/

if (button == 0)

{

noTone(7);

lcd.print("0 -> Pause..");

}

if (button == 1)

{

tone(7, notes[0]);

lcd.print("1 -> NOTE_C4");

}

if (button == 2)

{

tone(7, notes[1]);

lcd.print("2 -> NOTE_D4");

}

if (button == 3)

{

tone(7, notes[2]);

lcd.print("3 -> NOTE_E4");

}

if (button == 4)

{

tone(7, notes[3]);

lcd.print("4 -> NOTE_F4");

}

if (button == 5)

{

tone(7, notes[4]);

lcd.print("5 -> NOTE_G4");

}

if (button == 6)

{

tone(7, notes[5]);

lcd.print("6 -> NOTE_A4");

}

if (button == 7)

{

tone(7, notes[6]);

lcd.print("7 -> NOTE_B4");

}

if (button == 8)

{

tone(7, notes[7]);

lcd.print("8 -> NOTE_C5");

}

}

最后,在录制后,用户必须将DPST切换到另一个方向才能播放录制的音调。完成此操作后,程序将脱离上一个 while 循环并进入第二个 while 循环,我们按照按下的按钮顺序播放音符,持续时间为先前录制的持续时间。执行相同操作的代码如下所示。

while (digitalRead(6) == 1) //If the toggle switch is set in Playing mode

{

lcd.clear();

lcd.setCursor(0, 0);  lcd.print("Now Playing..");

for (int i = 0; i < sizeof(recorded_button) / 2; i++)

{

delay((recorded_time[i]) * 10); //Wait for before paying next tune

if (recorded_button[i] == 0)

noTone(7); //user dint touch any button

else

tone(7, notes[(recorded_button[i] - 1)]); //play the sound corresponding to the button touched by the user

}

}

}

播放,录制,重播和重复!:

按照所示电路图制作硬件,并将代码上传到Arduino板及其显示的时间。将SPDT置于录制模式并开始播放您选择的音调,按下每个按钮将产生不同的音调。在此模式下,LCD将显示“正在录制...”,在第二行,您将看到当前正在按下的笔记的名称,如下所示

Arduino

播放完音调后,将SPDT开关切换到另一侧,液晶屏应显示“正在播放....”,然后开始播放刚刚播放的音调。只要拨动开关保持在下图所示的位置,就会一遍又一遍地播放相同的音调。

Arduino

/*

Arduino based Piano and Record and play option

Code by: B. Aswinth Raj

Dated: 22-05-2017

*/


#include


int notes[] = {262, 294, 330, 349, 392, 440, 494, 523}; // Set frequency for C4, D4, E4, F4, G4, A4, B4, C5



const int rs = 8, en = 9, d4 = 10, d5 = 11, d6 = 12, d7 = 13; //Pins to which LCD is connected

LiquidCrystal lcd(rs, en, d4, d5, d6, d7);


char button = 0;

int analogVal;

char REC = 0;


int recorded_button[200];

int pev_button;


int recorded_time [200];

char time_index;


char button_index = 0;


unsigned long start_time;

int note_time;



void setup() {


Serial.begin(9600);

pinMode (6, INPUT);


lcd.begin(16, 2); //We are using a 16*2 LCD display

lcd.print("Arduino Piano"); //Display a intro message

lcd.setCursor(0, 1); // set the cursor to column 0, line 1

lcd.print("-CircuitDigest"); //Display a intro message


delay(2000); //Wait for display to show info

lcd.clear(); //Then clean it


}





void loop() 

{


while (digitalRead(6) == 0) //If the toggle switch is set in recording mode

{

lcd.setCursor(0, 0); lcd.print("Recording..");

lcd.setCursor(0, 1);


Detect_button();

Play_tone();

}




while (digitalRead(6) == 1) //If the toggle switch is set in Playing mode

{

lcd.clear();

lcd.setCursor(0, 0); lcd.print("Now Playing..");


for (int i = 0; i < sizeof(recorded_button) / 2; i++)

{

delay((recorded_time[i]) * 10); //Wait for before paying next tune


if (recorded_button[i] == 0)

noTone(7); //user dint touch any button

else

tone(7, notes[(recorded_button[i] - 1)]); //play the sound corresponding to the button touched by the user

}

}

}





void Detect_button()

{

analogVal = analogRead(A0); //read the analog voltag on pin A0


pev_button = button; //remember the previous button pressed by the user


if (analogVal < 550)

button = 8;


if (analogVal < 500)

button = 7;


if (analogVal < 450)

button = 6;


if (analogVal < 400)

button = 5;


if (analogVal < 300)

button = 4;


if (analogVal < 250)

button = 3;


if (analogVal < 150)

button = 2;


if (analogVal < 100)

button = 1;


if (analogVal > 1000)

button = 0;




/****Rcord the pressed buttons in a array***/

if (button != pev_button && pev_button != 0)

{

recorded_button[button_index] = pev_button; 

button_index++;

recorded_button[button_index] = 0;

button_index++;

}

/**End of Recording program**/

}



void Play_tone()

{


/****Rcord the time delay between each button press in a array***/

if (button != pev_button)

{

lcd.clear(); //Then clean it

note_time = (millis() - start_time) / 10;


recorded_time[time_index] = note_time;

time_index++;


start_time = millis();

}

/**End of Recording program**/


if (button == 0)

{

noTone(7);

lcd.print("0 -> Pause..");

}


if (button == 1)

{

tone(7, notes[0]);

lcd.print("1 -> NOTE_C4");

}


if (button == 2)

{

tone(7, notes[1]);

lcd.print("2 -> NOTE_D4");

}


if (button == 3)

{

tone(7, notes[2]);

lcd.print("3 -> NOTE_E4");

}


if (button == 4)

{

tone(7, notes[3]);

lcd.print("4 -> NOTE_F4");

}


if (button == 5)

{

tone(7, notes[4]);

lcd.print("5 -> NOTE_G4");

}


if (button == 6)

{

tone(7, notes[5]);

lcd.print("6 -> NOTE_A4");

}


if (button == 7)

{

tone(7, notes[6]);

lcd.print("7 -> NOTE_B4");

}


if (button == 8)

{

tone(7, notes[7]);

lcd.print("8 -> NOTE_C5");

}

}


 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分