Port of ChaN's FAT SD/MMC SPI to libopencm3 STM32F103

ChaN has provided a package for using the Microsoft FAT filesystem, FatFs, in embedded microcontroller applications. This is a generic package intended for use with various storage media including disk drives and memory cards or USB sticks. The software relies on a lower level driver library being provided to access the specific storage using the microcontroller hardware. A number of driver packages have been provided by ChaN and others.

In 2010 Martin Thomas released driver code for the FatFs filesystem, extending it to SD/MMC cards using the SPI interface with hardware drivers for ARM Cortex-M3 STM32F103.

This work adapts Martin's code to make use of libopencm3 rather than the STM library. This makes FatFs usable in libopencm3 based projects and may ease its adaptation to other ARM Cortex-M processors. In particular other STM32F series microcontrollers may be used with only minor configuration changes. In addition to this, Martin's code has been updated to the latest FatFs filesystem package release (R0.09b patched). There were some minor API changes which seemed to be only changes in variable names. Any future releases of FatFs should hopefully be simply a drop-in replacement, provided the API doesn't change any more.

The changes are minor, with a number of redundant files removed and others shifted around. The main modifications are to replace the disk driver file sd_spi_stm32.c with sd_spi_loc3_stm32.c. Some changes were also made to rtc.c which access the RTC counter. The Makefile is intended only as a template and should be replaced by one suited to the environment in which it will be used. Two other files sd_spi_stm32_freertos.c, sd_spi_stm32_almost_reentrant.c add FreeRTOS calls for timing and attempt (so far unsuccessfully) to make the driver code re-entrant.

Usage

The driver code uses a board.h header file to define the hardware ports and peripherals to be used by the drivers. There are some issues to be dealt with when setting up this file. The SPI interface is used for the SPM32F103 to access the storage media. In the header file provided there are several boards defined, some using SPI1 and others SPI2. Note carefully that in the STM32F series the SPI2 peripheral is clocked differently to the SPI1 peripheral and so the RCC setup is different, in particular the base clock divider for SPI2 will be different. This setup may impact on other peripherals as it is a global setting for all peripherals (there is more than one clock distribution network in the devices).

A second point to note is that the driver code supplies a function called disk_timerproc() which is not used by the FatFs code. This is used to update timer variables for providing delays and other timing within the driver code. This must be called in a timer ISR every 10ms. The ISR may be provided by any timer; preferably by systick (which is common to much of the ARM family).

Before writing code, test the hardware with Martin's ff_test_term using the USART1 for communications (the readme.txt packaged with this code includes a short tutorial sequence). The only commands needed to get started are fi to initialize the filesystem, and fs to print out the file system details.

Reentrant Code

The original driver code was NOT reentrant. This means that tasks in an RTOS must only write sequentially to one file at a time (typically by using queues and a dedicated file access task). FatFs is only reentrant when accessing different volumes but can be made thread safe when accessing the same volume. The driver code can also be made thread safe when accessing the same volume, but not when accessing different volumes. There are two critical global variables that are common for a single volume:

"Stat" holds the card status as required by FatFs. This is set in  disk_initialise() and is updated in the disk_timerproc() ISR function to check for a card inserted or removed.

"CardType" holds information about the card formatting that was tediously computed in disk_initialise() and would not reasonably be recomputed each time a read/write operation occurred.

There is a third global variable, the decrement timers used to provide timing functions. In order to move towards re-entrant code, these decrement timers  were changed to increment a counter on each tick in systick and implement a timeout function to allow this counter to be monitored. The global counter is not updated by the code, only by systick. The timeout function also incorporates calls to RTOS delay functions to allow a calling task to relinquish control for a time rather than waste time in loops.

FreeRTOS Support

This is added into the driver code in the file sd_spi_stm32_freertos.c where it uses the task delay functions to insert delays that relinquish control to other tasks. In addition the task synchronization support functions required by FatFs when reentrant code is used, are provided in freertos.c.

Debug

As an aid to debugging, the following gives some notes on how the code accesses the hardware drivers. These notes only go as far as I needed to go to get things working.

The first call from the user program is to f_mount to initialize a working space for each particular volume (disk drive/card or partition). For the code provided there is only one volume, given as 0. Any other will result in an error. The working space is simply a block of memory set aside for configuration data. This function does not access any hardware drivers.

A call to f_getfree will return the amount of free space on the card and doesn't access any file structure information. This calls the following:

  • chk_mounted is called by most interface functions to test if the card has been "mounted" and to mount it if not. Mounting reads the volume information from the card and sets the parameters in the work space. The variable fs_type indicates the filesystem format type and is zero if the card is not mounted. Initially the card is not mounted so a call is made to:
    • disk_initialize to set up the SPI interface and the ports used by the MMIC card.
    • disk_ioctl to get the sector size.
    • disk_read via check_fs which reads the first sector to get partitioning and format details.
This will have then filled the working space substantially with information about the card and is enough to get most of the hardware drivers working.
First created 30 August 2013
Last Modified 1 October 2013
Ken Sarkies 2013