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

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

[复制链接]
cuyebiren 发布时间:2018-1-24 13:29
个人感觉,做嵌入式,底层就是datasheet,顶层就是数理逻辑; T- K- d$ W! i% H8 F# j' z' ^, D! O
不管什么芯片,当我们遇到问题时,通过查阅datasheet上官网基本上都能找到解决方法。然而,这些基本都是英文。所以,英文好对做研发是有很大益处的。不过好在有翻译工具,如:有道(我就是用有道划的); u, o( G2 C, p5 B1 Z
C语言虽然没有class,但有struct。我们可以多用struct。ST库和ucos中就包含许多struct。6 ]8 B  O7 {+ V* I
程序结构多用状态机或switch。个人感觉,状态机看起来很有条理,很清晰,后期修改维护也方便。
* f/ S& p# f  v4 X; @鄙人浅见,请大家不吝赐教。. n8 [: n, L: f) T& `
进入正题:UART以DMA方式接收和发送的函数调用顺序
0 O3 U  W" e( ^- p! i) F0 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)% I* _5 }" G' Z6 S# D$ K
当然这当中还会调用传输 Half 中断,这里就不讨论了。9 j& R; R. m5 C; l: _
; P$ R& b, k) r& a# X; l6 r6 ~
正常模式发送: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)9 o* p0 s4 J% [8 T5 ^# E

1 Y5 f, C! O$ I2 v$ L这里只分析 HAL_UART_Receive_DMA 的源码,其它的大家自己分析:+ J# E0 n/ |  ?1 }/ ?
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)$ L. k. p( V+ f; h1 W, _0 J/ f
UART以DMA方式接收指定长度数据到指定缓冲区* u+ r2 X1 G! I! i5 I3 M5 Z* A. p
{
4 q# F7 p" j: w  uint32_t *tmp;( ~6 }3 O7 F( O& b
9 ?& O! H) b3 B
  /* Check that a Rx process is not already ongoing */0 H+ o! v  }/ ~8 o, x
  if(huart->RxState == HAL_UART_STATE_READY)   检查huart->RxState是否处于 接收空闲 状态。当这一状态标志非READY时,会跳过DMA接收参数设置,直接返回HAL_BUSY其它的UART接收函数也会检查这个状态,所以,哪个先调用就执行哪个。
& |2 r# N; z: k* w" ^  {9 g8 o0 x4 i# I0 V; T
    if((pData == NULL) || (Size == 0U))5 I; \( t! p* j4 X* \* h
    {  R+ H; F2 l- A9 ~4 q% Z& |
      return HAL_ERROR;
- o' b; V) J. i- ~    }9 [  R: W2 }! o: l& \
2 Z+ j" F$ s- u- R( I& M
    /* Process Locked */
. a6 ]* S8 H5 j; Z' y8 ~' z    __HAL_LOCK(huart);
9 k2 _& H2 i2 o7 y. o8 u0 W" B6 Y; Z9 R& H. u, T
    huart->pRxBuffPtr = pData;* A: p8 Y4 ^, R7 ]4 j/ _
    huart->RxXferSize = Size;& M. M& s: {+ B% v- u) X6 }
