你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

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

[复制链接]
STMCU-管管 发布时间:2025-2-24 09:28
01
( E% v! F- a) w( M: Q+ E9 J: [0 U客户需求
( k5 t$ i* @) H( V6 @: H1 J客户使用STM32H563开发产品,需要USB CDC ACM虚拟串口的工程,并且要求不使用PD功能,而我们STM32CubeH5代码库中是包含PD功能的工程。于是协助客户解决这个问题,提供给客户不带PD功能的虚拟串口工程。
6 u; {, @$ U2 w/ o  w1 [
$ U6 H% ?/ j# Z! T2 k: O5 `在STM32CubeMX软件中,选择ThreadX USBX模块完成USB CDC ACM虚拟串口的工程,并且不使能PD功能。8 b5 B( G  ^, x( b

: ~. ^+ H( L0 ]5 l- O' c02. `( w) w/ E' z  _+ o# x* B
基本硬件和STM32CubeMX的配置. w6 \5 @/ b. u1 [

/ A; J2 v6 R' E, E, V硬件方面使用客户开发板和NUCLEO_H563ZI同时来进行软件开发。& U, F9 z# S) `" q, @$ ~6 G
# _. A7 H. Y) E( c
下面是NUCLEO_H563ZI USB Type C接口原理图:
. q) i! ^5 ]( w4 y9 g
12.png
) J7 H, c8 o- l1 v) L2 M, @- M

" J, @, s* Z7 i" O
# z! D. J) A" E/ ~2 l
由于不使用PD功能,需要对NUCLEO_H563ZI的电路进行改动,在开发板上 PA11、PA12连接到了使用PD功能的Type-C接口,因此需要做如下的硬件修改:需要把SB27、SB28上的两个小电阻换移到SB22、SB21的位置,这样做是把  PA11、PA12连接到开发板的CN12上,然后就可以接USB线了。
. O- ^+ O9 @; w8 F6 q

" L; F! X7 ~9 `
13.png

) X' |& e! C$ H9 _
14.png : Q; ~4 D* s6 A

/ p; A" k" C; @3 W. ~
% ~' M% e7 H( T7 S; B" o
USB连接线需要接GND、D+、D-;在对NUCLEO_H563ZI开发板的调试中,VBUS没有连接起来,但客户开发板的设计中是需要连接的。以此来告诉USB Device端(STM32H563ZI)的软件工程,USB线连接到PC上了。
* o4 d* P% G, x, V6 r; S
2.1. 使用STM32CubeMX配置并生成工程(不带trustZone)

* A/ j9 N9 C* H
  |" {* O3 |! n# c  Z
$ W4 y) G3 D" ^- x" v

3 k9 w( B9 |9 _! a
第一步:开始一个新的不带trustZone工程:
/ T( v5 V6 U* c0 z: i
1 {. `+ z3 z2 K- V/ L* c# C, J* m
15.png 8 d+ Q9 H* B7 D9 N

5 a$ g6 g( M% G7 h6 g! f
3 X1 ?$ d7 c, b8 [
第二步:配置USB外设,基本上选择默认配置,同时使能USB的全局中断。

8 [5 a  m- z7 G

9 G' b3 X$ f0 r" @
16.png
3 S+ ?1 W+ P; u, V. U, K; D
第三步:配置ThreadX外设,使能Core,其它先选择默认配置。

$ s' ]4 }  j* t: T. `. A9 s7 R' \

1 E/ ?+ c, a+ n  O% J
17.png ' r2 A1 z$ _6 x0 |* D3 C

5 h: \, J7 F2 C' ^8 P1 g# e
2 ]( ~9 A4 m5 [1 I, A
第四步:配置USBX:
4 ^) Q) E/ c1 W! j8 L9 ?
1. 使能Core System。
  d; n: t. H4 t/ ]+ G1 D6 l
2. 在UX Device FS中,选择Device CoreStack FS和Device Contronllers FS。
# k" f/ Z0 Y7 t& }0 G/ Q+ W9 q
3. 在Device Class FS中,选择CDC ACM。
- _% b# `- F0 P1 R8 J

( f# t9 e. a0 |2 D5 e
18.png
, [9 M2 [7 |3 s! G. v

0 q% q, h# H7 r/ n" J0 e
2 U5 W1 L! O3 }8 F! ]
4. 配置USB基本参数。其配置除了对RAM需要设置外,其它选择基本配置就可以了。

* X# G& r% A, X6 a8 R: }

9 T5 y- M5 M4 Y  u* \% F: ]
19.png + n# v7 k. s+ W0 b
5. 其它的Platform,选择USB。

/ B  q6 I. q) h) R7 m& a
20.png
% ]4 B  ^* T# F# Y
4 t, h" ~( n* G5 d( q
2 D* M( z% {7 d4 g! ?  w
第五步:配置SYS,选择TIM6作为系统滴答时钟的时钟源。

7 s6 r; C6 f" |) B( y
, W9 u5 ~: n, `" m) l
21.png 3 _  g" r' i; N7 {

) j' i+ h4 h1 E8 g4 E- l$ Q& f
4 m6 v) P$ K) D$ F7 w7 w
第六步:为了模拟USB的断开也连接,使用一个GPIO来控制USB的断开和连接 (GPIO_EXTI13);并且使能外部中断。

" L5 v. C+ R8 c0 ~' m' r# }

7 [7 E& C. Y* e' a5 y+ [
22.png
) i! x* u) X, y3 u$ R9 s9 V0 m

) m' m' L+ C* d/ H" b* F6 }

/ l* W; ]# R# s6 D8 i. E
第七步:配置时钟,选择默认时钟源。配置系统时钟为250MHz。
7 W+ A& e6 H# S$ v5 O
* m1 C/ _& {9 O1 j$ @1 n; [5 n  B
23.png
" M/ w- H  k, V8 d

" O9 n9 M9 `- b4 G, j  M4 L9 w+ h
( Q$ C2 q$ S' J% p$ E" o9 [
请注意:使用HSI48作为CRS时钟,同时注意选择HSI48作为USB时钟源。
  k9 ~1 E% ~+ q- x0 }
  x" {! U8 ~( y5 X/ f! P
24.png
3 g) n3 t. C+ W2 r

* H; t6 \" n8 @

2 Z  g+ I: M6 ]# t
2.2. 使用STM32CubeMX配置并生成工程

