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

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

[复制链接]
STMCU-管管 发布时间:2025-2-24 09:28
01
" y, V8 P3 i& h; \5 C* C% q客户需求1 R* F3 ]% t7 D/ a9 v( v
客户使用STM32H563开发产品,需要USB CDC ACM虚拟串口的工程,并且要求不使用PD功能,而我们STM32CubeH5代码库中是包含PD功能的工程。于是协助客户解决这个问题,提供给客户不带PD功能的虚拟串口工程。* U  R+ C  J6 ^* Y8 K( p9 u' ~
: J: u+ Y9 K1 i
在STM32CubeMX软件中,选择ThreadX USBX模块完成USB CDC ACM虚拟串口的工程,并且不使能PD功能。
% K& X+ b1 r) b4 K/ i: |
3 S: ]* X+ v, y$ v02
0 ~  C& r( N' _# T! R7 q: |基本硬件和STM32CubeMX的配置9 B2 ^( v/ ^  x! k8 [) P
; p! J/ U0 A3 T( z! [
硬件方面使用客户开发板和NUCLEO_H563ZI同时来进行软件开发。
# @# @) W6 \/ D  f& v: v: Q
2 J0 t$ \% T9 M- S; I下面是NUCLEO_H563ZI USB Type C接口原理图:
5 h- D0 L* ]6 }0 f8 M  C
12.png 0 a6 u) w% a; v: |

; _- N1 R( m1 |; N! n9 Z3 I5 h

( t. Y6 F! I( w
由于不使用PD功能,需要对NUCLEO_H563ZI的电路进行改动,在开发板上 PA11、PA12连接到了使用PD功能的Type-C接口,因此需要做如下的硬件修改:需要把SB27、SB28上的两个小电阻换移到SB22、SB21的位置,这样做是把  PA11、PA12连接到开发板的CN12上,然后就可以接USB线了。
. v) R4 ?* A! {) j# J4 `* L+ @

3 ^' c. v  ~* Q0 W! o' r
13.png
3 [0 i+ i9 O8 F9 F  S
14.png
: F" Y. W' g! M' \1 y

0 g. A1 \# g# c; Z8 X  I
6 v1 ]  `2 a3 }. d# {
USB连接线需要接GND、D+、D-;在对NUCLEO_H563ZI开发板的调试中,VBUS没有连接起来,但客户开发板的设计中是需要连接的。以此来告诉USB Device端(STM32H563ZI)的软件工程,USB线连接到PC上了。
0 {5 h7 X8 e* s) r! i/ G  Q
2.1. 使用STM32CubeMX配置并生成工程(不带trustZone)

* b% @- G/ a6 E
% A: A, D" K* |; J1 C& t

0 ?- p: b) p4 M0 ^2 W6 y6 Z3 o4 h& p
# E9 X! J3 n0 _' I5 I+ \7 o- r& v
第一步:开始一个新的不带trustZone工程:

* N& s4 _# l5 l# H8 c4 U* c) X

1 K; H8 Z3 M# s# Q1 |( D
15.png
. z; f$ d  i% X2 b% ~) J
1 ]; H9 F! H  x0 v

4 N' G+ b% n$ [# [+ x+ J9 H
第二步:配置USB外设,基本上选择默认配置,同时使能USB的全局中断。

' Q) G* K  N* x. M4 K  v

% c, L; y  u( K) f" S
16.png
5 f0 h& a+ C  }
第三步:配置ThreadX外设,使能Core,其它先选择默认配置。

' [$ J# O1 D. J

) m1 ~) t7 `+ J9 ~
17.png
; R3 F' o6 T& X4 w$ H/ T7 t

) Q* x& _. L% ^, R
& p  }4 y8 Y! {9 g5 n' R
第四步:配置USBX:

+ i! A5 p9 F* Z. u7 Z: z
1. 使能Core System。
2 P. x7 r/ P' R! M" q$ b
2. 在UX Device FS中,选择Device CoreStack FS和Device Contronllers FS。

5 u0 h& S8 u, `- Z/ D
3. 在Device Class FS中,选择CDC ACM。

8 ?; s5 j% ?; I6 K

, e. s0 d- b2 l7 S4 j/ \. K
18.png + T" s7 G2 w: E5 @, K/ y. _. E& T. p* D

6 v' @! G0 k7 U  R
$ Z! u+ ?& N- L$ A  P5 X
4. 配置USB基本参数。其配置除了对RAM需要设置外,其它选择基本配置就可以了。

( q! M( P9 K% @0 I

  ]7 t5 L; |$ x3 N" {4 p4 W
19.png
$ X$ n1 z1 c2 ~. Q: U5. 其它的Platform,选择USB。

2 v! r6 _: ?  x5 M+ s# A2 ]- h
20.png
$ @- O0 {5 T7 c" T5 [

. [4 t, P3 M3 ^& |  A. b

5 Y" _& \) R  U  n
第五步:配置SYS,选择TIM6作为系统滴答时钟的时钟源。
3 U' ~! o6 J- b5 E/ W

( `4 ]# o* R. U1 c3 u" i( Z) Q; G; Q, M
21.png ( [) |6 ]( u. S3 ^
& a0 a" R& x9 T& C5 c

# N9 {/ p5 @, C6 @) l+ `( I/ M# Q
第六步:为了模拟USB的断开也连接,使用一个GPIO来控制USB的断开和连接 (GPIO_EXTI13);并且使能外部中断。

! _* n) k4 K  g6 E. {2 X
  B/ D$ J& w- J' Z2 _
22.png & m$ W7 M9 Z: x# ]) i) F
6 l2 G2 x* Q- T
3 o0 P  U$ E" U; ^) w
第七步:配置时钟,选择默认时钟源。配置系统时钟为250MHz。
( ]& U% b7 ~: u1 ?5 ]; ~, C

