Using SDXC Cards with exFAT on Raspberry Pi Pico (SPI IF)

Saturday, March 13, 2021

Raspberry Pi Pico

t f B! P L

I tested accessing exFAT-formatted microSDXC cards with the Raspberry Pi Pico.

Raspberry Pi Pico microSD access test scene 

FatFs Module

Since I wanted to support not only FAT and FAT32 but also exFAT, I did not use the sample in pico-examples but instead used theFatFs Module. FatFs can be freely used, modified, and distributed at one's own risk regardless of purpose, and has abundant implementation examples, making it invaluable for DIY microcontroller users. Since I had already verified exFAT operation with the FatFs module on the Sipeed Longan Nano, I modified from that implementation. The version used wasR0.14a_p2.

Wiring Diagram / Pin Configuration

The wiring diagram is as follows.
A 10K ohm pull-up resistor on the DAT0 line is required; without it, the card will not be recognized properly.
Wiring Diagram

The connection table is also provided below.

Pico
Pin #
Pico Pin
Name
FunctionmicroSD
Pin #
microSD
Pin Name
4GP2SPI0_SCK5CLK
5GP3SPI0_TX3CMD
6GP4SPI0_RX7DAT0
7GP5SPI0_CSn2CD/DAT3
8GNDGND6VSS
363V3(OUT)3.3V4VDD

Notes

The wiring between Raspberry Pi Pico and the microSD card slot is very sensitive, and stable operation could not be achieved with a typical breadboard microSD card module and pin wiring (especially with SDXC cards). I added functions to adjust the output pad drive strength and slew rate, which exist in the registers but are not in the Raspberry Pi Pico GPIO library, and tried various adjustments including frequency changes and inserting damping resistors, but saw little improvement. Ultimately, I confirmed stable operation by creating a direct-insertion connection module as shown in the photo below. There appears to be room for future improvement regarding SPI access timing margin.
microSD card direct-insertion module

Verified Cards

Below is information about the cards used for verification. microSD cards use FAT, microSDHC cards use FAT32, and microSDXC cards use exFAT. None of my cards failed to be recognized; all errors were due to the access timing issues mentioned above.
  • PQI microSD 1GB
  • Memorex microSD 2GB
  • Transcend microSDHC 32GB (C4)
  • Toshiba microSDXC 64GB (UHS-I C10)
  • SanDisk microSDXC Ultra A1 64GB (UHS-I C10/U1)
  • SanDisk microSDXC Ultra A1 512GB (UHS-I C10/U1)
  • SanDisk microSDXC Ultra A2 1TB (UHS-I U3/V30)

pico_fatfs_test Project

https://github.com/elehobica/pico_fatfs_test I have published the project. The features are as follows.
  • Starts when any key is pressed in the serial terminal
  • Mounts and recognizes the card. Displays the file system type (FAT, FAT32, exFAT) and capacity
  • Measures and displays Write/Read access speeds (two runs each)
The access speed measurement method was based on theTeensy 4.0 bench project. A bench.dat file will be overwritten in the card's root folder, so please back up the card used for testing.
Serial terminal output example


Below is a brief explanation of the code.

ffconf.h

FatFs functional configuration is performed inffconf.h. For this benchmark, I used a relatively broad configuration, but when using it in actual projects, you may want to consider the following parameters.

Current settings and considerations for other projects
  • FF_FS_READONLY 0: Can be set to 1 for read-only use
  • FF_FS_MINIMIZE 0: Can be set to 1, 2, etc. depending on the range of APIs used
  • FF_USE_EXPAND 1: Can be 0 if PreAllocation is not used
  • FF_USE_LFN 1: Can be 0 if Long File Name is not used
  • FF_FS_RPATH 1: Can be 0 if relative paths are not used or managed externally
  • FF_FS_EXFAT 1: Can be 0 if exFAT is not used
  • FF_FS_NORTC 1: Set to 0 and implement required functions to reflect timestamps via RTC

tf_card.c / tf_card.h

The device-dependent part of the FatFs module is described entirely intf_card.c / tf_card.h.
  • SPI0 pin assignments are defined in tf_card.h
  • SPI card initialization clock is set to 100KHz, high-speed clock to 50MHz (adjust as needed)
  • SPI access mode is 8-bit, CPOL = 0, CPHA = 0, MSB First

For reference, the SPI access implementation is excerpted below. Single-byte bidirectional access, multi-byte read access, and multi-byte write access are implemented using pico-sdk hardware/spi.h functions.
/* Exchange a byte */
static
BYTE xchg_spi (
	BYTE dat	/* Data to send */
)
{
	uint8_t *buff = (uint8_t *) &dat;
	spi_write_read_blocking(spi0, buff, buff, 1);
	return (BYTE) *buff;
}


/* Receive multiple byte */
static
void rcvr_spi_multi (
	BYTE *buff,		/* Pointer to data buffer */
	UINT btr		/* Number of bytes to receive (even number) */
)
{
	uint8_t *b = (uint8_t *) buff;
	spi_read_blocking(spi0, 0xff, b, btr);
}

...

/* Transmit multiple byte */
static
void xmit_spi_multi (
	const BYTE *buff,		/* Pointer to data buffer */
	UINT btx		/* Number of bytes to transmit (even number) */
)
{
	const uint8_t *b = (const uint8_t *) buff;
	spi_write_blocking(spi0, b, btx);
}

hardware_gpio_ex

Although not used in this project, functions for setting GPIO output pad drive strength, slew rate, and input pad Schmitt trigger were implemented in thehardware_gpio_ex directory below.
By adding the following to the project's CMakeLists.txt
add_subdirectory(hardware_gpio_ex)

// ... (other settings)

target_link_libraries(${bin_name} PRIVATE
    pico_stdlib
    // ... (other libraries)
    hardware_gpio_ex
)
and including the following in the source file,
#include "hardware/gpio_ex.h"
the following three functions become available. (See commented-out sections in tf_card.c for usage examples)
void gpio_set_drive_strength(uint gpio, uint value);
void gpio_set_schmitt(uint gpio, uint value);
void gpio_set_slew_rate(uint gpio, uint value);

Access Speed Measurement Results

The access speed figures are rather unimpressive. Of course, many applications work fine at this level, so whether to use it as-is or tune for higher performance would be decided on a case-by-case basis.
  • PQI microSD 1GB

    Type is FAT16
    Card size:    1.02 GB (GB = 1E9 bytes)
    
    FILE_SIZE_MB = 5
    BUF_SIZE = 512 bytes
    Starting write test, please wait.
    
    write speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    72.8148, 188153, 3175, 7028
    72.7216, 188240, 3177, 7037
    
    Starting read test, please wait.
    
    read speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    740.6934, 1591, 665, 690
    740.8031, 1589, 665, 690
        
  • Memorex microSD 2GB

    Type is FAT16
    Card size:    2.00 GB (GB = 1E9 bytes)
    
    FILE_SIZE_MB = 5
    BUF_SIZE = 512 bytes
    Starting write test, please wait.
    
    write speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    79.2543, 222555, 2452, 6456
    78.1408, 229029, 2457, 6549
    
    Starting read test, please wait.
    
    read speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    757.2978, 2044, 533, 675
    757.4125, 2039, 533, 675
        
  • Transcend microSDHC 32GB (C4)

    Type is FAT32
    Card size:   31.90 GB (GB = 1E9 bytes)
    
    FILE_SIZE_MB = 5
    BUF_SIZE = 512 bytes
    Starting write test, please wait.
    
    write speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    273.3709, 209797, 947, 1871
    287.3379, 208416, 968, 1780
    
    Starting read test, please wait.
    
    read speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    851.7343, 1192, 539, 600
    851.8794, 1189, 539, 600
        
  • Toshiba microSDXC 64GB (UHS-I C10)

    Type is EXFAT
    Card size:   61.89 GB (GB = 1E9 bytes)
    
    FILE_SIZE_MB = 5
    BUF_SIZE = 512 bytes
    Starting write test, please wait.
    
    write speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    149.3869, 180241, 2065, 3424
    155.4869, 173010, 2069, 3291
    
    Starting read test, please wait.
    
    read speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    620.7698, 960, 621, 824
    620.4617, 959, 621, 824
        
  • SanDisk microSDXC Ultra A1 64GB (UHS-I C10/U1)

    Type is EXFAT
    Card size:   63.83 GB (GB = 1E9 bytes)
    
    FILE_SIZE_MB = 5
    BUF_SIZE = 512 bytes
    Starting write test, please wait.
    
    write speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    464.5679, 19560, 881, 1101
    471.6234, 19881, 761, 1084
    
    Starting read test, please wait.
    
    read speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    1238.1575, 422, 394, 412
    1238.1575, 422, 399, 412
        
  • SanDisk microSDXC Ultra A1 512GB (UHS-I C10/U1)

    Type is EXFAT
    Card size:  511.80 GB (GB = 1E9 bytes)
    
    FILE_SIZE_MB = 5
    BUF_SIZE = 512 bytes
    Starting write test, please wait.
    
    write speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    430.8212, 24998, 951, 1187
    428.5318, 24974, 953, 1193
    
    Starting read test, please wait.
    
    read speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    1195.5237, 443, 413, 427
    1195.5237, 444, 415, 427
        
  • SanDisk microSDXC Ultra A2 1TB (UHS-I U3/V30)

    Type is EXFAT
    Card size: 1023.74 GB (GB = 1E9 bytes)
    
    FILE_SIZE_MB = 5
    BUF_SIZE = 512 bytes
    Starting write test, please wait.
    
    write speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    426.9217, 6290, 963, 1198
    427.0676, 32321, 958, 1197
    
    Starting read test, please wait.
    
    read speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    1192.9563, 445, 409, 428
    1192.9563, 445, 413, 428
        

About Me

My photo
Electronics, programming & audio

Featured Post

Synchronizing Radio-Controlled Clocks with Raspberry Pi Pico W (JJY Standard Radio Wave Emulator)

As a Raspberry Pi Pico W application, I built a JJY emulator for radio-controlled clocks (for time synchronization) with minimal peripheral...

QooQ