请选择 进入手机版 | 继续访问电脑版

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

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

[复制链接]
radio2radio 发布时间:2019-3-8 22:08
本帖最后由 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
  1. //main.c 添加的代码:- w+ o# ?& h4 m. Q( v

  2. 1 `- i* e# z& t6 p( o! s; M
  3. //变量:
    2 X1 A! b3 w3 P; W
  4. #define DMA_RX_BUFFER_SIZE            1288 J. B5 U9 Y8 K8 J% M3 Q- F+ O3 j
  5. uint8_t UART1_Rx_Buffer[DMA_RX_BUFFER_SIZE] = {0};2 A; |5 L0 Z8 u
  6. uint8_t UART2_Rx_Buffer[DMA_RX_BUFFER_SIZE] = {0};/ H  q* D7 |& D3 Q" h

  7. $ o( \4 t+ ~4 M9 F
  8. uint8_t recv_end_flag1 = 0;
    " L, O/ H- G9 K
  9. uint8_t rx_len1 = 0;1 G! d$ P3 _4 \+ S/ _
  10. uint8_t recv_end_flag2 = 0;  t1 w5 f% M6 {
  11. uint8_t rx_len2 = 0;3 U0 l8 _" j6 `- p5 }* i
  12. ) b) N( q/ f* X" Z: f
  13. 。。。
    + G7 F" l# E) l) |

  14. ( C) P$ j5 y5 Z' U/ X1 z3 Z
  15. //初始化
    , ]) u2 y/ k8 l4 V" T# Z
  16.   __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
      o$ G7 y, `; ^0 d/ q7 _6 M' L" l
  17.   __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
    , a  S" ~& t5 F/ a5 m
  18.   
    / U. I6 z* V: n5 [5 c
  19.   HAL_UART_Receive_DMA(&huart1, UART1_Rx_Buffer, DMA_RX_BUFFER_SIZE);
    ( C: v5 y; Q# y" X6 D$ t# K- o% L
  20.   HAL_UART_Receive_DMA(&huart2, UART2_Rx_Buffer, DMA_RX_BUFFER_SIZE);
    ( l! U9 k4 z0 e1 |- P% m$ b
  21. 9 W8 C( f/ }; U+ x9 E9 q
  22.   。。。; n: B+ s# p6 W1 W/ d+ Q! r8 A
  23.   
    + u$ z: g, h7 b% p5 K1 d
  24. //main()/ W. G+ Y" V! h2 S
  25.     if(recv_end_flag1 ==1){     //UART1 Rx, UART2 Tx
    9 |0 y8 x# A& Z+ e0 G7 L4 u3 N' L

  26. + i* E! Y$ `& l5 h6 b7 C- |( e3 g
  27.       HAL_UART_Transmit_DMA(&huart2,UART1_Rx_Buffer,rx_len1);0 ~. O6 P% q1 ^
  28. - S9 _2 s- n" L2 r* K$ {
  29.       rx_len1=0;
    0 R, W7 ~3 Q* i" a6 Y% }
  30.   @* L/ c9 {3 h  @- ^& E. m
  31.       recv_end_flag1=0;. X: I; ^) n6 y6 ?5 M/ T
  32. ( G4 u3 k, {; J; e+ ?
  33.       HAL_UART_Receive_DMA(&huart1,UART1_Rx_Buffer,DMA_RX_BUFFER_SIZE);+ ^/ k  z4 F  U
  34.     }
    ' ~$ b! u# e: y/ ~* ^/ M* R
  35.    
    # a3 W5 }0 u7 B- \# T! R# Q
  36.     if(recv_end_flag2 ==1){    //UART2 Rx, UART1 Tx2 d7 N9 `+ b5 s& x& ?) M
  37. & @- i4 ]% w6 \' i$ h/ M+ F2 y
  38.       HAL_UART_Transmit_DMA(&huart1,UART2_Rx_Buffer,rx_len2);
      E3 }: I' d( B3 [8 ]% j
  39. 9 w9 l# I, E- I7 V  A5 ^* ]
  40.       rx_len2=0;$ d) x2 Y- x- s8 f# L
  41. ! b0 q/ o) k: _, T8 B
  42.       recv_end_flag2=0;
    2 j$ {2 h0 q7 p! n) F! \! k
  43. + j0 w8 Y% y" R9 Q- r8 w5 Z, l
  44.       HAL_UART_Receive_DMA(&huart2,UART2_Rx_Buffer,DMA_RX_BUFFER_SIZE);6 O* H: g! ?; N9 |4 o; o8 P
  45.     }
    ; m- G7 K; [; b' Q4 }" H4 T
  46.     6 Z8 H8 q- ~, Q% b
  47.    
    * E# u/ ]" a( _  T# q
  48.     ' a/ w" v# n: \$ |) q1 M1 O
  49.     . ^/ t& D( `/ s% `
  50.    
    : Q2 Z7 J8 X3 h% }( x7 K
  51. //stm32f1xx_it.c 中断服务程序里面修改的代码:3 ~; l* q; d1 B( e8 A) |( [
  52. / f7 s4 c+ B4 j- `( _5 |
  53. extern uint8_t recv_end_flag1;4 T& Q$ j7 l+ X2 c3 ~
  54. extern uint8_t rx_len1;
      i! M4 ~( z6 Q2 [2 v
  55. extern uint8_t recv_end_flag2;
    & _+ k/ P1 s# J$ V- d. r
  56. extern uint8_t rx_len2;
    ; V5 D, l; S$ k$ I( L& s3 E% c
  57. ( h" O3 |# a- A' r" K& \# Q
  58. #define BUFFER_SIZE 128/ k2 N+ S2 c( c' J3 f& J% Z

  59. ( V$ p$ x6 b' l$ o  H
  60. void USART1_IRQHandler(void)/ c+ B- j* A! s- A# U" d4 e7 {) ^
  61. {
    ( r( @) f' u/ x) H( A
  62.   /* USER CODE BEGIN USART1_IRQn 0 */* @6 D( ~" |5 h- s0 M5 z
  63.     uint32_t tmp_flag = 0;
    9 w+ V! l! x/ E3 s
  64.     uint32_t temp;( k. e" l3 W' A+ G" H! O* D

  65. $ P0 L$ l& C4 O1 G
  66.   /* USER CODE END USART1_IRQn 0 */   
    * F* g8 C+ k' T4 j! m
  67.   HAL_UART_IRQHandler(&huart1);
    " \1 g8 k4 i+ A) u6 l- X! T9 {8 E
  68.   /* USER CODE BEGIN USART1_IRQn 1 */
    * \2 l1 O8 D; }8 M
  69.   n( T  G2 C! \2 ?# d& a
  70.   tmp_flag =  __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE);
    & q+ P3 a, G1 H  V

  71. : y6 D7 e7 E( ^1 s0 P
  72.   if((tmp_flag != RESET)){1 S9 N  ^0 K# M
  73. / _5 J" i$ E( C( ?
  74.     __HAL_UART_CLEAR_IDLEFLAG(&huart1);+ l( P( H) ^- X3 i8 o5 G, y
  75.   d" @5 X- y  y  F* B& b
  76.     HAL_UART_DMAStop(&huart1);
    + {8 t$ s1 z) R6 F% Q7 w" P9 a/ K
  77. % R# |  z! x' h9 G+ ]+ o) N
  78.     temp  = hdma_usart1_rx.Instance->CNDTR;) X1 ^0 {- r$ h2 Z8 C9 M8 E( K. d

  79. ; ?- E9 d( o2 d' P& _& F! L  ?
  80.     rx_len1 =  BUFFER_SIZE - temp;
    4 J/ t9 s4 Y: ]2 g2 [
  81. , @& ?! p* |6 F/ i
  82.     recv_end_flag1 = 1;
    7 j  G, B9 ^7 }, f- M5 I
  83.   }; K4 O- L" T6 l1 A" z6 E
  84.   /* USER CODE END USART1_IRQn 1 */
    2 g% l! D! F1 _2 L4 o+ p
  85. }
    & f1 e3 y! s9 ]5 F& c. |

  86. " R" _7 o; m2 L9 C1 ^6 F

  87. 7 b, ^" G; N1 T, H- V
  88. /**
    - ^2 Q; R" S4 f2 d/ Z
  89. * @brief This function handles USART2 global interrupt.
    $ Y. `5 s2 y* t! _+ D
  90. */
    $ b" p' ?" p0 L- m7 C+ N: E
  91. void USART2_IRQHandler(void)
    6 [0 ]0 q+ P; B$ z
  92. {# f+ K& P& q- R' }, ~! r
  93.   /* USER CODE BEGIN USART1_IRQn 0 */
    7 D( k  E. m" q/ n
  94.     uint32_t tmp_flag = 0;4 y. S% n2 T8 a: _9 y# d
  95.     uint32_t temp;
    ! s* m2 w5 ?4 u! ^0 q, n

  96. ( n8 Z7 V# n; {2 ?9 d; v3 o
  97.   /* USER CODE END USART1_IRQn 0 */   
    6 z" k9 m- L; r9 h# T9 Z+ t
  98.   HAL_UART_IRQHandler(&huart2);
    $ A& `4 V; {$ K6 [: L- N5 N/ {$ T; b
  99.   /* USER CODE BEGIN USART1_IRQn 1 */
    1 a: o- @6 p" b
  100. 6 |4 L  N+ `  g$ c% i( ^8 h- H
  101.   tmp_flag =  __HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE);8 ?& |( w0 a% ^' }3 ?' J! P/ V

  102. ! @5 J- ^& D1 z0 K* @" A/ Z
  103.   if((tmp_flag != RESET)){
    7 u; K* i2 f  N5 A( b

  104. 4 o! z# p2 e& G& j8 d" ~
  105.     __HAL_UART_CLEAR_IDLEFLAG(&huart2);# e/ v8 Q* e! x) l) b: M4 o: F
  106. , e% g$ E. R* C" z5 P
  107.     HAL_UART_DMAStop(&huart2);
    2 P  U) c9 a+ k
  108. 6 L4 H  e" }4 N( d3 Y+ X+ E
  109.     temp  = hdma_usart2_rx.Instance->CNDTR;
    / Y4 a1 V1 z/ b# @: F# C: z& O6 {

  110.   r/ _' F: T  X% G
  111.     rx_len2 =  BUFFER_SIZE - temp;
    7 G1 S+ ?/ m+ G' B8 [( ~) }

  112. / O9 F( _, e5 C" Q# h0 o/ W/ c
  113.     recv_end_flag2 = 1;6 t& {- j  p( N4 R$ ?6 r8 j# d
  114.   }
    : e5 s' u5 ~* k2 l1 J3 P
  115.   /* USER CODE END USART1_IRQn 1 */. _8 C  }: e9 F# D- w3 M8 U2 r3 T: H( V
  116. }) 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

STM32F103-USART-DMA.rar

下载

1.66 MB, 下载次数: 354

评分

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

查看全部评分

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

举报

14个回答
radio2radio 回答时间:2019-3-11 14:26:44
今天,有做了一些测试。从使用的角度来看,一楼附件的程序是可以使用的,只要,
0 Q' Z# T2 n! G3 j# z3 O+ ~5 L# `1. 数据包长度不超过DMA缓存的长度。2. 发送的间隔不少于200ms。
( e! {. \/ d9 {- j; u5 ]* Q就可以115200双向同时收发无差错。. F! s  z1 A$ _. b
( K6 Q6 U" K3 @; }5 |! m5 _
至于单方向收发,1Mbps,2Mbps,都没有问题的,放心使用。
riceglueball 回答时间:2019-3-21 09:50:33
学习了。
wh8 回答时间:2019-3-21 10:47:35
感谢LZ的分享。个人认为DMA的好处还是能释放CPU。一组大量的数据DMA传输,启动后CPU就不用怎么管了,DMA中断也只在一半完成或全部完成时触发;但无DMA时,每个字节收发时其实CPU都需要入中断操作一次,只不过库函数帮你做了而已。
- ~3 K, |+ B5 H所以个人认为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- A+ p3 I7 r- R5 l; e
感谢LZ的分享。个人认为DMA的好处还是能释放CPU。一组大量的数据DMA传输,启动后CPU就不用怎么管了,DMA中 ...
7 G& v' Q  S/ h5 K: N& Y0 G
同意,谢谢。
6 A1 z' J5 ~) l1 B& w
9 @. s# Q* }" H- K  U! U( F总之,串口通讯,本身是没有纠错重发功能,速度又很慢的外设。' _2 o& H6 s# W. a* t
影响丢数据的因素,也就是那么几种,照顾到了就没有问题。
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
谢谢 分享* `. m0 A# U+ j9 K( I. v+ ?% y
daxinxin12 回答时间:2020-7-23 13:59:38
学习一下DMA的操作
jesseqiao 回答时间:2021-1-19 17:11:51
谢谢了,好用

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版