Search This Blog

Jan 3, 2013

Porting FatFs to SiM3U1xx

1.    Introduction
    It has been one month since LUFA USB CDC worked. I was planning to add support USB mass storage device. I have to find storage media for testing. I have considered MCU internal RAM, Flash, they are too small to match my requirement. I need add SD as storage media. I have been looking around web to see whether there is an open source SD card device driver, and I found FatFS which is open source file system, and also contain a simple SD card SPI driver  for other platforms.  And my interesting had been changed to FatFS.  It was well designed for embedded system and well implemented for several MCU platform. As we lack of FAT 12/16/32 support. I would like to port this file system in our SiM3U1xx platform. And of course, the SD card driver also supported by the way. It isn't too far from our original target, right? :-)

2.    Background Knowledge
    We have three parts need to understand well for starting our task. First is SD card knowledge, second is FAT , third is FatFs structure.
2.1. SD card
   For SD card background knowledge, I would like to introduce this article, I think it has told everything need to implement SD card driver.    How to Use MMC/SDC.
2.2. FAT
   FAT was introduced by Microsoft. For long file name(LFN) support, we need to pay license fee to Microsoft. Or we can disable LFN function to avoid license fees.  For FAT specification, it can be found in  FAT32 Specification by Microsoft (The reference document on FAT file system)
2.3. FatFs
    FatFs is a generic FAT file system module for small embedded systems. The FatFs is written in compliance with ANSI C and completely separated from the disk I/O layer. Therefore it is independent of hardware architecture. It can be incorporated into low cost microcontrollers, such as AVR, 8051, PIC, ARM, Z80, 68k and etc..., without any change.
   The official web site link below http://elm-chan.org/fsw/ff/00index_e.html. The latest FatFs version is R0.09a. It can be downloaded from FatFs R0.09a.  Also, it contains several sample projects which is mot benefit for us to start porting.  Here is the link FatFs sample projects (AVR, PIC24, LPC2300, LPC1700, FM3, V850ES, H8/300H, SH-2A, RX62N, Win32 and Generic uC)

3.    Hardware
   We choose one SiM3U1xx  MCU card, Audio I/O board and one mother board. Here is the photo of the hardware platform.


Audio I/O board and MCU card connection
SD pin
MCU card
I/O card
DAT3/CS
PB0.8
SPI_NSS0_EZR
CMD/MOSI
PB0.7
SPI_MOSI_EZR
CLK/SCLK
PB0.5
SPI_SCK_EZR
DAT0/MISO
PB0.6
SPI_MISO_EZR
SW
PB2.9
SD_SW


4.    Code Porting
    We need to understand the structure of FatFS and implement low level communication SD card driver.
4.1 FatFs structure
    There is a FatFs module application note which is recommended to read first by Author. For my experience, we can go back to know more about FatFs after porting done.
All files under FatFs directory like below.
\ff9a\src\00readme.txt
\ff9a\src\diskio.c
\ff9a\src\diskio.h
\ff9a\src\ff.c
\ff9a\src\ff.h
\ff9a\src\ffconf.h
\ff9a\src\integer.h
   It doesn't contain SD card low level driver but only file system. We can get SD card simple low level driver code from \ffsample\avr\mmc.c, replace diskio.c with mmc.c .
4.2 SD card driver porting
We need set correct GPIO pin as SPI, set SPI initial clock lower than 400Khz for SD card initialization requirement. And change the SPI communication code in mmc.c with SiM3U1xx SPI peripheral.
4.2.1 GPIO setting
   SI32_PBSTD_A_set_pins_digital_input(SI32_PBSTD_0, 0x0040);
   SI32_PBSTD_A_set_pins_push_pull_output(SI32_PBSTD_0, 0x01A0);
   SI32_PBSTD_A_write_pbskipen(SI32_PBSTD_0, 0x001F);

   SI32_PBCFG_A_enable_xbar0l_peripherals(SI32_PBCFG_0,
                                          SI32_PBCFG_A_XBAR0L_SPI0EN |
                                          SI32_PBCFG_A_XBAR0L_SPI0NSSEN);
   SI32_PBCFG_A_enable_crossbar_0(SI32_PBCFG_0);
   SI32_PBCFG_A_enable_crossbar_1(SI32_PBCFG_0);
4.2.2 SPI peripheral initialization
   SI32_CLKCTRL_A_enable_apb_to_modules_0(SI32_CLKCTRL_0,
                                         SI32_CLKCTRL_A_APBCLKG0_SPI0 |
                                         SI32_CLKCTRL_A_APBCLKG0_FLASHCTRL0);
  
   SI32_SPI_A_select_4wire_master_mode_nss_high (SI32_SPI_0);
   SI32_SPI_A_write_clkrate(SI32_SPI_0, 0x00000040);
   SI32_SPI_A_set_data_length(SI32_SPI_0, 8);
   SI32_SPI_A_select_master_mode(SI32_SPI_0);
   SI32_SPI_A_enable_module(SI32_SPI_0);
   SI32_SPI_A_select_rx_fifo_threshold(SI32_SPI_0,  SI32_SPI_FIFO_THRESHOLD_FOUR);
   SI32_SPI_A_select_tx_fifo_threshold(SI32_SPI_0,  SI32_SPI_FIFO_THRESHOLD_FOUR);
4.2.3. SPI low level communication function
     In mmc.c, we have couple of functions and macros need to be replaced.
