FatFsを使ってみる
RX62NでSDカードのファットファイルシステム(FatFs)を操作する記事がインターフェイス2011年6月号にありましたが、このFatFsを使ってみます。FatFsは3年ほど前(v 0.06 )からテスト的に動作させていましたが、FAT32且つ、ロングファイルネームが扱えると知って、これはよさそうだと思いました。FAT(16)については、15年以上も前に自作で作成したことがあり、その頃、組み込み外部ストレージは、SCSIの240MB程度のHDDを使っていました。CPUは、SCSIインターフェイス付きの、モトローラのVMEバスマスター基板で、マシンは、MC68040でした。SCSIはUSBに置き換わって、今ではほとんど使われなくなりました。自作FAT16は、ほぼ問題なく動作しましたが、何分時代遅れって感じで、SDカードに移植する気が起こらず、そのままになっていました。

最近のFatFsを見ると、SDカード、しかもSPIモードで使うのを前提で作成されているようです。サンプルソフトもSPIでSDカードを操作する例になっています。それ以外の場合は、いくつか変更する必要があります。v 0.06 の場合は、SPI接続のSDカードに限定していなかったので、ストレージならなんでも簡単に移植できましたが、最近のはすんなりいきません。

ここでの解説は、RX62NのSPIでSDカードを操作した場合を元にしていますが、SPIのポートなどは変更しています。変更、追加の回路、プログラムソースは、こちら。

FatFsを使う場合の注意点。突然の電源断でも安全に使うには?
結論から言うと、1秒ぐらい持つUPS(無停電電源装置)の機能を基板に付けるしかありません。
無論、電源断をマイコンが検出できるという条件付きです。


FatFsの移植に関しては、今更ここで解説する必要はないと思いますが、組み込み工業用のスタンドアローンマイコンのストレージとして使うには、いくつか注意が必要です。それは、FATファイルシステムの構造によるものです。FATファイルシステムでなくても、書き込み中に電源が切れると、脆弱なファイルシステムでは、復旧が困難になることがあります。
SDカードをFATファイルシステムで使うには、以下の不具合が起こらないようにしなければなりませんが、小さなシステムでは簡単に対応できそうにありません。特にRAM容量が少ないと、#define _FS_TINY 0 にしずらいので、アクセス回数が増え、それだけ電源断の耐性が低くなります。
  1. 操作中に電源が切れると、最後のファイルを書いても、ディレクトリが更新されずに、いくつかデータ部分が抜けます。
  2. 操作中に電源が切れると、ファイルシステムが壊れて、パソコンから「フォーマット」を要求されることがあります。これはごくごくまれです。
  3. 操作中に電源が切れると、パソコンがSDカードを認識できなくなります。これは更にごくごくまれです。意図的に起こせるSDカードのメーカーがありますが、SPIモードではまだ未確認です。SDモードで操作する装置で、BIOSもそれなりに改造したものでこの現象の発生を確認できました。ただこうなっても、その装置で該当SDの終わり近くに何かを書くと、復帰でき、パソコンからもアクセスできるようになりました。無論、ファイルは一部こわれていました。
対策としては、
  1. 電源OFFの信号をマイコンのNMIに入れ、直ちにファイルをクローズします。
  2. ファイルのデータを書き足したとき、そのファイルをクローズします。頻繁にこれを実行すると書き込み回数が増えるので、1秒単位以上でクローズするようにします。
  3. 電源OFFの信号をマイコンのNMIで受け取ったあと、SDカードと操作CPUを1秒ほど動かせるように電源を設計します。これは、ファイルクローズに伴う最終のデータの書き込み完了が、長くても1秒以内に終わると思われるからです。(SDカードのメーカーによってこの時間は異なります)
さてここで、ファイルに512バイト追加してクローズするまで、どれだけ時間がかかるのか、時間を測定しながら何回もトライすると、たいていは20ms程度で終わりますが、数十回に一度ぐらいで、400msもかかってしまうことがあります。ファイルのデータを書き足したとき、そのファイルをクローズしてる最中に電源OFF信号が来ると、最大400msは動作させないと、正しくFATファイルシステムの更新が終わりませんので、たまたま時間がかかっている場合に電源断になると、ファイルに書けていない状態になります。

では、通常は、20msほどで終了するのに、ファイルに追加してクローズするのになぜ400msもかかる場合があるのか、詳しく調査してみます。FatFsでの操作が増えてアクセス回数が多くなり、400msもかかるとは思えません。

FatFsでのSDカードアクセスをロギングして、アクセス回数を調査
FatFsの動作を観察すると、次のような処理になっています。( _FS_TINY = 1, _USE_LFN = 0 の場合 )0x200バイトのデータを書き足した後、ファイルをクローズする操作をします。
  1. 対象ファイルのディレクトリを読みます。このとき、対象ファイルのディレクトリがルートディレクトリの先頭付近にあれば、1回で済みます。フォルダのネスティングが1段深くなると、最低1回のリードアクセスが追加されます。
  2. ディレクトリの情報から、書き足す位置を計算して、ファイルの最終位置のSDカードのセクタを読みます。
  3. ディレクトリの情報から、書き足す位置を計算して、512バイト境界で終了していれば、上記の読み出しは発生しません。
  4. 最終位置のセクタデータに、書き足すデータを追加します。この場合、読み出しサイズ+書き足しサイズが512バイトを超えると2セクタ分書くことになりますが、512バイト境界から512バイトしか追加しないので、2セクタ分書く操作は理論上必要がないと思われます。
  5. ファイルが大きくなったので、その旨サイズ情報を書き換えるため、ディレクトリを書換えます。
