How to get Data off an Embedded System: FatFS with USB MSD Host and FreeRTOS Direct Task Notification

This is a follow-up article of my earlier project presented in “FatFS, MinIni, Shell and FreeRTOS for the NXP K22FN512“. I wanted to extend it with a USB MSD (memory stick) device: The USB storage device gets automatically mounted, and depending on a configuration (.ini) file on the memory device I can perform various actions, for example automatically copy data from the SD card to the USB device. For example the system logs data, and to get the data I insert the memory stick, it copies the data on it and automatically unmounts it, and I can remove the memory stick.

FRDM-K22F USB Host Mode with Memory Sticks

FRDM-K22F USB Host Mode with Memory Sticks

Outline

In this article I’m using the same FRDM-K22F (NXP K22FN512) as in the previous article. The use case is that the system logs some data using FatFS as file system, and I want to have an easy way to get data copied to a host (PC) system with minimal user intervention. Think about a device in the field and the service person needs to collect data from the devices. The approach is to use a USB MSD (memory stick) device which can be plugged into the USB port and to which the data will be transferred. To make it flexible and configurable, I’m using an .ini file (see “FatFS, MinIni, Shell and FreeRTOS for the NXP K22FN512“) on the memory stick which can be used to configure the actions.

For logging data I’m using the data logger I have used in “McuLog: Logging Framework for small Embedded Microcontroller Systems“.

The full source code is available on GitHub (see the links at the end of the article). It uses the Eclipse based NXP MCUXpresso IDE with the SDK and the GNU tools.

FRDM-K22F Board USB Host Mode

To use the FRDM-K22F board in USB host mode, the Jumper J22 needs to be installed. Additionally an adapter from Micro-B to Standard-A is needed for plugging in normal thumb drives:

FRDM-K22F Jumper and USB Adapter

FRDM-K22F Jumper and USB Adapter

USB MSD Host Stack

The project uses a modified USB MSD host device stack from the NXP MCUXpresso SDK:

  • McuLib\FatFS\source\fsl_usb_disk: directory with the FatFS implementation for the MSD (USB disk)
  • usb\host: Directory with the generic USB host stack
  • source\host_msd_fatfs.c/.h: Application level callbacks of the MSD stack. Includes a command line (shell) handler
USB MSD Files

USB MSD Files

Disk Module

The ‘Disk’ module is responsible to automatically mount and unmount file systems. For an efficient implementation it uses FreeRTOS Direct Task Notification. If SD card and/or USB MSD support is used is configured through #define in platform.h. Below the interface for the disk module:

/*
 * Copyright (c) 2020, Erich Styger
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#ifndef DISK_H_
#define DISK_H_

#include "platform.h"
#if PL_CONFIG_USE_SD_CARD || PL_CONFIG_USE_USB_MSD
#include <stdbool.h>

typedef enum {
#if PL_CONFIG_USE_SD_CARD
  DISK_EVENT_SD_CARD_INSERTED = (1<<0),
  DISK_EVENT_SD_CARD_REMOVED = (1<<1),
  DISK_EVENT_SD_CARD_REQ_MOUNT = (1<<2),
  DISK_EVENT_SD_CARD_REQ_UNMOUNT = (1<<3),
#endif
#if PL_CONFIG_USE_USB_MSD
  DISK_EVENT_USB_MSD_INSERTED = (1<<4),
  DISK_EVENT_USB_MSD_REMOVED = (1<<5),
  DISK_EVENT_USB_MSD_REQ_MOUNT = (1<<6),
  DISK_EVENT_USB_MSD_REQ_UNMOUNT = (1<<7),
#endif
} DISK_Events;

#include "McuShell.h"
uint8_t DISK_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io);

#define DISK_DRIVE_SD_CARD  "0:/"
#define DISK_DRIVE_USB_MSD  "1:/"

/* checks if the device is inserted */
bool DISK_IsInserted(unsigned char *drive);

/* checks if the device is mounted and ready to use */
bool DISK_IsMounted(unsigned char *drive);

void DISK_SendEvent(DISK_Events event);

void DISK_Init(void);

#endif /* PL_CONFIG_USE_SD_CARD || PL_CONFIG_USE_USB_MSD */

