A FAT filesystem with SPI driver for SD card on Raspberry Pi Pico



Simple library for SD Cards on the Pico

At the heart of this library is ChaN's FatFs - Generic FAT Filesystem Module. It also contains a Serial Peripheral Interface (SPI) SD Card block driver for the Raspberry Pi Pico, derived from SDBlockDevice from Mbed OS 5. It is wrapped up in a complete runnable project, with a little command line interface, some self tests, and an example data logging application.



  • Supports multiple SD Cards
  • Supports multiple SPIs
  • Supports multiple SD Cards per SPI
  • Supports Real Time Clock for maintaining file and directory time stamps
  • Supports Cyclic Redundancy Check (CRC)
  • Plus all the neat features provided by FatFS


Using a Debug build: Writing and reading a file of 0xC0000000 (3,221,225,472) random bytes (3 GiB) on a SanDisk 32GB card with SPI baud rate 12,500,000:

  • Writing
    • Elapsed seconds 4113.8
    • Transfer rate 764.7 KiB/s
  • Reading (and verifying)
    • Elapsed seconds 3396.9
    • Transfer rate 926.1 KiB/s

On a SanDisk Class 4 16 GB card, I have been able to push the SPI baud rate as far as 20,833,333 which increases the transfer speed proportionately (but SDIO would be faster!).




SPI0 GPIO Pin SPI MicroSD 0 Description
MOSI TX 16 21 DI DI Master Out, Slave In
CS0 CSn 17 22 SS or CS CS Slave (or Chip) Select
SCK SCK 18 24 SCLK CLK SPI clock
MISO RX 19 25 DO DO Master In, Slave Out
CD 22 29 CD Card Detect
GND 18,23 GND Ground
3v3 36 3v3 3.3 volt power


  • The wiring is so simple that I didn't bother with a schematic. I just referred to the table above, wiring point-to-point from the Pin column on the Pico to the MicroSD 0 column on the Transflash.
  • Card Detect is optional. Some SD card sockets have no provision for it. Even if it is provided by the hardware, you might not care about it if you intend to leave the card in all the time, so you can skip it and save an I/O pin.
  • You can choose to use either or both of the Pico's SPIs.
  • Wires should be kept short and direct. SPI operates at HF radio frequencies.

Pull Up Resistors

  • The SPI MISO (DO on SD card, SPIx RX on Pico) is open collector (or tristate). It should be pulled up. The Pico internal gpio_pull_up is weak: around 56uA or 60kΩ. It's best to add an external pull up resistor of around 5kΩ to 3.3v. You might get away without one if you only run one SD card and don't push the SPI baud rate too high.
  • The SPI Slave Select (SS), or Chip Select (CS) line enables one SPI slave of possibly multiple slaves on the bus. This is what enables the tristate buffer for Data Out (DO), among other things. It's best to pull CS up so that it doesn't float before the Pico GPIO is initialized. It is imperative to pull it up for any devices on the bus that aren't initialized. For example, if you have two SD cards on one bus but the firmware is aware of only one card (see hw_config.c); you can't let the CS float on the unused one.


  • Follow instructions in Getting started with Raspberry Pi Pico to set up the development environment.
  • Install source code: git clone --recurse-submodules [email protected]:carlk3/no-OS-FatFS-SD-SPI-RPi-Pico.git no-OS-FatFS
  • Customize:
    • Tailor sd_driver/hw_config.c to match hardware
    • Customize ff14a/source/ffconf.h as desired
    • Customize pico_enable_stdio_uart and pico_enable_stdio_usb in CMakeLists.txt as you prefer
  • Build:
   cd no-OS-FatFS
   mkdir build
   cd build
   cmake ..
  • Program the device


  • Connect a terminal. PuTTY or tio work OK. For example:
    • tio -m ODELBS /dev/ttyACM0
  • Press Enter to start the CLI. You should see a prompt like:
  • The help command describes the available commands:
