打印
[STM32H5]

实战经验 | 选择USBX模块生成USB CDC ACM无PD的项目

[复制链接]
32|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
AC, ACM, CD, USB, dc
本帖最后由 STM新闻官 于 2025-2-24 15:03 编辑

01 客户需求
客户使用STM32H563开发产品,需要USB CDC ACM虚拟串口的工程,并且要求不使用PD功能,而我们STM32CubeH5代码库中是包含PD功能的工程。于是协助客户解决这个问题,提供给客户不带PD功能的虚拟串口工程。

在STM32CubeMX软件中,选择ThreadX USBX模块完成USB CDC ACM虚拟串口的工程,并且不使能PD功能。

02基本硬件和STM32CubeMX的配置
硬件方面使用客户开发板和NUCLEO_H563ZI同时来进行软件开发。

下面是NUCLEO_H563ZI USB Type C接口原理图:

由于不使用PD功能,需要对NUCLEO_H563ZI的电路进行改动,在开发板上PA11、PA12连接到了使用PD功能的Type-C接口,因此需要做如下的硬件修改:需要把SB27、SB28上的两个小电阻换移到SB22、SB21的位置,这样做是把  PA11、PA12连接到开发板的CN12上,然后就可以接USB线了。

USB连接线需要接GND、D+、D-;在对NUCLEO_H563ZI开发板的调试中,VBUS没有连接起来,但客户开发板的设计中是需要连接的。以此来告诉USB Device端(STM32H563ZI)的软件工程,USB线连接到PC上了。
2.1. 使用STM32CubeMX配置并生成工程(不带trustZone)

第一步:开始一个新的不带trustZone工程:

第二步:配置USB外设,基本上选择默认配置,同时使能USB的全局中断。

第三步:配置ThreadX外设,使能Core,其它先选择默认配置。

第四步:配置USBX:
1. 使能Core System。
2. 在UX Device FS中,选择Device CoreStack FS和Device Contronllers FS。
3. 在Device Class FS中,选择CDC ACM。

4. 配置USB基本参数。其配置除了对RAM需要设置外,其它选择基本配置就可以了。

5. 其它的Platform,选择USB。

第五步:配置SYS,选择TIM6作为系统滴答时钟的时钟源。

第六步:为了模拟USB的断开也连接,使用一个GPIO来控制USB的断开和连接 (GPIO_EXTI13);并且使能外部中断。

第七步:配置时钟,选择默认时钟源。配置系统时钟为250MHz。

请注意:使用HSI48作为CRS时钟,同时注意选择HSI48作为USB时钟源。



2.2. 使用STM32CubeMX配置并生成工程
定义一个自己的工程名,由于用的RTOS和USB,所以需要适增加堆栈空间:

最后生成的工程文件的结构如下:



2.3. 参数配置的解释
需要调整USBX Device System Stack Size和USBX Device memory pool size的参数。

调整依据请参考如下内容:

要定义USBX设备内存池(USBX Device memory pool size)大小的内存量,必须考虑以下因素:
1. USBX设备系统堆栈大小(USBX Device System Stack Size)
2. USBX设备应用程序线程堆栈大小(USBX Device Application Thread stack size)
3. USB应用程序创建的线程堆栈
4. 以及RX和TX缓冲区


03 对工程进行完善和修改
由于CubeMX的生成的代码有限,生成软件工程后需要对工程进行完善,具体改动如下:

代码开发的第一步是将ST HAL USB驱动程序与USBX固件连接起来,然后初始化USB外设。还需要添加一下必要的代码,下面蓝色部分为需要添加的,绿色部分是生成的代码,你可以搜索绿色部分快捷收到需要添加的位置。
第一步:在“app_usbx_device.h”添加必须包含的头文件和需要用到的函数声明。

第二步:在“app_usbx_device.c”添加需要用到的变量和函数声明。

第三步:app_ux_device_thread_entry函数中,添加链接驱动程序和初始化USB外设的代码。

先调用MX_USB_PCD_Init()来初始化USB外设;然后使用HAL_PCDEx_PMAConfig(…)进行配置,该函数为STM32H563配置专用USB RAM内存中的PMA(packet memory area),在本例中,端点为0 IN/OUT ( USB标准控制端点)、端点3 OUT  (CDC数据输出点)、端点1 IN  (CDC数据输入端点)和控制端点2 IN ( CDC 命令端点)。PCD_SNG_BUF参数意味着我们为端点使用单个缓冲区,这是必要的,因为我们在双向模式下使用端点。最后,该函数的最后一个参数是PMA地址,缓冲区大小为64Bytes。

