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

HAL库UART按DMA方式发送和接收函数简析及测试  

[复制链接]
cuyebiren 发布时间:2018-1-24 13:29
个人感觉,做嵌入式,底层就是datasheet,顶层就是数理逻辑。; k( p1 y& d1 ]2 h! }  A* w
不管什么芯片,当我们遇到问题时,通过查阅datasheet或上官网基本上都能找到解决方法。然而,这些基本都是英文。所以,英文好对做研发是有很大益处的。不过好在有翻译工具,如:有道(我就是用有道划的)
5 X% E/ Q# P7 N" R# I+ d/ a) N/ oC语言虽然没有class,但有struct。我们可以多用struct。ST库和ucos中就包含许多struct。; O6 V; }2 O! N! M0 a1 _
程序结构多用状态机或switch。个人感觉,状态机看起来很有条理,很清晰,后期修改维护也方便。( B+ I7 `* o) _; V1 r0 Z2 d4 p
鄙人浅见,请大家不吝赐教。
/ {* h# @) P" ~4 }进入正题:UART以DMA方式接收和发送的函数调用顺序:
8 e+ z, I. M" H' Z. E$ h循环模式接收:HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)==》DMA1_Channelx_IRQHandler(void)==》HAL_DMA_IRQHandler(&hdma_usartx_rx)==》UART_DMAReceiveCplt(DMA_HandleTypeDef *hdma)==》HAL_UART_RxCpltCallback(huart), S2 b% F/ U1 U4 O* w
当然这当中还会调用传输 Half 中断,这里就不讨论了。: R2 Z* y+ Y9 H. o
3 H0 m1 W: q. f/ a# m
正常模式发送:HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)==》DMA1_Channelx_IRQHandler(void)==》HAL_DMA_IRQHandler(&hdma_usartx_tx)==》UART_DMATransmitCplt(DMA_HandleTypeDef *hdma)==》USART3_IRQHandler(void)==》HAL_UART_IRQHandler(&huart3)==》UART_EndTransmit_IT(huart)==》HAL_UART_RxCpltCallback(huart), ]2 b+ Q2 C) o- V. H
& y0 f/ H- h5 }! W) l7 S
这里只分析 HAL_UART_Receive_DMA 的源码,其它的大家自己分析:) V7 E: e6 Y" W3 B
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
7 _1 n; r6 h  `5 K/ t& I/ i: u! XUART以DMA方式接收指定长度数据到指定缓冲区
0 e* E4 C. }) H{
3 N& ]: T# S/ H/ m  uint32_t *tmp;* x" E. e2 W& Z4 M5 W

, `4 D) d8 s  U5 f% t, }  /* Check that a Rx process is not already ongoing */. H3 {; F) q2 ^8 n9 l" S; k. d; q2 W
  if(huart->RxState == HAL_UART_STATE_READY)   检查huart->RxState是否处于 接收空闲 状态。当这一状态标志非READY时,会跳过DMA接收参数设置,直接返回HAL_BUSY。其它的UART接收函数也会检查这个状态,所以,哪个先调用就执行哪个。
0 {4 p5 x. `) c. t: n5 \5 z  {0 e! E, C' L, n& g! x9 o
    if((pData == NULL) || (Size == 0U))3 p8 }8 N) t+ s$ K/ A
    {# b) ^) E% n& J3 ?+ j$ w6 g% Q+ i4 G
      return HAL_ERROR;& M  L. Q% \- I1 K$ A# k7 e- y) w
    }
/ q4 f2 x' U' ~2 s8 A2 S$ s( E9 b9 H' B1 M+ ^% c
    /* Process Locked */  a! o; Z5 S$ A
    __HAL_LOCK(huart);- h! V4 O+ A4 t

+ }" }" ?' F/ D2 E    huart->pRxBuffPtr = pData;% @& F- F3 E2 P
    huart->RxXferSize = Size;
/ J! L  V# Z5 T$ r
6 F/ P6 ~6 y$ m/ G3 f    huart->ErrorCode = HAL_UART_ERROR_NONE;
- n: I5 _3 n! E  M$ B    huart->RxState = HAL_UART_STATE_BUSY_RX;" o6 ]" U7 k; b$ b- r4 I6 _: C

