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

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

[复制链接]
STMCU-管管 发布时间:2025-2-24 09:28
01
/ [$ \# \( S+ n  q客户需求
7 v! {8 j2 e; k# g; n: n; {$ J/ Q客户使用STM32H563开发产品,需要USB CDC ACM虚拟串口的工程,并且要求不使用PD功能,而我们STM32CubeH5代码库中是包含PD功能的工程。于是协助客户解决这个问题,提供给客户不带PD功能的虚拟串口工程。
9 J$ Y- G: a* ]6 d! L: ^8 D! {& R+ e, Y, M' |- c
在STM32CubeMX软件中,选择ThreadX USBX模块完成USB CDC ACM虚拟串口的工程,并且不使能PD功能。$ d% M0 h* \7 b# A9 {3 R- ?

- c+ Z$ {7 a# f! r* Y) b7 D1 h8 f02
* j0 J/ R* }$ F0 f' S5 U* `基本硬件和STM32CubeMX的配置# u4 {3 ?* x5 @" i0 q* X( \! n/ @
5 z! l' x* P; c, T2 ]5 S5 I3 z
硬件方面使用客户开发板和NUCLEO_H563ZI同时来进行软件开发。' ]7 s7 z- Q$ k# q( o( m

) G0 Z, d3 h& ?& u( w% h, x下面是NUCLEO_H563ZI USB Type C接口原理图:, H. a. t: H4 F; F. n
12.png
" j! s. e+ h8 q1 E' E$ [
1 q+ ~& F5 n& P: ]: v( Z
) g. Q% W: S0 J1 O6 T# f" c
由于不使用PD功能,需要对NUCLEO_H563ZI的电路进行改动,在开发板上 PA11、PA12连接到了使用PD功能的Type-C接口,因此需要做如下的硬件修改:需要把SB27、SB28上的两个小电阻换移到SB22、SB21的位置,这样做是把  PA11、PA12连接到开发板的CN12上,然后就可以接USB线了。

2 U  R7 K0 ^; v

$ L9 A  t4 i1 @, O/ p
13.png
8 m  C, t6 |) V) H1 N- w
14.png
! N2 r- Y( P7 r8 L0 T  m0 h, Y9 m

8 H3 L- [/ U2 F6 [

  H  q+ `: n7 |
USB连接线需要接GND、D+、D-;在对NUCLEO_H563ZI开发板的调试中,VBUS没有连接起来,但客户开发板的设计中是需要连接的。以此来告诉USB Device端(STM32H563ZI)的软件工程,USB线连接到PC上了。

& f' l# B; e, y! W4 Z$ ~& E7 U
2.1. 使用STM32CubeMX配置并生成工程(不带trustZone)

1 M+ E0 A$ H& B3 T/ Z- L
' }7 S- |' x8 K* d! f; N/ d

' z0 n* m' H0 ?" a# p

) B; V% C, D% V" ?$ A  T, O# ^
第一步:开始一个新的不带trustZone工程:

3 A  B) z! H6 `' j, c4 ]0 [

. E, I9 Z; P9 }
15.png
7 i5 y" k# A6 F% ~* F" V! o

: o- ]) y# D0 T3 ]- ?

# z1 `. J. i' `/ B0 ?' ^" {
第二步:配置USB外设,基本上选择默认配置,同时使能USB的全局中断。

; r6 l. ~0 T, u, v8 b  \+ s
- q* n* P( w5 R# w; z; |' ~
16.png
4 j6 v! R3 h1 K
第三步:配置ThreadX外设,使能Core,其它先选择默认配置。

. Y$ D7 q3 c3 S6 ]/ M5 J2 [
) T# d  M  h% T4 i& G% F8 }
17.png
- C* k$ U/ ^$ M- _1 J5 b* s6 y* x2 Q' R

7 ^% J+ J4 C5 T
8 B9 N5 t3 V  w5 Q0 y, X) c
第四步:配置USBX:

