Arduino對(duì)于不是電子背景的人來(lái)說是一個(gè)福音,可以輕松構(gòu)建東西。這是一個(gè)很棒的原型工具,或者嘗試一些很酷的東西,在這個(gè)項(xiàng)目中,我們將使用 Arduino 構(gòu)建一架小而有趣的鋼琴。這架鋼琴非常簡(jiǎn)單,只有8個(gè)按鈕和蜂鳴器。它使用Arduino的tone()函數(shù)在揚(yáng)聲器上創(chuàng)建各種類型的鋼琴音符。為了給它增添趣味,我們?cè)陧?xiàng)目中添加了錄音功能,這使我們能夠播放一首曲子,并在需要時(shí)重復(fù)播放。聽起來(lái)很有趣吧??!所以讓我們開始建設(shè)吧。
所需材料:
Arduino Uno
16 * 2液晶顯示器
蜂鳴器
修剪器 10k
單刀雙擲開關(guān)
按鈕 (8 否)
電阻器 (10k, 560R, 1.5k, 2.6k, 3.9, 5.6k, 6.8k, 8.2k, 10k)
面包板
連接線
電路圖:
完整的Arduino鋼琴項(xiàng)目可以構(gòu)建在帶有一些連接線的面包板上。使用弗里茨制作的電路圖顯示了項(xiàng)目的試驗(yàn)板視圖,如下所示
只需按照電路圖并相應(yīng)地連接電線,按鈕和蜂鳴器與PCB模塊一起使用,但在實(shí)際硬件中,我們只使用了開關(guān)和蜂鳴器,它不應(yīng)該讓您感到困惑,因?yàn)樗鼈兙哂邢嗤愋偷囊_輸出。您還可以參考下面的硬件圖像進(jìn)行連接。
左起電阻值按以下順序排列,10k、560R、1.5k、2.6k、3.9、5.6k、6.8k、8.2k 和 10k。如果您沒有相同的DPST開關(guān),則可以使用普通的撥動(dòng)開關(guān),如上面的電路圖所示?,F(xiàn)在讓我們看看項(xiàng)目的原理圖,以了解為什么我們進(jìn)行了以下連接。
原理圖和解釋:
上面顯示的電路圖原理圖如下,也是使用Fritzing制作的。
我們必須了解的一個(gè)主要連接是我們?nèi)绾瓮ㄟ^模擬A8引腳將8個(gè)按鈕連接到Arduino?;旧衔覀冃枰?8 個(gè)輸入引腳,可以連接到 8 個(gè)輸入按鈕,但對(duì)于這樣的項(xiàng)目,我們不能將微控制器的 8 個(gè)引腳僅用于按鈕,因?yàn)槲覀兛赡苄枰鼈児┮院笫褂?。在我們的例子中,我們有?a target="_blank">接口的LCD顯示器。
因此,我們使用Arduino的模擬引腳,并形成具有不同電阻值的電位分壓器來(lái)完成電路。這樣,當(dāng)按下每個(gè)按鈕時(shí),將向模擬引腳提供不同的模擬電壓。下面顯示了僅具有兩個(gè)電阻和兩個(gè)按鈕的示例電路。
在這種情況下,當(dāng)按鈕未按下時(shí),ADC引腳將接收+5V,如果按下第一個(gè)按鈕,則電位分壓器通過560R電阻完成,如果按下第二個(gè)按鈕,則使用1.5k電阻競(jìng)爭(zhēng)電位分壓器。這樣,ADC引腳接收到的電壓將根據(jù)分壓器的公式而變化。如果您想了解更多關(guān)于分壓器的工作原理以及如何計(jì)算ADC引腳接收到的電壓值,則可以使用此電位分壓器計(jì)算器頁(yè)面。
除此之外,所有連接都是直截了當(dāng)?shù)模琇CD 連接到引腳 8、9、10、11 和 12。蜂鳴器連接到引腳 7,SPDT 開關(guān)連接到 Arduino 的引腳 6。整個(gè)項(xiàng)目通過筆記本電腦的USB端口供電。您還可以通過直流插孔將Arduino連接到9V或12V電源,項(xiàng)目仍將正常工作。
了解 Arduino 的Tone()函數(shù):
Arduino有一個(gè)方便的tone()函數(shù),可用于生成不同的頻率信號(hào),這些信號(hào)可用于使用蜂鳴器產(chǎn)生不同的聲音。因此,讓我們了解該功能的工作原理以及如何將其與Arduino一起使用。
在此之前,我們應(yīng)該知道壓電蜂鳴器的工作原理.我們可能在我們學(xué)校了解過壓電晶體, 它只不過是一種將機(jī)械振動(dòng)轉(zhuǎn)化為電能的晶體,反之亦然.在這里,我們應(yīng)用一個(gè)可變的電流(頻率),晶體振動(dòng)從而產(chǎn)生聲音。因此,為了使壓電蜂鳴器產(chǎn)生一些噪音,我們必須使壓電晶體振動(dòng),噪聲的音調(diào)和音調(diào)取決于晶體振動(dòng)的速度.因此,可以通過改變電流頻率來(lái)控制音調(diào)和音高。
好的,那么我們?nèi)绾螐腁rduino獲得可變頻率呢?這就是音調(diào)()函數(shù)的用武之地。音調(diào)()可以在特定引腳上生成特定頻率。如果需要,也可以提及持續(xù)時(shí)間。音調(diào) () 的語(yǔ)法是
Syntax
tone(pin, frequency) tone(pin, frequency, duration)
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
引腳的值可以是您的任何數(shù)字引腳。我在這里使用了引腳 8??梢陨傻念l率取決于Arduino板中計(jì)時(shí)器的大小。對(duì)于UNO和大多數(shù)其他常見板,可以產(chǎn)生的最小頻率為31Hz,可以產(chǎn)生的最大頻率為65535Hz。然而,我們?nèi)祟愔荒苈牭?000Hz到5000Hz之間的頻率。
在Arduino上彈奏鋼琴音調(diào):
好的,在我開始這個(gè)話題之前,讓我明確一點(diǎn),我是一個(gè)音符或鋼琴的新手,所以如果這個(gè)標(biāo)題下提到的任何內(nèi)容都是胡言亂語(yǔ),請(qǐng)?jiān)徫摇?/p>
我們現(xiàn)在知道我們可以使用 Arduino 中的音調(diào)功能來(lái)產(chǎn)生一些聲音,但是我們?nèi)绾问褂孟嗤囊粽{(diào)來(lái)播放特定音符的音調(diào)。幸運(yùn)的是,有一個(gè)名為“pitches.h”的圖書館,由Brett Hagman編寫。該庫(kù)包含有關(guān)哪個(gè)頻率等同于鋼琴上哪個(gè)音符的所有信息。我很驚訝這個(gè)圖書館實(shí)際上可以很好地工作并演奏鋼琴上的幾乎每個(gè)音符,我用同樣的東西來(lái)彈奏加勒比海盜、瘋狂青蛙、馬里奧甚至泰坦尼克號(hào)的鋼琴音符,它們聽起來(lái)很棒。哎呀!我們?cè)谶@里有點(diǎn)偏離主題,所以如果你對(duì)此感興趣,請(qǐng)查看使用Arduino項(xiàng)目播放旋律。您還可以在該項(xiàng)目中找到有關(guān)pitches.h庫(kù)的更多解釋。
我們的項(xiàng)目只有 8 個(gè)按鈕,因此每個(gè)按鈕只能播放一個(gè)特定的音符,因此我們總共只能播放 8 個(gè)音符。我選擇了鋼琴上最常用的音符,但是您可以選擇任何8個(gè),甚至可以使用更多按鈕擴(kuò)展項(xiàng)目并添加更多音符。
此項(xiàng)目中選擇的音符是音符 C4、D4、E4、F4、G4、A4、B4 和 C5,可以分別使用按鈕 1 到 8 演奏。
對(duì) Arduino 進(jìn)行編程:
足夠的理論讓我們進(jìn)入Arduino編程的有趣部分。完整的Arduino程序在本頁(yè)末尾給出,如果您渴望或進(jìn)一步閱讀以了解代碼的工作原理,您可以跳下來(lái)。
在我們的Arduino程序中,我們必須從引腳A0讀取模擬電壓,然后預(yù)測(cè)按下了哪個(gè)按鈕并播放該按鈕的相應(yīng)音調(diào)。在執(zhí)行此操作時(shí),我們還應(yīng)該記錄用戶按下了哪個(gè)按鈕以及他/她按下了多長(zhǎng)時(shí)間,以便我們可以重新創(chuàng)建用戶稍后播放的音調(diào)。
在進(jìn)入邏輯部分之前,我們必須聲明我們將演奏哪 8 個(gè)音符。然后從 pitches.h 庫(kù)中獲取音符的相應(yīng)頻率,然后形成如下所示的數(shù)組。這里演奏音符C4的頻率是262,依此類推。
int notes[] = {262, 294, 330, 349, 392, 440, 494, 523}; // Set frequency for C4, D4, E4, F4, G4, A4, B4,
接下來(lái)我們必須提到LCD顯示器連接到哪些引腳。如果您遵循上面給出的完全相同的原理圖,則無(wú)需在此處更改任何內(nèi)容。
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);
接下來(lái),在我們的設(shè)置功能中,我們只需初始化LCD模塊和串行監(jiān)視器以進(jìn)行調(diào)試。我們還會(huì)顯示介紹消息,以確保事情按計(jì)劃進(jìn)行。接下來(lái),在主循環(huán)函數(shù)中,我們有兩個(gè) while 循環(huán)。
只要將SPDT開關(guān)放置在錄制更多內(nèi)容中,就會(huì)執(zhí)行一個(gè)while循環(huán)。在錄制模式下,用戶可以支付所需的音調(diào),同時(shí)正在播放的音調(diào)也將保存。所以 while 循環(huán)如下所示
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(); }
您可能已經(jīng)注意到,我們?cè)?while 循環(huán)中有兩個(gè)函數(shù)。第一個(gè)函數(shù)Detect_button() 用于查找用戶按下的按鈕,第二個(gè)函數(shù)Play_tone()用于播放相應(yīng)的音調(diào)。除了這個(gè)函數(shù)之外,Detect_button() 函數(shù)還記錄按下了哪個(gè)按鈕,Play_tone() 函數(shù)記錄按下了多長(zhǎng)時(shí)間。
在Detect_button()函數(shù)中,我們從引腳 A0 讀取模擬電壓,并將其與一些預(yù)定義的值進(jìn)行比較,以找出按下了哪個(gè)按鈕。該值可以通過使用上面的分壓器計(jì)算器或使用串行監(jiān)視器檢查每個(gè)按鈕讀取的模擬值來(lái)確定。
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**/
}
如前所述,在此功能中,我們還記錄按下按鈕的順序。記錄的值存儲(chǔ)在名為 recorded_button[] 的數(shù)組中。我們首先檢查是否按下了新按鈕,如果按下,我們還檢查它是否不是按鈕 0。其中按鈕 0 什么都沒有,但沒有按下按鈕。在 if 循環(huán)中,我們將值存儲(chǔ)在變量 button_index 給出的索引位置上,然后我們還增加此索引值,這樣我們就不會(huì)在同一位置過度寫入。
/****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()函數(shù)中,我們將使用多個(gè) if 條件播放按下按鈕的相應(yīng)音調(diào)。我們還將使用一個(gè)名為 recorded_time[] 的數(shù)組,在其中我們將保存按下按鈕的持續(xù)時(shí)間。該操作類似于記錄按鈕序列,我們使用 millis() 函數(shù)來(lái)確定每個(gè)按鈕被按下了多長(zhǎng)時(shí)間,也是為了減小變量的大小,我們將值除以 10。對(duì)于按鈕 0,這意味著用戶沒有按下任何內(nèi)容,我們?cè)谙嗤某掷m(xù)時(shí)間內(nèi)沒有播放任何音調(diào)。函數(shù)內(nèi)的完整代碼如下所示。
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切換到另一個(gè)方向才能播放錄制的音調(diào)。完成此操作后,程序?qū)⒚撾x上一個(gè) while 循環(huán)并進(jìn)入第二個(gè) while 循環(huán),我們按照按下的按鈕順序播放音符,持續(xù)時(shí)間為先前錄制的持續(xù)時(shí)間。執(zhí)行相同操作的代碼如下所示。
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
}
}
}
播放,錄制,重播和重復(fù)!:
按照所示電路圖制作硬件,并將代碼上傳到Arduino板及其顯示的時(shí)間。將SPDT置于錄制模式并開始播放您選擇的音調(diào),按下每個(gè)按鈕將產(chǎn)生不同的音調(diào)。在此模式下,LCD將顯示“正在錄制...”,在第二行,您將看到當(dāng)前正在按下的筆記的名稱,如下所示
播放完音調(diào)后,將SPDT開關(guān)切換到另一側(cè),液晶屏應(yīng)顯示“正在播放....”,然后開始播放剛剛播放的音調(diào)。只要撥動(dòng)開關(guān)保持在下圖所示的位置,就會(huì)一遍又一遍地播放相同的音調(diào)。
/*
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");
}
}
-
揚(yáng)聲器
+關(guān)注
關(guān)注
29文章
1307瀏覽量
63085 -
蜂鳴器
+關(guān)注
關(guān)注
12文章
892瀏覽量
46003 -
Arduino
+關(guān)注
關(guān)注
188文章
6472瀏覽量
187327
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論