2012/02/19

Arduino練習:霹靂車燈

之前以PWM製作會呼吸的LED以揚聲器演奏青花瓷,這一篇要用16個LED,製作出左右來回跑的效果,也就是霹靂車燈。

夥計,我需要你!


我將先用Arduino的16個腳位,控制16個LED,然後再加入兩顆74HC595,就只需3個腳位即可。

電路圖(Fritzing格式)與程式原始碼,可在此下載

電路圖:
16個220 ohm電阻,一端接地,另一端接到LED的短腳。
從Arduino板子的腳位A3、A2、A1、A0、2、3、4、5、6、7、8、9、10、11、12、13,分別接到LED的長腳。



程式碼:
#define NUM 16 // 首先定義LED的數目

// 然後是LED對應的腳位,請配合接線順序填寫。
int leds[NUM] = {
  A3, A2, A1, A0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
};

// 將每個腳位模式設為OUTPUT
void setup() {               
  for(int i = 0; i < NUM; i++){
    pinMode(leds[i], OUTPUT);
  }    
}
// 雖然A3、A2、A1、A0是類比腳位,
// 但也可以把它當做數位腳位使用。

// 然後是兩個迴圈,
// 第一個迴圈從這邊跑到那邊,
// 第二個迴圈跑回來。
void loop() {
  for(int i = 0; i < NUM; i++){
    digitalWrite(leds[i], HIGH);
    delay(100);
    digitalWrite(leds[i], LOW);
  }
  for(int i = NUM-1; i >= 0; i--){
    digitalWrite(leds[i], HIGH);
    delay(100);
    digitalWrite(leds[i], LOW);
  }
}
// 以degitalWrite點亮LED後,延遲100 milliseconds(0.1秒),
// 然後再滅掉,你可以修改延遲時間。
// 你可以把第二個迴圈改為 for(int i = NUM-2; i > 0; i--),
// 看看有何不同。

跑起來的效果,可以看看我上傳到YouTube的影片。(第二個迴圈為 for(int i = NUM-2; i > 0; i--)的情形。)


你可能覺得奇怪,左邊8個LED比較亮,右邊8個LED比較暗,其實也沒什麼道理,因為我買了兩組不同的LED,哈哈。

運用74HC595:

上面用了Arduino板的16個腳位,控制16個LED,感覺很浪費。接下來,我們將使用兩顆74HC595與函式shiftOut(),以3個腳位控制16個LED。

所謂74HC595,資料上寫著8-bit、serial-in、serial or parallel-out shift register、with output latches、3-state。

8-bit:它可以儲存8個bits的資料。
serial-in:以序列的方式,把資料傳給它,也就是一次傳一個bit給它。
serial or parallel-out:若是parallel-out並列輸出,就可控制8個LED。至於serial-out序列輸出,可用來接到下一顆74HC595,那麼,當你再傳入8個bits的資料時,先前的資料就會被送到下一顆74HC595,兩顆串起來後可控制16個LED。
shift register:移位暫存器,一次傳一個bit給它,前一個bit會被移位到下一個位置。
output latch:可以鎖住輸出腳位的狀態。
3-state:輸出可以有三種狀態HIGH、LOW、高阻抗(high impedance),本例不使用高阻抗狀態。

簡單講就是,我們將以三個腳位,將LED亮暗的資料存到74HC595,一顆74HC595有8個輸出、控制8個LED,兩顆就能控制16個LED。