#endif /* DISK_H_ */

The implementation uses ‘0’ as device for the SD card and ‘1’ for the USB memory device, but this can be easily changed if needed.
The module runs a FreeRTOS task which blocks on the notification bits so does not need any processing time if not notified.

 
/*
 * Copyright (c) 2020, Erich Styger
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "platform.h"
#if PL_CONFIG_USE_SD_CARD || PL_CONFIG_USE_USB_MSD
#include "disk.h"
#include "McuLog.h"
#include "McuRTOS.h"
#include "McuFatFS.h"
#include "host_msd_fatfs.h"

static TaskHandle_t diskTaskHandle;
#if PL_CONFIG_USE_SD_CARD
  static bool DISK_sdMounted = false;
#endif
#if PL_CONFIG_USE_USB_MSD
  static bool DISK_msdMounted = false;
#endif

bool DISK_IsInserted(unsigned char *drive) {
  switch(*drive) {
  #if PL_CONFIG_USE_SD_CARD
    case '0': return McuFatFS_isDiskPresent(drive);
  #endif
  #if PL_CONFIG_USE_USB_MSD
    case '1': return DISK_msdMounted; /* no special hardware pin */
  #endif
    default: break;
  }
  return false;
}

bool DISK_IsMounted(unsigned char *drive) {
  switch(*drive) {
  #if PL_CONFIG_USE_SD_CARD
    case '0': return DISK_sdMounted;
  #endif
  #if PL_CONFIG_USE_USB_MSD
    case '1': return DISK_msdMounted;
  #endif
    default: break;
  }
  return false;
}

void DISK_SendEvent(DISK_Events event) {
  xTaskNotify(diskTaskHandle, event, eSetBits);
}