使用内存的第一个地址来存储BTABLE,它是端点缓冲区的地址列表。BTABLE为每个端点存储8个字节。由于STM32H563有8个端点,因此它最多可以消耗64字节。

在本例中,我们使用从0到3的端点,因此可以不使用剩余的其余部分。如果需要可以减少TX Ep 0缓冲区的偏移量,优化内存的使用。但是,按照这个表,您可以毫无问题地分配所有8个端点缓冲区。

CDC类的配置描述符一般包含一个接口0(Interface 0),一个控制接口,另外一个是数据接口(Interface 1),除此之外,IAD(Interface Association Description),这个是可选的,根据实际情况来确定是否需要。

下一个调用的函数是ux_dcd_stm32_initialize(…) ,它负责将HAL USB驱动程序链接到USBX应用程序。最后,我们调用HAL_PCD_Start(..)来启动 USB PCD外设。

第四步:在app_usbx_device.c中MX_USBX_Device_Init(…)函数的最后用户代码部分,创建两个线程来处理数据的写和读,以及一个消息句柄,具体的代码如下,最后一个参数为TX_DONT_START,意思是任务先挂起,之后再启动。
/* USER CODE BEGIN MX_USBX_Device_Init1 */     /* Allocate Memory for the Queue */     if (tx_byte_allocate(byte_pool, (VOID **) &pointer, APP_QUEUE_SIZE*sizeof(ULONG),                        TX_NO_WAIT) != TX_SUCCESS)   {     /* USER CODE BEGIN MAIN_THREAD_ALLOCATE_STACK_ERROR */     return TX_POOL_ERROR;     /* USER CODE END MAIN_THREAD_ALLOCATE_STACK_ERROR */   }   /* Create the MsgQueue */   if (tx_queue_create(&ux_app_MsgQueue, "Message Queue app", TX_1_ULONG,                       pointer, APP_QUEUE_SIZE * sizeof(ULONG)) != TX_SUCCESS)   {     return TX_QUEUE_ERROR;   } /* Allocate memory for the UX RX thread */ if (tx_byte_allocate(byte_pool, (VOID **) &pointer, UX_DEVICE_APP_THREAD_STACK_SIZE,                        TX_NO_WAIT) != TX_SUCCESS)   {     /* USER CODE BEGIN MAIN_THREAD_ALLOCATE_STACK_ERROR */     return TX_POOL_ERROR;     /* USER CODE END MAIN_THREAD_ALLOCATE_STACK_ERROR */   } /* Create the UX RX thread */     if (tx_thread_create(&ux_cdc_read_thread, "cdc_acm_read_usbx_app_thread_entry", usbx_cdc_acm_read_thread_entry,                        1, pointer, UX_DEVICE_APP_THREAD_STACK_SIZE, UX_DEVICE_APP_THREAD_PRIO,                        UX_DEVICE_APP_THREAD_PREEMPTION_THRESHOLD, UX_DEVICE_APP_THREAD_TIME_SLICE,                        TX_DONT_START) != TX_SUCCESS)   {     /* USER CODE BEGIN MAIN_THREAD_CREATE_ERROR */     return TX_THREAD_ERROR;     /* USER CODE END MAIN_THREAD_CREATE_ERROR */   }    /* Allocate memory for the UX TX thread */ if (tx_byte_allocate(byte_pool, (VOID **) &pointer, UX_DEVICE_APP_THREAD_STACK_SIZE,                        TX_NO_WAIT) != TX_SUCCESS)   {     /* USER CODE BEGIN MAIN_THREAD_ALLOCATE_STACK_ERROR */     return TX_POOL_ERROR;     /* USER CODE END MAIN_THREAD_ALLOCATE_STACK_ERROR */   } /* Create the UX TX thread */     if (tx_thread_create(&ux_cdc_write_thread, "cdc_acm_write_usbx_app_thread_entry", usbx_cdc_acm_write_thread_entry,                        1, pointer, UX_DEVICE_APP_THREAD_STACK_SIZE, UX_DEVICE_APP_THREAD_PRIO,                        UX_DEVICE_APP_THREAD_PREEMPTION_THRESHOLD, UX_DEVICE_APP_THREAD_TIME_SLICE,                        TX_DONT_START) != TX_SUCCESS)  //UX_DEVICE_APP_THREAD_START_OPTION   {     /* USER CODE BEGIN MAIN_THREAD_CREATE_ERROR */     return TX_THREAD_ERROR;     /* USER CODE END MAIN_THREAD_CREATE_ERROR */   }

