本帖最后由 radio2radio 于 2019-3-11 14:32 编辑 + Q$ S) R; [8 w, J
. X( J1 u3 [5 `, [! U2 n
周末有时间,测试了一下STM32F103的DMA串口收发程序,基于CubeMX的,结果却是令人大失所望。7 i' F# ~3 T0 Z7 d1 P4 _
7 q/ q' l/ u( {9 O我在去年,测试了一下【中断模式】的,结果是速度超快。1 \/ B3 f! R1 t+ [
速度115200bps和1Mbps,双向同时收发100万字符无差错。 2Mbps,单方向100万字符无差错。
5 M! x3 g: U- H' w0 v# b. y详情请见: STM32基于CubeMX的高速串口收发程序(中断模式)
! P7 `6 E" A) C8 P- |4 |' q# z% o
; \2 s& |' d q ^& l那时就有网友,问我为什么不用DMA模式,我也认为DMA的好处多多,只是没有时间验证一下。- i' G v* Z1 B3 s# t- Q
现在,我得到的结果是,DMA模式用在UART这种低速外设上面,可能性能并不好,不如中断模式的。$ m+ R6 G9 w3 h9 A. [9 O
请网友们给看一看,希望我的代码有问题。5 n5 o# t. j" Q- T6 {- N* U' q
先说我的测试结果吧:. a4 H; B$ V! {6 u6 E" ~4 f# F
STM32F103C8T6 Bluepill板,MCU时钟72MHz,用CubeMX配置出DMA模式的两个串口收发。
4 O C* B# `3 D) }, c, g添加少量代码,就做成了两个串口互相收发。 与上面说的中断模式的用法一样。4 Y; |' l: A2 n9 G# o+ L: Y+ B( }+ m
结果是,115200波特率,以10ms间隔发送接收40个字符,单方向正常,双方向同时收发就丢失数据。
' q3 x; u! Q/ Y( u5 f, f6 \如果时间间隔放到200ms,双方向同时收发,也能正常了。/ w" o; r4 n. g1 x9 ~+ g
: |3 R# h1 i( M: s& }, _) @2 V8 F+ S下面,看看我用的代码:: D: u/ U( p; \) g P3 O8 T2 g+ w3 Q7 P
CubeMX的配置过程,就不累叙了,附件里面有配置文件。, }! ?& o3 m3 g$ [' T
& t( X. N5 }; _. r* \: d
- //main.c 添加的代码:4 k2 A# h- f( _) }
- N8 K2 [# p; V- //变量:" D$ ~2 ?9 ]+ R
- #define DMA_RX_BUFFER_SIZE 128
3 i' F) E4 `4 w' j3 }- H$ K - uint8_t UART1_Rx_Buffer[DMA_RX_BUFFER_SIZE] = {0};& ]/ _8 _8 H6 G. r- {8 g! H, Y
- uint8_t UART2_Rx_Buffer[DMA_RX_BUFFER_SIZE] = {0};$ `/ m6 F# c6 T7 u) S% k
- * T- H/ C3 P9 c; p+ k. G4 S' o+ \
- uint8_t recv_end_flag1 = 0;) F8 B& g1 e6 K
- uint8_t rx_len1 = 0;
$ z$ O5 I4 r, q" g& x - uint8_t recv_end_flag2 = 0;' C( X6 d6 r$ \/ n
- uint8_t rx_len2 = 0;1 g. {: ^: S ~8 O: A8 f6 y; M8 A; \. Q
- / m4 g5 X4 k b$ b& k
- 。。。9 S& ]: p9 i' ?. ?0 B8 U$ O
- 9 G( T4 O$ s- V5 j( V
- //初始化* _/ q: [7 h, v& [* N! u
- __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
8 D, m: R& I2 \, I0 { - __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);/ E7 z$ f1 w$ y& d
-
+ L; h8 k& @5 Z7 ^1 ` - HAL_UART_Receive_DMA(&huart1, UART1_Rx_Buffer, DMA_RX_BUFFER_SIZE);
" ?$ H( J9 b$ ^" V0 i3 E0 I - HAL_UART_Receive_DMA(&huart2, UART2_Rx_Buffer, DMA_RX_BUFFER_SIZE);: j* a9 p) A0 y" n% `7 s4 P
- + _) }! ~7 E# X" T
- 。。。- E$ r" V9 J, Z0 ]4 u9 T1 f
- : R+ j! [7 r& v& I
- //main()
) q' T) b# c4 C1 ~% Y - if(recv_end_flag1 ==1){ //UART1 Rx, UART2 Tx6 p- ?* o' s. W" }! v
- . W. T; h& P, z% ^6 b, s4 o; v
- HAL_UART_Transmit_DMA(&huart2,UART1_Rx_Buffer,rx_len1);
! H) `# z5 C' H/ Z' L9 L" J \: v. X
6 |9 L% u$ `8 {- rx_len1=0;
3 v, C/ g) ?1 ~" V; Y
. K4 t& i* C* p; n5 m- `- recv_end_flag1=0;5 G* N4 n; g3 m2 ~" x+ ^3 u' a. k4 l4 O
J) C1 q4 L: l- HAL_UART_Receive_DMA(&huart1,UART1_Rx_Buffer,DMA_RX_BUFFER_SIZE);+ |" A0 o* F/ A: F% r% _5 Q8 Q- G: [
- }( y' Y5 o) q( K: {
- $ d3 y7 k }+ p
- if(recv_end_flag2 ==1){ //UART2 Rx, UART1 Tx
0 }# ~* W' p7 f7 d. S" O1 V' P1 I
0 U% A% ]1 X! `8 x9 l- HAL_UART_Transmit_DMA(&huart1,UART2_Rx_Buffer,rx_len2);0 D% E4 Y6 n* L: B2 R: c, N' Y
# W3 a7 H' V5 a. B5 m1 j# D- rx_len2=0;
: b9 Z' w* r( Q0 y7 N$ n
7 t, J" D3 A4 D" J& E0 v- }' l2 H- recv_end_flag2=0;
! i' r5 l+ n: Y9 a8 ^5 _
* X5 Q7 P$ x8 ^+ m, Q9 B' x6 v( l- HAL_UART_Receive_DMA(&huart2,UART2_Rx_Buffer,DMA_RX_BUFFER_SIZE);
& v" N6 @3 f- E1 B/ N - }
" b# }0 Y! L: J! Q, m/ F -
5 x) i( N0 ~% ]1 f% G* G1 F -
) [( N0 o& Z0 C8 S -
" g( d5 [) k/ B) Y+ i# J - 4 f8 h: j5 T; `" ~
-
+ k" f1 O8 I( q$ w0 V* {$ C - //stm32f1xx_it.c 中断服务程序里面修改的代码:' n- r& s. k# S0 p
- 7 Z: k$ d4 C8 C4 ]: o
- extern uint8_t recv_end_flag1;4 s* j/ ?8 t! ]# I* ~2 y* {: d
- extern uint8_t rx_len1; k1 K) G) ~; b$ M5 j
- extern uint8_t recv_end_flag2;
: ^3 e9 h' o2 D& C - extern uint8_t rx_len2;, S! Q+ a6 _& v. W/ l" M) F" D* m. d& d
9 U( I& Z) q% M; v" c2 D' n- #define BUFFER_SIZE 1281 ~7 b+ N9 O# t) [* N. W# c
- 5 k5 `! Y+ H2 \8 a
- void USART1_IRQHandler(void)
' U% u! D5 O( D0 l: n! w) i - {
- d, S( b& w/ }% N+ u+ h - /* USER CODE BEGIN USART1_IRQn 0 *// f9 T+ V3 M; _0 O+ _1 G* B
- uint32_t tmp_flag = 0;
1 ^7 d5 Q8 o' M* [7 i% { - uint32_t temp;
- i- ^/ T- U2 J1 [, D - ) Y. u! H2 f# B0 K' K b
- /* USER CODE END USART1_IRQn 0 */ ' d/ `/ I x7 w8 z7 @; i/ l' F
- HAL_UART_IRQHandler(&huart1);
3 _( |& W2 Z5 D) {6 x- ]6 w - /* USER CODE BEGIN USART1_IRQn 1 */
- i2 m" {# K" n; z9 c1 H( |& n9 K2 T8 q - , ]. E; r1 l& ]) N3 H
- tmp_flag = __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE);
$ W0 s0 A4 J: U9 x1 }, n - m+ e: x6 B, X) T
- if((tmp_flag != RESET)){7 [# w6 S! t4 k/ Q8 }
- 0 ~) P0 i2 I2 G. W
- __HAL_UART_CLEAR_IDLEFLAG(&huart1);
9 I( M5 ]' H2 p" T: N! Y6 N - ( k% D N0 I, I% u$ G
- HAL_UART_DMAStop(&huart1);2 c9 K/ F! o0 {$ w$ [
! o2 T; C- r1 B! b- temp = hdma_usart1_rx.Instance->CNDTR;- S ^) T, q, ?, L D4 P0 l) q
% x) e5 {+ J' R- rx_len1 = BUFFER_SIZE - temp;
9 M: r0 g9 G f( O; T - . @# h x1 r, w. |- ^; ^
- recv_end_flag1 = 1;
: X Y) }+ q9 k2 k - }' J$ M! o$ e5 S( g+ k+ Z, m8 K5 @+ O
- /* USER CODE END USART1_IRQn 1 */
) P( b% P- D: A8 B - }
9 B1 B, k+ Q; }4 | - ' A; ?+ n' e, S/ Q0 \
* `1 S5 d' C' Z/ X- /**& I- A2 r" A6 K4 i
- * @brief This function handles USART2 global interrupt.! S% {( {3 j0 B! _, f' o
- */) f& Y, G( b- z' n6 P2 W
- void USART2_IRQHandler(void)
$ z3 r" J& H4 ^" F0 Z2 h& R1 e - {9 {1 Q8 O5 ^9 B0 n$ z
- /* USER CODE BEGIN USART1_IRQn 0 */. N( E5 ~8 R. A4 l3 { i! \
- uint32_t tmp_flag = 0;
% W! S4 r" n" U: r+ p - uint32_t temp;- G: \3 j2 m) K; n# ]4 c
- ) ]5 v8 r) }7 w C- h
- /* USER CODE END USART1_IRQn 0 */
" r! j, S$ f$ V6 o5 t - HAL_UART_IRQHandler(&huart2);4 f* D# P1 v2 y7 _" `
- /* USER CODE BEGIN USART1_IRQn 1 */: o7 g- L- _' i _4 g: n9 Y q2 j/ W' F
- j* Z( m% e5 Y+ \2 p n0 k- tmp_flag = __HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE);
) k9 I$ ^/ t5 ~. o* I
( j# W5 `8 w; W, g- if((tmp_flag != RESET)){4 O) h$ Z8 X* P+ p! K& z+ w
+ G2 p4 a: p0 d; U; ^: }* q0 F- __HAL_UART_CLEAR_IDLEFLAG(&huart2);, L6 E2 t5 D' n, O5 I
- 5 u M: ?+ m% h
- HAL_UART_DMAStop(&huart2);
+ Q- v! a9 t; z% C4 f, N$ M, Z - 9 G& Q+ s9 W1 L+ o, U; Y% y7 K- S
- temp = hdma_usart2_rx.Instance->CNDTR; b7 {1 o8 Q( y1 e( F
* }2 }: @* G! o- L& c- rx_len2 = BUFFER_SIZE - temp;2 Q6 j0 ~0 h" \0 [, p- d" W
; L4 `, R3 p4 j4 p- h- recv_end_flag2 = 1;
4 l, t7 C" Z" v8 F ? - }
6 E% b% O/ |! N; ?+ s* f, I: o - /* USER CODE END USART1_IRQn 1 */
1 `6 c6 e! i$ _% H. N% g1 Q7 T - }
3 V7 ~+ X( ~2 a! H9 c1 v
复制代码
1 c0 A; g+ S9 {' H& [; P2 n; h- u" Z4 d8 s; R! Q; L# R* e6 i' s
2 R7 D* R1 d, P- L) A( A& ^8 {上面的代码,也是参考了网上网友的帖子。 希望网友指出问题,和给出更好的代码方案。- |& u% p) }5 x- Y. A; \6 K
也还听说串口DMA有三种方法,我这里用的只是其中之一的“空闲中断”法。! b+ M$ H8 c- a% E) s
! r8 T9 E# I+ [) C; y
附完整代码:0 ~6 Z$ V# p% G! b6 Z! g
8 q* o3 N, |( p( P
|
1. 数据包长度不超过DMA缓存的长度。2. 发送的间隔不少于200ms。" J" N! F" p5 H2 ?
就可以115200双向同时收发无差错。
至于单方向收发,1Mbps,2Mbps,都没有问题的,放心使用。
所以个人认为LZ的测试方式其实问题在于,用中断CPU可以收一个马上发一个,虽然两边都CPU占满了但响应肯定快;但DMA是收完全部时再全部返回,那CPU虽然很闲,但响应就肯定慢。这样测试时DMA对CPU占用少的优势就测不出来。
同意,谢谢。
E) S* D( B/ b+ E$ N5 X! B
总之,串口通讯,本身是没有纠错重发功能,速度又很慢的外设。
影响丢数据的因素,也就是那么几种,照顾到了就没有问题。