今回は、microSDカードI/F(ハードウェア)を自作(手作り)してみようと思います。記事を読んでいただくとわかりますが、「自作」って程の事は無いです。microSDカードを購入した時についてきた、microSD→標準SDカード変換アダプタをmicroSDカードのソケットとして流用します。
まずは、SDカードの仕様から調べてみましょう。
SDメモリカードの仕様を検索(別ウインドウが開きます)
記憶容量の増加、アクセス速度の向上、低電圧動作、等々、何だかずいぶんと進化しているようですね……。Arduino標準ライブラリの中から、SDライブラリを使わせてもらおうと思っているので、アクセス可能なSDカードのファイルシステムは、FAT16またはFAT32に限られます。つまり、SDカードの仕様としては、SDカード(〜2Gバイト、FAT16)または、SDHCカード(〜32Gバイト、FAT32)が対象です。
ピン配置を確認しておきましょう。
裏面を見ると、端子が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チップとのインターフェイス用に購入済みなので、これを使います(下図)。
このレベルシフタはプッシュプルタイプのドライバではないので、信号遅延には注意が必要ですが、低電圧(LV)→高電圧(HV)の方向でも、高電圧(HV)→低電圧(LV)の方向でも、何のコンフィギュレーションもなく、双方向に使用可能な優れものです。上のモジュールで、4チャンネル分のレベルシフトが可能です。
レベルシフタの必要性については、「#4-7 ENC28J60を使う」で解説済みです。
最大の難関(?)が、ソケットです(1^^;)。売っているものを買ってくれば良さそうなものですが、売っている店まで出向くのが一苦労。と言って、ソケット1個だけを通販で購入しようにも、配送料とか手数料とか馬鹿にならず、結局Amazon辺りで出来合いのSDカードI/Fモジュールを買ってしまった方が安上がりです(次節参照)。
そこで目を付けたのが、microSDカード→標準SDカード変換アダプタです(以下図、上。下に見えるのはmicroSDカード本体)。
microSDカードを変換アダプタのスロットに差し込むと、標準サイズのSDカードとしてカードリーダに挿入できる、と言う代物です。昔は、microSDカードを購入した時に、パッケージの中に上図の様な変換アダプタがおまけで入っていたので、探したら5〜6個見つかりました。1つ潰して、microSDカード用の「ソケット」として使おうと思います。
以下、ピンヘッダを7本カットして、変換アダプタの端子部分に重ねてみたところです。
左右両端の端子(ピン#8と9)とは接触していませんが、これらの端子はSPIモードで未使用の端子なので、繋がなくてOKです。
また、よ〜く見ると、ピッチが合っていません。しかし、コンタクトすべき端子は7本と少なかったため、何とかはみ出すことなくはんだ付けできそうです。
はんだ付け後の写真(上図)、相変わらずヒドイ出来栄え……orz。
はんだごてを当てすぎて、プラスティック部分が溶けてしまっています。心配だったので、「#2-9 抵抗値を測定する」で作った抵抗測定回路とプログラムを使って、以下を確認しました。
結果、「問題なし」でした。出来栄えはさておき、実用には耐え得るでしょう。これでブレッドボードに挿せるので、ジャンパ線で配線することができます。
最大の難関も通り抜け、すべての部品が揃いましたので、まとめておくと、以下回路図となります。
※上の回路では、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カードの内容を参照・操作可能なプログラムを組んでみます。
Arduino標準ライブラリの中から、SDライブラリを使用します。
#include <SD.h>
先程のサンプルプログラム(listfiles)を参考に、setup()関数内でSDカードの初期化を実施します。
// sd memory card i/f
Serial.println ("OK+ SD initialize...");
if (!SD.begin (pin_SS))
{
// ERROR:
Serial.println ("ERR: SD initialize failed");
for (;;)
{
digitalWrite (pin_PILT, LOW);
delay (250);
digitalWrite (pin_PILT, HIGH);
delay (250);
}
}
Serial.println ("OK: SD initialize 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ライブラリもそのまま使えたし、応用が広がりそうです。上でご紹介した通り、出来合いのモジュールを購入しても、大した金額ではありませんので、何かデータを記録したり、読み込んだりする必要のあるアプリケーションの場合には、導入を検討してみてはいかがでしょうか?