The purpose of this application is to allow easy manipulation of the QSPI file system from a PC (**NOTE**: Application doesn't work with update to mbed OS 5 since USB Device support is currently not available for LPC4088)
Dependencies: DMBasicGUI DMSupport
Note
This application doesn't work with the latest updates to mbed OS 5 since USB Device support is currently not available for LPC4088
The purpose of this application is to allow easy manipulation of the QSPI file system from a PC.
The application makes the LPC4088 Display Module appear as a USB Memory Stick when connected to a PC. The PC will see the current content of the QSPI file system plus an image file of the file system that can be downloaded and (at a later time) be used to restore the file system to it's current state.
To use this application:
- Download the lpc4088_displaymodule_fs_aid application using drag-n-drop and then reset the board
- Optionally start a terminal program to read the status messages from the application
- Connect a USB cable to the mini USB slot on the back of the LPC4088 Display Module, and then to the PC
- The PC will install drivers if needed and then the USB Memory Stick will be available as a new drive
- Modify the file system to suit your needs
- With the USB cable still connected, press the USER/ISP/SW1 button on the LPC4088 Display Module
- The application will now:
- disconnect the USB Memory Stick
- write all changes to the QSPI flash memory
- create a new image file of the updated QSPI file system and store it in the .current/ folder
- connect the USB Memory Stick again
- Continue from step 5. until satisfied
Note 1: If the QSPI doesn't have a file system on it or to replace the current one with a new, possibly of a different size, file system just add a file with the name format_qspi_X_mb (where X should be replace with the wanted file system size in Mb). For a 10 Mb file system the file name should be format_qspi_10_mb.
Note 2: The file system that is exposed is a copy (in SDRAM) of the QSPI file system. The reason for this is that the USBMSD class requires a FAT file system.
Note 3: The image files created in step 7.3 above will be a *.fsX file (where the 'X' is the size of the file system in MB so *.fs1 for a 1MByte file system). The *.fsX file extensions are recognized by the HDK and can be used to drag-n-drop to the MBED drive in the same way as the *.bin files are. A *.fsX file will not overwrite the program stored in internal flash.
Diff: main.cpp
- Revision:
- 0:06e35dd73c95
- Child:
- 1:b04139d88c59
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Mon May 16 07:05:18 2016 +0000 @@ -0,0 +1,556 @@ +/****************************************************************************** + * Includes + *****************************************************************************/ + +#include "mbed.h" +#include "mbed_interface.h" +#include "rtos.h" + +#include "DMBoard.h" +#include "RAMFileSystem.h" +#include "USBMSD_RAMFS.h" + +#include "crc.h" + +#include "lpc_swim.h" +#include "lpc_swim_font.h" +#include "lpc_winfreesystem14x16.h" + +/****************************************************************************** + * Typedefs and defines + *****************************************************************************/ + +typedef bool (*syncFunc)(const char* name, bool isDir); + +/* Size of RAM file system */ +#define RAM_FS_SIZE (20*1024*1024) + + +/****************************************************************************** + * Local variables + *****************************************************************************/ + +//DigitalOut myled1(LED1); +//DigitalOut myled2(LED2); +//DigitalIn button(p23); + +//DigitalOut myled(LED1); +static char lsbuff[NAME_MAX+1]; + +static char image_file_name[128] = { '\0' }; + +/****************************************************************************** + * Local functions + *****************************************************************************/ + + +static void ledShowProgress() +{ + static int state = 0; + state = (state + 1) % 2; + + DMBoard* board = &DMBoard::instance(); + board->setLED(DMBoard::Led1, state==0); + board->setLED(DMBoard::Led2, state!=0); +} + +static void handleError(const char* msg) +{ + DMBoard* board = &DMBoard::instance(); + board->logger()->printf(msg); + while(true) { + board->setLED(DMBoard::Led1, false); + board->setLED(DMBoard::Led2, false); + wait(0.3); + board->setLED(DMBoard::Led1, true); + board->setLED(DMBoard::Led2, true); + wait(0.3); + } +} + +static void waitForButtonPress() +{ + DMBoard* board = &DMBoard::instance(); + printf("Press button to sync file systems\n"); + + board->setLED(DMBoard::Led1, false); + board->setLED(DMBoard::Led2, false); + while(!board->buttonPressed()) { + wait(0.1); + } + board->setLED(DMBoard::Led1, true); + printf("Button pressed, now release it\n"); + while(board->buttonPressed()) { + wait(0.1); + } +} + +static void addNotFormattedFile() +{ + FILE *fp = fopen("/ram/qspi_not_formatted.txt", "w"); + if (fp != NULL) { + fprintf(fp, "The QSPI file system has not be formatted and this program can't do it.\n"); + fprintf(fp, "Format the QSPI file system and then run this program again!\n"); + fclose(fp); + } +} + +static bool recursiveProcessFS(char* buff, const char* name, syncFunc func, bool adding) +{ + uint32_t len = strlen(buff); + if (len > 0) { + if (buff[len - 1] != '/') { + buff[len++] = '/'; + buff[len] = '\0'; + } + } + strcat(buff, name); + len += strlen(name); + + DIR *d = opendir(buff); + bool result = true; // success + if (d != NULL) { + if (adding) { + // when processing in adding mode folders must be created before it's content + result = func(buff, true); + } + struct dirent *p; + while (result && ((p = readdir(d)) != NULL)) { + result = recursiveProcessFS(buff, p->d_name, func, adding); + buff[len] = '\0'; + } + closedir(d); + if (result && !adding) { + // when processing in removing mode folders must be deleted after it's content + result = func(buff, true); + } + } else { + // a file + result = func(buff, false); + } + return result; +} + +static uint32_t fileLen(FILE* f) +{ + uint32_t pos = ftell(f); + fseek(f, 0, SEEK_END); + uint32_t size = ftell(f); + fseek(f, pos, SEEK_SET); + return size; +} + +static bool copy(FILE* fSrc, FILE* fDst) +{ + char buff[512]; + uint32_t left = fileLen(fSrc); + uint32_t num; + uint32_t chunk; + + fseek(fSrc, 0, SEEK_SET); + do { + chunk = (left < 512) ? left : 512; + num = fread(buff, 1, chunk, fSrc); + if (num > 0) { + uint32_t tmp = fwrite(buff, 1, num, fDst); + if (tmp != num) { + // failed to write + return false; + } + left -= num; + ledShowProgress(); + } + } while(num > 0 && left > 0); + + // copied entire file + return true; +} + +static bool identicalFiles(const char* srcName, const char* dstName) +{ + FILE* fSrc = NULL; + FILE* fDst = NULL; + bool identical = false; + do + { + fSrc = fopen(srcName, "r"); + if (fSrc == NULL) { + break; + } + fDst = fopen(dstName, "r"); + if (fDst == NULL) { + break; + } + if (fileLen(fSrc) != fileLen(fDst)) { + break; + } + if (crc_File(fSrc) != crc_File(fDst)) { + break; + } + + // All tests passed so the files are identical + identical = true; + + } while(false); + + if (fSrc != NULL) { + fclose(fSrc); + } + if (fDst != NULL) { + fclose(fDst); + } + + return identical; +} + +static bool addExisting(const char* srcName, bool isDir, const char* dstName) +{ + bool result = true; // success + if (isDir) { + DIR *d = opendir(dstName); + if (d == NULL) { + if (dstName[1] != 'r') { printf("%s, new dir, adding\n", dstName); } + if (mkdir(dstName, 0) != 0) { + printf("Failed to create folder %s\n", dstName); + result = false; // dir did not exist and could not be created + } + } else { + closedir(d); + } + } else if (!identicalFiles(srcName, dstName)) { + // Compare the files to avoid replacing with same + FILE* fSrc = fopen(srcName, "r"); + if (fSrc != NULL) { + FILE* fDst = fopen(dstName, "w"); // open and truncate + if (fDst != NULL) { + if (dstName[1] != 'r') { printf("%s, changed, updating\n", dstName); } + result = copy(fSrc, fDst); + if (!result) { + printf("Failed to copy %s to %s\n", srcName, dstName); + } + fclose(fDst); + } else { + printf("Failed to create file %s\n", dstName); + result = false; // unable to create file + } + fclose(fSrc); + } else { + printf("Failed to copen source file file %s\n", srcName); + result = false; // unable to open source + } + } else { + if (dstName[1] != 'r') { printf("%s identical, skipping\n", dstName); } + } + return result; +} + +static bool addExistingToQspi(const char* name, bool isDir) +{ + // create the target file name by replacing /ram/ with /qspi/ + char buff[256]; + buff[0] = '\0'; + strcat(buff, "/qspi/"); + strcat(buff, name+5); + + // Don't add the file created by createImageFile() + if (strcmp(name, image_file_name) == 0) { + return true; + } + + return addExisting(name, isDir, buff); +} + +static bool addExistingToRAM(const char* name, bool isDir) +{ + // create the target file name by replacing /qspi/ with /ram/ + char buff[256]; + buff[0] = '\0'; + strcat(buff, "/ram/"); + strcat(buff, name+6); + return addExisting(name, isDir, buff); +} + +static bool removeIfMissing(const char* toLookFor, const char* toRemove, bool isDir) +{ + int result = 0; //success + if (isDir) { + DIR *d = opendir(toLookFor); + if (d == NULL) { + // dir doesn't exist => delete it + if (toRemove[1] != 'r') { printf("%s, missing, deleting dir\n", toRemove); } + result = remove(toRemove); + ledShowProgress(); + } else { + // dir exist => don't delete + closedir(d); + } + } else { + FILE* f = fopen(toLookFor, "r"); + if (f == NULL) { + // file doesn't exist => delete it + if (toRemove[1] != 'r') { printf("%s, missing, deleting file\n", toRemove); } + result = remove(toRemove); + ledShowProgress(); + } else { + // file exist => don't delete + fclose(f); + } + } + return (result == 0); +} + +static bool removeMissingFromQspi(const char* name, bool isDir) +{ + // create the target file name by replacing /qspi/ with /ram/ + char buff[256]; + buff[0] = '\0'; + strcat(buff, "/ram/"); + strcat(buff, name+6); + removeIfMissing(buff, name, isDir); + return true; +} + +static bool removeMissingFromRAM(const char* name, bool isDir) +{ + // create the target file name by replacing /ram/ with /qspi/ + char buff[256]; + buff[0] = '\0'; + strcat(buff, "/qspi/"); + strcat(buff, name+5); + + // Don't remove the file created by createImageFile() + if (strcmp(name, image_file_name) == 0) { + return true; + } + + removeIfMissing(buff, name, isDir); + return true; +} + +static void syncDir(const char* to, const char* from) +{ + printf("Starting to sync %s on top of %s (This may take time. LED1 & 2 blink for each file)\n", from, to); + + char* buff = (char*)malloc(512); + if (buff != NULL) + { + buff[0] = '\0'; + if (strcmp(to, "/qspi/") == 0) { + if (!recursiveProcessFS(buff, to, removeMissingFromQspi, false)) { + printf("Failed to remove files from %s that were missing on %s\n", to, from); + } else { + buff[0] = '\0'; + if (!recursiveProcessFS(buff, from, addExistingToQspi, true)) { + printf("Failed to add files to %s that existed on %s\n", to, from); + } + } + } else { + if (!recursiveProcessFS(buff, to, removeMissingFromRAM, false)) { + printf("Failed to remove files from %s that were missing on %s\n", to, from); + } else { + buff[0] = '\0'; + if (!recursiveProcessFS(buff, from, addExistingToRAM, true)) { + printf("Failed to add files to %s that existed on %s\n", to, from); + } + } + } + free(buff); + } + printf("Sync completed\n"); +} + + +static void createImageFile(QSPIFileSystem* qspi) +{ + uint32_t startAddr; + uint32_t endAddr; + uint32_t size; + + printf("Creating image of existing (if any) QSPI file system\n"); + + if (!qspi->getMemoryBoundaries(&startAddr, &endAddr)) + { + handleError("QSPI FS not formatted or impossible to determine it's size\n"); + } + + // Align the start address to an even multiple of 1MB + startAddr = startAddr & 0xfff00000; + + // Update the file to match the size of the file system + size = endAddr - startAddr; + if ((size < 0x00100000) || (size > 0x01000000) || ((size & 0xfffff) > 0)) + { + sprintf(lsbuff, "QSPI FS size is not supported (%u bytes)\n", size); + handleError(lsbuff); + } + sprintf(image_file_name, "/ram/.current/fs_image.fs%d", (size >> 20)); + + // NOTE: The line below is very very !!!! important. For some weird reason the + // RAM file system must have at least one folder on it before USB is connected. + // If the RAM file system doesn't have any folders on it the result is that + // the content of the RAM file system will be the same after USB is disconnected + // as it was before connecting - regardless of what is added. Very strange indeed. + mkdir("/ram/.current", 0); + + printf("QSPI FS max size is %d MB\n", (size >> 20)); + + FILE *fp = fopen(image_file_name, "w"); + if (fp != NULL) + { + while (size > 0) + { + uint32_t written = fwrite((char*)(endAddr - size), 1, size, fp); + size -= written; + if (written == 0) + { + handleError("Failed to create QSPI image file\n"); + } + } + fclose(fp); + } +} + +static bool list(const char* name, bool isDir) +{ + if (isDir) { + printf("d: %s\n", name); + } else { + FILE* f = fopen(name, "r"); + if (f != NULL) { + uint32_t len = fileLen(f); + printf("f: %7u %s\n", len, name); + fclose(f); + } else { + printf("f: ??? %s\n", name); + } + } + return true; +} + +static void recursiveList(const char* dirname) +{ + printf("\nRecursive list of file and folders in %s\n", dirname); + char* buff = (char*)malloc(512); + if (buff != NULL) + { + buff[0] = '\0'; + recursiveProcessFS(buff, dirname, list, true); + free(buff); + } +} + +static void showInfoScreen() +{ + static SWIM_WINDOW_T* win = NULL; + static void* fb = NULL; + + Display* disp = DMBoard::instance().display(); + win = (SWIM_WINDOW_T*)malloc(sizeof(SWIM_WINDOW_T)); + fb = disp->allocateFramebuffer(); + + swim_window_open(win, + disp->width(), disp->height(), // full size + (COLOR_T*)fb, + 0,0,disp->width()-1, disp->height()-1, // window position and size + 0, // border + BLUE, WHITE, BLACK); // colors: pen, backgr, forgr + + swim_set_font(win, (FONT_T*)&font_winfreesys14x16); + + // Show a message + swim_put_text_centered_win(win, "In this version all instructions are printed on the console!", disp->height()/2 - 20); + swim_put_text_centered_win(win, "Connect a terminal application using 115200, 8N1.", disp->height()/2 + 20); + + // Start display in default mode (16-bit) + Display::DisplayError disperr = disp->powerUp(fb); + if (disperr != Display::DisplayError_Ok) { + DMBoard::instance().logger()->printf("Failed to initialize the display, got error %d\r\n", disperr); + wait_ms(2000); // allow RtosLog to flush messages + mbed_die(); + } +} + +/****************************************************************************** + * Main function + *****************************************************************************/ +int main() +{ + DMBoard::BoardError err; + DMBoard* board = &DMBoard::instance(); + RtosLog* log = board->logger(); + err = board->init(); + if (err != DMBoard::Ok) { + log->printf("Failed to initialize the board, got error %d\r\n", err); + wait_ms(2000); // allow RtosLog to flush messages + mbed_die(); + } + + log->printf("\n\n---\nQSPI file syncer app\nBuilt: " __DATE__ " at " __TIME__ "\n\n"); + + showInfoScreen(); + + // 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); + + while(true) + { + // add an empty file system on it + ramfs.format(); + + QSPIFileSystem* qspi = board->getQspiFS(); + bool qspiFormatted = qspi->isformatted(); + if (!qspiFormatted) + { + addNotFormattedFile(); + } + else + { + createImageFile(qspi); + + // Copy QSPI FS to RAM FS + syncDir("/ram/", "/qspi/"); + } + + printf("Insert the USB cable!\n"); + printf("Starting USB...\n"); + for (int i = 0; i < 10; i++) + { + if (usbmsd.connect()) + { + printf("Connected!\n"); + break; + } + printf("Failed to connect USB, testing again...\n"); + printf("Insert (or remove and then insert) the USB cable!\n"); + wait(1); + } + + // 6b) + if (qspiFormatted) + { + waitForButtonPress(); + } + else + { + // 6a) no point in waiting for buttons if no file system + while (1) {}; + } + + // 7) + usbmsd.disconnect(); + printf("Disconnected!\n"); + + // 8) Copy RAM FS to QSPI FS + recursiveList("/ram/"); + syncDir("/qspi/", "/ram/"); + } +} +