PR

Raspberry Pi PicoでSDカードライブラリを使用する

組み込みエンジニア
本記事はプロモーションが含まれています。

こんにちは、ENGかぴです。

Raspberry Pi PicoでSDカードライブラリを使用するとSPI通信を使ってSDカードにファイルを生成してデータを保存することができます。電源投入からの秒数をボタンを押したタイミングでSDカードに保存して動作確認を行いました。

Raspberry Pi Pico(以下Picoとする)と拡張基板のGrove Shield for Pi Picoを使用しています。SDカードモジュールはmicroSDカードスロットレベルシフタ付きブレークアウト基板キット:AE-microSD-LLCNV(秋月電子)を使用しています。

SDカードにファイルを保存

Arduino環境ではSDカードを操作する標準ライブラリが実装されています。SDカードの操作はSPI通信を使用するためSPIのデフォルトピンが使用状態になります。SPI通信の方法を下記記事にまとめています。

Raspberry Pi PicoでSPIライブラリを使用する

以下ではSDクラスのメンバー関数及びFileクラスのメンバー関数を赤文字で表記します。

PR:わからないを放置せず、あなたにあった最低限のスキルを身に着けるコツを教える テックジム 「書けるが先で、理解が後」を体験しよう!

SDカードライブラリの準備と初期化

#include <SD.h>

File myfile; //SDカードの状態を格納

void setup() {

  if(!SD.begin()){
    //初期化に失敗した場合の処理
  }
}

SDカードを操作するためSD.hをインクルードします。SDカードのファイルを管理するため、File型のクラス変数をインスタンス化します。例ではmyfileの宣言しています。myfileはファイルの読み込みや書き込みに使用します。

SDライブラリのbegin()関数を使用してSDカードに関する情報を初期化します。引数にはSDカードを選択するためのスレーブセレクト(SS)のピン番号を指定します。指定しない場合はデフォルトの17ピンで初期化が行われます。

SDカードが挿入されていない場合など失敗した場合は戻り値がfalseになるので、必要があれば失敗した場合の処理を追加します。

PR:アクセンチュアの転職なら【コンサルアクシスコンサルティング】

ファイルの書き込み

String filepath = "sample.txt";

myfile = SD.open(filepath,FILE_WRITE);

if( myfile ){ //ファイルが開けたら書き込む
  myfile.print("Grove Button->OK");
  myfile.println(seccnt);
  myfile.close(); //ファイルを閉じる
}

open()関数でファイルを書き込みモードで開きます。open()関数の第1引数にはファイルのパスを指定し、第2引数に書き込みを示すFILE_WRITEを指定します。ファイルが開けたらprint()関数を使用してデータを書き込みます。

print()関数で「Grove Button->OK:」の文字列を書き込みます。seccntは初期化後に経過した時刻(1sec更新)の変数をprintln()関数で書き込みを行います。println()は文字列の最後に改行コードを入れて文字列を生成します。

データを書き込んだ後はファイルを閉じるためclose()関数を使用します。

ファイルの読み込み

本記事ではデータの読み込みは使用しませんが参考の為データの読み込みについてまとめています。

//ファイル名を指定して読み込む場合の例
String filepath = "sample.txt"; //SDカード内に保存するファイル名

 if (SD.exists(filepath)) { //ファイルが存在するか
    Serial.println("sample.txt exists.");
    myfile = SD.open(filepath,FILE_READ); //ファイルを開く

    if(myfile){ //ファイルが開けた場合
      while(myfile.available()){
        str ="";
        str = myfile.readStringUntil('\n'); //読み込み
      }
      myfile.close(); //ファイルを閉じる
    }
  } else {
    Serial.println("sample.txt doesn't exist.");
  }

SDカードからデータを読み出す場合にファイルが存在しているかをexists()関数で確認します。引数には開くファイル名を含めたパスを指定します。

ファイルが存在するかを確認せずにopen()関数で読み込みを行っても読み込み失敗になるためexists()関数で確認してからファイルを開くかは好みになります。

ファイルが存在する場合はopen()関数でファイルを読み込み専用で開きます。第1引数にはファイルのパスを指定し、第2引数に読み込みを示すFILE_READを指定します。

ファイルのopenに成功するとFileクラスの変数(myfileオブジェクト)に戻り値として状態が引き継がれるためmyfileを使ってデータの読み込みを行います。

Fileクラスのavailable()関数を使って読み込むデータが存在するかを確認します。データが存在する場合は0より大きな値になるためRead()関数を使ってデータを読み込みます。例ではReadStringUntil()関数を使って改行コードが見つかるまでデータを読み込んでいます。

データを読み込んだ後はファイルを閉じるためclose()関数を使用します。

//ディレクトリのファイル名を表示
File root = SD.open("/");
File file = root.openNextFile();

while(file){
  Serial.println(file.name());
  file = root.openNextFile();
}

SDカード内のファイルを表示する例を示しています。open()関数に”/”を指定することで直下のディレクトリが開きます。openNextFile()関数でファイルを指定しながらname()関数でファイル名を取得しています。

while()で繰り返すことで存在するファイル名をすべて表示することができます。対象のファイルがない場合はfalseになるためwhile()から抜けます。

PR:スキマ時間で自己啓発!スマホで学べる人気のオンライン資格講座【スタディング】まずは気になる講座を無料で体験しよう!

動作確認

動作確認の回路図
動作確認の回路図

