1. 程式人生 > 其它 >【STM32H7】第3章 ThreadX USBX協議棧移植到STM32H7

【STM32H7】第3章 ThreadX USBX協議棧移植到STM32H7

教程更新中: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

實驗目的:

  1. 學習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方式檢視。

實驗操作:

  1. 測試前務必將SD卡插入到開發板左上角的卡座中。
  2. 支援以下6個功能,使用者通過電腦端串列埠軟體傳送數字1-6給開發板即可
  3. printf("1 - 顯示根目錄下的檔案列表\r\n");
  4. printf("2 - 建立一個新檔案armfly.txt\r\n");
  5. printf("3 - 讀armfly.txt檔案的內容\r\n");
  6. printf("4 - 建立目錄\r\n");
  7. printf("5 - 刪除檔案和目錄\r\n");
  8. printf("6 - 讀寫檔案速度測試\r\n");
  9. printf("a - 開啟SD卡模擬U盤\r\n");
  10. printf("b - 關閉SD卡模擬U盤\r\n");

串列埠列印的資訊:

波特率 115200,資料位 8,奇偶校驗位無,停止位 1

RTT列印:

3.6 總結

本章節就為大家講解這麼多,後面章節再為大家詳細講解USBX的玩法。

微信公眾號:armfly_com 安富萊論壇:www.armbbs.cn 安富萊淘寶:https://armfly.taobao.com