Vai al testo principale

Gestione SD Card con libreria FatFs su processore STM32F4

In questo post descriverò come sono riuscito ad adattare la libreria FatFs alla porta SDIO (SD Card) del microcontrollore STM32F411. Il codice è programmazione "bare-metal", scrive direttamente nei registri senza usare alcuna libreria esterna.

Cos'è la libreria FatFs

E' una libreria sviluppata da ChaN, e implementa la formattazione FAT, idealmente su qualsiasi dispositivo di archiviazione di massa. E' utile per i microcontrollori o per ogni altro situazione in cui non c'è un sistema operativo disponibile che si occupi della formattazione. Il sito ufficiale di FatFs è il seguente:

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

In internet si possono trovare molti adattamenti (porting) su differenti piattaforme hardware e software; ho pensato di scrivere questo progetto, sperando che sia di utilità per qualcuno, perché non sono riuscito a trovare nessuna implementazione bare metal per STM32F4.

Come adattare FatFs al proprio sistema

FatFs è del tutto indipendente dal layer di scrittura I/O su disco o memoria flash; quello che il programmatore deve fare, quando vuole integrare la libreria nel proprio progetto, è scrivere alcune funzioni che implementano l'inizializzazione, la lettura e la scrittura su disco

Dove si trovano i sorgenti?

Il codice è scaricabile gratuitamente da questo link: fatfs-stm32f4-v1.tgz

In alternativa, è possibile clonare il repository github: https://github.com/colosimo/fatfs-stm32

E' basato su FatFs v13. Il valore aggiunto, rispetto alla semplice v13, è nel file source/diskio_stm32f4xx.c: contiene tutto il necessario per il porting.

Oltre alla libreria FatFs (nella directory source), si può trovare la directory chiamata kim: insieme al file main.c, contiene il codice per una semplice demo, che dimostra il corretto funzionamento della libreria.

Come configurare il codice per l'Hardware

L'implementazione qui descritta ha delle parti di codice cablate per lavorare sulle schede di valutazione ufficiali della ST: STM32F411E-DISCO e DM-STF4BB (anche chiamata "Discovery more").

Ecco un'immagine dell'hardware che ho usato. La scheda STM32F411E-DISCO è montata sopra la DM-STF4BB:

/images/stm32f411e-disco.png

E' abbastanza semplice modificare il codice per le proprie esigenze.

La funzione disk_initialize (in source/diskio_stm32f4xx.c) inizializza la configurazione GPIO per i pin della SDIO:

/* 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);

Nel file kim/init.c si trovano invece i settaggi per la UART, usata per scopi di logging:

int putchar(int c)
{
        if (c == '\n')
                putchar('\r');
        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 */
}

Come compilare la libreria

Avendo a disposizione una toolchain gcc per ARM su un sistema Unix, è sufficiente lanciare make; verranno compilate sia la libreria sia il binario della demo di esempio:

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

Troverete quindi:

  • la libreria, in due formati differenti: archivio (ff_stm32f4.a) o "big object" (ff-stm32f4-bigobj.o), vale a dire un file oggetto che contiene tutto il necessario e che può essere linkato agli altri file oggetto di un applicazione;
  • L'applicazione demo, in formato elf e binario: ff_demo.elf and ff_demo.bin.

Far girare l'applicazione di esempio ff_demo.bin

Dopo aver flashato il binario sulla board, occorre accenderla con una microSD inserita: la demo creerà un file di esempio nella root della SD, chiamato "ff_demo.txt", con un contenuto dummy, e in seguito lo aprirà di nuovo e ne visualizzerà il contenuto. Il log sarà più o meno questo:

[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   ----

Qualche consiglio su come usare la libreria

E' possibile:

  • usare ff_demo come base per il proprio progetto;
  • compilare e linkare la libreria al proprio progetto, usando il file header ff.h incluso nei sorgenti;
  • prendere il codice e riadattarlo alle proprie esigenze. La licenza è MIT-style, quindi si può fare con il codice più o meno ciò che si vuole, con l'unico vincolo di mantenere le note riguardo autore e copyright.

Limitazioni attuali e prossimi passi

L'implementazione attuale non fa uso del DMA: i trasferimenti da card a memoria sono fatti dalla CPU. Questo limita il data rate, dato che la massima velocità di clock supportata è stata verificata (empiricamente) 4MHz. Una possibile futura miglioria comporterà l'utilizzo del DMA, per prestazioni ottimali.

Il progetto KIM ("Keep It Minimal")

Qui si trova il repository ufficiale di KIM, il posto in cui pubblico il mio codice FOSS ("Free and Open Source Software") per microcontrollori. Il codice è pensato per essere facilmente riadattabile in diversi contesti (come la ff_demo descritta sopra):

https://github.com/colosimo/kim-os

Contatti

Se hai qualche suggerimento e/o vuoi contribuire con qualche patch, per favore contattami.