Embedded Artists
We are the leading providers of products and services around prototyping, evaluation and OEM platforms using NXP's ARM-based microcontrollers.
You are viewing an older revision! See the latest version
LPC4088DM Using Filesystems
The display modules supports for USB Host, USB Device, uSD cards and have a 16 MB QSPI flash and 32 MB external SDRAM.
The DMSupport library handles the different memories and, using FATFileSystem with some modifications, allows FAT file systems to be mounted and used on all interfaces:
- MCIFileSystem handles access to uSD cards
- QSPIFileSystem handles access to a file system on the QSPI flash
- RAMFileSystem allows a chunk of RAM to be used as a file system
- USB Device MassStorage allows the LPC4088 Display Module to appear as a USB Memory Stick on a connected PC
- USB Host MassStorage handles access to a inserted USB Memory Stick
The mbed framework allows the different file systems to be mounted allowing access using the standard fopen/fclose/fread/fwrite calls without actually nowing what lies behind.
A short example showing from a user's perspective what is needed to copy one file from a USB memory stick to an uSD card:
void copy(const char* fDest, const char* fSrc) { FILE* fTo = NULL; FILE* fFrom = NULL; char buff[512]; int num; fTo = fopen(fDest, "w"); if (fTo != NULL) { fFrom = fopen(fSrc, "r"); if (fFrom != NULL) { num = fread(buff, 1, 512, fSrc); while(num > 0) { fwrite(buff, 1, num, fDest); num = fread(buff, 1, 512, fSrc); } } } if (fTo != NULL) { fclose(fTo); } if (fFrom != NULL) { fclose(fFrom); } } ... copy("/mci/output.txt", "/usb/input.txt");
Even if the file file operations are independent of the underlying hardware there is still some setup to be done. Read more about it in the sections below.
MCIFileSystem¶
The MCIFileSystem allows access to inserted uSD cards.
To use the file system it must first be enabled in the dm_board_config.h file:
#define DM_BOARD_USE_MCI_FS
The initialization of the file system is done in the init() function in DMBoard.
The file system will be mounted as /mci/
. There are no restrictions of use in combination with other file systems.
Warning
There is a limitation in the current implementation of the MCIFileSystem that causes unpredictable behavior when writing in chunks larger than 512 bytes. See code examples below on how to avoid this.
Wrong way:
void foo() { uint8_t* largeData = ... uint32_t size = 3000000; // open file // write all data with one call uint32_t written = fwrite(largeData, 1, size, f); // check return value and close file ... }
But for the MCIFileSystem that has to be broken down to:
void foo() { uint8_t* largeData = ... uint32_t size = 3000000; // open file // write all data in <=512 byte chunks uint32_t written = 0; uint32_t left = size; uint32_t chunk; while (left > 0) { chunk = ((left < 512) ? left : 512); uint32_t tmp = fwrite(largeData+written, 1, chunk, f); // check return value and handle errors ... written += tmp; left -= tmp; } // close file }
QSPIFileSystem¶
The QSPIFileSystem class is used to setup and use the upper part of the external QSPI flash as a FAT File System.
To use the file system it must first be enabled in the dm_board_config.h file:
#define DM_BOARD_USE_QSPI_FS
The initialization of the file system is done in the init() function in DMBoard.
The file system will be mounted as /qspi/
. There are no restrictions of use in combination with other file systems.
The file system can be formatted to a specific size in increments of 1MByte and will allways be placed at the top of the address range. For the 16 MByte memory on the LPC4088 Display Module this means:
0x28000000 0x29000000 |------|------|------|------|------|------|------|------| qspifs.format(1) | available for program | FS | |------|------|------|------|------|------|------|------| |------|------|------|------|------|------|------|------| qspifs.format(2) | available for program | FileSystem | |------|------|------|------|------|------|------|------| |------|------|------|------|------|------|------|------| qspifs.format(7) | PROG | FileSystem | |------|------|------|------|------|------|------|------| |------|------|------|------|------|------|------|------| qspifs.format(8) | FileSystem | |------|------|------|------|------|------|------|------|
The file system must be placed at the top of the memory because the linker scripts places your program at the bottom of the memory (if needed).
Limitations
The QSPIFileSystem has some limitations:
- The file system uses at least one block (same size as the erase block) per file. For the 16 MByte Macronix flash which has 4 KByte erase blocks this limits the number of files to ca 4000.
- The file system doesn't support empty directories. The reason for this is that directories are never stored on the file system - instead each file is stored with it's absolute path.
- The file system doesn't store date/time information and the attributes (hidden/system/read only).
Background
The QSPIFileSystem class is a subclass of FileSystemLike and not the FatFileSystem as a FAT file system has some disadvantages:
- A FAT file system stores it's FAT table (i.e. the table of content) near the start of the file system which for for the memory layout shown above would mean that the FAT table could be located in eight different locations depending on the size of the file system. The QSPIFileSystem stores it's table of content at the end of the file system, which means it is always at the top of the memory regardless of how large the file system is.
- When a FAT file system is formatted it will write zeroes in all the writable area. As described above in the SPIFI description this is not ideal as it requires an erase operation each time something should be stored. The QSPIFileSystem formats the writable area only when needed and always as 0xff, reducing the number of times a block must be erased.
- In a FAT file system the FAT table (i.e. the table of content) is located in a fixed place and it must be modified each time a file is added/modified/removed or its size is increased. This causes a lot of writing/erasing of the exact same sector(s) over and over again. The QSPIFileSystem has a small "pool" of table of contents to use which reduces the wear.
- In a FAT file system the content of a file might be spread out in multiple locations on the file system (i.e. it gets fragmented). The QSPIFileSystem always stores the entire file in a sequence. This means that if you know where a file is located it is possible to access the files content (read only) direcly in memory without copying.
A FAT file system has some features that the QSPIFileSystem hasn't:
- A FAT file system treats a folder as a file with special properties. Each folder is stored even if it is empty. The QSPIFileSystem handles this differently. A file is stored with its absolute name (i.e. including folders) but a folder is never stored in the file system. It is still possible to open a folder and to read it's files and sub folders as with a FAT file system. The big drawback to this is that the QSPIFileSystem cannot have empty folders. If a folder becomes empty it will disappear. If you absolutely need to have an empty folder (e.g. to show where the user should put his files) then you have to put a readme file or something else in it.
- A FAT file system supports additional properties for each file: access rights (read only, hidden, system), creation date and last modified date. None of those properties are available in the QSPIFileSystem. This is however no big issue as the FileSystemLike interface doesn't expose them anyway.
- A FAT file system can easily be exposed as a USB Memory Stick - QSPIFileSystem requires an a workaround to achieve it (see app_qspi_memstick below)
- The FAT file system can cause fragmentation as described above, but that is also one of it's strengths. If you first fill a 8 MByte file system with eight 1 MByte files and then remove every other one you will get a file system that has 4 MByte free, divided into four 1 MByte blocks. If you then attempt to create a 4 MByte file the FAT file system will split it into the four free blocks and is able to on comple the operation The QSPIFileSystem cannot handle this as each file must be stored in sequence and there is no 4 MByte block free so the operation will fail.
RAMFileSystem¶
The RAMFileSystem can be used to setup a temporary file system using a chunk of RAM. This is used by the USB Device MassStorage file system below.
There is no special configuration to do as the file system is not initialized by the init() function in DMBoard.
#include "mbed.h" #include "DMBoard.h" #include "RAMFileSystem.h" /* Size of RAM file system */ #define RAM_FS_SIZE (20*1024*1024) void someFunction() { DMBoard* board = &DMBoard::instance(); RtosLog* log = DMBoard::instance().logger(); // allocate a chunk of memory in the external SDRAM to use as a RAM file system void* fsmem = malloc(RAM_FS_SIZE); if (fsmem == NULL) { log->printf("Failed to allocate memory for RAM file system\n"); mbed_die(); } // create a file system based on the allocated memory RAMFileSystem ramfs((uint32_t)fsmem, RAM_FS_SIZE, "ram"); // add an empty file system on it ramfs.format(); // use the file system ... }
The file system will be mounted as /ram/
in the example above but any name can be used. There are no restrictions of use in combination with other file systems.
USB Device MassStorage¶
The USB Device MassStorage allows the LPC4088 Display Module to appear as a USB Memory Stick on a connected PC.
To use the file system it must first be enabled in the dm_board_config.h file:
#define DM_BOARD_USE_USB_DEVICE
The initialization of the file system is not done in the init() function in DMBoard. Instead the initialization has to be done by the user at startup. The USB Device stack does not provide any events upon changes to the USB bus (as is done in the USB Host stack) so the only way is to keep trying to connect until it works.
The file system that is exposed to the PC must have an actual file system behind it. In the example below we use a RAMFileSystem as a backend.
main.cpp
#include "mbed.h" #include "DMBoard.h" #include "USBMSD_RAMFS.h" /* Size of RAM file system exposed to PC as USB MassStorage */ #define RAM_FS_SIZE (20*1024*1024) void someFunction() { DMBoard* board = &DMBoard::instance(); RtosLog* log = DMBoard::instance().logger(); // allocate a chunk of memory in the external SDRAM to use as a RAM file system void* fsmem = malloc(RAM_FS_SIZE); if (fsmem == NULL) { log->printf("Failed to allocate memory for RAM file system\n"); mbed_die(); } // create a file system based on the allocated memory RAMFileSystem ramfs((uint32_t)fsmem, RAM_FS_SIZE, "ram"); USBMSD_RAMFS usbmsd(&ramfs); // add an empty file system on it ramfs.format(); // now we wait for the user to connect the board to the PC while(true) { if (usbmsd.connect()) { log->printf("Connected to PC\n"); // the file system is available as /usb/ // wait for it to go away while (usbmsd.connected()) { // only check once per second Thread::wait(1000); } // no longer connected log->printf("Disconnected from PC\n"); } // only try once per second Thread::wait(1000); } ... }
The file system will be mounted as /usb/
.
Note
The USB Device and USB Host interfaces cannot be used at the same time.
USB Host MassStorage¶
The USB Host MassStorage handles access to a inserted USB Memory Stick.
To use the file system it must first be enabled in the dm_board_config.h file:
#define DM_BOARD_USE_USB_HOST
stead the initialization has to be done by the user at startup. The USB Memory Stick might be inserted before booting, sometime after booting or never at all. To handle this it is recommended to create a separate thread dedicated to handing the USB events. This is the suggested way:
main.cpp
#include "mbed.h" #include "DMBoard.h" #include "USBHostMSD.h" // This identifier can be any 2^x number but must be same on sender and receiver side #define USB_CONNECTION_EVENT (1<<4) void usbTask(void const* args) { RtosLog* log = DMBoard::instance().logger(); bool msdConnected = false; // create the needed USB classes USBHostMSD* msd = new USBHostMSD("usb"); // register for events when devices are added/removed USBHost* host = USBHost::getHostInst(); host->signalOnConnections(Thread::gettid(), USB_CONNECTION_EVENT); while (true) { // wait for connect/disconnect message from USBHost Thread::signal_wait(USB_CONNECTION_EVENT); // something happened on the USB bus, see if it affects USB Mass Storage if (msd->connected()) { if (!msdConnected) { msdConnected = true; log->printf("USB MassStorage Device - Connected\n"); } } else { if (msdConnected) { msdConnected = false; log->printf("USB MassStorage Device - Ejected\n"); } if (msd->connect()) { msdConnected = true; haveUSBMSD = true; log->printf("USB MassStorage Device - Connected\n"); } } } } void main(void) { // Initialize DMBoard as usual... // Start other threads... // Start the USB thread Thread tUSBHandler(usbTask, NULL, osPriorityNormal, 8192); // Do other stuff ... }
The file system will be mounted as /usb/
.
Note
The USB Device and USB Host interfaces cannot be used at the same time.
Performance¶
The performance of the MCI, USB Host MassStorage and QSPI file systems has been measured and is available here.