Sistem berkas FatFS merupakan sistem berkas yang cukup terkenal di bidang mikrokontroler. Berkat bobotnya yang ringan dan kompatibilitasnya, sistem ini menjadi pilihan favorit para pengembang MCU. Saat mengimplementasikan tugas-tugas seperti membaca dan menulis berkas pada flash disk dan kartu SD, kita sering kali memerlukan sistem berkas untuk mendukung pekerjaan kita. Terutama dalam beberapa aplikasi MCU, penambahan sistem berkas dapat secara signifikan meningkatkan kemudahan interaksi sistem. Dalam artikel ini, kami memperkenalkan implementasi dan penerapan sistem berkas FatFS pada STM32F4.
Fitur FatFS
- Sistem berkas FAT/exFAT yang kompatibel dengan DOS/Windows.
- Tidak bergantung pada platform dan mudah diadaptasi.
- Kode program dan ruang kerja hanya membutuhkan sedikit ruang.
- Mendukung berbagai opsi konfigurasi berikut:
- Nama file panjang dalam format ANSI/OEM atau Unicode.
- Sistem file exFAT, LBA 64-bit, dan GPT untuk menyimpan data dalam jumlah besar.
- Keamanan benang (thread safety) pada RTOS.
- Beberapa volume (drive fisik dan partisi).
- Ukuran sektor variabel.
- Beberapa halaman kode, termasuk DBCS.
- Hanya baca, API opsional, buffer I/O, dll.
Transplantasi FatFS pada MCU
Persiapan
Kita perlu melakukan beberapa persiapan yang diperlukan sebelum memulai proses porting FatFS. Pertama, Anda perlu menyiapkan platform perangkat keras yang sesuai. Di sini, kami menggunakan platform operasi STM32F407VET6. Pekerjaan porting pustaka yang terkait dengan perangkat keras USB juga telah selesai.
Kedua, kita juga perlu menyiapkan kode sumber FatFS yang relevan. Di sini kami menggunakan versi terbaru R0.14b, yang dapat diunduh dari situs web:
http://elm-chan.org/fsw/ff/00index_e.html
Setelah kode sumber yang diunduh diekstrak, terdapat dua folder: document dan source. Folder document berisi dokumentasi yang relevan, yang sama dengan konten di situs web. Anda dapat melihat dokumen-dokumen ini untuk membantu selama proses porting. Folder Source berisi file-file terkait kode sumber, terutama meliputi:

