マイコン独立大作戦
SDカードインターフェースの製作
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
WindowsパソコンにUSB接続して使う現行方式はそれなりに便利ではありますが、ときとしてWindows
のしがらみから開放されて、小さいながらも独立した一個のパソコンとして機能したいと思うこともあります。
独立大作戦の作戦その1はCRTインターフェースボードの製作です。
作戦その2はキーボードインターフェースです。
そして作戦その3は、SDカードインターフェースです。
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
[第7回]
●SDカードの初期化
SDカードは電源投入後は本来のSDモードになっているため、SPIモードでのアクセスができません。
SPIモードでアクセスできるようにするためには、ちょっと面倒な初期化の手続きが必要です。
下にそのフローを示します。
本当は電源投入後またはSDカード挿入後1ms以上待ってから初期化の手続きに入れ、ということらしいのですが、まだテストの段階なので、マニュアル操作ですから、そのところは省略します。
最初にCSとMODIを1にしておいてSCKを74回以上出力します。
これでコマンドを受け付けるようになるので、CSを0にして、CMD0を送出します。
CMD0については後述します。
SDカードにコマンドを送ると返事(response)が送られてきます。
応答があるまでに多少時間がかかる場合があります。
正常な応答(01)があるまで読み続けます。
01を確認したら、CMD1を送ります。
CMD1についても後述します。
初期化が完了した場合には00が送られてきます。
00が送られてくるまでにかなり待たされる場合もあります。
00ではなくて01の場合は再度CMD1を送出します。
それ以外の場合には00または01が送られてくるまで読み続けます。
00、01が送られてくるまでにFFが連続して受信される場合があります。
response=01はアイドル状態(初期化プロセス実行中)であることを示しています。
00、01、FF以外の場合には何らかの異常があることを示していますが、左図の通りに実行していれば、そのほかのresponseは考えられません。
ずっと待っていてもFFまたは01のままという場合はハードに何らかの不具合(接続の不良、電圧異常、カード本体の異常など)があります。
●コマンドのフォーマット
SPIモードのコマンドは6バイトの固定長です。
第1バイトがコマンド本体ですが上位2ビットは01固定なので実質的にはコマンドを規定するindexは6ビットです。
先頭の01はスタートビットと考えられます。
続く4バイトはargument(引数)でコマンドによってはないものもありますが、6バイト固定長なので、引数がない場合は00 00 00 00を送ります。
最後のCRCは7ビットですが、最終ビット(ビット0)の1を付加して8ビット長になっています。
最後の1ビットはストップビットと考えられます。
CRCはSDモードでは必要ですが、SPIモードでは読み飛ばされますからダミーデータで構いません。
初期化の最初に送るCMD0はSPIモードになる前に送るため、正規のCRCが必要です。
●CMD0
[01000000]
ソフトウェアリセットコマンドです。
引数はありません。
初期化の最初に送るCMD0には正規のCRCが必要です。
CRCについてはのちほど説明をします。
ここではとりあえず計算の過程は省略して結果のみ記します。
CMD0のCRCは4Aになります。
1001010です。
その最後にストップビットを加えて8ビットにしますから、
10010101=95HがCRC部の8ビットの値となります。
つまりCMD0は、
40 00 00 00 00 95
の6バイトを送ることになります。
コマンド送出後はSDカードから01が送られてくるのを待ちます。
SDモードからSPIモードに切り換わると01が送られてきます。
なおSPIモードになるまではSCKを400KHz以下にして送出しなければならないようです。
●CMD1
[01000001]
初期化完了コマンド?です。
SPIモードでのREAD/WRITEが使えるようにするために必要ですが、2GBを越えるSDカード(SDHCなど)には使えません。
引数はありません。
CMD1を送る時点でSPIモードになっていますから、CRCは必要ありませんが、6バイト固定長なので、何かを送らなければなりません。
ダミーデータとしてFFを送ることにします。
つまりCMD1は
41 00 00 00 00 FF
の6バイトを送ることになります。
レスポンスが01→00になるまで送り続けます。
00が来たら、それ以後はREAD/WRITEが可能になります。
●CRC7
CRCはCyclic Redundancy Codeの略です。
Redundancyはお目にかかったことのない語ですが、冗長性とでも訳すとなんだかわかったような気になります。
CRCの日本語訳は巡回冗長検査だそうです。
いやあ。
やっぱりわかりませんね。
しかしなんとか計算しないことには先には進めません。
ネット上にはCRCの計算式とか考え方とかいろいろ散見できるのですけれど、どうも説明が難しいのか、私の頭が悪いのか、いまひとつよくわかりません。
まあ、しかし、いつものことなのですけれど、そこで諦めてしまってはいけません。
投げてしまいたくなるところを、じっと我慢してもうひとふんばりしておりますと、突然「わかったぞぉ」という至福の時がやってまいります。
CRCは計算のもとになる多項式によっていろいろなバリエーションがあるようですが、SDカードコマンドの場合には7ビット長のCRC7を使います。
CRC7は計算の対象になるビット列に対して8ビットの多項式で順に演算を行ないます。
多項式はX7+X3+1で、ビット表現では10001001になります(ここがよくわからないところなのですが、そういうものなのだそうです。ビット7とビット3とビット0が1ということだと解釈しました)。
それでこの多項式の値(10001001)で対象になるビット列を除算することでCRCが求まる、という説明が多いのですが、それがまたよくわかりません。
よくわからないのですが、ビットごとに繰り下がりのない除算を行なうということのようです。
演算の結果を見てみますとどうやらビット列と多項式のXORをとっているらしいということがわかってきました。
以下具体的な計算例としてCMD0のCRCの算出例について説明をします。
CRC部を除いたCMD0は40 00 00 00 00です。
CRC計算は算出したCRC自身も計算の対象にしますから、対象になるビット列は、初期値としてCRC部を0と置いた、
01000000 00000000 00000000 00000000 00000000 0000000
の47ビットです。
このビット列に対して、先頭の1をあわせて多項式との間でXORの演算を行い、その結果に対して順次下位桁に向かって同じようにXORの計算を行なっていきます。
01000000 00000000 00000000 00000000 00000000 0000000 初期値
1000100 1
0000100 10000 最初のXOR演算の結果の値(下の桁を必要なだけ追加して次の計算に備える)
100 01001 結果の値の先頭ビットが0の場合には、1になるところまで演算をパスする
000 11001 2回目のXOR演算の結果の値
この計算を最後のビットの桁が多項式の末尾の桁と一致するところまで繰り返します。
●実際の計算プログラム
ネット上でのプログラム例は皆様Cをお使いのようですけれど、こういう計算はやっぱりマシン語の独壇場であると私は思います。
ええ。
私は例によりまして、ND80Z3.5を使いましてマシン語で演算を行ないました。
下がCRC7の計算プログラムです。
2016/11/2 11: crc7.txt END=8140 ;;; CRC7 for SD card PCI command ;16/11/2 ; ORG $8100 ; ADISP=$1015 CRLF=$101B REENT=$1033 HXDP2=$104B ; 8100 C30881 JP START0 8103 00 CMD1:DB 00 8104 00 DB 00 8105 00 DB 00 8106 00 DB 00 8107 00 CMD5:DB 00 8108 CD3281 START0:CALL SHFTL 810B 0627 LD B,27;=39 810D 1E89 LD E,89 810F 210381 LOOP:LD HL,CMD1 8112 7E LD A,(HL) 8113 B7 OR A 8114 F21981 JP P,NEXT;bit7=0 8117 AB XOR E 8118 77 LD (HL),A 8119 3A0381 NEXT:LD A,(CMD1) 811C 67 LD H,A 811D CD4B10 CALL HXDP2 8120 3E20 LD A,20 8122 CD1510 CALL ADISP 8125 CD3281 CALL SHFTL 8128 05 DEC B 8129 C20F81 JP NZ,LOOP 812C CD1B10 CALL CRLF 812F C33310 JP REENT ; ;shift left 8132 210781 SHFTL:LD HL,CMD5 8135 0E05 LD C,05 8137 B7 OR A;clear CF 8138 7E SHFTL2:LD A,(HL) 8139 17 RLA 813A 77 LD (HL),A 813B 2B DEC HL 813C 0D DEC C 813D C23881 JP NZ,SHFTL2 8140 C9 RET ; ;END ADISP =1015 CMD1 =8103 CMD5 =8107 CRLF =101B HXDP2 =104B LOOP =810F NEXT =8119 REENT =1033 SHFTL =8132 SHFTL2 =8138 START0 =8108 |
これだけの簡単なものです。
ビット列は47ビットですが、8ビットの多項式で除算を行ないますから、実際のシフト回数は39回です。
先ほどの説明では多項式のほうを右にシフトしていきましたが、プログラムでは初期値を左シフトします。
最後のCRC部の初期値は0ですからそれはシフトの過程で自然にセットされてしまいます(左シフトのときに最下位ビットには0が入れられる)。
ですので初期値はCRC部を除いた5バイトにします。
SPIコマンドはコマンドの最上位ビットは常に0です。
そのためさきほどの説明例のように最初の計算で第1バイトの下位7ビットと第2バイトの上位1ビットが計算の対象になってしまいます。
ここがプログラムのちょいとした工夫です。
計算を始める前に全体を1ビット左にシフトします。
こうすることでXOR計算は常に最上位の8ビットに対して行なうだけで済むようになり、非常にすっきりしたプログラムになります。
計算が進むにつれて下位桁は0で埋まってきますから、いつも全部の桁を左シフトするのは冗長なのですが、シフトの演算は1回あたり8ビット単位で5回行なうだけですから、余計な判断をするよりもこのようにしたほうがかえってプログラムが簡単になります。
計算の途中の過程と最終結果は常に最上位の8ビットに置かれます。
プログラムの目的としては最後の結果さえ求まればよいのですが、参考までに39回の全部の途中過程も表示するようにしました。
最上位ビットが0の時は計算しないままの値がそのまま表示され(前回の値を1ビット左シフトした値が表示されます)、最上位ビットが1のときは、多項式とXORした結果が表示されます。
HXDP2はHレジスタの値を16進数2桁で表示するサブルーチンで、ADISPはAレジスタの値をASCIIコードとみなして1文字を表示するサブルーチンで、ともにZB3BASICのシステムサブルーチンです。
下が上のプログラムをND80Z3.5で実行して、CMD0のCRCを求めた結果です。
>/ld crc7.bin,8100 loading CRC7.BIN ...0041(65)bytes loaded,from 8100 to 8140 >cm 8103 8103 00-40 8104 00- >jp 8100 09 12 24 48 19 32 64 41 0B 16 2C 58 39 72 6D 53 2F 5E 35 6A 5D 33 66 45 03 06 0C 18 30 60 49 1B 36 6C 51 2B 56 25 4A |
39回のシフト演算を繰り返した結果、4Aが求まりました。
SDカードインターフェースの製作[第7回]
2016.11.2upload
前へ
次へ
ホームページトップへ戻る