Picoを拡張基板のGrove Shield for Pi Picoに挿入します。SPIはデフォルトでGP16(SPI0RX SDA)、GP18(SPI0 SCK)、GP19(SPI0 TX)、GP17(SPI0 CSn)を使用します。GP17はスレーブセレクトですが、任意のDOを使用しても問題ありません。任意のスレーブセレクトを使用する場合はSD.begin()関数でピンを指定する必要があります。

GP26はデフォルトでA0ピンですがGrove Button(P)のDIとし、ボタンを押すとシリアルモニターにファイルに保存する内容を表示し、SDカードにシリアルモニターの内容を保存します。またボタンを5秒間長押しすると、SDカードにファイルを削除します。

シリアルモニターの結果
シリアルモニターの結果

電源をONすると「Raspberry Pi Pico」「Arduino IDE」をシリアルモニターに表示します。表示後は1秒毎に起動時間を更新してボタンが押されるのを待機します。ボタンを押すとシリアルモニターに「Grove Button->OK:」の文字列と経過時間を表示します。

SDカードに生成したファイル
SDカードに生成したファイル

SDカードを取り出してファイルが生成されているかを確認すると「SAMPLE.TXT」が生成できていることが確認できました。

ファイルの内容を確認
ファイルの内容を確認

ファイルを開くとシリアルモニターに表示した内容が保存されていることが確認できました。

スポンサーリンク

ソースコード全体

ソースコードは記事作成時点において動作確認できていますが、使用しているライブラリの更新により動作が保証できなくなる可能性があります。また、ソースコードを使用したことによって生じた不利益などの一切の責任を負いかねます。参考資料としてお使いください。

#include <SD.h>

#define PIN_DI1 A0
#define PIN_DO1 25

#define TIME_UP 0
#define TIME_OFF -1
#define BASE_CNT 10 //10msがベースタイマとなる
#define LED_ONOFF 50
#define TIM_REMOVE 500
#define TIM_1SEC 100
#define FILT_MIN 1
#define DI_FILT_MAX 4

struct DIFILT_TYP{
  uint8_t wp;
  uint8_t buf[DI_FILT_MAX];
  uint8_t di1;
};

String filepath = "sample.txt";
uint32_t beforetimCnt = millis();
bool flg;
File myfile;
int8_t timled = LED_ONOFF;
int8_t timdifilt = TIME_OFF;
int16_t timSdWait = TIME_OFF;
int16_t timsec = TIME_UP;
int8_t ledcnt;
DIFILT_TYP difilt;
uint32_t seccnt;

/* Local function prototypes */
void mainTimer(void);
void DiFilter(void);
void mainApp(void);

void setup() {

  Serial.begin(115200);
  pinMode( PIN_DO1, OUTPUT);
  pinMode( PIN_DI1, INPUT_PULLUP);

  Serial.print("Initializing SD card...");

  if(!SD.begin()){
    Serial.println("initialization failed!");
    while (1);
  }

  delay(3000);
  Serial.println("Raspberry PI Pico");
  Serial.println("Arduino IDE");

  timdifilt = FILT_MIN;

  for( uint8_t i=0; i < 10; i++ ){
    mainTimer();
    DiFilter();
    delay(10);
  }
}

void loop() {

  mainTimer();
  mainApp();
  DiFilter();
}

/* タイマ管理 */
void mainTimer(void){

  if( millis() - beforetimCnt >= BASE_CNT){
    beforetimCnt = millis();

    if( timSdWait > TIME_UP ){
      timSdWait--;
    }
    if( timdifilt > TIME_UP ){
      --timdifilt;
    }
    if( timsec > TIME_UP){
      --timsec;
    }

    if( timled > TIME_UP){
      --timled;
    }

  }
}
/* メイン処理 */
void mainApp(void){

  if( timled == TIME_UP ){
    timled = LED_ONOFF;
    ++ledcnt;

    if( ledcnt % 2 ){
      digitalWrite(PIN_DO1, HIGH);
    }
    else{
      digitalWrite(PIN_DO1, LOW);
    }
  }

  if( difilt.di1 == 1 ){
    if( flg == false ){
      flg = true;
      timSdWait = TIM_REMOVE;
      myfile = SD.open(filepath,FILE_WRITE);

      if( myfile ){ //ファイルが開けたら書き込む
        myfile.print("Grove Button->OK:");
        myfile.println(seccnt);
        myfile.close(); //ファイルを閉じる
        Serial.print("Grove Button->OK:");
        Serial.println(seccnt);
      }
    }
  }
  else{
    flg = false;
    timSdWait = TIME_OFF;
  }

  if( timSdWait == TIME_UP ){
    timSdWait = TIME_OFF;
    SD.remove(filepath); //ファイルを削除
    Serial.println("file delete");
  }
  if( timsec == TIME_UP ){
    timsec = TIM_1SEC;
    ++seccnt;
  }
}
/* DiFilter function add */
void DiFilter(void){

  if( timdifilt == TIME_UP ){
    difilt.buf[difilt.wp] = digitalRead(PIN_DI1);
    
    if( difilt.buf[0] == difilt.buf[1] &&
        difilt.buf[1] == difilt.buf[2] &&
        difilt.buf[2] == difilt.buf[3] ){ //4回一致を確認
          difilt.di1 = difilt.buf[0];
        }
    
    if( ++difilt.wp >= DI_FILT_MAX ){
      difilt.wp = 0;
    }

    timdifilt = FILT_MIN;
  }
}

関連リンク

Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方

ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方

PR: わからないを放置せず、あなたにあったスキルを身に着けるコツを教える テックジムPython入門講座の申込

最後まで、読んでいただきありがとうございました。

タイトルとURLをコピーしました