1 r6 D# B7 B1 L9 o' J, {2 z; U: L: y
23.png % C; `. y4 c3 O( L
; [( s5 n& m$ J- a
9 ?0 Z) r% f  {" n) k* q  {
请注意:使用HSI48作为CRS时钟,同时注意选择HSI48作为USB时钟源。

6 ^# j+ Z* _5 X1 M
) v- f4 Y' T7 ]+ V. F
24.png 7 M0 Z! R4 ~( D9 C
" M0 B* h! G2 ~* T
* M9 X) o. Y1 H& C
2.2. 使用STM32CubeMX配置并生成工程

' _8 c3 Z3 j) K9 G3 o; N. {4 }* q! G
定义一个自己的工程名,由于用的RTOS和USB,所以需要适增加堆栈空间:

0 i- d, ]6 l2 \  j2 f" }+ {
! h% J" Z. y/ K0 R
25.png % g* L; O& q; t7 {% {
最后生成的工程文件的结构如下:
! J7 W9 \/ G6 A  Y2 M( [

* Z' _. [3 t0 X# B6 k& a
26.png % u+ O2 v; U: G
( X6 x- i) w9 e
2.3. 参数配置的解释
9 d" h) _8 w; p: T, Q- q& r9 l
需要调整USBX Device System Stack Size和USBX Device memory pool size的参数。
+ b0 c6 b; Z. H7 F  I+ e

8 `# N$ T. g; O% Y
27.png
; P, y- v+ `& x  \
调整依据请参考如下内容:
  ], p+ `$ W2 m8 K$ u4 }+ c1 C" s
9 H) z( {! d/ {' U4 _0 X# l
28.png
( K- H( }% l# E: t$ N# `( b
* m& h. s% }! A7 n0 [3 d

5 a3 a: K8 q, e
要定义USBX设备内存池(USBX Device memory pool size)大小的内存量,必须考虑以下因素:
4 a9 x: f, L4 P- F7 u5 t
1. USBX设备系统堆栈大小(USBX Device System Stack Size)
- g8 T1 R  J) m
2. USBX设备应用程序线程堆栈大小(USBX Device Application Thread stack size)
1 J/ ^7 d4 x. z$ p6 H! F, Y
3. USB应用程序创建的线程堆栈
- l/ A, J, `: M
4. 以及RX和TX缓冲区
, N/ R" C, j" }- t

" t9 F. y8 ]5 {2 J* g, P& d! a
29.png   l* T- K3 ?" ?4 C

# h1 K2 w9 V+ I$ s
03

. N7 u; i% m9 x7 {% J" E6 P8 g
对工程进行完善和修改
0 T" c. C5 Q8 F# M& p  ^
由于CubeMX的生成的代码有限,生成软件工程后需要对工程进行完善,具体改动如下:
. {, t+ I7 E- N- @4 m8 S  v. Q

0 W3 g0 N) Z! X  a: o, F1 q! c& ~
30.png
/ f: T$ D! f6 h' |/ S# [
- j* ]0 A: N' @5 a4 P# q, B
* B6 D- ]& b- p* _  V
代码开发的第一步是将ST HAL USB驱动程序与USBX固件连接起来,然后初始化USB外设。还需要添加一下必要的代码,下面蓝色部分为需要添加的,绿色部分是生成的代码,你可以搜索绿色部分快捷收到需要添加的位置。
4 ^  N+ o* @/ S  B5 B& A* F3 e
8 M3 o# y; Y3 _4 z3 F* w+ `
第一步:在“app_usbx_device.h”添加必须包含的头文件和需要用到的函数声明。

- g* h4 W7 m4 g9 a) S
  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>
复制代码
7 R: ]5 Z5 P- w" ]- Y! e* N
第二步:在“app_usbx_device.c”添加需要用到的变量和函数声明。
6 ?+ e/ Q4 ~# s2 b5 p; _1 H/ X& J
  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>
复制代码

. y% ?9 P* y: G
第三步:在app_ux_device_thread_entry函数中,添加链接驱动程序和初始化USB外设的代码。
$ ]3 g) E- M/ v; g- i6 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>
复制代码
  Z9 a4 a) i4 Q9 L- 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。
