ぷち fatfs を PC 上で試してみる

組み込み機器用のミドルウェアであるぷち fatfs (以下、pff) は移植性の高いソースとなっており、 fat のディスクイメージを用意すれば PC 上でも簡単に動作を確認することができます。

PC 上で動かす利点

一見遠回りする感がありますが、ターゲット上でのデバッグでのハマる時間を減らすことができるはずです。

  • 機種依存しない処理が大半であり、簡単なデバッグは PC 上でやる方が便利
  • 必要な機器が特にいらない
  • 2 つでの環境を意識したソースを作成することによって、必然的に保守性があがる

イメージファイルを用意する

Unix/Cygwin/Mingw で簡単に利用できる dd コマンドを使用します。ここでは Mingw から K ドライブに接続された 64MB の CFcard を使用しました。

 $ ls -l /k
 total 2
 -rw-r--r--    1 Satono   Administ     1655 Dec  7  2010 00readme.txt
 -rw-r--r--    1 Satono   Administ      128 Jan  2 18:54 Makefile
 drwxr-xr-x    2 Satono   Administ        0 Jan  7 01:10 d

 $ dd if=\\\\.\\k: of=e:/cf64mb.dd

 125152+0 records in
 125152+0 records out

 $ ls -l /e/cf64mb.dd
 -rw-r--r--    1 Satono   Administ 64077824 Jan  7 00:42 /e/cf64mb.dd

dd で作成されるイメージファイルはいわゆるベタデータというやつで、先頭から順番にデータ領域のみを順番に記録されたファイルです。

当初は dd なしで 10MB 程度のフォーマット済みファイルイメージを用意してみたかったのですが、作れるソフトは見つかりませんでした。ただ、ぷちがついていない fasfs で初期化コマンドがあるようです。

diskio.c に処理を追加する

diskio.c は pff から呼び出される、機種依存/メディア依存の処理です。ここでは dd から作成されたイメージを stdio.h のファイル操作で拾ってくることにします。

今回はお試しということで read の処理のみ作成しました。

/*-------------------------------------------------------------------*/
/* Low level disk I/O module skeleton for Petit FatFs (C)ChaN, 2009  */
/*-------------------------------------------------------------------*/
#include "diskio.h"
#include <stdio.h>

static FILE *f = NULL;
/*-------------------------------------------------------------------*/
/* Initialize Disk Drive                                             */
/*-------------------------------------------------------------------*/
DSTATUS disk_initialize (void)
{
	DSTATUS stat;

	f = fopen("cd64mb.dd", "rb");
	return f == NULL ? STA_NOINIT : 0;
}

/*-------------------------------------------------------------------*/
/* Read Partial Sector                                               */
/*-------------------------------------------------------------------*/
DRESULT disk_readp (
	BYTE* dest, /* Pointer to the destination object */
	DWORD sector, /* Sector number (LBA) */
	WORD sofs, /* Offset in the sector */
	WORD count /* Byte count (bit15:destination) */
)
{
	DRESULT res;

	int offset = sector * 512;
	offset += sofs;
	if(fseek(f, offset, SEEK_SET) != 0){
		return RES_ERROR;
	}
	
	fread(dest, 1, count, f);

	return RES_OK;
}

とても簡単な構造になっていますが、ファイルポインタの f がグローバル変数になっていることと、イメージファイル名を直接指定していることに注意してください。PC 上で機能体験すると割り切って手抜きにしてあります。

セクタサイズは資料をあまり確認しなかったのでとりあえず 512 にしました。

void disk_close(void)
{
	fclose(f);
	f = NULL;
}

通常はファイルポインタなんてものは使わないのですが、PC上では終了時にファイルポインタを呼ぶべきでしょう。

アプリケーションから pff の関数を呼び出す

ここではイメージファイルの中のルートディレクトリにあるファイル一覧を出す処理と 00readme.txt を読み出して、PC 上のファイルに保存する処理を作りました。

#include <stdio.h>
#include <stdlib.h>
#include "pff.h"

void disk_close(void);

static void file_save(const char *name, BYTE *buf, int size)
{
	FILE *f = fopen(name, "wb");
	fseek(f, 0, SEEK_SET);
	fwrite(buf, 1, size, f);
	fclose(f);
}

static void dir(const char *name)
{
	DIR dir;
	FILINFO fileinfo;
	
	if(pf_opendir(&dir, name) != FR_OK){
		return;
	}
	do{
		if(pf_readdir(&dir, &fileinfo) != FR_OK){
			return;
		}
		printf("%s %d\n", fileinfo.fname, fileinfo.fsize);
	}while(fileinfo.fname[0] != '\0');
}

int main(int c, char **v)
{
	FATFS f;
	const int filesize = 1655;
	WORD read_count;
	BYTE *data = malloc(filesize);
	BYTE *d = data;
	if(pf_mount(&f) != FR_OK){
		free(data);
		return 0;
	}
	dir("/");
	if(pf_open("00readme.txt") != FR_OK){
		free(data);
		disk_close();
		return 0;
	}
	int length = filesize;
	while(length > 0){
		WORD l = 0xfffe;
		if(length < l){
			l = length;
		}
		if(pf_read(d, l, &read_count) != FR_OK){
			free(data);
			disk_close();
			return 0;
		}
		d += l;
		length -= l;
	}
	file_save("d", data, filesize);
	free(data);
	disk_close();
	return 0;
}

ファイルサイズは手抜きでソースファイルから指定しました。

注意したい点は pf_read の読み出し数の型が WORD であることです。ファイルサイズが 65535 byte を越えることは頻繁に発生するので、0xffff 以内でループを分割する必要があります。

こういった点のロジックの確認などは PC 上でやっておく方が対処がしやすいと思います。

Makefile を作る

暗黙のルールを使い記述量を減らしました。ターゲットの Makefile はしっかり作りましょう。

OBJS = diskio.o pff.o fataccess.o
CFLAGS = -O2
CC = gcc
all = fataccess.exe

fataccess.exe: $(OBJS)
	$(CC) -o $@ $(OBJS)

Make, 実行

make, 実データ, アプリケーション実行, 出力された 00readme.txt の先頭を表示, イメージから取り出したファイルと元のファイルが同じか sha1sum を出してみました。

終わりに

最初は面倒そうだと思ってたのですが、わりとあっさりできてしまいました。これをベースにターゲットに持って行けば、動かなかったとしても問題の切り分けがしやすくなると思います。

実はいままで pff の write 処理は使用したことがありませんが、今回作った PC 向けソースで動作を試してみたいと思います。