教程更新中:http://www.armbbs.cn/forum.php?mod=viewthread&tid=108546
第3章 ThreadX USBX協議棧移植到STM32H7
本章節為大家講解USBX協議棧移植到STM32H7。
3.1 初學者重要提示
3.2 USBX移植步驟
3.3 USBX的模擬U盤移植接口文件ux_device_msc.c。
3.4 使用的MicroUSB接口並注意跳線帽設置
3.5 實驗例程
3.6 總結
3.1 初學者重要提示
1、 本章使用的ST專門為STM32H7提供的軟件包:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=108816
2、 本章配套例子使用SD卡模擬一個U盤,使用的MicroUSB接口。
3.2 USBX移植步驟
ThreadX USBX的移植步驟如下:
3.2.1 第1步,了解整體設計框架
為了方便大家移植,需要大家先對移植好的工程有個整體認識:
3.2.2 第2步,添加USBX和USB驅動到工程
這里我們在FileX教程做的模板例子基礎上添加USBX文件和USB驅動文件,大家可以直接從本章教程提供的例子里面復制。
- 模擬U盤驅動文件ux_device_msc.c/.h和ux_device_descriptors.c/.h添加到自己的工程里面,路徑不限。
配套例子是放在\User\usb文件。
- USB驅動文件stm32h7xx_hal_hcd.c,stm32h7xx_hal_pcd.c,stm32h7xx_hal_pcd_ex.c和stm32h7xx_ll_usb.c。
這個是STM32H7的HAL庫自帶的。
- USBX相關源文件。
大家可以將所有相關文件都復制到自己的工程里面,配套例子是放在\USBX。
3.2.3 第3步,添加工程路徑
大家根據自己添加的源文件位置,添加相關路徑即可:
3.2.4 第4步,禁止掉添加進來一些文件
之所以要禁止掉是因為這些文件要用到NetXDUO網絡協議棧,或者大家不添加進來都是可以的。添加進來后再禁止的優勢是添加時候可以全選添加。
禁止的方法是右擊此文件去掉如下對鈎即可:
3.2.5 第4步,配置GPIO和時鍾
USB時鍾配置在bsp.c文件的函數SystemClock_Config里面:
{ /* USB工作需要48MHz的時鍾,可以由PLL1Q,PLL3Q和HSI48提供 PLL1Q用於給SDMMC提供時鍾 PLL3Q給LTDC提供時鍾,也可以跟USB共用,不過得更USB設置相同的頻率才可一起用。 HSI48可以供USB獨享,就是精度不是很高。 */ RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; #if 0 /* PLL3-Q for USB Clock = 48M */ PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB; PeriphClkInitStruct.PLL3.PLL3M = 5; PeriphClkInitStruct.PLL3.PLL3N = 48; PeriphClkInitStruct.PLL3.PLL3P = 2; PeriphClkInitStruct.PLL3.PLL3Q = 5; PeriphClkInitStruct.PLL3.PLL3R = 2; PeriphClkInitStruct.PLL3.PLL3RGE = RCC_PLL3VCIRANGE_2; PeriphClkInitStruct.PLL3.PLL3VCOSEL = RCC_PLL3VCOWIDE; PeriphClkInitStruct.PLL3.PLL3FRACN = 0; PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_PLL3; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct)!= HAL_OK) { Error_Handler(__FILE__, __LINE__); } #else PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB; PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_HSI48; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { Error_Handler(__FILE__, __LINE__); } HAL_PWREx_EnableUSBVoltageDetector(); #endif }
USB的GPIO,NVIC初始化在demo_sd_usbx.c:
/* 配置USB GPIO, NVIC */ { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = (GPIO_PIN_11 | GPIO_PIN_12); GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF10_OTG1_FS; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); /* 使能USB FS時鍾 */ __HAL_RCC_USB2_OTG_FS_CLK_ENABLE(); /* 在睡眠模式下禁用USB時鍾 */ __HAL_RCC_USB2_OTG_FS_ULPI_CLK_SLEEP_DISABLE(); /* 配置USB FS中斷 */ HAL_NVIC_SetPriority(OTG_FS_IRQn, 0x2, 0); HAL_NVIC_EnableIRQ(OTG_FS_IRQn); } /* 初始化USB */ { HAL_PWREx_EnableUSBVoltageDetector(); memset(&hpcd_USB_OTG_FS, 0x0, sizeof(PCD_HandleTypeDef)); hpcd_USB_OTG_FS.Instance = USB2_OTG_FS; hpcd_USB_OTG_FS.Init.dev_endpoints = 8; hpcd_USB_OTG_FS.Init.use_dedicated_ep1 = 0; hpcd_USB_OTG_FS.Init.ep0_mps = 0x40; hpcd_USB_OTG_FS.Init.low_power_enable = 0; hpcd_USB_OTG_FS.Init.phy_itface = PCD_PHY_EMBEDDED; hpcd_USB_OTG_FS.Init.Sof_enable = 0; hpcd_USB_OTG_FS.Init.speed = PCD_SPEED_FULL; hpcd_USB_OTG_FS.Init.vbus_sensing_enable = 0; hpcd_USB_OTG_FS.Init.lpm_enable = 0; /* 初始化USB */ HAL_PCD_Init(&hpcd_USB_OTG_FS); /* 設置TX FIFO和RX FIFO */ HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_FS, 1024); HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 0, 64); HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 1024); /* 注冊STM32到USBX協議棧並初始化 */ status = ux_dcd_stm32_initialize((ULONG)USB2_OTG_FS, (ULONG)&hpcd_USB_OTG_FS); if (status != FX_SUCCESS) { return; } }
3.2.6 第5步,MPU配置
為了方便大家移植測試,我們這里直接關閉AXI SRAM的讀Cache和寫Cache(這樣就跟使用STM32F1或者STM32F4系列里面的SRAM一樣)。此配置是在bsp.c文件的MPU_Config函數里面實現:
/* ********************************************************************************************************* * 函 數 名: MPU_Config * 功能說明: 配置MPU * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void MPU_Config( void ) { MPU_Region_InitTypeDef MPU_InitStruct; /* 禁止 MPU */ HAL_MPU_Disable(); #if 0 /* 配置AXI SRAM的MPU屬性為Write back, Read allocate,Write allocate */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x24000000; MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); #else /* 當前是采用下面的配置 */ /* 配置AXI SRAM的MPU屬性為NORMAL, NO Read allocate,NO Write allocate */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x24000000; MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); #endif /* 配置FMC擴展IO的MPU屬性為Device或者Strongly Ordered */ MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress = 0x60000000; MPU_InitStruct.Size = ARM_MPU_REGION_SIZE_64KB; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER1; MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.SubRegionDisable = 0x00; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); /*使能 MPU */ HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); }
3.2.7 第7步,添加應用代碼(USB中斷,打開U盤和關閉U盤)
USB操作專門整理到了文件demo_sd_usbx.c。主要三部分,打開U盤,關閉U盤和USB中斷服務程序。這三個函數基本是通用的,大家直接復制粘貼使用即可
特別注意USB中斷服務程序別忘了添加。
3.3 USBX的模擬U盤移植接口文件ux_device_msc.c說明
這里將USBX的底層接口文件ux_device_msc.c的實現為大家簡單做個說明。
3.3.1 狀態函數app_usb_device_thread_media_status
代碼如下:
UINT app_usb_device_thread_media_status(VOID *storage, ULONG lun, ULONG media_id, ULONG *media_status) { /* The ATA drive never fails. This is just for app_usb_device only !!!! */ return (UX_SUCCESS); }
此函數主要用於獲取SD卡模擬U盤的狀態。
3.3.2 讀取函數app_usb_device_thread_media_read
代碼如下:
/** * @brief Function implementing app_usb_device_thread_media_read. * @param storage : Not used * @param lun: Logical unit number * @param lba: Logical block address * @param number_blocks: Blocks number * @param data_pointer: Data * @param media_status: Not used * @retval Status (0 : OK / -1 : Error) */ UINT app_usb_device_thread_media_read(VOID *storage, ULONG lun, UCHAR *data_pointer, ULONG number_blocks, ULONG lba, ULONG *media_status) { UINT status = 0U; BSP_SD_ReadBlocks((uint32_t *) data_pointer, lba, number_blocks, 500); //while(BSP_SD_GetCardState() != SD_TRANSFER_OK); status = check_sd_status(0); return (status); }
用於實現SD模擬U盤的讀取功能。
3.3.3 寫入函數app_usb_device_thread_media_write
代碼如下:
/** * @brief Function implementing app_usb_device_thread_media_write. * @param storage : Not used * @param lun: Logical unit number * @param lba: Logical block address * @param number_blocks: Blocks number * @param data_pointer: Data * @param media_status: Not used * @retval Status (0 : OK / -1 : Error) */ UINT app_usb_device_thread_media_write(VOID *storage, ULONG lun, UCHAR *data_pointer, ULONG number_blocks, ULONG lba, ULONG *media_status) { UINT status = 0U; BSP_SD_WriteBlocks((uint32_t *) data_pointer, lba, number_blocks, 500); //while(BSP_SD_GetCardState() != SD_TRANSFER_OK); status = check_sd_status(0); return (status); }
用於實現SD模擬U盤的寫入功能。
3.3.4 接口函數注冊
接口函數的注冊是在文件demo_sd_usbx.c里面:
storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_read = app_usb_device_thread_media_read; storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_write = app_usb_device_thread_media_write; storage_parameter.ux_slave_class_storage_parameter_lun[0].ux_slave_class_storage_media_status = app_usb_device_thread_media_status;
3.4 使用的MicroUSB接口並注意跳線帽設置
本周教程移植的例子使用內部RAM模擬了一個U盤,效果如下:
注意使用的是MicroUSB接口:
注意板子左下角跳線帽的設置:
這里是用於選擇CAN1 TX使用PB9或者PA12引腳,CAN1 RX使用PB8或者PA11引腳。大家這里可以什么都不接,或者CAN1 TX通過跳線帽短接PA12,CAN1 RX通過跳線帽短接PA11。切記不可以短接到PA12和PA11引腳上,USB要使用這兩個引腳。
3.5 實驗例程
配套例子:
V7-2401_ThreadX USBX Template
實驗目的:
- 學習USBX模板,通過SD來模擬U盤。
實驗內容:
1、共創建了如下幾個任務,通過按下按鍵K1可以通過串口或者RTT打印任務堆棧使用情況
========================================================
CPU利用率 = 0.89%
任務執行時間 = 0.586484645s
空閑執行時間 = 85.504470575s
中斷執行時間 = 0.173225395s
系統總執行時間 = 86.264180615s
=======================================================
任務優先級 任務棧大小 當前使用棧 最大棧使用 任務名
Prio StackSize CurStack MaxStack Taskname
2 4092 303 459 App Task Start
5 4092 167 167 App Msp Pro
4 4092 167 167 App Task UserIF
5 4092 167 167 App Task COM
0 1020 191 191 System Timer Thread
串口軟件可以使用SecureCRT或者H7-TOOL RTT查看打印信息。
App Task Start任務 :啟動任務,這里用作BSP驅動包處理。
App Task MspPro任務 :消息處理。
App Task UserIF任務 :按鍵消息處理。
App Task COM任務 :這里用作LED閃爍。
System Timer Thread任務:系統定時器任務
2、(1) 凡是用到printf函數的全部通過函數App_Printf實現。
(2) App_Printf函數做了信號量的互斥操作,解決資源共享問題。
3、默認上電是通過串口打印信息,如果使用RTT打印信息
(1) MDK AC5,MDK AC6或IAR通過使能bsp.h文件中的宏定義為1即可
#define Enable_RTTViewer 1
(2) Embedded Studio繼續使用此宏定義為0, 因為Embedded Studio僅制作了調試狀態RTT方式查看。
實驗操作:
- 測試前務必將SD卡插入到開發板左上角的卡座中。
- 支持以下6個功能,用戶通過電腦端串口軟件發送數字1-6給開發板即可
- printf("1 - 顯示根目錄下的文件列表\r\n");
- printf("2 - 創建一個新文件armfly.txt\r\n");
- printf("3 - 讀armfly.txt文件的內容\r\n");
- printf("4 - 創建目錄\r\n");
- printf("5 - 刪除文件和目錄\r\n");
- printf("6 - 讀寫文件速度測試\r\n");
- printf("a - 打開SD卡模擬U盤\r\n");
- printf("b - 關閉SD卡模擬U盤\r\n");
串口打印的信息:
波特率 115200,數據位 8,奇偶校驗位無,停止位 1
RTT打印:
3.6 總結
本章節就為大家講解這么多,后面章節再為大家詳細講解USBX的玩法。