PICで気圧、温度、湿度計 by 24FJ64GA004 SDカードに記録する その4
FAT16ファイルシステムを狙い撃ちする実験 2


SDメモリーカードにアクセスする方法は、MMCモードとSPIモードの2つがありますが、SDカードアソシエーション(SD協会、SDA)の会員であり、ライセンス(HALA)の契約を結んでいなければ非公開部の高速なパラレルアクセスコマンド等を知る事も、使用する事も出来ません。

SDカードメモリーに対して、MMCモードでパラレルデータ転送での高速なデーター授受用サンプルソフトウエアを無償提供しているサイトが見つからないのは、この為です。

従って、それらの非公開のコマンドの代わりに、公開されているMMCのSPIアクセスモードおよび、コマンドを使用します。そのため、SDHCカードにアクセスする事はできません。また、SPIによるシリアル通信の為に、SDメモリーカードとのデータ授受速度も著しく低下して、実験した2GB SDメモリーカードでも0.5MB/sec程になってしまいました。

しかしながらこの方法であれば、ライセンスの契約をすることなく誰でも自由にSDメモリーカードにアクセスする事ができます。(というか、様々な公開されている方法や、無償で提供されているAPI等は、全てこの方法という訳です・・)

(重要)SDメモリーカードには「相性」の問題がありますので、今回のサンプルプログラムで必ず全てのSDメモリーカードにアクセスできるという補償はありません。ご容赦下さいませ。

 

◎FATファイルシステムについて理解をする為に必要な用語

FAT(File alocation table):
ファイルの先頭位置をクラスタという量で示すテーブルのことであり、通常は読み書きの不良を避ける為に同じ物が2重に用意されている。

セクタ:
記憶装置の最小記録単位を現す。SDメモリーカードでは、FAT16ファイルシステムとスムーズなアクセスを行う為に、通常は512バイト/セクタである。(16進数で0x0200/セクタ)

物理セクタ:
物理セクタは、ファイルシステム自身を含む、SDメモリーカード上の全ての記憶可能な領域。

倫理セクタ:
論理セクタは、FATファイルシステム等が扱う領域。

SDメモリーカードでは、物理セクタと論理セクタの記録単位は、通常は供に512バイト/セクタと同じであり、アドレス0から始まる物理セクタに対して、一定量の物理セクタ分シフトした物理セクタ位置が、倫理セクタの開始位置となる。

クラスタ:記憶領域を管理するのに使われる単位で、論理セクタの集合体である。FATファイルシステムでの各ファイルの先頭の位置や、FATテーブル上でファイルが1クラスタ内に治まる大きさか、あるいは複数に跨がっているのかを示すのに使用される。


◎FATファイルシステムの構造

マスターブートレコード(MBR)、パーティションブートレコード(PBR)、FAT、ルートディレクトリエントリ(RDE)、ユーザーデータ領域等の、5つの領域で構成されています。

さて、Windows等で記憶装置のアイコンをクリックして始めに現れるディレクトリーやファイル名称の一覧やファイルの容量、そして、画面には現れない情報として、その装置内のファイルの実際の先頭アドレス等はどうやってパソコンは知るのでしょうか?

具体的に順を追って見ることにします。

 
 

FAT16でフォーマットされたSDメモリーカードの先頭アドレス 0x00000000から始まる512バイトは、物理セクタの0であって、倫理セクタの0(いわゆるセクタ0)ではありません。

実際の論理セクタの始まりを現す情報(パーティションテーブル1の相対アドレス:物理セクタ)が0x01C6(10進で454)、0x01C7(10進で455)、0x01C8(10進で456)、0x01C9(10進で457) の32ビットでかかれています。ただし、リトルエンディアン表記なので、大きなアドレスに書かれた情報が上位のビットを現しています。

上記の例での倫理セクタ0の物理セクタ番号は、16進で0x00 00 00 87(10進で物理セクタ135)からと云う事です。

