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

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

[复制链接]
STMCU-管管 发布时间:2025-2-24 09:28
01
3 ^# D. b9 b- H. }客户需求
, p$ p, V' I, V$ c* \" {0 i% J客户使用STM32H563开发产品,需要USB CDC ACM虚拟串口的工程,并且要求不使用PD功能,而我们STM32CubeH5代码库中是包含PD功能的工程。于是协助客户解决这个问题,提供给客户不带PD功能的虚拟串口工程。; M# c  u! l5 `5 ]; t3 N& E+ _2 f
# d8 N5 k& s: E5 u5 H. @2 e5 I2 I
在STM32CubeMX软件中,选择ThreadX USBX模块完成USB CDC ACM虚拟串口的工程,并且不使能PD功能。# g" X# _& f; J6 e# f3 I

3 l8 \% Y; t7 h( m; I) O5 M6 E02
0 O7 C) z) g$ d6 J) [5 _. S5 i基本硬件和STM32CubeMX的配置
) i7 T4 V7 l" a! j2 Z" w/ Y. O/ q9 b
6 Y4 B1 M- \- ], D- d7 @1 H
硬件方面使用客户开发板和NUCLEO_H563ZI同时来进行软件开发。3 l' l+ }/ t9 |+ q' }

( R5 e! E- d7 A$ h下面是NUCLEO_H563ZI USB Type C接口原理图:! y4 c1 k! D' P# c4 d
12.png
* V" ~: T% F- @2 V4 ^4 T

5 s; c; j" m# r; I1 f' U
$ Q/ H2 M8 y$ r1 v3 w) Z
由于不使用PD功能,需要对NUCLEO_H563ZI的电路进行改动,在开发板上 PA11、PA12连接到了使用PD功能的Type-C接口,因此需要做如下的硬件修改:需要把SB27、SB28上的两个小电阻换移到SB22、SB21的位置,这样做是把  PA11、PA12连接到开发板的CN12上,然后就可以接USB线了。
# v% h, n! Y0 L4 O
" F5 R( O6 \  @# A
13.png

9 x8 N! W$ [/ u4 c! E
14.png ; @: Q9 y- ~) M$ P; Y$ x, o

, F& d5 q" F$ s

1 {, E8 b$ Z$ l& ^4 D+ h8 H& b
USB连接线需要接GND、D+、D-;在对NUCLEO_H563ZI开发板的调试中,VBUS没有连接起来,但客户开发板的设计中是需要连接的。以此来告诉USB Device端(STM32H563ZI)的软件工程,USB线连接到PC上了。
5 H1 U; }9 q& _& Q: m$ b& u
2.1. 使用STM32CubeMX配置并生成工程(不带trustZone)

- U- E6 M7 o6 K- G- V
; g" a$ K' v5 O4 g; F1 w8 B

  U" J' h) b* L( N" k, n: o+ y9 ~

: r7 j  m+ b& c. u. {
第一步:开始一个新的不带trustZone工程:

2 g& T7 I% J7 v

* ?( j2 m1 y. F  g9 ]
15.png
# f7 i' G; _: |
4 Z: x' E- `5 k7 k

# Z# h$ f# y( M0 I
第二步:配置USB外设,基本上选择默认配置,同时使能USB的全局中断。
* H0 Y/ n% V; I' K
) D, y) B. _+ f
16.png
+ E3 z0 W* U7 A7 w
第三步:配置ThreadX外设,使能Core,其它先选择默认配置。

# @9 Q- P, P; X3 }

2 I! Y7 ~! O8 }$ T7 y' J
17.png
* M' K1 V, H* {1 p! R
$ w( e) }1 i5 Q. Z% x" t  E
9 j: v% _  X! f% v) V
第四步:配置USBX:

3 Y# r* y8 P, @4 z+ x& c
1. 使能Core System。

4 ^- N+ i" A' K4 Z; t4 M
2. 在UX Device FS中,选择Device CoreStack FS和Device Contronllers FS。
4 A, ]- Z6 M* R3 j
3. 在Device Class FS中,选择CDC ACM。

( {  N. }; F$ }  s- L8 s# d  Q

4 O: t, K" L1 `; D2 a6 b8 R$ ~& z
18.png
6 x0 D9 ~  U% t  s  Y& u# l' _
0 S5 g  }( H3 L8 B6 D4 ]

% i% E0 M3 c; _9 w" W
4. 配置USB基本参数。其配置除了对RAM需要设置外,其它选择基本配置就可以了。

! X7 c& \5 L8 U' W

