Skip to main content

FatFs bare-metal porting to STM32F4xx

In this post I will describe how I adapted the FatFs library to the SDIO (SD Card) port of a STM32F4xx family microcontroller. The code is bare-metal programming, directly writing to the registers, with no usage of external libraries.

What is FatFs library

It is a library developed by ChaN, implementing the FAT formatting over virtually any memory mass device. It is useful for microcontrollers or any other usage where no operating system, taking care of formatting, is available. The official website for FatFs is the following:

Browsing in the Internet you can find many porting on different platforms; I thought to wrote this project, hoping it will be useful for somebody, because I couldn't find any bare-metal implementation for STM32F4 microcontrollers.

How to make FatFs port for your needs

FatFs is totally independent on the disk I/O layer; what the programmer is required to do, when integrating it on his/her project, is to write a few functions implementing disk initialize, disk read and disk write.

Where is the source code?

You can download the source code here: fatfs-stm32f4-v1.tgz

If you prefer, you can clone the github repository:

It is based on FatFs v13. The value added, with respect to bare v13, is in the file source/diskio_stm32f4xx.c: it contains all is necessary for the porting.

Along with the FatFs library (in source/), you will find the kim/ subdirectory: together with the main.c, it contains the code for a simple demo, showing the library running properly.

How is my code configured for the HW

The implementation here described is hard-coded to work on official ST evaluation boards: STM32F411E-DISCO and DM-STF4BB ("Discovery more") board.

Here is a picture of the hardware I used:


It is quite simple to modify the code for your needs.

The disk_initialize function (in source/diskio_stm32f4xx.c) sets the proper GPIO settings for the SDIO pins:

/* SDIO_CD: input gpio, card detect */
gpio_func(IO(PORTB, 15), 0);
gpio_dir(IO(PORTB, 15), 0);
gpio_mode(IO(PORTB, 15), PULL_NO);

/* SDIO_D0 */
gpio_func(IO(PORTC, 8), 12);
gpio_mode(IO(PORTC, 8), PULL_NO);

/* SDIO_D1 */
gpio_func(IO(PORTC, 9), 12);
gpio_mode(IO(PORTC, 9), PULL_NO);

/* SDIO_D2 */
gpio_func(IO(PORTC, 10), 12);
gpio_mode(IO(PORTC, 10), PULL_NO);

/* SDIO_D3 */
gpio_func(IO(PORTC, 11), 12);
gpio_mode(IO(PORTC, 11), PULL_NO);

/* SDIO_CK */
gpio_func(IO(PORTC, 12), 12);
gpio_mode(IO(PORTC, 12), PULL_NO);

/* SDIO_CMD */
gpio_func(IO(PORTD, 2), 12);
gpio_mode(IO(PORTD, 2), PULL_NO);

In the file kim/init.c you can find the UART settings for log purpose:

int putchar(int c)
        if (c == '\n')
        wr32(R_USART2_DR, c);
        while (!(rd32(R_USART2_SR) & BIT6));
        return c;

void init_uart(void)
        /* USART2 on PD5/PD6 */
        gpio_func(IO(PORTD, 5), 7);
        gpio_func(IO(PORTD, 6), 7);
        gpio_mode(IO(PORTD, 5), PULL_NO);
        gpio_mode(IO(PORTD, 6), PULL_NO);
        /* fPCLK=42MHz, br=115.2KBps, USARTDIV=22.8125, see table 80 pag. 519 */
        wr32(R_USART2_BRR, (22 << 4) | 13);
        or32(R_USART2_CR1, BIT13 | BIT5 | BIT3 | BIT2);
        or32(R_NVIC_ISER(1), BIT6); /* USART2 is irq 38 */

How to compile the library

If you have a gcc toolchain compiled for ARM and a Unix system, you can easily run make, and it will compile both the library and a sample demo binary:

colosimo@asus:~/fatfs-stm32f4$ make
arm-none-eabi-gcc -c -mthumb -Wall -Werror -Os -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -ggdb -Isource -Ikim/include -o source/ff.o source/ff.c
arm-none-eabi-gcc -c -mthumb -Wall -Werror -Os -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -ggdb -Isource -Ikim/include -o source/ffunicode.o source/ffunicode.c
arm-none-eabi-gcc -c -mthumb -Wall -Werror -Os -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -ggdb -Isource -Ikim/include -o source/ffsystem.o source/ffsystem.c
arm-none-eabi-gcc -c -mthumb -Wall -Werror -Os -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -ggdb -Isource -Ikim/include -o source/diskio_stm32f4xx.o source/diskio_stm32f4xx.c
arm-none-eabi-ld source/ff.o source/ffunicode.o source/ffsystem.o source/diskio_stm32f4xx.o -r -o ff-stm32f4-bigobj.o
arm-none-eabi-ar rs ff_stm32f4.a ff-stm32f4-bigobj.o
arm-none-eabi-ar: creating ff_stm32f4.a
arm-none-eabi-gcc -c -mthumb -Wall -Werror -Os -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -ggdb -Isource -Ikim/include -o kim/init.o kim/init.c
arm-none-eabi-gcc -c -mthumb -Wall -Werror -Os -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -ggdb -Isource -Ikim/include -o kim/kprint.o kim/kprint.c
arm-none-eabi-gcc -c -mthumb -Wall -Werror -Os -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -ggdb -Isource -Ikim/include -o main.o main.c
arm-none-eabi-ld kim/init.o kim/kprint.o main.o ff_stm32f4.a -Tkim/stm32f4xx.ld -o ff_demo.elf
arm-none-eabi-objcopy -O binary ff_demo.elf ff_demo.bin

It will produce:

  • the library, in two different formats: archive, (ff_stm32f4.a) or "big object" (ff-stm32f4-bigobj.o), meaning a full object file you can link to your objects;
  • the demo application, in elf and binary format: ff_demo.elf and ff_demo.bin.

Running the ff_demo.bin sample application

Switch on or reset the board, with a micro SD Card inserted: it will create a demo file in the SD Card root, named "ff_demo.txt", with a dummy content, then will open it again and dump its content. Something like this:

[LOG] Hello from ff_demo
[LOG] card type: SD2
[LOG] card OCR: 0080ffc0
[LOG] I will now create file ff_demo.txt with a dummy content
[LOG] ff_demo.txt size: 41 bytes
[LOG] I will now try to open the file ff_demo.txt and dump its content here
[LOG] ----ff_demo.txt dump begin ----
This is a test file, created by ff_demo
[LOG] ----ff_demo.txt dump end   ----

Some tips about how to use the library

You can:

  • use the ff_demo as the base for your project;
  • compile and link the library to your project, by using the included ff.h as header file;
  • take the code and readapt it for your needs. License is MIT-style, so you can do whatever you want with the code, as long as you retain the notice about Author and Copyright.

Current Limitations and next steps

The current implementation does not make use of DMA: the transfers are done by the CPU. This limits the data rate, as the maximum clock speed was empirically found at 4MHz. A future improvement will involve the usage of DMA, for maximum performances.

The KIM ("Keep It Minimal") project

Here is the official repository for KIM, the place I publish my FOSS code targeted for MCUs, easily readaptable for many needs (like the ff_demo above):


If you have any suggestion and/or want to contribute, please contact me.