- K& q; q. m" F# `  u! R    /* Set the UART DMA transfer complete callback */; M9 {1 P  v; G( y
    huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; 设置DMA传输完成的回调函数。当DMA以循环方式传输时会调用UART接收完成中断的回调函数;以Normal方式传输时会关闭UART的DMA通道,并使能UART传输完成中断,触发UART传输完成中断,设置huart->RxState为READY,并调用1 u: B+ u; `4 s: a, I- A4 {
UART接收完成中断的回调函数。所以,
不管DMA按循环或正常模式传输,到最后都会调用UART接收完成中断的回调函数6 l2 y3 |: m, z" y* q7 H
    /* Set the UART DMA Half transfer complete callback */
" [5 H* B9 M) C0 _/ l& h7 R) \    huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;& ?/ D8 |0 p+ |3 D0 K" r

& s  o. c& V7 S7 O+ t    /* Set the DMA error callback */2 ^( P* I3 d# M3 {( B$ s3 `9 W; m3 d, d3 u
    huart->hdmarx->XferErrorCallback = UART_DMAError;
  a) s% z$ j7 a* t; x2 L# C  [2 f4 V% T5 T
    /* Set the DMA abort callback */; R. m4 ~2 |! E* V& U
    huart->hdmarx->XferAbortCallback = NULL;
+ z1 R" M& ]3 ]4 q. c8 W* N- L1 I+ u6 v( g! c
    /* Enable the DMA channel */6 [$ K# y, L9 n% M( Q, v- v/ p
    tmp = (uint32_t*)&pData;
4 p: f* h9 \3 @) O    HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);以中断方式打开DMA传输。所以CubeMx中DMA中断默认是打开的切不可更改$ H/ G$ Q1 }# \1 B

+ q) a+ ~0 S* B5 Q    /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */, [# E# o8 v. k$ B% G
    __HAL_UART_CLEAR_OREFLAG(huart);
1 e, C& p. g0 n7 u, @' }8 [5 t( T: N( O9 P, ^4 @
    /* Process Unlocked */
4 }; c3 G4 m  z7 D    __HAL_UNLOCK(huart);
& i2 X. c& ^" C% I& B0 M$ c& L# s, Z: @
    /* Enable the UART Parity Error Interrupt */# Y5 f. T. W" B8 W; [) S/ j/ W
    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);; `: |+ r2 e+ v3 [
4 R- }8 [$ M3 h+ u8 s  H( f: r9 y# z% b
    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
3 N6 i/ z1 w1 ]. f    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
2 F( d2 m. C; ?2 Y
2 _/ V4 D! V% ?% ?, b    /* Enable the DMA transfer for the receiver request by setting the DMAR bit 1 \5 k& k# C# p2 N/ s
    in the UART CR3 register */
6 w' n) x: P) h    SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);) M" z& H' T: U9 K
% Y, y- `8 O8 L# t4 [6 I# X. H  M
    return HAL_OK;
' s8 q( w5 [' t/ |. \' v! d0 N; _  }; @( v/ r0 `% E& z+ A& a% I' J
  else
6 s+ E/ F4 X% p" q* W9 [, g1 j! l  {
3 C+ u+ ]5 E3 I" C# s0 a, M, g    return HAL_BUSY;3 k0 n% U4 y8 ^# z- w
  }
# `/ h' j3 o6 v; P6 V7 _/ l}
5 a  {# ]  s/ _下面上我做的测试:一、Cube配置
0 m3 j9 e& A3 q# W- A5 M, n6 s1、DMA接收要用 循环模式,只需调用一次接收函数即可,重复调用也只有第一次调用有效。若是 正常模式,需要在每次接收完成后重复调用。. t% k* r) [' x! k
2、DMA发送要用 正常模式,需在每次发送时重复调用。还要打开UART中断,否则即使重复调用DMA发送函数也只有第一次发送有效,其余不会执行(UART发送状态一直处于 BUSY)。若是 循环模式,则会连续不断地发送,不会停止。
/ K: o% U; ^) S4 g: v' z9 [$ m8 Q以上两点大家可以自己改改测试。
( E7 s9 N5 [5 B+ x 2.png 0 H; b5 f; x7 }" @. ]
3.png * y  P- d: Z+ {' \
1.png
5 {. M$ C2 u" b2 N% j3 p# J) S' a  D
二、Keil例程编写6 V4 P& y3 W& @  U3 Q- R
4.png
$ Z# e5 E+ E# n3 U: `1 o8 z 6.png " K( n3 T( V9 A6 h0 E
5.png
4 \; L5 E; g3 p8 \+ b$ B4 q( d$ u9 |1 I7 O' \; ^
三、测试
$ A, P5 `) \% P' I7 J5 H+ I& b. d可以看到,发送数 = 接收数。大家可以做更大数据的短间隔长时间测试,看看会不会出错。
* d! j7 {6 N$ v7 Y5 n- ]) z+ j 7.png
* v" e  D% V# E3 {0 H% \4 X' U2 t8 B4 S! ?
最后,奉上测试工程: uart_dma_test.rar (3.7 MB, 下载次数: 2111)

评分

参与人数 2 ST金币 +11 收起 理由
buffoon51 + 1 很给力!
zero99 + 10 谢谢分享

查看全部评分

赞 收藏 10 评论27 发布时间:2018-1-24 13:29

举报

27个回答
sgsong 回答时间:2018-7-26 14:00:57
虽然几年前就讨论过HAL串口全双工通信问题,不过还是要重提下老问题,使用HAL库串口全双工通信记住个原则,不管是哪种工作模式,都不要同时收发数据,__HAL_LOCK(huart) ,锁的是整个外设,不是TX通道或RX通信,非HAL库无影响,如果以后串口出现锁死的话,再想想我说的话,或许有点帮助。串口发送前检测下发送状态标志,串口中断函数中,先暂停下发送通道,处理数据,再恢复发送通道。本贴的测试代码还是基于一收一发模式。8 d* ?6 Y, C1 X+ R

7 u6 M* _: Y' f0 ^) x/ D, C* Z9 A: m+ K" K
xzrsh 回答时间:2019-8-9 20:57:26
不能用,编译路径错误。
+ n" n) T$ x/ q. }  t5 _' K"no source": Error:  #5: cannot open source input file "C:/Users/Administrator/STM32Cube/Repository/STM32Cube_FW_F1_V1.6.0/Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_rcc.c": No such file or directory
进阶的熊猫 回答时间:2019-11-1 14:43:10
采用NUCLEO版测试官方CUBE例程“UART_TwoBoards_ComDMA”,按照楼主讲解一步步跟踪调试,各函数的跳转果然如此。。另外文中“正常模式发送”中的函数调用顺序最后应该是HAL_UART_TxCpltCallback。谢谢分享。
zero99 回答时间:2018-2-1 09:25:49
感谢支持,已汇总到1月技术原创
; c& O; U  e. y# mhttps://www.stmcu.org.cn/module/forum/thread-614550-1-1.html
zhjb1 回答时间:2018-2-1 13:59:04
心得分享,谢谢了
zhao.zhao 回答时间:2018-2-2 09:13:13
分享经验,共同进步,谢谢楼主
dark_ness 回答时间:2018-2-3 22:03:44
感谢分享
junmayu 回答时间:2018-2-4 20:16:50
学习下
河狸先生 回答时间:2018-2-8 15:11:46
谢谢分享,正在捣鼓这一块,很有帮助
glfglf168 回答时间:2018-3-29 21:03:55
敬佩楼主的奉献精神!
wangwang4428 回答时间:2018-4-25 08:38:11
谢谢分享,参考一下
板子粉丝 回答时间:2018-4-25 08:40:34
谢谢分享
liyoude 回答时间:2018-5-7 14:18:06
   谢谢楼主
maomoa 回答时间:2018-5-15 23:04:06
敬佩楼主的奉献精神!
samhong 回答时间:2018-5-16 07:23:21
谢谢分享,谢谢分享。
电子星辰 回答时间:2018-5-16 17:02:25
好像不能只用DMA接收,printf发送。
12下一页
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版