はじめに
dsPICマイコンボードA33FにはMicroSDカードインターフェイスが付いています。ここではdsPICのSPIを使ってSDカードにアクセスし、データの読み書きを行ってみます。SDカードはMicroタイプで2007年11月現在で2Gバイトのものが売られており、コンビニなどでも購入でき、しかも安価な大容量外部記憶装置です。
このページでは16進数表示を 0x00 又は 00h のように表します。
※このページで紹介する内容はあくまでも一例です。個別の作成のご相談ご質問はお答えできませんのでご了承下さい。このページと同じ内容についてのご質問についてはロボット掲示板にてお願いいたします。
SDカードについての概要
以下の情報は2007年11月現在のものです。ご注意ください。細かい点はかなり割愛していますので興味がある方はキーワードでインターネットを検索して深く勉強するのも良いでしょう。このページの内容をちょっと改造するだけでも詳しいSDカードの知識が必要になります。
SDカードは1990年代後半に、SanDisk社、松下電器、東芝が開発したメモリーカードで、現在ではデジカメやビデオカメラ、音楽再生装置、携帯電話などの記憶装置として広く使われており、コンビニなどで簡単に入手することができます。大きさにより SDカード miniSDカード microSDカード の3種類があり、A33Fには現在一番小さいタイプのmicroSDカードのインターフェイスが実装されています。現時点で2Gバイトの容量のカードが約7千円と、容量が大きい割には非常に安価な記憶装置と言えます。
SDカードは一見、単なるメモリーカードに見えますが、実際にはメモリ以外にもコンピュータが入っており、データの読み書きを(比較的)簡単にできるようになっています。
SDカードは基本的にOSが搭載されているコンピュータで使用されることを想定しており、あらかじめ一般的なファイルシステムであるFATシステムが書き込まれています。FATシステムはフォルダを作ったりファイル名を管理したりするシステムです。以後、マイコンでSDカードの中身を書き換えるとFATシステムが壊れてPCなどで読み取れなくりますのでご注意ください。
尚、このページではFATシステムまでは解説しませんが、がんばって勉強すれば、FATシステムを壊さないようにしつつデータを読み書きすることができるでしょう。つまり、マイコンボードで読み書きしたデータをPC(FATシステムを使っている)で読み書きもできることになります。FATシステムは少し難しいので、ビギナーの方はSDカードを単なる大容量記憶装置として使うだけでもよいでしょう(とりあえず)。
SDカードは2つのモードで動かすことができます。1つはMMCモード、1つはSPIモードです。これらは電源投入後の初期化処理でモードを選択します。
MMCモードは、SDカードを最高の速度で動かせるモードで、本来のカードの性能を全て引き出すことができます。但し、データラインが多かったり(MMCモードは4本、SPIモードは1本)命令が複雑だったりするので、ホビーで使われるような低機能なマイコンでは制御がむずかしいモードです。A33FのマイコンはMMCモードを動かすのに十分な性能ですが、制御線を多く使われるMMCモードではなく、次に説明するSPIモードで動かします。
SPIモードは、マイコンなどに多く実装されているシリアル通信機能であるSPI(Serial Peripheral Interface)で読み書きすることが出来るモードです。MMCモードは電源を除いて6本接続する必要がありますが、SPIモードでは4本しか接続する必要がありません。但し読み書きの速度は遅くなります。A33FのCPUであるdsPIC33FにはSPI専用として使えるポートが2つついており、SPI1がmicroSDインターフェースに接続されています。SPI2は別途説明する音声コーデックICに接続されています。
SDカード購入についての注意
現在、普通のSDカードとSDHCというタイプの2種類があります。このページで解説している方法は通常のSDカードの方です。SDカードは2GBまでしか記録できませんが、SDHCは32GBまで記録する仕様になっています。よって、現在では2GB以下のSDカードの多くはこのページの方法で扱えるはずです。このページと同じように実験する場合はSDHCの方を買わないように注意してください。きちんとしたメーカーのものでしたらSDHCの場合はSDのロゴの下にHCと入るはずです。HCは「Hi
Capacity」の略のようです。
もう少し細かく説明すると、これまでのSDカードは出荷時にFAT16(FAT12のもあるらしいが)でフォーマットされていましたが、HCの方はFAT32になっています。初期化の手順、つまり初期化時のコマンドと送信順番が異なりますので、このページと同じ方法でSDHCを動かそうとしても、カードの初期化−>起動ができません。初期化時にソフト的にSDなのかSDHCなのか判定する方法もあります(ある命令に反応するとどちら、とか)が、ここでは割愛しました。興味のある方は、これらのキーワードで検索してください。
SDカードはMMCカードというものをベースに作られており、形状がほぼ同じ(厚さが若干違う)でMMCカードというものもあります。このページではコンビニなどで簡単に入手できるSDカードの場合のみを説明しています。SDカードはMMCカードの上位互換です。MMCカードでも同じように使うことができるかもしれませんが、本ページではMMCカードでの実験を行っておりません。
なお、本来はMMCカードの中の種類のSDカードなのであり、SDカードもMMCカードと言えると思います。また多くの書籍ではSDカードをMMCカードとして説明しています。但し、本ページではメーカーによる微妙な違いなどもあるとの情報より、あえてMMCカードとは言わずにSDカードとして説明しています。
SPIについて
一般的なマイコンに搭載されているシリアル通信機能の代表的なものに、 UART(ユーアートと読む)、 SPI、
I2C(アイ・スクエア・シーと読む) などがあります。 UARTはコンピュータ間の通信などによく使われます。 I2Cは専用チップの設定などによくつかわれ、他の2つと違って複数の装置を1つの通信経路に接続できます。 SPIはメモリ装置などと高速でデータをやりとりするのによく使われます。今回のmicroSDはこのSPIでデータをやり取りします。
高速で、とは言ってもシリアル通信なのでRAMのバス接続(データ線やアドレス線が数十本つながっている)などと比べると非常に遅く、ランダムアクセスなどは苦手です。SDカードも、あるきまったサイズを1つのブロックとして設定し、ブロック単位で読み書きします。通常は1ブロック512バイトに設定します。
SPIはマスタ(主人)とスレイブ(奴隷)に別れ、マスタ側でクロックを生成してそのクロックのタイミングでデータをやりとりします。2つの機器はお互いにシフトレジスタというものをもっており、1ビット受信すると同時に1ビット送信します。たとえば1つのデータを8ビットとすると、8クロックで1つのデータを送信しつつ受信します。つまり8クロックでお互いのシフトレジスタの内容が交換されます。
通信は全て、マスタ側での操作により行われます。スレイブ側で積極的になにかを通信することはしません。
下はA33Fボード上の、dsPICとSDカードのブロック図(機能を簡単に表した図)です。この構成(A33Fの接続)ではdsPIC側が常にマスターとなります。SDカードにはSPIスレイブの機能が入っています。
SPI1SRは「SPI1のシフトレジスタ」です。シフトレジスタは、1ビットづつ入ってきたデータを順番に格納するもので、dsPIC33Fの場合は16ビットの幅があります。設定により8ビット幅で使うこともできます。今回は8ビット幅で使います。
SPI1BUFは「SPI1のバッファ」です。プログラムではここにアクセスして操作をします。シフトレジスタを直接操作することはできません。SPI1BUFも16ビットの幅がありますが設定により8ビットにもできます。
たとえば、今、dsPICからSDカードに1バイト、つまり8ビット(8クロック)分のデータを送りたいとします。この場合は以下の流れとなります。
<送信>
1)バッファ(SPI1BUF)にデータを入れる。
2)自動的にSPI1SRにデータがセットされる。
3)自動的に8クロックが生成され、SDO1を通じてSDカードに送られる。
このように送信の場合はSPI1BUFにデータを書き込むだけで自動的に送信されます。
次に受信です、
<受信>
1)バッファに適当な値を入れる。このとき、SDカードではすでにシフトレジスタにデータが入っているとする。
2)自動的に8クロック生成される。
3)SPI1BUFに入っていたデータはSDO1から出て行き、SDI1からはSDカードのシフトレジスタのデータが入ってくる。
4)8クロック終わった時点でSPIのステータスが受信終了になる。
5)受信終了になったか確認した上で、SPI1BUFのデータを読む
というようになります。つまり、データはぐるぐる回っているような状態になっています。
他の機能と同じように、dsPICのSPIの端子も他の機能と共有になっていますので、SPIを有効にするかどうかなどの設定ができるようになっています。
dsPIC33Fデータシートより、
また、割り込みを設定することもできます。今回はSPIの割り込みは使いませんが、他の割り込み処理と同じやり方で使うことができます。
回路図・SDカード端子
ハードウェアの接続は次のようになっています。
※全回路図は製品に添付されています。
上のブロック図と同じように結線されています。データ線は抵抗でプルアップ(動いていないときは強制的にHIGHになる。)されています。これはSDカードの仕様です。また、回路図にはありませんが、SDカードの中にもマイコンが入っていますので、電源ラインにはパスコンの役割の0.1uFのセラミックコンデンサが入っています。
SDカードは3.3Vで動くので、A33Fと直結できます。
MicroSDカードの端子は下のようになっています。非常に小さいです。私の手の小指のツメぐらいの大きさ。近くのスーパーで買いました。
上の端子の説明はSPIモードの場合です。1と8番が使われていませんが、これはMMCモードの時に使用します。A33FではSPIモードで通信するのでこの2つは接続されていません。
活線挿入(電源を入れたままコネクタを差すこと)を想定していますので、SDカードの電源端子は他の信号線と比べて長くなっており、信号線がつながる前にカードに電源が供給されるようになっているようです。
A33Fのコネクタはプッシュ方式になっています。カードを差し込むとカチっととまり、取り出すときは押すと飛び出してきます。カードの種類によっては若干厚さが違うようで、動きがシブくて押しても飛び出さないものもありましたが、数回カチカチやっているとスムーズに動くようになります。上のマクセルの2GBもシブかったののですが、数回出し入れすると表面が少々削れて(上の写真の2GBの上の三角マークのとこるが少しケズれているのが見える)スムーズに動くようになりました。
SDカードのSPIモード
SDカードの中にはコンピュータが入っており、SPIモードの場合はSPI通信でコマンドを送って
・初期化
・読み込み
・書き込み
などの処理を行います。
<コマンドのフォーマット>
マスタ(dsPIC)からSDカードにコマンドを送る場合には下のように6バイトのデータを送ります。先頭1バイトがコマンド、その後5バイトが引数、最後がCRCになります。それぞれビットに0と1が入っているビットは必ずその値にします。
・コマンド番号:
ある決まった値をセットするとそれがある命令になります。次で説明します。
・引数:
コマンドの引数で、4バイト(32bit)のデータを指定できます。データアドレスなどが入ります。
・CRC7:
データが正しく送られたかを確認するためのCRCチェック符号が7bit分入ります。CRC7の値は前5バイトの値から計算されます。これは結構複雑な計算になるのですが、SPIモードの場合は一番最初に送るコマンドの「CMD0」だけCRCを判定するので、初期化後はCRCの値は適当でもかまいません。フォーマットは6バイトと決まってますので、CRCを判定しないSPIモードになった後でも6バイト目は送る必要があります。
CMD0の場合、引数を全て0にすると、最後の6バイト目は 0x95 になります。
<代表的なコマンド>
SDカードに対してコマンドを送ることを「コマンドを発行する」といいます。「コマンド」は命令の意味です。
SDカードへ発行するコマンドには下記の5つの代表的なコマンドがあります。この5つでSDカードへのデータの読み書きができます。その他の命令もあり、もっと細かい操作もできますがここでは扱いません。興味のある方はインターネットなどで調べてください。(但し、深く勉強しないと動かせないので、とりあえず動かしたい場合は深入りしない方がよいかも)
コマンド番号 |
略称 |
送信時の値 |
引数 |
レスポンスタイプ |
説明 |
0 |
CMD0 |
0x40 |
無視される。(全部0などでよい) |
R1 |
カードをリセットする。 |
1 |
CMD1 |
0x41 |
無視される。(全部0などでよい) |
R1 |
リセットが終了したか判定する。 |
16 |
CMD16 |
0x50 |
ブロック長 |
R1 |
ブロック長を指定する。 |
17 |
CMD17 |
0x51 |
読み込むデータアドレスを指定する |
R1 |
ブロック長分読み込む |
24 |
CMD24 |
0x58 |
書き込むデータアドレスを指定する |
R1 |
ブロック長分書き込む |
CMD0は、電源投入後にSDカードに対して必ず発行しなければなりません。CMD1はカードのモード設定などの初期化プロセスを起動させるコマンドです。CMD1のレスポンスにより初期化プロセスが終了したかわかりますので何回も発行してレスポンスを見ます。詳しくは後ほど説明しますが、CMD0発行後にCS端子をLOWの状態でCMD1を発行するとSPIモードへ移行します。先に説明したSDHCタイプのカードや、MMCモードの場合はこの初期化手順が大きく異なります。
<コマンドのレスポンス>
全ての操作はマスタであるdsPIC側からのコマンド発行により始められます。上のコマンド(詳しい発行の仕方は後ほど)を発行すると、SDカード側はコマンドが受け入れられたかどうかをとりあえず返します。これをコマンドのレスポンスと言います。
レスポンスにもフォーマットがあり、R1、R2、R3という種類のフォーマットタイプがあります。今回使うコマンドは全てR1タイプのレスポンスになります。ビット7はつねに0です。それ以外は下の通りとなります。
<SPIモードの場合の電源投入〜初期化プロセスの流れ>
SDカードは電源投入後に毎回必ず「初期化プロセス」を実行しなければなりません。このプロセスはカードの種類やモードによってことなります。ここではSPIモードの場合を説明します。手順は次の流れになります。
1)電源投入。電源投入時はMMCモードになっている。CSはHIGHにしておく。初期化プロセスは通信速度が400Kbps以下の必要がある(SDカードの仕様)ので、dsPICの通信速度を400Kbps以下に設定する必要があります。
2)マスタからSDカードへ80クロックのダミークロックを送る。(適当な値10バイトを送る。)これでモード設定待ち状態になる。
3)CSをLOWにして、CMD0(CRC付き)を発行する。CS=LOWの時にCMD0を送るとSPIモードが選択され、SDカードはSPIモードに移行するプロセスを実行します。CMD0のCRCは引数を全て0にすると
0x95 になります。これを1バイトづつ表すと
[0x40] [0x00] [0x00] [0x00] [0x00] [0x95] になります。
4)レスポンスを読み込んで、SPIモードへの移行準備ができたかどうか判定する。すぐにはレスポンスはきませんので、3)の後に、1バイトづつ受信を行ってみます。SDカードがBUSY(作業が終わってない)の時は、データラインがHIGHのままなので、1バイト受信をすると
0xFF が返ってきます。そのうち 0xFF 以外の値が来ます。これがレスポンス(R1タイプ)です。移行準備ができたらレスポンスの値は
0x01 つまり「カードは初期化中。その他はエラーなし。」(上の図参照)になります。
5)CMD1を発行して、初期化プロセスを起動させる。このコマンドに対するレスポンスが 0x00 になると初期化プロセスが終了したことになります。CMD1を発行した後にまだ
0x01 がくる場合はまだ終わっていませんので、レスポンスが0x00になるまでCMD1を発行しつづけます。
CMD1を送る段階ではすでにSPIモードに移行しはじまっていますのでCRCはなんでも良いです。一応ですが、CMD1で引数を全て0にした場合のCRCは
0xF9 になります。これを1バイトづつ表すと
[0x41] [0x00] [0x00] [0x00] [0x00] [0xF9] になります。
6)レスポンスで0x00が来たらSPIモードへの初期化プロセスが終了したことになります。必要であれば、SPIの通信速度を再設定します(出来るだけ速い方がよいが、あまり速すぎても動かない。)。
上の流れではCMD0とCMD1の2つのコマンドを発行するあいだはCSはずっとLOWのままのように書いてありますし、SDカードの仕様が書いたドキュメント類を読むとそのようになっているようですが、実際に実験してみた結果、CMD1後にCSをHIGHにして1バイトダミーバイトを送信しないと同期が狂うことがわかりました。よって実際のプログラムではCMD1を何回も発行する前にCS=HIGHにして1バイト送信し、CS=LOWにしてからCMD1を発行しています。(この部分でかなりの時間を消費してしまった。SDカードのドキュメントわかりづらいです。)
詳しい流れは後で説明するプログラムの解説のところを参照願います。
<ブロック長の設定>
DinはSDカードに入ってくるデータ、DoutはSDカードから出力されるデータです。
ブロック長の設定はCMD16で行います。引数は上位バイトを先に送ります(ビックエンディアン方式)。SDカードでは出荷時のブロック長設定が512バイトのものが多く、容量が大きなものは1024バイトのものなどがあります。ここでは統一した処理ができるように、起動後には必ず512バイトに設定することとします。512を16進数で表すと
0x200 となります。この段階ではすでにSPIモードに入っているので、CRCには何を入れてもよいです。ということで送信するデータは次のようになります。
[0x50] [0x00] [0x00] [0x02] [0x00] [CRC(なんでも良い)]
このデータを送った後、1バイトづつ受信してみてレスポンスを確認します。上の図ではCMD16を送ったあと少ししてから1バイト受信しているように見えますが、CMD16を送信した直後から1バイト受信してもOKです。CMD16を送った直後はBUSY状態(まだ設定中)なので0xFFが返るはずです。なんども受信してみて0xFF以外が来たら、それがレスポンスです。0x00のレスポンスで正常終了となります。
波形の注意点ですが、CLKはデータのやり取りをしていない時はHIGHにしておく必要があります。上の図ではDinもデータのやりとり以外ではHIGHになっていますが、ココはデータやりとり以外の時はHIGHでもLOWでもかまいません。DoutはSDカードの方からコントロールされるものですが、レスポンス以外(及び書き込みのBUSY時以外)はHIGHになっています。以下、ブロック読み出し、ブロック書き込みの場合も同様です。
<1つのブロックの読み出し>
CMD17ではCMD16で設定したブロック単位でデータを読み出します。SDカードは単なるメモリだけではなく、コンピュータも入っており、コマンドによって読み書きを制御していますので、基本的には販売している状態で設定されているブロック長が一番効率が良い、つまり一番速くアクセスできる設定になっている場合が多いです。よって、CMD16でブロック長を初期設定と違う値にするとアクセスが遅くなる場合があります。極端な例だと1/10ぐらいの速度になってしまう場合があるようです。これを避けるには、あらかじめコマンドで出荷時のブロック長を読み込んで、そのブロック長単位で読み出し(書き込み)するのが良いのですが、ここでは難しくなるので割愛しました。ここでは標準的なSDカードで一番多い設定の512バイト単位にします(前のCMD16で設定済みとする)。
CMD17もCMD16と同じようにコマンドを発行したら、レスポンスが来るまで1バイトづつチェックします。レスポンスが0x00ならば正常に読み出しモードになったことを表します。SPIモードなのでコマンドのCRCには適当な値を入れます。たとえば、今、アドレス512(バイト目)からデータを読み出したいとしますと、引数は0x200になりますので、CMD17の実際の送信値は次のようになります。
[0x51] [0x00] [0x00] [0x02] [0x00] [CRC(適当)]
ブロック長を512にした場合は、アドレス指定を512の倍数で指定しないとエラーになります。
レスポンスの後(直後でもよい)、また1バイトづつ読み込み、SDカードからデータが送信されてくるのを待ちます。データが送信されるまではSDカードの出力はHIGHのまま保持されるので、1バイトづつ読み込んでも0xFFが返ります。
SDカードからデータが送信されるときは、先頭にスタートバイトとして0xFEが送られるので、それを待ちます。0xFEが送られたら、CMD16で設定した回数分だけデータを受信します。データの最後にはCRCが2バイト送信されますのでこれも読み込みます。CRCは特に使わなくてもかまいませんので、このCRCの値は保持しなくても良いでしょう。
受信終了後、ダミークロックを1バイト分送ると、SDカードはCMD17を終了し、次のコマンドが受け付けられる状態になります。(これはSDカードの仕様のようです。)
ダミークロック1バイトを生成するにはdsPICの場合は単純に1バイトの意味の無いデータを送信すればOKです。
スタートバイトですが、もし、レスポンスが0x00で正常だったにもかかわらずに何らかのエラーが出た場合は、スタートバイトとしての0xFEではなく「エラーレスポンス」というものが返ります。エラーレスポンスのフォーマットは下のようになっています。
<1つのブロックへの書き込み>
おなじように、CMD24ではデータを1ブロック書き込みます。コマンドの引数は書き込みの先頭アドレスになります。たとえば、アドレス512(バイト目)からデータを書き込みたい場合のコマンドの値は次のようになります。
[0x58] [0x00] [0x00] [0x02] [0x00] [CRC(適当)]
ブロック長を512にした場合は、アドレス指定を512の倍数で指定しないとエラーになります。
レスポンスを待ち、0x00が返ってきたら書き込み準備が整ったということになります。この後、データの送信を開始するのですが、先頭に、開始の合図であるスタートバイト
0xFE をまず送ってからデータを送ります。SPIモードではCRCは無視されるのですが、フォーマットではデータ後に2バイトのCRCを送る必要があるので、適当な値を2バイト送信します。
その次に、1バイトの「データレスポンス」がSDカードより送信されるのでこれを読み取ります。データレスポンスは、送信したデータが上手くメモリに書き込まれたかどうかを返信するもので、下のようなフォーマットになっています。ステータスはビット3〜1となり、ビット0はかならず1になります。正常に書き込まれた場合はステータスが010となるので、データレスポンスの下位4ビットは 0101 つまり値 5 になるはずです。ということで、データレスポンスの下位4ビットの値が5かどうかで正常書き込みだったかを判定できます。正常書き込み以外の場合はデータは拒否され、メモリには書き込まれません。
データレスポンスの後はSDカードはBUSY状態となり、DoutがLOWになります。この時1バイト受信すると0x00を受信することになります。よって、1バイト受信をつづけて0x00以外になったらBUSYが終了したというのがわかります。
その後、CMD17と同様にダミークロックを1バイト分送ってから終了します。
dsPIC33FのSPIの使い方
いままではSDカードのSPIモードでの挙動を説明しましたが、ここではdsPIC33F側のSPIの使い方について説明します。
まずは、データシートより関係する部分を抜き出してみます。
SPIでのデータの受け渡しは、前に説明したとおり、「SPI1BUF」を通して行われます。下の図ではSPI1RXB、SPI1TXB、SPI1SRなども書いてありますがこれらは直接操作することはできません。送信の場合はバッファ(SPI1BUF)に書き込むだけで自動的に送信され、読み込むときはバッファに適当な値を書き込んでから受信が終了するのを待ちます(RXBがフルになったというのがわかるようになっている)。その後、SPI1BUFを読み出すと、それが受信データ1バイトとなります。
その他には状態を表す特殊レジスタの「SPI1STAT」 と設定などを行う「SPI1CON1」があります。「SPI1CON2」という特殊レジスタもありますが、これはフレーム同期方式というもので使い、今回は使用しません。フレーム同期の場合はSS端子も使用します。
<SPI1の設定>
設定はSPI1CON1で行います。主に、
・マスターになるか、スレーブになるかの設定。デフォルトでマスタの設定なので変えなくて良い。
・16ビット幅にするか、8ビット幅にするか(16ビットマイコンなので16ビット幅の設定もできるが、通常は8ビット幅でSDカードとも8ビット(1バイト)単位で通信します。デフォルト設定も8ビットです。
・クロックの速度設定
などを設定します。
SDカードの場合、見落としやすい仕様に、「クロックの待機時はHIGHにする必要がある」というものがあります。これはSPI1CON1のCKPの設定で変えられますが、デフォルト時にはLOWになっているのでここは設定する必要があります。
クロックの速度はCPUの内部クロックより分周したのもを使用できます。他の事例のページと同様、今回もクロックは内部クロックを使い、最高速の36.85MHzのFcyで行います。
(Fcyとは実質のクロック速度。dsPICは特殊なクロック構成になっているのでクロック=動作速度ではない。Fcyが実質の動作速度のようなものに近い)
SPIのクロックは上の式17−1のように、FcyをSPREとPPREで分周した値となります。
SDカードの場合は、CMD0で始まる一連の起動プロセスのときは400KHz以下のクロックを使用する必要があります。(ココでかなりハマった。SDカードの仕様をすみずみまで調べないとわからない。)SPIの基準クロックが36.85MHzなので、400kHz以下にするにはSPREで1/2、PPREで1/64にしすると
36.85MHz/2/64 = 287.9KHz
と、ちょうどよい具合になります。
次は、基本の1バイト送信と1バイト受信の説明をします。
<1バイト送信>
1バイト送信する場合には前にも書いたとおり、SPI1BUFにデータを書き込めばあとは自動的に送信してくれます。送信中に次の操作をしないように、BUFにデータをセットしたら送信が終了するまで待ちます。これはSPITBFフラグを見ることでわかります。送信中は1になり、送信終了すると0になります。ということで書いたプログラムが次の通りです。これはマイクロチップのSPIのサンプルプログラムそのままです(よって16ビットかどうかの判定もしていますが、今回は8ビットできまっていますので判定は不要です。)。
これは簡易的なものなので、行55は本当はタイマなどを使ってタイムアウトを設けないと、なにかの拍子にSPITBFが変わらなくなるとフリーズする可能性があります。(ほとんどそうゆうことはありませんが)。
<1バイト受信>
受信の場合は前になんらかの通信があってオーバーフローしていると受信できないので、まずオーバーフローフラグをリセットします。その後、バッファに適当な値を書き込むとクロックが生成されて送信=受信が開始されます。
受信が終了したかはSPIRBFフラグを見て判定します。1なら受信中、0なら受信終了です。
これは簡易的なものなので、行67は本当はタイマなどを使ってタイムアウトを設けないと、なにかの拍子にSPIRBFが変わらなくなるとフリーズする可能性があります。(ほとんどそうゆうことはありませんが)。
この2つの操作を組み合わせてSDカードと通信することができます。
実際のプログラムと解説
SDカードを起動し、1ブロックの読み書きを行うプログラムを作ってみました。こちらです。長いので2ファイルに分割してます。ソースファイルだけなので、MPLABに移す場合はまずプロジェクトを作ってから内容をコピペしてください。
A33F.c
A33F_sub.c
A33Fの方はdsPICのハードウェア設定とメインルーチンが入っています。メインでは1バイト読み込んで、そのデータを少し変えて同じアドレスに書き込んでみました。さらにもう一度読み込んで、正しく書き込まれたか確認しています。確認するにはMPLABのWatchを使いますので、デバックモードでコンパイル&プログラム転送してください。
<SDカードの初期化>
実際のSPI初期化とSDカードの起動ルーチンの部分です。注意点は今までにも書きましたが、
1)SDカード起動時には400KHz以下のクロックで通信する。
2)レスポンスが返ってくるまで、1バイトづつ受信して待つ。
というところでしょうか。
通信速度ですが、手持ちのSDカード(INX製とマクセル製)で試しましたが、sdPICで生成できるクロックの中では9.2125MHzより上げると通信がコケました。バイト/秒に直すと1.15MB/sです。マクセルは7MB/s、INXは12MB/sという仕様でしたが、どうやらSPIモードの表記ではないようです。ということで、購入したSDカードの仕様の限界速度で動かせるわけではないということに注意してください。ということで、初期化後はSPIのクロックを9.2125MHzに設定し直しています。
<ブロック長の設定、1ブロック読み込みと1ブロック書き込み>
これらはA33F_sub.cの方に入っています。長いのでソースファイルの中を見てください。今までで説明したことをプログラムにしているだけです。
<1ブロックの読み書き実験>
では実際に動かして、SDカードの読み書きができるか実験してみましょう。まず、View->WatchでWatchウィンドウを開き、Add_Symbolボタンで SPI1BUF/SDreadBuffer/SDwriteBuffer
の3つぐらいを監視できるようにします。SDreadBufferはSDカードから読み込んだデータを格納するための512個のバイト長データ配列です。SDwriteBufferは同じように書き込むデータをあらかじめ入れておく配列です。MPLABもVer7.5を超えたら配列の中身まで見れるようになって便利になりました(以前は配列はデバックできなかった(泣))。
実験の部分は次のようになっています。ちなみに、この前の部分でSDカードは初期化を終えています。
まず、上のように94行目にブレークポイントを設定して、コンパイル−>RUN します。90〜92行でSDreadBufferの全配列の中身を0にしているので、行93でCMD17を実行した結果、この中身が0以外になっていれば読み込みが成功したことになります。販売されている状態ではFATシステムが書き込まれているハズなので何らかの数値が読めるはずです。実際にやってみると、アドレス0番地から512バイト読み取ると、ほとんどが0ですが何バイトか数値が返ってきます。何度も読み取っても同じ値になる(カードが違うと違う値になる)ので読み取れていると判断します。
次に行94で止まったのをもう一度RUNして最後まで(行104で無限ループで停止)実行すると途中で先頭5バイトを変更したものが読み取れるはずです。ということで実際にやってみた結果が上のWatchの画面のように先頭5バイトが変更されていました。先頭5バイトが行97〜101で変えた値と同じなのがわかります。
おわりに
このようにしてマイコンボードでも大容量の記憶装置にアクセスすることができました。注意点としては、SDカードはあくまでファイルシステムで動かすことが前提なものなので、ランダムアクセスや高速アクセスには向いていないということです。ということで用途としては、あまり速くなくても良いが大きなデータの記録、ということになります。ロボット向けで考えられるのは、モーションデータや音声データあたりでしょうか?もしくは動くMP3プレーヤ(どこかで聞いたような...)のようなものが作れるかもしれません。
2007年11月3日
|