Porting FatFs file system to KL26 SPI SD card code

Document created by Kerry Zhou Employee on May 6, 2016Last modified by Kerry Zhou Employee on May 9, 2017
Version 3Show Document
  • View in full screen mode

Porting FatFs file system to KL26 SPI SD card code

1 Abstract

     Without the SDHC module, Kinetis KL series need to use the SPI interface to communicate with the SD card. Normally, when customer use the SD card, they are not only want to write and read the SD card, but also prefer to create files(eg, text file, csv file,etc.) in the SD card to record some important data. Use the file to record the data, then the data can be read easily by the PC. MCU need to use the file system to operate the files, the file system should realize the function of file creating, file deleting, file reading and writing, etc. FatFs is a generic FAT/exFAT file system module for small embedded systems. This document mainly describe how to port a FatFs file system to the KL26 SPI SD card code, SD card SPI interface hardware circuit and the SD card basic operation code.

2 FatFs file system introduction

2.1 FatFs feature

  • Windows compatible FAT/exFAT file system.
  • Platform independent. Easy to port.
  • Very small footprint for program code and work area.
  • Various configuration options to support for:
    • Multiple volumes (physical drives and partitions).
    • Multiple ANSI/OEM code pages including DBCS.
    • Long file name in ANSI/OEM or Unicode.
    • exFAT file system.
    • RTOS envilonment.
    • Fixed or variable sector size.
    • Read-only, optional API, I/O buffer and etc...

2.2 FatFs file system organizations

 

From the above pictures, we can see that in a project with Fatfs module, there mainly 4 parts: application, Fatfs, Disk I/O layer and the Media(SD card).

(1) Application, user just need to call the FatFs API function to realize the file creation, read, write and delete.

(2) FatFs module, this module contains 6 important files which customer need to use, it is: diskio.c, diskio.h, ff.c, ff.h, ffconf.h, integer.h.  diskio.c and diskio.h is used to call the SD card operation function from the Disk I/O layer, user need to modify this file to match the disk I/O layer, or write the disk I/O layer match this file. ff.c,ff.h is the FatFs file system layer, it defines the API function, user don’t need to modify it. ffconf.h is the system configuration file. integer.h is the data type define file, user don’t need to modify these two files.

(3) Disk I/O layer, there has mmc.c and spi.c, actually, the detail name can be defined by the user, it is not fixed. Mmc.c is used to realize the SD card function, eg, SD initialization, SD block writing and reading.  Spi.c is the MCU SPI interface file, it realize the SPI communication function, because the Kinetis series don’t have the SDHC interface, then it use the SPI interface to communicate with the SD card.

(4) Media, it can be SD,MMC, USB, NAND flash, here we use the SD card.

More details, please refer to FatFs Module application note.

2.3 Common API function

More functions, please go to this link: http://elm-chan.org/fsw/ff/00index_e.html

3 SPI SD operation

3.1 Hardware

      This document use the YL_KL26 as the testing board, customer also can add an external SD card circuit to the FRDM-KL26 board.

The board is using the TF card, SD SPI interface circuit is:

 

The pin assignment in the YL-KL26 board is defined as follows:

KL26 pin

SPI name

PTC4

SPI_CS0

PTC5

SPI_SCK

PTC6

SPI_MOSI

PTC7

SPI_MISO

3.2 Softwave

     The test code project is based on the MDK5.1x.

3.3 SD I/O Layer

3.3.1 SD card initialization

The communication speed for SD card initialization can’t exceed 400kb/s, if the speed is higher than 400kbps, user need to add the delay in the initialization code, otherwise the initialization will be failure. After the initialization is successful, user can increase the SD card communication speed.

Initialization process:

(1)  Initialize the SPI interface which connect to the SD card, down to low speed.

(2)  Power on delay 72clks, wait for the SD card ready

(3)  Go idle state, CMD0, this command will trigger the SD card to use the SPI interface.

(4)  Get SD card information, CMD8, get the SD card version.

(5) Active the SD card,  with CMD55+CMD41

(6) Read OCR data,CMD59.

(7) Set SD card block size to 512Byte. CMD16

(8) Read CSD, get other information, CMD9

(9) Change to high speed and disable the CS

uint8 MMCInit(void)

