【STM32U385RG 测评】7、USB DEVICE MSC设备(RAM模拟U盘)
本帖最后由 sujingliang 于 2025-7-21 13:25 编辑STM32U385RG采用ThreadX+USBX实现对USB设备驱动。STM32U385RG提供的示例中没有device msc的设备示例,本文利用ThreadX+USBX将RAM虚拟为U盘。
一、STM32CuteMX配置
1、RCC
2、时钟图
3、SYS
4、USB
5、THREADX
勾选core,缺省配置
6、USBX
7、Project Manager->Advanced Settings
生成代码
二、代码修改
1、app_usbx_device.c
增加定义
extern PCD_HandleTypeDef hpcd_USB_DRD_FS;
增加一个函数
VOID USBX_APP_Device_Init(VOID)
{
/* 初始化 USB 控制器 */
MX_USB_PCD_Init();
/* 配置 PMA */
HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, 0x00, PCD_SNG_BUF, 0x14);
HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, 0x80, PCD_SNG_BUF, 0x54);
HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, USBD_MSC_EPOUT_ADDR, PCD_SNG_BUF, 0x94);
HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, USBD_MSC_EPIN_ADDR, PCD_SNG_BUF, 0xD4);
/* 绑定 USBX 和 STM32 HAL */
_ux_dcd_stm32_initialize((ULONG)USB_DRD_FS, (ULONG)&hpcd_USB_DRD_FS);
/* 启动 USB 连接 */
HAL_PCD_Start(&hpcd_USB_DRD_FS);
}该函数主要用于初始化 STM32 的 USB 设备控制器,并配置 USBX(Azure RTOS USBX 协议栈)与硬件层的交互
MX_USB_PCD_Init()
作用:初始化 USB 外设控制器(如 USB DRD FS/HS),通过 STM32 HAL 库配置时钟、中断、GPIO 等。
HAL_PCDEx_PMAConfig()
作用:配置 USB 端点的 Packet Memory Area (PMA),即 USB 数据缓冲区在 STM32 内部的物理内存布局。
_ux_dcd_stm32_initialize()
作用:将 STM32 的 USB 控制器绑定到 USBX 协议栈。
在app_ux_device_thread_entry中调用USBX_APP_Device_Init()
static VOID app_ux_device_thread_entry(ULONG thread_input)
{
/* USER CODE BEGIN app_ux_device_thread_entry */
TX_PARAMETER_NOT_USED(thread_input);
USBX_APP_Device_Init();
/* USER CODE END app_ux_device_thread_entry */
}
2、ux_device_msc.c
storage_media内容来自tinyUsb,原内容没有改变
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file ux_device_msc.c
* @authorMCD Application Team
* @brief USBX Device applicative file
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "ux_device_msc.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define STORAGE_MEDIA_SECTOR_COUNT 16
#define STORAGE_MEDIA_SECTOR_SIZE 512
static ULONG storage_media_status = 0;
#define README_CONTENTS \
"This is tinyusb's MassStorage Class demo.\r\n\r\n\
If you find any bugs or get any questions, feel free to file an\r\n\
issue at github.com/hathach/tinyusb"
static UCHAR storage_media
=
{
//------------- Block0: Boot Sector -------------//
// byte_per_sector = DISK_BLOCK_SIZE; fat12_sector_num_16= DISK_BLOCK_NUM;
// sector_per_cluster = 1; reserved_sectors = 1;
// fat_num = 1; fat12_root_entry_num = 16;
// sector_per_fat = 1; sector_per_track = 1; head_num = 1; hidden_sectors = 0;
// drive_number = 0x80; media_type = 0xf8; extended_boot_signature = 0x29;
// filesystem_type = "FAT12 "; volume_serial_number = 0x1234; volume_label = "TinyUSB MSC";
// FAT magic code at offset 510-511
{
0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x01, 0x01, 0x00,
0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'T' , 'i' , 'n' , 'y' , 'U' ,
'S' , 'B' , ' ' , 'M' , 'S' , 'C' , 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00,
// Zero up to 2 last bytes of FAT magic code
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA
},
//------------- Block1: FAT12 Table -------------//
{
0xF8, 0xFF, 0xFF, 0xFF, 0x0F // // first 2 entries must be F8FF, third entry is cluster end of readme file
},
//------------- Block2: Root Directory -------------//
{
// first entry is volume label
'T' , 'i' , 'n' , 'y' , 'U' , 'S' , 'B' , ' ' , 'M' , 'S' , 'C' , 0x08, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// second entry is readme file
'R' , 'E' , 'A' , 'D' , 'M' , 'E' , ' ' , ' ' , 'T' , 'X' , 'T' , 0x20, 0x00, 0xC6, 0x52, 0x6D,
0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43, 0x02, 0x00,
sizeof(README_CONTENTS)-1, 0x00, 0x00, 0x00 // readme's files size (4 Bytes)
},
//------------- Block3: Readme Content -------------//
README_CONTENTS
};
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
VOID USBD_STORAGE_Init(VOID)
{
memset(storage_media, 0, sizeof(storage_media));
storage_media_status = 0;
}
/* USER CODE END 0 */
/**
* @briefUSBD_STORAGE_Activate
* This function is called when insertion of a storage device.
* @paramstorage_instance: Pointer to the storage class instance.
* @retval none
*/
VOID USBD_STORAGE_Activate(VOID *storage_instance)
{
/* USER CODE BEGIN USBD_STORAGE_Activate */
UX_PARAMETER_NOT_USED(storage_instance);
storage_media_status = 0;
/* USER CODE END USBD_STORAGE_Activate */
return;
}
/**
* @briefUSBD_STORAGE_Deactivate
* This function is called when extraction of a storage device.
* @paramstorage_instance: Pointer to the storage class instance.
* @retval none
*/
VOID USBD_STORAGE_Deactivate(VOID *storage_instance)
{
/* USER CODE BEGIN USBD_STORAGE_Deactivate*/
UX_PARAMETER_NOT_USED(storage_instance);
storage_media_status = 0xff;
/* USER CODE END USBD_STORAGE_Deactivate */
return;
}
/**
* @briefUSBD_STORAGE_Read
* This function is invoked to read from media.
* @paramstorage_instance : Pointer to the storage class instance.
* @paramlun: Logical unit number is the command is directed to.
* @paramdata_pointer: Address of the buffer to be used for reading or writing.
* @paramnumber_blocks: number of sectors to read/write.
* @paramlba: Logical block address is the sector address to read.
* @parammedia_status: should be filled out exactly like the media status
* callback return value.
* @retval status
*/
UINT USBD_STORAGE_Read(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_Read */
UX_PARAMETER_NOT_USED(storage_instance);
UX_PARAMETER_NOT_USED(lun);
UX_PARAMETER_NOT_USED(data_pointer);
UX_PARAMETER_NOT_USED(number_blocks);
UX_PARAMETER_NOT_USED(lba);
UX_PARAMETER_NOT_USED(media_status);
if ((lba + number_blocks) > STORAGE_MEDIA_SECTOR_COUNT)
{
*media_status = 0;
return UX_ERROR;
}
memcpy(data_pointer,
&storage_media,
number_blocks * STORAGE_MEDIA_SECTOR_SIZE);
*media_status = 0;
/* USER CODE END USBD_STORAGE_Read */
return status;
}
/**
* @briefUSBD_STORAGE_Write
* This function is invoked to write in media.
* @paramstorage_instance : Pointer to the storage class instance.
* @paramlun: Logical unit number is the command is directed to.
* @paramdata_pointer: Address of the buffer to be used for reading or writing.
* @paramnumber_blocks: number of sectors to read/write.
* @paramlba: Logical block address is the sector address to read.
* @parammedia_status: should be filled out exactly like the media status
* callback return value.
* @retval status
*/
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 */
UX_PARAMETER_NOT_USED(storage_instance);
UX_PARAMETER_NOT_USED(lun);
UX_PARAMETER_NOT_USED(data_pointer);
UX_PARAMETER_NOT_USED(number_blocks);
UX_PARAMETER_NOT_USED(lba);
UX_PARAMETER_NOT_USED(media_status);
if ((lba + number_blocks) > STORAGE_MEDIA_SECTOR_COUNT)
{
*media_status = 0;
return UX_ERROR;
}
memcpy(&storage_media,
data_pointer,
number_blocks * STORAGE_MEDIA_SECTOR_SIZE);
*media_status = 0;
/* USER CODE END USBD_STORAGE_Write */
return status;
}
/**
* @briefUSBD_STORAGE_Flush
* This function is invoked to flush media.
* @paramstorage_instance : Pointer to the storage class instance.
* @paramlun: Logical unit number is the command is directed to.
* @paramnumber_blocks: number of sectors to read/write.
* @paramlba: Logical block address is the sector address to read.
* @parammedia_status: should be filled out exactly like the media status
* callback return value.
* @retval status
*/
UINT USBD_STORAGE_Flush(VOID *storage_instance, ULONG lun, ULONG number_blocks,
ULONG lba, ULONG *media_status)
{
UINT status = UX_SUCCESS;
/* USER CODE BEGIN USBD_STORAGE_Flush */
UX_PARAMETER_NOT_USED(storage_instance);
UX_PARAMETER_NOT_USED(lun);
UX_PARAMETER_NOT_USED(number_blocks);
UX_PARAMETER_NOT_USED(lba);
UX_PARAMETER_NOT_USED(media_status);
*media_status = 0;
/* USER CODE END USBD_STORAGE_Flush */
return status;
}
/**
* @briefUSBD_STORAGE_Status
* This function is invoked to obtain the status of the device.
* @paramstorage_instance : Pointer to the storage class instance.
* @paramlun: Logical unit number is the command is directed to.
* @parammedia_id: is not currently used.
* @parammedia_status: should be filled out exactly like the media status
* callback return value.
* @retval status
*/
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);
UX_PARAMETER_NOT_USED(media_status);
*media_status = storage_media_status;
/* USER CODE END USBD_STORAGE_Status */
return status;
}
/**
* @briefUSBD_STORAGE_Notification
* This function is invoked to obtain the notification of the device.
* @paramstorage_instance : Pointer to the storage class instance.
* @paramlun: Logical unit number is the command is directed to.
* @parammedia_id: is not currently used.
* @paramnotification_class: specifies the class of notification.
* @parammedia_notification: response for the notification.
* @parammedia_notification_length: length of the response buffer.
* @retval status
*/
UINT USBD_STORAGE_Notification(VOID *storage_instance, ULONG lun, ULONG media_id,
ULONG notification_class, UCHAR **media_notification,
ULONG *media_notification_length)
{
UINT status = UX_SUCCESS;
/* USER CODE BEGIN USBD_STORAGE_Notification */
UX_PARAMETER_NOT_USED(storage_instance);
UX_PARAMETER_NOT_USED(lun);
UX_PARAMETER_NOT_USED(media_id);
UX_PARAMETER_NOT_USED(notification_class);
UX_PARAMETER_NOT_USED(media_notification);
UX_PARAMETER_NOT_USED(media_notification_length);
/* USER CODE END USBD_STORAGE_Notification */
return status;
}
/**
* @briefUSBD_STORAGE_GetMediaLastLba
* Get Media last LBA.
* @paramnone
* @retval last lba
*/
ULONG USBD_STORAGE_GetMediaLastLba(VOID)
{
ULONG LastLba = 0U;
/* USER CODE BEGIN USBD_STORAGE_GetMediaLastLba */
return STORAGE_MEDIA_SECTOR_COUNT - 1;
/* USER CODE END USBD_STORAGE_GetMediaLastLba */
return LastLba;
}
/**
* @briefUSBD_STORAGE_GetMediaBlocklength
* Get Media block length.
* @paramnone.
* @retval block length.
*/
ULONG USBD_STORAGE_GetMediaBlocklength(VOID)
{
ULONG MediaBlockLen = 0U;
/* USER CODE BEGIN USBD_STORAGE_GetMediaBlocklength */
MediaBlockLen= STORAGE_MEDIA_SECTOR_SIZE;
/* USER CODE END USBD_STORAGE_GetMediaBlocklength */
return MediaBlockLen;
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
三、运行效果
USB USER接入PC,识别出USB Device磁盘设备
USB控制器中增加了USB大容量存储设备
系统多出一个盘符
打开U盘,可以查看README.TXT内容,也可以新建或COPY文件到U盘
四、源码
https://gitee.com/sujingliang/stm32u385/tree/master/MSC_test
这个方法可以,学习下,看起来有点难度,慢慢理解。 很多选项不知道啥意思。 可以通过配置 USBX 的 MSC设备模式,将片内 RAM 模拟为存储介质 大佬,你好!我目前使用 STM32U5xx ,想开发 USB DEVICE HS MSC设备(eMMC模拟U盘)
目前还有些问题,可以请教下你吗(有偿)?
我的联系电话 17620355576(微信同号)
感谢!
页:
[1]