static void DiskTask(void *pv) {
  uint32_t ulNotificationValue;
  BaseType_t res;
#if PL_CONFIG_USE_SD_CARD
  static McuFatFS_FATFS fsSdCard;
#endif
#if PL_CONFIG_USE_USB_MSD
  static McuFatFS_FATFS fsUsbMSD;
#endif
  uint8_t result;
  McuFatFS_FRESULT fres;

#if PL_CONFIG_USE_SD_CARD
  DISK_sdMounted = false;
#endif
#if PL_CONFIG_USE_USB_MSD
  DISK_msdMounted = false;
#endif
  for(;;) {
    res = xTaskNotifyWait(
        0, /* do not clear anything on enter */
        0 /* dummy */
    #if PL_CONFIG_USE_SD_CARD
        |DISK_EVENT_SD_CARD_INSERTED|DISK_EVENT_SD_CARD_REMOVED
        |DISK_EVENT_SD_CARD_REQ_MOUNT|DISK_EVENT_SD_CARD_REQ_UNMOUNT
    #endif
    #if PL_CONFIG_USE_USB_MSD
        |DISK_EVENT_USB_MSD_INSERTED|DISK_EVENT_USB_MSD_REMOVED
        |DISK_EVENT_USB_MSD_REQ_MOUNT|DISK_EVENT_USB_MSD_REQ_UNMOUNT
    #endif
        ,
        &ulNotificationValue,
        portMAX_DELAY);
    if (res==pdTRUE) { /* notification received */
    #if PL_CONFIG_USE_SD_CARD
      if (ulNotificationValue&DISK_EVENT_SD_CARD_INSERTED) {
        McuLog_info("SD card inserted");
        DISK_SendEvent(DISK_EVENT_SD_CARD_REQ_MOUNT);
      }
      if (ulNotificationValue&DISK_EVENT_SD_CARD_REMOVED) {
        McuLog_info("SD card removed");
        DISK_SendEvent(DISK_EVENT_SD_CARD_REQ_UNMOUNT);
      }
      if (ulNotificationValue&DISK_EVENT_SD_CARD_REQ_MOUNT) {
        if (DISK_sdMounted) {
          McuLog_error("Mount request, but SD card is already mounted. Unmount it first.");
        } else {
          McuLog_info("SD card mount request");
          /* mount card */
          result = McuFatFS_CheckCardPresence(&DISK_sdMounted, (unsigned char*)DISK_DRIVE_SD_CARD, &fsSdCard, NULL);
          if (result==ERR_OK) {
            McuLog_info("SD card mounted successfully");
          } else {
            McuLog_error("failed mounting SD card");
          }
        }
      }
      if (ulNotificationValue&DISK_EVENT_SD_CARD_REQ_UNMOUNT) {
        McuLog_info("SD card unmount request");
        if (DISK_sdMounted) {
          /* unmount sd card */
          fres = McuFatFS_mount(NULL, (const TCHAR*)DISK_DRIVE_SD_CARD, 0);
          if (fres==FR_OK) {
            DISK_sdMounted = false;
            McuLog_info("SD card unmounted successfully");
          } else {
            McuLog_error("Failed unmounting SD card");
          }
        } else {
          McuLog_error("Unmount request, but SD card is not mounted");
        }
      }
    #endif /* PL_CONFIG_USE_SD_CARD */

    #if PL_CONFIG_USE_USB_MSD
      if (ulNotificationValue&DISK_EVENT_USB_MSD_INSERTED) {
        McuLog_info("USB MSD inserted");
        DISK_SendEvent(DISK_EVENT_USB_MSD_REQ_MOUNT);
      }
      if (ulNotificationValue&DISK_EVENT_USB_MSD_REMOVED) {
        McuLog_info("USB MSD removed");
        DISK_SendEvent(DISK_EVENT_USB_MSD_REQ_UNMOUNT);
      }
      if (ulNotificationValue&DISK_EVENT_USB_MSD_REQ_MOUNT) {
        if (DISK_msdMounted) {
          McuLog_error("Mount request, but USB MSD is already mounted. Unmount it first.");
        } else {
          McuLog_info("USB MSD mount request");
          /* mount card */
          result = USB_HostMsdCheckDiskPresence(&DISK_msdMounted, (unsigned char*)DISK_DRIVE_USB_MSD, &fsUsbMSD, NULL);
          if (result==ERR_OK) {
            McuLog_info("USB MSD mounted successfully");
          } else {
            McuLog_error("Failed mounting USB MSD");
          }
        }
      }
      if (ulNotificationValue&DISK_EVENT_USB_MSD_REQ_UNMOUNT) {
        McuLog_info("USB MSD unmount request");
        if (DISK_msdMounted) {
          /* unmount USB MSD */
          fres = McuFatFS_mount(NULL, (const TCHAR*)DISK_DRIVE_USB_MSD, 0);
          if (fres==FR_OK) {
            DISK_msdMounted = false;
            McuLog_info("USB MSD unmounted successfully");
          } else {
            McuLog_error("Failed unmounting USB MSD");
          }
        } else {
          McuLog_error("Unmount request, but USB MSD is not mounted");
        }
      }
    #endif /* PL_CONFIG_USE_USB_MSD */
    }
  }
}

static uint8_t PrintStatus(const McuShell_StdIOType *io) {
  unsigned char buf[32];

  McuShell_SendStatusStr((unsigned char*)"disk", (unsigned char*)"disk status\r\n", io->stdOut);
#if PL_CONFIG_USE_SD_CARD
  McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"\"");
  McuUtility_strcat(buf, sizeof(buf), (unsigned char*)DISK_DRIVE_SD_CARD);
  McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\", mounted: ");
  McuUtility_strcat(buf, sizeof(buf), DISK_sdMounted? (unsigned char*)"yes\r\n":(unsigned char*)"no\r\n");
  McuShell_SendStatusStr((unsigned char*)"  sd drive", buf, io->stdOut);
#endif
#if PL_CONFIG_USE_USB_MSD
  McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"\"");
  McuUtility_strcat(buf, sizeof(buf), (unsigned char*)DISK_DRIVE_USB_MSD);
  McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\", mounted: ");
  McuUtility_strcat(buf, sizeof(buf), DISK_msdMounted? (unsigned char*)"yes\r\n":(unsigned char*)"no\r\n");
  McuShell_SendStatusStr((unsigned char*)"  msd drive", buf, io->stdOut);
#endif
  return ERR_OK;
}