/* Port controls  (Platform dependent) */
#define CS_LOW()        SI32_SPI_A_clear_nss (SI32_SPI_0)                /* CS=low */
#define CS_HIGH() SI32_SPI_A_set_nss (SI32_SPI_0)                        /* CS=high */
#define SOCKINS        true  /* Card detected.   yes:true, no:false, default:true */
#define SOCKWP        fales /* Write protected. yes:true, no:false, default:false */

/* Exchange a byte */
static BYTE xchg_spi(BYTE dat)
{
SI32_SPI_A_write_tx_fifo_u8(SI32_SPI_0, dat);
while(0 == SI32_SPI_A_is_shift_register_empty_interrupt_pending(SI32_SPI_0));
dat = (BYTE)SI32_SPI_A_read_rx_fifo_u8(SI32_SPI_0);
return dat;
}
/* Send a data block */
static void xmit_spi_multi(const BYTE *p,UINT cnt)
{
do {
SI32_SPI_A_write_tx_fifo_u8(SI32_SPI_0, *p++);
while(0 == SI32_SPI_A_is_shift_register_empty_interrupt_pending(SI32_SPI_0));
SI32_SPI_A_write_tx_fifo_u8(SI32_SPI_0, *p++);
while(0 == SI32_SPI_A_is_shift_register_empty_interrupt_pending(SI32_SPI_0));
} while (cnt -= 2);
    SI32_SPI_A_flush_rx_fifo(SI32_SPI_0);
    SI32_SPI_A_flush_tx_fifo(SI32_SPI_0);
}
/* Receive a data block */
static void rcvr_spi_multi(BYTE *p,UINT cnt)
{
do {
SI32_SPI_A_write_tx_fifo_u8(SI32_SPI_0, 0xFF);
while(0 == SI32_SPI_A_is_shift_register_empty_interrupt_pending(SI32_SPI_0));
*p++ = SI32_SPI_A_read_rx_fifo_u8(SI32_SPI_0);
SI32_SPI_A_write_tx_fifo_u8(SI32_SPI_0, 0xFF);
while(0 == SI32_SPI_A_is_shift_register_empty_interrupt_pending(SI32_SPI_0));
*p++ = SI32_SPI_A_read_rx_fifo_u8(SI32_SPI_0);
} while (cnt -= 2);
    SI32_SPI_A_flush_rx_fifo(SI32_SPI_0);
    SI32_SPI_A_flush_tx_fifo(SI32_SPI_0);
}
/* Wait for card ready           */
static int wait_ready (void)
{
BYTE d;
uint32_t time_out;

time_out = msTicks + 50;        /* Wait for ready in timeout of 500ms */
do
{
d = xchg_spi(0xFF);
}while ((d != 0xFF) && (time_out > msTicks));

return (d == 0xFF) ? 1 : 0;
}
OK we are done with porting job, now let start with test.

5.    Function verify
     We prepare simple file system test sequence. First, we read one file from SD card and print part of contents, , here we have "test.abp"; Second, we create a hello.txt file and put "Hello world!" in it; Third, we make a root directory list, it should show all files under root directory.

void fatfs_test()
{
FRESULT rc;                                /* Result code */
DIR dir;                                /* Directory object */
FILINFO fno;                        /* File information object */
UINT bw, br, i;

f_mount(0, &Fatfs);                /* Register volume work area (never fails) */

printf("\nOpen an existing file (test.abp).\n");
rc = f_open(&Fil, "TEST.ABP", FA_READ);
if (rc) die(rc);

printf("\nType the file content.\n");
//for (;;)
do
{
rc = f_read(&Fil, Buff, sizeof Buff, &br);        /* Read a chunk of file */
if (rc || !br) break;                        /* Error or end of file */
for (i = 0; i < br/4; i++)                /* Type the data */
putchar(Buff[i]);
}while(0);
if (rc) die(rc);

printf("\nClose the file.\n");
rc = f_close(&Fil);
if (rc) die(rc);

printf("\nCreate a new file (hello.txt).\n");
rc = f_open(&Fil, "HELLO.TXT", FA_WRITE | FA_CREATE_ALWAYS);
if (rc) die(rc);

printf("\nWrite a text data. (Hello world!)\n");
rc = f_write(&Fil, "Hello world!\r\n", 14, &bw);
if (rc) die(rc);
printf("%u bytes written.\n", bw);

printf("\nClose the file.\n");
rc = f_close(&Fil);
if (rc) die(rc);

printf("\nOpen root directory.\n");
rc = f_opendir(&dir, "");
if (rc) die(rc);

printf("\nDirectory listing...\n");
for (;;) {
rc = f_readdir(&dir, &fno);                /* Read a directory item */
if (rc || !fno.fname[0]) break;        /* Error or end of dir */
if (fno.fattrib & AM_DIR)
printf("     %s\n", fno.fname);
else
printf("%8lu  %s\n", fno.fsize, fno.fname);
}
if (rc) die(rc);

printf("\nTest completed.\n");
while(1);
}

Here is the output of the test:
******************************************
Open an existing file (test.abp).

Type the file content.
\357\273\277
Close the file.

Create a new file (hello.txt).

Write a text data. (Hello world!)
14 bytes written.

Close the file.

Open root directory.

Directory listing...
  540011  TEST.ABP
      14  HELLO.TXT

Test completed.
********************************************
OK, The test result shows file system works very well. We have done the porting job. Cheers!
6.    Source code
   Source code can be found in https://github.com/MarkDing/fatfs