具有录制和重放功能的基于Arduino的钢琴

原文:https://circuitdigest.com/microcontroller-projects/arduino-piano-with-record-and-playback

具有录制和重放功能的基于Arduino的钢琴

ARDUINO项目

经过**阿斯温斯·拉吉(Aswinth Raj)** 2018年6月12日
在这里插入图片描述

Arduino对于那些不具备电子学背景的人来说,轻松构建东西是一个福音。它曾经是一个很棒的原型制作工具,或者尝试一些很棒的东西,在这个项目中,我们将使用Arduino来构建一个小型而有趣的钢琴。这架钢琴只有8个按钮和蜂鸣器,非常普通。它使用Arduino的*tone()*函数在扬声器上创建各种类型的钢琴音符。为了给它增添些许趣味,我们在项目中添加了录音功能,这使我们能够播放音乐并将其录制下来,并在需要时重复播放。听起来很有趣吧!因此,让我们开始构建…。

所需材料:

  • Arduino Uno
  • 16 * 2 LCD显示屏
  • 蜂鸣器
  • 微调10k
  • SPDT开关
  • 按钮(8个)
  • 电阻器(10k,560R,1.5k,2.6k,3.9,5.6k,6.8k,8.2k,10k)
  • 面包板
  • 连接线

电路图:

完整的Arduino钢琴项目可以建立在带有一些连接线的面包板上。下图显示了使用fritzing制作的电路图,该电路图显示了项目的试验板视图
在这里插入图片描述

只需按照电路图并相应地连接电线,与PCB模块一起使用的按钮和蜂鸣器即可,但是在实际硬件中,我们仅使用了开关和蜂鸣器,它不会给您造成太大的困扰,因为它们具有相同的引脚类型。您也可以参考下面的硬件图像进行连接。
在这里插入图片描述
左起的电阻值按以下顺序排列:10k,560R,1.5k,2.6k,3.9、5.6k,6.8k,8.2k和10k。如果没有相同的DPST开关,则可以使用普通的拨动开关,如上面的电路图所示。现在,让我们看一下项目的示意图,以了解为什么建立以下连接。

原理图和说明:

下面给出了上面显示的电路图的原理图,它也是使用Fritzing制作的。
在这里插入图片描述

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

因此,我们使用Arduino的模拟引脚并形成一个电阻值可变的分压器以完成电路。这样,当每个按钮被按下时,将向模拟引脚提供不同的模拟电压。仅显示两个电阻和两个按钮的示例电路如下所示。
在这里插入图片描述
在这种情况下,当未按下按钮时,ADC引脚将接收+ 5V电压;如果按下第一个按钮,则分压器通过560R电阻器完成;如果按下第二个按钮,则分压器将使用1.5竞争。 k电阻。这样,ADC引脚接收的电压将根据分压器的公式而变化。如果您想进一步了解分压器的工作原理以及如何计算ADC引脚接收到的电压值,则可以使用此分压器计算器页面。

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

了解 Arduino的 *Tone()*函数:

Arduino具有方便的tone()函数,可用于生成变化的频率信号,该信号可用于通过蜂鸣器产生不同的声音。因此,让我们了解该函数如何工作以及如何与Arduino一起使用。

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

好的,我们如何从Arduino获得可变频率?这是使用 tone()函数 的地方。tone()可以在特定的引脚上生成特定的频率。如果需要,还可以提及持续时间。tone()的语法是

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至5000 Hz之间的频率。

在Arduino上弹钢琴音:

好的,在我开始这个话题之前,请先明确我是音符或钢琴的新手,因此,如果本标题下提及的任何内容令人讨厌,请原谅我。

现在我们知道可以在Arduino中使用音调功能来产生一些声音,但是如何使用相同的音调来演奏特定音符的音调。对我们来说幸运的是,有一个由布雷特·哈格曼Brett Hagman)编写的名为“ pitches.h”图书馆**。**该库包含有关哪个频率等于钢琴上的哪个音符的所有信息。我很惊讶这个库实际上可以很好地工作并弹奏钢琴上的每个音符,我用它来演奏加勒比海盗,疯狂青蛙,马里奥甚至泰坦尼克号的钢琴音符,它们听起来很棒。糟糕!我们在这里没有话题,因此,如果您对此感兴趣,可以使用Arduino项目弹奏旋律。您还将找到有关pitches.h的更多说明。 该项目中的库。

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

在该项目中选择的音符音符C4,D4,E4,F4,G4,A4,B4和C5,可以分别使用按钮1至8进行演奏。

编程Arduino:

足够的理论知识让我们开始进行Arduino编程的有趣部分。在完整的Arduino计划在本页面末尾给出,如果你渴望你能跳了下去或者进一步阅读,以了解如何代码工作。

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

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

int notes [] = {262,294,330,349,392,440,494,523}; //设置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(digitalRead(6)== 0)//如果拨动开关设置为录制模式
  {
    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置于录音模式并开始播放您选择的音调,按每个按钮将产生不同的音调。在此模式下,液晶显示屏将显示“ Recording …”,在第二行中,您将看到当前按下的音符名称,如下所示
在这里插入图片描述

弹奏完音后,将SPDT开关拨到另一侧,液晶显示屏应显示“ Now Play …”,然后开始播放刚播放的音调。只要将拨动开关保持在下图所示的位置,就会一次又一次地播放相同的音调。
在这里插入图片描述
该项目的完整工作可以在下面的视频中找到。希望您理解该项目并喜欢构建它。如果您在构建此文档时遇到任何问题,请在评论部分中发布它们,或使用论坛获取有关您项目的技术帮助。另外,请不要忘记查看下面的演示视频

代码

/*
   Arduino based Piano and Record and play option
   Code by: B. Aswinth Raj
   Website: www.circuitdigest.com
   Dated: 22-05-2017
*/

#include <LiquidCrystal.h>

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");
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值