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 f_mount - Register/Unregister a work area of a volume f_open - Open/Create a file f_close - Close an open file f_read - Read data f_write - Write data f_lseek - Move read/write pointer, Expand size f_truncate - Truncate size f_sync - Flush cached data 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] << 😎 + 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.
View full article