公開日:2019/07/06
title.png

6th Season


#6-5 microSDカードI/Fを自作する

 今回は、microSDカードI/F(ハードウェア)を自作(手作り)してみようと思います。記事を読んでいただくとわかりますが、「自作」って程の事は無いです。microSDカードを購入した時についてきた、microSD→標準SDカード変換アダプタをmicroSDカードのソケットとして流用します。

SDメモリカードの仕様調査

 まずは、SDカードの仕様から調べてみましょう。

  SDメモリカードの仕様を検索(別ウインドウが開きます)

 記憶容量の増加、アクセス速度の向上、低電圧動作、等々、何だかずいぶんと進化しているようですね……。Arduino標準ライブラリの中から、SDライブラリを使わせてもらおうと思っているので、アクセス可能なSDカードのファイルシステムは、FAT16またはFAT32に限られます。つまり、SDカードの仕様としては、SDカード(〜2Gバイト、FAT16)または、SDHCカード(〜32Gバイト、FAT32)が対象です。

 ピン配置を確認しておきましょう。

ar65_sdpin.jpg

 裏面を見ると、端子が9個見えます。並びはイレギュラですが、「SD Association」のドキュメントから参照しています。以下、ArduinoのSDライブラリが使用する、SPIモードの端子機能です。

SPIモード Arduino UNO
ピン# 名称 説明 接続端子
1 CS チップセレクト(アクティブLOW)
=LOWを入力すると、SPIモードになる
D10(SS)等
2 DI データ入力 D11(MOSI)
3 VSS 接地 GND
4 VDD 電源:2.7〜3.6[V] 3.3V電源出力
5 SCLK クロック入力 D13(SCK)
6 VSS2 接地 GND
7 DO データ出力 D12(MISO)
8 RSV リザーブ(SPIモードでは未使用)
9 RSV

 ご覧の様に、SDカードは3.3V定格ですので、5VのArduino UNO等と接続するためには、3.3V電源とレベルシフタが必要になります。あと、SDカードとコンタクトして、信号を引き出すためのソケットですね。
 以下、1つ1つクリアして行きましょう。

電源の確保

 まず、SDカードの消費電流ですが、調べてみると、メーカによって多少のばらつきがあります。それ以上に深刻なのが、記憶容量による違い。記憶容量が大きくなる程、消費電流も大きくなります。今回実験に使ったのは、だいぶ昔に買った2GバイトのmicroSDカードで、100mA(MAX)@25MHz辺りが消費電流スペックの様です。
 Arduino UNOには、ボード上に5V→3.3Vの降圧レギュレータが載っていて、3.3V電源出力があるのですが、米粒の様に小さなレギュレータで、実はほとんど電流を引き出せません。今回使用するELEGOO UNO R3(互換機)のデータシートによると、3.3V電源から引き出せる電流は、MAX 50mAとなっていました。

※色々なUNO互換機が発売されていますが、機種によって搭載されているレギュレータが異なります(電流スペックも異なる可能性がある)。所有する機種のデータシートで確認して使ってください。

 スペック面では、繋げませんね。SDカード(MAX 100mA)>UNO(MAX 50mA)ですから。ただ、周波数ですが、UNOのSPIは最大でも8MHz(=fcpu/2)でしか通信できないユニットなので、平均消費電流は100mA@25MHzに対して8/25になることが期待でき、MAX 32mA@8MHzと見ることができます。これなら、何とか、UNOの3.3V電源出力を使用できるでしょう。

※大容量(2Gバイト以上)のSDカードを使う場合、消費電流スペックがさらに大きな値になるので、SDカードのデータシートで確認して使ってください。UNOの3.3V電源出力で不足する場合は、別途所望の電流を供給可能なレギュレータを外付けに接続する必要があります。

 「平均電流」ではなく、「ピーク電流」で考えると、ちょっと不安が残りますが、「OK、 UNOの3.3V出力で間に合う(に、違いない)」と、思い込んで実験することにします(1^^;)。マネして実験する場合は、各自の責任において、お願いします。万一、レギュレータの発熱などの異常を検知した場合は、直ちに電源を遮断してください。

