FATシステムを使ってSDカードを使えるようにする (SH2-7144)
HEW開発環境
Dec2011 coskx
1.はじめに
SH2/7144にSDカードソケットをつけて,FATシステムを導入し,ファイルの読み 書きができるようにしている。
FATシステムを0から作るのは大変なので,「FatFs(小規模な組み込 みシステム向けの汎用FATファイルシステム・モジュール)」を用いる。このモジュールはChaN氏作成のフリー・ソフトウェアであ り,使いやすく作成されているものである。SDカードの使い方の説明もChaN氏のサイトにあるので,そちらで学んでほしい。
このページで公開しているFATシステムでできることは次の通りである。
(1)ファイルの読み書き(fwrite, fprintf, fread)
(2)ディレクトリの作成,カレントディレクトリの移動,ディレクトリの消去,カレントディレクトリの取得
(3)ディレクトリ内のファイル名の巡回取得
(4)1バイト文字限定のロングファイル名の使用
対応しているのはMMC,SDC,SDHCのカードである。また,このシステムでは,SH2/7144-12.5MHz(4逓倍)にて転送速度 6.25Mbps(純粋に転送する速度,システムクロック4周期で1bit転送)を実現している。(SH2のSCI0のクロック同期 通信とDMAを利用,このアイディアは専攻科生田畑さんのH8/3048foneのシステム)
さらに,ChaN氏の示す下位レイヤ(サンプルプログラムによる)を2つに分け,SDカードIOレイヤとハードウェアIOレイヤに分けている。ハードウェアIOレイヤを差し替えると,別な構成のマイクロコンピュータへの移 植が簡単にできるようになっている。
2.3つのレイヤよりなるFATシステム
このFATシステムは次に示す3つのレイヤよりできている。
このシステムは最初にH8/3048fone用に作成したが,「FAT Moduleレイヤ」と「SD Card IOレイヤ」はハードウェア依存がないため,H8/3048fone用のものをそのまま持ってきたものであり。sh2/7144向けには, 「HardwareIOレイヤ」のみ作り直している。
レイヤ 説明 FAT Moduleレイヤ ChaN氏の作成した論理的なFATシステムレイヤで,SDカード限定ではなく,複数の用途に使えるモジュールである。オリジナルからコンフィグファイル(ffconf.h)を一部変更 しただけで,そのまま利用している。 SD Card IOレイヤ ChaN氏の作成したサンプルを分割・チューニングして作成した論理的なSDカードIOモジュールで,CPUの構造から独立したレイヤである。(CPUのIOを直接操作しないレイヤ) HardwareIOレイヤ CPUのIOレジスタに対する操作を行うレイヤである。CPUが変更されたり,あるいは使用するIOセットが変更されたりした場合は,このレイヤだけ変更すればよいようになっている。
2つの通信方法から用途に応じて選べるようになっている。
3.SDカードソケット
SDカードソケットはDM1B-DSF-PEJ(秋月電子で入手)を用いており,配線は次 の通りとした。(この回路の元は専攻科生田畑さんの製作のH8/3048fone用回路)
SCIを使った通信の場合の配線(polling方式,DMA方式)
4.サンプルプログラム
使用しているリソース
・CMT1
・SCI0(SCIを用いた転送方式(polling方式,DMA方式)の時)
・PortEFATシステムオプション設定
ロングファイルネーム対応(ただし,日本語など2バイト文字は使えない)
5.各レイヤとグローバル関数
各レイヤモジュールが提供する関数は次のとおりである。
レイヤ ファイル レイヤモジュールが提供する
グローバル関数レイヤモジュールが使用する
下位モジュールレイヤの関数FATモジュールレイヤ ff.c
ff.h
ffconf.h
(FATモジュール
設定ファイル)
ccsbcs.c
(ロングファイル
名対応関数)f_mount - ワークエリアの登録・削除
f_open - ファイルのオープン・作成
f_close - ファイルのクローズ
f_read - ファイルの読み出し
f_write - ファイルの書き込み
f_lseek - リード/ライト・ポインタの
移動,ファイルの拡張
f_truncate - ファイル・サイズの
切り詰め
f_sync - キャッシュされたデータの
フラッシュ
f_opendir - ディレクトリのオープン
f_readdir - ディレクトリの読み出し
f_getfree - ボリューム空き領域の取得
f_stat - ファイル・ステータスの取得
f_mkdir - ディレクトリの作成
f_unlink - ファイル/ディレクトリの削除
f_chmod - ファイル/ディレクトリの
属性の変更
f_utime - ファイル/ディレクトリの
タイムスタンプの変更
f_rename - ファイル/ディレクトリの
名前変更・移動
f_chdir - カレント・ディレクトリの変更
f_chdrive - カレント・ドライブの変更
f_getcwd - カレント・ディレクトリの取得
f_forward - ファイル・データを
ストリーム関数に転送
f_mkfs - 論理ドライブのフォーマット
f_fdisk - 物理ドライブの分割
(関数f_fdiskはサポートされていない。
必要ならばffconf.hを設定変更する)
f_gets - 文字列の読み出し
f_putc - 文字の書き込み
f_puts - 文字列の書き込み
f_printf - 書式化文字列の書き込み
f_tell - 現在のリード/ライト・
ポインタの取得
f_eof - ファイル終端の有無の取得
f_size - ファイル・サイズの取得
f_error - ファイルのエラーの有無の取得
詳細はこちらdisk_initialize - ドライブの
初期化
disk_status - ドライブの
状態取得
disk_read - データの読み出し
disk_write - データの書き込み
disk_ioctl - その他の
ドライブ制御
get_fattime - 日付・時刻の
取得
(ファイルのタイムスタンプ用
としてファイル書き込みに使わ
れる。これは別に用意しておく
必要がある。この関数は存在し
なければならない。
GPSや電波時計などで時刻がわか
る場合はそれを使えばよい。
現在時刻がわからない場合は
0を返すとタイムスタンプは
一定の無意味な値となる。)SDカードIOモジュールレイヤ
diskio.c
diskio.h
disk_initialize - ドライブの初期化
disk_status - ドライブの状態取得
disk_read - データの読み出し
disk_write - データの書き込み
disk_ioctl - その他のドライブ制御詳細はこちら
SPI_putData - SDCに複数
バイト出力
SPI_getData - SDCから複数
バイト入力
SPI_assertCS - CS信号をLに
SPI_negateCS - CS信号をHに
SPI_setDownCounter - タイム
アウト用ダウンカウンタ
のセット
SPI_getDownCounter - ダウン
カウンタの読み出しハードウェアIOモジュールレイヤ intprg.c
(割り込み関数)
spi.c
spi.hinitSPI - SCI0を初期化
initSPI_CS - CS出力ポートを初期化
SPI_putData - SDCに複数バイト出力
SPI_getData - SDCから複数バイト入力
SPI_assertCS - CS信号をLに
SPI_negateCS - CS信号をHに
SPI_setDownCounter - タイム
アウト用ダウンカウンタ
のセット
SPI_getDownCounter - ダウン
カウンタの読み出しSPI_TimerWork - 10msecごとに
呼び出される作業関数
(タイマ割り込みで呼び出されるよう
にしておく必要がある)
6.ハードウェアIOモジュールレイヤについて
SDCに複数バイト入出力する関数は,
(1)ポーリング方式SCI (転送速度 125kbps[低速モード],6250kbps[高速モード])
(2)DMA方式SCI (転送速度 125kbps[低速モード],6250kbps[高速モード])
の2種類を準備している。SDメモリの操作に専念できるアプリケーションの場 合はDMA方式SCIで高速にデータ転送を行うと良い。そうでない場合(タイマ割り込み作業が優先で,そちらが忙しい場合など)では ポーリング方式SCIでデータ転送を行うと良い。どちらもデータ転送速度は同じだが,ポーリング方式ではCPU内のデータハンドリン グに時間がかかる。
低速モードはSDカードとの通信を初期化するときに用いる.初期化(SDカードのフォーマットではない)が終ったら,高速モードが使 える関数の詳細
void initSPI(int mode);
SCI0を初期化 mode 0:低速モード 1:高速モード 各baudrate値はspi.cで設定
転送速度を変更する目的で,mainから複数回呼び出されても構わない。
void initSPI_CS(void);
CS信号ポートの初期化
int SPI_putData(unsigned char *data, int num);
ポーリング方式SCIまたはDMA方式SCI,ソフトウェア駆動方式で
*dataからnumバイト出力
どの方式を用いるかは,spi.h中の設定で定める。
int SPI_getData(unsigned char *data, int num);
ポーリング方式SCIまたはDMA方式SCI,ソフトウェア駆動方式で
*dataにnumバイト入力
バッファサイズは呼び出し側の責任で確保しておく。
どの方式を用いるかは,spi.h中の設定で定める。
void SPI_assertCS();
CS信号をLにする
void SPI_negateCS();
CS信号をHにする
void SPI_setDownCounter(unsigned char chan, unsigned char csec);
タイムアウト用ダウンカウンタ値csecのセット(10msec単位)
このダウンカウンタは2つあり,chan=0,1で選択する
unsigned char SPI_getDownCounter(unsigned char chan);
タイムアウト用ダウンカウンタ値の読み出し(10msec単位)
このダウンカウンタは2つあり,chan=0,1で選 択する
7.サンプルプログラムの動作について
SDCに複数バイト入出力する方式が3種類(ソフトウェアポート駆動方式,ポーリング方式SCI,DMA方式SCI)
あるが,どれを使うかは,「spi.h」内で設定できる。
サンプルプログラムのmain()を含むfile_SDC.cには7つの関数main()が記述されている。
それぞれmain0からmain6までの名前で「file_SDC.c」の先頭で条件コンパイルにより,1つだけ実行される。
(1)main0
信号をオシロスコープで見ながら,主にハードウェアIOモジュールの関数をループで実 行する
実行の様子のスコープ画面の参照(SDカード未挿入で観 察,CPUのRXD(カードのDO)端子は常にhigh)(2)main1
SDカードIOモジュール内の「disk_initialize - ドライブの初期化」をループで実行する。
PCのモニタアプリ(teratermなど)に表示する。うまく動作すると「res=0」が表示される。(3)main2
SDカードIOモジュール内の「disk_ioctl - その他のドライブ制御」でSDカード用法を読み出して,
PCのモニタアプリ(teratermなど)に表示する。
実行例 (KINGMAX SDHC 4GB の例)
level 2
res=0 disk_initialize
res=0 CTRL_SYNC
res=0 size_sect=512
res=0 num_sect=7909376
res=0 type_flag=12
res=0 CSD
40 0e b0 32 5b 59 00 00 1e 2b 7f 80 0a 40 00 f3
res=0 CID
13 4b 47 53 44 30 34 47 00 00 c8 02 66 00 9c f1 .KGSD04G....f...
completed(4)main3
SDカードIOモジュール内の「disk_read - データの読み出し」でSDカードのセクタ読み出しを行い,
PCのモニタアプリ(teratermなど)に表示する。
実行例 (KINGMAX SDHC 4GB の例)
level 3 read sector
res=0 disk_initialize
res=0 disk_read
sector number=0 [0]
0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0120: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0170: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0190: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 ................
01c0: 03 01 0b 69 e9 d4 00 20 00 00 00 90 78 00 00 00 ...i........x...
01d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa ..............U.
completed(5)main4
FATモジュールを使って,ファイル"filetest.txt"をSDカードのルートディレクトリに書き込み,
次に読み出して,PCのモニタアプリ(teratermなど)に表示する。
実行例
level 4 read file
res=0 disk_initialize
res=0 f_open
res=0 f_close
res=0 f_open
Hello world.
This is the line 1.
This is the line 2.
This is the line 3.
This is the last line.
res=0 f_close
res=0 f_opendir
----A 1980/00/00 00:00 96 filetest.txt
----A 2011/11/15 15:53 14 hello0.txt
2 File(s), 110 bytes
0 Dir(s), 3946400K bytes free
completed(6)main5
FATモジュールを使って,以下のディレクトリ操作を行う。
ルートディレクトリにディレクトり「testdir」がなければ,
ディレクトり「testdir」を作り,その中にファイル「filetest.txt」を作る。
もし,ディレクトり「testdir」があれば,その中のファイル「filetest.txt」を
表示し,ファイルを消去し,ディレクトり「testdir」も消去する。
この状況をPCのモニタアプリ(teratermなど)に表示する。
実行例
level 5 make dir
res=0 disk_initialize
res=0 f_getcwd
current directory name : [0:/]
res=0 f_opendir
directory name : []
----A 1980/00/00 00:00 96 filetest.txt
----A 2011/11/15 15:53 14 hello0.txt
2 File(s), 110 bytes
0 Dir(s), 3946400K bytes free
res=5 f_opendir testdir
res=0 f_mkdir testdir
res=0 f_chdir testdir
res=0 f_open filetest.txt
res=0 f_close
res=0 f_getcwd
current directory name : [0:/TESTDIR]
res=0 f_opendir
directory name : []
D---- 1980/00/00 00:00 0 .
D---- 1980/00/00 00:00 0 ..
----A 1980/00/00 00:00 96 filetest.txt
1 File(s), 96 bytes
0 Dir(s), 3946336K bytes free
res=0 f_chdir ..
res=0 f_getcwd
current directory name : [0:/]
res=0 f_opendir
directory name : []
----A 1980/00/00 00:00 96 filetest.txt
D---- 1980/00/00 00:00 0 testdir
----A 2011/11/15 15:53 14 hello0.txt
2 File(s), 110 bytes
1 Dir(s), 3946336K bytes free
completedlevel 5 make dir
res=0 disk_initialize
res=0 f_getcwd
current directory name : [0:/]
res=0 f_opendir
directory name : []
----A 1980/00/00 00:00 96 filetest.txt
D---- 1980/00/00 00:00 0 testdir
----A 2011/11/15 15:53 14 hello0.txt
2 File(s), 110 bytes
1 Dir(s), 3946336K bytes free
res=0 f_opendir testdir
res=0 f_chdir testdir
res=0 f_open filetest.txt
Hello world.
This is the line 1.
This is the line 2.
This is the line 3.
This is the last line.
res=0 f_close
res=0 f_unlink filetest.txt
res=0 f_chdir ..
res=0 f_getcwd
current directory name : [0:/]
res=0 f_opendir testdir
directory name : [testdir]
D---- 1980/00/00 00:00 0 .
D---- 1980/00/00 00:00 0 ..
0 File(s), 0 bytes
0 Dir(s), 3946368K bytes free
res=0 f_unlink testdir
res=0 f_getcwd
current directory name : [0:/]
res=0 f_opendir
directory name : []
----A 1980/00/00 00:00 96 filetest.txt
----A 2011/11/15 15:53 14 hello0.txt
2 File(s), 110 bytes
0 Dir(s), 3946400K bytes free
completed(7)main6
FATモジュールを使って,ロングファイル名のファイル"filetest_longname.txt"を
SDカードのルート ディレクトリに書き込み,次に読み出して,
PCのモニタアプリ(teratermなど)に表示する。
実行例
level 6 long file name
res=0 disk_initialize
file name = (filetest_longname.txt)
res=0 f_open
res=0 f_close
res=0 f_open
Hello world.
This is the line 1.
This is the line 2.
This is the line 3.
This is the last line.
res=0 f_close
res=0 f_getcwd
current directory name : [0:/]
res=0 f_opendir
directory name : []
----A 2011/11/18 09:59 98 filetest.txt
----A 2011/11/21 11:54 14 hello0.txt
D---- 1980/00/00 00:00 0 testdir
----A 1980/00/00 00:00 96 filetest_longname.txt
3 File(s), 208 bytes
1 Dir(s), 3946304K bytes free
completed
8.サンプルプログラム中mainの初期化記述について
関数mainの初期化部分を抜き出すと次のようになる。
最初は低速モード(通信速度)でドライブを初期化し(disk_initialize(DRIVE_NO);),
初期化が終わったら高速モード(通信速度)に切り替える。
initLed(); /*sh_7144.cで 定義*/
initCMTInt(10000,1,15); /*10msecインターバルタイマ割り込み,CMT.ch1使用,割り込みレベル15 sh_7144.cで定義*/
startCMT1(); /* コンペアマッチタイマ1スタート sh_7144.cで定義*/
initSCI1(); /*sh_7144.cで 定義*/
initSPI_CS(); /*spi.cで定 義*/
msecwait(10); /*SD初期化のためには1msec以上のwaitが必要*/
initSPI(0); /*モード0(低速)で初期化 spi.cで定義*/
initDMAC(); /*spi.cで 定義*/
set_imask(14); //レベル15は割り込み許可
SCI1_printf("\nlevel 6 long file name\n");
do {
res=disk_initialize(DRIVE_NO); /*diskio.cで定義,ff.cからも呼び出される*/
SCI1_printf("res=%d disk_initialize\n",res);
msecwait(100);
if (tick=1-tick) turnOnLed(0);
else turnOffLed(0);
} while (res);
initSPI(1); /*モード1(高速)で初期化 spi.cで定義*/
f_mount(DRIVE_NO, &fatfs); /*FATシステムのマウント ff.cで定義*/