595的腳位說明:
Pin 1~7(Q1~Q7)、Pin 15(Q0),8 bits的輸出。
Pin 8(GND)接地。
Pin 9(Q7" ),序列輸出。
Pin 10(MR),Master Reclear清除所有資料,active low低電位有效。
Pin 11(SH_CP),SHift register Clock Pin位移時脈腳位。又稱clock pin。
Pin 12(ST_CP),STorage register Clock Pin儲存時脈腳位,又稱latch pin鎖住腳位。
Pin 13(OE),Output enable允許輸出,active low低電位有效。
Pin 14(DS),Data Serial input序列資料輸入,又稱data pin資料腳位。
Pin 16(Vcc),供應正電壓。

電路圖:
先前從Arduino板子接到LED的16條線,全部拔掉。

從Arduino板的5V與GND腳位,接到麵包板上。

Pin 1~7(Q1~Q7)、Pin 15(Q0),依序接到8個LED,我把Q0接到最旁邊的LED。
Pin 8(GND),接地。
Pin 9(Q7" ),目前暫時未使用,之後會接到下一個74HC595。
Pin 10(MR) ,此例不使用此功能,所以接到5V。
Pin 11(SH_CP),接Arduino板子的腳位4。
Pin 12(ST_CP),接Arduino板子的腳位3。
Pin 13(OE),此例不使用此功能,所以接地。
Pin 14(DS),接Arduino板子的腳位2。
Pin 16(Vcc) ,接5V。

還有8個LED,目前暫時未使用。

P.S. 若有LED閃爍的現象,可在Pin 12(ST_CP)上接0.1 uF的電容,除去閃爍現象。







程式碼:
首先先寫個小程式,從右到左,輪流點亮每一個LED,一次只點亮一個LED,然後不斷循環。

// 首先定義腳位
#define DataPin 2 // 接到74HC595的DS
#define LatchPin 3 // 接到74HC595的ST_CP
#define ClockPin 4 // 接到74HC595的SH_CP

// 將三個腳位模式設定為OUTPUT
void setup() {               
  pinMode(DataPin, OUTPUT);
  pinMode(LatchPin, OUTPUT);
  pinMode(ClockPin, OUTPUT);
}

void loop() {
  uint8_t number; // 這個變數裡的每一個bit,代表每一個LED亮不亮。

  // 輪流點亮每一個LED,共有8個LED。
  for (int i = 0; i < 8; i++) {
    // 送出資料前,將LatchPin設為LOW,鎖定輸出的資料。
    digitalWrite(LatchPin, LOW);

    number = 0; // 所有bit清為0,代表全部LED都不亮。
    bitSet(number, i); // 將某bit設為1,代表點亮該LED。

    // 利用shiftOut()送出資料
    shiftOut(DataPin, ClockPin, MSBFIRST, number); 

    // 送出資料結束,將LatchPin設為HIGH,更新輸出腳位。
    digitalWrite(LatchPin, HIGH);
    delay(500);
  }
}

shiftOut()用起來很簡單,給它74HC595的DataPin與ClockPin,以及資料number,它就會把資料輸出給74HC595。至於MSBFIRST的意思是most significant bit first,也就是說,最先輸出的會是number(是個uint8_t,有8個bits,第7個bit是MSB,第0個bit是LSB)的第7個bit。如果改用LSBFIRST(least significant bit first),那麼最先輸出的會是第0個bit。

shiftOut()是Arduino內建的函式,有興趣的話可以到安裝目錄下的hardware\arduino\cores\arduino\wiring_shift.c看看原始碼。

void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)
{
    uint8_t i;

    for (i = 0; i < 8; i++)  {
        if (bitOrder == LSBFIRST)
            digitalWrite(dataPin, !!(val & (1 << i)));
        else   
            digitalWrite(dataPin, !!(val & (1 << (7 - i))));
           
        digitalWrite(clockPin, HIGH);
        digitalWrite(clockPin, LOW);       
    }
}
可以得知,val接受大小為8 bits,所以shiftOut只能輸出8 bits的資料,根據參數bitOrder為LSBFIRST或MSBFIRST,依序輸出每一個bit,寫入dataPin;然後將clockPin拉上(從下到上),告知74HC595把資料吃進去,最後把clockPin拉下,準備給下一次的資料使用。

你可能會覺得奇怪,為什麼要用兩個驚嘆號(!!),這是一種慣用法。因為digitalWrite()的第二個參數應該是個布林值,如果直接丟個整數進去,會需要進行一些轉換,用了兩次驚嘆號後,就可確保得到一個型別為布林的值。(不過,在此例裡,digitalWrite()的第二個參數其實是個uint8_t。)

好,讓我們修改一下,改成霹靂車燈。

#define DataPin 2
#define LatchPin 3
#define ClockPin 4

void setup() {              
  pinMode(DataPin, OUTPUT);
  pinMode(LatchPin, OUTPUT);
  pinMode(ClockPin, OUTPUT);
}

void loop() {
  uint8_t number;
  for (int i = 0; i < 8; i++) {
    digitalWrite(LatchPin, LOW);

    number = 0;
    bitSet(number, i);
  
    shiftOut(DataPin, ClockPin, MSBFIRST, number);

    digitalWrite(LatchPin, HIGH);
    delay(300);
  }
  for (int i = 0; i < 8; i++) {
    digitalWrite(LatchPin, LOW);

    number = 0;
    bitSet(number, i);
  
    shiftOut(DataPin, ClockPin, LSBFIRST, number);

    digitalWrite(LatchPin, HIGH);
    delay(300);
  }
}
東西都跟以前一樣,第一個迴圈從頭跑到尾後,利用新加入的第二個迴圈,讓LED從尾跑回頭。第二個迴圈有很多寫法,這裡我把MSBFIRST改成LSBFIRST就行了。

第二個迴圈也可以用底下這種寫法:
  for (int i = 7; i >=0; i--) {
    digitalWrite(LatchPin, LOW);

    number = 0;
    bitSet(number, i);
  
    shiftOut(DataPin, ClockPin, MSBFIRST, number);

    digitalWrite(LatchPin, HIGH);
    delay(300);
  }

好,接下來,就是加入第二顆74HC595,總共控制16個LED。

電路圖:延續之前的接線,然後,
第一顆74HC595:
Pin 9(Q7" ),接到第二顆74HC595的Pin 14(DS)。
Pin 11(ST_CP),接到第二顆74HC595的Pin 11(ST_CP)。
Pin 12(SH_CP),接到第二顆74HC595的Pin 12(SH_CP)。

第二顆74HC595:
Pin 1~7(Q1~Q7)、Pin 15(Q0),依序接到剩下來的8個LED,請注意順序,我的第二顆74HC595的Q0,接到上一顆74HC595的Q7的旁邊,我的第二顆74HC595的Q7,接到最旁邊的LED。
Pin 8(GND),接地。
Pin 9(Q7" ),不使用。
Pin 10(MR) ,此例不使用此功能,所以接到5V。
Pin 11(ST_CP),接到第一顆74HC595的Pin 11(ST_CP)。
Pin 12(SH_CP),接到第一顆74HC595的Pin 12(SH_CP)。
Pin 13(OE),此例不使用此功能,所以接地。
Pin 14(DS),接到第一顆74HC595的Pin 9(Q7")。
Pin 16(Vcc) ,接5V。







程式碼:
#define DataPin 2 // 接到74HC595的DS
#define LatchPin 3 // 接到74HC595的ST_CP
#define ClockPin 4 // 接到74HC595的SH_CP

void setup() {              
  pinMode(DataPin, OUTPUT);
  pinMode(LatchPin, OUTPUT);
  pinMode(ClockPin, OUTPUT);
}

void loop() {
  uint16_t number;
  uint8_t highbyte;
  uint8_t lowbyte;
  for (int i = 0; i < 16; i++) {
    digitalWrite(LatchPin, LOW);

    // 因為shiftOut()一次只能輸出8 bits的資料,
    // 所以這裡分成highbyte與lowbyte,
    // highbyte的第7個bit,代表最左邊的LED,
    // lowbyte的第0個bit,代表最右邊的LED。
    number = 0; // 先將全部bit清為0
    bitSet(number, i); // 將某bit設為1

    // 然後利用 >> 與 & 運算子,將資料取出來。
    highbyte = (number >> 8) & 0xFF;
    lowbyte = number & 0xFF;
  
    shiftOut(DataPin, ClockPin, MSBFIRST, highbyte);
    shiftOut(DataPin, ClockPin, MSBFIRST, lowbyte);

    digitalWrite(LatchPin, HIGH);
    delay(100);
  }
  for (int i = 0; i < 16; i++) {
    digitalWrite(LatchPin, LOW);

    number = 0;
    bitSet(number, i);
    highbyte = (number >> 8) & 0xFF;
    lowbyte = number & 0xFF;

    // 注意,這裡以LSBFIRST的順序寫出資料,
    // 所以要先送出lowbyte,然後是highbyte。
    // 你可以先highbyte再lowbyte,看看有何不同。
    shiftOut(DataPin, ClockPin, LSBFIRST, lowbyte);
    shiftOut(DataPin, ClockPin, LSBFIRST, highbyte);

    digitalWrite(LatchPin, HIGH);
    delay(100);
  }
}
當然,第二個迴圈也可以有別種寫法,不過我就不列出來了。

跑起來的效果。跟之前以16個腳位控制的版本不太一樣,當跑到最左邊、最右邊時,感覺會停頓一下,因為那兩個LED亮的時間是其他的兩倍。想要改成不停頓效果的程式碼,我就不列出來了,改法就是去修改迴圈裡的i值。


參考資料:


2012/02/17

Arduino軟體開發環境的設定檔preferences.txt

所謂的Arduino軟體開發環境,裡頭包含了很多部分:

  • USB轉換序列埠的驅動程式
  • 範例程式碼(閃爍LED、讀取數位腳位、PWM、等等)
  • Arduino的核心(定義HIGH與LOW常數值、函式pinMode()、等等)
  • 內建程式庫(讀寫EEPROM、控制LCD、控制伺服馬達、步進馬達、等等)
  • 開發工具鏈(編譯器、連結器、燒錄程式等等)
  • bootloader(要放進板子裡的快閃記憶體)
  • 文件資料
  • 整合式開發環境(以Java語言撰寫)
  • 等等其他東西
這一篇的主角是Arduino整合式開發環境的偏好設定值。

P.S. 我的Arduino軟體開發環境是1.0版、Windows版


首先以File-Preferences開啟偏好設定值的視窗。


說明如下:

Sketchbook location:
指向sketchbook目錄,底下存放你寫的程式碼。若你有從別的地方抓取額外的程式庫,請在這個目錄底下新增一個子目錄,並命名為libraries,然後把程式庫放在這個libraries子目錄底下。

設定好後,就可以File-Sketchbook開啟程式碼。若有安裝程式庫,以Tools-Import Library...匯入程式庫的標頭檔,若程式庫裡有範例程式,則會出現在File-Examples底下。

sketchbook目錄預設路徑為:
Windows是C:\Documents and Settings\My Documents\Arduino 
Mac是使用者家目錄下的Documents/Arduino/(/Users//Documents/Arduino/),
Linux家目錄下的sketchbook目錄。

Editor font size:
字型大小,要重新啟動才生效。

Show verbose output during:
  compilation,編譯程式時,輸出較詳細的訊息。
  upload,上傳程式時(從電腦到板子),輸出較詳細的訊息。

Delete previous applet or application folder on export
這似乎是Arduino開發環境的前身,Processing,的設定值。Processing可以將程式輸出成為內嵌在HTML檔裡的Java Applet,這個設定值會控制要不要刪除先前的內容。在Arduino上這個設定值似乎沒用(我不確定)。

Use external editor
你嫌Arduino開發環境的編輯器不好用嗎,打開這個選項,使用你愛用的編輯器吧,編輯存檔後,按下Arduino開發環境裡的Verify/Compile鈕或Upload鈕,就會載入更新後的檔案。

Check for updates on startup
啟動時檢查有無新版本。我通常會關掉。

Update sketch files to new extension on save (.pde -> .ino)
Arduino 1.0版將程式碼的附檔名從先前的.pde改成.ino,打開這個選項會幫你自動重新改名。你可以取消勾選,新版還是看得懂以前的.pde檔。

Automatically associate .ino files with Arduino
將附檔名.ino跟Arduino關聯在一起,也就是說,預設以Arduino開發環境開啟.ino檔。

More preferences can be edited directly in the file
C:\Documents and Settings\\Application Data\Arduino\preferences.txt
(edit only when Arduino is not running)
之前所設定的值,都會存在這個preferences.txt檔裡,裡面還有更多其他的設定值。

preferences.txt的預設路徑如下:
  • Windows XP,C:\Documents and Settings\Application Data\Arduino\preferences.txt。
  • Windows Vista,C:\Users\\AppData\Roaming\Arduino\preferences.txt。
  • Mac,/Users//Library/Arduino/preferences.txt。
  • Linux,~/.arduino/preferences.txt。

更多設定值:

不過光看它不知道怎麼設定,請參考Arduino目錄下的arduino-1.0\lib\preferences.txt(不要直接修改此檔),裡頭有設定值的敘述說明,而且,可以找出更多可用的設定值,部分說明如下:

注意,修改preferences.txt時,要先把Arduino關掉,不然設定值會被覆寫。

sketchbook.path=
路徑,這就是設定先前的Sketchbook location,可以用相對路徑(相對於Arduino開發環境的執行檔arduino.exe)、或用絕對路徑。

settings.path=
路徑,preferences.txt這支設定檔的路徑,可以用相對路徑(相對於Arduino開發環境的執行檔arduino.exe)、或用絕對路徑。

build.path=
路徑,編譯時的暫存目錄,若不設定就會使用系統的暫存目錄。

platform.auto_file_type_associations=
true或false,這就是之前的Automatically associate .ino files with Arduino。

build.verbose=
upload.verbose=
true或false,這就是之前Show verbose output during:。

update.check =
true或false,這就是之前的Check for updates on startup。

platform.auto_file_type_associations =
true或false,這就是之前的Automatically associate .ino files with Arduino。

editor.window.width.default = 
editor.window.height.default =
editor.window.width.min = 
editor.window.height.min = 
數字,設定視窗預設與最小的高度寬度,請自己試試看。

editor.font=
例如Monospaced,plain,12,設定字型與大小。

editor.antialias=
true或false,true的話會打開反鋸齒效果,文字會比較好看。

editor.external=
true或false,這就是之前的Use external editor。

last.sketch.count=
0或1或其他數字,啟動後會開啟上次離開時的程式。但是,若是以視窗右上角的X叉叉關閉的話,這個值會變成0。所以,要以File-Quit離開Arduino軟體開發環境。

上次開啟的程式檔會記錄在下列設定值裡。

last.sketch0.path=
last.sketch1.path=
...
路徑。

console= true
true或false,要不要把編譯、上傳過程的訊息輸出到主控台。

console.output.file=
預設為stdout.txt,標準輸出的儲存檔案。裡面會是編譯時的訊息。

console.error.file=
預設為stderr.txt,標準錯誤的儲存檔案。裡面會是上傳時的訊息。

console.auto_clear=
true或false,要不要清除主控台先前的內容。

console.length=
預設為500,主控台能記住的行數。

board =
例如uno,你的板子,也就是Tools-Board,可用設定值在hardware\arduino\boards.txt。

serial.port=
例如COM1,序列埠號,也就是Tools-Serial Port。

serial.databits=8
serial.stopbits=1
serial.parity=N
serial.debug_rate=9600
序列傳輸的設定。

programmer=arduino:avrispmkii
額外的programmer燒錄器(上傳),可用設定值在hardware\arduino\programmers.txt。

upload.using=bootloader
上傳的方式,預設值為bootloader,代表利用Arduino板子微控制器裡的bootloader進行上傳(韌體燒錄)。可用設定值在hardware\arduino\programmers.txt,以另外的programmer燒錄器進行上傳,譬如AVR ISP,

裡頭還有其他的設定值,可以自行改改看,但請記得先做備份。


參考資料:

2012/02/16

Arduino練習:loudspeaker揚聲器

之前一直在跟LED奮鬥(以開關切換LED明滅狀態呼吸燈霹靂車燈),現在玩玩聲音吧。

先發出一段官方範例程式碼的聲音,然後再播放兒歌妹妹揹著洋娃娃,然後再播放周杰倫的青花瓷。

電路圖(Fritzing格式)與程式原始碼,可在此下載。 

電路圖:

從Arduino板子的腳位8接到100 ohm電阻,電阻另一端接到揚聲器的正極,揚聲器的負極接地GND。


P.S. 我原本不清楚揚聲器、喇叭、蜂鳴器、loudspeaker、buzzer、等等有何不同,看了這幾篇後才了解:蜂鳴器(buzzer)與喇叭(speaker)有何不同,壓電式蜂鳴器(piezo buzzer)與電磁式蜂鳴器(magnetic buzzer)有何不同如何選用蜂鳴器。所以下圖、我買的應該叫做蜂鳴器揚聲器(到底是哪一個啊)。


程式碼:

我們將使用函式tone()發出聲音。

首先需要各音階的頻率。其中的NOTE_C5就是一般的中音Do(523Hz),NOTE_D5是Re(587Hz),NOTE_E5是Mi(659Hz),NOTE_F5是Fa(698Hz),NOTE_G5是So(784Hz),NOTE_A5是La(880Hz),NOTE_B5是Si(988Hz),而NOTE_C6是高音Do(1047Hz)。

我把NOTE_定義為頻率0,將拿來當做休止符。

#define NOTE_    0
#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

然後準備歌曲的資料,底下先用個短短的範例。
int melody[] = {
  NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3, 0, NOTE_B3, NOTE_C4
};

int noteDurations[] = {
  4, 8, 8, 4, 4, 4, 4, 4
};
4代表四分音符,8代表八分音符,以此類推,數字越小,音符的持續時間越長。

然後寫個函式,傳入歌曲的資料,進行播放。
void play(int *melody, int *noteDurations, int num){
  for(int note = 0; note < num; note++){
    int noteDuration = 3000 / noteDurations[note];
    tone(8, melody[note], noteDuration);

    delay(noteDuration * 1.30);
  }
}
函式的第一個參數是所有音符的頻率,第二個參數是每一個音符要播放多久,第三個參數代表總共有幾個音符。

函式裡,先根據noteDurations算出該音符的播放時間長度,我以3000 milliseconds(三秒)為基準,你可以自行修改。

然後以tone()函式播放,第一個參數是腳位,第二個參數是音符的頻率,第三個參數代表播放多久。

然後,根據播放時間長度,增加0.3倍的延遲時間。前一個音符撥放完後,停頓一下下,再播放下一個。

然後就是setup()與loop()了。
void setup(){
}

void loop(){
  play(melody, noteDurations, sizeof(melody) / sizeof(int));
 
  delay(2000);
}
setup()不作事。在loop()裡播放歌曲,放完後停兩秒再播放一次。

上面這份歌曲資料是從Arduino官方範例抄來的,底下則是兒歌妹妹揹著洋娃娃的歌曲資料。

int melody[] = {
  NOTE_G4, NOTE_G4, NOTE_E4, NOTE_D4, NOTE_E4, NOTE_D4, NOTE_C4,
  NOTE_E4, NOTE_D4, NOTE_C4, NOTE_A3, NOTE_G3, NOTE_A3, NOTE_G3,
  NOTE_A3, NOTE_A3, NOTE_C4, NOTE_A3, NOTE_C4, NOTE_D4, NOTE_E4,
  NOTE_D4, NOTE_D4, NOTE_G4, NOTE_G4, NOTE_E4, NOTE_D4, NOTE_C4,
};

int noteDurations[] = {
  6, 16, 8, 8, 8, 8, 4,
  6, 16, 8, 8, 8, 8, 4,
  6, 16, 8, 8, 8, 8, 4,
  6, 16, 8, 8, 8, 8, 4,
};

如果還不過癮的話,可以試試看周杰倫的青花瓷。

int melody[] = {
  NOTE_, NOTE_D4, NOTE_C4, NOTE_A3, NOTE_C4, NOTE_C4, NOTE_A3, NOTE_C4, NOTE_C4, NOTE_A3, NOTE_C4, NOTE_A3, NOTE_G3, NOTE_,
  NOTE_, NOTE_D4, NOTE_C4, NOTE_A3, NOTE_C4, NOTE_C4, NOTE_A3, NOTE_C4, NOTE_C4, NOTE_E4, NOTE_D4, NOTE_C4, NOTE_C4, NOTE_,
 
  NOTE_, NOTE_G3, NOTE_A3, NOTE_E4, NOTE_E4, NOTE_E4, NOTE_D4, NOTE_E4, NOTE_E4, NOTE_D4, NOTE_E4, NOTE_G4, NOTE_E4, NOTE_E4, NOTE_,
  NOTE_, NOTE_E4, NOTE_E4, NOTE_E4, NOTE_D4, NOTE_D4, NOTE_D4, NOTE_D4, NOTE_D4, NOTE_C4, NOTE_E4, NOTE_E4, NOTE_D4, NOTE_,
 
  NOTE_, NOTE_D4, NOTE_C4, NOTE_A3, NOTE_C4, NOTE_C4, NOTE_A3, NOTE_C4, NOTE_C4, NOTE_A3, NOTE_C4, NOTE_A3, NOTE_G3, NOTE_,
  NOTE_, NOTE_G3, NOTE_A3, NOTE_E4, NOTE_G4, NOTE_G4, NOTE_E4, NOTE_G4, NOTE_G4, NOTE_E4, NOTE_D4, NOTE_C4, NOTE_C4, NOTE_,
 
  NOTE_, NOTE_D4, NOTE_C4, NOTE_D4, NOTE_E4, NOTE_D4, NOTE_D4, NOTE_C4, NOTE_D4,
  NOTE_C4, NOTE_A3, NOTE_D4, NOTE_C4, NOTE_C4, NOTE_A3, NOTE_C4, NOTE_C4, NOTE_C4, NOTE_,
  //
  NOTE_, NOTE_G4, NOTE_G4, NOTE_E4, NOTE_D4, NOTE_E4, NOTE_A3, NOTE_D4, NOTE_E4, NOTE_G4, NOTE_E4, NOTE_D4, NOTE_,
  NOTE_, NOTE_G4, NOTE_G4, NOTE_E4, NOTE_D4, NOTE_E4, NOTE_G3, NOTE_D4, NOTE_E4, NOTE_G4, NOTE_D4, NOTE_C4, NOTE_,
 
  NOTE_, NOTE_C4, NOTE_D4, NOTE_E4, NOTE_G4, NOTE_A4, NOTE_G4, NOTE_F4, NOTE_G4, NOTE_E4, NOTE_E4, NOTE_D4, NOTE_D4, NOTE_,
  NOTE_, NOTE_C4, NOTE_D4, NOTE_C4, NOTE_C4, NOTE_D4, NOTE_C4, NOTE_D4, NOTE_D4, NOTE_E4, NOTE_G4, NOTE_E4, NOTE_E4, NOTE_,
 
  NOTE_, NOTE_G4, NOTE_G4, NOTE_E4, NOTE_D4, NOTE_E4, NOTE_A3, NOTE_D4, NOTE_E4, NOTE_G4, NOTE_E4, NOTE_D4, NOTE_,
  NOTE_, NOTE_G4, NOTE_G4, NOTE_E4, NOTE_D4, NOTE_E4, NOTE_G3, NOTE_D4, NOTE_E4, NOTE_G4, NOTE_D4, NOTE_C4, NOTE_,
 
  NOTE_, NOTE_C4, NOTE_D4, NOTE_E4, NOTE_G4, NOTE_A4, NOTE_G4, NOTE_F4, NOTE_G4, NOTE_E4, NOTE_E4, NOTE_D4, NOTE_D4,
  NOTE_G3, NOTE_E4, NOTE_D4, NOTE_D4, NOTE_D4, NOTE_C4, NOTE_,
};

int noteDurations[] = {
  16, 16, 16, 16, 8, 16, 16, 8, 16, 16, 16, 16, 8, 8,
  16, 16, 16, 16, 8, 16, 16, 8, 16, 16, 16, 16, 8, 8,
 
  16, 16, 16, 16, 8, 16, 16, 8, 16, 16, 16, 16, 16, 16, 8,
  16, 16, 16, 16, 16, 16, 16, 16, 8, 16, 16, 16, 8, 8,
 
  16, 16, 16, 16, 8, 16, 16, 8, 16, 16, 16, 16, 8, 8,
  16, 16, 16, 16, 8, 16, 16, 8, 16, 16, 16, 16, 8, 8,
 
  16, 16, 16, 16, 16, 16, 16, 16, 8,
  16, 16, 16, 16, 16, 16, 8, 16, 4, 8,
  //
  16, 16, 16, 16, 16, 16, 8, 16, 16, 16, 16, 4, 8,
  16, 16, 16, 16, 16, 16, 8, 16, 16, 16, 16, 4, 8,
 
  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 4, 8,
  16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 16, 16, 8, 8,
 
  16, 16, 16, 16, 16, 16, 8, 16, 16, 16, 16, 4, 8,
  16, 16, 16, 16, 16, 16, 8, 16, 16, 16, 16, 4, 8,
 
  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 4,
  16, 8, 16, 16, 16, 4, 8,
};

完成後,播放出來的聲音效果,我有上傳到YouTube,有興趣的話可以聽聽看:範例樂音妹妹揹著洋娃娃青花瓷


參考資料: