本帖最后由 radio2radio 于 2019-3-11 14:32 编辑 * d: P) {+ Q# A& k* v0 a8 X
- i1 r0 T) a) D周末有时间,测试了一下STM32F103的DMA串口收发程序,基于CubeMX的,结果却是令人大失所望。
! B. ?" s) j. W: r7 Z+ I" \5 }2 u) F# p. I/ L
我在去年,测试了一下【中断模式】的,结果是速度超快。
/ s$ g& w& J' A5 B' b5 C2 m速度115200bps和1Mbps,双向同时收发100万字符无差错。 2Mbps,单方向100万字符无差错。4 d5 {4 i8 F6 a/ k
详情请见: STM32基于CubeMX的高速串口收发程序(中断模式)
. s1 _& p6 J0 {) X4 }8 Q& e7 n( |6 t/ ?$ ]& \7 n! j/ v
那时就有网友,问我为什么不用DMA模式,我也认为DMA的好处多多,只是没有时间验证一下。* |! U. J: h) i8 M* t8 g
现在,我得到的结果是,DMA模式用在UART这种低速外设上面,可能性能并不好,不如中断模式的。
! |8 V! p) c! ]* J: h4 ^+ p0 P请网友们给看一看,希望我的代码有问题。4 y$ b; {8 T0 l& l# {% m U
先说我的测试结果吧:% \* @4 d# \( D7 z$ v5 d( v4 W
STM32F103C8T6 Bluepill板,MCU时钟72MHz,用CubeMX配置出DMA模式的两个串口收发。, Q4 n8 B3 v- G* \2 `5 P- ?/ F
添加少量代码,就做成了两个串口互相收发。 与上面说的中断模式的用法一样。# } s* y r: t2 ^- n5 W! ]4 C* {
结果是,115200波特率,以10ms间隔发送接收40个字符,单方向正常,双方向同时收发就丢失数据。
5 G9 z+ Q( W2 ~0 _. W; X: X( c' e如果时间间隔放到200ms,双方向同时收发,也能正常了。9 H7 ?# l% b, F, b& E) C# p6 C
' T. w, t2 A, T: n+ a3 O/ k
下面,看看我用的代码:
0 }6 [& K9 I$ H$ a8 zCubeMX的配置过程,就不累叙了,附件里面有配置文件。) F' s8 w( k( b
/ ^. g( Q# r/ g# |! L' a% i: i" I; R- //main.c 添加的代码:- w+ o# ?& h4 m. Q( v
1 `- i* e# z& t6 p( o! s; M- //变量:
2 X1 A! b3 w3 P; W - #define DMA_RX_BUFFER_SIZE 1288 J. B5 U9 Y8 K8 J% M3 Q- F+ O3 j
- uint8_t UART1_Rx_Buffer[DMA_RX_BUFFER_SIZE] = {0};2 A; |5 L0 Z8 u
- uint8_t UART2_Rx_Buffer[DMA_RX_BUFFER_SIZE] = {0};/ H q* D7 |& D3 Q" h
$ o( \4 t+ ~4 M9 F- uint8_t recv_end_flag1 = 0;
" L, O/ H- G9 K - uint8_t rx_len1 = 0;1 G! d$ P3 _4 \+ S/ _
- uint8_t recv_end_flag2 = 0; t1 w5 f% M6 {
- uint8_t rx_len2 = 0;3 U0 l8 _" j6 `- p5 }* i
- ) b) N( q/ f* X" Z: f
- 。。。
+ G7 F" l# E) l) |
( C) P$ j5 y5 Z' U/ X1 z3 Z- //初始化
, ]) u2 y/ k8 l4 V" T# Z - __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
o$ G7 y, `; ^0 d/ q7 _6 M' L" l - __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
, a S" ~& t5 F/ a5 m -
/ U. I6 z* V: n5 [5 c - HAL_UART_Receive_DMA(&huart1, UART1_Rx_Buffer, DMA_RX_BUFFER_SIZE);
( C: v5 y; Q# y" X6 D$ t# K- o% L - HAL_UART_Receive_DMA(&huart2, UART2_Rx_Buffer, DMA_RX_BUFFER_SIZE);
( l! U9 k4 z0 e1 |- P% m$ b - 9 W8 C( f/ }; U+ x9 E9 q
- 。。。; n: B+ s# p6 W1 W/ d+ Q! r8 A
-
+ u$ z: g, h7 b% p5 K1 d - //main()/ W. G+ Y" V! h2 S
- if(recv_end_flag1 ==1){ //UART1 Rx, UART2 Tx
9 |0 y8 x# A& Z+ e0 G7 L4 u3 N' L
+ i* E! Y$ `& l5 h6 b7 C- |( e3 g- HAL_UART_Transmit_DMA(&huart2,UART1_Rx_Buffer,rx_len1);0 ~. O6 P% q1 ^
- - S9 _2 s- n" L2 r* K$ {
- rx_len1=0;
0 R, W7 ~3 Q* i" a6 Y% } - @* L/ c9 {3 h @- ^& E. m
- recv_end_flag1=0;. X: I; ^) n6 y6 ?5 M/ T
- ( G4 u3 k, {; J; e+ ?
- HAL_UART_Receive_DMA(&huart1,UART1_Rx_Buffer,DMA_RX_BUFFER_SIZE);+ ^/ k z4 F U
- }
' ~$ b! u# e: y/ ~* ^/ M* R -
# a3 W5 }0 u7 B- \# T! R# Q - if(recv_end_flag2 ==1){ //UART2 Rx, UART1 Tx2 d7 N9 `+ b5 s& x& ?) M
- & @- i4 ]% w6 \' i$ h/ M+ F2 y
- HAL_UART_Transmit_DMA(&huart1,UART2_Rx_Buffer,rx_len2);
E3 }: I' d( B3 [8 ]% j - 9 w9 l# I, E- I7 V A5 ^* ]
- rx_len2=0;$ d) x2 Y- x- s8 f# L
- ! b0 q/ o) k: _, T8 B
- recv_end_flag2=0;
2 j$ {2 h0 q7 p! n) F! \! k - + j0 w8 Y% y" R9 Q- r8 w5 Z, l
- HAL_UART_Receive_DMA(&huart2,UART2_Rx_Buffer,DMA_RX_BUFFER_SIZE);6 O* H: g! ?; N9 |4 o; o8 P
- }
; m- G7 K; [; b' Q4 }" H4 T - 6 Z8 H8 q- ~, Q% b
-
* E# u/ ]" a( _ T# q - ' a/ w" v# n: \$ |) q1 M1 O
- . ^/ t& D( `/ s% `
-
: Q2 Z7 J8 X3 h% }( x7 K - //stm32f1xx_it.c 中断服务程序里面修改的代码:3 ~; l* q; d1 B( e8 A) |( [
- / f7 s4 c+ B4 j- `( _5 |
- extern uint8_t recv_end_flag1;4 T& Q$ j7 l+ X2 c3 ~
- extern uint8_t rx_len1;
i! M4 ~( z6 Q2 [2 v - extern uint8_t recv_end_flag2;
& _+ k/ P1 s# J$ V- d. r - extern uint8_t rx_len2;
; V5 D, l; S$ k$ I( L& s3 E% c - ( h" O3 |# a- A' r" K& \# Q
- #define BUFFER_SIZE 128/ k2 N+ S2 c( c' J3 f& J% Z
( V$ p$ x6 b' l$ o H- void USART1_IRQHandler(void)/ c+ B- j* A! s- A# U" d4 e7 {) ^
- {
( r( @) f' u/ x) H( A - /* USER CODE BEGIN USART1_IRQn 0 */* @6 D( ~" |5 h- s0 M5 z
- uint32_t tmp_flag = 0;
9 w+ V! l! x/ E3 s - uint32_t temp;( k. e" l3 W' A+ G" H! O* D
$ P0 L$ l& C4 O1 G- /* USER CODE END USART1_IRQn 0 */
* F* g8 C+ k' T4 j! m - HAL_UART_IRQHandler(&huart1);
" \1 g8 k4 i+ A) u6 l- X! T9 {8 E - /* USER CODE BEGIN USART1_IRQn 1 */
* \2 l1 O8 D; }8 M - n( T G2 C! \2 ?# d& a
- tmp_flag = __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE);
& q+ P3 a, G1 H V
: y6 D7 e7 E( ^1 s0 P- if((tmp_flag != RESET)){1 S9 N ^0 K# M
- / _5 J" i$ E( C( ?
- __HAL_UART_CLEAR_IDLEFLAG(&huart1);+ l( P( H) ^- X3 i8 o5 G, y
- d" @5 X- y y F* B& b
- HAL_UART_DMAStop(&huart1);
+ {8 t$ s1 z) R6 F% Q7 w" P9 a/ K - % R# | z! x' h9 G+ ]+ o) N
- temp = hdma_usart1_rx.Instance->CNDTR;) X1 ^0 {- r$ h2 Z8 C9 M8 E( K. d
; ?- E9 d( o2 d' P& _& F! L ?- rx_len1 = BUFFER_SIZE - temp;
4 J/ t9 s4 Y: ]2 g2 [ - , @& ?! p* |6 F/ i
- recv_end_flag1 = 1;
7 j G, B9 ^7 }, f- M5 I - }; K4 O- L" T6 l1 A" z6 E
- /* USER CODE END USART1_IRQn 1 */
2 g% l! D! F1 _2 L4 o+ p - }
& f1 e3 y! s9 ]5 F& c. |
" R" _7 o; m2 L9 C1 ^6 F
7 b, ^" G; N1 T, H- V- /**
- ^2 Q; R" S4 f2 d/ Z - * @brief This function handles USART2 global interrupt.
$ Y. `5 s2 y* t! _+ D - */
$ b" p' ?" p0 L- m7 C+ N: E - void USART2_IRQHandler(void)
6 [0 ]0 q+ P; B$ z - {# f+ K& P& q- R' }, ~! r
- /* USER CODE BEGIN USART1_IRQn 0 */
7 D( k E. m" q/ n - uint32_t tmp_flag = 0;4 y. S% n2 T8 a: _9 y# d
- uint32_t temp;
! s* m2 w5 ?4 u! ^0 q, n
( n8 Z7 V# n; {2 ?9 d; v3 o- /* USER CODE END USART1_IRQn 0 */
6 z" k9 m- L; r9 h# T9 Z+ t - HAL_UART_IRQHandler(&huart2);
$ A& `4 V; {$ K6 [: L- N5 N/ {$ T; b - /* USER CODE BEGIN USART1_IRQn 1 */
1 a: o- @6 p" b - 6 |4 L N+ ` g$ c% i( ^8 h- H
- tmp_flag = __HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE);8 ?& |( w0 a% ^' }3 ?' J! P/ V
! @5 J- ^& D1 z0 K* @" A/ Z- if((tmp_flag != RESET)){
7 u; K* i2 f N5 A( b
4 o! z# p2 e& G& j8 d" ~- __HAL_UART_CLEAR_IDLEFLAG(&huart2);# e/ v8 Q* e! x) l) b: M4 o: F
- , e% g$ E. R* C" z5 P
- HAL_UART_DMAStop(&huart2);
2 P U) c9 a+ k - 6 L4 H e" }4 N( d3 Y+ X+ E
- temp = hdma_usart2_rx.Instance->CNDTR;
/ Y4 a1 V1 z/ b# @: F# C: z& O6 {
r/ _' F: T X% G- rx_len2 = BUFFER_SIZE - temp;
7 G1 S+ ?/ m+ G' B8 [( ~) }
/ O9 F( _, e5 C" Q# h0 o/ W/ c- recv_end_flag2 = 1;6 t& {- j p( N4 R$ ?6 r8 j# d
- }
: e5 s' u5 ~* k2 l1 J3 P - /* USER CODE END USART1_IRQn 1 */. _8 C }: e9 F# D- w3 M8 U2 r3 T: H( V
- }) K5 p4 `: ]( D. U! {
复制代码 4 A* E2 p# V! u) `8 ]
* L' z/ k" z" V, f& g4 X+ ]
9 a5 [: m9 E" F# ?上面的代码,也是参考了网上网友的帖子。 希望网友指出问题,和给出更好的代码方案。$ |( {& G% U$ X
也还听说串口DMA有三种方法,我这里用的只是其中之一的“空闲中断”法。
+ g' D! Q& a- t7 [, u0 l3 z* V* m" r5 ] {: g3 l+ d6 q! S
附完整代码:
+ m ~# \4 o( x1 {( E) M9 Y8 u1 M; M( N
8 ~' q8 w' P9 g5 X( o; s4 }0 q |
1. 数据包长度不超过DMA缓存的长度。2. 发送的间隔不少于200ms。
就可以115200双向同时收发无差错。. F! s z1 A$ _. b
( K6 Q6 U" K3 @; }5 |! m5 _
至于单方向收发,1Mbps,2Mbps,都没有问题的,放心使用。
所以个人认为LZ的测试方式其实问题在于,用中断CPU可以收一个马上发一个,虽然两边都CPU占满了但响应肯定快;但DMA是收完全部时再全部返回,那CPU虽然很闲,但响应就肯定慢。这样测试时DMA对CPU占用少的优势就测不出来。
同意,谢谢。
总之,串口通讯,本身是没有纠错重发功能,速度又很慢的外设。' _2 o& H6 s# W. a* t
影响丢数据的因素,也就是那么几种,照顾到了就没有问题。