5 ^% D1 N% I  D; j( [

4 f/ [5 l) k( P( P
31.png
! }- `5 m. F# D* q7 j
使用内存的第一个地址来存储BTABLE,它是端点缓冲区的地址列表。BTABLE为每个端点存储8个字节。由于STM32H563有8个端点,因此它最多可以消耗64字节。

3 z9 C4 I: z1 ^2 ], t4 U

. `; F$ O; t; J0 g/ A

/ k- P" V+ W8 j! L! p0 z
9 C+ V5 P4 W) N8 u
在本例中,我们使用从0到3的端点,因此可以不使用剩余的其余部分。如果需要可以减少TX Ep 0缓冲区的偏移量,优化内存的使用。但是,按照这个表,您可以毫无问题地分配所有8个端点缓冲区。

, Y  T8 W3 e/ ~7 B9 G3 X8 J9 ~' w( a
/ c) G8 J+ S; N! h# j

) X; B" F9 M( ?# U5 i9 k
5 S# M  h/ B+ T* z
CDC类的配置描述符一般包含一个接口0(Interface 0),一个控制接口,另外一个是数据接口(Interface 1),除此之外,IAD(Interface Association Description),这个是可选的,根据实际情况来确定是否需要。
4 X- X! z2 [$ z$ a8 D. ?# M
8 T' J# m, p' {' R& Z3 N
: O0 M1 d3 M, L7 ?8 Z- v& D

. u& @6 y: b" @+ m- s; t
下一个调用的函数是ux_dcd_stm32_initialize(…) ,它负责将HAL USB驱动程序链接到USBX应用程序。最后,我们调用HAL_PCD_Start(..)来启动 USB PCD外设。

7 \& E! s8 W, B+ S- i8 k- ^
3 ]* a1 P6 q$ e: S2 v: Q# I% j

  I$ S  X+ p4 e& l! T

* q* w. h" Z6 O& z
第四步:在app_usbx_device.c中MX_USBX_Device_Init(…)函数的最后用户代码部分,创建两个线程来处理数据的写和读,以及一个消息句柄,具体的代码如下,最后一个参数为TX_DONT_START,意思是任务先挂起,之后再启动。

1 ^( O+ z+ J& H+ f
  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>
复制代码
. J' U* e1 u- D% k# ~" F7 h
第五步:下面的usb_connect()函数是根据客户需要设计的。在PC端连接到设备端STM32H563,VBUS连接时触发中断后调用的函数(客户产品中把VBUS分压后连接到一个GPIO口,由于客户第一版本的PCB没有设计这部分电路,因此调试时使用按键中断替换了Device连接到PC的情况)。Usb_disconnect()函数,是处理STM32H563从PC断开的情况(调试时使用按键再次按下来替换VBUS断开的具体功能)。保留了信号量,程序处理中如果不使用这个信号量可以删除,具体请参考软件工程文件:

8 P7 v/ U4 F* d0 @7 f& L* ?& [
  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>
复制代码

9 y, N, n; C: d2 |4 r
第六步,在ux_device_cdc_acm.h文件并添加必要的头文件包含和函数声明:
$ M. H6 E; ~3 P* K
  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>
复制代码

  j! J$ C0 {# {
第七步,在ux_device_cdc_acm.c添加私有变量并在“USBD_CDC_ACM_Activate”初始化。
) Y0 S6 Q, {$ W; R* L, S3 O
  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>
复制代码
2 [2 j$ v3 d) V5 R7 T1 h
这样,我们已经有了USB应用程序的功能。类资源在ux_device_cdc_acm.c文件中可用。

( ^! K, F8 S2 W) y! D  M* D' T5 w; k/ ]2 s/ I! ]' v0 v1 l2 z
第八步:在“ux_device_cdc_acm.c”中定义线程的实现程序。

* z4 M  Z: z* C+ Q' E# j
  1. <div style="text-align: left;">/* USER CODE BEGIN 1 */</div> <div style="text-align: left;">
    2 ?& \% ^! L/ O* ]2 n- r
  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>
复制代码
% l1 U8 u( q8 f4 s& H" p

4 W5 o0 d: b7 K# x( Q2 L0 b
04
& T# |+ j5 o6 ^) n
对代码进行的测试

" l$ r3 z$ {* Q) U
对软件工程添加代码后就可以进入编译和调试环节。

2 g; y0 b: P  e  W& N) i6 s' ]
& c1 m# W3 }0 q
客户第一版的硬件产品板上无PC13按键,要手动触发按键中断,需要在下面蓝色栏中写‘1’,然后按下键盘中的回车键来模拟按键按下的动作。(NUCLEO_H563ZI硬件有这个按键,可以直接使用这个按键来生成按键中断)。
! q4 q" y2 m7 z& d. s5 O
. @. q4 W. c; n9 c- O2 b
32.png ; m- @8 L, X7 a0 [, M0 q
或EXTI_SWIER1.SWI13中写1回车后:
6 l9 O$ H+ p, p3 r
# \; p& l! u% T9 D1 k. x
33.png : x6 w0 `3 N- [3 C# c3 R( A" G
然后产生外部按键中断:

* [$ K3 m+ J# l5 q" h* |- }: q  o/ k+ p
, X; }' N7 \# \* z4 |' q
34.png
. w* m: d5 g: P* H8 w  Y
使用USB线连接PC,并且程序全速运行后会看到识别的端口号:

$ z7 Z) u3 j. k- s
& B" j  r; ?7 F1 v: N
35.png # H; J6 r5 ^: s  j# y, o6 v
代码运行后在PC端打开UART调试助手,可以看到从虚拟串口接收到的内容,然后PC发送“1”,在usbx_cdc_acm_read_thread_entry函数中设置断点就可以看到接收到的数据:
3 A/ C) f8 }; a$ Y# y% B6 [
; l8 p, B1 S. z! J% f
36.png
6 g/ {. ~5 \' G2 P' v
按键(Nucleo_H563ZI 上的 USER蓝色按键)第一次按下(模拟VBUS上电的动作)后启动(resume)usbx_cdc_acm_write_thread_entry和 usbx_cdc_acm_read_thread_entry任务,在第二次按键按下时挂起(suspend)那两个任务。
$ D4 B! m3 {( {& _( Y9 Z
& G* u$ d8 h2 ?/ A5 h5 \+ C4 n1 Y! \
使用时需要根据情况修改HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13)函数,以实现使用Vbus上电代替第一次按下Key的功能,Vbus掉电代替第二次按下Key的功能。

$ X# `4 Q! v# f. [7 @- _3 F( P
, |) i; ?( }6 v; G$ B. H
+ Y$ q: o6 }) T: ~" Z. \- V% [
3 X+ `0 u! [  C( ^
代码调试时Vbus上电(第一次按键)和Vbus下电(第二次按键)之间需要暂停程序,然后在调试状态下,在观察窗中修改KeyNumber的值从0到1;Vbus下电(第二次按键)后需要在窗口中改KeyNumber为0:
# g% C. a* N# _4 \
( d0 ]3 T$ Z- F

0 h" r: u4 r& g; i$ ^0 p; S

2 l+ J6 @9 ?) ]( N* w' c$ q0 U
下面的功能在程序中预留了,如果后续程序开发中不使用可以删除。

' Q9 h& y2 \& n
  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>
复制代码
/ o' r, ?0 c! c' s3 t$ ?
初次上电后,请全速运行代码,等待初始化完成,第一次按下按键后走usb_connect流程(vbus 上电,参考HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13)处理函数,代码运行后PC端会看到具体的串口端口号。用串口调试助手测试过数据收发功能后,在调试中暂停代码运行,然后修改KeyNumber从0到1(在观察窗中直接修改后回车)。然后全速运行然后再次按下按键就会走usb_disconnect流程。之后再修改KeyNumber从1到0,再次按下按键(vbus上电),进入的设备枚举阶段…
( X+ X8 Y5 }* G: E" n' \
7 b) q* k* x) j* ^: \
37.png
4 o" c/ s7 b$ q) n" ?6 _/ V; m/ o+ m
05
$ e& e/ m9 J1 l$ H1 b% Q2 I3 ~
小结

) ~4 p5 ~- W2 k% J+ [, b  C7 V( s' c
具体的软件工程放在了附件中,使用NUCLEO_H563ZI,然后修改硬件就可以进行测试。

* J1 w2 f( y' M% L
; X  C) P# b' [! v% X* ?! A7 e7 {' q
赞 收藏 评论1 发布时间:2025-2-24 09:28

举报

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

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

所属标签

相似技术帖

官网相关资源

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