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

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

[复制链接]
cuyebiren 发布时间:2018-1-24 13:29
个人感觉,做嵌入式,底层就是datasheet,顶层就是数理逻辑$ L7 `6 c! N" r3 v7 R9 v
不管什么芯片,当我们遇到问题时,通过查阅datasheet上官网基本上都能找到解决方法。然而,这些基本都是英文。所以,英文好对做研发是有很大益处的。不过好在有翻译工具,如:有道(我就是用有道划的)
  Y. p! R; G7 x0 q  MC语言虽然没有class,但有struct。我们可以多用struct。ST库和ucos中就包含许多struct。
9 s* w1 P$ P! d, v, `9 J3 V程序结构多用状态机或switch。个人感觉,状态机看起来很有条理,很清晰,后期修改维护也方便。$ f6 [2 C0 p. k4 J3 q& N0 ^
鄙人浅见,请大家不吝赐教。
6 c8 Z" ?6 |( J# T) Z" P5 D进入正题:UART以DMA方式接收和发送的函数调用顺序, [- S$ ]% t. B; z4 s9 k3 `. W
循环模式接收: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)
, V7 T2 D! i, @7 `" u+ Z4 r+ L当然这当中还会调用传输 Half 中断,这里就不讨论了。
/ Y# \0 Z8 L  y# z" r" m3 T' ^3 X7 @2 s8 V  [! Q) U2 M1 x9 t( 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)
8 d1 A# q7 u5 b# K- F0 f" R
' U3 M  d% ^# s) O9 |( Q这里只分析 HAL_UART_Receive_DMA 的源码,其它的大家自己分析:5 l2 f2 m+ k- J* r* g7 T8 _
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)( ?& ], K" Q1 d- ?* j- {
UART以DMA方式接收指定长度数据到指定缓冲区1 a8 b) n2 E+ q4 z- u4 [* V5 G
{1 A9 a; v+ {% [4 d! l: E" W
  uint32_t *tmp;
7 C, \, v" L& q4 J+ K2 g1 f
/ T$ x  p- _$ [0 T  /* Check that a Rx process is not already ongoing */
4 E7 r6 ?% s4 }( S) I/ d  if(huart->RxState == HAL_UART_STATE_READY)   检查huart->RxState是否处于 接收空闲 状态。当这一状态标志非READY时,会跳过DMA接收参数设置,直接返回HAL_BUSY其它的UART接收函数也会检查这个状态,所以,哪个先调用就执行哪个。
9 y! {4 T. m' r# B  {
" d, m7 ^8 n! V5 }  b: ~    if((pData == NULL) || (Size == 0U))
# C  k2 H3 Y8 T0 A    {4 U: g& }/ O& x: w4 P3 t* B
      return HAL_ERROR;& G& a6 o) L- E; u; v: i- i/ j
    }
; }* C  ]  ?2 b
* v! ?. J6 [/ n; R% C, k; `    /* Process Locked */
/ r0 d0 h0 i7 L- j$ R1 j& h0 U/ k3 c    __HAL_LOCK(huart);" S* l  e6 O1 Y' \' j' j
4 I$ |; p4 v$ `! l: ]- i0 l. c
    huart->pRxBuffPtr = pData;/ E( k+ a$ C1 X0 S6 k7 d
    huart->RxXferSize = Size;
' }! n- U( e0 s- z
4 o1 F* f! [5 d# z    huart->ErrorCode = HAL_UART_ERROR_NONE;
+ [/ w/ y8 C2 w& L9 V! E    huart->RxState = HAL_UART_STATE_BUSY_RX;! R$ B- `1 }/ h7 q6 o, y0 }/ h
- @" V# `; l1 R5 v- }. t
    /* Set the UART DMA transfer complete callback */0 W! ~2 Y. M, I/ ]1 d1 s
    huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; 设置DMA传输完成的回调函数。当DMA以循环方式传输时会调用UART接收完成中断的回调函数;以Normal方式传输时会关闭UART的DMA通道,并使能UART传输完成中断,触发UART传输完成中断,设置huart->RxStateREADY,并调用
( T+ g/ {* o' @5 `4 o( GUART接收完成中断的回调函数。所以,
不管DMA按循环或正常模式传输,到最后都会调用UART接收完成中断的回调函数! p' c$ `7 p5 Z7 J5 L& k( ?
    /* Set the UART DMA Half transfer complete callback */6 J$ F9 }& t5 D4 M2 P4 P
    huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;5 Q0 p! x, t' ]5 i% M( {' U: \( S

! u. G  h' v6 o    /* Set the DMA error callback */& Y$ r1 x9 O( U+ b5 c6 H/ S" \
    huart->hdmarx->XferErrorCallback = UART_DMAError;! T: `8 `+ `' f# f% K

& ^& `' L& i) b: e, y* X    /* Set the DMA abort callback */. H& Y/ ~& X" I* J
    huart->hdmarx->XferAbortCallback = NULL;" ~5 y" |3 K/ O, J$ R0 m
  G; M8 e4 n9 u- E$ v- K1 L
    /* Enable the DMA channel */
8 j9 i( W1 {" }8 y& ^    tmp = (uint32_t*)&pData;/ ?, v  g6 ^& ?8 A9 b
    HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);以中断方式打开DMA传输。所以CubeMx中DMA中断默认是打开的切不可更改
- a( \! |+ x  B4 e
0 d" A2 M- T4 {; _  _    /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */
8 s. j: e% V. R- \, [    __HAL_UART_CLEAR_OREFLAG(huart);9 O/ @, G* o7 C8 L8 C$ I8 C5 A

$ v; U" |: D, n, U    /* Process Unlocked */
$ K" U! \+ ^* p& k7 d4 V) u    __HAL_UNLOCK(huart);6 b8 B# \; {; K. g' G
6 J5 v  ]: L' N4 x, p
    /* Enable the UART Parity Error Interrupt */
) q$ g& J2 z8 G' U1 }    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);
, w5 X4 G0 M9 Q5 l- y; j0 x$ y% P! V7 u1 y
    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
/ ?9 E+ K) K7 ~) y% a8 a    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
5 F* v" b  Y0 n# T& q8 c4 \  j5 ]! }( i7 L1 z$ Z! N5 X# I
    /* Enable the DMA transfer for the receiver request by setting the DMAR bit
' t+ O( D  O' k+ P. P    in the UART CR3 register */  I" O% S; [  i# T' G9 \* u
    SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);( Z  [" I* x3 v/ @3 I5 P6 W

( A" C5 a7 t+ s: z- B7 x    return HAL_OK;9 N; E  ~% h4 f
  }
! x, i6 [: H# ^' t4 k, C  else
  ?" M- h, b! m, W. U* p  {/ a! ?, J( b! \8 Q' [* L1 t- U
    return HAL_BUSY;9 d) g' K  K; w* A. t" W- x
  }
; i( d) B. s$ ^5 M: }' F$ f}9 {: T$ T4 [: t. a) B3 P- U' _0 I* G
下面上我做的测试:一、Cube配置. Z, t; @- d& T/ w( }3 C+ K  A
1、DMA接收要用 循环模式,只需调用一次接收函数即可,重复调用也只有第一次调用有效。若是 正常模式,需要在每次接收完成后重复调用。
: K. c. n& ^0 G- Y5 Y9 P( S2、DMA发送要用 正常模式,需在每次发送时重复调用。还要打开UART中断,否则即使重复调用DMA发送函数也只有第一次发送有效,其余不会执行(UART发送状态一直处于 BUSY)。若是 循环模式,则会连续不断地发送,不会停止。8 [% l( Z! m* o5 f& l% o
以上两点大家可以自己改改测试。
' |  p0 {+ _7 q 2.png , J# b% ^% D4 K2 V
3.png
" E. P! P5 [2 T8 p7 m8 m8 U 1.png , Y  t; d) ~7 Q( O3 Y1 M

  H+ W, ?$ A+ B1 N9 K二、Keil例程编写
; I* i$ ~) T; j( ?2 {$ g 4.png " Y% ]) r- U% y4 B4 w/ C7 ?
6.png
- [$ H7 ?6 a* U: O3 R- P 5.png
- j; N: d7 {0 |( M2 L  l& m# K! @3 d6 z- m; w
三、测试# ~& K( p! @2 h! E4 o! ]& {
可以看到,发送数 = 接收数。大家可以做更大数据的短间隔长时间测试,看看会不会出错。; a& q; O5 k9 O1 `7 c6 j  p
7.png
# |& K. x+ b/ A7 M9 \. U; A  w$ n) M+ a
最后,奉上测试工程: uart_dma_test.rar (3.7 MB, 下载次数: 2093)

评分

参与人数 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库无影响,如果以后串口出现锁死的话,再想想我说的话,或许有点帮助。串口发送前检测下发送状态标志,串口中断函数中,先暂停下发送通道,处理数据,再恢复发送通道。本贴的测试代码还是基于一收一发模式。
( _1 d+ L1 u7 n& D" w
/ q7 _5 x$ Z. i, O  B
# O" O- {" A, H1 w8 S, e2 Y) D3 f
xzrsh 回答时间:2019-8-9 20:57:26
不能用,编译路径错误。* ?2 D' ^5 l1 S" V+ E# W+ c
"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 E  h  Q% P: `, x& i3 F' O3 ahttps://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 手机版