{

                uint8 i = 0,k = 0,tmp = 0;

                uint16 cnt=0;

                uint8  buff[512];

               

                SSP0LowSpeed();                                      // low speed

                MMCDelayUs(5000);

                                                                 

                for (i=0; i<0x0F; i++)              

                {

                   Send_Byte(0xFF);          // send 72 clocks

                }

                // Send Command CMD0 to SD/SD Card  enter idle

                do

                {

                    tmp = MMCWriteCmd(CMD0,0x00,0x95);   // CMD0

                     k++;

                }while ((tmp != 1) && (k < 200));

                 

                if(k == 0)

                {

                  MMCCS(1);           //cs pullup, disconnect

                  Send_Byte(0xFF);

                  printf("\n SD reset fail");

                  return 1;//

                }                             

                //get SD card version

                 tmp = MMCWriteCmd( CMD8,0x1AA,0x87 );

                 printf( "SD_CMD8  return  %d........\n\n", tmp ); 

if(tmp == 1)// 2.0 card

{

         cnt=0xffff;

               

   do

   {

    MMCWriteCmd( CMD55, 0, 0xff );

    tmp = MMCWriteCmd( CMD41,0x40000000, 0xff);//CMD41 

    cnt--;

   } while ((tmp) && (cnt));

                 //Get OCR information

                 tmp = MMCWriteCmd(CMD58, 0, 0 );

   if ( tmp != 0x00 )

   {

                  MMCCS(1);           //cs pullup, SD card disconnect

                                printf( "\nSD_CMD58 return  %d....\n", tmp );

                  return 1;//

   }

 

   for ( i = 0; i < 4; i++ )

   {

    buff[ i ] = Get_Byte();

   }

   MMCCS(1);

   printf( "OCR return: %x %x %x %x....\n\n", buff[0],buff[1],buff[2],buff[3] );

 

   if ( buff[0] & 0x40 )

   {

                                 SD_Type = SD_TYPE_V2HC;

     printf( "card is V2.0 SDHC.....\n\n" );

   }

   else {

                                 SD_Type = SD_TYPE_V2;

     printf( "card is V2.0.....\n\n" );

   }          

   while(MMCWriteCmd(CMD16,512,0xff)!=0);

                 MMCWriteCmd(CMD9,0,0xff);

  }

                SSP0HighSpeed();                    //back to high speed

                MMCCS(1);            

                return 0;                       

}

3.3.2 Read one SD card block

The block size is 512Byte, the read process is:

  • Send CMD17 and wait the response
  • Receive the start token 0XFE
  • Receive the 512Byte data
  • Receive 2 bytes CRC
  • Disable the CS pin

 

uint8 MMCReadSingleBolck(uint32 addr,uint8 *buf)

{

                uint16 i;

                uint8 sta;

                if(SD_Type!=SD_TYPE_V2HC)

                {

                      addr= addr<<9;

                }

                sta = MMCWriteCmd(CMD17,addr,0x01);

                while(sta !=0)

                {

                  sta = MMCWriteCmd(CMD17,addr,0x01);

                }

 

                while (Get_Byte() != 0xFE){;}

                  if(sta == 0)

                {

                  for (i=0; i<512; i++)     

                  {

                    buf[i] = Send_Byte(0xFF);

                  }           

                }

                Send_Byte(0xFF);                                                 

                Send_Byte(0xFF);

                MMCCS(1);

                return 0;

}

3.3.3 Read multiple SD card block

uint8 MMCReadMultipleBolck(uint32 addr,uint8 *buf,uint8 count)

{

         uint16 i;

                if(SD_Type!=SD_TYPE_V2HC)

                {

                    addr= addr<<9;

                }

                               

                if (MMCWriteCmd(CMD18,addr,0xFF) != 0x00)   

                {

                    return 1;                         

                }

               

                MMCCS(0);

                do

                {

                    while (Send_Byte(0xFF) != 0xFE){;}

                    for (i=0; i<512; i++)                    

                    {

                        *buf++ = Send_Byte(0xFF);

                    }

                    Send_Byte(0xFF);                                                                    

                    Send_Byte(0xFF);

               

                }while (--count);

                MMCCS(1);

                MMCWriteCmd(CMD12,0x00,0xFF);  

                Send_Byte(0xFF);//delay

                return 0;

}

3.3.4 Write one SD card block

The procedure is:

  • Send CMD24 and wait the response
  • Receive the start token 0XFE
  • Send the 512Byte data
  • Send 2 bytes CRC
  • Disable the CS pin

 

uint8 MMCWriteSingleBlock(uint32 addr,const uint8 *buf)

{

                uint16 i,retry ;

                uint8  temp;

               

                if(SD_Type!=SD_TYPE_V2HC)

                {

                     addr=addr<<9 ;

                }                             

                if (MMCWriteCmd(CMD24,addr,0x01) != 0x00)        

                {

                    return 1;                                 

                }

                MMCCS(0);

                //wait SD card ready

                Send_Byte(0xFF);         

                Send_Byte(0xFF);

                Send_Byte(0xFF);

                Send_Byte(0xFE);            

 

                for (i=0; i<512; i++)                

                {

                    Send_Byte(buf[i]);

                }

                //Dummy CRC

                Send_Byte(0xFF);                                                             

                Send_Byte(0xFF);

                temp = Send_Byte(0xFF);                                       

                temp &= 0x1F;       

                if (temp != 0x05)

                {

                    MMCCS(1);

                    return 1;                                                                                 

                }

                               

                while (Send_Byte(0xFF) == 0x00)

                {

                     retry++;

                     if(retry>0xfffe)

                    {

                      MMCCS(1);

                       return 1 ;

                     }

                }

                MMCCS(1);

                Send_Byte(0xFF);

                return 0;

}