# |" e; _  s$ ~( q6 \7 k
定义一个自己的工程名,由于用的RTOS和USB,所以需要适增加堆栈空间:
$ I. m/ D2 U6 U

8 T$ G# r/ e8 s0 a! W
25.png
) w1 Z& I. O+ \6 s
最后生成的工程文件的结构如下:

4 S8 s' f0 h- t0 T5 q
+ d' k% u) P5 {1 U1 U
26.png
5 b+ ~* a: _6 W6 V  p# E/ t: T7 z
2.3. 参数配置的解释

0 k* W2 `5 G  i
需要调整USBX Device System Stack Size和USBX Device memory pool size的参数。
# e# a+ \4 b3 Y/ |* L4 X1 }
' i8 b" J) T$ ^) e3 {
27.png * l% Y' N. D( T2 B: L
调整依据请参考如下内容:

7 F' O9 F  X2 a, l$ G/ Y' T+ @  y# y

7 m3 ?, Z. f- y4 f5 z6 n
28.png ; t( K& O1 Y4 c0 P+ c' X7 e1 s

' ^; {8 f2 q$ ~3 X, k9 s
! ?; r8 u  m7 B/ \
要定义USBX设备内存池(USBX Device memory pool size)大小的内存量,必须考虑以下因素:
& p( Z( F6 \+ p1 y3 _; T
1. USBX设备系统堆栈大小(USBX Device System Stack Size)
1 |- c: f. b! A1 c; K
2. USBX设备应用程序线程堆栈大小(USBX Device Application Thread stack size)

  P- W8 E" O: O+ S, e" }
3. USB应用程序创建的线程堆栈
" ~( j7 y% y( u  H# v) z
4. 以及RX和TX缓冲区

+ e$ G5 E; _" i5 e
( M2 i. o3 w9 W4 R
29.png
  V% C; P5 L  Z* b  l! v+ A! b' c
03

5 _7 F6 l, s) o& v' F  N' X: y2 X
对工程进行完善和修改

) h2 @# O$ `/ c7 t. b
由于CubeMX的生成的代码有限,生成软件工程后需要对工程进行完善,具体改动如下:

( h- g9 u3 V- y' K# K( f

9 d7 ^7 U# q$ j2 G6 Q5 F
30.png - d1 y* H" G" Z

; d# J& |0 s6 h/ r8 z) Q

& p8 J4 M' Z; U. }/ m
代码开发的第一步是将ST HAL USB驱动程序与USBX固件连接起来,然后初始化USB外设。还需要添加一下必要的代码,下面蓝色部分为需要添加的,绿色部分是生成的代码,你可以搜索绿色部分快捷收到需要添加的位置。
! ~; p/ f4 q( j$ G

& l0 w( L) [# x9 Y, `0 Y  K
第一步:在“app_usbx_device.h”添加必须包含的头文件和需要用到的函数声明。

/ L- s0 S* w- E
  1. <div style="text-align: left;">/* USER CODE BEGIN Includes *///</div><div style="text-align: left;">#include "ux_dcd_stm32.h"   </div><div style="text-align: left;">#include "main.h"</div><div style="text-align: left;">/* USER CODE END Includes */  </div><div style="text-align: left;">/* USER CODE BEGIN 1 */</div><div style="text-align: left;">#define APP_QUEUE_SIZE  5</div><div style="text-align: left;">typedef enum     </div><div style="text-align: left;">{</div><div style="text-align: left;">STOP_USB_DEVICE = 1,</div><div style="text-align: left;">START_USB_DEVICE,</div><div style="text-align: left;">} USB_MODE_STATE;</div><div style="text-align: left;">/* USER CODE END 1 */</div>
复制代码
  k; R  ~9 M0 }4 H: i* |  ^
第二步:在“app_usbx_device.c”添加需要用到的变量和函数声明。
* ?2 J* ]& _& U. g
  1. <div style="text-align: left;">/* USER CODE BEGIN PTD */</div><div style="text-align: left;">extern PCD_HandleTypeDef hpcd_USB_DRD_FS;</div><div style="text-align: left;">#if defined ( __ICCARM__ ) /* IAR Compiler */</div><div style="text-align: left;">  #pragma data_alignment=4</div><div style="text-align: left;">#endif /* defined ( __ICCARM__ ) */</div><div style="text-align: left;">__ALIGN_BEGIN USB_MODE_STATE                  USB_Device_State_Msg   __ALIGN_END;   </div><div style="text-align: left;">TX_QUEUE                         ux_app_MsgQueue;   </div><div style="text-align: left;">extern TX_QUEUE     ux_app_MsgQueue;</div><div style="text-align: left;">extern TX_BYTE_POOL ux_device_app_byte_pool;</div><div style="text-align: left;">extern unsigned int USB_connect_State;</div><div style="text-align: left;">/* USER CODE END PTD */</div><div style="text-align: left;">/* USER CODE BEGIN PV */</div><div style="text-align: left;">extern PCD_HandleTypeDef hpcd_USB_DRD_FS;</div><div style="text-align: left;">static TX_THREAD ux_cdc_read_thread;   </div><div style="text-align: left;">static TX_THREAD ux_cdc_write_thread;</div><div style="text-align: left;">unsigned int KeyNumber=0;  </div><div style="text-align: left;">void usb_connect(PCD_HandleTypeDef* hpcd);</div><div style="text-align: left;">void usb_disconnect(PCD_HandleTypeDef* hpcd);</div><div style="text-align: left;">void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin);</div><div style="text-align: left;">void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin);</div><div style="text-align: left;">/* USER CODE END PV */</div>
复制代码

7 E" V( x2 N6 y3 Y2 X7 h8 t
第三步:在app_ux_device_thread_entry函数中,添加链接驱动程序和初始化USB外设的代码。

' W* f  Z$ [4 l; B
  1. <div style="text-align: left;">static VOID app_ux_device_thread_entry(ULONG thread_input)      </div><div style="text-align: left;">  /* USER CODE BEGIN app_ux_device_thread_entry */</div><div style="text-align: left;">{</div><div style="text-align: left;">      MX_USB_PCD_Init();   </div><div style="text-align: left;">      HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, 0x00 , PCD_SNG_BUF, 0x40);</div><div style="text-align: left;">      HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, 0x80 , PCD_SNG_BUF, 0x80);</div><div style="text-align: left;">      HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, 0x03, PCD_SNG_BUF, 0xC0);</div><div style="text-align: left;">      HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, 0x81, PCD_SNG_BUF, 0x100);</div><div style="text-align: left;">      HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, 0x82, PCD_SNG_BUF, 0x140);</div><div style="text-align: left;">      ux_dcd_stm32_initialize((ULONG)USB_DRD_FS, (ULONG)&hpcd_USB_DRD_FS);</div><div style="text-align: left;">  /* USER CODE END app_ux_device_thread_entry */</div><div style="text-align: left;">}</div>
复制代码

+ o) K4 U5 i" i' @9 _3 f
先调用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。
' W+ G4 _: u; y* g) h
. z7 N1 V7 G) h! L1 _9 C7 e
31.png ) ?6 d( E9 r3 ~- E* Q. v
使用内存的第一个地址来存储BTABLE,它是端点缓冲区的地址列表。BTABLE为每个端点存储8个字节。由于STM32H563有8个端点,因此它最多可以消耗64字节。
: a- W& r2 C4 J7 o! R
2 Y3 S2 A' @! m/ m
  q" W6 I. c% @8 D

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

2 V( n+ G8 T+ |5 `: n, H& J

; K# h# u3 _5 T$ d

- o0 l2 ?" h" N) u6 f) ^+ }/ o
- Z. z+ A; j5 w+ Z' O
CDC类的配置描述符一般包含一个接口0(Interface 0),一个控制接口,另外一个是数据接口(Interface 1),除此之外,IAD(Interface Association Description),这个是可选的,根据实际情况来确定是否需要。
3 G+ F/ a4 O9 L' |7 ^: H
+ ?: t# |* y2 K/ q4 a
1 H7 K9 d* `3 R/ i0 P

8 c# X9 Y8 c- b5 J+ G$ {- c8 j" L
下一个调用的函数是ux_dcd_stm32_initialize(…) ,它负责将HAL USB驱动程序链接到USBX应用程序。最后,我们调用HAL_PCD_Start(..)来启动 USB PCD外设。

! E/ g7 f$ S& f7 W; Q" C. y/ ]* t
( T: G, \$ y% L; Z- f, @) k2 a
( u! f* y2 S/ g9 n
: |, I& U3 _/ s1 c/ I1 ?
第四步:在app_usbx_device.c中MX_USBX_Device_Init(…)函数的最后用户代码部分,创建两个线程来处理数据的写和读,以及一个消息句柄,具体的代码如下,最后一个参数为TX_DONT_START,意思是任务先挂起,之后再启动。
7 ]$ k1 ^1 W8 l% k
  1. <div style="text-align: left;">/* USER CODE BEGIN MX_USBX_Device_Init1 */</div><div style="text-align: left;">    /* Allocate Memory for the Queue */</div><div style="text-align: left;">    if (tx_byte_allocate(byte_pool, (VOID **) &pointer, APP_QUEUE_SIZE*sizeof(ULONG),</div><div style="text-align: left;">                       TX_NO_WAIT) != TX_SUCCESS)</div><div style="text-align: left;">  {</div><div style="text-align: left;">    /* USER CODE BEGIN MAIN_THREAD_ALLOCATE_STACK_ERROR */</div><div style="text-align: left;">    return TX_POOL_ERROR;</div><div style="text-align: left;">    /* USER CODE END MAIN_THREAD_ALLOCATE_STACK_ERROR */</div><div style="text-align: left;">  }</div><div style="text-align: left;">  /* Create the MsgQueue */</div><div style="text-align: left;">  if (tx_queue_create(&ux_app_MsgQueue, "Message Queue app", TX_1_ULONG,</div><div style="text-align: left;">                      pointer, APP_QUEUE_SIZE * sizeof(ULONG)) != TX_SUCCESS)</div><div style="text-align: left;">  {</div><div style="text-align: left;">    return TX_QUEUE_ERROR;</div><div style="text-align: left;">  }</div><div style="text-align: left;">/* Allocate memory for the UX RX thread */</div><div style="text-align: left;">if (tx_byte_allocate(byte_pool, (VOID **) &pointer, UX_DEVICE_APP_THREAD_STACK_SIZE,</div><div style="text-align: left;">                       TX_NO_WAIT) != TX_SUCCESS)</div><div style="text-align: left;">  {</div><div style="text-align: left;">    /* USER CODE BEGIN MAIN_THREAD_ALLOCATE_STACK_ERROR */</div><div style="text-align: left;">    return TX_POOL_ERROR;</div><div style="text-align: left;">    /* USER CODE END MAIN_THREAD_ALLOCATE_STACK_ERROR */</div><div style="text-align: left;">  }</div><div style="text-align: left;">/* Create the UX RX thread */</div><div style="text-align: left;">    if (tx_thread_create(&ux_cdc_read_thread, "cdc_acm_read_usbx_app_thread_entry", usbx_cdc_acm_read_thread_entry,</div><div style="text-align: left;">                       1, pointer, UX_DEVICE_APP_THREAD_STACK_SIZE, UX_DEVICE_APP_THREAD_PRIO,</div><div style="text-align: left;">                       UX_DEVICE_APP_THREAD_PREEMPTION_THRESHOLD, UX_DEVICE_APP_THREAD_TIME_SLICE,</div><div style="text-align: left;">                       TX_DONT_START) != TX_SUCCESS)</div><div style="text-align: left;">  {</div><div style="text-align: left;">    /* USER CODE BEGIN MAIN_THREAD_CREATE_ERROR */</div><div style="text-align: left;">    return TX_THREAD_ERROR;</div><div style="text-align: left;">    /* USER CODE END MAIN_THREAD_CREATE_ERROR */</div><div style="text-align: left;">  }</div><div style="text-align: left;">   </div><div style="text-align: left;">/* Allocate memory for the UX TX thread */</div><div style="text-align: left;">if (tx_byte_allocate(byte_pool, (VOID **) &pointer, UX_DEVICE_APP_THREAD_STACK_SIZE,</div><div style="text-align: left;">                       TX_NO_WAIT) != TX_SUCCESS)</div><div style="text-align: left;">  {</div><div style="text-align: left;">    /* USER CODE BEGIN MAIN_THREAD_ALLOCATE_STACK_ERROR */</div><div style="text-align: left;">    return TX_POOL_ERROR;</div><div style="text-align: left;">    /* USER CODE END MAIN_THREAD_ALLOCATE_STACK_ERROR */</div><div style="text-align: left;">  }</div><div style="text-align: left;">/* Create the UX TX thread */</div><div style="text-align: left;">    if (tx_thread_create(&ux_cdc_write_thread, "cdc_acm_write_usbx_app_thread_entry",</div><div style="text-align: left;">usbx_cdc_acm_write_thread_entry,</div><div style="text-align: left;">                       1, pointer, UX_DEVICE_APP_THREAD_STACK_SIZE, UX_DEVICE_APP_THREAD_PRIO,</div><div style="text-align: left;">                       UX_DEVICE_APP_THREAD_PREEMPTION_THRESHOLD, UX_DEVICE_APP_THREAD_TIME_SLICE,</div><div style="text-align: left;">                       TX_DONT_START) != TX_SUCCESS)  //UX_DEVICE_APP_THREAD_START_OPTION</div><div style="text-align: left;">  {</div><div style="text-align: left;">    /* USER CODE BEGIN MAIN_THREAD_CREATE_ERROR */</div><div style="text-align: left;">    return TX_THREAD_ERROR;</div><div style="text-align: left;">    /* USER CODE END MAIN_THREAD_CREATE_ERROR */</div><div style="text-align: left;">  }</div>
复制代码

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

: `) G- N9 ~& a
  1. <div style="text-align: left;">/* USER CODE BEGIN PFP */</div><div style="text-align: left;">void usb_connect(PCD_HandleTypeDef* hpcd) {</div><div style="text-align: left;">  HAL_PCD_Start(hpcd);   </div><div style="text-align: left;">  USB_Device_State_Msg = START_USB_DEVICE;</div><div style="text-align: left;">  if (tx_queue_send(&ux_app_MsgQueue, &USB_Device_State_Msg, TX_NO_WAIT) != TX_SUCCESS)</div><div style="text-align: left;">  {</div><div style="text-align: left;">    Error_Handler();  }</div><div style="text-align: left;">   if(tx_thread_resume(&ux_cdc_read_thread)==TX_THREAD_ERROR)</div><div style="text-align: left;">   {</div><div style="text-align: left;">     Error_Handler();   }</div><div style="text-align: left;">  if(tx_thread_resume(&ux_cdc_write_thread)==TX_THREAD_ERROR)</div><div style="text-align: left;">   {</div><div style="text-align: left;">     Error_Handler();  }</div><div style="text-align: left;">}</div><div style="text-align: left;">void usb_disconnect(PCD_HandleTypeDef* hpcd)  {</div><div style="text-align: left;">  HAL_PCD_Stop(hpcd);  </div><div style="text-align: left;">   USB_Device_State_Msg = STOP_USB_DEVICE;</div><div style="text-align: left;">  if (tx_queue_send(&ux_app_MsgQueue, &USB_Device_State_Msg, TX_NO_WAIT) != TX_SUCCESS)</div><div style="text-align: left;">  {</div><div style="text-align: left;">    Error_Handler();  }</div><div style="text-align: left;">  if(tx_thread_suspend(&ux_cdc_read_thread)==TX_THREAD_ERROR)</div><div style="text-align: left;">   {</div><div style="text-align: left;">     Error_Handler();   }</div><div style="text-align: left;">  if(tx_thread_suspend(&ux_cdc_write_thread)==TX_THREAD_ERROR)   {</div><div style="text-align: left;">     Error_Handler();</div><div style="text-align: left;">   }</div><div style="text-align: left;">}</div><div style="text-align: left;">void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin){</div><div style="text-align: left;">   if(KeyNumber==0)  //please use the status of "VBUS connect and disconnect" to control program direction.</div><div style="text-align: left;">  {</div><div style="text-align: left;">    usb_connect(&hpcd_USB_DRD_FS);   }</div><div style="text-align: left;">   if(KeyNumber==1)</div><div style="text-align: left;">  {</div><div style="text-align: left;">    usb_disconnect(&hpcd_USB_DRD_FS);  </div><div style="text-align: left;">  }</div><div style="text-align: left;">}</div><div style="text-align: left;">void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin){</div><div style="text-align: left;">  if(KeyNumber==0)  //please use the status of "VBUS connect and disconnect" to control program direction.</div><div style="text-align: left;">  {   </div><div style="text-align: left;">    usb_connect(&hpcd_USB_DRD_FS);   }</div><div style="text-align: left;">   if(KeyNumber==1)</div><div style="text-align: left;">  {</div><div style="text-align: left;">    usb_disconnect(&hpcd_USB_DRD_FS);  </div><div style="text-align: left;">  }</div><div style="text-align: left;">}</div><div style="text-align: left;">/* USER CODE END PFP */</div>
复制代码
, w( B1 i/ e. K) L2 M
第六步,在ux_device_cdc_acm.h文件并添加必要的头文件包含和函数声明:

7 Q5 F( A8 H9 A" Y/ m* d5 x
  1. <div style="text-align: left;">/* Private includes ----------------------------------------------------------*/</div><div style="text-align: left;">/* USER CODE BEGIN Includes */</div><div style="text-align: left;">#include "app_usbx_device.h"</div><div style="text-align: left;">/* USER CODE END Includes */</div><div style="text-align: left;">/* USER CODE BEGIN EFP */</div><div style="text-align: left;">#define APP_RX_DATA_SIZE   2048   </div><div style="text-align: left;">#define APP_TX_DATA_SIZE   2048</div><div style="text-align: left;">VOID usbx_cdc_acm_write_thread_entry(ULONG thread_input);</div><div style="text-align: left;">VOID usbx_cdc_acm_read_thread_entry(ULONG thread_input);</div><div style="text-align: left;">/* USER CODE END EFP */</div>
复制代码
9 x! g2 U: k$ X
第七步,在ux_device_cdc_acm.c添加私有变量并在“USBD_CDC_ACM_Activate”初始化。
. Q; g$ I5 }3 Y7 E6 d( H
  1. <div style="text-align: left;">/* Private define ------------------------------------------------------------*/</div><div style="text-align: left;">/* USER CODE BEGIN PD */</div><div style="text-align: left;">UX_SLAVE_CLASS_CDC_ACM *cdc_acm;   </div><div style="text-align: left;">/* USER CODE END PD */</div><div style="text-align: left;">VOID USBD_CDC_ACM_Activate(VOID *cdc_acm_instance)</div><div style="text-align: left;">{</div><div style="text-align: left;">/* USER CODE BEGIN USBD_CDC_ACM_Activate */</div><div style="text-align: left;">cdc_acm = (UX_SLAVE_CLASS_CDC_ACM *)cdc_acm_instance;     </div><div style="text-align: left;">//UX_PARAMETER_NOT_USED(cdc_acm_instance);</div><div style="text-align: left;">/* USER CODE END USBD_CDC_ACM_Activate */</div><div style="text-align: left;">return;</div><div style="text-align: left;">}</div>
复制代码

! v, S* I0 S. }* g$ }* @% }' w
这样,我们已经有了USB应用程序的功能。类资源在ux_device_cdc_acm.c文件中可用。

0 B, o+ M  W4 r' f2 X8 Y; J1 N- ]! _# U
第八步:在“ux_device_cdc_acm.c”中定义线程的实现程序。
# H( f! s2 J* A- v
  1. <div style="text-align: left;">/* USER CODE BEGIN 1 */</div> <div style="text-align: left;">2 e% ?4 q2 x' s+ |9 U& i
  2. </div><div style="text-align: left;">VOID usbx_cdc_acm_write_thread_entry(ULONG thread_input)</div><div style="text-align: left;">{</div><div style="text-align: left;">      ULONG tx_actual_length;</div><div style="text-align: left;">      const uint8_t message[] = "USBX Application Running!\r\n";</div><div style="text-align: left;">   while(1)</div><div style="text-align: left;">      {</div><div style="text-align: left;">        ux_device_class_cdc_acm_write(cdc_acm, (UCHAR *)(message), sizeof(message), &tx_actual_length);</div><div style="text-align: left;">                     tx_thread_sleep(100);</div><div style="text-align: left;">      }</div><div style="text-align: left;">}</div><div style="text-align: left;">VOID usbx_cdc_acm_read_thread_entry(ULONG thread_input)</div><div style="text-align: left;">{</div><div style="text-align: left;">      /* Private Variables */</div><div style="text-align: left;">      ULONG rx_actual_length;</div><div style="text-align: left;">     uint8_t UserRxBuffer[64];</div><div style="text-align: left;">      while(1)</div><div style="text-align: left;">      {</div><div style="text-align: left;">          if(cdc_acm != UX_NULL)</div><div style="text-align: left;">             {</div><div style="text-align: left;">                    ux_device_class_cdc_acm_read(cdc_acm, (UCHAR *)UserRxBuffer, 64, &rx_actual_length);</div><div style="text-align: left;">                   switch(UserRxBuffer[rx_actual_length-1])</div><div style="text-align: left;">                   {</div><div style="text-align: left;">                   case '1':</div><div style="text-align: left;">                          break;</div><div style="text-align: left;">                   case '0':</div><div style="text-align: left;">                          break;</div><div style="text-align: left;">                   }</div><div style="text-align: left;">             }   </div><div style="text-align: left;">      }</div><div style="text-align: left;">}</div>
复制代码
& p4 Z* z9 j' G2 m2 T: e
5 r7 r7 ^) C9 Z! X
04

) f/ w( \1 ?% C- A; z& \9 Y
对代码进行的测试
, d2 ~* M: v- d* L* D( _
对软件工程添加代码后就可以进入编译和调试环节。

7 F9 t) A& R& z3 s. ~# e

+ h5 A# y2 X) T& U1 g
客户第一版的硬件产品板上无PC13按键,要手动触发按键中断,需要在下面蓝色栏中写‘1’,然后按下键盘中的回车键来模拟按键按下的动作。(NUCLEO_H563ZI硬件有这个按键,可以直接使用这个按键来生成按键中断)。

. K2 S2 O4 i0 z# {2 o
" r8 f' h! T1 Q+ e' z5 v
32.png * _; l8 p/ s5 R
或EXTI_SWIER1.SWI13中写1回车后:

* l3 {- T& y" |2 Y6 h
0 k( g0 ~! `8 x' y9 W
33.png
( _7 Q2 k; }( z$ p+ H% H
然后产生外部按键中断:

6 J% j. e0 `" b) S: i$ ~+ Z

+ X& l+ n+ _7 V0 Y. [
34.png 9 `& }! ~0 z! g
使用USB线连接PC,并且程序全速运行后会看到识别的端口号:
6 m. g; |3 B6 r+ a/ u; L6 F) E

+ g  ]. S5 d7 W- j; y  `) m" d
35.png
, {9 P/ R2 c6 K3 o+ c$ K- ~
代码运行后在PC端打开UART调试助手,可以看到从虚拟串口接收到的内容,然后PC发送“1”,在usbx_cdc_acm_read_thread_entry函数中设置断点就可以看到接收到的数据:
/ b+ f) c" g8 f# e- u

) T9 D0 O, K) D" L- |
36.png
- V% Q2 J" u# o. u
按键(Nucleo_H563ZI 上的 USER蓝色按键)第一次按下(模拟VBUS上电的动作)后启动(resume)usbx_cdc_acm_write_thread_entry和 usbx_cdc_acm_read_thread_entry任务,在第二次按键按下时挂起(suspend)那两个任务。
% A. c9 x0 a$ x$ ^8 o' L

- |7 O9 R7 Y; T9 X5 V# ^
使用时需要根据情况修改HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13)函数,以实现使用Vbus上电代替第一次按下Key的功能,Vbus掉电代替第二次按下Key的功能。

6 n& b5 E! r2 i% W& p% w

- T3 ~" ^' A$ t8 N# _1 ~
# J" U# K" L5 u0 r# O' v& j; p
( o  Y$ w* S6 x2 ~; W! R
代码调试时Vbus上电(第一次按键)和Vbus下电(第二次按键)之间需要暂停程序,然后在调试状态下,在观察窗中修改KeyNumber的值从0到1;Vbus下电(第二次按键)后需要在窗口中改KeyNumber为0:

2 Q' |. c' e- k+ V( ^! X
' }# I) I" j, B$ m# z4 n

9 ^3 k9 A, B- c
+ Q4 `4 b) D  |+ J) K  c3 E* G2 _) J
下面的功能在程序中预留了,如果后续程序开发中不使用可以删除。