また、この場合のSDメモリー上の倫理セクタ0のバイトアドレスは、
0x87(10進で物理セクタ135)× 0x0200(10進で512)= 0x00010E00 (10進で69120バイト目から)
が、「論理セクタの0の始まり」になります。

SDメモリーカード自信の記憶容量や、製造元、同じSDカードでも以前にフォーマットした機器の違い等によってここの値は変化します。再フォーマットしなければ変化する事はありません。

ここで、PICからSDメモリーカードとデーターを読み書きする場合に最も重要なのは、セクタやクラスタから計算された「バイトアドレス値(32ビット)」です。PICからアクセスする場合には、上記の情報から最終的にはこの「バイトアドレス値(32ビット)」を絶えず計算しておかなくては成りません。

 
 
 

上記の2つの画面は、同じ論理セクタ0(いわゆるセクタ0)をバイトアドレスの表現方法変えて表示しています。

上がバイトアドレス表示で、下がセクタ単位での相対的なバイトアドレス表示です。どちらも同じ領域を表示しています。

この論理セクタ0にはたくさんの重要な情報が書き込まれています。そのままでも、MSDOS5.0とか、NO NAMEとか、FAT16等が見て判ります。

この例では、論理セクタ0がバイトアドレスの0x10E00から始まっていすが、この開始アドレスは、SDメモリーカードの容量の違いや、同一容量のSDメモリーカードであっても、以前にどの機器でフォーマットしたか等に寄って異なりますが、全てのFAT16システム上で論理セクタ0内での相対的な位置関係は全く同じなので、下の相対表示画面で説明をいたします。

また、FATファイルシステムでの数値表現は全て「16進数のリトルエンディアン表記:大きなアドレス値に上位ビットの値」です。


◎論理セクタ0:「 」内は今回の値(なお、今回の論理セクタ開始位置は物理セクタ0x87、10進で物理セクタ135)

0x0B「00」、0x0C「02」 セクタサイズ(バイト):16ビット長
ー>0x0200バイト(10進で512バイト)

0x0D「40」 クラスタサイズ(セクタ):8ビット長 
ー>0x40セクタ(10進で64セクタ分、32768バイト分)

