本帖最后由 radio2radio 于 2019-3-11 14:32 编辑 $ g( ~' I3 u6 @7 @* d8 E- \& [" c
- l1 \+ v5 i; l
周末有时间,测试了一下STM32F103的DMA串口收发程序,基于CubeMX的,结果却是令人大失所望。
; S( @. @/ R! ^" n# U) O5 W) t& H2 Z w8 i
我在去年,测试了一下【中断模式】的,结果是速度超快。
c `8 w$ n3 \) b# V- `' w速度115200bps和1Mbps,双向同时收发100万字符无差错。 2Mbps,单方向100万字符无差错。
% \$ Y! @3 h: c Z0 A: ^/ D详情请见: STM32基于CubeMX的高速串口收发程序(中断模式). ^& {9 c4 V& N9 ~9 V4 j
X* _; y) v9 B+ T2 q那时就有网友,问我为什么不用DMA模式,我也认为DMA的好处多多,只是没有时间验证一下。3 F h0 k9 t: M- R
现在,我得到的结果是,DMA模式用在UART这种低速外设上面,可能性能并不好,不如中断模式的。; P5 n6 f& @6 P" l7 j" ~1 }% Z
请网友们给看一看,希望我的代码有问题。- c6 P) [' o; c' o6 k5 B
先说我的测试结果吧:, Q, b. I% _4 H2 V0 ?- S
STM32F103C8T6 Bluepill板,MCU时钟72MHz,用CubeMX配置出DMA模式的两个串口收发。) p! ^. f, Y( K9 P+ u
添加少量代码,就做成了两个串口互相收发。 与上面说的中断模式的用法一样。
7 r% i7 ~" B# J- s7 {结果是,115200波特率,以10ms间隔发送接收40个字符,单方向正常,双方向同时收发就丢失数据。
, x/ V1 f# A7 @7 }( T9 L, V8 V如果时间间隔放到200ms,双方向同时收发,也能正常了。5 m/ V5 q& E/ V: n1 o. a3 U6 f4 _
+ G4 J$ _" H+ b
下面,看看我用的代码:
1 h& \' J8 H N9 n$ J! vCubeMX的配置过程,就不累叙了,附件里面有配置文件。6 p F) O Q6 v& b( w& A+ I
5 Z$ v; Y! G$ Z3 |3 T- //main.c 添加的代码:5 M. Y/ |, z7 t/ @1 |9 ?- b: ?. i
- 0 T6 t) J% x; [0 _8 I
- //变量:
6 T5 U2 Q0 ~0 N - #define DMA_RX_BUFFER_SIZE 128
; ?! w: L4 i' ^6 {+ x - uint8_t UART1_Rx_Buffer[DMA_RX_BUFFER_SIZE] = {0};
1 @+ n/ |4 C2 X - uint8_t UART2_Rx_Buffer[DMA_RX_BUFFER_SIZE] = {0};( X4 v: O9 ]$ h Y Y+ \
: V2 R) D. {7 o! G+ G% o- uint8_t recv_end_flag1 = 0;* K$ o; _& o) W" l. e9 z, [& U g; b7 v( p
- uint8_t rx_len1 = 0;) |/ V. i* w% J& H8 n$ N
- uint8_t recv_end_flag2 = 0;
) ^' [& ]0 g* y( Z; d0 V - uint8_t rx_len2 = 0;9 R9 n, I' W2 I/ r
4 b& r$ v0 J T5 `. n8 W* f. y- 。。。% A9 q% t7 g0 J# X" q0 X4 x* F
* F* x! A _; m. }4 a- //初始化
9 Y! d/ U8 f1 ~" g* p( z7 z - __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);% h2 O A- v# m5 ^
- __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
9 y7 B, z( v `6 W( G - 0 r" m$ s9 B/ Z7 X V
- HAL_UART_Receive_DMA(&huart1, UART1_Rx_Buffer, DMA_RX_BUFFER_SIZE);
. O8 `- l5 u9 p& x) x. v - HAL_UART_Receive_DMA(&huart2, UART2_Rx_Buffer, DMA_RX_BUFFER_SIZE);& V7 V/ e( V( ^
- $ N1 A- Y! h9 d$ v, Y2 S
- 。。。2 S2 O! g+ Q* d2 c
- 4 Q" m% m1 u% x' e, |. e4 v8 f1 g
- //main()
% j! S* C9 L& W( H0 z1 d - if(recv_end_flag1 ==1){ //UART1 Rx, UART2 Tx- C% T; ^9 g7 t v
- * J& D3 u+ G" e7 `
- HAL_UART_Transmit_DMA(&huart2,UART1_Rx_Buffer,rx_len1);, G3 A+ P; p6 A9 H0 ~ P
/ {9 d# g" D! y& v- rx_len1=0;+ L! Y* ~# f/ j5 S3 N; ~
' Q' D- m% {0 y1 [% q6 D- recv_end_flag1=0;/ f; }+ I/ J* c! U! g! j
- 8 p1 }( h: S8 l
- HAL_UART_Receive_DMA(&huart1,UART1_Rx_Buffer,DMA_RX_BUFFER_SIZE);
+ m' d9 p# Y5 V* L! h0 h, O - }
( o$ o" M2 w9 k; u, e- c. K - 6 l+ `* V- \& K$ O8 H/ `1 R
- if(recv_end_flag2 ==1){ //UART2 Rx, UART1 Tx" u% D' K) [ \: O1 u
$ y A0 a; ^( h7 L0 [: Z- HAL_UART_Transmit_DMA(&huart1,UART2_Rx_Buffer,rx_len2);9 @% o) ?5 A m; v, R9 a3 e
1 n; y' v* J% ]" ]* a) R4 @6 ]- rx_len2=0;0 D8 g8 C0 \8 P" k8 S1 f
& {1 X' \& g& p7 [. i: H- recv_end_flag2=0;; R) ]9 N) Y. @ I K. t
- 7 J# n4 Y) V7 d( a5 F
- HAL_UART_Receive_DMA(&huart2,UART2_Rx_Buffer,DMA_RX_BUFFER_SIZE);) V% M- f! l, g9 T. ?
- } L! X2 S% y+ {. y% f" W% S
-
2 i$ \% b+ a& e - & U( S: N# |# n9 o: l8 O
- 6 t7 B+ m, r! g9 l
-
0 q5 \3 f" ]3 Z6 e7 W - + I( b6 X. `: D4 K: G0 c( b+ l; e
- //stm32f1xx_it.c 中断服务程序里面修改的代码:% [2 H0 T" {. Y
- 7 Q3 F3 x0 N* {: x
- extern uint8_t recv_end_flag1;6 f* n& z# U! x H/ F
- extern uint8_t rx_len1;
, o6 s. e m% c - extern uint8_t recv_end_flag2;' R S8 k+ c& ^) h( x
- extern uint8_t rx_len2;
$ t- _1 \( `' m& w5 a+ s" r - : c; R; n* L" U9 r) _3 B: {: W
- #define BUFFER_SIZE 128' P0 ]7 j6 [( J% V$ B
0 T t1 o6 f: Y% ?' U, X& x4 b- void USART1_IRQHandler(void)
! Z$ K) T0 f6 E& c) J6 m - {6 L: R6 Q% I, c; E% S! m3 @
- /* USER CODE BEGIN USART1_IRQn 0 */
- k: j# r! o, n9 @5 i- u0 @( E - uint32_t tmp_flag = 0;
- W6 _0 q/ u2 y7 C% D; w - uint32_t temp;6 S( U8 y9 G. `$ z2 m' l) `
8 q4 w# z" }# `5 e- /* USER CODE END USART1_IRQn 0 */ " e" d% j2 Q. o9 L3 m: G
- HAL_UART_IRQHandler(&huart1);
/ T. Z l9 ~2 J/ N4 b: }4 o - /* USER CODE BEGIN USART1_IRQn 1 */, m' N6 ]3 U4 b' u% p) h
- 0 ]1 N' m1 Q2 h( T* k; E
- tmp_flag = __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE);
4 g% U8 Y j$ {9 _) I - 4 j, j. }# U( \7 o' v
- if((tmp_flag != RESET)){
# b- T5 ~" _- F4 g" Z. Q/ N - : c0 p4 ?; s1 O1 ? j+ r: v- W
- __HAL_UART_CLEAR_IDLEFLAG(&huart1);
9 ~* c- {# A* j2 g6 P# w
4 f6 T% Q4 H6 S1 b& x; k1 V/ C+ V2 ~- HAL_UART_DMAStop(&huart1);7 M8 q. A& | t, f
5 Y& h% }6 K: |2 o% T0 b- temp = hdma_usart1_rx.Instance->CNDTR;; W$ g P8 B, L" k
- 6 g; a' c6 e( b1 n4 }9 e, F5 Y' u
- rx_len1 = BUFFER_SIZE - temp;$ p; }1 @4 n# h
, _/ ]6 G, Z' m w9 ?- recv_end_flag1 = 1;
3 G4 A6 j9 b, T - }
/ i4 e. u' S. T0 r+ z5 _, R - /* USER CODE END USART1_IRQn 1 */: E; E, S8 ^3 r$ I
- }
, |: W/ P( ?/ J - $ P0 y) J. f% g" I5 W' Q9 x) q% M8 Z
& l) \$ E9 k4 P7 k) e- /**0 ]: G$ I, N" q4 r4 q- L
- * @brief This function handles USART2 global interrupt.' n. T. _. H2 W% b5 w
- */
, E$ A4 E) k, v K7 W, t; T5 k - void USART2_IRQHandler(void)
1 R" [, r+ H. t2 `, C x) Q v5 R - {% w0 @3 V1 x& i2 M# z# D
- /* USER CODE BEGIN USART1_IRQn 0 */
2 [+ k/ q) Q" F8 B* ]0 R# V - uint32_t tmp_flag = 0;
; E9 X0 W7 H1 E! D( u* T9 C5 N9 y7 f - uint32_t temp;6 t) ^- z& l+ L
0 A) i; R+ \3 Y( c8 Y- /* USER CODE END USART1_IRQn 0 */ , {) V( ~9 ]/ Y
- HAL_UART_IRQHandler(&huart2);/ l& q( Z; G% g
- /* USER CODE BEGIN USART1_IRQn 1 */2 L: o8 ~ C& _
- R- R. o2 V9 \ B
- tmp_flag = __HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE);
/ g, O/ F* D/ L i& R
/ k; w2 J4 w Y- if((tmp_flag != RESET)){
- ^' _0 [, I/ E D! _. r - 9 J0 }. A/ ?1 I
- __HAL_UART_CLEAR_IDLEFLAG(&huart2);9 j6 N- U/ J+ p0 z# Q- i
- 9 b, [6 ]: V* @3 I# W
- HAL_UART_DMAStop(&huart2);" F7 | @& p! I* F! |6 B M
- 9 a6 s" k& F3 l. A
- temp = hdma_usart2_rx.Instance->CNDTR;
8 ~( z/ }' e, x3 l - ( O: }: ^& k8 H3 X+ ]2 n! C
- rx_len2 = BUFFER_SIZE - temp;
* I0 Y; ]2 F: y0 i6 ~9 z9 @
; v" e3 V' g0 j- recv_end_flag2 = 1;. W/ p# H5 t* i9 y
- }' H; A" @9 Y3 l0 s. s( e
- /* USER CODE END USART1_IRQn 1 */
8 A' G* c4 I: {9 J% U/ b$ S3 n - }
+ n$ d6 k- f; I
复制代码
. O' e |$ k3 J: k& o$ ~
) ]9 [* g0 h. o G# K9 a! \) c0 q/ Z! o" n+ |
上面的代码,也是参考了网上网友的帖子。 希望网友指出问题,和给出更好的代码方案。
" D; p8 ^8 T# K: N8 \2 U也还听说串口DMA有三种方法,我这里用的只是其中之一的“空闲中断”法。/ w( I" G& j- m6 W. E
5 M) L# O+ s8 S2 Y: F9 r4 Y
附完整代码:' ~! s$ v9 e1 R1 v Q- g
0 e( o7 N# |0 {! A. C: ~6 }9 S! A |
1. 数据包长度不超过DMA缓存的长度。2. 发送的间隔不少于200ms。
就可以115200双向同时收发无差错。
至于单方向收发,1Mbps,2Mbps,都没有问题的,放心使用。
所以个人认为LZ的测试方式其实问题在于,用中断CPU可以收一个马上发一个,虽然两边都CPU占满了但响应肯定快;但DMA是收完全部时再全部返回,那CPU虽然很闲,但响应就肯定慢。这样测试时DMA对CPU占用少的优势就测不出来。
同意,谢谢。
总之,串口通讯,本身是没有纠错重发功能,速度又很慢的外设。: d% D6 K! S2 E# w' \& l! K
影响丢数据的因素,也就是那么几种,照顾到了就没有问题。