你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32基于CubeMX的高速串口收发程序(DMA模式)  

[复制链接]
radio2radio 发布时间:2019-3-8 22:08
本帖最后由 radio2radio 于 2019-3-11 14:32 编辑 ; k$ A9 W, S5 F- i

# B- v3 M1 ?" L周末有时间,测试了一下STM32F103的DMA串口收发程序,基于CubeMX的,结果却是令人大失所望。: `, c: y2 Y" L+ k
, d/ C! J1 j. N6 H
我在去年,测试了一下【中断模式】的,结果是速度超快。
' z5 [  i& d4 w1 [3 F, \速度115200bps和1Mbps,双向同时收发100万字符无差错。 2Mbps,单方向100万字符无差错。
- J% T' M+ p6 j1 E详情请见:  STM32基于CubeMX的高速串口收发程序(中断模式)+ \8 l! J9 \# v& b! C. m! N

7 p! S) @% n6 i6 B那时就有网友,问我为什么不用DMA模式,我也认为DMA的好处多多,只是没有时间验证一下。
7 {' {( A- \; T+ L4 W现在,我得到的结果是,DMA模式用在UART这种低速外设上面,可能性能并不好,不如中断模式的。
& G+ w& f9 m9 x, ?, |6 i% f请网友们给看一看,希望我的代码有问题。% Z1 S9 U8 U& h9 x) a6 T0 \
先说我的测试结果吧:7 e& I1 e( _( {+ _. c8 M2 W
STM32F103C8T6 Bluepill板,MCU时钟72MHz,用CubeMX配置出DMA模式的两个串口收发。( W# f) z0 H1 N$ ]  b$ p6 c" S
添加少量代码,就做成了两个串口互相收发。 与上面说的中断模式的用法一样。( q8 i- Y  V2 R0 l. n
结果是,115200波特率,以10ms间隔发送接收40个字符,单方向正常,双方向同时收发就丢失数据。
0 o- Q9 \3 m) h& R* e! J9 z如果时间间隔放到200ms,双方向同时收发,也能正常了。1 f5 N6 W  J( M8 N- C

1 u1 E" \! V7 {! m4 B下面,看看我用的代码:
$ m! i' G% K* h* V% I. h/ u2 {CubeMX的配置过程,就不累叙了,附件里面有配置文件。1 q4 f% L& x% L0 D( W2 E0 s- C6 d
' i0 W: z- H$ V1 w0 S
  1. //main.c 添加的代码:- P5 S7 |9 S2 b) a; v# l  ~
  2. . b) ~( u; F, {: m
  3. //变量:
    8 h& U1 b# x; g4 a
  4. #define DMA_RX_BUFFER_SIZE            128
    ! h2 R; K4 d7 r# C+ w6 y6 h$ y
  5. uint8_t UART1_Rx_Buffer[DMA_RX_BUFFER_SIZE] = {0};0 }  @2 W- a& T
  6. uint8_t UART2_Rx_Buffer[DMA_RX_BUFFER_SIZE] = {0};
    - m: R, B5 g) W0 \8 F3 L
  7. ' n  r, ?+ B1 n# B! M7 d  w
  8. uint8_t recv_end_flag1 = 0;
    & \$ I. v" c. K, X" H  i& ^' v
  9. uint8_t rx_len1 = 0;
    . A( J. Q, A8 J
  10. uint8_t recv_end_flag2 = 0;. z$ K% o3 i. W/ R
  11. uint8_t rx_len2 = 0;. Z9 h. e% }9 d$ }" z

  12. " z& C4 j# ?6 Z3 D8 D: a  p; \6 t/ _
  13. 。。。
    4 H. r; X8 O2 G" A5 w
  14. 5 a/ z( n- e' V9 B
  15. //初始化% |- X% b8 U) s1 S; R* b
  16.   __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);  v1 @% c; v8 i# c$ m
  17.   __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
    ; ~/ z* I! b  V- _# i- ~/ \/ T
  18.   
    ' r8 t) d: B6 _3 h' H* _
  19.   HAL_UART_Receive_DMA(&huart1, UART1_Rx_Buffer, DMA_RX_BUFFER_SIZE);
    7 O4 D# d) T% k; T, u- N: m
  20.   HAL_UART_Receive_DMA(&huart2, UART2_Rx_Buffer, DMA_RX_BUFFER_SIZE);
    / a6 ]& b+ i1 h- J4 u# x! B, P1 |1 |
  21. 6 R9 y( n% X- k. H6 i
  22.   。。。( t! [; J0 K2 N* o
  23.   
    8 |4 U' h2 G- _9 N( p! D
  24. //main()
    : z7 ]7 J2 H$ j9 V- z. n: h
  25.     if(recv_end_flag1 ==1){     //UART1 Rx, UART2 Tx
    ' V% V9 S$ F! O' o9 S; n4 J2 w) N

  26. , A6 i" ^3 U5 D
  27.       HAL_UART_Transmit_DMA(&huart2,UART1_Rx_Buffer,rx_len1);3 l) c' Q, O  j( F) f+ x

  28. : B* O% z8 u) N8 p8 Z9 L; y
  29.       rx_len1=0;: `5 e7 @% }1 N1 u
  30. 2 u6 L1 M2 j2 D4 Q- N: Q( v; [# Y
  31.       recv_end_flag1=0;5 |: G8 R5 ~/ m5 C
  32. ! w: O# S! _1 R- K
  33.       HAL_UART_Receive_DMA(&huart1,UART1_Rx_Buffer,DMA_RX_BUFFER_SIZE);9 V# q9 ~; t* z; H
  34.     }
    * {6 d$ t- l: U' p
  35.    
    1 T$ g6 r9 v: x6 I, X7 Z
  36.     if(recv_end_flag2 ==1){    //UART2 Rx, UART1 Tx; n! {8 |& R" _3 a; y3 d$ P. r9 J

  37. 4 f* ~1 I8 H# H4 \; [/ [! d7 e
  38.       HAL_UART_Transmit_DMA(&huart1,UART2_Rx_Buffer,rx_len2);
    4 A4 n( r# y# G- {
  39. ) T: G/ G& R0 l  ?5 \, z; q
  40.       rx_len2=0;
    6 P$ q( C0 T3 ^4 U# Z% N
  41. & L4 z5 }0 T- d7 h
  42.       recv_end_flag2=0;5 H7 ~- o( m4 N8 Y% d+ B
  43. # a" ?: w  f5 i
  44.       HAL_UART_Receive_DMA(&huart2,UART2_Rx_Buffer,DMA_RX_BUFFER_SIZE);+ N0 ], h# ~/ w8 i/ V. f- \
  45.     }
    " j0 E' v1 B# J
  46.     + J. r% F. \# ^6 J+ I0 b
  47.     ( z& q( z$ P0 J
  48.    
    5 L# ~, L( m0 ]% H+ G
  49.    
    , U9 p8 ~0 q4 J6 K! S5 n' |' y* P) M
  50.    
    " c! Z! u, P! d  V5 ~( M; K" S
  51. //stm32f1xx_it.c 中断服务程序里面修改的代码:
    2 g9 F1 [7 l0 ^, M
  52. + c, ~5 g4 F; }: o" \& U  w
  53. extern uint8_t recv_end_flag1;+ i3 J, p' D8 b0 B$ b
  54. extern uint8_t rx_len1;
    . @9 N/ c% p; i, x0 q
  55. extern uint8_t recv_end_flag2;
    , A3 V1 j. w! c
  56. extern uint8_t rx_len2;, g, T7 I& x" p; S" W

  57. " v1 Z7 b: c/ B
  58. #define BUFFER_SIZE 128
    ) ]9 E& Y& q+ r3 S( U! b7 C

  59. 5 {2 ]  h6 l+ i1 N8 p! W7 ?
  60. void USART1_IRQHandler(void)& J; a; b3 W& \7 J. W- U
  61. {+ Z8 r5 b; r, A6 h
  62.   /* USER CODE BEGIN USART1_IRQn 0 */
    + `) M4 X  Y+ p2 M
  63.     uint32_t tmp_flag = 0;) |) G; J. E" Y( a' L" v
  64.     uint32_t temp;+ a+ R# x# }2 q  z+ d& W9 |2 t
  65. 1 k( ^( v% \. v- Q
  66.   /* USER CODE END USART1_IRQn 0 */    8 I7 J  U  C9 x9 F$ M! c/ ~
  67.   HAL_UART_IRQHandler(&huart1);
    9 i5 X2 o, P) F: a* C. W1 G7 r! ?
  68.   /* USER CODE BEGIN USART1_IRQn 1 */
    7 }! V& f4 s& f
  69. $ j3 O7 _* M) m& \" I* I, c
  70.   tmp_flag =  __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE);
    , C0 l6 S5 j) B( @4 K8 r6 A0 s

  71. * A* n) w  K- f
  72.   if((tmp_flag != RESET)){
    $ M$ E/ ?! a- {8 L( M) |& M! U! A; S
  73. 6 c+ w2 [5 {# k) j3 b6 U
  74.     __HAL_UART_CLEAR_IDLEFLAG(&huart1);& M( I4 C2 ]$ J" e( r; ~* U$ B
  75. % J7 _& M8 r, [2 T9 U. M
  76.     HAL_UART_DMAStop(&huart1);
    2 B, n# P, d3 N# ~+ Q" {/ }

  77. 0 S- ~; i6 h$ ?8 }/ r* N6 T
  78.     temp  = hdma_usart1_rx.Instance->CNDTR;: B: X$ j# _6 [1 O$ C& F
  79. 3 x5 d9 l8 }5 m
  80.     rx_len1 =  BUFFER_SIZE - temp;
    ! \4 k- K, X2 F5 T0 u" Y2 _5 q
  81. 1 @1 g/ f$ J8 P$ y6 i5 z
  82.     recv_end_flag1 = 1;
    5 V- t1 s  y$ _, c
  83.   }
    # I2 [3 A4 x3 \" B/ q
  84.   /* USER CODE END USART1_IRQn 1 */: e9 }9 {9 Z9 i0 l5 C- z3 q- T
  85. }6 _' W. w" u* s, A! Q* r, h

  86. / z/ S- C: a# v: E2 M! ^

  87. " I$ A7 t0 w- O  n3 e! N( p. {  ~7 V
  88. /**" H+ ^0 J% ?3 x- `
  89. * @brief This function handles USART2 global interrupt.# L8 H; P* e% s1 ~  C; n
  90. */1 q) |1 C' b% z) i) g# y
  91. void USART2_IRQHandler(void)
    ( t. x9 X# [$ N* Q9 g, M
  92. {/ S4 L9 Z# W$ I  T8 T$ x7 Y0 f/ @
  93.   /* USER CODE BEGIN USART1_IRQn 0 */, X" a3 \$ C8 I
  94.     uint32_t tmp_flag = 0;
    & \% {) v2 K5 H' |/ K
  95.     uint32_t temp;
    5 k) S7 f) ]! Q" B# c& @+ N+ p, e' B

  96. 7 h& v  C  M+ [4 W! [( H  c; f6 L
  97.   /* USER CODE END USART1_IRQn 0 */   
    3 g0 }; R. S/ r6 b2 n
  98.   HAL_UART_IRQHandler(&huart2);
    5 U. a* ^1 C3 m4 o- d/ h7 |# n: e
  99.   /* USER CODE BEGIN USART1_IRQn 1 */- j) X/ v0 N+ c! W
  100. ( n9 K  Q1 d/ i) e$ e
  101.   tmp_flag =  __HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE);. k% |# J+ Z8 i* Z: N) {
  102. * f. z; n2 g* e0 V
  103.   if((tmp_flag != RESET)){5 m  E& c3 h$ _7 e* {
  104. ) A5 E# P- I9 ~9 W
  105.     __HAL_UART_CLEAR_IDLEFLAG(&huart2);
    ( N2 G, L+ O( f, H

  106. % p: j& b* _( w  X/ ?% L% g' j& _
  107.     HAL_UART_DMAStop(&huart2);
    : j) v* s: V( e" U
  108. ( u/ A; A/ p/ S
  109.     temp  = hdma_usart2_rx.Instance->CNDTR;
    4 @" J7 n0 X5 f
  110. $ }8 T  `* R+ O& l  m" D4 A
  111.     rx_len2 =  BUFFER_SIZE - temp;
    ; D1 a; d. f8 g. M

  112. 4 s1 h5 A; R% G" g! I
  113.     recv_end_flag2 = 1;+ V/ y6 _- k4 H: M9 p- R% U
  114.   }# _" N  O3 f* a( a' Y
  115.   /* USER CODE END USART1_IRQn 1 */5 _$ F5 P5 _( K
  116. }& j' [' U5 Z( u: t, d9 [3 M
复制代码
) K; f: e0 J  M5 H9 L. i
) m9 ?' Z3 @; q2 I
' H& d& U6 x7 Z* Y: s
上面的代码,也是参考了网上网友的帖子。 希望网友指出问题,和给出更好的代码方案。
3 \4 U% j8 R9 a也还听说串口DMA有三种方法,我这里用的只是其中之一的“空闲中断”法。
1 ]4 i% y4 B7 U; j9 _1 e5 t+ E2 h8 a; g/ [! ]$ H
附完整代码:
4 O5 p7 B- \+ l/ E: N& U4 v& K. q% L0 J

STM32F103-USART-DMA.rar

下载

1.66 MB, 下载次数: 364

评分

参与人数 1 ST金币 +1 收起 理由
生命在于折腾! + 1

查看全部评分

赞 1 收藏 8 评论14 发布时间:2019-3-8 22:08

举报

14个回答
radio2radio 回答时间:2019-3-11 14:26:44
今天,有做了一些测试。从使用的角度来看,一楼附件的程序是可以使用的,只要,
# x) W( Q% p1 v1 `" v6 q: M8 W1. 数据包长度不超过DMA缓存的长度。2. 发送的间隔不少于200ms。) s& y: ^/ s9 _; k
就可以115200双向同时收发无差错。
5 v5 I% ]9 C; R) z) h1 `( t5 U  b$ I% X) u/ \- E
至于单方向收发,1Mbps,2Mbps,都没有问题的,放心使用。
riceglueball 回答时间:2019-3-21 09:50:33
学习了。
wh8 回答时间:2019-3-21 10:47:35
感谢LZ的分享。个人认为DMA的好处还是能释放CPU。一组大量的数据DMA传输,启动后CPU就不用怎么管了,DMA中断也只在一半完成或全部完成时触发;但无DMA时,每个字节收发时其实CPU都需要入中断操作一次,只不过库函数帮你做了而已。
1 B8 `' ~( Q3 c9 t# g所以个人认为LZ的测试方式其实问题在于,用中断CPU可以收一个马上发一个,虽然两边都CPU占满了但响应肯定快;但DMA是收完全部时再全部返回,那CPU虽然很闲,但响应就肯定慢。这样测试时DMA对CPU占用少的优势就测不出来。
适苦欲死 回答时间:2019-3-21 11:50:19
学习中,之前没有用到DMA,用的好像就是空闲中断
radio2radio 回答时间:2019-3-21 16:36:32
wh8 发表于 2019-3-21 10:47
% x5 s3 l6 T, K- l感谢LZ的分享。个人认为DMA的好处还是能释放CPU。一组大量的数据DMA传输,启动后CPU就不用怎么管了,DMA中 ...
7 b: e2 k( ]9 ~
同意,谢谢。6 v& l. {" |2 z4 ]' r" f- Q4 W

% k4 J7 n: h: Y5 k总之,串口通讯,本身是没有纠错重发功能,速度又很慢的外设。
& o# Q4 f6 Z( y3 z- F0 Q影响丢数据的因素,也就是那么几种,照顾到了就没有问题。
Kevin_G 回答时间:2019-3-25 12:52:36
收藏
eugenia2019 回答时间:2019-5-7 19:06:08
学习了
polarbear7 回答时间:2019-11-7 17:18:25
你这个怎么测试的啊,一点串口一点反应都没有的?
适苦欲死 回答时间:2019-12-23 17:19:02
学习了                                   
happyavr128 回答时间:2020-2-6 15:10:14
学习了
owenouyihui 回答时间:2020-5-9 12:32:41
有测试作为数据支持,不错
生命在于折腾! 回答时间:2020-7-21 18:04:16
谢谢 分享  c% z8 @+ Q4 R" d
daxinxin12 回答时间:2020-7-23 13:59:38
学习一下DMA的操作
jesseqiao 回答时间:2021-1-19 17:11:51
谢谢了,好用

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版