: m9 X/ D* s) Q" g8 @
19.png 9 }* i: _& |$ Q$ {
5. 其它的Platform,选择USB。

! R& H0 A/ a6 _' d; R: D1 @
20.png # a% Y- c( `$ I; _

9 u4 r; \5 X& S% m( y5 o

3 m. b9 ?# {2 v. L* i% t/ z
第五步:配置SYS,选择TIM6作为系统滴答时钟的时钟源。
1 Y# K# o- g- x9 x. b  x# u( f  `
7 W  \- A; a8 U7 ]! N
21.png " A- G0 T  [  g* p* p& f+ ?

# K. F* F0 y/ [  K* c8 B

: M6 T* M/ l( x0 ~) B7 }" u6 l0 K
第六步:为了模拟USB的断开也连接,使用一个GPIO来控制USB的断开和连接 (GPIO_EXTI13);并且使能外部中断。

1 }! ?" d9 z6 P- g
6 D# ?8 Q5 f1 H! _% }/ O7 y' S# b( c
22.png % b9 X; i) O# x! p! S2 p
, Y7 @6 G( o7 l% ?3 e9 z
$ _1 E' L# ~; j1 C& }
第七步:配置时钟,选择默认时钟源。配置系统时钟为250MHz。
2 g% Z: L7 m" l- s$ U
0 a4 P$ [, A  U
23.png # x" f9 d# i8 M. W" ]8 L

% T% {8 u- l5 f7 p) u

% X- Y5 k) y9 o% y
请注意:使用HSI48作为CRS时钟,同时注意选择HSI48作为USB时钟源。
4 j, n& A+ p$ @+ _" J& `* M
& u) _$ ~' g4 c- [- w: t
24.png : X! }2 I$ Q0 m. m
1 n1 c' f9 e" ?: n: z" ]+ w
9 v/ L, ]- p- |  c. R2 z7 }* ]- x
2.2. 使用STM32CubeMX配置并生成工程

: [; H% L( }, t8 l/ t( G- T3 r
定义一个自己的工程名,由于用的RTOS和USB,所以需要适增加堆栈空间:
) r) w9 V& \5 m5 M6 I
, r5 A* X7 {7 Y6 _8 A
25.png
0 ?/ J; C: l# r2 p
最后生成的工程文件的结构如下:
/ m1 A( v. W4 d( T
5 Z0 N- V6 _" T( t
26.png
% T$ a2 V' s9 S3 F% Q# U) T: W5 D# q: w  k3 z, P* a- x
2.3. 参数配置的解释

' F3 G1 [9 }% b0 ^% G$ \
需要调整USBX Device System Stack Size和USBX Device memory pool size的参数。
" w9 j, T$ n3 l# Q7 S+ b4 Q

+ P* L" Q1 u- S8 o4 F. {
27.png
7 M) u; r) a- Z% ]- V
调整依据请参考如下内容:

! o& P( h8 t; c( J

( i/ m9 a" `) b, D2 B+ ]2 M
28.png * W* B9 R4 n* w" r! i2 [5 u" a  ]
8 E4 ~6 x9 P; ~3 q+ Z: @

: o" T! k) a0 f% |  Y- m
要定义USBX设备内存池(USBX Device memory pool size)大小的内存量,必须考虑以下因素:

& T& b, N, b, @. a+ @
1. USBX设备系统堆栈大小(USBX Device System Stack Size)
4 |% G0 n0 h/ @7 k
2. USBX设备应用程序线程堆栈大小(USBX Device Application Thread stack size)

  }7 s9 `! g6 g6 Q- k/ d
3. USB应用程序创建的线程堆栈

+ l* j- h: e  g4 T( U
4. 以及RX和TX缓冲区

: O$ @, Z, [! I, n; j8 ]3 b# h
' y  y$ a- p) s) ?
29.png
, |2 l+ U3 N- l2 }+ N0 ~9 z4 R& g* w9 l6 U4 i
03
1 N8 m3 ?+ _1 O' `
对工程进行完善和修改
2 T) N) n- h' M
由于CubeMX的生成的代码有限,生成软件工程后需要对工程进行完善,具体改动如下:
% i* i0 m/ }, M6 \2 V
$ m6 [7 r( {' U; ]5 n* B1 l  C
30.png
% {! f9 P. z: q- D( J& l
* G! x6 k) T  Y$ N& X

& f6 [  x9 e5 y4 \9 v/ i9 v
代码开发的第一步是将ST HAL USB驱动程序与USBX固件连接起来,然后初始化USB外设。还需要添加一下必要的代码,下面蓝色部分为需要添加的,绿色部分是生成的代码,你可以搜索绿色部分快捷收到需要添加的位置。

3 l* x8 [! F5 c2 i$ y( r+ l. D2 v/ W. b+ a
第一步:在“app_usbx_device.h”添加必须包含的头文件和需要用到的函数声明。

; L" T. Z0 r: }" {
  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>
复制代码
. a$ W( `  H# S3 z; ~' Q8 x) _
第二步:在“app_usbx_device.c”添加需要用到的变量和函数声明。
; u3 P' s" @; q: q
  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>
复制代码
* b: B0 q; G- H$ R+ ^
第三步:在app_ux_device_thread_entry函数中,添加链接驱动程序和初始化USB外设的代码。
" b  `. [8 u+ u/ O! P
  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>
复制代码
. R4 C" i* s( U9 _/ O  |
先调用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。

" U* u; X% p# }  H3 n

+ ~7 K% I$ ~4 j3 I
31.png
5 a! A9 v8 t- U1 n- _8 W
使用内存的第一个地址来存储BTABLE,它是端点缓冲区的地址列表。BTABLE为每个端点存储8个字节。由于STM32H563有8个端点,因此它最多可以消耗64字节。
: f1 D! D/ s, V6 p
0 t2 v+ O! i. k) o( K9 s8 v
' d9 }6 q  G- y+ V

7 _* d4 X/ |8 T2 p
在本例中,我们使用从0到3的端点,因此可以不使用剩余的其余部分。如果需要可以减少TX Ep 0缓冲区的偏移量,优化内存的使用。但是,按照这个表,您可以毫无问题地分配所有8个端点缓冲区。
, Y. w) r$ J- k, Y
4 }) v# n, J8 ]! Y) T. J) a

, u+ F% p/ A  m2 `7 M
: w, S* S* K) T
CDC类的配置描述符一般包含一个接口0(Interface 0),一个控制接口,另外一个是数据接口(Interface 1),除此之外,IAD(Interface Association Description),这个是可选的,根据实际情况来确定是否需要。

- a$ m9 ~) a: P" w
6 u! d1 f8 t& E; J1 s
3 S, e0 y5 q) y
& G: M# ?1 V  \9 v' P) d  V
下一个调用的函数是ux_dcd_stm32_initialize(…) ,它负责将HAL USB驱动程序链接到USBX应用程序。最后,我们调用HAL_PCD_Start(..)来启动 USB PCD外设。

% J- d: [7 V  e1 r, X1 q/ ]/ H9 o
0 \8 Z% p+ x. ]  }

/ h5 f7 a4 T8 M( P
- F: N5 _) u9 p2 ^
第四步:在app_usbx_device.c中MX_USBX_Device_Init(…)函数的最后用户代码部分,创建两个线程来处理数据的写和读,以及一个消息句柄,具体的代码如下,最后一个参数为TX_DONT_START,意思是任务先挂起,之后再启动。

0 G# r7 w% ^' S: j
  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>
复制代码
) d2 U& K0 j  y, C2 R( a) b
第五步:下面的usb_connect()函数是根据客户需要设计的。在PC端连接到设备端STM32H563,VBUS连接时触发中断后调用的函数(客户产品中把VBUS分压后连接到一个GPIO口,由于客户第一版本的PCB没有设计这部分电路,因此调试时使用按键中断替换了Device连接到PC的情况)。Usb_disconnect()函数,是处理STM32H563从PC断开的情况(调试时使用按键再次按下来替换VBUS断开的具体功能)。保留了信号量,程序处理中如果不使用这个信号量可以删除,具体请参考软件工程文件:
2 h, E  V8 G- ]# u5 A5 V
  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>
复制代码
& d' ]8 |& u  ~% e' ^
第六步,在ux_device_cdc_acm.h文件并添加必要的头文件包含和函数声明:
/ V/ C* ?2 X8 _0 t- ~9 [
  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>
复制代码

! X) z; I- X% S" a" S6 V
第七步,在ux_device_cdc_acm.c添加私有变量并在“USBD_CDC_ACM_Activate”初始化。
8 X; M( V( |$ t  D% P) y6 e! _" ~
  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>
复制代码

8 p  M% {, D+ u- H+ o' b. }
这样,我们已经有了USB应用程序的功能。类资源在ux_device_cdc_acm.c文件中可用。

. i1 F' e7 C. @( O9 n; T; K$ e. ]+ e
第八步:在“ux_device_cdc_acm.c”中定义线程的实现程序。

4 g( G) b6 Q5 j) z
  1. <div style="text-align: left;">/* USER CODE BEGIN 1 */</div> <div style="text-align: left;">
    : L$ V( X( a- @# [2 v
  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>
复制代码
4 H1 S0 O% N% _. j& V
8 [2 z3 x- W( g7 l
04

, @& z1 J6 c8 ?7 t- H. Y! M, H
对代码进行的测试

! u: k7 @( J0 u8 [9 M
对软件工程添加代码后就可以进入编译和调试环节。

' v3 S4 _: R' U

% M0 s5 ]1 _6 ?+ z3 }
客户第一版的硬件产品板上无PC13按键,要手动触发按键中断,需要在下面蓝色栏中写‘1’,然后按下键盘中的回车键来模拟按键按下的动作。(NUCLEO_H563ZI硬件有这个按键,可以直接使用这个按键来生成按键中断)。

1 w5 |: E& f) |1 f* k! V6 h

0 X9 y+ T0 H8 i3 a+ B6 O! E: G
32.png
2 }+ t8 ^  c7 @/ i, f
或EXTI_SWIER1.SWI13中写1回车后:
' k! w  y  o: i4 r; I
5 \" c) H4 X3 I0 d
33.png - N" C( Z/ L+ E9 T
然后产生外部按键中断:

  v, J! L! |6 ?
* k9 Z* F5 z- u
34.png
2 g) B5 G3 K6 I* n9 g  }: L* B2 O
使用USB线连接PC,并且程序全速运行后会看到识别的端口号:

9 b: ^; \7 U5 H) Y$ Z- A8 _; o

: z& }7 t( a6 f3 Q5 V. r( Z7 e3 f. |
35.png ; T6 _) I+ E6 d- b( {: P' U2 m
代码运行后在PC端打开UART调试助手,可以看到从虚拟串口接收到的内容,然后PC发送“1”,在usbx_cdc_acm_read_thread_entry函数中设置断点就可以看到接收到的数据:

9 j8 x- k: @1 W% `1 i( c
. m3 h% G. F$ F  O9 |0 D& p
36.png # `- a) A+ z/ K0 b& w4 @
按键(Nucleo_H563ZI 上的 USER蓝色按键)第一次按下(模拟VBUS上电的动作)后启动(resume)usbx_cdc_acm_write_thread_entry和 usbx_cdc_acm_read_thread_entry任务,在第二次按键按下时挂起(suspend)那两个任务。

! S  }- {6 C5 V8 u
1 X8 K8 {' `. P2 @1 D
使用时需要根据情况修改HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13)函数,以实现使用Vbus上电代替第一次按下Key的功能,Vbus掉电代替第二次按下Key的功能。
/ e: y2 |1 d; j/ m, q6 e
$ `/ l% w# y: J3 c: C6 s* D
! ]; ]" V2 k0 @- e& }1 w1 X6 _

" V- G' H7 `! [! L; x) o: U+ F( U
代码调试时Vbus上电(第一次按键)和Vbus下电(第二次按键)之间需要暂停程序,然后在调试状态下,在观察窗中修改KeyNumber的值从0到1;Vbus下电(第二次按键)后需要在窗口中改KeyNumber为0:

& k- r0 f( ~% \* M- h( D- @

1 Q8 A. m- G9 j* Q. ^
( y/ K/ z9 x4 H! f6 p$ D6 k
+ x; I1 H8 L6 ^6 t3 @  P
下面的功能在程序中预留了,如果后续程序开发中不使用可以删除。

; a( k: ^" @9 l( j4 O, e& K4 l
  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>
复制代码

7 A- P4 K, u' \, f; R' B
初次上电后,请全速运行代码,等待初始化完成,第一次按下按键后走usb_connect流程(vbus 上电,参考HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13)处理函数,代码运行后PC端会看到具体的串口端口号。用串口调试助手测试过数据收发功能后,在调试中暂停代码运行,然后修改KeyNumber从0到1(在观察窗中直接修改后回车)。然后全速运行然后再次按下按键就会走usb_disconnect流程。之后再修改KeyNumber从1到0,再次按下按键(vbus上电),进入的设备枚举阶段…
4 L/ e: w: b/ F+ h8 D1 E* s( M8 j' ?+ B! j

8 A9 Q( h; P, b, B! E
37.png
* D$ H3 l6 S9 r* f2 {
05

0 D0 v( o1 h- r! ~' a5 h' H
小结
$ y6 q1 R) |! R) s" o
具体的软件工程放在了附件中,使用NUCLEO_H563ZI,然后修改硬件就可以进行测试。

/ [9 D' c# G' W8 `3 w

5 f" w9 h1 M/ r' P* n$ C" Q
收藏 评论0 发布时间:2025-2-24 09:28

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版