setrtc <DD> <MM> <YY> <hh> <mm> <ss>:
  Set Real Time Clock
  Parameters: new date (DD MM YY) new time in 24-hour format (hh mm ss)
	e.g.:setrtc 16 3 21 0 4 0

 Print current date and time

lliot <drive#>:
 !DESTRUCTIVE! Low Level I/O Driver Test
	e.g.: lliot 1

format [<drive#:>]:
  Creates an FAT/exFAT volume on the logical drive.
	e.g.: format 0:

mount [<drive#:>]:
  Register the work area of the volume
	e.g.: mount 0:

unmount <drive#:>:
  Unregister the work area of the volume

chdrive <drive#:>:
  Changes the current directory of the logical drive.
  <path> Specifies the directory to be set as current directory.
	e.g.: chdrive 1:

getfree [<drive#:>]:
  Print the free space on drive

cd <path>:
  Changes the current directory of the logical drive.
  <path> Specifies the directory to be set as current directory.
	e.g.: cd 1:/dir1

mkdir <path>:
  Make a new directory.
  <path> Specifies the name of the directory to be created.
	e.g.: mkdir /dir1

  List directory

cat <filename>:
  Type file contents

  Run simple FS tests

big_file_test <pathname> <size in bytes> <seed>:
 Writes random data to file <pathname>.
 <size in bytes> must be multiple of 512.
	e.g.: big_file_test bf 1048576 1
	or: big_file_test big3G-3 0xC0000000 3

  Create Disk and Example Files
  Expects card to be already formatted and mounted

  Start Data Log Demo

  Stop Data Log Demo



  • The first thing to try is lowering the SPI baud rate (see hw_config.c). This will also make it easier to use things like logic analyzers.
  • Try another brand of SD card. Some handle the SPI protocol better than others. (Most consumer devices like cameras or PCs use the SDIO interface.) I have had good luck with SanDisk.
  • Tracing: Most of the source files have a couple of lines near the top of the file like:
#define TRACE_PRINTF(fmt, args...) // Disable tracing
//#define TRACE_PRINTF printf // Trace with printf

You can swap the commenting to enable tracing of what's happening in that file.

Next Steps

There is a example data logging application in data_log_demo.c. It can be launched from the CLI with the start_logger command. (Stop it with the stop_logger command.) It records the temperature as reported by the RP2040 internal Temperature Sensor once per second in files named something like /data/2021-03-21/11.csv. Use this as a starting point for your own data logging application!

If you want to use FatFs_SPI as a library embedded in another project, use something like:

git submodule add [email protected]:carlk3/no-OS-FatFS-SD-SPI-RPi-Pico.git


git submodule add https://github.com/carlk3/no-OS-FatFS-SD-SPI-RPi-Pico.git

You will need to pick up the library in CMakeLists.txt:

add_subdirectory(no-OS-FatFS-SD-SPI-RPi-Pico/FatFs_SPI build)
target_link_libraries(_my_app_ FatFs_SPI)

and #include "ff.h".

Happy hacking! image

Appendix: Adding Additional Cards

When you're dealing with information storage, it's always nice to have redundancy. There are many possible combinations of SPIs and SD cards. One of these is putting multiple SD cards on the same SPI bus, at a cost of one (or two) additional Pico I/O pins (depending on whether or you care about Card Detect). I will illustrate that example here.

To add a second SD card on the same SPI, connect it in parallel, except that it will need a unique GPIO for the Card Select/Slave Select (CSn) and another for Card Detect (CD) (optional).

Name SPI0 GPIO Pin SPI MicroSD 0 MicroSD 1
CD1 14 19 CD
CS1 15 20 SS or CS CS
CS0 17 22 SS or CS CS CS
CD0 22 29 CD
GND 18, 23 GND GND
3v3 36 3v3 3v3


As you can see from the table above, the only new signals are CD1 and CS1. Otherwise, the new card is wired in parallel with the first card.


  • sd_driver/hw_config.c must be edited to add a new instance to static sd_card_t sd_cards[]
  • Edit ff14a/source/ffconf.h. In particular, FF_VOLUMES:
#define FF_VOLUMES		2
  • Question about using the library

    Question about using the library

    I think I might be doing something wrong, as the example program does not seem to respond to serial commands, and if I try to use the library in my own program, the pico will hang on f_open.

    opened by thaumatichthys 14
  • Cannot mount sd card

    Cannot mount sd card

    Hello, I am trying to use this library. But I cannot mount sd card. For example, I uploaded the FatFS_SPI_example.cpp example then I send mount 0: command. After that I receive that error:

    > mount 0:
    sd_spi_go_low_frequency: Actual frequency: 398089
    V2-Version Card
    R3/R7: 0x1aa
    Illegal command CMD:59 response 0x5
    R3/R7: 0x80ff8000
    R3/R7: 0x80ff8000
    Card Initialized: Standard Capacity Card: Version 2.x
    SD card initialized
    _read_bytes: Invalid CRC received 0xc904 result of computation 0x8657
    Couldn't read csd response from disk
    f_mount error: The physical drive cannot work (3)

    I am using this sd module: image

    I set my card as Fat32

    and I changed sd_cards section in hw_config.c to that

    static sd_card_t sd_cards[] = {  // One for each SD card
            .pcName = "0:",           // Name used to mount device
            .spi = &spis[0],          // Pointer to the SPI driving this card
            .ss_gpio = 17,            // The SPI slave select GPIO for this SD card
            .use_card_detect = false,
            .card_detect_gpio = 22,   // Card detect
            .card_detected_true = -1,  // What the GPIO read returns when a card is
                                      // present. Use -1 if there is no card detect.
            .m_Status = STA_NOINIT,

    What am I doing wrong?

    opened by oguzkaganyaglioglu 4
  • Not really an issue but there seems to be no better way to communicate with author

    Not really an issue but there seems to be no better way to communicate with author

    You might want to change the README.md and have people do the https clone instead of the ssh clone (because I think you may be the only one with ssh access to your project) git clone --recurse-submodules https://github.com/carlk3/no-OS-FatFS-SD-SPI-RPi-Pico.git no-OS-FatFS

    Sorry to bother you with this not really issue thing ... but as a radio operator I wanted to add a little note. Obviously keeping the leads as short as one can is a good thing but just a note on the limits where one should start to worry about it: HF band is 3 to 30 MHz and at those sort of frequencies 1/4 wavelength is in the range of 2.5m ro 25m. It is somewhat less then optimal but I think you would get away with 1/40 wavelength leads ... which in the worst case would be 25cm. It turns out that the max SCK output frequency the RP2040 can support is 66MHz (already in the VHF band) ... I somehow doubt one will ever go that high unless you want the pico just to do SPI I/O and not much more (let alone on a breadboard) but in that case I think you might still get away with 11cm leads. Now let's suppose that it is more likely that you will be working with a SCK frequency of 11MHz (wich is still the best the PR2040 can do in input) 1/400 wavelength would be about 7cm so just don't get too worried about the leads being too long.

    opened by louigi600 4
  • re-initialize SD card after hotplug

    re-initialize SD card after hotplug

    I am sorry i was new of FATFS and pi pico,

    May i know is any method to re-initialize the SD card after hotplug.

    I try different way but it still show as below:

    No response CMD:17
    No response CMD:17
    No response CMD:17
    No response CMD:17 response: 0xff
    f_mount error: A hard error occurred in the low level disk I/O layer (1)

    the code i used is in the example but remove panic, because i want my pico still running after unable mount SD card

        static FATFS fatfs;
        FRESULT fr = f_mount(&fatfs, "", 1); 
        if (FR_OK != fr)
            printf("f_mount error: %s (%d)\n", FRESULT_str(fr), fr);
        FIL fil;
        const char * const filename = "filename.txt";
        fr = f_open(&fil, filename, FA_OPEN_APPEND | FA_WRITE);
        if (FR_OK != fr && FR_EXIST != fr)
            printf("f_open(%s) error: %s (%d)\n", filename, FRESULT_str(fr), fr);
        if (f_printf(&fil, "Hello, world!\n") < 0) {
            printf("f_printf failed\n");
        fr = f_close(&fil);
        if (FR_OK != fr) {
            printf("f_close error: %s (%d)\n", FRESULT_str(fr), fr);

    I try to

    FATFS fatfs;

    But still unable re-initialize SD card. May i know anything i was missing?

    Many Thanks ;)

    opened by wingyukyuk 4
  • Help compiling library for GCC (C++)

    Help compiling library for GCC (C++)

    The library compiles fine for .c files, but when switching the main to .cpp and updating the Cmake configuration 8 errors pop up from the sd_driver folder, all saying expected ',' or '...' before 'this'. These errors appear in the files sd_card.h between lines 64 to 70 and in 'spi.h` between lines 46 to 49.

    I'm not sure how to solve this issue (I tried to extern "C", but that didn't do anything).

    Any help would be appreciated. Here's both the log and error files if they can be of any help. CMakeError.log CMakeOutput.log

    opened by MisterSandman7 3
  • Configurable GPIO pins

    Configurable GPIO pins

    Firstly, thanks for publishing this. I am using it on my mini-project (https://github.com/fruit-bat/pico-sorcerer-2) and it works really well.

    I would love to be able to configure the GPIO pins without having to modify the source code... my sub-module on github won't have the changes needed for other people to download and compile.

    Thanks in advance,


    opened by fruit-bat 2
  • Does not work when using with scanvideo from pico-extras

    Does not work when using with scanvideo from pico-extras

    This is the pico-extras repository : https://github.com/raspberrypi/pico-extras When using this project together with scanvideo_dpi, the spi initialization hangs when requesting DMA channels.

    Specifically these two lines:

        pSPI->tx_dma = dma_claim_unused_channel(true);
        pSPI->rx_dma = dma_claim_unused_channel(true);

    These function calls never return and the Pico does not do anything else.

    Any idea as to where this might be coming from ? AFAIK the scanvideo code uses a single DMA channel. The bug occurs as soon as I include scanvideo, I don't even need to call any code from it.

    If it helps, scanvideo would be called on the second core.

    opened by jfoucher 2
  • DMA IRQs should be handled in RAM

    DMA IRQs should be handled in RAM

    I have some issues with the PicoDVI library; if the IRQ handlers execute from flash memory rather than RAM. The 2 routines in question are defined in spi.h :

    1. void spi_irq_handler(spi_t *pSPI);
    2. bool spi_transfer(spi_t *pSPI, const uint8_t *tx, uint8_t *rx, size_t length);

    Please could they be pinned to RAM:

    1. void __not_in_flash_func(spi_irq_handler)(spi_t *pSPI);
    2. bool __not_in_flash_func(spi_transfer)(spi_t *pSPI, const uint8_t *tx, uint8_t *rx, size_t length);

    I have included the git diff below if it is clearer.

    (I can fork and pull request if you prefer)

    Many thanks in advance.

    diff --git a/FatFs_SPI/sd_driver/spi.h b/FatFs_SPI/sd_driver/spi.h index fc13226..749506c 100644 --- a/FatFs_SPI/sd_driver/spi.h +++ b/FatFs_SPI/sd_driver/spi.h @@ -47,9 +47,9 @@ extern "C" { #endif

     // SPI DMA interrupts
    • void spi_irq_handler(spi_t *pSPI);
    • void __not_in_flash_func(spi_irq_handler)(spi_t *pSPI);^M
    • bool spi_transfer(spi_t *pSPI, const uint8_t *tx, uint8_t *rx, size_t length);
    • bool __not_in_flash_func(spi_transfer)(spi_t *pSPI, const uint8_t *tx, uint8_t *rx, size_t length);^M bool my_spi_init(spi_t *pSPI);

    #ifdef __cplusplus

    opened by fruit-bat 1
  • Disable GP25 LED

    Disable GP25 LED

    The code in spi.h allows for the use of the Pico LED (on GP25) to be configured, but it is hard coded to flash. A simple change #ifndef NO_PICO_LED
    #define USE_LED 1
    Allows the LED to be disabled via CMake for a project that uses no-OS-FatFS-SD-SPI-RPi-Pico as a submodule, whilst maintaining backwards compatibility.

    I can submit a pull request if you'd like?

    opened by ikjordan 1
  • No License Specified

    No License Specified

    Nice work on the library Carl.

    It doesn't look like you have specified a license for your modifications/additions. Did you intend for your work to be Apache 2.0, public domain or something else?

    opened by JayesonLS 1
  • Hardware Hookup in README.md file

    Hardware Hookup in README.md file

    In the first table in README.md listing the hardware connections, the numbers in the GPIO column for pins 25 and 21 appear to be reversed. MOSI on pin 25 is GPIO 19 and MISO on pin 21 is GPIO 16.

    My SD card adaptor has no CD pin. I saw the comments in hw_config.c, but you might want to mention it in the README.md file in case it confuses people trying to hook up the hardware who can't find that pin.

    The code is working fine for me and is just what I needed for a Raspberry Pi Pico project that needs to save data to an SD card.

    opened by jefftranter 1
  • CMD:13


    I see strange behaviours on certain SD cards that II don't know how to diagnose. Frequently

    No response CMD:13

    then everything appears to work fine and files are written correctly.

    But on a 64GB Integral SD card I see this repeated message sequence :

    R2: 0x33ff Card is Locked
    WP Erase Skip, Lock/Unlock Cmd Failed
    CC Error
    Card ECC Failed
    WP Violation
    Erase Param
    Out of Range, CSD_Overwrite
    In Idle State
    Erase Reset
    Erase Sequence Error
    Address Error

    but files seem to be written successfully.

    opened by philatkin 10
  • Fails to compile with SDK 1.2.0 and 1.3.0

    Fails to compile with SDK 1.2.0 and 1.3.0

    Just a note to document this problem and a workaround:

    The project fails to compile with pico-sdk 1.2.0. "mutex_is_initialized" is mispelled in pico/mutex.h as "mutex_is_initialzed".

    The 1.3.0 sdk fixed this error, but there is a bigger regression which prevents compilation. That is fixed in the develop-branch for the next release.

    I decided to stick with 1.2.0 and fix the spelling in pico/mutex.h. After that, I could successfully build the examples.

    opened by bablokb 0
Carl J Kugler III
Carl J Kugler III
A laser cut Dreamcast Pop'n Music controller and integrated memory card using the Raspberry Pi Pico's Programmable IO

Dreamcast Pop'n Music Controller Using Raspbery Pi Pico (RP2040) Intro This is a homebrew controller for playing the Pop'n Music games on the Sega Dre

null 29 Apr 6, 2022
Tetris on a Raspberry Pi Pico mounted on a Pimoroni Pico Explorer

PicoTetris Classic Tetris game running on a Raspberry Pi Pico microcontroller. Pico C port by Richard Birkby Original JavaScript implementation - Jake

Richard Birkby 30 Mar 21, 2022
Breakout game for Raspberry Pi Pico with Pimoroni Pico Display pack

breakout_rpi_pico Breakout game for Raspberry Pi Pico with Pimoroni Pico Display pack Prebuilt binary (breakout.uf2) is here. To build your own binary

null 18 Jan 17, 2022
Pico-uart-bridge - Raspberry Pi Pico UART-USB bridge

Raspberry Pi Pico USB-UART Bridge This program bridges the Raspberry Pi Pico HW UARTs to two independent USB CDC serial devices in order to behave lik

Álvaro Fernández Rojas 86 May 8, 2022
Digital rain animation gif with glow squeezed into a raspberry pi pico and pimoroni pico-display

pico-display-matrix Digital rain animation gif with glow squeezed into a raspberry pi pico and pimoroni pico-display or how to actually use all Flash

null 28 Apr 3, 2022
Prueba del Raspberry PI PICO con un display Raspberry PI TFT 3.5"

Raspberry-PI-PICO-display-RPI35 Prueba del Raspberry PI PICO con un display Raspberry PI TFT 3.5" Con ayuda de la libreria https://github.com/khoih-pr

null 1 Nov 10, 2021
MFAT is a minimal I/O library for FAT (File Allocation Table) volumes.

MFAT MFAT is a minimal I/O library for FAT (File Allocation Table) volumes. The library has been designed for embedded systems, and its small code and

null 3 Jan 30, 2022
WinChipHead CH341 linux driver for I2C / SPI and GPIO mode

WinChipHead (沁恒) CH341 linux driver for I2C / SPI and GPIO mode The CH341 is declined in several flavors, and may support one or more of UART, SPI, I2

Frank Zago 8 Mar 4, 2022
Memory FRAM MB85RS16 SPI Driver Library

mb85rs16 package 中文页 | English Introduction The mb85rs16 software package is a software package developed by XiaojieFan for the SPI FRAM MB85RS16. Usi

null 1 Feb 10, 2022
An unofficial Realtek PCIe-based card reader driver for macOS

Realtek PCIe Card Reader Driver for macOS Unleash the full potential of your SDXC UHS-I cards Introduction An unofficial macOS kernel extension for Re

FireWolf 123 May 7, 2022
An unofficial Realtek PCIe/USB-based SD card reader driver for macOS

Realtek Card Reader Driver for macOS Unleash the full potential of your SDXC UHS-I cards Introduction An unofficial macOS kernel extension for Realtek

FireWolf 123 May 7, 2022
Loads a signed kernel driver which allows you to map any driver to kernel mode without any traces of the signed / mapped driver.

CosMapper Loads a signed kernel driver (signed with leaked cert) which allows you to map any driver to kernel mode without any traces of the signed /

null 94 May 4, 2022
Raspberry Pi Pico (RP2040) and Micro-ROS (ROS 2) Integration

The Pico is an amazing microcontroller and I couldn't wait for ROS 2 support or Arduino Core, so here is my approach. Once the Arduino Core for RP2040 is out it will be easier to use micro_ros_arduino.

Darko Lukić 17 Aug 3, 2021
built-in CMSIS-DAP debugger tailored especially for the RP2040 “Raspberry Pi Pico”

RP2040 has two ARM Cortex-M0+ cores, and the second core normally remains dormant. pico-debug runs on one core in a RP2040 and provides a USB CMSIS-DAP interface to debug the other core. No hardware is added; it is as if there were a virtual debug pod built-in.

null 169 May 8, 2022
🦠 µnix is a UNIX-like operating system for the raspberry pi pico.

The µnix Operating System "µnix", "munix" or, "micro unix" aims to be a micro kernel based operating system targeting the Raspberry Pi Pico. "µnix" is

Sleepy Monax 56 Apr 29, 2022
Fractal rendering for Raspberry Pi Pico microcontroller

picofract Mandelbrot Set rendering demo for Raspberry Pi Pico microcontroller with Pico Display Pack. Building If you already have the Pimoroni SDK bu

null 20 Apr 29, 2022
Arduino API for the Raspberry Pico

Raspberry PI Pico - Arduino API On Friday I was receiving my Raspberry PI Pico and I had the opportunity to play around with it. Actually most of the

Phil Schatzmann 47 Apr 27, 2022
x86 emulator on Raspberry Pi Pico

picox86 x86 emulator on Raspberry Pi Pico https://user-images.githubusercontent.com/10139098/110543817-13299080-812b-11eb-9c88-674cdae919fc.mp4 PCB fr

null 34 Apr 7, 2022
Web Server based on the Raspberry Pico using an ESP8266 with AT firmware for WiFi

PicoWebServer This program runs on a Raspberry Pico RP2040 to provide a web server when connected to an Espressif ESP8266. This allows the Pico to be

null 42 May 15, 2022