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

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

[复制链接]
cuyebiren 发布时间:2018-1-24 13:29
个人感觉,做嵌入式,底层就是datasheet,顶层就是数理逻辑。# z2 G2 b8 ~  m0 G9 T& n
不管什么芯片,当我们遇到问题时,通过查阅datasheet或上官网基本上都能找到解决方法。然而,这些基本都是英文。所以,英文好对做研发是有很大益处的。不过好在有翻译工具,如:有道(我就是用有道划的)
  q! g! E1 n9 V6 hC语言虽然没有class,但有struct。我们可以多用struct。ST库和ucos中就包含许多struct。! V+ ^" D& O) I
程序结构多用状态机或switch。个人感觉,状态机看起来很有条理,很清晰,后期修改维护也方便。
7 y5 [/ ^3 `# `9 B鄙人浅见,请大家不吝赐教。
' A0 b# s& F+ E7 _& }8 B进入正题:UART以DMA方式接收和发送的函数调用顺序:
! Z) `) g' m7 @: }. d! r% g/ N循环模式接收: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)
6 j! Q( l3 M/ H* P0 |; Q, j当然这当中还会调用传输 Half 中断,这里就不讨论了。
" b, q& j! Y1 X2 L9 K( g% r% w. c! I" U5 U( ?" N/ r
正常模式发送: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)7 H* g9 U. i% Q; P$ v/ e2 V8 P

/ S5 P1 \4 l$ G) J& E; S这里只分析 HAL_UART_Receive_DMA 的源码,其它的大家自己分析:5 }* m8 B* `& K& Q
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)1 {# R' I3 q4 P; `
UART以DMA方式接收指定长度数据到指定缓冲区; M1 h$ S, e: m' R: b$ v9 f3 j* D# I
{; P" F# Q* z, k/ u% o
  uint32_t *tmp;2 S5 w% m, E4 b4 m4 \! e5 q' v
/ N* X0 j5 f+ n
  /* Check that a Rx process is not already ongoing */
' S6 l$ H: P8 e) E) y( L$ }  r  if(huart->RxState == HAL_UART_STATE_READY)   检查huart->RxState是否处于 接收空闲 状态。当这一状态标志非READY时,会跳过DMA接收参数设置,直接返回HAL_BUSY。其它的UART接收函数也会检查这个状态,所以,哪个先调用就执行哪个。# C6 L& H/ s/ R# _9 n8 K
  {
: f4 f( h& V3 W' W; G5 a    if((pData == NULL) || (Size == 0U))
/ W, H: p0 F$ e* P2 t    {
3 V6 Y- ^6 _& V, M      return HAL_ERROR;
% l. Z. ^  z5 a0 W! Q: M7 J5 Q$ W    }# X8 w2 e" W9 b5 c4 L$ T

3 `* ~8 K4 s! x9 A    /* Process Locked */1 I+ e4 h& z; {3 l3 n; w, K
    __HAL_LOCK(huart);
1 x4 J+ i7 w* A* E5 O0 i& D8 o" f9 H; ^3 o2 h6 G
    huart->pRxBuffPtr = pData;
$ E; c1 e* _! d    huart->RxXferSize = Size;4 o! s& n) }$ G8 d8 J
5 x# D, b0 O% C' N0 p3 m; A! F
    huart->ErrorCode = HAL_UART_ERROR_NONE;, v( I# z( q6 _6 G. E
    huart->RxState = HAL_UART_STATE_BUSY_RX;/ U1 _9 W# B7 }$ Y' Z
3 D% C. |7 u% o% r. ]
    /* Set the UART DMA transfer complete callback */
1 ^0 _/ X: s; m; _    huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; 设置DMA传输完成的回调函数。当DMA以循环方式传输时会调用UART接收完成中断的回调函数;以Normal方式传输时会关闭UART的DMA通道,并使能UART传输完成中断,触发UART传输完成中断,设置huart->RxState为READY,并调用4 z- U; T9 L; ~- j
UART接收完成中断的回调函数。所以,
不管DMA按循环或正常模式传输,到最后都会调用UART接收完成中断的回调函数
' D: s' e9 d$ H* b4 a    /* Set the UART DMA Half transfer complete callback */  \# o" y( o+ C0 v4 ]
    huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;
/ x! N- `/ u3 h4 g/ K4 g( r: t# [% H6 R& `: Y6 }" M  }( R' d; G
    /* Set the DMA error callback */( X) `) s7 g# U+ C0 ^9 o8 M
    huart->hdmarx->XferErrorCallback = UART_DMAError;, G7 X8 J# |1 G* P: i/ _

7 k* J, |6 |- \/ h    /* Set the DMA abort callback *// n( t+ d+ j- ?; \& O- h. R
    huart->hdmarx->XferAbortCallback = NULL;
: W1 ]( X7 W4 T2 ?/ {6 m' K! k. g8 E$ q3 T3 M
    /* Enable the DMA channel */
) n9 {# K3 M- S" h% i) b8 }' j    tmp = (uint32_t*)&pData;2 `+ Z. [6 b' i! _$ j9 H
    HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);以中断方式打开DMA传输。所以CubeMx中DMA中断默认是打开的切不可更改
6 \+ J3 V3 r& L1 w7 }
  A3 d: n  V$ y9 o1 D& \    /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */+ L6 L9 O% M5 o! y; f$ p* y, }
    __HAL_UART_CLEAR_OREFLAG(huart);
. I1 e6 x  j: V: Q6 ^  b. q; W& K. G6 G
6 O5 W& \. _% _) Q: ^" D; ^) v% {& J    /* Process Unlocked */
" ~+ L" ^6 A# j7 `+ K$ [* b    __HAL_UNLOCK(huart);
8 b+ f2 d3 @( u5 P# A5 M( I( f( `% g" n/ A( G; g' u/ q6 U; H
    /* Enable the UART Parity Error Interrupt */) P  Y3 }# [& _" m! j
    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);: ^" P! B& h- Q