9 P4 r$ T* v' c
    huart->ErrorCode = HAL_UART_ERROR_NONE;  A, p' D9 h4 f: n
    huart->RxState = HAL_UART_STATE_BUSY_RX;0 p1 \; T. f  i7 E3 Z  K7 {4 O
# `  Y, Y" R) {. k& @
    /* Set the UART DMA transfer complete callback */8 [% N8 M  K5 [( \( j5 h& Q0 C
    huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; 设置DMA传输完成的回调函数。当DMA以循环方式传输时会调用UART接收完成中断的回调函数;以Normal方式传输时会关闭UART的DMA通道,并使能UART传输完成中断,触发UART传输完成中断,设置huart->RxStateREADY,并调用" A, b4 O1 j% h; p6 R
UART接收完成中断的回调函数。所以,
不管DMA按循环或正常模式传输,到最后都会调用UART接收完成中断的回调函数
0 j. b  D! M5 n4 T) q) U    /* Set the UART DMA Half transfer complete callback */6 h1 i* j2 I1 Y$ L7 b3 p
    huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;, P3 @, H4 F- O4 Q7 ?" g7 d$ z

  i$ x, p. s- Z5 V: B$ q; b    /* Set the DMA error callback */
* t, P/ {4 }8 H: W    huart->hdmarx->XferErrorCallback = UART_DMAError;" [# W7 K' n7 n7 X8 C' n* b7 q! B" M

  h. s' `2 x2 Y% E. U* ^! H    /* Set the DMA abort callback */
; p& r* l$ I- }+ w    huart->hdmarx->XferAbortCallback = NULL;
( B; D# K( T2 ]* c/ T- J& R, W/ ^* a0 F
    /* Enable the DMA channel */
- M; l. h) k1 p/ r1 ~0 C/ o. R& \; z    tmp = (uint32_t*)&pData;0 w/ x) v% `7 e" ?7 b9 Z
    HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);以中断方式打开DMA传输。所以CubeMx中DMA中断默认是打开的切不可更改7 ?/ \' u2 P9 E
( h3 ^' S5 R* O1 A
    /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */9 C' [1 Q. K7 z
    __HAL_UART_CLEAR_OREFLAG(huart);
' v" D+ Y/ }. H- I$ y) B8 @8 u/ m, m9 s8 m
    /* Process Unlocked */
3 h5 h/ O6 ?5 l# Y1 D. Y! Q% u2 ?    __HAL_UNLOCK(huart);' {/ I& b/ k+ R, Z
8 e2 {. m- v) Y: t; `. t
    /* Enable the UART Parity Error Interrupt */1 [$ |! Q3 P. w5 m, i
    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);4 c, P: q0 v  k
$ s* l% T' [: R# F; T
    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
- H- o0 T$ e8 ?7 ^    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);* L# o4 @% h! @; D
/ Y2 A4 Y& g" _
    /* Enable the DMA transfer for the receiver request by setting the DMAR bit
, M1 \! ^5 t: r; K1 w5 W* K% }$ r    in the UART CR3 register */
9 M1 s$ c5 l, m/ G( b+ y, [    SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);% d. Q/ V  R  E% r% @$ O3 r: S

! k# o7 R7 b, o% F    return HAL_OK;* H0 u2 @! U* ^! Q! X5 {2 x
  }
. I- Q9 q7 }; _9 \  v5 o/ s  else6 F( t+ e. h, r1 B
  {; R! \! t! g9 y4 S* ~
    return HAL_BUSY;6 E! A% S! h, ~4 |; Y, `8 V, t/ _
  }
7 t! l8 x9 L* s, Q! Q}
0 e" @' f- p, b5 ?( w2 Q下面上我做的测试:一、Cube配置
: X% Q3 u; G1 N% \5 k1、DMA接收要用 循环模式,只需调用一次接收函数即可,重复调用也只有第一次调用有效。若是 正常模式,需要在每次接收完成后重复调用。& L  D0 l( n; m% B' r: G
2、DMA发送要用 正常模式,需在每次发送时重复调用。还要打开UART中断,否则即使重复调用DMA发送函数也只有第一次发送有效,其余不会执行(UART发送状态一直处于 BUSY)。若是 循环模式,则会连续不断地发送,不会停止。
! P7 R+ f# S! M4 [5 a以上两点大家可以自己改改测试。' ~" j, A4 x9 d/ \
2.png / p7 a, u4 ?" }& l' N
3.png ; t7 h- G3 q1 S( n
1.png
+ [* E, W; X) P/ S5 a4 C+ j* `' L& B' b1 G, V/ w
二、Keil例程编写
1 }+ ?1 o3 D) S  ? 4.png
1 X) D  g" r* F* K 6.png - G3 ]& _, o5 X! e; {1 W1 ~. j7 B+ g
5.png
# g' a* }1 ~* M5 `8 j- j# u6 S( ~6 e1 O
三、测试* A: h/ u: I4 n" h, h$ H
可以看到,发送数 = 接收数。大家可以做更大数据的短间隔长时间测试,看看会不会出错。! |( i9 G, ]- N4 ?: v7 K% T2 z
7.png ! l" T4 w. K  d0 f: Y( l; u
8 S6 B3 S; u6 Z# z( U. X
最后,奉上测试工程: uart_dma_test.rar (3.7 MB, 下载次数: 2085)

评分

参与人数 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库无影响,如果以后串口出现锁死的话,再想想我说的话,或许有点帮助。串口发送前检测下发送状态标志,串口中断函数中,先暂停下发送通道,处理数据,再恢复发送通道。本贴的测试代码还是基于一收一发模式。
6 z  M0 n  w! l5 X- Q! \0 |5 B8 {# q% f1 x3 T+ g" d8 i

6 e9 P3 I/ T1 A; w- J
xzrsh 回答时间:2019-8-9 20:57:26
不能用,编译路径错误。
# Z% A0 Y2 C" Z3 f' d% \4 Z2 S"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 \, G( Z, b7 Chttps://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管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版