3.3.5 Write multiple SD card block

uint8 MMCReadMultipleBolck(uint32 addr,uint8 *buf,uint8 count)

{

    uint16 i;

                if(SD_Type!=SD_TYPE_V2HC)

                {

                                  addr= addr<<9;

                }

                               

                if (MMCWriteCmd(CMD18,addr,0xFF) != 0x00)   

                {

                    return 1;                         

                }

               

                MMCCS(0);

                do

                {

                    while (Send_Byte(0xFF) != 0xFE)

                    {

                        ;                                                               

                    }

               

                    for (i=0; i<512; i++)                    

                    {

                        *buf++ = Send_Byte(0xFF);

                    }

               

                    Send_Byte(0xFF);                                                                    

                    Send_Byte(0xFF);

               

                }while (--count);

               

                MMCCS(1);

                MMCWriteCmd(CMD12,0x00,0xFF);  

                Send_Byte(0xFF);//delay

                return 0;

}

4 FatFs file system porting

4.1 FatFs source code download

Go to FatFs official website download the source code, the link is:

http://elm-chan.org/fsw/ff/00index_e.html

The latest version is FatFs R0.12.

   Unzip it, like the following picture, just need 6 files, user can copy it to the project SPI driver folder, and create a new folder named as fatfs.

4.2 Modify diskio.c file

We need to modify these functions:

disk_initialize:Disk initialize

disk_status     :Get the Disk status

disk_read       :Read Disk block

disk_write      :Write Disk block

disk_ioctl       :control device character

get_fattime    :Get current time

4.2.1 disk_initialize function

DSTATUS disk_initialize (

                BYTE pdrv                                                

)

{

                DSTATUS stat;

                   stat=MMCInit();  //SD card initialization

                 if(stat == STA_NODISK)

                   {

                        return STA_NODISK;

                    }

                else if(stat != 0)

                  {

                        return STA_NOINIT; 

                 }

              else

               {

                     return 0;          

              }

}

4.2.2 disk_status  function

DSTATUS disk_status (

                BYTE pdrv                 /* Physical drive nmuber to identify the drive */

)

{

 

     if(pdrv)

    {

        return STA_NOINIT; 

    }

                return RES_OK;

}

4.2.3 disk_read function

DRESULT disk_read (

                BYTE pdrv,                                /* Physical drive nmuber to identify the drive */

                BYTE *buff,                               /* Data buffer to store read data */

                DWORD sector,        /* Sector address in LBA */

                UINT count                               /* Number of sectors to read */

)

{

    DRESULT res;

    if (pdrv || !count)

    {   

        return RES_PARERR; 

    }          

                if (count == 1) 

                {

                                res = MMCReadSingleBolck(sector,buff);

                }

                else          

                {

                                res = MMCReadMultipleBolck(sector,buff,count);

                }

    if(res == 0x00)

    {

        return RES_OK;

    }

    else

    {

        return RES_ERROR;

    }

}

4.2.4 disk_write function

DRESULT disk_write (

                BYTE pdrv,                                                /* Physical drive nmuber to identify the drive */

                const BYTE *buff,      /* Data to be written */

                DWORD sector,                        /* Sector address in LBA */

                UINT count                                               /* Number of sectors to write */

)

{

                DRESULT res;

                  if (pdrv || !count)

    {   

        return RES_PARERR; 

    }

    if(count == 1)

    {

        res = MMCWriteSingleBlock(sector, buff);

    }

    else

    {

        res = MMCWriteMultipleBlock(sector, buff, count);

    }

    if(res == 0)

    {

        return RES_OK;

    }

    else

    {

        return RES_ERROR;

    }

}

 

4.2.5 disk_ioctl function

DRESULT disk_ioctl (

                BYTE pdrv,                                /* Physical drive nmuber (0..) */

                BYTE cmd,                /* Control code */

                void *buff                /* Buffer to send/receive control data */

)

