2025-10-13 7:35 AM - edited 2025-10-13 7:43 AM
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.:
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:
Thanks in advance and best regards
Daniel