第五步:下面的usb_connect()函数是根据客户需要设计的。在PC端连接到设备端STM32H563,VBUS连接时触发中断后调用的函数(客户产品中把VBUS分压后连接到一个GPIO口,由于客户第一版本的PCB没有设计这部分电路,因此调试时使用按键中断替换了Device连接到PC的情况)。Usb_disconnect()函数,是处理STM32H563从PC断开的情况(调试时使用按键再次按下来替换VBUS断开的具体功能)。保留了信号量,程序处理中如果不使用这个信号量可以删除,具体请参考软件工程文件:

/* USER CODE BEGIN PFP */ void usb_connect(PCD_HandleTypeDef* hpcd) {   HAL_PCD_Start(hpcd);     USB_Device_State_Msg = START_USB_DEVICE;   if (tx_queue_send(&ux_app_MsgQueue, &USB_Device_State_Msg, TX_NO_WAIT) != TX_SUCCESS)   {     Error_Handler();  }    if(tx_thread_resume(&ux_cdc_read_thread)==TX_THREAD_ERROR)    {      Error_Handler();   }   if(tx_thread_resume(&ux_cdc_write_thread)==TX_THREAD_ERROR)    {      Error_Handler();  } } void usb_disconnect(PCD_HandleTypeDef* hpcd)  {   HAL_PCD_Stop(hpcd);     USB_Device_State_Msg = STOP_USB_DEVICE;   if (tx_queue_send(&ux_app_MsgQueue, &USB_Device_State_Msg, TX_NO_WAIT) != TX_SUCCESS)   {     Error_Handler();  }   if(tx_thread_suspend(&ux_cdc_read_thread)==TX_THREAD_ERROR)    {      Error_Handler();   }   if(tx_thread_suspend(&ux_cdc_write_thread)==TX_THREAD_ERROR)   {      Error_Handler();    } }  void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin){    if(KeyNumber==0)  //please use the status of "VBUS connect and disconnect" to control program direction.   {     usb_connect(&hpcd_USB_DRD_FS);   }    if(KeyNumber==1)   {     usb_disconnect(&hpcd_USB_DRD_FS);    } } void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin){   if(KeyNumber==0)  //please use the status of "VBUS connect and disconnect" to control program direction.   {        usb_connect(&hpcd_USB_DRD_FS);   }    if(KeyNumber==1)   {     usb_disconnect(&hpcd_USB_DRD_FS);    } } /* USER CODE END PFP */

第六步,在ux_device_cdc_acm.h文件并添加必要的头文件包含和函数声明:

第七步,在ux_device_cdc_acm.c添加私有变量并在“USBD_CDC_ACM_Activate”初始化。

这样,我们已经有了USB应用程序的功能。类资源在ux_device_cdc_acm.c文件中可用。

第八步:在“ux_device_cdc_acm.c”中定义线程的实现程序。
/* USER CODE BEGIN 1 */  VOID usbx_cdc_acm_write_thread_entry(ULONG thread_input) {       ULONG tx_actual_length;       const uint8_t message[] = "USBX Application Running!\r\n";    while(1)       {         ux_device_class_cdc_acm_write(cdc_acm, (UCHAR *)(message), sizeof(message), &tx_actual_length);                      tx_thread_sleep(100);       } } VOID usbx_cdc_acm_read_thread_entry(ULONG thread_input) {       /* Private Variables */       ULONG rx_actual_length;      uint8_t UserRxBuffer[64];       while(1)       {           if(cdc_acm != UX_NULL)              {                     ux_device_class_cdc_acm_read(cdc_acm, (UCHAR *)UserRxBuffer, 64, &rx_actual_length);                    switch(UserRxBuffer[rx_actual_length-1])                    {                    case '1':                           break;                    case '0':                           break;                    }              }         } }

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:意法半导体(中国)投资有限公司
简介:您的嵌入式应用将得益于意法半导体领先的产品架构、技术、多源产地和全方位支持。意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器。

1266

主题

1469

帖子

19

粉丝