0x0E「01」、0x0F「00」 FATテーブルの開始論理セクタ位置(セクタ):16ビット長
ー> 0x0001(物理セクタ0x87 + 0x01 = 0x88(10進で物理セクタ136)

0x10「02」 FATテーブルの数:8ビット長
ー> 2組分(同じ物が読み書きのミスを防止する為に2重に存在している!)

0x11「00」、0x12「02」 ディレクトリーのエントリー数:16ビット長
ー>0x0200(10進で512)

0x13「00」、0x14「00」 総論理セクタ数:16ビット長
 ※ただし、16ビットで現しきれない場合は、ここの値は「0x0000」として0x20から始まる32ビットで表記する。

0x16「EC」、0x17「00」 FATに使用するセクタ数 :16ビット長
ー>0x00EC(10進で236セクタ分)

0x20「39」、0x21「E0」、0x22「3A」、0x23「00」 総論理セクタ数:32ビット長
ー>0x003AE039(10進で3,858,489)

0x36「46」、0x37「41」、0x38「54」、0x38「31」、0x39「36」 FAT12/16識別コード:ASCIIコード5文字分
ー>FAT16

 

これらをまとめると、このSDカードは、

FATタイプ:FAT16
論理セクタ0の物理セクタとの相対位置:135セクタ(10進で論理セクタ135の位置が、論理セクタ0)
クラスタサイズ:64セクタ
FAT1開始セクタ:論理セクタ1(10進で物理セクタ136)[1+135]
FAT2開始セクタ:論理セクタ237[1+236](10進で物理セクタ372)[136+236]
ルートディレクトリーエントリー(RDE)開始セクタ:論理セクタ473[237+236](10進で物理セクタ608)[372+236]

また、ルートディレクトリーエントリー(RDE)内では、32バイトで1エントリー情報を現すので、
このRDEの領域の大きさは:32セクタ分[512×32/512]

データ開始セクタ:論理セクタ505 [473+32](10進で物理セクタ640[608+32]:バイトアドレス0x00050000)

となります。実際に物理セクタ640を開いてみました。

 
 

確かに私がテキストエディターで作ったASCIIファイルデーターが見つかりました。
でも、ファイルの名称や大きさ、次のファイルの始まりはどうやって情報を得るのでしょうか?

 
 
Windows XP でSDデータメモリーを開いて行くと、ファイル名称(この例では「data01.txt」)のついたテキストファイルアイコンが現れます。

このアイコンを選択したり、カーソルを合わせると、種類や更新日時、ファイルのサイズが表示されます。

それでは、FATファイルシステムでこれらの情報がどこにあるのか調べてみましょう。それはルートディレクトリーエントリー(RDE)と、FATに書かれています。
(Jun. the 27th 2011)
 
 

上記が今回のSDメモリーカードのRDEです。物理セクタ608です。

内容は、32バイトで1つのファイルの情報を示します。

0x00「44」、0x01「41」、0x02「54」、0x03「41」、0x04「30」、0x05「31」、0x06「20」、0x07「20」 ファイル名(ASCIIコード):8文字
ー> data

0x08「54」、0x09「58」、0x0A「54」 拡張子(ASCIIコード):3文字
ー> txt

0x0B「20」 フォルダ 0x10/ファイル 0x20 で現す。
ー> ファイル

0x1A「02」、0x1B「00」 先頭クラスタ番号:16ビット長
ー>  クラスタ2

0x1C「40」、0x1D「00」、0x1E「00」、0x1F「00」 バイトサイズ:32ビット長
ー> 0x00000040(10進で64)バイト

また、SDカードメモリ自身に名称がある場合は、始めの32バイトが使用されます。
このRDE領域では、全てが名称で管理されています。

さあ、これで全てがわかった!FATの説明が未だです。でも、ここまでの情報があれば、ファイルの大きさも、名前も、先頭アドレスも解ったのだからこれで良いはず?

と、思うのは未だ早いのです。

ルートディレクトリーに、1つのファイルが、1クラスタ長の中に治まっているのであれば、殆ど問題はありません。また、複数のファイルが存在していても、次々に追加されたあとで、何も編集されないのであれば、これも今までの情報だけで問題はありません。

ファイルは、自由に内容を削除したり、追加したり、ファイル名を変更したり、ファイル自身を削除したり、ファイルを追加したりと様々な操作を行う事が出来ます。

こういったときに、SDメモリーカードの中で、ファイルはいったいどうなっているのでしょうか?
その管理の詳しい方法は、今回は説明を省きますが、結果としてデーターが飛び飛びに存在してしまうのです。そのデーターのイモずる式なリンク情報がFATに書かれているのです。

このFATの情報のおかげで、いかなる編集操作が行われても、ファイルの連続した記憶情報を追う事が出来るのです。

まず、単純にルートディレクトリーに1つのファイルが始めて書かれた場合のFATを覗いてみます。

 
 

特に面白そうな情報は何も無い様に見えますが、じつはたいへん重要な事が表現されています。

全ての値はクラスタで現されます。今回は1クラスタは64セクタでしたね。

FAT16の場合は、16ビットのデータ構造をもっており、これがFAT16の名前の由来です。始めの16ビット(0x00、0x01)がクラスタ0、次の16ビット(0x02、0x03)がクラスタ1の情報を現し、順番に全てのクラスタに対する情報を示しています。ちなみに、FAT12では、ここが12ビットづつの区切れで現されて、しかもリトルエンディアンなので、たいへん見にくくなります。(説明は省きます)

ただし、クラスタ0と、クラスタ1は、予約されたクラスタなので、先頭のファイルは必ず「クラスタ2」から始まります。

ここの内容は、そのクラスタ内でファイルが収まらない場合に次のクラスタへのリンク(リンク・チェイン)を現し、そのクラスタが最終クラスタの場合は0xFFFFで現わされます。

今回のファイル data.txt の先頭クラスタは2ですが、ファイル容量が1クラスタの容量内に収まるので、クラスタ2の情報は0xFFFFとなり、それ以上ファイルがリンクされていない事を示しています。

他の例も見てみることにします。

 
 
 

上記2つは、別のSDカードのFATやRDEの例ですが、FATやRDEの内部構成はみな同じです。この例では、上図のFATの0x04、0x05が、クラスタ2から始まる先頭ファイルのクラスタリンク情報が書かれています。

その値は、「03」、「00」で1クラス内にデーターが収まらなかったので、ファイルの内容が「クラスタ03へ続く」事を示しています。そしてクラスタ03の内容を見ると、「クラスタ04へ続く」と、以後順番に続いていって・・・0x100、0x101のクラスタ129でやっとリンク終了の「FF」、「FF」が出てきました。このファイルは、クラスタ02から始まってクラスタ129までの128クラスタを使っています。

今回は1クラスタが64セクタで、1セクタが512バイトなので、最大128×64×512=4,194,304(4MiB)のファイルである事が解ります。

ここで、最も重要なのは、クラスタのリンクが順番に続いているかどうかという事です。この例では、空っぽのSDメモリーカードに順番にファイルデーターを書き込んで行ったので、FATのクラスタリンクが順番に続いています。これは、1つの例であって、いろいろな順番んで、いろいろな大きさのファイルを、追加したり、削除したり、中身を変数したりすれば、元々のリンクだけでは足りなくなったり、余ったりするのですから、連続した空きクラスタを必ず得られる分けではないのです。

FATはこの時に、1つのファイルのつながりをクラスタリンクで「次はここのクラスタに書かれていますよ」と飛んでしまったクラスタを教える為にあるのです。

1クラスタの容量に収まるファイルは、始めに与えられたクラスタから内容が途中から何所かの別のクラスタに飛んでしまう事はありません。

また、カード内のクラスタリンクが飛び地になっても、一度データーを別の記憶媒体にコピーしておき、SDメモリーカードをフォーマットし直した後に、改めて外部にコピーしたデーターをSDカードにコピーし直せば、連続したクラスタリンクに直ります。

FATファイルシステム用のAPIを利用していれば、この飛び地になってしまったFATのクラスタリンクは問題にはなりませんが、 今回の様に、単純なI/OコマンドだけでSDカードにアクセスしてファイルを扱う場合には、十分に注意する必要があります。

PICは比較的プログラム容量が少ないのですから、巨大なFATファイルシステム用のAPIを利用せずに、SDカード上にパソコンが読み込む事の出来るデーターを書き込まなくてはなりません。そこで最低限守らなくては成らないのは、FATのクラスタリンク情報が飛び地に成らない様なダミーデーターを前もってSDカード上に書き込んで用意しておく事です。

そして、そのファイルのFATファイルシステム上の構造を変えないで、ファイルの中身をそっくり書き換えてしまえば、書換を行ったSDメモリーカードをパソコンに繋いでも、何も無かった様にパソコンで認識し、読み書きできるという訳です。

また、パソコンで表示されるファイル名やファイルサイズ等は、下図のRDE内で取得できます。data01.txtと云う名前のファイルは、0x400000バイト=4,194,304バイト=4MiBの大きさがあり、今回はクラスタをちょうど隅々まで使い切っている状態であるとわかります。

このように、RDEとFATのセットで始めてファイルの全ての状況が解るのです。
次回はPICで具体的にファイルに書き込みをする為の、ダミーファイルについてお話しします。
(Jun. the 29th 2011)


注意事項
General disclaimer
トッ プページへ

なお、当ホームページで公開しているデーター(写真、音声)等を個人の枠を超えて複製・転用する事はご遠慮下さいませ。
ご意見/苦情/ご感想はこちらまで