##はじめに
Cortex-M4を使ったマイコンシステムのソフトウェアを開発するにあたり、対象ボードが使えるようになるまでの間、別の市販マイコンボードを使って作業を行うこととしました。
また、開発するソフトウェアの評価に大きなデータを扱う必要が出てきたため、データをメモリカードから読み書きでるようにしようと考えました。
このため市販マイコンボードでFatfsを使えるようにしました。
Cortex-M4でRAMやFlashメモリをそれなりに搭載しているマイコンボードとして、Cypress(現在は infineon+Cypressか)のFM4を搭載した FM4-176L-S6E2CC-ETH が手元にあったことから、このボードでSDカード(今回はマクロSDカードを利用)とFatfsが使えるようにしました。
・FM4-176L-S6E2CC-ETH
https://japan.cypress.com/documentation/development-kitsboards/sk-fm4-176l-s6e2cc-fm4-family-quick-start-guide
・FM4 32-bit Arm Cortex-M4 Microcontroller (MCU) Families
https://japan.cypress.com/products/fm4-32-bit-arm-cortex-m4-microcontroller-mcu-families
・FM4 S6E2C-Series High Performance Arm Cortex-M4 Microcontroller (MCU) Family
https://japan.cypress.com/products/fm4-s6e2c-series-high-performance-arm-cortex-m4-microcontroller-mcu-family
##ハードウェア
今回は制御や配線が容易で、過去に別MCUやマイコンボードで利用実績のある、SPIでSDカードを接続するとにしました。
SDカードソケットは amazon で購入した以下のモジュールを使用し、マイコンボード FM4-176L-S6E2CC-ETH のArduino Shield I/Fのピンを使て接続します。
・Micro SD TFカードメモリシールドモジュール
https://www.amazon.co.jp/gp/product/B010GXAFFU/ref=ppx_od_dt_b_asin_image_s00?ie=UTF8&psc=1
##流用元ソフトウェア
流用元となるFatfsのソースコードを入手します。
使用するのは Fatfs本体のコードと、デバイスアクセス用のサンプルコードの2つです。
それぞれの場所からダウンロードします。
###Fatfs本体のコード
Fatfs本体のコードは以下のURLのページからダウンロードします。
この記事執筆時点では 0.14b というバージョンが最新のようです。
以下の赤矢印 「Download: FatFs R0.14b」をクリックすることでダウンロードすることができます。
ダウンロードファイルは ff14b.zip になります。
###サンプルコード
Fatfsを使ってマイコンボードからSDカードへアクセスするには、通信デバイスの制御プログラムが必要になります。
今回は通信デバイスとして SPIを使いSDカードへのアクセスコマンドやデータの受信を行います。
通信デバイスの制御コードを独自に作り込むこともさほど難しくはないと思いますが、別MCU用ですがSPIを使った制御プログラムがサンプルプログラムとして提供されているので、今回はサンプルプログラムを流用することにしました。
サンプルコードはFatfs本体と同じページからダウンロードできます。
以下の赤矢印 「Download: FatFs sample projects for various platforms」をクリックすることでダウンロードできます。
ダウンロードファイルは ffsample.zip になります。
##FM4へのFatfsの対応
FatfsをFM4搭載のFM4-176L-S6E2CC-ETHでアクセスするための対応方法を記します。
###FM4用定義ファイルの取得
以下の赤矢印 「Download S6E2CC series Template Project Version 1.0」をクリックすることでダウンロードできます。
ただし、ダウンロードには infineon か Cypressのレジストレーションが必要になるので、必要に応じてレジストレーションしてください。
(各チップベンダもですが、ちょっと面倒なところです)
ダウンロードファイルは s6e2cc-series.zip になります。
https://japan.cypress.com/documentation/development-kitsboards/sk-fm4-176l-s6e2cc-fm4-family-quick-start-guide
ダウンロードしたzipファイル内の以下のディレクトリのヘッダファイル群を使うとMCU搭載デバイスのレジスタへのアクセスを容易に行うことができます。
あとで紹介するプログラムのアクセスに必要となるので、ビルド時にインクルードパスへ登録しておいてください。
\s6e2cc-series\s6e2cc_template-v13\common
s6e2cc.h
system_s6e2cc.h
\s6e2cc-series\s6e2cc_template-v13\example\source
interrupt_type.h
mcu.h
###CMSISファイルの取得
前項でダウンロードしたFM4用のヘッダファイルを使い Cortex-Mのマイコンリソースへアクセスするための CMSISの定義ファイルが必要になります。
以下のURLからダウンロードします。
以下の赤矢印 「Download ZIP」をクリックすることでダウンロードできます。
ダウンロードファイルは CMSIS_5-develop.zip になります。
ダウンロードしたzipファイル内の以下のディレクトリのヘッダファイル群を s6e2cc.h からインクルードして使われるため、今回のFatfsのビルド時にインクルードパスへ登録しておいてください。
\CMSIS_5-develop\CMSIS\Core\Include
###Fatfsコードの改造、作成
Fatfs本体のソースコードを取り出します。
ff14b.zipファイル内から sourceディレクトリを取り出し、PC内の任意のディレクトリへコピーします。
以下のファイルが取得できると思います。
00history.txt
00readme.txt
diskio.c
diskio.h
ff.c
ff.h
ffconf.h
ffsystem.c
ffunicode.c
通信デバイスを制御するため、ffsample.zipファイル内から**\stm32\mmc_stm32f1_spi.cファイルを取り出し、mmc_fm4_spi.cというファイル名に置き替えます。
ファイルは前述のFatfs本体のソースディレクトリsource** へ配置してください。
なお、mmc_fm4_spi.cを使う場合、diskio.cは不要となります。ビルド対象外にするか、削除してしまって構いません。
コピーした状態では mmc_fm4_spi.c はSTM32用の制御コードとなっているため、14行目から208行目までを以下のコードへ置き替えます。
#include "mcu.h"
#define DEF_FAST_HZ 8000000
#define DEF_SLOW_HZ 400000
#define DEF_BUSCLK_HZ 96000000
#define MFS FM4_MFS3
#define FCLK_FAST() { MFS->BGR = DEF_BUSCLK_HZ / DEF_FAST_HZ - 1; }
#define FCLK_SLOW() { MFS->BGR = DEF_BUSCLK_HZ / DEF_SLOW_HZ - 1; }
#define CS_HIGH() { FM4_GPIO->PDORF |= (0x1<<0); /* HIGH */ }
#define CS_LOW() { FM4_GPIO->PDORF &= ~(0x1<<0); /* LOW */ }
#define MMC_CD 1 /* Card detect (yes:true, no:false, default:true) */
#define MMC_WP 0 /* Write protected (yes:true, no:false, default:false) */
void WaitMsec( int ms );
/*--------------------------------------------------------------------------
Module Private Functions
---------------------------------------------------------------------------*/
#include "ff.h" /* Obtains integer types */
#include "diskio.h" /* Declarations of disk functions */
/* MMC card type flags (MMC_GET_TYPE) */
#define CT_MMC3 0x01 /* MMC ver 3 */
#define CT_MMC4 0x02 /* MMC ver 4+ */
#define CT_MMC 0x03 /* MMC */
#define CT_SDC1 0x02 /* SDC ver 1 */
#define CT_SDC2 0x04 /* SDC ver 2+ */
#define CT_SDC 0x0C /* SDC */
#define CT_BLOCK 0x10 /* Block addressing */
/* MMC/SD command */
#define CMD0 (0) /* GO_IDLE_STATE */
#define CMD1 (1) /* SEND_OP_COND (MMC) */
#define ACMD41 (0x80+41) /* SEND_OP_COND (SDC) */
#define CMD8 (8) /* SEND_IF_COND */
#define CMD9 (9) /* SEND_CSD */
#define CMD10 (10) /* SEND_CID */
#define CMD12 (12) /* STOP_TRANSMISSION */
#define ACMD13 (0x80+13) /* SD_STATUS (SDC) */
#define CMD16 (16) /* SET_BLOCKLEN */
#define CMD17 (17) /* READ_SINGLE_BLOCK */
#define CMD18 (18) /* READ_MULTIPLE_BLOCK */
#define CMD23 (23) /* SET_BLOCK_COUNT (MMC) */
#define ACMD23 (0x80+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
#define CMD24 (24) /* WRITE_BLOCK */
#define CMD25 (25) /* WRITE_MULTIPLE_BLOCK */
#define CMD32 (32) /* ERASE_ER_BLK_START */
#define CMD33 (33) /* ERASE_ER_BLK_END */
#define CMD38 (38) /* ERASE */
#define CMD55 (55) /* APP_CMD */
#define CMD58 (58) /* READ_OCR */
static volatile DSTATUS Stat = STA_NOINIT; /* Physical drive status */
static volatile UINT Timer1, Timer2; /* 1kHz decrement timer stopped at zero (disk_timerproc()) */
static BYTE CardType; /* Card type flags */
/*-----------------------------------------------------------------------*/
/* SPI controls (Platform dependent) */
/*-----------------------------------------------------------------------*/
/* Initialize MMC interface */
static void init_spi (void)
{
/*
P40:SIN3_1
P41:SOT3_1
P42:SCK3_1
PF0:GPIO : /CS
*/
/* PFR 0:GPIO 1:PERI */
FM4_GPIO->PFR4 |= 0x0007;
FM4_GPIO->PFRF &= ~0x0001;
/* EPFR07 0/1:_0 2:_1 3:_2 */
FM4_GPIO->EPFR07 &= ~((0x3<<22)|(0x3<<24)|(0x3<<26));
FM4_GPIO->EPFR07 |= ((0x2<<22)|(0x2<<24)|(0x2<<26));
/* PF0 :GPIO/pull-up/OUT */
FM4_GPIO->PCRF |= (0x1<<0);
FM4_GPIO->DDRF |= (0x1<<0);
FM4_GPIO->PDORF |= (0x1<<0); /* HIGH */
// FM4_GPIO->PDORF &= ~(0x1<<0); /* LOW */
MFS->SCR = 0x80; /* UPCL:reset CSIO */
MFS->FBYTE2 = 0x01; /* RX FIFO data num */
MFS->FCR = 0x000f; /* Enable Tx/Rx FIFO */
MFS->ESCR = 0x00;
MFS->SMR = 0x4f; /* MD:010 */
MFS->SCR = 0x23; /* SPI, RXE, TXE */
CS_HIGH(); /* Set CS# high */
#if 0
for (Timer1 = 10; Timer1; ) ; /* 10ms */
#else
WaitMsec( 10 );
#endif
}
/* Exchange a byte */
static BYTE xchg_spi (
BYTE dat /* Data to send */
)
{
MFS->TDR = dat; /* Start an SPI transaction */
while ( !(MFS->SSR & 0x04) ) ; /* Wait for end of the transaction */
return (BYTE)MFS->RDR; /* Return received byte */
}
/* Receive multiple byte */
static void rcvr_spi_multi (
BYTE *buff, /* Pointer to data buffer */
UINT btr /* Number of bytes to receive (even number) */
)
{
int n;
for (n = 0; n < 14; n++)
MFS->TDR = 0xFF;
btr -= 14;
while (btr) {
while (!(MFS->SSR & 0x04)) ;
*buff++ = MFS->RDR;
MFS->TDR = 0xFF;
btr--;
}
for (n = 0; n < 14; n++) {
while (!(MFS->SSR & 0x04)) ;
*buff++ = MFS->RDR;
}
}
/* Send multiple byte */
static void xmit_spi_multi (
const BYTE *buff, /* Pointer to the data */
UINT btx /* Number of bytes to send (even number) */
)
{
int n;
for (n = 0; n < 14; n++)
MFS->TDR = *buff++;
btx -= 14;
while (btx) {
while (!(MFS->SSR & 0x04)) ;
MFS->RDR;
MFS->TDR = *buff++;
btx--;
}
for (n = 0; n < 14; n++) {
while (!(MFS->SSR & 0x04)) ;
MFS->RDR;
}
}
この中のプログラムの説明をします。
####1)定義
修正プログラムの先頭部分は FM4用のヘッダファイルの読み込みやデバイスアクセスに必要な定数やマクロを定義しています。
####2)init_spi()
今回、SDカードのアクセスに SPIを用いるので、SPIデバイスの初期化と使用するピンの設定を行っています。
SPIの信号は以下のピンを用いています。
P40:SIN3_1
P41:SOT3_1
P42:SCK3_1
PF0:GPIO : /CS
なお、init_spi()内でデバイス初期化の待ち時間としてWaitMsec()という関数を呼び出しています。
使用するシステムに合ったミリ秒単位の待ち処理を用意してください。
WaitMsec( 10 );
####3)xchg_spi()
SPIへのデータの送信と受信を行う関数です。
送受信データレジスタへの書き込み、読み出しを行っています。
####4)rcvr_spi_multi()
複数データの受信を行う関数です。
FM4のSPIデバイスでは、データを送信することで通信クロックが出力されるので、0xFFデータを送信後、データレジスタから受信データの読み出しを行っています。
####5)xmit_spi_multi()
複数データの送信を行う関数です。
FM4のSPIデバイスでは、データを送信すると受信も同時に行ってしまうため、データ送信後に空の読み出しも行っています。
###Fatfsのコンフィグレーション
Fatfsをどのように使うかのコンフィグレーションをFatfs本体内のファイル ffconf.hで設定することができます。
今回はファイルのタイムスタンプに必要な時間を取得する機能がなたいめ、ファイル更新日時をマクロ固定で取得することにしました。
コンフィグレーションを以下のように変更します。
#define FF_FS_NORTC 0
↓
#define FF_FS_NORTC 1
##サクセスサンプル
最後にFM4対応した Fatfsでのアクセス確認用のサンプルプログラムを紹介します。
###初期化
#include "ff.h"
#include "diskio.h"
#include <string.h>
int fat_test_init( void )
{
DSTATUS ret;
int result = 0;
ret = disk_initialize( 0 );
if( ret & STA_NOINIT ) {
result = -1;
}
return result;
}
###読み出しサンプル
SDカード上にデータの入った(適当なテキストデータが入った)test.txtというファイルがある想定です。
int fat_test_read( char *buff, int bsize )
{
FRESULT ret;
FATFS fs;
FIL fil;
UINT rdsz ;
ret = f_mount( &fs, "", 0 );
if( ret != FR_OK ) {
return -1;
}
ret = f_open( &fil, "test.txt", FA_READ );
if( ret != FR_OK ) {
return -2;
}
ret = f_read( &fil, buff, (UINT)bsize, &rdsz );
if( ret != FR_OK ) {
return -3;
}
f_close( &fil );
return (int)rdsz;
}
###書き込みサンプル
int fat_test_write( char *filename, char *buff, int size )
{
FRESULT ret;
FATFS fs;
FIL fil;
UINT wsize ;
ret = f_mount( &fs, "", 1 );
if( ret != FR_OK ) {
return -1;
}
ret = f_open( &fil, filename, FA_WRITE|FA_CREATE_ALWAYS );
if( ret != FR_OK ) {
return -2;
}
ret = f_write( &fil, buff, (UINT)size, &wsize );
if( ret != FR_OK ) {
return -3;
}
f_close( &fil );
return (int)wsize;
}
-以上 -