レベルシフタ

 UNOは5V I/Oなので、3.3V I/OのSDカードとインターフェイスするためには、レベルシフタが必要となります。レベルシフタは、ESP32やENC28J60など、3.3Vチップとのインターフェイス用に購入済みなので、これを使います(下図)。

ar47_lvshft.jpg

 このレベルシフタはプッシュプルタイプのドライバではないので、信号遅延には注意が必要ですが、低電圧(LV)→高電圧(HV)の方向でも、高電圧(HV)→低電圧(LV)の方向でも、何のコンフィギュレーションもなく、双方向に使用可能な優れものです。上のモジュールで、4チャンネル分のレベルシフトが可能です。

 レベルシフタの必要性については、「#4-7 ENC28J60を使う」で解説済みです。

ソケット

 最大の難関(?)が、ソケットです(1^^;)。売っているものを買ってくれば良さそうなものですが、売っている店まで出向くのが一苦労。と言って、ソケット1個だけを通販で購入しようにも、配送料とか手数料とか馬鹿にならず、結局Amazon辺りで出来合いのSDカードI/Fモジュールを買ってしまった方が安上がりです(次節参照)。

 そこで目を付けたのが、microSDカード→標準SDカード変換アダプタです(以下図、上。下に見えるのはmicroSDカード本体)。

ar65_sdadop.jpg

 microSDカードを変換アダプタのスロットに差し込むと、標準サイズのSDカードとしてカードリーダに挿入できる、と言う代物です。昔は、microSDカードを購入した時に、パッケージの中に上図の様な変換アダプタがおまけで入っていたので、探したら5〜6個見つかりました。1つ潰して、microSDカード用の「ソケット」として使おうと思います。
 以下、ピンヘッダを7本カットして、変換アダプタの端子部分に重ねてみたところです。

