本帖最后由 radio2radio 于 2019-3-11 14:32 编辑 3 R$ V( z% B" q5 y0 D
- G4 L; c- [1 }8 y8 Q: x
周末有时间,测试了一下STM32F103的DMA串口收发程序,基于CubeMX的,结果却是令人大失所望。2 H! d e, p, o) e2 I5 o' B$ [4 w
9 E3 d6 E$ v- {" f1 K u: g' k
我在去年,测试了一下【中断模式】的,结果是速度超快。
- @8 }( p) o* \速度115200bps和1Mbps,双向同时收发100万字符无差错。 2Mbps,单方向100万字符无差错。' V' x" Z, {9 B6 ^9 F" f0 Q
详情请见: STM32基于CubeMX的高速串口收发程序(中断模式)/ E( _ h2 Y( J
& R$ g: R; G+ |6 e" a
那时就有网友,问我为什么不用DMA模式,我也认为DMA的好处多多,只是没有时间验证一下。# @/ L1 n3 S0 P9 R. Z
现在,我得到的结果是,DMA模式用在UART这种低速外设上面,可能性能并不好,不如中断模式的。
3 ] R6 d& `+ {- }请网友们给看一看,希望我的代码有问题。4 ^/ W. m5 @4 p
先说我的测试结果吧:( n, e% s: T( g" e/ y
STM32F103C8T6 Bluepill板,MCU时钟72MHz,用CubeMX配置出DMA模式的两个串口收发。
8 g" D0 [% P; L$ ?7 m0 G+ N- J+ T添加少量代码,就做成了两个串口互相收发。 与上面说的中断模式的用法一样。
7 U6 J9 E' `9 @! z3 [8 k w结果是,115200波特率,以10ms间隔发送接收40个字符,单方向正常,双方向同时收发就丢失数据。3 l8 _- b3 }/ S, B
如果时间间隔放到200ms,双方向同时收发,也能正常了。
5 e! `) X& ?7 r6 [0 ?
: G& X" B+ I N$ I& T下面,看看我用的代码:: }) S' Y- f# y$ x& w/ X
CubeMX的配置过程,就不累叙了,附件里面有配置文件。
$ O. r4 S7 k7 H7 G2 y' n# x! }0 u! Y, l7 ]
- //main.c 添加的代码:
) C" X% J) b5 J& x3 e/ B& K4 c8 L - 1 m7 Q$ K# V6 `* \
- //变量:4 u0 I2 U( u7 E7 K3 v+ v
- #define DMA_RX_BUFFER_SIZE 128
! C8 J5 x) N+ H - uint8_t UART1_Rx_Buffer[DMA_RX_BUFFER_SIZE] = {0};
6 S" T ~/ \9 W - uint8_t UART2_Rx_Buffer[DMA_RX_BUFFER_SIZE] = {0};
9 \- Y8 I* A* T" `$ N - . K5 E& l. b5 q* w) \5 [9 _ {
- uint8_t recv_end_flag1 = 0;
2 X$ {! t _) t4 F2 Z5 i - uint8_t rx_len1 = 0;
; g) ? G$ B0 p6 E - uint8_t recv_end_flag2 = 0;* y; g, z. a$ C M/ L+ S& E! N
- uint8_t rx_len2 = 0;
5 s) R) n% B2 U" K
1 ^0 L1 h+ J( K. p- 。。。
' ]9 p+ e* P6 f
% b) x7 Q# y$ i7 A/ D% _! {( i. d- //初始化
2 o: X$ \; J2 C) W5 d2 H" b2 N - __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);* T; u; C8 n S
- __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);; A) _5 ^; l1 v" I
- , s; D- y# h% ^: j, C z
- HAL_UART_Receive_DMA(&huart1, UART1_Rx_Buffer, DMA_RX_BUFFER_SIZE);
7 y3 G( w% Y* \ - HAL_UART_Receive_DMA(&huart2, UART2_Rx_Buffer, DMA_RX_BUFFER_SIZE);7 _: e1 _- a4 k
, A9 q5 g! I0 V6 w& m. }- 。。。
" G9 }; z1 Q' l4 ]2 ~ -
5 L9 q% q" j8 U! [, @ - //main()" v" \4 w+ l0 Y% j3 ~- u2 a- B
- if(recv_end_flag1 ==1){ //UART1 Rx, UART2 Tx
: q% ^5 k" F# u" |. Y
. W+ Y- G f" J2 Y' y, S" j- HAL_UART_Transmit_DMA(&huart2,UART1_Rx_Buffer,rx_len1);
* ^) [5 t0 ]" q
& y3 S; h4 B" P1 K+ H1 [5 s- rx_len1=0;
+ }0 K3 O# Y8 x6 L
4 [) Y5 ~: u# d3 |% f) Q- recv_end_flag1=0;7 j7 P3 v* _% `& p0 s. _
9 N6 J6 X6 B- W; q/ Q, y- HAL_UART_Receive_DMA(&huart1,UART1_Rx_Buffer,DMA_RX_BUFFER_SIZE);# y2 p9 _, Z. m2 A4 s
- }
3 T' h/ p& r; {) h2 z. I - , t8 m( ?* \8 J/ f5 v
- if(recv_end_flag2 ==1){ //UART2 Rx, UART1 Tx# `; _: z% L* q2 Z' c9 r4 Y
. O* q: I( {/ f4 h* B8 g6 i8 i- HAL_UART_Transmit_DMA(&huart1,UART2_Rx_Buffer,rx_len2);
- E2 L7 r, V6 E- ]" z
u- e9 Z0 W* i2 b" D2 C- rx_len2=0;' ?0 U1 p* O! h; j( \" K5 g
" E' F1 q4 w2 \7 I# j: u- recv_end_flag2=0;5 A* B- |. r6 K; ~7 y
& V: T: C0 B. r3 t! e- `- HAL_UART_Receive_DMA(&huart2,UART2_Rx_Buffer,DMA_RX_BUFFER_SIZE);
& n' j: F3 p+ Z - }
( i% n% c: u: t9 Z - - j; ` \4 e) f [, ~) @
- & m+ h9 X1 J7 w# S& K1 f
-
1 o7 K- D; V8 u7 q2 m4 c, C/ _ - 4 H) F" |% X$ X
- . d, L8 |' J' k: Q: @, Z4 o
- //stm32f1xx_it.c 中断服务程序里面修改的代码:! t& ~& [$ K2 ~ j: a* R( u2 {3 M; X
- / z* K6 {7 d- f' r+ Q
- extern uint8_t recv_end_flag1;2 `3 C9 I1 K o( L" P: k& f8 Z
- extern uint8_t rx_len1;
" z4 }4 S% ^- i- C7 s3 E - extern uint8_t recv_end_flag2;
: U8 L+ O3 Y2 N# T! [ D - extern uint8_t rx_len2;" T' ]& v8 I0 ` E; w
- % u9 C& t7 H4 h
- #define BUFFER_SIZE 128
, e" D. k2 M5 s* f0 a. M0 e. j8 n# ? - + [: G7 f) ^0 Y* f
- void USART1_IRQHandler(void)$ d2 Y5 h( m' ~$ v) Y6 H5 e3 x
- {
# i+ `( ^% Q% o" e# D" N3 {% Y - /* USER CODE BEGIN USART1_IRQn 0 */
8 e; n- Q7 h) _/ T* I" G7 O - uint32_t tmp_flag = 0;, A7 g# z& W6 f& [
- uint32_t temp;" n5 w, u& t- I8 m# C1 [
3 o, X/ E, L6 ]7 E" N( H- /* USER CODE END USART1_IRQn 0 */ 2 _+ p' b9 V8 Z" y/ g# V& L1 p% T
- HAL_UART_IRQHandler(&huart1);
$ G7 ^2 k* \ c; k- ~ - /* USER CODE BEGIN USART1_IRQn 1 */
/ P9 K! G: ~% X. A! B - 1 Y; P4 U9 B( u1 L
- tmp_flag = __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE);
\7 w! g# m( v
( j# q5 h7 z0 r" e' `6 Y- if((tmp_flag != RESET)){9 \; n- @5 d+ L, N9 O
4 v6 n2 U' h9 U$ C9 u: Y- __HAL_UART_CLEAR_IDLEFLAG(&huart1);. p6 y: B6 v5 h5 \
- 6 J) W( H8 E8 S6 h" K$ ?6 R$ m
- HAL_UART_DMAStop(&huart1);$ ]3 W( R% x# H+ P
" O$ U4 t% O4 r& P- temp = hdma_usart1_rx.Instance->CNDTR;" M6 A, w$ r! \, Q
2 c: G' g7 A% K$ e/ i! G- rx_len1 = BUFFER_SIZE - temp;0 w0 n/ y$ m2 u- z( U4 u7 M2 e
/ \ F% w/ N1 D" u/ v- recv_end_flag1 = 1;
" v4 |* F' A% x* M5 B) s - }5 b% B ?! y. C& g( t* b
- /* USER CODE END USART1_IRQn 1 */9 T" j5 `- H1 f. K8 ^' P% Q
- }3 I' f4 g: t/ X: y( r
- : w" B* q- P& q: c8 B; L* T" }( c
/ q8 z: i* k8 u) p& d% q" V- /**
O8 T3 ]& f. M2 F! N( k5 n' G - * @brief This function handles USART2 global interrupt.2 k$ K7 b% Q5 W t2 j
- */& @* n2 _' Z3 N3 w
- void USART2_IRQHandler(void)
% {9 I! E# ?8 |' E% p( B - {
) ?6 i# h# l! F3 L$ [ - /* USER CODE BEGIN USART1_IRQn 0 */
7 x4 e5 O! o2 U6 ~ - uint32_t tmp_flag = 0;
; l% ?: |. ^) [6 ~7 p' L j7 Q - uint32_t temp;
0 Y: x- @! w9 C# V - + |, y5 `/ |5 _4 ]) ]# A
- /* USER CODE END USART1_IRQn 0 */ , h2 J, |2 s8 a5 W, d
- HAL_UART_IRQHandler(&huart2);" O* P! \6 V5 Q) C# J6 Q$ g5 d
- /* USER CODE BEGIN USART1_IRQn 1 */
]8 k8 K/ b' Q* E I, F - X% Z2 x1 A) D( H4 a2 c& ?$ ]. O
- tmp_flag = __HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE);2 }6 ?) Q1 ~' I- X- x+ b. H
' R) d7 U# v8 @" y. C$ c' {: O- if((tmp_flag != RESET)){2 u4 V) p; g. F+ d
* @& ^' X2 B. S- __HAL_UART_CLEAR_IDLEFLAG(&huart2);
" Z- J3 n0 Q7 c
& v; M6 ~$ |7 P7 Z- HAL_UART_DMAStop(&huart2);3 I+ {% H3 b$ h4 x" P8 [ A
# T1 ?! \4 ^) Q1 a- temp = hdma_usart2_rx.Instance->CNDTR;
! y0 h3 J2 Q( s8 L - 2 s4 L: c7 e: Z v% _8 F! D4 F" w/ N
- rx_len2 = BUFFER_SIZE - temp;
; G* R9 E/ j. w% l: ]# Z! Z
; Y1 O' D7 s/ a- recv_end_flag2 = 1;+ N- a+ P9 }' j& f& ~- [- r0 ]
- }
; [* M3 K9 ~) O: {6 s$ H - /* USER CODE END USART1_IRQn 1 */
E. L7 O" t) p% w; e5 ] - }- n! b- o5 K; ~% K, L
复制代码 " t0 t& G3 V4 \5 I0 O
& a; ]9 d/ f1 J7 |2 C; y9 C/ d) ^: ~
上面的代码,也是参考了网上网友的帖子。 希望网友指出问题,和给出更好的代码方案。1 W- r, _8 _8 D1 @4 i' N
也还听说串口DMA有三种方法,我这里用的只是其中之一的“空闲中断”法。: g" ?# P# H% x) b: |0 u
7 F' P7 v+ v6 k7 H附完整代码:
) r7 H9 f1 `' t8 }" I2 h) N+ C
6 j2 @. \3 v. i/ t, w |
1. 数据包长度不超过DMA缓存的长度。2. 发送的间隔不少于200ms。$ J1 t2 I* H" A- Q6 o
就可以115200双向同时收发无差错。
) S: F0 m% Y; v$ {$ H' p
至于单方向收发,1Mbps,2Mbps,都没有问题的,放心使用。
所以个人认为LZ的测试方式其实问题在于,用中断CPU可以收一个马上发一个,虽然两边都CPU占满了但响应肯定快;但DMA是收完全部时再全部返回,那CPU虽然很闲,但响应就肯定慢。这样测试时DMA对CPU占用少的优势就测不出来。
同意,谢谢。! C0 J! H" e" K. U( s* V
总之,串口通讯,本身是没有纠错重发功能,速度又很慢的外设。
影响丢数据的因素,也就是那么几种,照顾到了就没有问题。