がじぇっとるねさす

ホビーとエレクトロニクスを繋げるサイト

がじぇるね工房

作品タイトル:GR-KURUMIの機能を使う SDカード(SPI)編

表示名:@chobichan

GR-KURUMIの機能を使う SDカード(SPI)編

コンセプト・作品説明
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;
}


 

@chobichan

極一般のハードウエアエンジニア。たまに雑誌記事を書いています。
twitterアカウント:https://twitter.com/chobichan

フォローする

share

ページトップへ