mbed NXP LPC1768+☆board Orangeで.WAVファイルの再生
「Arduino UNO/Mega 2560 .WAVファイルの再生」では、非力なCPUで「16000Hz/8bit/Mono」形式の.WAVファイルを再生してみました。
今回は、mbed NXP LPC1768で「44100Hz/16bit/Stereo」形式の.WAVファイルの再生を試行します。
「44100Hz/16bit/Stereo」形式になると単純計算で44,100*2*2=176,400(Byte/s)の転送能力が必要になります。
最初のネックはSDからの.WAVファイルの読み出し速度ですが、SDFileSystemのデフォルトでは、SPIのクロックは1MHzに設定されているために約125,000(Byte/s)となり、必要な転送速度を下回ります。
実験のためにSDFileSystem.cpp内のSPIクロックの設定を24MHzに変更し、3MB/s(理論値)の転送速度にして実験しています。
SDFileSystem.cppの変更行
1 |
_spi.frequency(24000000); // Set to 24MHz for data transfer |
PlayWav.cpp (2014/8/24)
出力は前回と同様にPWMによって出力しますが、今回は44100Hzで出力します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
#include "mbed.h" #include "SDFileSystem.h" SDFileSystem sd(p5, p6, p7, p8, "sd"); // Star board ORANGE PwmOut Line_L(p21), Line_R(p22); DigitalOut statLED1(LED1), statLED2(LED2); Ticker Tick_Timer; uint16_t Wave_CHs, Wave_BITs; uint32_t Wave_BPS, Wave_dlen; volatile uint16_t bufGP, bufPP; // buffer index for Get/Put #define BUFSIZE 512 // buffer size uint8_t Wave_Buff[BUFSIZE * 2]; #define _WU8(v) Wave_Buff[v] #define _WS16(v) *( int16_t*)&Wave_Buff[v] #define _WU16(v) *(uint16_t*)&Wave_Buff[v] #define _WU32(v) *(uint32_t*)&Wave_Buff[v] void ISR_Tick(void) { if (bufGP != bufPP) { statLED2 = 1; switch (Wave_BITs) { case 8: Line_L = float(_WU8(bufGP)) / 255.0; if (Wave_CHs > 1) bufGP ++; Line_R = float(_WU8(bufGP)) / 255.0; bufGP ++; break; case 16: Line_L = float(_WS16(bufGP) + 32768) / 65537.0; if (Wave_CHs > 1) bufGP += 2; Line_R = float(_WS16(bufGP) + 32768) / 65537.0; bufGP += 2; break; } if (bufGP >= (BUFSIZE * 2)) bufGP = 0; statLED2 = 0; } } void disp_Err(int rc) { while (1) { for (int i = 0; i < rc; i++) { statLED1 = 1; wait(0.5); statLED1 = 0; wait(0.5); } wait(2); } } int main() { DIR *d; FILE *f; char path[256]; struct dirent *p; int getln; Line_L.period_us(10); Line_R.period_us(10); if ((d = opendir("/sd/music")) == NULL) disp_Err(2); while ((p = readdir(d)) != NULL) { char* s = p->d_name + (strlen(p->d_name) - 4); if (strcmp(s, ".wav") != 0) continue; snprintf(path, sizeof path, "/sd/music/%s", p->d_name); if ((f = fopen(path, "rb")) == NULL) disp_Err(3); bufPP = fread(Wave_Buff, 1, BUFSIZE, f); if (bufPP < 46) disp_Err(4); // invalid file if ((_WU32( 0) != 0x46464952) || // "RIFF" (_WU32( 8) != 0x45564157) || // "WAVE" (_WU32(12) != 0x20746D66)) disp_Err(5); // "fmt " Wave_CHs = _WU16(22); Wave_BPS = _WU32(24); Wave_BITs = _WU16(34); bufGP = 38; if (_WU32(bufGP) == 0x74636166) bufGP+=12; //"fact" if (_WU32(bufGP) != 0x61746164) disp_Err(6); //"data" bufGP += 4; Wave_dlen = _WU32(bufGP) - (bufPP - (bufGP + 4)); bufGP += 4; Tick_Timer.attach(&ISR_Tick, 1.02 / float(Wave_BPS)); while (Wave_dlen) { if (((bufPP < bufGP) && ((bufPP + BUFSIZE) < bufGP)) || ((bufPP > bufGP) && ((bufPP - BUFSIZE) < bufGP))) { statLED1 = 1; getln = (Wave_dlen > BUFSIZE) ? BUFSIZE : Wave_dlen; if (fread(&Wave_Buff[bufPP], 1, getln, f) != getln) disp_Err(7); if ((Wave_dlen -= getln) == 0) getln &= -4; bufPP += getln; if (bufPP >= (BUFSIZE * 2)) bufPP = 0; statLED1 = 0; } } fclose(f); for (int i=0; ((i<10)||(bufGP!=bufPP)); i++) wait(0.1); Tick_Timer.detach(); // Stop! } closedir(d); while (1); } |
mbed NXP LPC1768+☆board Orange
両チャンネルのPWM出力から330Ωの制限抵抗を通して、ヘッドフォンに接続しています。
処理時間計測
上記の「mbed LPC1768」+「☆board Orange」で実行したときの処理時間を計測してみました。
SD上の.WAVファイルの読込処理時間観測(LED1の波形)
上記は通常の読込時間ですが、実際にはFATの読込みなどでこれ以上の時間が必要なこともあります。これを見るとNXP LPC176(ARM Cortex-M3 96MHz)でも再生中には、ファイル読込み以外の処理に使える時間は約37%位しか残りません。
(音楽ファイルを再生しながら、TFT液晶にジャケット写真などを表示するようなプログラムは厳しいかも)
PWM出力処理時間観測(LED2の波形)
割込間隔は、44,100Hzであれば22.675usになりますが、タイマー精度の関係から23us(誤差=約-1.4%)に設定しています。
割込処理の中での両チャンネルのPWM値の書込みに約7.6usを要しています、この割込処理中はSD(SPI)からの割込みは保留されるために読込み速度が低下します。
仮に、このPWM値の設定を行わないと割込処理時間は約0.43usと短くなります、この場合P11のSD読込処理時間は1.13msになり、空き時間の割合は約62%に増えます。
PwmOutライブラリを書替えて、実数演算を回避することで割込み処理時間は大幅に短縮できます。
(「短縮できました」ではなく、「短縮できるだろうなぁ」です( ̄ー ̄;)
コメント:0