cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 USBX Device and mount/eject SD card

DZimm.9
Associate II

Hi ST community

// --- EDIT --- Missed preview and made some corrections / clarifications

I'm looking for a way to mount a SD card using the Azure RTOS UsbX MSC class, while the MCU is running. The code is mostly based on the ST examples, e.g.:

https://github.com/STMicroelectronics/x-cube-azrtos-h7/tree/main/Projects/STM32H735G-DK/Applications/USBX/Ux_Device_MSC 

The example expects that the SD card is already inserted on power up - therefore the MX_SDMMC1_SD_Init() is called before the USBX device is registered. Therefore the LBA and Blocksize values are already available when the USBX device is registered.

I want to have a more general case, where I can remove and insert a SD card while the application is running. A separate thread (SD_thread_entry) which handles this behaviour and is waiting for the SD card pin to be detected. The card is then power reset, the HAL is initialized and the CardInfo read:

void SD_thread_entry(ULONG thread_input)
{
    // Thread variables
    ULONG flags;
    bool isCardOk;
    uint32_t sdCardstart;

    // Thread init
    if(GPIO_SD_isCardPresent()) {
        tx_event_flags_set(&eventSd, EVT_CARD_INSERTED, TX_OR);
    }

    // Start of thread loop
    while (true)
    {
        // Wait until card is detected
        tx_event_flags_get(&eventSd, EVT_CARD_INSERTED, TX_OR_CLEAR, &flags, TX_WAIT_FOREVER);

        // Setup SD card
        // --------------------------------------------------------

        // Power off SD card
        printf("SD card power reset.\n");
        GPIO_SD_powerEnable(false);
        tx_thread_sleep(TX_TIMER_TICKS_PER_SECOND / 2); // wait 500ms

        // Power on SD card
        GPIO_SD_powerEnable(true);
        tx_thread_sleep(TX_TIMER_TICKS_PER_SECOND / 2); // wait 500ms

        // Check that card is still present
        if (!GPIO_SD_isCardPresent())
        {
            continue;
        }

        // Init SD card
        printf("SD card hardware init.\n");
        MX_SDMMC1_SD_Init();

        // Check card status
        isCardOk = false;
        sdCardstart = tx_time_get();
        while (tx_time_get() - sdCardstart < 2000 && GPIO_SD_isCardPresent())
        {
            // fetch card state
            HAL_SD_CardStateTypeDef state = HAL_SD_GetCardState(&hsd1);
            if (state == HAL_SD_CARD_TRANSFER)
            {
                isCardOk = true;
                break;
            } else {
                printf("SD card state: %lu, present: %d\n", state, GPIO_SD_isCardPresent());
            }

            // not yet ready - sleep 100ms
            tx_thread_sleep(TX_TIMER_TICKS_PER_SECOND / 10);
        }

        if(!isCardOk) {
            // Card error - TODO handle error
            printf("SD card state failed\n");
            continue;
        }

        // Get card info
        if (HAL_SD_GetCardInfo(&hsd1, &SD_CardInfo) != HAL_OK)
        {
            // Card info error - TODO handle error
            printf("SD card info failed\n");
            continue;
        }

        // Update LBA and block size
        USBX_updateSdParameters();

        // Flag card ready
        tx_event_flags_set(&eventSd, EVT_CARD_READY, TX_OR);
        printf("SD card ready.\n");

        // Card ready - wait for write requests or card removed events
        while (true)
        {
            tx_event_flags_get(&eventSd, EVT_CARD_REMOVED, TX_OR_CLEAR, &flags, TX_WAIT_FOREVER);

            if (flags & EVT_CARD_REMOVED)
            {
                break;
            }

            // perform SD card tasks
        }

        // Clear SD flags
        tx_event_flags_get(&eventSd,
            EVT_CARD_READY | EVT_READ_DONE | EVT_WRITE_DONE, TX_OR_CLEAR, &flags, TX_NO_WAIT);

        printf("SD card not ready - waiting for insertion.\n");

        // Deinit SD card
        MX_SDMMC1_SD_DeInit();
    }
}

Only once the SD card is mounted, the blocksize and LBA are known. Therefore the following function is used to update the UsbX storage parameters:

VOID USBX_updateSdParameters() {
    /* Initialize the storage class parameters for reading/writing to the Flash Disk */
    storage_parameter.ux_slave_class_storage_parameter_lun[0].
      ux_slave_class_storage_media_last_lba = USBD_STORAGE_GetMediaLastLba();

    storage_parameter.ux_slave_class_storage_parameter_lun[0].
      ux_slave_class_storage_media_block_length = USBD_STORAGE_GetMediaBlocklength();
}

Furthermore, I implemented a simple media_status function, which just reports NO_SENSE (for card detected) or NOT_READY (for card not detected):

UINT USBD_STORAGE_Status(VOID *storage_instance, ULONG lun, ULONG media_id,
                         ULONG *media_status)
{
  UINT status = UX_SUCCESS;

  /* USER CODE BEGIN USBD_STORAGE_Status */
  UX_PARAMETER_NOT_USED(storage_instance);
  UX_PARAMETER_NOT_USED(lun);
  UX_PARAMETER_NOT_USED(media_id);

  // overwrite default success status
  status = UX_ERROR;

  // only report SD card if SD thread is ready
  if (SD_isCardReady())
  {
      *media_status = UX_DEVICE_CLASS_STORAGE_SENSE_STATUS(UX_SLAVE_CLASS_STORAGE_SENSE_KEY_NO_SENSE, 0x00, 0x00);
      return UX_SUCCESS; // media present
  }
  else
  {
      *media_status = UX_DEVICE_CLASS_STORAGE_SENSE_STATUS(UX_SLAVE_CLASS_STORAGE_SENSE_KEY_NOT_READY, 0x3A, 0x00);
      return UX_ERROR;   // media absent
  }
  /* USER CODE END USBD_STORAGE_Status */

  return status;
}

Each read/write/flush checks that the media status is correct or otherwise reports an UX_Error if not ready:

UINT USBD_STORAGE_Write(VOID *storage_instance, ULONG lun, UCHAR *data_pointer,
                        ULONG number_blocks, ULONG lba, ULONG *media_status)
{
  UINT status = UX_SUCCESS;

  /* USER CODE BEGIN USBD_STORAGE_Write */
  if(UX_SUCCESS != USBD_STORAGE_Status(storage_instance, lun, 0, media_status)) {
      return UX_ERROR;
  }

  status = SD_writeBlocks(data_pointer, number_blocks, lba);

  /* USER CODE END USBD_STORAGE_Write */

  return status;
}

Is this the correct way to mount a SD card while the application is already running?

On a side note: I'm using a composite class of ACM (Virtual com port) and MSC (mass storage). Starting first with a ACM only class and then registering an addtional class later seems also not to work, probably because the enumeration of the device is already complete.

Unfortunately, the rtos docs regarding the media_status are not that clear and I don't really understand what to return there:

https://github.com/eclipse-threadx/rtos-docs/blob/main/rtos-docs/usbx/usbx-device-stack-5.md#usb_device_storage_class 

Thanks in advance and best regards

Daniel

 

0 REPLIES 0