# USBX
## USBX 的架構總覽
USBX 的架構主要分為 三個層級:
### 1️⃣ 控制器層 (Controller Layer)
硬體操作、暫存器控制、USB Host/Device 硬體驅動
#### 對應的 USBX API:
ux_hcd_stm32_initialize() → HCD 初始化函數 (初始化 STM32 的 USB 硬體)
### 2️⃣ 堆疊層 (Stack Layer)
USB 主機 (Host) 或裝置 (Device) 與應用層之間的「橋樑」,負責處理所有 USB 資料傳輸與裝置管理
#### 堆疊層包含四大核心功能:

#### 對應的 USBX API:
##### 主機模式 (Host Stack)
ux_host_stack_initialize() → 初始化 USB 主機堆疊
ux_host_stack_class_register() → 註冊 USB 類別 (Mass Storage、HID、CDC 等)
ux_host_stack_device_configuration_select() → 選擇設備的 Configuration
ux_host_stack_transfer_request() → 發送 USB 傳輸請求
ux_host_stack_hcd_register() → 註冊 USB 控制器
##### 裝置模式 (Device Stack)
ux_device_stack_initialize() → 初始化 USB 裝置堆疊
ux_device_stack_class_register() → 註冊 USB 裝置類別
ux_device_stack_transfer_request() → 處理 USB 裝置端的傳輸請求
### 3️⃣ 類別層 (Class Layer)
定義 USB 裝置的功能,例如:
Mass Storage (隨身碟)
HID (鍵盤、滑鼠)
CDC/ACM (虛擬序列埠)
Audio (USB 音訊)
#### 對應的 USBX API:
##### 主機模式
ux_host_class_storage_entry() → USB 隨身碟
ux_host_class_hid_entry() → HID 設備 (鍵盤/滑鼠)
ux_host_class_cdc_acm_entry() → 虛擬 COM Port (CDC ACM)
ux_host_class_audio_entry() → USB 音訊
##### 裝置模式
ux_device_class_storage_entry() → USB Mass Storage
ux_device_class_hid_entry() → HID 裝置
ux_device_class_cdc_acm_entry() → CDC/ACM 裝置
:::spoiler app_usbx_host.c
```c=
UINT MX_USBX_Host_Init(VOID *memory_ptr)
{
UINT ret = UX_SUCCESS;
TX_BYTE_POOL *byte_pool = (TX_BYTE_POOL*)memory_ptr;
/* 控制器層 (Controller Layer) - 分配 USBX 內存池 */
UCHAR *pointer;
(void)byte_pool;
/* ===========================
控制器層 (Controller Layer) - 記憶體分配
=========================== */
if (tx_byte_allocate(byte_pool, (VOID **)&pointer, USBX_HOST_MEMORY_STACK_SIZE, TX_NO_WAIT) != TX_SUCCESS)
{
return TX_POOL_ERROR;
}
/* ===========================
堆疊層 (Stack Layer) - 初始化 USBX 記憶體
=========================== */
if (ux_system_initialize(pointer, USBX_HOST_MEMORY_STACK_SIZE, UX_NULL, 0) != UX_SUCCESS)
{
return UX_ERROR;
}
/* ===========================
堆疊層 (Stack Layer) - 初始化 USB 主機堆疊
=========================== */
if (ux_host_stack_initialize(ux_host_event_callback) != UX_SUCCESS)
{
return UX_ERROR;
}
/* 堆疊層 (Stack Layer) - 註冊錯誤回調函數 */
ux_utility_error_callback_register(&ux_host_error_callback);
/* ===========================
類別層 (Class Layer) - 註冊 USB Mass Storage Class
=========================== */
if (ux_host_stack_class_register(_ux_system_host_class_storage_name, ux_host_class_storage_entry) != UX_SUCCESS)
{
return UX_ERROR;
}
/* ===========================
控制器層 (Controller Layer) - 分配 USB 主機應用執行緒的記憶體
=========================== */
if (tx_byte_allocate(byte_pool, (VOID **) &pointer, UX_HOST_APP_THREAD_STACK_SIZE, TX_NO_WAIT) != TX_SUCCESS)
{
return TX_POOL_ERROR;
}
/* ===========================
應用層 (Application Layer) - 創建 USB 主機應用執行緒
=========================== */
if (tx_thread_create(&ux_host_app_thread, "USBX App Host Main Thread", app_ux_host_thread_entry,
0, pointer, UX_HOST_APP_THREAD_STACK_SIZE, 10, 10, TX_NO_TIME_SLICE, TX_AUTO_START) != TX_SUCCESS)
{
return TX_THREAD_ERROR;
}
/* ===========================
控制器層 (Controller Layer) - 分配 USB Mass Storage 應用執行緒的記憶體
=========================== */
if (tx_byte_allocate(byte_pool, (VOID **) &pointer, UX_HOST_APP_THREAD_STACK_SIZE, TX_NO_WAIT) != TX_SUCCESS)
{
return TX_POOL_ERROR;
}
/* ===========================
應用層 (Application Layer) - 創建 USB Mass Storage 應用執行緒
=========================== */
if (tx_thread_create(&msc_app_thread, "MSC App thread", msc_process_thread_entry,0, pointer, UX_HOST_APP_THREAD_STACK_SIZE, 30, 30, 0, TX_AUTO_START) != TX_SUCCESS)
{
return TX_THREAD_ERROR;
}
/* ===========================
應用層 (Application Layer) - 創建事件旗標 (Event Flag)
=========================== */
if (tx_event_flags_create(&ux_app_EventFlag, "Event Flag") != TX_SUCCESS)
{
return TX_GROUP_ERROR;
}
return ret;
}
UINT ux_host_event_callback(ULONG event, UX_HOST_CLASS *current_class, VOID *current_instance)
{
UINT status = UX_SUCCESS;
switch (event)
{
case UX_DEVICE_INSERTION: // 當 USB 設備插入
/* 檢查設備類型是否為 USB Mass Storage */
if (current_class -> ux_host_class_entry_function == ux_host_class_storage_entry)
{
if (storage == UX_NULL) // 確保 storage 變數為 NULL,避免重複初始化
{
/* 取得 USB 存儲設備的實例 */
storage = (UX_HOST_CLASS_STORAGE *)current_instance;
/* 顯示 USB 設備資訊 (PID/VID) */
printf("\nUSB Mass Storage Device Plugged\n");
printf("PID: %#x \n", (UINT)storage -> ux_host_class_storage_device -> ux_device_descriptor.idProduct);
printf("VID: %#x \n", (UINT)storage -> ux_host_class_storage_device -> ux_device_descriptor.idVendor);
/* 取得存儲媒體 (Storage Media) */
storage_media = (UX_HOST_CLASS_STORAGE_MEDIA *)current_class -> ux_host_class_media;
if (storage_media -> ux_host_class_storage_media_lun != 0)
{
storage_media = UX_NULL; // 若 LUN 不為 0,則不支援
}
else
{
/* 取得儲存媒體指標 */
media = &storage_media->ux_host_class_storage_media;
}
/* 確保 USB 存儲設備處於可用狀態 */
if (storage -> ux_host_class_storage_state == UX_HOST_CLASS_INSTANCE_LIVE)
{
/* 設定事件旗標 (Event Flag),通知應用程式 USB 設備已就緒 */
if (tx_event_flags_set(&ux_app_EventFlag, 0x01, TX_OR) != TX_SUCCESS)
{
Error_Handler();
}
}
}
}
break;
case UX_DEVICE_REMOVAL: // 當 USB 設備拔除
/* 確保拔除的設備是當前註冊的存儲設備 */
if ((VOID*)storage == current_instance)
{
/* 清除 USB 設備資訊 */
storage = UX_NULL;
storage_media = UX_NULL;
media = UX_NULL;
printf("\nUSB Mass Storage Device Unplugged");
}
break;
#if defined (UX_HOST_CLASS_STORAGE_NO_FILEX)
case UX_STORAGE_MEDIA_INSERTION: // 存儲媒體插入
break;
case UX_STORAGE_MEDIA_REMOVAL: // 存儲媒體移除
break;
#endif
case UX_DEVICE_CONNECTION: // 設備連接
break;
case UX_DEVICE_DISCONNECTION: // 設備斷線
break;
default:
break;
}
return status;
}
VOID ux_host_error_callback(UINT system_level, UINT system_context, UINT error_code)
{
switch (error_code)
{
case UX_DEVICE_ENUMERATION_FAILURE:
printf("USB Device Enumeration Failure");
break;
case UX_NO_DEVICE_CONNECTED:
printf("USB Device disconnected");
break;
default:
break;
}
}
static VOID app_ux_host_thread_entry(ULONG thread_input)
{
/* 初始化 USBX 主機 (Host) */
USBX_APP_Host_Init();
}
VOID USBX_APP_Host_Init(VOID)
{
UINT status;
/* 初始化低階驅動 (LL Driver) */
MX_USB_OTG_FS_HCD_Init();
/* 註冊 USB 主機控制器 */
status = ux_host_stack_hcd_register(_ux_system_host_hcd_stm32_name,_ux_hcd_stm32_initialize,USB_OTG_FS_PERIPH_BASE,(ULONG)&hhcd_USB_OTG_FS);
/* 檢查 HCD 註冊是否成功 */
if (status != UX_SUCCESS)
{
printf("Error: Host controller registration failed (0x%02X)\n", status);
return;
}
/* 啟動 VBUS,為 USB 設備供電 */
USBH_DriverVBUS(1);
/* 啟動 USB 主機模式 */
status = HAL_HCD_Start(&hhcd_USB_OTG_FS);
if (status != HAL_OK)
{
printf("Error: HAL_HCD_Start failed.\n");
return;
}
/* 顯示啟動訊息 */
printf(" **** USB OTG HS in FS MSC Host **** \n");
printf("USB Host library started.\n");
/* 提示使用者插入 USB 設備 */
printf("Starting MSC Application\n");
printf("Connect your MSC Device\n");
}
void USBH_DriverVBUS(uint8_t state)
{
if (state == 0)
{
/* 使能 Charge Pump,提供 VBUS 電源 */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_SET);
}
else
{
/* 關閉 Charge Pump,關閉 VBUS */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_RESET);
}
/* 等待 200ms,確保 VBUS 穩定 */
HAL_Delay(200);
}
```
:::
### UINT ux_host_stack_initialize
UINT my_callback_function(ULONG event, UX_HOST_CLASS *current_class, VOID *current_instance);


