前言
' T3 [# p+ A1 k- m6 n! Z# J* |Cube 软件包的提供,极大的降低了开发难度。使用者在开发的过程中,只需参考 Cube 包中提供的例程就能快速的实现对应功能开发。开发者为了快速开发 UART 功能,参考 Cube 包中的 UART 例程,并根据应用情况,扩展了另一组 UART 接口。但是在应用过程中,发现两路 UART 不能共存。本文分析了这种情况出现的原因。
& L- @7 S, b% ~6 v7 n$ a% E# u' l5 W. U8 V/ z m
背景介绍$ |. C& l; t. c2 h; b0 n
在 Cube 软件包中 CDC 例程实现了虚拟串口通信功能,数据传输链路如下图所示。4 P" C, ?4 B; m ?, G% W& j
Q% ^4 `. o0 q) G, p' o- u9 m
$ s5 I' E/ K& c* |' S; {: t; v2 u7 K
在开发者的应用中,需要实现下图数据传输链路。根据应用用需求,推荐参考 Cube 软件包中的 USBCDC 例程。1 E4 b$ _2 k" B( v9 u. A
; I1 y* J$ c F/ v
2 o) q5 F2 @$ r9 Q0 A5 x2 X. |
& B) @% s u: {/ |
1 f9 w! `# m6 R5 U问题描述
% l; I( O) P- B( r( G; T `+ R问题复现平台: STM324xG-Eval; m3 H3 n5 |, H; _
例程路径:
" l/ Y4 f _% I5 i3 M+ ZSTM32Cube_FW_F4_V1.15.0\Projects\STM324xG_EVAL\Applications\USB_Device\CDC_Standalone/ @1 n- p4 t7 \5 I
3 M2 K4 t6 O+ }/ ]7 t3 ]
% ?2 ^- A. K# ?9 v6 F! ?
在对 CDC 例程了解后,参考已有的 UART 应用,新增了一路 UART。对应初始化程序如下所示。其中USARTx 为例程原有部分,而 USARTy 为新增的一路 UART 初始化部分。9 G! H' }0 V; K! B
- void HAL_UART_MspInit(UART_HandleTypeDef *huart)+ |' T" x0 `1 q2 \: l
- {+ p1 r9 s0 e, x9 d. ?$ k
- static DMA_HandleTypeDef hdma_tx;) E& s# y4 B9 _
- GPIO_InitTypeDef GPIO_InitStruct;5 I0 g2 Y$ x- s) r
2 c( H2 r) r! [- g7 @/ g- if(huart->Instance == USARTx )% j2 O( F% O2 p7 M& O5 \' }
- {
' p# E' u+ ~: A: ^ - /*##-1- Enable peripherals and GPIO Clocks #################################*/; @3 H+ x( F9 B# g: g
- /* Enable GPIO clock */
$ }1 B0 X2 _, B+ H) B: g - USARTx_TX_GPIO_CLK_ENABLE();
4 n8 E" o/ k/ G, U' `) t0 b- K; P - USARTx_RX_GPIO_CLK_ENABLE(); b4 N6 R1 l4 |. g
- /* Enable USARTx clock */* ^/ K& a" w3 W6 {& m$ h
- USARTx_CLK_ENABLE();
# X* |7 y. O4 ^. f5 A* m8 {" Z - /* Enable DMAx clock */
# t1 x4 }0 V: Y; B X3 e* H - DMAx_CLK_ENABLE();6 h2 n; u+ B, y4 k/ t) x
2 J/ j- t) t9 {- /*##-2- Configure peripheral GPIO ##########################################*/2 j0 P+ B$ I8 Y" |1 `( \) J$ m o1 x
- /* UART TX GPIO pin configuration */3 @; ?* v& B; A: u6 r( F0 j
- GPIO_InitStruct.Pin = USARTx_TX_PIN;
# q. i3 e. f8 l- @ U( v. M5 } - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;, b$ v" H. e- x; i# R! S5 X
- GPIO_InitStruct.Pull = GPIO_PULLUP;9 P& Z" a: E4 `; F+ {" I
- GPIO_InitStruct.Speed = GPIO_SPEED_FAST;) j# Y- f1 ?( \
- GPIO_InitStruct.Alternate = USARTx_TX_AF;3 S0 a! g B7 A, v9 r m. l+ Y
- 1 E: W) A% p% D6 E% V
- HAL_GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStruct);8 v S8 x6 B5 M
/ G. U' S1 R4 [, \* m- /* UART RX GPIO pin configuration */( ^/ b$ @, N Z( p4 k6 [
- GPIO_InitStruct.Pin = USARTx_RX_PIN;1 Q) B6 R9 B# ]# v, ?0 e; g. k
- GPIO_InitStruct.Alternate = USARTx_RX_AF;& ^- o+ a$ d& j- W
- ; i" v: c* \' U$ B: S
- HAL_GPIO_Init(USARTx_RX_GPIO_PORT, &GPIO_InitStruct);- C) }1 R3 P" B5 I6 k5 E7 ^: ~
- 5 p( L& x. p( \% o# w
- /*##-3- Configure the NVIC for UART ########################################*/
2 y1 `2 @% d" n# t( Q% ^ - HAL_NVIC_SetPriority(USARTx_IRQn, 5, 0);
6 T: s" ^/ Q( I) z: {' h. t - HAL_NVIC_EnableIRQ(USARTx_IRQn);
: |( l: _$ e( S+ z! n" x - 5 T, R2 }6 E ]# z; M; L6 q: I
- /*##-4- Configure the DMA streams ##########################################*/
: \5 V, f* i. u) T) _9 a - /* Configure the DMA handler for Transmission process */
- o5 s- N7 n3 g - hdma_tx.Instance = USARTx_TX_DMA_STREAM;
6 x. I" }+ O K" n- r4 J$ i
2 u/ {1 G& { O n6 Q( s- ]- hdma_tx.Init.Channel = USARTx_TX_DMA_CHANNEL;
7 V+ O" |7 `* r - hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
# o4 m6 M3 I, H3 Z2 d6 f) k2 C# s - hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE;) w) b2 J8 `' ^
- hdma_tx.Init.MemInc = DMA_MINC_ENABLE;
, d/ Y% F/ i! k j" a! ~ - hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
1 z0 f5 p% _: e) `* |: C3 a - hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;" H n5 @- q' D4 Y Q6 l6 f
- hdma_tx.Init.Mode = DMA_NORMAL;
, t' [) p! T; L% _( F - hdma_tx.Init.Priority = DMA_PRIORITY_LOW;
$ d7 [/ F0 p- F - hdma_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
8 {3 ~0 [7 h1 i7 ]# u - hdma_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;7 |, A& ~- }. R
- hdma_tx.Init.MemBurst = DMA_MBURST_INC4;
6 C, T/ e1 ~ h S, {; G8 m- m4 J( ? - hdma_tx.Init.PeriphBurst = DMA_PBURST_INC4;
1 ]1 i! @. \: z3 x/ [
$ u) c8 o; R$ M/ i1 l* Z9 F- HAL_DMA_Init(&hdma_tx);; M& \1 X1 j1 O" w8 Y
- $ M; [5 c/ B) _# [2 X7 i
- /* Associate the initialized DMA handle to the UART handle */
& [5 E j i$ H1 e5 s" I - __HAL_LINKDMA(huart, hdmatx, hdma_tx);
: w/ M4 h$ b: V1 P" l6 o/ {9 a - 8 }* M4 o2 g! t, y0 o
- /*##-5- Configure the NVIC for DMA #########################################*/
( A9 D* N* p7 }& S8 b# c \0 @" V - /* NVIC configuration for DMA transfer complete interrupt (USARTx_TX) */4 C1 _( |- L* t; z+ K
- HAL_NVIC_SetPriority(USARTx_DMA_TX_IRQn, 6, 0);
7 E# s3 f- R+ {4 b0 `: I - HAL_NVIC_EnableIRQ(USARTx_DMA_TX_IRQn);) H/ O6 w& F2 L) ], E! f1 V/ I
r' w/ ]/ K8 Y3 i- }
+ r+ N" h1 ]# x0 v' ?; Y - if(huart->Instance == USARTy )
I4 `- k6 z+ g- A$ O7 }& M. g: T i - {
( D. s- e- D: T! [% U; [ - /*##-1- Enable peripherals and GPIO Clocks #################################*/0 k' u; o! Y5 B
- /* Enable GPIO clock */4 [$ \& t' l5 N/ W3 V& I
- USARTy_TX_GPIO_CLK_ENABLE();$ l9 w* _6 V& v2 Q
- USARTy_RX_GPIO_CLK_ENABLE();2 \6 y% C5 I6 Q0 ]$ e- w. A
- /* Enable USARTx clock */
. H5 H5 h+ l, Z7 G8 n- c6 { - USARTy_CLK_ENABLE();
: k! p. ]& u6 \; {. @ - /* Enable DMAx clock */. u- {2 {+ Y1 H3 s, d5 k0 `
- DMAy_CLK_ENABLE();
3 X8 E# q- r' @4 y
- R% R6 Y! g, F- /*##-2- Configure peripheral GPIO ##########################################*/
" @, u7 p3 J# A2 b5 Z - /* UART TX GPIO pin configuration */
0 r: V+ A' F: C- r$ ] - GPIO_InitStruct.Pin = USARTy_TX_PIN;
4 N1 f4 N1 n% B& F% j0 t/ \! ^ - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;3 m& s4 n3 x# J: l8 r$ y, B2 X
- GPIO_InitStruct.Pull = GPIO_PULLUP;* A2 j+ Z# d' }. N8 E+ V0 C
- GPIO_InitStruct.Speed = GPIO_SPEED_FAST;" _' ^9 U+ }# R8 Y
- GPIO_InitStruct.Alternate = USARTy_TX_AF;
- N( `7 T3 Z8 w, J. _0 |+ i) p - 4 ~* ]9 I4 \* a! m) X
- HAL_GPIO_Init(USARTy_TX_GPIO_PORT, &GPIO_InitStruct);
/ O! ]" u/ _4 S- G0 W9 H - 4 z* j* T% x1 w3 k, U
- /* UART RX GPIO pin configuration */
3 d+ o% h9 X6 i' T1 Y - GPIO_InitStruct.Pin = USARTy_RX_PIN;
6 D3 z M" `. b. q) ?, m# o6 q5 J! d/ I - GPIO_InitStruct.Alternate = USARTy_RX_AF;7 Q% n! L5 |/ w2 R. P5 w
- - s' V9 l/ Y9 B# [( K
- HAL_GPIO_Init(USARTy_RX_GPIO_PORT, &GPIO_InitStruct);4 p3 \$ |" v- }" |. B! k
' `7 ?5 w: H1 U. {1 ?5 m( f- /*##-3- Configure the NVIC for UART ########################################*/. \" R- W6 `+ y2 k# e
- HAL_NVIC_SetPriority(USARTy_IRQn, 7, 0);! f" `# s0 s! ^0 Q
- HAL_NVIC_EnableIRQ(USARTy_IRQn);) Y$ @) L% G1 z8 s X( W3 I, P/ I
- ! P. s, v U! l [. S+ ^
- /*##-4- Configure the DMA streams ##########################################*/1 X5 C9 i/ q) t* k; H: y2 `0 y+ t
- /* Configure the DMA handler for Transmission process */6 C' {- Y# K: V5 l9 K3 d5 O7 r5 ?! ~
- hdma_tx.Instance = USARTy_TX_DMA_STREAM;- U) ]* F |7 K/ q) C
% ^# Y0 [9 _ P L3 e0 D! J- hdma_tx.Init.Channel = USARTy_TX_DMA_CHANNEL;
: Z% ~1 ?# q6 s/ j* n. K - hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
$ O$ x& S E( H( p) J6 e - hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE;
/ e- C( y: V( ?7 i. } - hdma_tx.Init.MemInc = DMA_MINC_ENABLE;
* F; a v: G2 ]' R6 A1 ` - hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
/ d' K1 x6 p+ l - hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;6 `- L% o2 c6 d
- hdma_tx.Init.Mode = DMA_NORMAL;
8 r% ], k' E# s% @0 ^: D" g0 x - hdma_tx.Init.Priority = DMA_PRIORITY_LOW;3 f- o. @" J. L" h& p% [
- hdma_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;8 V/ T: R B+ E
- hdma_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
% [8 H& e2 c% B8 B+ q/ ~; s/ N - hdma_tx.Init.MemBurst = DMA_MBURST_INC4;5 P8 ~7 M2 Z3 c* b! w
- hdma_tx.Init.PeriphBurst = DMA_PBURST_INC4;2 O1 }0 m2 f- _& X9 E7 g
- ( p, L/ C. _/ m3 `% l
- HAL_DMA_Init(&hdma_tx);, G! R" m# V* Y8 v' U0 i( R
- 4 H3 n1 U e+ P1 J( I5 _
- /* Associate the initialized DMA handle to the UART handle */
( ^. {6 R- ]$ }2 d& u5 ?7 Z7 L - __HAL_LINKDMA(huart, hdmatx, hdma_tx);
5 ?- C7 W9 |, F6 z. w' p, n8 R6 H - ! G9 j4 P( ?7 A0 F' ^- q2 K5 ^6 p
- /*##-5- Configure the NVIC for DMA #########################################*/+ `/ ]) G% i7 E' e! `6 g: V
- /* NVIC configuration for DMA transfer complete interrupt (USARTx_TX) */
# u: U* H# h) f6 r6 _- i K+ P5 r4 V! v - HAL_NVIC_SetPriority(USARTy_DMA_TX_IRQn, 8, 0);2 S3 G; Y/ p7 q; U
- HAL_NVIC_EnableIRQ(USARTy_DMA_TX_IRQn);
4 T; O1 [ g9 t4 ^/ D# Q( H - }
5 f5 r3 a' ?2 g) U - …
" O2 k I. c. f4 s; U7 T - }
复制代码 * V5 z! y* S. F8 ?$ |
在之前的 UART 发送和接收处,同样新增了一路 UART 的发送和接收。为了方便描述,这里不对应用层面进行描述。而是直接在例程中,时钟配置后执行下述语句,复现问题。
. t/ l, m$ J/ ?! [! d- #ifdef USART_X_INIT_ENABLE- h+ o* t+ j$ ^/ ~( s
- UartHandle.Instance = USARTx;! k: E# `) {6 M
- …- X6 C. p/ A! N" ]* [' q
- if(HAL_UART_Init(&UartHandle) != HAL_OK). ?, L% v+ O- @% g4 L1 G$ _. J
- …
8 H5 x9 I( q' d; ~ - if(HAL_UART_Receive_IT(&UartHandle, (uint8_t *)UserTxBuffer, 1) != HAL_OK)7 e* B" v: M3 L4 O2 y1 ]/ G
- …; |$ a' S: x! I' Z0 `& |; t; h
- #endif // USART_X_INIT_ENABLE1 P2 n2 ?* {1 A' e) U c
- #ifdef USART_Y_INIT_ENABLE& z; s4 v; u0 X0 E% t
- UartHandley.Instance = USARTy;
' m. P r3 J; \' Y M# y( R I9 E - …( d1 A) F4 q7 L# _( n: ^ a
- if(HAL_UART_Init(&UartHandley) != HAL_OK)
% v6 C# c$ D# v6 z - …
7 O W/ ~- L( R- [% _- | - if(HAL_UART_Receive_IT(&UartHandley, (uint8_t *)UserTxBuffer, 1) != HAL_OK)
6 K4 P: @1 Z p( { Y6 f& T9 u - …
" E8 [3 v% M! A5 \ - #endif //USART_Y_INIT_ENABLE& {: A! b; T$ ~0 Z8 U
- while(1)+ E1 K: s, w3 Q4 w" z9 A# _' i
- {7 s6 Y# Q& z a& A/ o
- #ifdef USART_X_ENABLE
2 Q9 ^7 p. c+ {2 Q+ ^0 o - HAL_UART_Transmit_DMA(&UartHandle, “UARTx Test”, 11);' m9 J5 Q3 a, z
- #endif //USART_X_ENABLE- E$ b6 [5 y0 V ~; l& `
- #ifdef USART_Y_ENABLE. A" f+ k7 a D* h- c u5 R* L) s+ h2 E
- HAL_UART_Transmit_DMA(&UartHandley,”UARTy Test”, 11);" f% H7 [2 |7 d; x2 v
- #endif // USART_Y_ENABLE; R- [$ p) s7 d* H, j7 C
- HAL_Delay(100);% l. ]& w \4 x$ u- Y* v' h
- }
复制代码
0 l' ~4 i1 c5 t# N4 I结果如下表。1 E b K- I U1 f/ M
7 {& M$ z j" i& m" M$ B
0 n) I, {2 _- H6 U) C
% V8 G0 w) R! d9 U3 t/ M7 z: f' j
* {' i P, D. o
* ]2 _4 A+ l5 o' u问题分析' ]. U2 Z+ H9 k5 Y6 w6 _+ ]
不同 UART 接口相互独立。UART IP 设计上,不同 UART 同时发送不会相互影响。初步判断应该是UART 初始化过程中,存在冲突。
. x( }" k0 n. S. E通过对结果分析,在 UARTx 和 UARTy 都进行初始化后,只执行 UARTx DMA 发送时,执行异常;只执行 UARTy DMA 发送时,执行正常。而在初始化中,UARTx 先于 UARTy 初始化,进一步确认是初始化时,存在冲突。并且将问题范围缩小到 HAL_UART_MspInit()函数中。7 W6 `) u8 R! Q% _- J
在线调试,发现在执行 UARTy 初始化时(UARTx 已经在之前初始化结束),UARTx 发送 DMA 对应的 Instant 和 Channel 值都被改变。其中 Instant 中保存着对应 DMA 的寄存器基地址。2 R; T# L' Z$ t
仔细检查 HAL_UART_MspInit()函数,发现对于 DMA 的初试化,两个 UART 共用了例程中原有的DMA 句柄变量,为上述程序中的 static DMA_HandleTypeDef hdma_tx;
. |) H2 e7 u. I4 Q) Y6 e; L5 Y而在 HAL_UART_Transmit_DMA 函数中,会对对应的 DMA 进行配置并使能,而配置过程会依据DMA 句柄中的参数。从而导致发送异常。
& R# A2 E8 ~4 ^$ }对问题的产生原因清楚后,在新增 UART 接口的时候,初始化函数 HAL_UART_MspInit()中同样需要新增 DMA 句柄变量,并利用在新增 UART 的 DMA 初始化中。如下所示。2 _# [. b7 G$ `. A* G0 |2 n9 h
- void HAL_UART_MspInit(UART_HandleTypeDef *huart)
- P0 J5 e! q1 E- f - {
3 r9 o [/ b, h: \. ?1 Y - static DMA_HandleTypeDef hdma_tx;
; T3 D2 T+ I+ {% N; m, n& Z/ J - static DMA_HandleTypeDef hdma_tx_y;
8 Q) E8 @0 l4 ^ - …
4 A/ H4 Z0 M5 j; s# A% P - if(huart->Instance == USARTy )
$ A- H( c, i" S - {
$ R, h3 P7 Y9 [1 {2 z, p& N6 V - …1 |2 X V+ t4 i2 T: J2 U$ X
- hdma_tx_y.Instance = USARTy_TX_DMA_STREAM;
+ j' ^. s* G4 f - hdma_tx_y.Init.Channel = USARTy_TX_DMA_CHANNEL;9 e G. x8 `5 r7 C
- hdma_tx_y.Init.Direction = DMA_MEMORY_TO_PERIPH;
1 R1 u/ {5 [2 v, |6 R7 r - hdma_tx_y.Init.PeriphInc = DMA_PINC_DISABLE;
9 b% q2 B! Z: J A4 T3 q - hdma_tx_y.Init.MemInc = DMA_MINC_ENABLE;
' b1 Q. ?5 B% }. y' |5 \ - hdma_tx_y.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
1 k% n8 v, a' v5 ` z4 C7 ] - hdma_tx_y.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
# _! B& W! w. ]' j - hdma_tx_y.Init.Mode = DMA_NORMAL;4 w/ v7 r; t1 q+ I
- hdma_tx_y.Init.Priority = DMA_PRIORITY_LOW;7 o+ p6 z+ Y4 B, M
- hdma_tx_y.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
( w" Q& G- f4 C6 z+ E - hdma_tx_y.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;, W7 G3 i8 [- [& \+ f# P+ e
- hdma_tx_y.Init.MemBurst = DMA_MBURST_INC4;, } g e6 E/ W' i; t
- hdma_tx_y.Init.PeriphBurst = DMA_PBURST_INC4;
1 j* Q8 W! |; Y# {0 R - HAL_DMA_Init(&hdma_tx_y);, H f4 r3 V4 |! V9 c- j) B
- /* Associate the initialized DMA handle to the UART handle */
1 s& U' {% Q% o1 z* a, n# h- y( I - __HAL_LINKDMA(huart, hdmatx, hdma_tx_y);( ^ V1 {/ I( O/ f7 L" L
- …
) }# ]2 t- c8 Q( q* h- q - }
复制代码 2 M* O2 \! m8 F' [; F2 z
) w9 U/ K) X% @9 Y总结
$ R- r% }! z) j所描述的问题,可以归结为一个开发漏洞。而导致这个开发漏洞的原因,更多的可能是在参考 Cube 例程时,对于各函数的了解以及变量的使用情况,没有逐个了解。建议在基于 Cube 例程进行开发时,能够对各函数及变量等加以了解,在针对应用进行修改过程中,能够减小错误率,从而减少排错时间。
8 G6 ?2 Q# Z0 i4 n z9 m" B
! j E: h- u3 V( D/ u. E! ^ |