static uint8_t PrintHelp(const McuShell_StdIOType *io) {
  McuShell_SendHelpStr((unsigned char*)"disk", (unsigned char*)"Group of disk commands\r\n", io->stdOut);
  McuShell_SendHelpStr((unsigned char*)"  help|status", (unsigned char*)"Print help or status information\r\n", io->stdOut);
#if PL_CONFIG_USE_SD_CARD
  McuShell_SendHelpStr((unsigned char*)"  sd mount|unmount", (unsigned char*)"Mount or unmount SD card device\r\n", io->stdOut);
#endif
#if PL_CONFIG_USE_USB_MSD
  McuShell_SendHelpStr((unsigned char*)"  msd mount|unmount", (unsigned char*)"Mount or unmount USB MSD device\r\n", io->stdOut);
  return ERR_OK;
#endif
}

uint8_t DISK_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io) {
  uint8_t res = ERR_OK;

  if (McuUtility_strcmp((char*)cmd, McuShell_CMD_HELP) == 0
    || McuUtility_strcmp((char*)cmd, "disk help") == 0)
  {
    *handled = TRUE;
    return PrintHelp(io);
  } else if (   (McuUtility_strcmp((char*)cmd, McuShell_CMD_STATUS)==0)
             || (McuUtility_strcmp((char*)cmd, "disk status")==0)
            )
  {
    *handled = TRUE;
    res = PrintStatus(io);
#if PL_CONFIG_USE_SD_CARD
  } else if (McuUtility_strcmp((char*)cmd, "disk sd mount")==0) {
    *handled = TRUE;
    DISK_SendEvent(DISK_EVENT_SD_CARD_REQ_MOUNT);
  } else if (McuUtility_strcmp((char*)cmd, "disk sd unmount")==0) {
    *handled = TRUE;
    DISK_SendEvent(DISK_EVENT_SD_CARD_REQ_UNMOUNT);
#endif
#if PL_CONFIG_USE_USB_MSD
  } else if (McuUtility_strcmp((char*)cmd, "disk msd mount")==0) {
    *handled = TRUE;
    DISK_SendEvent(DISK_EVENT_USB_MSD_REQ_MOUNT);
  } else if (McuUtility_strcmp((char*)cmd, "disk msd unmount")==0) {
    *handled = TRUE;
    DISK_SendEvent(DISK_EVENT_USB_MSD_REQ_UNMOUNT);
#endif
  }
  return res;
}

void DISK_Init(void) {
  BaseType_t result;

  result =xTaskCreate(DiskTask, "DiskTask", 2500/sizeof(StackType_t), NULL, tskIDLE_PRIORITY+2, &diskTaskHandle);
  if (result!=pdPASS) {
    McuLog_fatal("Failed creating DiskTask");
    for(;;) {}
  }
}
#endif /* PL_CONFIG_USE_SD_CARD || PL_CONFIG_USE_USB_MSD */

Using the command line interface the status of the system can be inspected and devices manually mounted and unmounted. It automatically detects devices plugged in (or out) and performs the needed actions and logs them:

Command Line Interface

Command Line Interface

Configuration with MinIni

To make it easy for the service person in the field, the memory stick can contain a configuration (.ini) file which configures what shall happen when the memory stick is inserted. Below is an example configuration file:

[Mount]
copyLog=yes
srcLogFile=0:\log.txt
dstLogFile=1:\log.txt
unmount=yes

With the above, after mounting the memory stick it will copy the log file. The source file name is “0:\log.txt” (on the SD card) and the destination file is “1:\log.txt” (on the thumb drive). After completion of the copy command, the memory stick is automatically unmounted so it can be removed.