:::spoiler ux_host_msc.c
```c=
#include "app_usbx_host.h"
#include "fx_api.h"
#include "ux_host_class_storage.h"
/* 外部變數,來自其他模組 */
extern TX_EVENT_FLAGS_GROUP ux_app_EventFlag; // USB 事件旗標 (Event Flag)
extern FX_MEDIA *media; // 指向 USB 隨身碟的媒體結構
extern UX_HOST_CLASS_STORAGE *storage; // 指向 USB Mass Storage 設備的實例
/* RAM Disk 設定 */
#define RAM_DISK_SIZE (64 * 512) // 64 個扇區,每個扇區 512 bytes
#define SECTOR_SIZE 512 // 扇區大小
/* 記憶體緩衝區 */
UCHAR m_memory[1024]; // 一般用於 USBX 操作的記憶體
UCHAR ram_disk_memory[RAM_DISK_SIZE]; // 模擬 RAM Disk 的記憶體
/* 函數宣告 */
void msc_process_thread_entry(ULONG arg); // USB Mass Storage 主處理執行緒
void my_media_driver(FX_MEDIA *media); // FileX 媒體驅動 (目前未使用)
void msc_process_thread_entry(ULONG arg)
{
ULONG storage_media_flag = 0;
UINT status;
while (1)
{
/* 等待 STORAGE_MEDIA 旗標,當 USB 裝置插入時觸發 */
if (tx_event_flags_get(&ux_app_EventFlag, 0x01,TX_OR_CLEAR,&storage_media_flag, TX_WAIT_FOREVER) != TX_SUCCESS)
{
printf("發生錯誤:無法等待 USB 事件。\n");
Error_Handler();
}
/* 確保 media 變數不為 NULL,表示 USB 隨身碟已正確偵測 */
if (media != NULL)
{
printf("\n=== 偵測到 USB Mass Storage 裝置 ===\n");
printf("🔹 正在格式化 USB 隨身碟...\n");
/* 格式化 USB 隨身碟 */
status = fx_media_format(media,_ux_host_class_storage_driver_entry, storage,m_memory, sizeof(m_memory), "MY_USB_DISK",1, 32, 0, 64, 512, 8, 1, 1);
if (status != FX_SUCCESS)
{
printf("格式化失敗,錯誤碼: 0x%02X\n", status);
continue; // 如果格式化失敗,則跳過這次循環
}
printf(" 格式化成功!\n");
/* 掛載 USB 隨身碟 */
status = fx_media_open(media, "USB_MEDIA",_ux_host_class_storage_driver_entry,storage, m_memory,sizeof(m_memory));
if (status != FX_SUCCESS)
{
printf(" 開啟媒體失敗,錯誤碼: 0x%02X\n", status);
continue; // 如果仍然無法開啟,則跳過這次循環
}
printf("\n=== 開始檔案操作 ===\n");
/* 測試 */
create_dir(media);
fx_file_create(media, "TEST123.TXT");
fx_directory_default_set(media, "Folder1"); // 設定當前目錄為 Folder1
/* 建立 TEST.TXT 檔案 */
status = App_File_Create(media);
if (status != UX_SUCCESS)
{
printf(" 檔案建立失敗,錯誤碼: 0x%X\n", status);
continue;
}
printf(" 檔案 TEST.TXT 已成功建立!\n");
/* 寫入檔案 */
printf("🔹 開始寫入檔案...\n");
if (App_File_Write(media) == UX_SUCCESS)
{
printf(" 檔案寫入成功!\n");
/* 讀取檔案 */
printf("🔹 開始讀取檔案...\n");
if (App_File_Read(media) == UX_SUCCESS)
{
printf(" 檔案讀取成功!\n");
printf("🔹 關閉檔案。\n");
printf("=== 檔案操作完成 ===\n");
}
else
{
printf(" 檔案讀取失敗!\n");
}
}
else
{
printf(" 檔案寫入失敗!\n");
}
/* 關閉 USB 隨身碟媒體 */
printf("\n=== 關閉 USB 隨身碟 ===\n");
status = fx_media_close(media);
if (status != FX_SUCCESS)
{
printf(" 媒體關閉失敗,錯誤碼: 0x%X\n", status);
}
}
else
{
/* 若 media 仍為 NULL,等待 10ms 再次檢查 */
tx_thread_sleep(10);
}
}
}
```
:::
fx_directory_create():建立資料夾
fx_directory_default_set():設定 FileX 當前的「工作目錄 (Default Directory)」
:::spoiler app_filex.c
```c=
#include "app_filex.h"
/* 用於讀取檔案的緩衝區 */
static UCHAR Read_buffer[100];
/* 用於寫入檔案的字串 */
static UCHAR Write_buffer[] = "USBX_STM32_Host_Mass_Storage";
UINT MX_FileX_Init(VOID *memory_ptr)
{
UINT ret = FX_SUCCESS;
TX_BYTE_POOL *byte_pool = (TX_BYTE_POOL*)memory_ptr;
/* 避免未使用變數警告 */
(void)byte_pool;
/* 初始化 FileX 系統 */
fx_system_initialize();
return ret;
}
UINT App_File_Write(FX_MEDIA *fx_media)
{
FX_FILE my_file;
UINT status;
/* 開啟 "TEST.TXT" 檔案,設定為可寫入模式 */
status = fx_file_open(fx_media, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE);
if (status == FX_SUCCESS)
{
/* 移動檔案指標至開頭 */
fx_file_seek(&my_file, 0);
/* 將 Write_buffer 內容寫入檔案 */
status = fx_file_write(&my_file, Write_buffer, sizeof(Write_buffer));
if (status == FX_SUCCESS)
{
/* 寫入成功後關閉檔案 */
fx_file_close(&my_file);
/* 刷新媒體,確保資料寫入儲存裝置 */
fx_media_flush(fx_media);
}
else
{
/* 如果寫入失敗,關閉檔案並刪除錯誤檔案 */
fx_file_close(&my_file);
fx_file_delete(fx_media, "TEST.TXT");
}
}
return status;
}
UINT App_File_Read(FX_MEDIA *fx_media)
{
FX_FILE my_file;
ULONG requested_length;
UINT file_attribute;
UINT status;
/* 讀取 "TEST.TXT" 檔案的屬性 */
status = fx_file_attributes_read(fx_media, "TEST.TXT", &file_attribute);
if (status == FX_SUCCESS)
{
/* 確保該檔案不是目錄 */
if ((file_attribute & (FX_DIRECTORY | FX_VOLUME)) == 0U)
{
/* 開啟 "TEST.TXT" 檔案,設定為可讀取模式 */
status = fx_file_open(fx_media, &my_file, "TEST.TXT", FX_OPEN_FOR_READ);
if (status == FX_SUCCESS)
{
/* 移動檔案指標至開頭 */
fx_file_seek(&my_file, 0);
/* 讀取檔案內容到 Read_buffer */
status = fx_file_read(&my_file, Read_buffer, sizeof(Read_buffer), &requested_length);
if (status == FX_SUCCESS)
{
/* 比對讀取的內容是否與原先寫入的內容一致 */
status = ux_utility_memory_compare(Write_buffer, Read_buffer, sizeof(Write_buffer));
}
/* 讀取完成後關閉檔案 */
fx_file_close(&my_file);
}
}
}
return status;
}
UINT App_File_Create(FX_MEDIA *fx_media)
{
UINT status;
/* 在根目錄中建立 "TEST.TXT" 檔案 */
status = fx_file_create(fx_media, "TEST.TXT");
/* 如果檔案已存在,則回傳成功 */
if (status == FX_ALREADY_CREATED)
{
status = FX_SUCCESS;
}
return status;
}
void create_dir(FX_MEDIA *media)
{
FX_FILE my_file;
if (fx_directory_create(media, "Folder1") != FX_SUCCESS)
printf("Failed to create 'Folder1'\n");
if (fx_directory_create(media, "Folder1/Folder2") != FX_SUCCESS)
printf("Failed to create 'Folder1/Folder2'\n");
if (fx_file_create(media, "Folder1/Folder2/file666.txt") == FX_SUCCESS &&
fx_file_open(media, &my_file, "Folder1/Folder2/file666.txt", FX_OPEN_FOR_WRITE) == FX_SUCCESS)
{
fx_file_write(&my_file, "I did it!", 9);
fx_file_close(&my_file);
}
else
{
printf("Failed to create/write 'Folder1/Folder2/file666.txt'\n");
}
}
```
:::