: U1 V( i6 i) V7 p, m. k4 G3 p
  1. <div style="text-align: left;">tx_queue_create(&ux_app_MsgQueue, "Message Queue app", TX_1_ULONG,</div><div style="text-align: left;">pointer, APP_QUEUE_SIZE * sizeof(ULONG))</div><div style="text-align: left;">tx_queue_send(&ux_app_MsgQueue, &USB_Device_State_Msg, TX_NO_WAIT)</div><div style="text-align: left;">tx_queue_receive(…)</div>
复制代码
' V, E% Z/ ^/ T, T& A2 A/ }
初次上电后,请全速运行代码,等待初始化完成,第一次按下按键后走usb_connect流程(vbus 上电,参考HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13)处理函数,代码运行后PC端会看到具体的串口端口号。用串口调试助手测试过数据收发功能后,在调试中暂停代码运行,然后修改KeyNumber从0到1(在观察窗中直接修改后回车)。然后全速运行然后再次按下按键就会走usb_disconnect流程。之后再修改KeyNumber从1到0,再次按下按键(vbus上电),进入的设备枚举阶段…
; K3 ~; D% ?2 {# @
3 N1 P4 _8 o  c7 {( b
37.png 4 s0 H, K3 f- n& }% G! E4 ]7 Y3 f
05
8 ~, N+ M4 N6 L9 _0 `
小结

' K/ e8 T$ E8 a" H1 t5 ]
具体的软件工程放在了附件中,使用NUCLEO_H563ZI,然后修改硬件就可以进行测试。

' _( G1 T# m4 I
% O* Y: Q1 n. a2 W& W3 {( _
收藏 评论1 发布时间:2025-2-24 09:28

举报

1个回答
郭若 回答时间:2025-11-4 14:56:50

请问下这个软件工程的附件在哪,怎么没找见😄

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版