以上のような過程でファイルと管理部分の更新が行われ、最短で1回の読み出し、2回のライトで、ファイルデータの追加が終わります。

では、最悪どれだけのアクセスがあるのか、考察してみます。ただし、対象ファイルのディレクトリが、ルートの先頭付近にあるものとします。(ロングファイル名でなければ、ファイルディレクトリのサイズは32バイトですから、ルートの最初のディレクトリの場所には、16個のファイル情報を格納できます)
  1. ファイルの追加で再オープン時、ファイルのサイズが大きいと、FATテーブルの読み出しが行われ、FAT32の場合、ファイルサイズがクラスタ容量の128倍を超えるたびに、1回のリードが追加されます。FAT16の場合は、クラスタ容量の256倍単位。
  2. データの追加で、512バイトの境界を越えた場合、1回のライトが追加されます。
  3. データの追加で、クラスタ境界を超えたばあい、FATテーブルの読み出しが最低1回追加され、ライトも2回追加されます。
  4. 上記の条件の場合で、FATテーブルが、512バイト境界にまたがって更新される場合、さらに、2回の読み出し、2回のライトが追加されます。
とまあこんな具合ですが、いわゆる細かく断片化したフラグメンテーションだと、どんどんアクセス回数が増加していきます。上記の予想は、断片化が無いものとしての、理論的に予想される回数ですが、実際に、FatFsでアクセス回数のカウントをしてみると、さらに1回多い結果になりました。ちなみに、0x200バイトのデータを、ファイルに追加する場合、元のファイルが、0x200の整数倍のサイズだったとし、約1MBあったとして、0x200単位で追加のためのファイルオープン、追加、クローズを1024回ほど繰り返すと、最大リード回数7回、ライト回数7回という結果になりました。たった0x200バイトのデータをファイルに追加するだけで、7回もライトされることがあるということです。

しかし、たとえ7回リード、7回ライトであっても、リードで通常2〜3ms、ライトで5〜10msほどなので、合計で約70ms程度です。そこで、ファイルに 0x200 バイトのデータを追加する操作で、どれだけ時間がかかるのか、テストするプログラムを作ってみました。ライトするデータは、0x200 バイト固定で、0x200 バイトの先頭に、時間データを埋め込んで、後で詳しく経過を見られるようにしました。時間データは、RX62N のリアルタイムクロックのレジスタを読み出して文字コードに変換して書きます。時間の分解能は、1/128 秒になりますが、RX62N の、RTC レジスタから読み出す途中で変化が起こると困るので、1/128 秒のカウンタポート( 0x8C400 番地の byte ポート ) が、127 の時は、1秒が繰り上がるのを待ってから、全体を読み出して時間データとします。
また、0x200 バイトを追加する処理を64回繰り返した後で、途中経過として、セクタリード最大回数、セクタライト最大回数、最大処理時間、平均処理時間、平均セクタライト回数を、コンソールターミナルに報告するようにします。

以下は、スクリーンショットです。
1、パワーONの後、RAM用プログラムをロードして、0x400 番地から実行
2、date コマンドで日付を確認。その後、mount コマンドで FatFs を使えるようにしています。
3、dir コマンドで、ディレクトリの表示。すでに、TEST という1MBのファイルがあります。
4、F10000,200 test で、0x200 バイト追加を、0x200 回行います。
この例でも判る通り、0x200バイトの追加で、最大 0.437 秒かかっています。

このテストを行う回路は、RX62NのSPIでSDカードを操作で紹介している回路から、以下のように追加、変更しています。
  1. SPIポートとして、ポート0を選択できるようにしています。これは、JTAGを使えるようにするためです。
  2. UARTポートとして、ポート1、ポート2を選択できるようにしています。これは、SIOでブートできるように、SIO1を選択できるようにするためと、SIO2を追加で使えるようにするためです。
ポート選択を可能にした、SDカード操作のモニタ( FatFs 機能付き ) の cygwin ソースファイル一式です。 rxsdfs2t.lzh 

SPIポートの選択追加。SPIのポート0を使えるように改造。

UARTポートの追加、選択。コンソール用簡易RS232Cは、SIOポート0を使います。

工事中

SDカードへのライトで、コマンド、レスポンス、ライト完了までの時間を個別に測定してみる

このような測定をするのにFatFsで行おうとすると、時間を測定する位置をきっちりと指定しにくいので、RX62NのSPIでSDカードを操作での操作で測定します。というのは、FatFsの send_cmd 関数が、コマンドを出す前に、SDカードのBUSYを確認しているので、「ちょうどこのライトのときの時間」をすんなり測定できないからです。ライトコマンドの後、データを送ってSDカードからレスポンスがあり、その後SDカードのBUSYが解除されるまで待つという1連の処理の、始めと終わり、さらに途中も含めた時間計測をするには、FatFsで用意された、RX62N用の、mmc_rspi.c のBIOSインターフェイスを改造するには、ff.c 本体に立ち入った変更箇所が多くなって、SDカードの正常な制御ができなくなる恐れがあるので、あきらめました。

FatFsでは、SDカードへのライト処理は、SDカードにデータを送ってしまうと、f_write() 関数からすぐに戻ってきます。そして次にコマンドを出す前に、BUSYを見ているのです。無論、こうすると、SDカード側での処理を待つことなく、CPUは別の処理ができるので、全体のパーフォーマンスを上げることができるので、このような操作になっているものと思われます。



工事中


ホーム に戻る

inserted by FC2 system