Porting FatFs file system to KL26 SPI SD card code

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Porting FatFs file system to KL26 SPI SD card code

Porting FatFs file system to KL26 SPI SD card code

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

 170385_170385.pngpastedImage_2.png170386_170386.pngpastedImage_3.png

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.

170396_170396.pngpastedImage_4.png

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

 

170397_170397.pngpastedImage_5.png

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.

Labels (1)
Attachments
Comments

Hi,

this document was useful for me as I ported FatFs + SPI SD card code to a KL27 microcontroller.

The example code I currently use is the composite msd cdc example (KSK 2.0), now with FatFs + SPI SD card code :smileyhappy:.

But how can I assign the SD card storage to the MSD component?

Hi Joe

MSD doesn't work with FAT, but only with the lowest level SD card sector accesses (which you will have ported). FAT is only really needed when the internal application also uses the card and for checking that the card is ready and formatted to set the MSD flags accordingly.

To avoid the need to port any code you can also get complete working solutions for this for the KL26 or KL27 (and most other Kinetis parts without porting exercises) (builds with KDS, CW, IAR, Keil, Atollic, Green Hills, Rowley, CooCox, GCC etc.) at
KL26: http://www.utasker.com/kinetis/TEENSY_LC.html#MSD3
KL27: http://www.utasker.com/kinetis/FRDM-KL27Z.html#SD and http://www.utasker.com/kinetis/Capuccino-KL27.html

It is based on the more powerful utFAT (although not relevant for MSD as noted above) and allows the complete operation to be simulated for most powerful development environment.

Regards

Mark
Kinetis for professionals: http://www.utasker.com/kinetis.html

It's true that MSD doesn't need FAT. 

I know that my application should avoid to have access to the SD card while the device is connected to a PC.

My device should only write to the SD after disconnected from the PC, (it's a datalogger).

For the KSDK 2.0 there are several USB examples, I need to merge the composite cdc msc with the msc sd card example,

that's why I ask if anyone could give me some advices.

Where can I find some instructions for doing that ?

Joe

Immediate and complete CDC + MSD + FAT composite solutions are available for the KL26 and KL27 at the links that I gave in case you need to develop a product quickly with proven reliability and smallest memory footprint.

If you need to do it with KSDK it is probably best to directly ask in the KSDK group: Kinetis Software Development Kit 

Regards

Mark

Thank you for the advice

Hi,

Can you elaborate on the SD.h file. I m a little confused. Is the SD.h something you downloaded somewhere or is it the file where you are including the functions used in the diskio files. eg

               ==>  uint8_t MMCInit()

               ==>  uint8_t MMCWriteCmd(uint8_t cmd, uint32_t arg, uint8_t crc )

               ==>  uint8_t MMCReadSingleBlock(uint32_t addr, uint8_t *buf)

               ==>  uint8_t MMCReadMultipleBlock(uint32_t addr,uint8_t *buf,uint8_t count)

               ==>  uint8_t MMCWriteSingleBlock(uint32_t addr,const uint8_t *buf)

               ==> uint8_t MMCWriteMultipleBlock(uint32_t addr, uint8_t *buf, uint8_t count)

Are these functions supposed to be in the SD.h file? Is the full source code available anywhere?

Thank you

No ratings
Version history
Last update:
‎05-06-2016 02:30 AM
Updated by: