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

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

[复制链接]
radio2radio 发布时间:2019-3-8 22:08
本帖最后由 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
  1. //main.c 添加的代码:4 k2 A# h- f( _) }

  2. - N8 K2 [# p; V
  3. //变量:" D$ ~2 ?9 ]+ R
  4. #define DMA_RX_BUFFER_SIZE            128
    3 i' F) E4 `4 w' j3 }- H$ K
  5. uint8_t UART1_Rx_Buffer[DMA_RX_BUFFER_SIZE] = {0};& ]/ _8 _8 H6 G. r- {8 g! H, Y
  6. uint8_t UART2_Rx_Buffer[DMA_RX_BUFFER_SIZE] = {0};$ `/ m6 F# c6 T7 u) S% k
  7. * T- H/ C3 P9 c; p+ k. G4 S' o+ \
  8. uint8_t recv_end_flag1 = 0;) F8 B& g1 e6 K
  9. uint8_t rx_len1 = 0;
    $ z$ O5 I4 r, q" g& x
  10. uint8_t recv_end_flag2 = 0;' C( X6 d6 r$ \/ n
  11. uint8_t rx_len2 = 0;1 g. {: ^: S  ~8 O: A8 f6 y; M8 A; \. Q
  12. / m4 g5 X4 k  b$ b& k
  13. 。。。9 S& ]: p9 i' ?. ?0 B8 U$ O
  14. 9 G( T4 O$ s- V5 j( V
  15. //初始化* _/ q: [7 h, v& [* N! u
  16.   __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
    8 D, m: R& I2 \, I0 {
  17.   __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);/ E7 z$ f1 w$ y& d
  18.   
    + L; h8 k& @5 Z7 ^1 `
  19.   HAL_UART_Receive_DMA(&huart1, UART1_Rx_Buffer, DMA_RX_BUFFER_SIZE);
    " ?$ H( J9 b$ ^" V0 i3 E0 I
  20.   HAL_UART_Receive_DMA(&huart2, UART2_Rx_Buffer, DMA_RX_BUFFER_SIZE);: j* a9 p) A0 y" n% `7 s4 P
  21. + _) }! ~7 E# X" T
  22.   。。。- E$ r" V9 J, Z0 ]4 u9 T1 f
  23.   : R+ j! [7 r& v& I
  24. //main()
    ) q' T) b# c4 C1 ~% Y
  25.     if(recv_end_flag1 ==1){     //UART1 Rx, UART2 Tx6 p- ?* o' s. W" }! v
  26. . W. T; h& P, z% ^6 b, s4 o; v
  27.       HAL_UART_Transmit_DMA(&huart2,UART1_Rx_Buffer,rx_len1);
    ! H) `# z5 C' H/ Z' L9 L" J  \: v. X

  28. 6 |9 L% u$ `8 {
  29.       rx_len1=0;
    3 v, C/ g) ?1 ~" V; Y

  30. . K4 t& i* C* p; n5 m- `
  31.       recv_end_flag1=0;5 G* N4 n; g3 m2 ~" x+ ^3 u' a. k4 l4 O

  32.   J) C1 q4 L: l
  33.       HAL_UART_Receive_DMA(&huart1,UART1_Rx_Buffer,DMA_RX_BUFFER_SIZE);+ |" A0 o* F/ A: F% r% _5 Q8 Q- G: [
  34.     }( y' Y5 o) q( K: {
  35.     $ d3 y7 k  }+ p
  36.     if(recv_end_flag2 ==1){    //UART2 Rx, UART1 Tx
    0 }# ~* W' p7 f7 d. S" O1 V' P1 I

  37. 0 U% A% ]1 X! `8 x9 l
  38.       HAL_UART_Transmit_DMA(&huart1,UART2_Rx_Buffer,rx_len2);0 D% E4 Y6 n* L: B2 R: c, N' Y

  39. # W3 a7 H' V5 a. B5 m1 j# D
  40.       rx_len2=0;
    : b9 Z' w* r( Q0 y7 N$ n

  41. 7 t, J" D3 A4 D" J& E0 v- }' l2 H
  42.       recv_end_flag2=0;
    ! i' r5 l+ n: Y9 a8 ^5 _

  43. * X5 Q7 P$ x8 ^+ m, Q9 B' x6 v( l
  44.       HAL_UART_Receive_DMA(&huart2,UART2_Rx_Buffer,DMA_RX_BUFFER_SIZE);
    & v" N6 @3 f- E1 B/ N
  45.     }
    " b# }0 Y! L: J! Q, m/ F
  46.    
    5 x) i( N0 ~% ]1 f% G* G1 F
  47.    
    ) [( N0 o& Z0 C8 S
  48.    
    " g( d5 [) k/ B) Y+ i# J
  49.     4 f8 h: j5 T; `" ~
  50.    
    + k" f1 O8 I( q$ w0 V* {$ C
  51. //stm32f1xx_it.c 中断服务程序里面修改的代码:' n- r& s. k# S0 p
  52. 7 Z: k$ d4 C8 C4 ]: o
  53. extern uint8_t recv_end_flag1;4 s* j/ ?8 t! ]# I* ~2 y* {: d
  54. extern uint8_t rx_len1;  k1 K) G) ~; b$ M5 j
  55. extern uint8_t recv_end_flag2;
    : ^3 e9 h' o2 D& C
  56. extern uint8_t rx_len2;, S! Q+ a6 _& v. W/ l" M) F" D* m. d& d

  57. 9 U( I& Z) q% M; v" c2 D' n
  58. #define BUFFER_SIZE 1281 ~7 b+ N9 O# t) [* N. W# c
  59. 5 k5 `! Y+ H2 \8 a
  60. void USART1_IRQHandler(void)
    ' U% u! D5 O( D0 l: n! w) i
  61. {
    - d, S( b& w/ }% N+ u+ h
  62.   /* USER CODE BEGIN USART1_IRQn 0 *// f9 T+ V3 M; _0 O+ _1 G* B
  63.     uint32_t tmp_flag = 0;
    1 ^7 d5 Q8 o' M* [7 i% {
  64.     uint32_t temp;
    - i- ^/ T- U2 J1 [, D
  65. ) Y. u! H2 f# B0 K' K  b
  66.   /* USER CODE END USART1_IRQn 0 */    ' d/ `/ I  x7 w8 z7 @; i/ l' F
  67.   HAL_UART_IRQHandler(&huart1);
    3 _( |& W2 Z5 D) {6 x- ]6 w
  68.   /* USER CODE BEGIN USART1_IRQn 1 */
    - i2 m" {# K" n; z9 c1 H( |& n9 K2 T8 q
  69. , ]. E; r1 l& ]) N3 H
  70.   tmp_flag =  __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE);
    $ W0 s0 A4 J: U9 x1 }, n
  71.   m+ e: x6 B, X) T
  72.   if((tmp_flag != RESET)){7 [# w6 S! t4 k/ Q8 }
  73. 0 ~) P0 i2 I2 G. W
  74.     __HAL_UART_CLEAR_IDLEFLAG(&huart1);
    9 I( M5 ]' H2 p" T: N! Y6 N
  75. ( k% D  N0 I, I% u$ G
  76.     HAL_UART_DMAStop(&huart1);2 c9 K/ F! o0 {$ w$ [

  77. ! o2 T; C- r1 B! b
  78.     temp  = hdma_usart1_rx.Instance->CNDTR;- S  ^) T, q, ?, L  D4 P0 l) q

  79. % x) e5 {+ J' R
  80.     rx_len1 =  BUFFER_SIZE - temp;
    9 M: r0 g9 G  f( O; T
  81. . @# h  x1 r, w. |- ^; ^
  82.     recv_end_flag1 = 1;
    : X  Y) }+ q9 k2 k
  83.   }' J$ M! o$ e5 S( g+ k+ Z, m8 K5 @+ O
  84.   /* USER CODE END USART1_IRQn 1 */
    ) P( b% P- D: A8 B
  85. }
    9 B1 B, k+ Q; }4 |
  86. ' A; ?+ n' e, S/ Q0 \

  87. * `1 S5 d' C' Z/ X
  88. /**& I- A2 r" A6 K4 i
  89. * @brief This function handles USART2 global interrupt.! S% {( {3 j0 B! _, f' o
  90. */) f& Y, G( b- z' n6 P2 W
  91. void USART2_IRQHandler(void)
    $ z3 r" J& H4 ^" F0 Z2 h& R1 e
  92. {9 {1 Q8 O5 ^9 B0 n$ z
  93.   /* USER CODE BEGIN USART1_IRQn 0 */. N( E5 ~8 R. A4 l3 {  i! \
  94.     uint32_t tmp_flag = 0;
    % W! S4 r" n" U: r+ p
  95.     uint32_t temp;- G: \3 j2 m) K; n# ]4 c
  96. ) ]5 v8 r) }7 w  C- h
  97.   /* USER CODE END USART1_IRQn 0 */   
    " r! j, S$ f$ V6 o5 t
  98.   HAL_UART_IRQHandler(&huart2);4 f* D# P1 v2 y7 _" `
  99.   /* USER CODE BEGIN USART1_IRQn 1 */: o7 g- L- _' i  _4 g: n9 Y  q2 j/ W' F

  100. - j* Z( m% e5 Y+ \2 p  n0 k
  101.   tmp_flag =  __HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE);
    ) k9 I$ ^/ t5 ~. o* I

  102. ( j# W5 `8 w; W, g
  103.   if((tmp_flag != RESET)){4 O) h$ Z8 X* P+ p! K& z+ w

  104. + G2 p4 a: p0 d; U; ^: }* q0 F
  105.     __HAL_UART_CLEAR_IDLEFLAG(&huart2);, L6 E2 t5 D' n, O5 I
  106. 5 u  M: ?+ m% h
  107.     HAL_UART_DMAStop(&huart2);
    + Q- v! a9 t; z% C4 f, N$ M, Z
  108. 9 G& Q+ s9 W1 L+ o, U; Y% y7 K- S
  109.     temp  = hdma_usart2_rx.Instance->CNDTR;  b7 {1 o8 Q( y1 e( F

  110. * }2 }: @* G! o- L& c
  111.     rx_len2 =  BUFFER_SIZE - temp;2 Q6 j0 ~0 h" \0 [, p- d" W

  112. ; L4 `, R3 p4 j4 p- h
  113.     recv_end_flag2 = 1;
    4 l, t7 C" Z" v8 F  ?
  114.   }
    6 E% b% O/ |! N; ?+ s* f, I: o
  115.   /* USER CODE END USART1_IRQn 1 */
    1 `6 c6 e! i$ _% H. N% g1 Q7 T
  116. }
    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

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
今天,有做了一些测试。从使用的角度来看,一楼附件的程序是可以使用的,只要,
, E4 f6 V5 @: y( z6 s3 f$ a' ~' p1. 数据包长度不超过DMA缓存的长度。2. 发送的间隔不少于200ms。" J" N! F" p5 H2 ?
就可以115200双向同时收发无差错。
) b& E4 k# ^) C2 z
* R  L, e% W6 ~+ y* I/ n* K至于单方向收发,1Mbps,2Mbps,都没有问题的,放心使用。
riceglueball 回答时间:2019-3-21 09:50:33
学习了。
wh8 回答时间:2019-3-21 10:47:35
感谢LZ的分享。个人认为DMA的好处还是能释放CPU。一组大量的数据DMA传输,启动后CPU就不用怎么管了,DMA中断也只在一半完成或全部完成时触发;但无DMA时,每个字节收发时其实CPU都需要入中断操作一次,只不过库函数帮你做了而已。
% [% O4 W$ c3 o0 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
$ q6 `( V0 M1 F* G% L感谢LZ的分享。个人认为DMA的好处还是能释放CPU。一组大量的数据DMA传输,启动后CPU就不用怎么管了,DMA中 ...

* _" b9 z7 j' L1 @3 r- ?9 F同意,谢谢。
- X8 y; \1 a* {( k/ ~, `- |2 l  E) S* D( B/ b+ E$ N5 X! B
总之,串口通讯,本身是没有纠错重发功能,速度又很慢的外设。
6 t0 C) U1 P. R% g$ J* n" U影响丢数据的因素,也就是那么几种,照顾到了就没有问题。
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
谢谢 分享
9 ^( E+ c5 W+ V% M
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 手机版