3 i, Y, ~, {% s9 X, n/ k    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */- f: Y9 R0 r% p
    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);0 i3 Q5 h6 d8 s  \6 Y9 O
/ b: K' A( V* F. x6 L
    /* Enable the DMA transfer for the receiver request by setting the DMAR bit
7 K" E  Z) y1 W3 Q, }0 j0 s0 r    in the UART CR3 register */
+ d0 G% d0 O% }4 k: K9 g) @  \    SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);, x- k+ {, A4 l

" u" s% e9 M) |  c7 N; f    return HAL_OK;
3 t* O/ B8 k+ D4 i! E2 C& B- x  }- ]! N* Q$ K' T- u" g3 E. T
  else
9 U8 s$ Y( ?( _, ~4 I2 r  {
1 `- S. v! x4 U" C: S9 b" O' j    return HAL_BUSY;* X6 z2 @+ \2 d5 ]0 ]8 Y0 b
  }
* E' B1 g) ?% m+ a% e9 L}
4 o: X# T5 L( N7 c4 O下面上我做的测试:一、Cube配置
. h3 S' `/ V: U7 l. X6 g7 I1、DMA接收要用 循环模式,只需调用一次接收函数即可,重复调用也只有第一次调用有效。若是 正常模式,需要在每次接收完成后重复调用。; Q3 r& M( m; n( Q2 A
2、DMA发送要用 正常模式,需在每次发送时重复调用。还要打开UART中断,否则即使重复调用DMA发送函数也只有第一次发送有效,其余不会执行(UART发送状态一直处于 BUSY)。若是 循环模式,则会连续不断地发送,不会停止。
  c3 ]  `" `8 u1 V6 T以上两点大家可以自己改改测试。" A6 F) |  P  S2 p) S: _- ^
2.png
, \* ~8 c  {* c; @- {/ i: w6 z 3.png , \7 H4 |- I4 m3 i# P- l+ [
1.png
! ]- [: W$ ?7 W& [7 O5 B
+ X" s/ ?8 T3 B; [7 g2 Z二、Keil例程编写' [4 c7 a5 U- d$ t8 z' a# q, {0 D
4.png
! H/ L2 ?4 Y& J/ O# I# R 6.png
. {. {7 C3 m7 q. _ 5.png $ b! q2 s. Q1 `
& {3 o8 R0 \3 ]; p
三、测试& T( x# p1 h: [/ p5 a/ `
可以看到,发送数 = 接收数。大家可以做更大数据的短间隔长时间测试,看看会不会出错。
" Q3 n0 Y' m6 s+ O: l: y 7.png
+ J1 _6 k. H& h* C
* h: D' v( w  I$ e最后,奉上测试工程: 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库无影响,如果以后串口出现锁死的话,再想想我说的话,或许有点帮助。串口发送前检测下发送状态标志,串口中断函数中,先暂停下发送通道,处理数据,再恢复发送通道。本贴的测试代码还是基于一收一发模式。
. p0 o( C* n2 O) h8 ?' {' `; e, y! ]) T; |2 [6 O
( Y' A; {. b1 G1 @& G! k# M
xzrsh 回答时间:2019-8-9 20:57:26
不能用,编译路径错误。
0 o- D6 V/ D; O! m& M' o"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月技术原创
% H* M7 A$ d- W" S& @* P) s0 yhttps://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 手机版