{

                DRESULT res;

                BYTE n, csd[16];

                DWORD csize;

                 if (pdrv)

                 {   

                     return RES_PARERR; 

                }

                res = RES_ERROR;

                switch (cmd)

                {

                    case CTRL_SYNC       : res = RES_OK; break;

                    case GET_SECTOR_COUNT: /* Get number of sectors on the disk (WORD) */

                                                                if((MMCWriteCmd(0x49,0x00,0x95) == 0) && MMCCSD_CID(0x49, csd))

                                                                {

                                                                if((csd[0] >> 6) == 1) /* SDC ver 2.00 */

                                                                {

                                                                csize = csd[9] + ((WORD)csd[8] << 8) + 1;

                                                                *(DWORD*)buff = (DWORD)csize << 10;

                                                                }

                                                                else /* MMC or SDC ver 1.XX */

                                                                {

                                                                n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;

                                                                csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;

                                                                *(DWORD*)buff = (DWORD)csize << (n - 9);

                                                                }

                                                                res = RES_OK;

                                                                }

                                                                break;

                    case GET_SECTOR_SIZE : /* Get sectors on the disk (WORD) */

                                                                   *(WORD*)buff = 512;

                                                                   res = RES_OK;

                                                                   break;

                    case GET_BLOCK_SIZE  :

                                                            if ((MMCWriteCmd(0x49,0x00,0x95) == 0) && MMCCSD_CID(0x49, csd)) /* Read CSD */

                                                       {

                             *(DWORD*)buff = (((csd[10] & 63) << 1) + ((WORD)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1);

                                                                       res = RES_OK;

                                                       }

                                           break;

 

                    default              : res = RES_PARERR; break;

                }

 

                return res;

}

4.2.6 Get_fattime function

  This function is used to get the current time, and write it in the file attribute when create, modify the files. It should associate with the RTC, this project didn’t add this function, so just write the code like this:

DWORD get_fattime (void)

{

return 0;

}

4.2.7 include SD.h file

Comment usb, ATA include files, and add the user SD.h file, this is the SD card IO layer header file.

#include "diskio.h"                   /* FatFs lower layer API */

//#include "usbdisk.h"              /* Example: Header file of existing USB MSD control module */

//#include "atadrive.h"            /* Example: Header file of existing ATA harddisk control module */

//#include "sdcard.h"                               /* Example: Header file of existing MMC/SDC contorl module */

#include "SD.h"

/* Definitions of physical drive number for each drive */

//#define ATA                           0              /* Example: Map ATA harddisk to physical drive 0 */

//#define MMC                        1              /* Example: Map MMC/SD card to physical drive 1 */

//#define USB                          2              /* Example: Map USB MSD to physical drive 2 */

4.3 Modify main function

This project function is to create two files: Test.csv and Test.txt.  Write four items in these files: Test1, Test2, Test3, Test4.

int main (void)

{

                uint16 i,j;

                FATFS fs;              

                FRESULT fr;

                FIL          fil;                                           

                UINT bw;

                char file_name1[12]="Test.csv";

                char file_name2[12]="Test.txt";

                System_init();

                spiInit(SPI0_BASE_PTR , Master);

                fr= f_mount(&fs,file_name1,0);

                if(fr)

                {

                                printf("\nError mounting file system\r\n");

                                for(;;){}

                }

                fr = f_open(&fil, file_name1, FA_WRITE | FA_OPEN_ALWAYS);//create csv file

                if(fr)

                {

                                printf("\nError opening text file\r\n");

                                for(;;){}

                }

                fr = f_write(&fil, "Test1 ,Test2 ,Test3 ,Test4 \r\n", 29, &bw); //write data to the excel file

                if(fr)

                {

                                printf("\nError write text file\r\n");

                                for(;;){}

                }

                 fr = f_close(&fil);

                if(fr)

                {

                                printf("\nError close text file\r\n");

                                for(;;){}

                }

                fr= f_mount(&fs,file_name2,0);

                if(fr)

                {

                                printf("\nError mounting file system\r\n");

                                for(;;){}

                }             

                fr = f_open(&fil, file_name2, FA_WRITE | FA_OPEN_ALWAYS);//create txt file

                if(fr)

                {

                                printf("\nError opening text file\r\n");

                                for(;;){}

                }

                fr = f_write(&fil, "Test1 ,Test2 ,Test3 ,Test4 \r\n", 29, &bw); //write data to the txt file

                if(fr)

                {

                                printf("\nError write text file\r\n");

                                for(;;){}

                }

                fr = f_close(&fil);

                if(fr)

                {

                                printf("\nError close text file\r\n");

                                for(;;){}

                }

           while(1)

                {

                         for(i=0;i<10;i++) for(j=0;j<65535;j++);

                        printf("\ntest_sd\n");//

                }

}

Add FatFs header files in the main.h.

#include "spi.h"

#include "SD.h"

#include "diskio.h"

#include "ff.h"

5 Test result

    After download the code to the KL26 board, then insert a 8G microSD card which already format with the Fat32, press the reset button on the board, user can find the following printf log from the com port:

It means the SD card is identified.

     Now, take out the SD card and insert it to the PC, user will find there has two files: Test.csv and Test.txt. Open these files, data Test1, Test2, Test3, Test4 can be find in it,  it means the FatFs file system is porting successfully.

3 people found this helpful

Outcomes