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

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

[复制链接]
cuyebiren 发布时间:2018-1-24 13:29
个人感觉,做嵌入式,底层就是datasheet,顶层就是数理逻辑# U+ g) i& s3 w
不管什么芯片,当我们遇到问题时,通过查阅datasheet上官网基本上都能找到解决方法。然而,这些基本都是英文。所以,英文好对做研发是有很大益处的。不过好在有翻译工具,如:有道(我就是用有道划的)
) A9 d% m( m8 P. `5 fC语言虽然没有class,但有struct。我们可以多用struct。ST库和ucos中就包含许多struct。  V& @* o6 {* L7 V9 G8 X9 s
程序结构多用状态机或switch。个人感觉,状态机看起来很有条理,很清晰,后期修改维护也方便。/ `* i" I; ~* j7 E7 z0 n
鄙人浅见,请大家不吝赐教。' v) |8 H6 ?  f% E
进入正题:UART以DMA方式接收和发送的函数调用顺序
9 |% y; `7 s; z8 D0 x  ]循环模式接收: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)
: l. R7 S' r# u; b* b当然这当中还会调用传输 Half 中断,这里就不讨论了。
7 G1 d) @/ u) I. F
$ }9 q0 ?3 l  G8 y正常模式发送: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 Q- o! ?& S4 s  Q% t4 B$ j9 Z7 I$ M$ i5 \
这里只分析 HAL_UART_Receive_DMA 的源码,其它的大家自己分析:
. t" w: b5 }  e( R. bHAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
* K" \# M5 q! L6 ^UART以DMA方式接收指定长度数据到指定缓冲区
- _" G3 @& j* E; T! i{
5 q5 b1 b; w' R5 c2 t  uint32_t *tmp;% y( p! j0 F+ B( z
% u9 U. z& D1 r0 _  t9 Y2 [
  /* Check that a Rx process is not already ongoing *// S$ o) r1 G2 |" }. g% d- K
  if(huart->RxState == HAL_UART_STATE_READY)   检查huart->RxState是否处于 接收空闲 状态。当这一状态标志非READY时,会跳过DMA接收参数设置,直接返回HAL_BUSY其它的UART接收函数也会检查这个状态,所以,哪个先调用就执行哪个。9 Z# n3 J2 |+ u0 _! a; X
  {
* G( c6 n9 ^! ]7 J" Y; m    if((pData == NULL) || (Size == 0U))
5 \2 w8 G2 Q: m& ~8 u    {
! ]! p' t2 R# B, F. i! }# C      return HAL_ERROR;1 F' ~1 S4 D" c$ D2 }
    }
8 h0 J/ Z' A& D5 t' F( j; h3 O3 P, b6 |8 t  o" K; N. {5 U
    /* Process Locked */# b6 x" m# y+ O5 d4 o
    __HAL_LOCK(huart);: {, v; }5 h" C. }5 f
& i* R% b8 v0 u# |: }$ `6 J
    huart->pRxBuffPtr = pData;+ Q! F3 P# D; q- V( u  w
    huart->RxXferSize = Size;
3 G5 `4 @, f& E
# \* d5 ?0 M, X. Z* h& |& J6 ~    huart->ErrorCode = HAL_UART_ERROR_NONE;4 m0 F" x. o5 C' K* `6 R% U. h
    huart->RxState = HAL_UART_STATE_BUSY_RX;! }1 U3 r5 L1 e( K4 W( O

, H) f1 Y8 r4 s7 d! h    /* Set the UART DMA transfer complete callback */
9 a3 d' O% |$ _0 s' T9 T    huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; 设置DMA传输完成的回调函数。当DMA以循环方式传输时会调用UART接收完成中断的回调函数;以Normal方式传输时会关闭UART的DMA通道,并使能UART传输完成中断,触发UART传输完成中断,设置huart->RxStateREADY,并调用) l& L- B5 @7 b* `1 X
UART接收完成中断的回调函数。所以,
不管DMA按循环或正常模式传输,到最后都会调用UART接收完成中断的回调函数
- w* J$ U8 ~0 [2 z$ y3 \    /* Set the UART DMA Half transfer complete callback */
; u( \5 y5 z+ N# e    huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;( t0 o" K8 e+ T: W

$ ?) T4 C0 d9 U  d) e& V) n    /* Set the DMA error callback */5 }( h* v; K% W1 q
    huart->hdmarx->XferErrorCallback = UART_DMAError;
5 p0 R* Q* H+ {; ]" M" e) ?# G. V' x) ~$ r
    /* Set the DMA abort callback */
3 \+ j# c5 v8 j" Z& O/ d: L- p0 Q    huart->hdmarx->XferAbortCallback = NULL;
& t7 M+ }8 v" m2 U- r1 k- H4 j4 E: s4 M0 q+ s
    /* Enable the DMA channel */8 l1 h4 n) w5 J  L- `/ ]/ h
    tmp = (uint32_t*)&pData;
