手边有一个项目需要通过TF卡存储日志,同时不方便频繁插拔TF卡使用读卡器浏览。因而想出通过USB插入的事件以动态热切换“本机读写”和“暴露LUN给USBMSC”两个模式。

两个模式本身的实现方法本文不再赘述。

具体实现思路为:两个模式属于竞争关系,使用FreeRTOS的锁绑定TF卡资源,一个Task进行本机读写,一个Task模拟USBMSC的占用,在模式切换时,根据需求配置锁的归属。

使用OTG_FS_IRQHandler获取USB状态,hUsbDeviceFS.dev_state表示当前USB设备的状态,当其改变时通过消息队列发布。
USB任务捕获队列信息,切换状态,申请或者释放TF卡设备锁。
日志任务判断当前状态,申请或者释放TF卡设备锁。申请后/释放前需重新挂载或卸载文件系统。

相关代码:

void storage_usb_state_check(void) {
    static uint8_t last_state = 0xFF;
    uint8_t state = hUsbDeviceFS.dev_state;

    if ((last_state != state) && (usbStateQueueHandle != NULL)) {
        if (osMessagePut(usbStateQueueHandle, (uint32_t)state, 0) == osOK) {
            last_state = state;
        }
    }
}

stm32h7xx_it.c

void OTG_FS_IRQHandler(void)
{
  /* USER CODE BEGIN OTG_FS_IRQn 0 */

  /* USER CODE END OTG_FS_IRQn 0 */
  HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS);
  /* USER CODE BEGIN OTG_FS_IRQn 1 */
  storage_usb_state_check();
  /* USER CODE END OTG_FS_IRQn 1 */
}

usb_mgr_task.c

// ...

extern osMessageQId usbStateQueueHandle;

extern osMutexId sdCardMutexHandle;

void StartUSBMgrTask(void const * argument)
{
    osEvent event;
    uint8_t sd_mutex = 0;
    storage_usb_on = 0;
    storage_sd_on = 1;

    for (;;) {
        event = osMessageGet(usbStateQueueHandle, osWaitForever);
        
        if (event.status == osEventMessage) {
            uint8_t usb_dev_state = (uint8_t)event.value.v;

            switch (usb_dev_state)
            {
            case USBD_STATE_ADDRESSED:
//            case USBD_STATE_CONFIGURED:
                if(!sd_mutex)
                {
                    storage_sd_on = 0;

                    osMutexWait(sdCardMutexHandle, osWaitForever);
                    sd_mutex = 1;
                    storage_usb_on = 1;
                }
                break;

            case USBD_STATE_DEFAULT:
            case USBD_STATE_SUSPENDED:
                if (sd_mutex){
                    storage_usb_on = 0;

                    osMutexRelease(sdCardMutexHandle);
                    sd_mutex = 0;
                    storage_sd_on = 1;
                }
                break;
            }



        }
    }
}

logger_task.c

// ...

void StartLoggerTask(void const *argument)
{
    uint8_t sd_mutex = 0;

    vTaskDelay(pdMS_TO_TICKS(1000));

    TickType_t xLastWakeTime = xTaskGetTickCount();
    TickType_t xPeriod = pdMS_TO_TICKS(10);
    for (;;)
    {
        if (storage_sd_on)
        {
            if (!sd_mutex){
                if(osMutexWait(sdCardMutexHandle, 0) != osOK){
                    continue;
                }
                sd_mutex = 1;
            }

            storage_mount();
            // ...
        }
        else
        {
            if (sd_mutex){
                storage_unmount();
                osMutexRelease(sdCardMutexHandle);
                sd_mutex = 0;
            }
        }

        vTaskDelayUntil(&xLastWakeTime, xPeriod);
    }
}
富婆饿饿饭饭