Di antara serangkaian berkas yang ditampilkan pada gambar di atas, berkas 00readme.txt berisi pengantar untuk setiap berkas. Berikut ini isi berkas tersebut:
| File Name | Description |
|---|---|
| 00readme.txt | Readme file |
| 00history.txt | Version history file |
| ff.c | FatFs module |
| ffconf.h | Configuration file for the FatFs module |
| ff.h | Header file for the FatFs application module |
| diskio.h | Header file for the FatFs and disk I/O module |
| diskio.c | An instance that attaches disk I/O functions to FatFS |
| ffunicode.c | Unicode encoding utility functions |
| ffsystem.c | Optional operating system-related file instance |
Di antara berkas-berkas tersebut, ff.c dan ff.h merupakan berkas inti. ffunicode.c berisi pengkodean karakter, dan pengkodean tersebut akan dipilih sesuai dengan pengaturan berkas konfigurasi. Berkas ffsystem.c disesuaikan dengan kebutuhan Anda. Oleh karena itu, berkas-berkas yang terkait dengan platform aplikasi tertentu dan perlu diimplementasikan oleh kami adalah berkas konfigurasi ffconf.h serta berkas operasi disk diskio.h dan diskio.c. Berkas-berkas ini juga menjadi fokus utama dalam proses porting kami.
Melaksanakan Transplantasi
Kami telah menyelesaikan persiapan untuk proses migrasi, dan sekarang kami akan melaksanakan migrasi aplikasi ke disk USB berkapasitas besar. Seperti yang telah kami sampaikan sebelumnya, berkas-berkas yang perlu diproses untuk migrasi ini adalah berkas konfigurasi ffconf.h serta berkas operasi disk diskio.h dan diskio.c.
Mengenai berkas konfigurasi ffconf.h, berkas ini sebenarnya memiliki salinan dari dirinya sendiri. Kami hanya perlu memodifikasi konfigurasinya sesuai kebutuhan. Parameter konfigurasi yang perlu dimodifikasi di sini meliputi:
Parameter konfigurasi metode pengkodean yang didukung FF_CODE_PAGE berkaitan dengan masalah pengkodean file. Kami mengonfigurasinya agar mendukung bahasa Mandarin Sederhana.
Jumlah drive logis dikonfigurasi dengan parameter FF_VOLUMES. FatFS dapat diterapkan ke beberapa drive secara bersamaan, jadi kita perlu mengonfigurasi jumlah drive sesuai dengan situasi sebenarnya.
Parameter konfigurasi cap waktu FF_FS_NORTC; sebagian besar waktu kita tidak perlu mencatat cap waktu, jadi kita menonaktifkannya di sini.
Sisanya adalah fungsi-fungsi terkait untuk mengimplementasikan operasi IO disk. Dokumen bantuan FatFS menyebutkan bahwa ada dua jenis fungsi yang perlu diimplementasikan: yang pertama adalah fungsi-fungsi terkait kontrol perangkat disk, terutama fungsi untuk memperoleh status perangkat, menginisialisasi fungsi perangkat, dan membaca. Fungsi data, fungsi penulisan data, dan fungsi yang terkait dengan kontrol perangkat; kategori kedua adalah fungsi operasi jam waktu nyata, yang terutama digunakan untuk mendapatkan fungsi waktu saat ini. Oleh karena itu, mengimplementasikan 6 fungsi ini adalah tugas utama dari transplantasi.
Fungsi untuk memeriksa status perangkat
Fungsi pendeteksi status disk disk_status. Digunakan untuk mendeteksi status disk dan akan dipanggil dalam berkas ff.c. Prototipe fungsinya adalah sebagai berikut:
ˆ DSTATUS disk_status(BYTE drV);
Berdasarkan definisi prototipe dan persyaratan perangkat penyimpanan massal USB kami, kita dapat mengimplementasikan fungsi pengambilan status disk sebagai berikut:
/*Status acquisition function for USBH*/
static DSTATUS USBH_status(BYTE lun)
{
DRESULT res = RES_ERROR;
if(USBH_MSC_UnitIsReady(&hUsbHostFS, lun))
{
res = RES_OK;
}
else
{
res = RES_ERROR;
}
return res;
}
Fungsi inisialisasi perangkat
Fungsi inisialisasi media penyimpanan disk_initialize. Fungsi ini digunakan untuk menginisialisasi perangkat disk dan akan dipanggil dalam berkas ff.c. Prototipe fungsinya adalah sebagai berikut:
ˆ DSTATUS disk_initialize(BYTE drv);
Berdasarkan definisi prototipe dan persyaratan perangkat penyimpanan massal USB kami, kita dapat mengimplementasikan fungsi inisialisasi drive disk, tetapi sebenarnya kita tidak membutuhkannya di sini, karena proses inisialisasi telah diselesaikan di perpustakaan USB HOST, jadi cukup kembalikan fungsi yang benar secara langsung.
/*Initialization function for USBH*/
static DSTATUS USBH_initialize(BYTE lun)
{
//Initialization has been completed in the USB HOST library
return RES_OK;
}
Membaca data
Baca fungsi sektor disk_read. Fungsi ini digunakan untuk membaca data disk. Fungsi ini ditulis sesuai dengan spesifikasi I/O disk dan akan dipanggil dalam berkas ff.c. Prototipe fungsinya adalah sebagai berikut:
- DRESULT disk_read(BYTE drv, BYTE*buff, DWORD sector, BYTE.count);
Berdasarkan definisi prototipe dan persyaratan perangkat penyimpanan massal USB kami, kita dapat mengimplementasikan fungsi pembacaan data disk sebagai berikut:
/*Read sector function for USBH*/
static DRESULT USBH_read(BYTE lun, BYTE *buff, DWORD sector, UINT count)
{
DRESULT res = RES_ERROR;
MSC_LUNTypeDef info;
if(USBH_MSC_Read(&hUsbHostFS, lun, sector, buff, count) == USBH_OK)
{
res = RES_OK;
}
else
{
USBH_MSC_GetLUNInfo(&hUsbHostFS, lun, &info);
switch(info.sense.asc)
{
case SCSI_ASC_LOGICAL_UNIT_NOT_READY:
case SCSI_ASC_MEDIUM_NOT_PRESENT:
case SCSI_ASC_NOT_READY_TO_READY_CHANGE:
USBH_ErrLog ("USB Disk is not ready!");
res = RES_NOTRDY;
break;
default:
res = RES_ERROR;
break;
}
}
return res;
}
Menulis data
Tulis fungsi sektor disk_write. Fungsi ini digunakan untuk menulis data ke disk. Fungsi ini ditulis sesuai dengan spesifikasi I/O disk dan akan dipanggil dalam berkas ff.c. Prototipe fungsinya adalah sebagai berikut:
- DRESULT disk_write(BYTE drv, const BYTE*buff, DWORD sector, BYTE count);
Berdasarkan definisi prototipe dan persyaratan perangkat penyimpanan massal USB kami, kita dapat mengimplementasikan fungsi penulisan data disk sebagai berikut:
/*Write sector function for USBH*/
static DRESULT USBH_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count)
{
DRESULT res = RES_ERROR;
MSC_LUNTypeDef info;
if(USBH_MSC_Write(&hUsbHostFS, lun, sector, (BYTE *)buff, count) == USBH_OK)
{
res = RES_OK;
}
else
{
USBH_MSC_GetLUNInfo(&hUsbHostFS, lun, &info);
switch(info.sense.asc)
{
case SCSI_ASC_WRITE_PROTECTED:
USBH_ErrLog("USB Disk is Write protected!");
res = RES_WRPRT;
break;
case SCSI_ASC_LOGICAL_UNIT_NOT_READY:
case SCSI_ASC_MEDIUM_NOT_PRESENT:
case SCSI_ASC_NOT_READY_TO_READY_CHANGE:
USBH_ErrLog("USB Disk is not ready!");
res = RES_NOTRDY;
break;
default:
res = RES_ERROR;
break;
}
}
return res;
}
Fungsi yang berkaitan dengan perangkat pengendali
Fungsi kontrol media penyimpanan disk_ioctl. Anda dapat menulis kode fungsional yang diperlukan dalam fungsi ini, seperti mendapatkan ukuran media penyimpanan, mendeteksi apakah media penyimpanan dalam keadaan menyala atau tidak, serta jumlah sektor media penyimpanan, dan sebagainya. Jika ini adalah aplikasi sederhana, tidak perlu menulis kode tersebut. Prototipe fungsinya adalah sebagai berikut:
- DRESULT disk_ioctl(BYTE drv, BYTE ctrl, VoiI*buff);
Berdasarkan definisi prototipe dan persyaratan perangkat penyimpanan massal USB kami, kita dapat mengimplementasikan fungsi-fungsi yang berkaitan dengan pengendalian perangkat disk sebagai berikut:
/*USBH IO control function */
static DRESULT USBH_ioctl(BYTE lun, BYTE cmd, void *buff)
{
DRESULT res = RES_ERROR;
MSC_LUNTypeDef info;
switch(cmd)
{
/* Make sure that no pending write process */
case CTRL_SYNC:
res = RES_OK;
break;
/* Get number of sectors on the disk (DWORD) */
case GET_SECTOR_COUNT :
if(USBH_MSC_GetLUNInfo(&hUsbHostFS, lun, &info) == USBH_OK)
{
*(DWORD*)buff = info.capacity.block_nbr;
res = RES_OK;
}
else
{
res = RES_ERROR;
}
break;
/* Get R/W sector size (WORD) */
case GET_SECTOR_SIZE :
if(USBH_MSC_GetLUNInfo(&hUsbHostFS, lun, &info) == USBH_OK)
{
*(DWORD*)buff = info.capacity.block_size;
res = RES_OK;
}
else
{
res = RES_ERROR;
}
break;
/* Get erase block size in unit of sector (DWORD) */
case GET_BLOCK_SIZE :
if(USBH_MSC_GetLUNInfo(&hUsbHostFS, lun, &info) == USBH_OK)
{
*(DWORD*)buff = info.capacity.block_size / USB_DEFAULT_BLOCK_SIZE;
res = RES_OK;
}
else
{
res = RES_ERROR;
}
break;
default:
res = RES_PARERR;
}
return res;
}
Lihat waktu saat ini
Fungsi jam waktu nyata get_fattime. Digunakan untuk mendapatkan waktu saat ini dan mengembalikan bilangan bulat tak bertanda 32-bit. Informasi waktu tersimpan dalam 32 bit tersebut. Jika Anda tidak menggunakan cap waktu, Anda dapat langsung mengembalikan sebuah angka, seperti 0. Prototipe fungsinya adalah sebagai berikut:
ˆ DWORD get_fattime(Void);
Berdasarkan definisi prototipe dan persyaratan perangkat penyimpanan massal USB kami, kita dapat mengimplementasikan fungsi pengambilan status disk sebagai berikut:
/*Read clock function*/
DWORD get_fattime(void)
{
return 0;
}
Selesaikan penulisan keenam program di atas, dan pekerjaan porting pada dasarnya telah selesai. Anda mungkin menyadari bahwa nama fungsi yang telah kita implementasikan tampak berbeda dari fungsi prototipe. Hal ini terutama dilakukan untuk memudahkan pengoperasian ketika terdapat beberapa perangkat penyimpanan secara bersamaan. Kita cukup memanggil fungsi yang telah kita implementasikan dalam fungsi target.
Pengujian Aplikasi
Kami telah menyelesaikan proses migrasi FatFS; sekarang mari kita periksa apakah migrasi tersebut telah berjalan dengan benar. Untuk itu, mari kita buat sebuah aplikasi yang dapat menulis data ke berkas di flash disk USB dan membaca data berkas tersebut.
/* USB HOST MSC operation function, this part of the function is set according to needs */
static void MSC_Application(void)
{
FRESULT res; /* FatFs function return value */
uint32_t byteswritten, bytesread; /* Number of files read and written */
uint8_t wtext[] = "This is STM32 working with FatFs!"; /* Write file buffer */
uint8_t wtext2[] = "This is an example of FatFs reading and writing!"; /* Write file buffer */
uint8_t wtext3[] = "This is a test of appending data to a file!"; /* Write file buffer */
uint8_t rtext[100]; /* Read file buffer */
/* Register the file system object to the FatFs module */
if(f_mount(&USBHFatFS, (TCHAR const*)USBHPath, 0) != FR_OK)
{
/* Error handling */
Error_Handler();
}
else
{
/* Open a file */
if(f_open(&USBHFile, "STM32.TXT", FA_OPEN_EXISTING | FA_WRITE) != FR_OK)
{
/* Error handling */
Error_Handler();
}
else
{
res=f_lseek(&USBHFile,f_size(&USBHFile)); //Point the pointer to the end of the file
//res=f_lseek(&USBHFile,100); //Point the pointer to the end of the file
/* Write data to file */
res = f_write(&USBHFile, wtext, sizeof(wtext), (void *)&byteswritten);
res = f_write(&USBHFile, "\r\n", sizeof("\r\n")-1, &byteswritten);
res = f_write(&USBHFile, wtext2, sizeof(wtext2), (void *)&byteswritten);
res = f_write(&USBHFile, "\r\n", sizeof("\r\n")-1, &byteswritten);
res = f_write(&USBHFile, wtext3, sizeof(wtext3), (void *)&byteswritten);
res = f_write(&USBHFile, "\r\n", sizeof("\r\n")-1, &byteswritten);
if((byteswritten == 0) || (res != FR_OK))
{
/* Error handling */
Error_Handler();
}
else
{
/* Close file */
f_close(&USBHFile);
/* Open file for reading */
if(f_open(&USBHFile, "STM32.TXT", FA_READ) != FR_OK)
{
/* Error handling */
Error_Handler();
}
else
{
/* Read data from file */
res = f_read(&USBHFile, rtext, sizeof(rtext), (void *)&bytesread);
if((bytesread == 0) || (res != FR_OK))
{
/* Error handling */
Error_Handler();
}
else
{
/* Close file */
f_close(&USBHFile);
/* Compare read and written data */
if((bytesread != byteswritten))
{
/* Error handling */
Error_Handler();
}
else
{
/* No errors */
}
}
}
}
}
}
FATFS_UnLinkDriver(USBHPath);
}
Berkas yang pertama kali kita buat di disk USB bernama "STM32.TXT". Dalam kode sumber di atas, setelah kita membuat berkas tersebut, kita memodifikasinya agar dapat dibuka dan ditutup. Tulis "Ini adalah STM32 yang bekerja dengan FatFs!" ke dalam berkas STM32.TXT yang telah dibuat, seperti yang ditunjukkan pada gambar di bawah ini:

Selanjutnya, kita akan mencoba menambahkan konten ke berkas yang sudah ada. Berkas tersebut masih berformat STM32.TXT, seperti yang ditunjukkan di bawah ini:

Saat ini, kami telah menyelesaikan proses migrasi dan pengujian sistem berkas FatFS. Berdasarkan hasil pengujian, proses migrasi tersebut berjalan dengan baik; setidaknya tidak ditemukan masalah pada aplikasi-aplikasi sederhana.
Kesimpulan
Dalam artikel ini, kami telah memporting sistem berkas FatFS ke STM32F4 dan melakukan uji baca-tulis sederhana. Berdasarkan hasil uji tersebut, tidak ada masalah dalam proses pemortingan FatFS.
Saat melakukan porting, kami mempertimbangkan kemudahan pengoperasian ketika beberapa drive tersedia secara bersamaan. Fungsi operasi I/O disk yang kami definisikan perlu diimplementasikan berdasarkan perangkat keras yang sebenarnya, dan kemudian fungsi I/O disk yang kami tulis dipanggil dalam fungsi callback yang ditentukan oleh sistem.