, }5 p" f8 z: B    HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);以中断方式打开DMA传输。所以CubeMx中DMA中断默认是打开的切不可更改2 E1 y+ j0 b( W" g* d/ T2 D

* s4 g$ U8 w& v4 D- C* O8 u    /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */! g& q; w! X; E0 M  ~
    __HAL_UART_CLEAR_OREFLAG(huart);1 f. t- B. V7 J9 x! [6 T1 I

3 j  h- t5 Z! y, E6 C    /* Process Unlocked */
* W$ ?1 c3 O8 l- A2 O    __HAL_UNLOCK(huart);% w' O/ [9 y7 K) [# a

! l5 p! {% X8 ?4 Z4 @    /* Enable the UART Parity Error Interrupt */
/ J( d' \. `8 m% ^$ T- V3 D5 O$ u    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);
8 D. h3 ]8 B, f2 L
3 q$ C3 [* |3 z: A    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */7 i+ b' w. Q  o8 I/ ?- o* |# r2 X
    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
) f4 E% s) J/ I0 U. R0 m7 @! K, H) l3 ~2 \+ c2 `+ G
    /* Enable the DMA transfer for the receiver request by setting the DMAR bit 9 C$ ~" U9 A' Q9 Q. L2 ~" G2 J) ]) W
    in the UART CR3 register */. Y, u' T; v1 k: l% q( U- h$ V+ u
    SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);% j9 @  E/ Q9 N& i' f0 S! J; W
& O% P8 K1 u# d. e
    return HAL_OK;
) U& @6 q  E, {  }
- z* S( m! M2 C: s# S8 x0 z  else1 I1 c$ A& v  r
  {
0 N% Y+ p. \; w( W2 g  `9 }8 b    return HAL_BUSY;
1 E- }$ w) P, K- V  }
% @) D* x( N6 v' H0 f4 _}
) [5 h* q: g; o, {( h" n1 H下面上我做的测试:一、Cube配置2 `: ]) E0 K  M
1、DMA接收要用 循环模式,只需调用一次接收函数即可,重复调用也只有第一次调用有效。若是 正常模式,需要在每次接收完成后重复调用。; i& h$ |$ y# `+ ~! J/ u
2、DMA发送要用 正常模式,需在每次发送时重复调用。还要打开UART中断,否则即使重复调用DMA发送函数也只有第一次发送有效,其余不会执行(UART发送状态一直处于 BUSY)。若是 循环模式,则会连续不断地发送,不会停止。
" A3 N; F. t. x; K( \2 Q以上两点大家可以自己改改测试。4 @; I! O( B) C8 N" u3 P) r
2.png 1 l+ T# X2 ^- t* T
3.png
% p5 N, ?* O! U7 g- r% S 1.png $ X( J, j6 Z& y* S
+ D% r7 D4 X. ]& C
二、Keil例程编写  D! ?; V5 W+ ]1 J, N9 {& R$ |% C
4.png & R5 c0 |- `9 l4 h
6.png
$ \# g0 I  S' Y7 w; K/ k! N. Q 5.png
  {; l; m$ e. [) I* x2 W2 \$ T
+ }3 D: a* P* ~三、测试# t- c2 P9 Z( H+ F6 b+ w2 p2 ~
可以看到,发送数 = 接收数。大家可以做更大数据的短间隔长时间测试,看看会不会出错。
  z7 y6 \* H9 B) H 7.png
: V# M" l: F4 l# A" {  b7 L) @! g; p) I  B" x
最后,奉上测试工程: uart_dma_test.rar (3.7 MB, 下载次数: 2104)

评分

参与人数 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库无影响,如果以后串口出现锁死的话,再想想我说的话,或许有点帮助。串口发送前检测下发送状态标志,串口中断函数中,先暂停下发送通道,处理数据,再恢复发送通道。本贴的测试代码还是基于一收一发模式。
  G% R" v% L# f- ^: u% r  b4 K
$ P4 t( v4 }7 ^+ H7 A( d3 h  p2 p
xzrsh 回答时间:2019-8-9 20:57:26
不能用,编译路径错误。
6 f4 f( E1 ]* M0 G3 G/ v3 o% i"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月技术原创7 v3 E6 L. N. s4 i/ m  n5 H- p5 V
https://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管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版