File System on Internal Flash Memory
Let's turn the Pico board's internal flash memory into a file system. This page explains how to build a LittleFS or FAT file system on the Pico board's flash memory.
About Flash Memory
The Pico board's flash memory has the following address ranges:
- Pico: 0x1000'0000 - 0x1020'0000 (2MB)
- Pico2: 0x1000'0000 - 0x1040'0000 (4MB)
The program is written from the head (0x1000'0000), so the remaining flash memory can be used as a file system. To check the range occupied by the program, use one of the following methods:
- Check the map file
-
In the
builddirectory of your project, a file likeyour-project.elf.mapis generated. It should contains a symbol named.flash_endlike the following:The value of this symbol is the end address of the flash memory occupied by the program.
- Use picotool
-
picotool is a command-line tool included in the Pico SDK that can display information about the built ELF file. On Windows, you can find the executable file in
C:\Users\username\.pico-sdk\picotool\x.x.x\picotool. Run the following command from the terminal:and you'll get output like:
File .\build\your-project.elf: Program Information name: your-project version: 0.1 features: UART stdin / stdout binary start: 0x10000000 binary end: 0x10005770 target chip: RP2350 image type: ARM Securebinary endis the end address of the flash memory occupied by the program. - Use pico-jxglib's shell
-
If the pico-jxglib shell is running on the Pico board, you can use the
about-mecommand to get information about the running program. Connect to the Pico board's shell and run the following command:
You can decide the starting address of the file system by adding margin to the end address of the program. For example, if the program occupies up to 0x10005770, you can set the file system to start from 0x10008000. It depends on your expectation on how much your program will grow in the future.
Creating a Project
From the VSCode command palette, run >Raspberry Pi Pico: New Pico Project and create a project with the following settings. For details on creating a Pico SDK project, building, and writing to the board, see "Getting Started with Pico SDK".
- Name ... Enter the project name. In this example, enter
fs-flash. - Board type ... Select the board type.
- Location ... Select the parent directory where the project directory will be created.
- Stdio support ... Select the port (UART or USB) to connect Stdio.
- Code generation options ... Check
Generate C++ code
Assume the project directory and pico-jxglib are arranged as follows:
From here, edit CMakeLists.txt and the source file based on this project to create your program.
Creation of LittleFS Drive
LittleFs is a lightweight file system designed for embedded applications, suitable for flash memory with limited write cycles. pico-jxglib provides a class LFS::Flash to implement LittleFS on the Pico board's flash memory.
To include LittleFS in your project, add the following lines to the end of CMakeLists.txt:
target_link_libraries(fs-flash jxglib_LFS_Flash)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../pico-jxglib pico-jxglib)
Edit the source file as follows:
#include <stdio.h>
#include "pico/stdlib.h"
#include "jxglib/LFS/Flash.h"
using namespace jxglib;
int main()
{
::stdio_init_all();
LFS::Flash drive("LFS:", 0x0004'0000); // 256kB
// any job
}
LFS::Flash instances are generated on the Pico board's flash memory, allowing you to treat it as a LittleFS file system. The constructor details are as follows:
LFS::Flash(const char* driveName, uint32_t bytesXIP)drivename: A string name for the drive, can contain any charactersbytesXIP: The number of bytes to reserve from the end of the flash memory for the LittleFS file system
Note
LFS::Flash instances are generated, but the file system is not created. You must run FS::Format() to format the drive.
Creation of FAT Drive
FAT is a widely used generic file system, common on SD cards and USB storage. pico-jxglib provides a class FAT::Flash to implement FAT on the Pico board's flash memory.
To include FAT in your project, add the following lines to the end of CMakeLists.txt:
target_link_libraries(fs-flash jxglib_FAT_Flash)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../pico-jxglib pico-jxglib)
jxglib_configure_FAT(fs-flash FF_VOLUMES 1)
FatFs library configuration file ffconf.h is generated by running jxglib_configure_FAT(). FF_VOLUMES is the number of FAT file systems (pico-jxglib settings are the same as the number of drives). Here, we specify 1 to ensure one FAT drive.
Edit the source file as follows:
#include <stdio.h>
#include "pico/stdlib.h"
#include "jxglib/FAT/Flash.h"
using namespace jxglib;
int main()
{
::stdio_init_all();
FAT::Flash drive("FAT:", 0x0004'0000); // 256kB
// any job
}
FAT::Flash instances are generated on the Pico board's flash memory, allowing you to treat it as a FAT file system. The constructor details are as follows:
FAT::Flash(const char* driveName, uint32_t bytesXIP)drivename: A string name for the drive, can contain any charactersbytesXIP: The number of bytes to reserve from the end of the flash memory for the FAT file system
Note
FAT::Flash instances are generated, but the file system is not created. You must run FS::Format() to format the drive.
Creation of Multiple Drives
You can also implement multiple drives. For example, to have 2 LittleFS drives and 2 FAT drives on the Pico board, add the following lines to CMakeLists.txt:
target_link_libraries(fs-flash jxglib_LFS_Flash jxglib_FAT_Flash)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../pico-jxglib pico-jxglib)
jxglib_configure_FAT(fs-flash FF_VOLUMES 2)
jxglib_configure_FAT()'s argument FF_VOLUMES is 2, ensuring 2 FAT drives.
Edit the source file as follows:
#include <stdio.h>
#include "pico/stdlib.h"
#include "jxglib/LFS/Flash.h"
#include "jxglib/FAT/Flash.h"
using namespace jxglib;
int main()
{
::stdio_init_all();
LFS::Flash driveLFS1("LFS1:", 0x1010'0000, 0x0004'0000); // 256kB
LFS::Flash driveLFS2("LFS2:", 0x1014'0000, 0x0004'0000); // 256kB
FAT::Flash driveFAT1("FAT1:", 0x1018'0000, 0x0004'0000); // 256kB
FAT::Flash driveFAT2("FAT2:", 0x101c'0000, 0x0004'0000); // 256kB
// any job
}
Different address ranges are specified to create 4 file systems on the Pico board's flash memory. The constructor details are as follows:
LFS::Flash(const char* driveName, uint32_t addrXIP, uint32_t bytesXIP)FAT::Flash(const char* driveName, uint32_t addrXIP, uint32_t bytesXIP)driveName: A string name for the drive, can contain any charactersaddrXIP: The starting address of the flash memory for the drivebytesXIP: The size of the flash memory for the drive
File System API
File Writing
An example of writing a file to a LittleFS file system:
CMakeLists.txt's end should include:
target_link_libraries(fs-flash jxglib_LFS_Flash)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../pico-jxglib pico-jxglib)
Edit the source file as follows:
#include <stdio.h>
#include "pico/stdlib.h"
#include "jxglib/LFS/Flash.h"
using namespace jxglib;
int main()
{
::stdio_init_all();
LFS::Flash drive("Drive:", 0x0004'0000); // 256kB
if (!FS::Mount("Drive:") && !FS::Format(Stdio::Instance, "Drive:")) return 1;
FS::File* pFile = FS::OpenFile("Drive:/test.txt", "w");
if (pFile) {
for (int i = 0; i < 10; ++i) {
pFile->Printf("Line %d\n", i + 1);
}
pFile->Close();
delete pFile;
}
}
LFS::Flash drive("Drive", 0x0004'0000); // 256kBCreates a LittleFS file system drive namedDrivewith 256kB sizeif (!FS::Mount("Drive:") && !FS::Format(Stdio::Instance, "Drive:")) return 1;Checks if the drive is mounted. If not, formats the driveFile* pFile = FS::OpenFile("Drive:/test.txt", "w");Opens a file for writingpFile->Printf("Line %d\n", i + 1);Writes a line to the filepFile->Close();Closes the filedelete pFile;Deletes the file pointer
The same operations are possible with FAT file systems.
File Reading
An example of reading a file from a LittleFS file system:
CMakeLists.txt's end should include:
target_link_libraries(fs-flash jxglib_LFS_Flash)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../pico-jxglib pico-jxglib)
Edit the source file as follows:
#include <stdio.h>
#include "pico/stdlib.h"
#include "jxglib/LFS/Flash.h"
using namespace jxglib;
int main()
{
::stdio_init_all();
LFS::Flash drive("Drive:", 0x0004'0000); // 256kB
FS::File* pFile = FS::OpenFile("Drive:/test.txt", "r");
if (pFile) {
char line[256];
while (pFile->ReadLine(line, sizeof(line)) > 0) {
::printf("%s\n", line);
}
pFile->Close();
delete pFile;
}
}
File* pFile = FS::OpenFile("Drive:/test.txt", "r");Opens a file for readingpFile->ReadLine(line, sizeof(line))Reads a line from the file
Directory Information
An example of getting directory information from a LittleFS file system:
CMakeLists.txt's end should include:
target_link_libraries(fs-flash jxglib_LFS_Flash)
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../pico-jxglib pico-jxglib)
Edit the source file as follows:
#include <stdio.h>
#include "pico/stdlib.h"
#include "jxglib/LFS/Flash.h"
using namespace jxglib;
int main()
{
::stdio_init_all();
LFS::Flash drive("Drive:", 0x0004'0000); // 256kB
FS::Dir* pDir = FS::OpenDir("Drive:/");
if (pDir) {
FS::FileInfo* pFileInfo;
while (pFileInfo = pDir->Read()) {
::printf("%-16s%d\n", pFileInfo->GetName(), pFileInfo->GetSize());
delete pFileInfo;
}
pDir->Close();
delete pDir;
}
}
FS::Dir* pDir = FS::OpenDir("Drive:/");Opens a directorypFileInfo = pDir->Read()Reads file information into an FS::FileInfo instancepDir->Close();Closes the directory
API Reference
FS::CopyFile
Copies a file
FS::Move
Renames or moves a file or directory (directory moving is not supported)
FS::RemoveFile
Deletes a file
FS::RemoveDir
Deletes a directory
FS::CreateDir
Creates a directory
FS::ChangeCurDir
Changes the current directory
FS::Format
Formats the file system
FS::Mount
Mounts the file system
FS::Unmount
Unmounts the file system