How RPi_Pico_WAV_Player Handles exFAT SD Cards: FatFs Implementation Guide
The RPi_Pico_WAV_Player uses the FatFs library (v0.90) to mount and detect exFAT volumes, automatically identifying the file system type during initialization and implementing a specific workaround for the FatFs parent directory navigation bug on exFAT cards.
The RPi_Pico_WAV_Player is an open-source audio player for the Raspberry Pi Pico that relies on the FatFs library to manage SD card file systems. Understanding how it handles exFAT-formatted cards requires examining the initialization sequence in the file menu driver and the UI layer's navigation workarounds.
FatFs Library Integration and SD Card Mounting
The player initializes SD card access through the file_menu_init() function in lib/file_menu/file_menu_FatFs.c. This function configures the SPI interface for the micro-SD slot and attempts to mount the volume using FatFs's f_mount() function.
The initialization sequence performs the following steps:
- Configures the SPI hardware with
pico_fatfs_spi_config_t, setting the SPI instance, clock speed (40 MHz), and pin assignments. - Attempts to mount the volume with
f_mount(&fs, "", 1), retrying up to five times if the initial attempt fails. - Detects the file system type by reading
fs.fs_typeafter successful mounting.
// lib/file_menu/file_menu_FatFs.c – mount and detect FS type
FRESULT file_menu_init(uint8_t* fs_type) {
pico_fatfs_spi_config_t config = {
spi0, CLK_SLOW_DEFAULT, 40 * MHZ,
PIN_SPI0_MISO_DEFAULT, PIN_SPI0_CS_DEFAULT,
PIN_SPI0_SCK_DEFAULT, PIN_SPI0_MOSI_DEFAULT,
true // use internal pull-up
};
pico_fatfs_set_config(&config);
for (int i = 0; i < 5; i++) {
fr = f_mount(&fs, "", 1); // <- mount attempt
if (fr == FR_OK) {
*fs_type = fs.fs_type; // <-- detected FS (FAT12/16/32/EXFAT)
break;
}
pico_fatfs_reboot_spi(); // retry on failure
}
return fr;
}
Detecting exFAT File System Type
After successful mounting, the player determines whether the card uses exFAT, FAT32, FAT16, or FAT12 by examining the fs_type field of the FatFs object. The UI layer in src/UIMode.cpp maps these numeric constants to human-readable strings for display on the LCD console.
The mapping array defined in the UI layer translates the detected type:
const char* fs_type_str[5] = {"NOT_MOUNTED","FAT12","FAT16","FAT32","EXFAT"};
printf("SD Card File System = %s\r\n", fs_type_str[vars->fs_type]);
This detection occurs in UIOpeningMode::entry, which calls file_menu_init and stores the returned fs_type in the global state variables.
The exFAT Parent Directory Bug Workaround
FatFs version 0.90 contains a known bug when navigating the parent directory ("..") on exFAT volumes. The RPi_Pico_WAV_Player implements a specific workaround in src/UIMode.cpp to handle this edge case.
When the user attempts to navigate up a directory level and vars->fs_type == FS_EXFAT, the UI layer executes a directory stack rebuilding routine:
- Temporarily stores the current navigation stack in a temporary stack.
- Closes the current directory with
file_menu_close_dir(). - Re-opens the root directory with
file_menu_open_dir("/"). - Replays the stored directory stack to rebuild the path, excluding the final directory (the one being exited).
if (vars->fs_type == FS_EXFAT) { // <-- EXFAT detection
// Work-around for FatFs bug with ".." on exFAT
// Re-open the root and replay the directory-stack
std::stack<stack_data_t> temp_stack;
while (dir_stack.size() > 0) {
item = dir_stack.top();
dir_stack.pop();
temp_stack.push(item);
}
file_menu_close_dir();
file_menu_open_dir("/"); // root
while (temp_stack.size() > 1) { // rebuild intermediate dirs
item = temp_stack.top(); temp_stack.pop();
file_menu_sort_entry(item.head+item.column, item.head+item.column+1);
file_menu_ch_dir(item.head+item.column);
dir_stack.push(item);
}
item = temp_stack.top(); temp_stack.pop();
dir_stack.push(item);
}
This workaround ensures reliable navigation on exFAT-formatted cards without requiring modifications to the underlying FatFs library.
Practical Implementation Examples
Mounting and Detecting the File System Type
When implementing SD card initialization in your own projects based on this player, use the file_menu_init function to handle both mounting and file system detection:
uint8_t fs_type;
FRESULT res = file_menu_init(&fs_type);
if (res != FR_OK) {
lcd->setMsg("No SD Card Found!", true);
return;
}
const char* names[] = {"NOT_MOUNTED","FAT12","FAT16","FAT32","EXFAT"};
printf("Mounted SD card, FS = %s\n", names[fs_type]);
Opening Directories on exFAT Volumes
After successful mounting, open the root directory to begin navigation. The FatFs abstraction layer handles exFAT transparently for most operations:
if (file_menu_open_dir("/") != FR_OK) {
lcd->setMsg("SD Card Read Error!", true);
return;
}
uint16_t total = file_menu_get_num(); // includes ".." entry
printf("Entries in root = %u\n", total);
Handling Directory Navigation with exFAT Awareness
When changing directories, the UI layer automatically applies the exFAT workaround when necessary. Application code should use the standard file_menu_ch_dir interface:
// Assume the user selected entry 'entryIdx'
if (file_menu_is_dir(entryIdx)) {
// The internal EXFAT workaround is automatically invoked
// when the UI later asks to navigate up (..).
file_menu_ch_dir(entryIdx);
}
Summary
- The RPi_Pico_WAV_Player uses FatFs v0.90 to handle SD card access, supporting FAT12, FAT16, FAT32, and exFAT file systems.
- File system detection occurs during mounting via
file_menu_init()inlib/file_menu/file_menu_FatFs.c, which reads thefs.fs_typefield and returns it to the UI layer. - exFAT volumes require special handling for parent directory navigation due to a known FatFs bug with the
".."entry. - The workaround in
src/UIMode.cpprebuilds the directory stack by closing the current directory, reopening the root, and replaying the navigation history whenvars->fs_type == FS_EXFAT.
Frequently Asked Questions
Does RPi_Pico_WAV_Player support exFAT formatted SD cards?
Yes, the player fully supports exFAT through the FatFs library. When an SD card is mounted, the file_menu_init() function detects the file system type and stores it in vars->fs_type. The UI displays "EXFAT" when this value equals FS_EXFAT, confirming successful recognition of exFAT volumes.
What is the FatFs exFAT parent directory bug?
FatFs version 0.90 contains a bug where navigating to the parent directory using the ".." entry fails on exFAT volumes. This occurs because the library incorrectly handles directory entry lookups for the parent link in exFAT's directory structure. The RPi_Pico_WAV_Player works around this by completely rebuilding the directory path from root rather than relying on the standard parent directory entry.
How does the player detect which file system is mounted?
Detection happens in lib/file_menu/file_menu_FatFs.c immediately after the f_mount() call succeeds. The function reads the fs.fs_type member of the FatFs object, which contains a numeric constant representing FAT12, FAT16, FAT32, or EXFAT. This value is copied to the caller via the fs_type pointer parameter and later used by the UI in src/UIMode.cpp to display the file system name and apply exFAT-specific logic.
Where is the exFAT workaround implemented in the source code?
The workaround resides in src/UIMode.cpp within the directory navigation logic. When the user attempts to move up a directory level and vars->fs_type == FS_EXFAT, the code executes a stack rebuilding routine. It temporarily stores the current directory path, closes the open directory, reopens the root directory with file_menu_open_dir("/"), and then replays the stored navigation stack to reconstruct the path without using the problematic ".." entry.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →