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.
main.cpp
- Committer:
- alindvall
- Date:
- 2016-05-16
- Revision:
- 1:b04139d88c59
- Parent:
- 0:06e35dd73c95
File content as of revision 1:b04139d88c59:
/****************************************************************************** * 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. To format create a\n"); fprintf(fp, "file with the name \"format_qspi_X_mb\" on the drive and then\n"); fprintf(fp, "press the USER/ISP/SW1 button to format\n"); fprintf(fp, "The X in the file name should be replaced with the desired\n"); fprintf(fp, "file system size in Mb (1 to 16). For a 10 Mb file sytem the file\n"); fprintf(fp, "name would be format_qspi_10_mb.\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 bool formatIfRequested() { DMBoard* board = &DMBoard::instance(); RtosLog* logger = board->logger(); char marker[50]; int size; uint32_t maxSize = SPIFI::instance().memorySize()>>20; for (size = 1; size <= maxSize; size++) { sprintf(marker, "/ram/format_qspi_%d_mb", size); FILE *fp = fopen(marker, "r"); if (fp != NULL) { logger->printf("Found a marker file requesting to place a %d Mb file system on QSPI\n", size); logger->printf("This operation may take up to a couple of minutes!\n"); QSPIFileSystem* qspi = board->getQspiFS(); int err = qspi->format(size); if (err == 0) { logger->printf("Successfully added a %d Mb file system to QSPI!\n", size); } else { logger->printf("Failed to format QSPI!\n"); } // formatting was requested return true; } } // no formatting requested return false; } 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); } waitForButtonPress(); usbmsd.disconnect(); printf("Disconnected!\n"); // Look for (re)format instruction file if (formatIfRequested()) { continue; } // Copy RAM FS to QSPI FS recursiveList("/ram/"); syncDir("/qspi/", "/ram/"); } }