$ v( }- Q  i& G  p! {2 _; n
1. 使能Core System。
9 o! [. s2 n3 e4 l7 `
2. 在UX Device FS中,选择Device CoreStack FS和Device Contronllers FS。
' w& ~, p0 B; {- z1 o
3. 在Device Class FS中,选择CDC ACM。
" t- Z6 n& ~+ p7 ?4 f
6 J6 l# c( w" Y( e$ ~" r% c
18.png - ?! I8 y2 I% s' {$ I5 t4 x" P

3 q6 K7 V' ]% D

! _+ T+ }! G! B* L) L" N- z0 E1 {
4. 配置USB基本参数。其配置除了对RAM需要设置外,其它选择基本配置就可以了。
* n! |1 [# Y, f1 d: j. ?
; {! \  O, E6 f3 u& w9 Y" c% v, G
19.png ! t0 K  `. X. C; L* L" O
5. 其它的Platform,选择USB。

% q1 \2 G6 e% t, n! R& t
20.png
' @5 y/ C  H' j5 P/ U

+ v6 F, t# ]( w, n2 [! N7 a

) r1 x0 W" j  F( W3 G7 s' F' k$ }. W
第五步:配置SYS,选择TIM6作为系统滴答时钟的时钟源。

9 _3 u: x8 O0 X/ h+ r

$ z' B# P2 Z+ o
21.png
) l% @( J" j" B

' v9 L8 s+ F: t9 T! T
! S: f; e( @$ K2 K& A5 S' L/ b
第六步:为了模拟USB的断开也连接,使用一个GPIO来控制USB的断开和连接 (GPIO_EXTI13);并且使能外部中断。

4 m$ S- v3 j( _$ y% P" C. i

" ^  b6 ?( A$ Z+ b
22.png ( I1 F# s( n: i# k5 R7 @6 w; c

! C2 J* l9 C7 N

: Z8 f" D+ ~, U1 ~/ J- H
第七步:配置时钟,选择默认时钟源。配置系统时钟为250MHz。

# v0 E& G+ W  ~# b
0 h, |/ ?" K- v9 I+ h1 c6 o% `
23.png 9 w' H7 U2 V+ m* N9 n& ^5 c4 }
4 |) E& I& a7 M
8 x  t3 `4 X! P  i
请注意:使用HSI48作为CRS时钟,同时注意选择HSI48作为USB时钟源。
8 V5 k% a4 G8 y
: F) A+ O/ [& ^, T! b" t
24.png ! E6 b- x& [) \' a2 m
. }9 [# o/ A$ u& F# I

" d* v: P; n# q" F, P
2.2. 使用STM32CubeMX配置并生成工程

! y4 d" m0 {3 a( R
定义一个自己的工程名,由于用的RTOS和USB,所以需要适增加堆栈空间:

; W, \# f6 L+ O
7 Q- A. ]5 Z0 N2 X
25.png # @8 y: m. ?( A0 K1 V" U$ @5 o
最后生成的工程文件的结构如下:
" [  ^4 n! m$ k
8 F7 l: b8 ^0 e5 S' u; t
26.png
/ E$ z/ ~) P& g! c$ d; ?
2 p+ v0 A+ k$ V8 \: @
2.3. 参数配置的解释
# {2 d$ p9 m& `- Z/ C( r/ @
需要调整USBX Device System Stack Size和USBX Device memory pool size的参数。

/ t6 @9 d( Y- m
( v- f. v+ ]& o, ~1 C; \
27.png
/ g8 @1 S9 m0 X! W% T5 Q
调整依据请参考如下内容:
, h' Y2 ?9 Q) f; X1 J3 B
5 e5 m$ ]; _9 o
28.png
3 ^7 n4 f, H) i

1 _2 `/ E% z* W9 d% g! ~
0 s" R4 ]1 [1 A
要定义USBX设备内存池(USBX Device memory pool size)大小的内存量,必须考虑以下因素:
" d, t! ?$ a3 G' K  J
1. USBX设备系统堆栈大小(USBX Device System Stack Size)

( u  r# `' G  M) T) v
2. USBX设备应用程序线程堆栈大小(USBX Device Application Thread stack size)

, F7 G! A7 `5 ?; y3 B
3. USB应用程序创建的线程堆栈

/ x6 s# ?( B+ A5 T0 L0 Z* E
4. 以及RX和TX缓冲区
, H  u$ V( P* b& W9 n
0 e4 ]/ C! k1 x# ^) r; w
29.png 1 `" P6 `9 c; b* u% u: F- B
8 t6 ^3 q, T7 i- V/ C+ q) h' r
03
% A) l! u' v; U& h2 S/ t
对工程进行完善和修改
) B: ~0 a2 m. G
由于CubeMX的生成的代码有限,生成软件工程后需要对工程进行完善,具体改动如下:

5 f7 j0 J( G$ `6 D1 ?
( b3 t* F! V2 D4 L/ }& J
30.png 9 [6 _) N/ `5 r: e: u

- y; B8 k8 J, z! \4 c4 Q5 `

4 @# l" U7 Z0 V% f3 b
代码开发的第一步是将ST HAL USB驱动程序与USBX固件连接起来,然后初始化USB外设。还需要添加一下必要的代码,下面蓝色部分为需要添加的,绿色部分是生成的代码,你可以搜索绿色部分快捷收到需要添加的位置。
0 A+ M5 g0 D" c, V$ ]; {! M: I4 O2 {& F

* G; v+ \  o" @7 I4 b; T2 p
第一步:在“app_usbx_device.h”添加必须包含的头文件和需要用到的函数声明。
3 I( `3 T, Y6 f3 A5 b6 G/ Q! p
  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>
复制代码

  [. I  ?; j8 e6 R第二步:在“app_usbx_device.c”添加需要用到的变量和函数声明。
3 s0 ^; u  G& r0 \1 H
  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>
复制代码
5 C: l+ j& c/ ^( `. k
第三步:在app_ux_device_thread_entry函数中,添加链接驱动程序和初始化USB外设的代码。

5 X/ h, ~: d" i: A) V( `
  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>
复制代码
0 z* u+ L9 z6 E. Z! W$ N
先调用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。

( S% I' H* W7 y% f

; j  e& y* l6 ~* B
31.png * E5 [* r$ [' h! c8 r* Q0 q/ m! B
使用内存的第一个地址来存储BTABLE,它是端点缓冲区的地址列表。BTABLE为每个端点存储8个字节。由于STM32H563有8个端点,因此它最多可以消耗64字节。

' U% Z- \% g' B9 Y
2 y: O& a% J  o9 d2 J

1 T6 z. ]. M8 W% G" o% R- r( v4 [

% ]" r; ]( D: C. q1 _! K
在本例中,我们使用从0到3的端点,因此可以不使用剩余的其余部分。如果需要可以减少TX Ep 0缓冲区的偏移量,优化内存的使用。但是,按照这个表,您可以毫无问题地分配所有8个端点缓冲区。
' @6 G' @  I5 U( m! J# V
- W) ^3 r6 @2 j; S8 A- k* r2 r

1 r0 h; l0 f/ u& Z

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

! a8 r0 F: {5 y3 b+ G9 o. Z- `
% C9 {9 H$ t$ z8 o6 Z' U4 ^

) m3 y( ]5 n, I) ?6 j: Q

, e% }5 S* l' x+ w1 Z
下一个调用的函数是ux_dcd_stm32_initialize(…) ,它负责将HAL USB驱动程序链接到USBX应用程序。最后,我们调用HAL_PCD_Start(..)来启动 USB PCD外设。

( M( j  S* F  p7 N: {( B: I

3 L1 e, [% Y4 {

. s4 N7 ]# ^' a7 x) i/ {7 i

3 g% i! j1 B' N2 b7 j, M
第四步:在app_usbx_device.c中MX_USBX_Device_Init(…)函数的最后用户代码部分,创建两个线程来处理数据的写和读,以及一个消息句柄,具体的代码如下,最后一个参数为TX_DONT_START,意思是任务先挂起,之后再启动。
. r/ O# ?5 J" p, U4 {* u3 q
  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>
复制代码

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

% K) C7 e" T: A$ r2 Q3 |& u
  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 I1 c9 z. @' s3 n
第六步,在ux_device_cdc_acm.h文件并添加必要的头文件包含和函数声明:
! `3 I0 H% V: }6 w: v- L
  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; W5 c  W( ?3 L* v- c: {* N
第七步,在ux_device_cdc_acm.c添加私有变量并在“USBD_CDC_ACM_Activate”初始化。

1 T. l9 `6 A0 U. R' e* g! I
  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>
复制代码

$ s& T- Y4 Z2 W4 Q- S6 w" `" T+ {
这样,我们已经有了USB应用程序的功能。类资源在ux_device_cdc_acm.c文件中可用。
' F5 J/ l; S5 W1 \; D+ S+ ^

) w: l+ `* s3 h2 }
第八步:在“ux_device_cdc_acm.c”中定义线程的实现程序。

# u7 ~% Y. p0 g! _) f+ N
  1. <div style="text-align: left;">/* USER CODE BEGIN 1 */</div> <div style="text-align: left;">5 V" _1 D1 i- m$ H
  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>
复制代码

+ Z$ O; s: w! s
, m) I  C1 g* ~  Z% q  N
04

9 k5 [- ~1 n& B5 p% w% Q
对代码进行的测试

6 ]( {3 N# f( a, a: V/ k2 R
对软件工程添加代码后就可以进入编译和调试环节。
5 e# {0 Q$ s' \; @
7 w% J0 q6 [" Z
客户第一版的硬件产品板上无PC13按键,要手动触发按键中断,需要在下面蓝色栏中写‘1’,然后按下键盘中的回车键来模拟按键按下的动作。(NUCLEO_H563ZI硬件有这个按键,可以直接使用这个按键来生成按键中断)。

8 }; u2 z4 z# W0 i" g$ `- x
- v% V4 ?! d" h0 J
32.png 1 r" |. F$ s: I9 G- Q- `
或EXTI_SWIER1.SWI13中写1回车后:
7 t0 n1 n9 l7 S$ V  B1 C
/ c; s1 p; y0 p1 e9 |" N
33.png
: G- f6 v% _8 n
然后产生外部按键中断:

) R$ g7 q: }( q1 L: ~! n
3 @# y& r3 n+ e1 {( d, x
34.png & t% E! B0 X7 y
使用USB线连接PC,并且程序全速运行后会看到识别的端口号:

, ?3 x! H; R# X, Y3 j
9 J9 T  w1 o3 w* O
35.png 2 w# i; F! X4 E4 a2 l* L
代码运行后在PC端打开UART调试助手,可以看到从虚拟串口接收到的内容,然后PC发送“1”,在usbx_cdc_acm_read_thread_entry函数中设置断点就可以看到接收到的数据:
9 P0 Z" `7 a. L9 Y- ]' c

1 C& Z2 r( s* e
36.png
! I* @5 J6 w# |. T
按键(Nucleo_H563ZI 上的 USER蓝色按键)第一次按下(模拟VBUS上电的动作)后启动(resume)usbx_cdc_acm_write_thread_entry和 usbx_cdc_acm_read_thread_entry任务,在第二次按键按下时挂起(suspend)那两个任务。

/ f+ {( I) H: n3 r# e9 v
& X2 e+ K) }: q  w
使用时需要根据情况修改HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13)函数,以实现使用Vbus上电代替第一次按下Key的功能,Vbus掉电代替第二次按下Key的功能。

, c1 y8 k" f$ |$ h: ]1 D4 t9 X8 J

6 _- J4 R0 _( G+ W% [
! T# @2 S( i' d9 ?/ z# F, W' x

' r0 T: M0 ]9 W" C
代码调试时Vbus上电(第一次按键)和Vbus下电(第二次按键)之间需要暂停程序,然后在调试状态下,在观察窗中修改KeyNumber的值从0到1;Vbus下电(第二次按键)后需要在窗口中改KeyNumber为0:
; R. I% Y5 e1 F7 o2 \; R

' _# h2 v* W0 [0 Z: |9 j- p

" \& P! P) D4 i" D& d8 V
. m  e) x( k3 s! G/ p
下面的功能在程序中预留了,如果后续程序开发中不使用可以删除。

8 r6 c! |/ D* n! D( S4 Q7 r7 q
  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>
复制代码
: C6 A1 c6 E+ L8 n, [6 M
初次上电后,请全速运行代码,等待初始化完成,第一次按下按键后走usb_connect流程(vbus 上电,参考HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13)处理函数,代码运行后PC端会看到具体的串口端口号。用串口调试助手测试过数据收发功能后,在调试中暂停代码运行,然后修改KeyNumber从0到1(在观察窗中直接修改后回车)。然后全速运行然后再次按下按键就会走usb_disconnect流程。之后再修改KeyNumber从1到0,再次按下按键(vbus上电),进入的设备枚举阶段…
6 }: q% j" u- e8 h. {% S$ B/ I
) x/ d& L% m# R/ ?: g
37.png 7 `6 ~! P1 r6 z" `
05

/ U. D) @0 c* Z& o& N. z6 ?9 D
小结
* ?/ g% ^7 [" E$ T! C9 q/ M/ U) j, R  i
具体的软件工程放在了附件中,使用NUCLEO_H563ZI,然后修改硬件就可以进行测试。

8 y; @; T5 b3 b2 @0 M( ^$ ~; o+ B; Q

- B. z4 v  _0 _9 G
收藏 评论0 发布时间:2025-2-24 09:28

举报

0个回答

所属标签

相似分享

官网相关资源

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