ar65_sdhdr.jpg

 左右両端の端子(ピン#8と9)とは接触していませんが、これらの端子はSPIモードで未使用の端子なので、繋がなくてOKです。
 また、よ〜く見ると、ピッチが合っていません。しかし、コンタクトすべき端子は7本と少なかったため、何とかはみ出すことなく半田付けできそうです。

ar65_sdsock.jpg

 半田付け後の写真(上図)、相変わらずヒドイ出来栄え……orz。
 半田ごてを当てすぎて、プラスティック部分が溶けてしまっています。心配だったので、「#2-9 抵抗値を測定する」で作った抵抗測定回路とプログラムを使って、以下を確認しました。

 結果、「問題なし」でした。出来栄えはさておき、実用には耐え得るでしょう。これでブレッドボードに挿せるので、ジャンパ線で配線することができます。

 最大の難関も通り抜け、すべての部品が揃いましたので、まとめておくと、以下回路図となります。

ar65_schem.png

※上の回路では、SPIクロックが高速(1〜8[MHz])なため、L/Sの信号レベルが十分に上がり切らないことが判りました。こちら(「#9-6 SDカードI/F回路の改定」)の記事で回路を改定したので、考察も含め、ご参照ください。

 今回は、SDカードのCS端子にD10(SS)を接続します。もし、複数のSPIモジュールを接続する場合には、各モジュールにユニークなSS端子を割り当てる必要があります。

「工作面倒」な、方は……

 SDカードI/Fモジュールが販売されているので、それを購入して使うのが手っ取り早いです。

  Amazonで検索(別ウインドウが開きます)

 色々なタイプがあるみたいなので、よく確かめてご購入ください。

 時々、「TFカード」と記載されている場合がありますが、「TFカード」と、「microSDカード」は、電気的にも、フォームファクタ的にも、同じ物らしいです。microSDカード用のソケットになりますので、miniSDカードや標準サイズのSDカードは挿せません。

自作した治工具を使って実験

 自作した治工具を使って、実験してみます。勿論、SDカードI/Fモジュールを購入した場合(前節)でも、以下の実験は可能です。出来合いのモジュールと、先輩方がデバッグしてくれた標準ライブラリ(SD.h)を使うだけなので、「動いて当たり前」、ワクワク感は無いでしょうけど……。
 自作した場合は、ちゃんと設計した通りに動くのか、ワクワク・ドキドキの時間です。

 はやる気持ちを抑えて、まずはmicroSDカードを準備します。筆者は2GバイトのmicroSDカードを使いました。ノートPCのSDカードスロットに挿入して、フォーマットを確認したところ、ファイルシステムがNTFSになっていました。SDライブラリは、FAT16またはFAT32のファイルシステムであることが必要なので、Windowsで「既定」フォーマットし直しました。結果、FAT16でフォーマットされたようです。

※フォーマットするとデータがすべて消去されるので、必要に応じ、データのバックアップを取ってからフォーマットしてください。また、もし、規定値がFAT16/32以外のファイルシステムだった場合は、FAT16/32のどちらかを選択してフォーマットしてください。

 適切に数個のテキストファイルをmicroSDカードに転送しておきます。SDライブラリがハンドリングするのは、8.3形式と呼ばれるファイル名です。8.3形式とは、ファイル名8文字+拡張子3文字と言う、MS-DOS時代に使われていたファイル名の形式です。
 現在のWindows10等でも、8.3形式のファイル名は有効で、例えば、「ABCDEFGHIJK.XYZ」などの様に、8.3形式を超えるファイル名の場合、8.3形式に適合するよう、内部的にユニークな名前(例えば「ABCDEF~1.XYZ」)が割り振られています。
 予め、Arduinoでファイルリストを表示する場合を想定して、8.3形式のファイル名に変更してmicroSDカードに転送することを推奨します。また、マルチバイト文字を使ったファイル名も、SDライブラリでエラーになってしまって、表示できない様でした。7ビットASCII文字のみのファイル名を使いましょう。

※Windowsで参照している間は、ちゃんと、長い名前が見えています。Arduinoで確認すると、8.3形式の方しか見えないので、予めWindowsの内から8.3形式にしておきましょうネ、と言う提案です。

 まずは、SDライブラリのサンプルプログラムをそのまま使って、自作治工具のテストを実施します。Arduino IDEを起動して、「ファイルメニュー ⇒ スケッチ例 ⇒ SD ⇒ listfiles」を開きます。
 一か所、setup()関数内の「SD.begin(4)」となっているところを探して、「SD.begin(10)」に変更してください。SDカードI/Fのスレーブセレクト(SS)にD4端子を使う設定になっていたところを、今回の回路に合わせてD10に変更します。
 これだけでOKなので、Arduino UNOを接続してサンプルプログラムlistfileを書き込み、シリアルモニタを開いてください。

  実行サンプル:
  Initializing SD card...initialization done.
  SYSTEM~1/
    INDEXE~1  76
    WPSETT~1.DAT  12
  MML_SE.TXT  871
  MML_KIYO.TXT  266
  MML_KOI.TXT  274
  MML_RURU.TXT  79
  MML_RED.TXT  866
  TST_EX~1.UNK  59
  TST_DATA.CSV  96
  done!

※もし、うまく表示されなかった場合、どこかの回路が間違っています。自作したソケット代わりの治工具か、Arduino ⇔ レベルシフタ ⇔ ソケット間の配線ミスなどが原因として考えられます。

 「SYSTEM~1」ディレクトリは、Windowsからは見えていませんでした。恐らく、不可視属性の付いたシステムディレクトリでしょう。その他、Windowsから転送しておいたファイルは全て見えています。一部、ファイル名が8.3形式に収まらなかったファイルは、「SYSTEM~1」、「TST_EX~1.UNK」などの様に、短縮されたファイル名で見えています。

SDカードの参照・操作プログラムを作る

 SDライブラリのサンプルプログラムは正常動作したので、もう少し踏み込んで、SDカードの内容を参照・操作可能なプログラムを組んでみます。

初期設定

 Arduino標準ライブラリの中から、SDライブラリを使用します。

  #include <SD.h>

 先程のサンプルプログラム(listfiles)を参考に、setup()関数内でSDカードの初期化を実施します。

  // sd memory card i/f
  Serial.println ("OK+ SD init ...");
  if (!SD.begin (pin_SS))
  {
    // ERROR:
    Serial.println ("ERR: SD init failed");
    for (;;)
    {
      digitalWrite (pin_PILT, LOW);
      delay (250);
      digitalWrite (pin_PILT, HIGH);
      delay (250);
    }
  }
  Serial.println ("OK: SD init done.");


 SDカードの初期化に失敗した場合(SDカードが挿さっていなかった場合を含む)は、パイロットLED(pin_PILT=D6)を点滅させて、無限ループに落としています。
 UNOには、オンボードのLED(LED_BUILTIN)があるので、わざわざ、外付けにLEDを繋がなくても良さそうなものです……。実はLED_BUILTINは、UNOではD13ピンであり、SPIのSCK端子と同じです。つまり、SPIユニットを使用すると、実質、LED_BUILTINは使用できなくなります。

 実は今回、USBなどで接続してシリアルモニタ通信や電源を供給するのではなく、eneloopモバイルバッテリを使って電源を供給し、独立動作でデータをロギングするアプリケーションを想定しています。従って、外部(ユーザ)に対して「正常動作していますよ」とか「エラーが発生しましたよ」などと伝える手段として、LED_BUILTINは有用だったのですが、SPIと共存できないため、外付けにLEDを接続するようにしました。pin_PILT(=D6)がそれです。パイロットLEDを使う場合は、UNOのD6端子に、適切な電流制限抵抗とLEDを接続してください。

ファイル内容の表示

 UNIXのユーティリティで言うところの"cat"に相当する機能を作り込みます。

  void cat_file (
  const char *fname)
  {
    // cat file <fname> to Serial
    File fin;

    // parameter
    if (fname==NULL || (*fname)=='\0')
    {
      // ERROR: ignore
      Serial.println (F("ERR: cat_file: empty file name"));
      return;
    }

    // cat
    fin=SD.open (fname,FILE_READ);
    if (fin)
    {
      while (fin.available ())
        Serial.write (fin.read ());
      fin.close ();
    }
    else
    {
      Serial.print ("ERR: cat_file: open ");
      Serial.println (fname);
    }
  }


 引数でファイル名(fname)を受け取ります。fin=SD.open()でファイルを開き、Fileクラスのインスタンスを受け取ります。FileクラスはSerialなどと同様なstreamクラスを継承しているので、fin.read()など、おなじみのメソッドを使ってアクセス可能です。上のコードでは、fin.available()の間、fin.read()したデータをSerial.write()でシリアルモニタにリダイレクトしています(以下、該当箇所抜粋)。

  while (fin.available ())
    Serial.write (fin.read ());


 ファイルのアクセスを終了したら、fin.close()でファイルを閉じることをお忘れなく。

 同様に、ファイルをコピーするコマンドも作っておきます。エラー処理を省いた基本部分は以下の通りです。

  fin=SD.open (sname,FILE_READ);
  fout=SD.open (dname,FILE_WRITE);
  while (fin.available ())
    fout.write (fin.read ());
  fout.close ();
  fin.close ();

コマンドの目標仕様

 以下、シリアルモニタで受け付けるコマンドの目標仕様です。

 最初に使った本格的なOSがOS-9だったので、【dir】とか【del】とか、OS-9ライクなコマンドも用意しておきました(1^^;)v。その他、ファイル・ディレクトリ操作と、SDカードに対するデータの送受信ができるような目標仕様になっているので、一通り必要な処理を実行することが可能だと思います。
 XMODEMによる送受信やプロトコルの詳細については、「#5-3 高速ソフトウェアシリアル(続編)」を参照してください。「#5-3 高速ソフトウェアシリアル(続編)」では、受信データを捨てていたり、乱数データを送信したりと、ちょっとイレギュラな使い方をしましたが、基本的なところは解説してあります。今回は、ちゃんとしたデータを送受信するように改造して使います。

 さて、ちょっと長くなってきましたので、プログラムの完成は次回に持ち越します。ヘンなところで切ってしまってゴメンナサイ。


 自作したSDカードI/Fを使って、microSDのデータにアクセスできることを確認できました。SDライブラリもそのまま使えたし、応用が広がりそうです。上でご紹介した通り、出来合いのモジュールを購入しても、大した金額ではありませんので、何かデータを記録したり、読み込んだりする必要のあるアプリケーションの場合には、導入を検討してみてはいかがでしょうか?


btn_arduino.png

btn_prev.png btn_next.png

home