The implementation of this is done in the ‘application’ task: it checks if the thumb drive is detected, then reads the .ini file and performs the actions:

 
    unsigned char srcLogFileName[32], dstLogFileName[32];

    if (DISK_IsMounted((unsigned char*)DISK_DRIVE_USB_MSD)) {
      if (ini_gets(
          (const TCHAR *)"Mount", /* section */
          (const TCHAR *)"copyLog", /* key */
          (const TCHAR *)"no",  /* default value */
          (TCHAR *)keyValueBuf, sizeof(keyValueBuf), /* key value from ini */
          (const TCHAR *)USB_MSD_INI_FILENAME /* ini file */
          )
          > 0) /* success */
      {
        if (McuUtility_strcmp((char*)keyValueBuf, (char*)"yes")==0) { /* copyLog=yes ? */
          if (
              ini_gets(
              (const TCHAR *)"Mount", /* section */
              (const TCHAR *)"srcLogFile", /* key */
              (const TCHAR *)DEFAULT_LOG_SRC_FILENAME,  /* default value */
              (TCHAR *)srcLogFileName, sizeof(srcLogFileName), /* key value from ini */
              (const TCHAR *)USB_MSD_INI_FILENAME /* ini file */
              ) > 0 /* success */
              &&
              ini_gets(
              (const TCHAR *)"Mount", /* section */
              (const TCHAR *)"dstLogFile", /* key */
              (const TCHAR *)DEFAULT_LOG_DST_FILENAME,  /* default value */
              (TCHAR *)dstLogFileName, sizeof(dstLogFileName), /* key value from ini */
              (const TCHAR *)USB_MSD_INI_FILENAME /* ini file */
              ) > 0) /* success */
          {
            /* copy log file */
           McuLog_info("Copy log file '%s' => '%s'", srcLogFileName, dstLogFileName);
           if (McuFatFS_CopyFile(srcLogFileName, dstLogFileName, NULL)!=ERR_OK) {
             McuLED_Off(led);
             for(int i=0;i<10;i++) { /* indicate error with 10 red blinks */ McuLED_Toggle(LEDS_LedRed); vTaskDelay(pdMS_TO_TICKS(500)); } McuLog_error("Copy of log file failed"); } } } } if (ini_gets( (const TCHAR *)"Mount", /* section */ (const TCHAR *)"unmount", /* key */ (const TCHAR *)"no", /* default value */ (TCHAR *)keyValueBuf, sizeof(keyValueBuf), /* key value from ini */ (const TCHAR *)USB_MSD_INI_FILENAME /* ini file */ ) > 0) /* success */
      {
        if (McuUtility_strcmp((char*)keyValueBuf, (char*)"yes")==0) { /* unmount=yes ? */
          DISK_SendEvent(DISK_EVENT_USB_MSD_REQ_UNMOUNT);
          McuLED_Off(led);
          for(int i=0;i<10;i++) { /* indicate unmount with 5 blue blinks */
            McuLED_Toggle(LEDS_LedBlue);
            vTaskDelay(pdMS_TO_TICKS(500));
          }
        }
      }

The task uses FreeRTOS Direct Task Notification to trigger disk actions like unmounting the device after finishing the copy operation. The LED on the board is used to indicate that status (e.g. blinking blue or red for a failure).

Result

Below is an example output and log: the thumb drive gets detected, the file copied to it and then automatically unmounted so it can be removed:

Automatic copy operation

Automatic copy operation from SD to MSD

The example project uses around 140 KByte of FLASH memory (no optimizations) and 109 KByte with -O3. RAM is around 30 KByte, with plenty of headroom for all the task stacks. This certainly could be trimmed down depending on the features needed: the FatFS and USB stack are consuming the most of it.

Summary

I have found that using a combination of MinIni with a dual-FatFS system is very powerful: having the .ini file on the thumb drive with configuration data removes the need for an operator to ‘eject’ the thumb drive as no ‘hot-unplug’ is easily possible with FatFS.

I hope you find this useful and might be able to use the presented approach for your projects.

Happy Copying 🙂

Links

 

9 thoughts on “How to get Data off an Embedded System: FatFS with USB MSD Host and FreeRTOS Direct Task Notification

  1. Hi Erich , I have previously performed some evaluation of the USB function of the K22F and K64F boards about 3 years ago but was not convinced of the consistency of operation of the demo applications. I may have to give it another try. Logging to a large onboard serial flash chip and then using a serial port to upload/ download to a PC is the simplest and usually most reliable solution, but is a bit old school and may require a custom application on the PC.

    Like

    • I have used the ‘new’ SDK stack. I agree that the previous ‘Freescale’ (MQX based?) stack was not very good. So think that three years ago it was a different stack. The one for the K22F so far has performed well (I had to tweak the CDC device one as it was not reentrant). I agree that a serial download would be ideal, but in my case the requirement was to use a memory stick. One way could have been to create a dedicated ‘download’ board with a serial connection and then have an onboard SD card to swap out and move to the PC with the maintenance person getting back to the office.

      Like

  2. Hello Eric

    I use 4 different techniques to collect logged data from a FRDM-K22F
    – doing it by copying from the SD card to a memory stick as you do here (I have a DOS-like FAT interface on a shell too), whereby the project is 61.7k code and 11.7k RAM (including heap usage during operation). Using utFAT there is no additional overhead to use on multiple interface in FAT32 LFN mode and the (uTasker) USB host stack is fairly efficient too.
    – copying directly from the SD card via USB-MSD device: This is the easiest and fastest method if the data is destined for a PC and doesn’t need to be transported on a memory stick to get it there. This project is 60.6k in size with 9k RAM utilisation, showing that the fact that there is no FAT interface for the memory stick doesn’t make a big difference.
    – RNDIS device so the PC can access the SD card via FTP (over USB). Code size is 76.4k and 18k RAM (heavier due to the need for TCP/IP), but this is of course more complicated and is only used in circumstances when RNDIS is needed generally anyway.
    – emulated FAT to USB-MSD. I use this to allow logging data to internal or SPI based flash in a linear fashion (more efficient and faster than FAT for such memory) and then retrieving them via USB-MSD device where the PC “sees” the logged data as formatted file data, whereby the concept is explained here:

    Click to access uTaskerEmulatedFAT.pdf


    Code size 38.5k, RAM usage 12k and so good code efficiency.
    Code sizes are with GCC optimisation for size and the project runs with or without FreeRTOS (which, when enabled, uses up additional space)

    Shell output during SD card /memory stick operation:
    =========================================

    Disk D mounted
    USB FS device detected
    USB device information ready:
    USB2.0 device with 64 byte pipe
    Vendor/Product = 0x1d0d/0x0213
    Manufacturer = “TDKMedia”
    Product = “CDC ”
    Serial Number = “12345”

    Bus-powered device (max. 200mA) with 1 interface(s)
    Mass Storage Class : Sub-class = 0x06 interface protocol = 0x50
    Endpoints:
    2 = BULK IN with size 64
    2 = BULK OUT with size 64
    3 = INTERRUPT with size 2 (interval = 1ms)
    Enumerated 0 (1)
    LUN = 1
    UFI INQUIRY -> Status transport – Passed
    UFI REQUEST SENSE -> Status transport – Passed
    UFI FORMAT CAP. -> (3:512:16777216) Status transport – Passed
    UFI READ CAP. -> (16785408:1016575) Status transport – Passed
    Mem-Stick mounting…
    Disk E mounted

    Serial number: 00
    Software version V1.4.012
    Device identification: KINETIS

    Main menu
    ===================
    1 Configure LAN interface
    2 Configure serial interface
    3 Go to I/O menu
    4 Go to administration menu
    5 Go to overview/statistics menu
    6 Go to USB menu
    7 Go to I2C menu
    8 Go to utFAT disk interface
    9 FTP/TELNET commands
    a CAN commands
    help Display menu specific help
    quit Leave command mode
    8

    Disk interface
    ===================
    up go to main menu
    info utFAT/card info
    dir [path] show directory content
    dird [path] show deleted directory content
    dirh [path] show hidden content
    infof [path] show file info
    infod [path] show deleted info
    cd [path] change dir. (.. for up)
    comp compare [file1] with [file2]
    file [path] new empty file
    write [path] test write to file
    mkdir new empty dir
    rename [from] [to] rename
    trunc truncate to [length] [path]
    copy [file1] to [file2]
    hide [path] file/dir to hide
    unhide [path] file/dir to un-hide
    prot [path] file/dir to write-protect
    unprot [path] file/dir to un-protect
    print [path] print file content
    del [path] delete file or dir.
    format [-16/12] [label] format (unformatted) disk
    fformat [-16/12] [label] full format (unformatted) disk
    re-format [-16/12] [label] reformat disk!!!!!
    re-fformat [-16/12] [label] full reformat disk!!!!!
    sect [hex no.] display sector
    sectw [hex no.] [offset] [val] [cnt]
    help Display menu specific help
    quit Leave command mode
    >up

    Main menu
    ===================
    1 Configure LAN interface
    2 Configure serial interface
    3 Go to I/O menu
    4 Go to administration menu
    5 Go to overview/statistics menu
    6 Go to USB menu
    7 Go to I2C menu
    8 Go to utFAT disk interface
    9 FTP/TELNET commands
    a CAN commands
    help Display menu specific help
    quit Leave command mode

    #5

    Stats. menu
    ===================
    up go to main menu
    up_time Show operating time
    memory Show memory use
    help Display menu specific help
    quit Leave command mode

    #memory

    System memory use:
    ==================
    Free heap = 0x45a0 from 0x6000
    Unused stack = 113k

    #USB device removed
    Mem-Stick unmounted
    SD-card removed

    Regards

    Mark

    Liked by 1 person

    • Hi Mark,
      thanks for sharing the numbers! I just provided the numbers ‘as is’. The application has a lot of extra stuff in it, from logging to FreeRTOS to trace support to extended shell support. The FatFS has turned on lots extras too.
      So for sure if someones needs to trimm it down, there is a lot of potential.

      Erich

      Like

  3. Just started looking at LPC546xx to use as a device logger to SPI flash which can also be a USB “thumb drive” minic to read data and write new configuration.
    We’ve had this ten years on Microchip but want to update to NXP. Struggling with the examples so far, but hopefully I’ll eventually find enough hints to get started!

    Like

  4. Hey, thanks for this write-up; it has been helpful as I have been investigating the USB capabilities of the K22. I’m in the process of deciding whether we want to move to the K22 from a more expensive TI part, and one deciding feature is the USB stack (we need to support MSD as a host). I have a question for those more knowledgeable about the NXP/MCUXpresso ecosystem.

    I have tested a few of the USB demos in the SDK for a FRDM-K22 board, and was generally happy with what I saw until I found several NOP-based delay loops in the usb_host_khci.c file. The worst offender is a 120ms delay in _USB_HostKhciAttach() (which comes out to about 133ms based on the number of instructions in the core loop @ 120MHz).

    I think this completely disqualifies this stack unless we also move to an RTOS (we use a simpler co-operative scheduler). Without an RTOS I could easily crash the system by repeatedly inserting a USB drive and starving other tasks, and with an RTOS this is an unreliable method of implementing delays.

    Is there something I’m missing here as far as options go, or will I have to look for other drivers if I move to this platform?

    Thanks in advance (and sorry if I sound too negative – I’m just a little bummed about this discovery)

    Like

    • Hi Tom,
      The stack in my view is good for normal usage, but I do not consider it (and other drivers, and as well from other vendors) as the ‘golden code’: I mean it is working, but for sure not the most efficient thing of the world.
      In the past I always have tweaked the USB stacks from all vendors to fit my needs, and so I do here too.
      As for using an RTOS: to me this is kind of mandatory for any real application, especially one doing communication things (which USB is).
      I have not looked yet into the delay with nops you pointed out.

      Like

      • Hi, thanks for the quick response!

        We are currently using a Tiva part with the Tivaware usb library, which works well with the non-preemptive task scheduler we use. They use counters which assume a 1ms call rate to do timing delays, so other tasks can run when it is doing nothing but waiting. All of our code is written to run on a non-preemptive scheduler, and we only really use USB for infrequently-used “support” features, such as reading out logs and doing firmware updates (fatfs file read/write).

        Our scheduler does leave open the possibility of timing issues for long-running tasks, though, and we have run into problems in the past that required things to be broken up into several steps to yield some time to the scheduler. An RTOS would be an improvement in this area, but I will still have to sell it 😉

        Like

        • Hi Tom,
          I had a look at the delays with NOP’s and they are only in case of a device enumeration. If using FreeRTOS you could use a vTaskDelay() instead. For your custom scheduler, you could change the code using a static local variable which acts as a counter/state variable and you would wait the needed time everytime the function gets called until the delay is up.

          Like

What do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.