作品タイトル:GR-KURUMIの機能を使う SDカード(SPI)編
表示名:@chobichan
コンセプト・作品説明 |
---|
GR-KURUMIのSDカード(SPI)機能を使います |
マイコンでSDカードを扱う
GR-KURUMIは簡単にSDカードを扱うことができます。
この機能は本当にありがたいもので、例えばセンサーのデータをログするのに、一々ネットワーク接続せずに装置単体でそれができてしまいます。
さっそく温湿度をログしてみましょう。
次にコードを掲載しておきます。なお一部のコードはHDC1000のサンプルと同一であり、掲載していません。
※SDカードライブラリを使うにあたって注意点があります。
1.ファイル名はロングファイル形式に対応していないので、ロングファイル名が与えられているファイルは強制的に8+3形式のファイル名で取り扱われます。
2.ファイル名やディレクトリ名に小文字があれば全て大文字で取り扱われます。その為プログラムの処理で比較等を行う時は注意が必要です。
3.サブディレクトリにも対応していますが、ファイルにアクセスするにはFULL PATHでアクセスする必要があります。
4.どのくらい影響があるのか判りませんが、よくネットで書かれている事として、WindowsでSDカードをフォーマットするのではなく、SDFormatterを使う事が推奨されています。
https://www.sdcard.org/jp/downloads/formatter_4/
/*GR-KURUMI Sketch Template Version: V1.12*/ #include#include //for RTC #include //for SD card #include //I2Cを使う為に宣言 #define SD_SS A2 //sd card slave select #define SD_CD A3 //sd card detect この辺り省略 void setup() { Serial.begin( 9600 ); Wire.begin(); //for display and temp sensor HDC1000writeConfig(0x8000); //reset delay(10); HDC1000writeConfig(0x1000); //config set resolution 14bit 0b0000 0000 00000000 rtc_init(); //RTC initialize RTC_TIMETYPE rtc_t = { 16,1,12,0,0,0 }; rtc_set_time( &rtc_t ); //date and time set SUBCUD.subcud = 0xB8; //calibration pinMode( SD_CD, INPUT ); //SD cardが入っているとLOW、無ければHIGHとなる Serial.print("wait for SD card inserted."); while( digitalRead( SD_CD ) == HIGH ) //SDカードが刺さるまでやっている { Serial.print("."); delay( 100UL ); } Serial.println("SD card detected."); if( !SD.begin( SD_SS ) ) //SSに使用する端子番号を指定している { Serial.println("SD initialization failed!"); while( 1 ) ; } Serial.println("SD initialization cleared."); while( 1 ) { while( 1 ) //wait for 10second period. { rtc_get_time( &rtc_t ); if( !(rtc_t.second % 10) ) break; //interval time = 10s } unsigned short tempUS,humiUS; HDC1000readTemperature( &tempUS, &humiUS ); //温度、湿度データを取得 float tempF = HDC1000temperature2Degree( tempUS ) + 0.05f; //変換値を摂氏に換算 float humiF = HDC1000humidity2Percent( humiUS ) + 0.05f; //変換値をパーセントに換算 char buf[32]; String log = ""; log += rtc_t.year + 2000; log += "/"; log += rtc_t.mon; log += "/"; log += rtc_t.day; log += " "; log += rtc_t.hour; log += ":"; log += rtc_t.min; log += ":"; log += rtc_t.second; log += ","; sprintf( buf, "%2.1f", tempF ); log += buf; log += "c,"; sprintf( buf, "%2.1f", humiF ); log += buf; log += "%"; char fn[] = "temper.csv"; File logFile; logFile = SD.open( fn, FILE_WRITE ); /*ファイル書き込み*/ logFile.println( log ); logFile.close(); Serial.println( log ); delay( 10 * 1000UL ); } } void loop() { } 以下、省略
Temper.csvのファイルの中身は以下の様になりました。
2016/1/12 0:0:10,26.2c,45.5%
2016/1/12 0:0:20,24.8c,42.9%
2016/1/12 0:0:30,24.4c,40.4%
2016/1/12 0:0:40,24.1c,38.9%
2016/1/12 0:0:50,23.9c,37.9%
2016/1/12 0:1:0,23.9c,37.9%
2016/1/12 0:1:10,23.7c,37.8%
2016/1/12 0:1:20,23.6c,37.4%
2016/1/12 0:1:30,23.6c,37.2%
2016/1/12 0:1:40,23.6c,37.0%
ファイルを書き込みでOpenした時は常に追記モードです。
ファイルの前の内容を削除して新たに書き込みたい時は
Openする前に該当ファイルをRemoveするしか無さそうです。
SDカードから読みだしてみる
先ほどSDカードに書き込んだ内容を読み出してみます。コードを掲載しておきます。
/*GR-KURUMI Sketch Template Version: V1.12*/ #include#include //for SD card #define SD_SS A2 //sd card slave select #define SD_CD A3 //sd card detect void setup() { Serial.begin( 9600 ); pinMode( SD_CD, INPUT ); //SD cardが入っているとLOW、無ければHIGHとなる Serial.print("wait for SD card inserted."); while( digitalRead( SD_CD ) == HIGH ) //SDカードが刺さるまでやっている { Serial.print("."); delay( 100UL ); } Serial.println("SD card detected."); if( !SD.begin( SD_SS ) ) //SSに使用する端子番号を指定している { Serial.println("SD initialization failed!"); while( 1 ) ; } Serial.println("SD initialization cleared."); char fn[] = "temper.csv"; File logFile; logFile = SD.open( fn, FILE_READ ); /*ファイル読み込み*/ while( logFile.available() ) { unsigned char c = logFile.read(); Serial.write( (const unsigned char*)&c,1 ); } logFile.close(); } void loop() { }
SDカードにどの程度の速度で書き込みができるか?
気になるところですね。
以下に結果を掲載しておきます。
SDカードに1Mbyteのテキストの書き込み時間を計測してみました。10483msで約10秒掛かっています。
1秒辺り約100Kbyteになります。※いったんデータは512byteにバッファされ、その上で書き込みを行っているので、おそらく効率は良い筈です。
1byte毎に書き込みをすると遅くなると思われます。
実験したコードを記載しておきます。
/*GR-KURUMI Sketch Template Version: V1.12*/ #include#include //for SD card #define SD_SS A2 //sd card slave select #define SD_CD A3 //sd card detect void setup() { Serial.begin( 9600 ); pinMode( SD_CD, INPUT ); //SD cardが入っているとLOW、無ければHIGHとなる Serial.print("wait for SD card inserted."); while( digitalRead( SD_CD ) == HIGH ) //SDカードが刺さるまでやっている { Serial.print("."); delay( 100UL ); } Serial.println("SD card detected."); if( !SD.begin( SD_SS ) ) //SSに使用する端子番号を指定している { Serial.println("SD initialization failed!"); while( 1 ) ; } Serial.println("SD initialization cleared."); char fileBuf[512]; char testString[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\r\n"; //26 + 26 + 10 + 2 #define STRING_SIZE (sizeof(testString) - 1) char *ptr = fileBuf; for( int i = 0; i < sizeof(fileBuf) / STRING_SIZE; i++ ) { memcpy( ptr, testString, STRING_SIZE ); ptr += STRING_SIZE; } char fn[] = "abcd.txt"; if( SD.exists(fn) ) SD.remove(fn); File txtFile; txtFile = SD.open( fn, FILE_WRITE ); /*ファイル書き込み*/ #define LOOP_LIMIT (int)(1024UL * 1024UL / sizeof(fileBuf)) unsigned long previousTime = millis(); for( int i = 0; i < LOOP_LIMIT; i++ ) { txtFile.write( (unsigned char*)fileBuf, sizeof(fileBuf) ); } txtFile.flush(); unsigned long nextTime = millis(); txtFile.close(); Serial.print("SD card write time:"); Serial.println( nextTime - previousTime, DEC ); } void loop() { }
SDカードの音楽ファイルを再生してみる
先の実験でSDカードの読み出しがそこそこ良かったので、SDカードに保存されている音楽データを再生してみます。
音楽データはMP3形式の物、MP3データの再生には専用のデコーダーIC(VS1063A)を使用します。
http://akizukidenshi.com/catalog/g/gI-06585/
デコーダーICのインタフェースはSPI接続となります。
SDカードもSPIを使用しますので、この2つの間で齟齬が起きなければよいのですが。
GR-KURUMIはSPIを使う為のライブラリも用意されています。
デコーダーICのSPIの条件はSCKが約5MHz以下です。
SCKとデータの位相を決めるモードは4つある内のモード0です。
この変更でSDカードと問題を起こさないか気になるところですね。特にSPIのモードは1つに統一すれば良いのに4種類もあるなんて、百害あって一利なしですよ。プログラムは、SDカードのサブディレクトリを含む中のファイル名を調べて、拡張子が.MP3のファイルをmp3ファイルとしてデータを読み出し、デコーダーICとハンドシェークしながらデータを送りだします。
全てのmp3ファイルの再生が終了すればそこで終わりです。
無事、動いて音楽が再生されました。
コードを掲載しておきます。
/* ------------------------------------------------------------------------ */ /* SDカード内の音楽ファイルをMP3デコーダーICに転送して再生 */ /* designed by hamayan */ /* Copyright(C) hamayan */ /* since 2004 - 2016 */ /* ------------------------------------------------------------------------ */ #include#include #include //for SD card #include //for vs1063a extern "C" { #include "string.h" } #if !defined( _MULTITASK_H_ ) #define dly_tsk(tim) delay(tim) #define rot_rdq() #endif /*_MULTITASK_H_ */ void searchMp3( File dir, const char *path ); void playMp3( const char *fn ); static void VS1011e_Wait_DREQ( void ); void vs1011eInit( void ); void VS1011e_SCI_Write( unsigned char adr, unsigned short data ); unsigned short VS1011e_SCI_Read( unsigned char adr ); void VS1011e_SDI_Write( const char *data, unsigned long size ); void setVolum( unsigned short vol ); bool isDREQ( void ); #define SD_SS A3 //sd card slave select #define SD_CD A2 //sd card detect /*DREQ:D3(p16),XRESET:D5(p15),XCS:D6(p10),XDCS:D9(p13) */ #define VS_DRQ (P1.BIT.bit6 == 1) #define VS_RST_IS_0 P1.BIT.bit5 = 0 #define VS_RST_IS_1 P1.BIT.bit5 = 1 #define VS_XDCS_IS_0 P1.BIT.bit3 = 0 #define VS_XDCS_IS_1 P1.BIT.bit3 = 1 #define VS_XCS_IS_0 P1.BIT.bit0 = 0 #define VS_XCS_IS_1 P1.BIT.bit0 = 1 #define VS_SCI_OPE_WRITE 0x02 #define VS_SCI_OPE_READ 0x03 enum VS_REGISTERS { VS_MODE, VS_STATUS, VS_BASS, VS_CLOCKF, VS_DECODE_TIME, VS_AUDATA, VS_WRAM, VS_WRAMADDR, VS_HDAT0, VS_HDAT1, VS_AIADDR, VS_VOL, VS_AICTRL0, VS_AICTRL1, VS_AICTRL2, VS_AICTRL3 }; const unsigned short loud_table[] = {0x0000,0x1010,0x2020,0x3030,0x4040,0x5050,0x6060,0x7070,0x8080,0x9090,0xa0a0,0xb0b0,0xc0c0,0xd0d0,0xe0e0,0xf0f0}; extern const unsigned long mp3_dat_size; extern const unsigned char mp3_file[]; void setup() { Serial.begin( 9600 ); SPI.begin(); SPI.setClockDivider(SPI_CLOCK_DIV8); //2mhz //SPI.setClockDivider(SPI_CLOCK_DIV4); //4mhz SPI.setDataMode(SPI_MODE0); Serial.println("initialize vs1011e..."); vs1011eInit(); VS1011e_SDI_Write( (const char *)mp3_file, (unsigned long)mp3_dat_size ); //setVolum( 0x2020 ); pinMode( SD_CD, INPUT ); //SD cardが入っているとLOW、無ければHIGHとなる Serial.println("wait for SD card inserted."); while( digitalRead( SD_CD ) == HIGH ) //SDカードが刺さるまでやっている { Serial.print("."); delay( 100UL ); } Serial.println("SD card detected."); if( !SD.begin( SD_SS ) ) //SSに使用する端子番号を指定している { Serial.println("SD initialization failed!"); while( 1 ) ; } Serial.println("SD initialization cleared."); File root = SD.open( "/" ); //open root directory searchMp3( root, "" ); } void loop() { } void searchMp3( File dir, const char *path ) { while( 1 ) { File fil = dir.openNextFile(); if( fil == false ) return; const char *fn = fil.name(); //Serial.println( fn ); if( fil.isDirectory() == true ) { char cpyPath[256]; strcpy( cpyPath, path ); strcat( cpyPath, "/" ); strcat( cpyPath, fil.name() ); searchMp3( fil, cpyPath ); } if( strcmp(".MP3", strchr( fn, '.' )) == 0 ) { char cpyFn[256]; strcpy( cpyFn, path ); strcat( cpyFn, "/" ); strcat( cpyFn, fn ); playMp3( cpyFn ); } } } void playMp3( const char *fn ) { File mp3File = SD.open( fn, FILE_READ ); if( mp3File == false ) { Serial.print( fn ); Serial.println( ":open error." ); return; } unsigned long fileSize = mp3File.size(); Serial.print("file name:"); Serial.print(fn); Serial.print(" file size="); Serial.println( fileSize, DEC ); char buf[2048]; unsigned long previousTime = millis(); #if 1 while( fileSize > 0 ) { unsigned long readSz = (fileSize > sizeof(buf)) ? sizeof(buf) : fileSize; mp3File.read( buf, readSz ); fileSize -= readSz; VS1011e_SDI_Write( (const char *)buf, readSz ); } #else delay( 1 * 1000UL ); #endif unsigned long nextTime = millis(); mp3File.close(); Serial.print("mp3 play time:"); Serial.println( nextTime - previousTime, DEC ); } /****************************************************************************/ /* vs1011e初期化 */ /****************************************************************************/ void vs1011eInit( void ) { int i; char dummy; unsigned short loud; /*PORTの設定*/ PM1.BIT.bit6 = 1; //dreq input mode PIM1.BIT.bit6 = 1; //ttl input PM1.BIT.bit5 = 0; //xreset output mode POM1.BIT.bit5 = 0; //cmos PM1.BIT.bit3 = 0; //xdcs output mode POM1.BIT.bit3 = 0; //cmos PM1.BIT.bit0 = 0; //xcs output mode POM1.BIT.bit0 = 0; //cmos /*XRESET*/ VS_RST_IS_0; dly_tsk(10); /*XRESETの解除*/ VS_RST_IS_1; /*ソフトウエアリセット*/ VS1011e_SCI_Write( VS_MODE, 0x0804 ); VS1011e_SCI_Write( VS_MODE, 0x0800 ); /*モード設定完了*/ VS1011e_SCI_Write( VS_CLOCKF, 0xa000 + 1072 ); /*クロック設定 x4 0x8000:x3.5*/ VS1011e_SCI_Write( VS_VOL, 0x2020 ); /*音量設定 夜、寝ている時にいい感じ*/ //VS1011e_SCI_Write( VS_VOL, 0x1010 ); /*音量設定*/ loud = VS1011e_SCI_Read( VS_VOL ); dly_tsk(100); /*バッファを0で埋める*/ for( i = 0, dummy = 0; i < 2048; i++ ) VS1011e_SDI_Write( &dummy, 1 ); } /****************************************************************************/ /* VS1011eのDREQがネゲートされるのを待つ */ /****************************************************************************/ static void VS1011e_Wait_DREQ( void ) { while( VS_DRQ == false ) rot_rdq(); } /****************************************************************************/ /* SCIの書き込み */ /****************************************************************************/ void VS1011e_SCI_Write( unsigned char adr, unsigned short data ) { /*DREQのチェック*/ if( VS_DRQ == false ) VS1011e_Wait_DREQ(); /*XCSのアサート*/ VS_XCS_IS_0; /*オペレーションコードの書き込み*/ SPI.transfer(VS_SCI_OPE_WRITE); /*アドレスの書き込み*/ SPI.transfer(adr); /*データの書き込み*/ SPI.transfer(data >> 8); SPI.transfer(data >> 0); /*XCSのネゲート*/ VS_XCS_IS_1; } /****************************************************************************/ /* SCIの読み込み */ /****************************************************************************/ unsigned short VS1011e_SCI_Read( unsigned char adr ) { int i; unsigned char ope; unsigned short data = 0; /*DREQのチェック*/ if( VS_DRQ == false ) VS1011e_Wait_DREQ(); /*XCSのアサート*/ VS_XCS_IS_0; /*オペレーションコードの書き込み*/ SPI.transfer(VS_SCI_OPE_READ); /*アドレスの書き込み*/ SPI.transfer(adr); /*データの読み込み*/ data = SPI.transfer(0xff) << 8; data |= SPI.transfer(0xff) << 0; /*XCSのネゲート*/ VS_XCS_IS_1; return data; } /****************************************************************************/ /* SDIの読み込み */ /****************************************************************************/ void VS1011e_SDI_Write( const char *data, unsigned long size ) { /*XDCSのアサート*/ VS_XDCS_IS_0; for( ;size > 0; size-- ) { /*書き込めるまで待ちを入れる*/ while( size > 32 && VS_DRQ == false ) rot_rdq(); //while( size > 32 && digitalRead(dreq) == LOW ) rot_rdq(); /*データの書き込み*/ SPI.transfer(*data++); } /*XDCSのネゲート*/ VS_XDCS_IS_1; } /****************************************************************************/ /* volume control */ /****************************************************************************/ void setVolum( unsigned short vol ) { VS1011e_SCI_Write( VS_VOL, vol ); /*音量設定*/ } /****************************************************************************/ /* what is state of dreq pin ? */ /****************************************************************************/ bool isDREQ( void ) { return VS_DRQ; }
極一般のハードウエアエンジニア。たまに雑誌記事を書いています。
twitterアカウント:https://twitter.com/chobichan