本帖最后由 radio2radio 于 2019-3-11 14:32 编辑
5 z) [3 x4 u( c* a2 Q4 ^$ e, H+ o, S% e
周末有时间,测试了一下STM32F103的DMA串口收发程序,基于CubeMX的,结果却是令人大失所望。
' B* h9 ]4 u1 `' `0 A; u5 d. F5 L9 Q/ C7 }/ U* x
我在去年,测试了一下【中断模式】的,结果是速度超快。
+ j6 X7 a s, p& c7 k/ P3 b速度115200bps和1Mbps,双向同时收发100万字符无差错。 2Mbps,单方向100万字符无差错。* ~! }2 T! U1 }5 g; d9 R6 Q
详情请见: STM32基于CubeMX的高速串口收发程序(中断模式)
6 q( z% y" w6 D' x# r5 `( W) B
( k. }. O/ _3 e那时就有网友,问我为什么不用DMA模式,我也认为DMA的好处多多,只是没有时间验证一下。. X ~5 V1 } j7 x
现在,我得到的结果是,DMA模式用在UART这种低速外设上面,可能性能并不好,不如中断模式的。
5 n# ~. g5 ~0 G% W' }5 k9 W请网友们给看一看,希望我的代码有问题。
, E" [6 B# }; S; J7 Y/ X. w先说我的测试结果吧:" T7 w) S n3 y
STM32F103C8T6 Bluepill板,MCU时钟72MHz,用CubeMX配置出DMA模式的两个串口收发。
3 k" d1 P+ a" z- u- L3 X添加少量代码,就做成了两个串口互相收发。 与上面说的中断模式的用法一样。
; p0 Q" y: O" K! m/ N5 r结果是,115200波特率,以10ms间隔发送接收40个字符,单方向正常,双方向同时收发就丢失数据。
8 Z5 {4 m' Y8 g: ]6 U5 n6 {) k如果时间间隔放到200ms,双方向同时收发,也能正常了。
: y/ d! {' S' h R6 y, y- F
, w: Z/ p$ A9 M: d; f3 t下面,看看我用的代码:7 J3 v% a- w) J; z2 D: t+ h( \
CubeMX的配置过程,就不累叙了,附件里面有配置文件。' w, v U& U3 D5 w' j% L0 t) ~
/ m o( m6 Y+ }9 ?" u/ T% r
- //main.c 添加的代码:
2 \$ D7 h& d1 j* |5 e: v - 1 H- B( v/ s. f- h( E, r' Y; p" Q
- //变量:7 P% P) w) H( _; Z
- #define DMA_RX_BUFFER_SIZE 128
5 [2 t' z/ M) o" e0 ~ - uint8_t UART1_Rx_Buffer[DMA_RX_BUFFER_SIZE] = {0};
7 E2 z F9 v' ~, O% E7 [7 K2 P - uint8_t UART2_Rx_Buffer[DMA_RX_BUFFER_SIZE] = {0}; | n! m4 U, h. a6 X& X
- 1 z; m1 U$ K. D& F! Q2 z$ g
- uint8_t recv_end_flag1 = 0;2 ~5 b/ d! C7 y7 Z- h
- uint8_t rx_len1 = 0;4 v7 F, Y7 h" a% X+ g4 Y' m
- uint8_t recv_end_flag2 = 0;) P0 ?& y! Y! J |
- uint8_t rx_len2 = 0;
! K% i, [1 t6 u6 @7 ~, b6 m. o3 g; G - 1 V3 x$ r. y" p
- 。。。
$ U2 H! N' o9 q
5 a+ u; @2 x, Q D& F& u$ B- //初始化
' `% }9 e3 [& a M - __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
- P. F4 @' ^' z4 w( S$ O7 U. N - __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);% Y+ }- O. l: ]9 M: L" S) H
-
- V7 Q( R8 H. V+ c( t - HAL_UART_Receive_DMA(&huart1, UART1_Rx_Buffer, DMA_RX_BUFFER_SIZE);. Z6 [/ V1 L; X
- HAL_UART_Receive_DMA(&huart2, UART2_Rx_Buffer, DMA_RX_BUFFER_SIZE);
( m' d3 ]' _* [ |/ `
$ d0 w9 y0 r4 X7 M* I- 。。。
# f) k& ]' q1 i# u5 f -
& P5 j' [3 h9 Z q; x d" c) d - //main()+ f9 m$ j1 D; a) R- q5 I* p
- if(recv_end_flag1 ==1){ //UART1 Rx, UART2 Tx
b v; K; [ o: ^; l4 v - 1 u6 I6 m: Q. N- R+ Z4 m
- HAL_UART_Transmit_DMA(&huart2,UART1_Rx_Buffer,rx_len1);5 F2 q: _4 [6 x/ }8 t5 F+ u R
5 u0 Z+ b6 @/ O- rx_len1=0;
3 p+ Y- v% k3 r - . d$ V/ E1 j2 O, Q$ o8 f
- recv_end_flag1=0;
' u3 @ R& r! O, s5 D' x' G5 [
5 T6 ]7 N3 U" {- HAL_UART_Receive_DMA(&huart1,UART1_Rx_Buffer,DMA_RX_BUFFER_SIZE);
3 t8 `8 o! Q/ v( h' r$ i - }% e J' M& }. _6 p: A
-
1 V5 B+ `" |9 W0 l9 [, b+ F( u - if(recv_end_flag2 ==1){ //UART2 Rx, UART1 Tx V: s- a/ x6 V5 E3 N$ ?1 f
- ) f: T2 A% @ v# x% L6 ^" W# D- P- r$ h
- HAL_UART_Transmit_DMA(&huart1,UART2_Rx_Buffer,rx_len2);
9 s5 U& P" Q5 T5 y2 x# q - 3 O& }. z+ d$ d; x; x
- rx_len2=0;: O& @# w* }9 l- j: X+ ~
: B7 @% _8 w% U/ |3 S4 u- recv_end_flag2=0;
% x* `7 R) |# ^& \2 x
+ N5 { e- L2 U% W% v- HAL_UART_Receive_DMA(&huart2,UART2_Rx_Buffer,DMA_RX_BUFFER_SIZE);* N# I2 r& H# f/ p
- }' |' X! ]* R- d0 q# I
- ; p+ `8 O* k- Z- E7 g T
-
4 y+ _% z# @3 `! D6 i1 ^ - $ b0 f2 I0 Y% ]
- - \+ E- C8 `4 d0 u; X! W/ X) Q
-
" {- F' w/ w/ P7 S" A+ I - //stm32f1xx_it.c 中断服务程序里面修改的代码:
! h" |& I1 ?- V7 T: i8 ~" H2 `+ K
' q0 W! X4 w. P0 P9 }- extern uint8_t recv_end_flag1;4 r7 U; p/ j c/ i7 p3 \
- extern uint8_t rx_len1;
* a: ]! J- G) H; {: J# N - extern uint8_t recv_end_flag2;* f" P' `) F2 H" Z6 e3 ~0 b. b
- extern uint8_t rx_len2;. Q( R4 t6 s! W8 g& t8 ]1 F, V
- ; h5 ^+ H) X6 T( {6 H2 q1 M) N
- #define BUFFER_SIZE 128
' O3 F& `( x9 F* c3 ]. B
4 x" p, ^/ u1 w( X8 v3 F- void USART1_IRQHandler(void)$ s% z2 D6 |: ]
- {
1 ]8 K2 D' y8 Z; n; f - /* USER CODE BEGIN USART1_IRQn 0 */: n+ b6 Y' A O# U
- uint32_t tmp_flag = 0;' K& z7 |! X6 Z9 b
- uint32_t temp;+ \. a5 I6 S' N1 G" A; a
- , W0 x4 S, o, ~
- /* USER CODE END USART1_IRQn 0 */ ' H( \) Y/ [. \' N
- HAL_UART_IRQHandler(&huart1);' m# Y' U. j% g, v3 n& T
- /* USER CODE BEGIN USART1_IRQn 1 */
B% w. v+ Z9 @" w - 8 Z6 K& [5 |" n5 p- |5 n' {
- tmp_flag = __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE);9 B+ ?7 B$ b' J7 }7 M* Z
- " h, f. R; B' c, [" T7 F0 Q8 ~
- if((tmp_flag != RESET)){
7 s. W- z3 T) `6 f- u- L5 x6 p) y
+ \* {# f! w8 C: R- __HAL_UART_CLEAR_IDLEFLAG(&huart1);% m- _! ?! f7 Y5 H9 c9 }8 P4 W
; x& i- n9 o# E% i& C0 S& R- HAL_UART_DMAStop(&huart1);( v# `& f6 V% h$ V. Z; j1 i3 }/ X8 d
3 s8 o/ z; j# G& Z3 E- temp = hdma_usart1_rx.Instance->CNDTR;! }1 j4 n# i; H. q
- $ _0 b# u5 l2 N& K+ h' `
- rx_len1 = BUFFER_SIZE - temp;
; {* g H% x% w: Y1 o# P( i - , y$ ~& M% c, R/ p
- recv_end_flag1 = 1;- C6 o/ J* ~' n; d* K0 c% z
- }+ _5 D6 Y# X( R
- /* USER CODE END USART1_IRQn 1 */% z) i7 I& |2 K% S
- }* ?: C! F- q. N9 T9 O1 e
- o) t8 Z6 q* ] i5 { ?
) j6 R' F4 K# ]1 b; V- /**) a8 a( C( o- ^1 R* O
- * @brief This function handles USART2 global interrupt.5 Z; }: `' O. [% t7 o
- */
$ C9 \) j+ R: }3 x3 @% C0 d+ D% L - void USART2_IRQHandler(void)' R5 n/ Q+ G! w0 `; n0 h3 H
- {
# v8 \& \+ F0 @ a - /* USER CODE BEGIN USART1_IRQn 0 */
! G2 B4 v* D0 H6 z, _2 D - uint32_t tmp_flag = 0;
1 W$ J: E% s2 E3 K4 g. |( u - uint32_t temp;3 j! M e' R3 h; n3 N
3 Q4 O2 o% }% g, t' s- [3 Y4 a% Q- /* USER CODE END USART1_IRQn 0 */
! |4 p: M D( i1 E; ^' y) Q/ I - HAL_UART_IRQHandler(&huart2);; _- x9 ~/ `% Q0 o
- /* USER CODE BEGIN USART1_IRQn 1 */2 m+ ~' F9 w T; B9 \
0 D% i7 I, p M7 E; y# g0 q- tmp_flag = __HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE);: e8 a4 V9 D% P& w
- 6 t+ P4 f8 U. ?2 w0 s
- if((tmp_flag != RESET)){
4 N- a3 h8 Q& I: v% S- r4 _. H7 [# X - , _/ c) U) N2 F
- __HAL_UART_CLEAR_IDLEFLAG(&huart2);7 c) [$ S. A3 S g! J
' o& K4 @7 u* q0 A- HAL_UART_DMAStop(&huart2);
5 K M: k- G' [$ t" i3 ?
" q+ r: k5 c% t& v+ H6 C- temp = hdma_usart2_rx.Instance->CNDTR; b, Q3 i; P0 R" T% u
- 4 t) I/ `/ \3 ~+ E- ~) c
- rx_len2 = BUFFER_SIZE - temp;7 ?- M: d: c2 c- k7 v8 {
9 w3 W! N9 S0 |) N0 k2 h1 q/ f" s- recv_end_flag2 = 1;" P5 W/ o* `0 `8 e( e) a6 r
- }2 _+ o( ^9 V" ?5 i6 j) Z; k0 b& x
- /* USER CODE END USART1_IRQn 1 */2 i9 x; o" |. {3 p& G
- }; ^# J- G; w& D. L
复制代码 ( ^/ b4 M4 E1 b% Z- p- Q: S, L9 ^
' J- {( p" C9 g7 _9 u+ r# ~
; M6 ~6 d( `4 Z3 s4 @' j上面的代码,也是参考了网上网友的帖子。 希望网友指出问题,和给出更好的代码方案。
! h( Z2 T% o; @7 Q也还听说串口DMA有三种方法,我这里用的只是其中之一的“空闲中断”法。
$ n. t6 a3 X: [ o8 X0 O
9 V3 p A. w5 @: `1 w0 O& G附完整代码:
* Q& |' b" B# H6 N4 k4 U" H! N5 j4 X* l5 Q
|
1. 数据包长度不超过DMA缓存的长度。2. 发送的间隔不少于200ms。
就可以115200双向同时收发无差错。
' A$ w$ J; h) q4 w$ ]; m9 G* M
至于单方向收发,1Mbps,2Mbps,都没有问题的,放心使用。
所以个人认为LZ的测试方式其实问题在于,用中断CPU可以收一个马上发一个,虽然两边都CPU占满了但响应肯定快;但DMA是收完全部时再全部返回,那CPU虽然很闲,但响应就肯定慢。这样测试时DMA对CPU占用少的优势就测不出来。
同意,谢谢。; Z- {' T# f: \8 {* s
总之,串口通讯,本身是没有纠错重发功能,速度又很慢的外设。
影响丢数据的因素,也就是那么几种,照顾到了就没有问题。