【STM32H7】第3章 ThreadX USBX协议栈移植到STM32H7

2023-04-23  

3.1 初学者重要提示

1、 本章使用的ST专门为STM32H7提供的软件包:

armbbs.cn/forum.php?

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添加到自己的工程里面,路径不限。

配套例子是放在Userusb文件。

  • 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.

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。