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

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

[复制链接]
cuyebiren 发布时间:2018-1-24 13:29
个人感觉,做嵌入式,底层就是datasheet,顶层就是数理逻辑。
/ ?% u, ~+ n% @6 G7 y不管什么芯片,当我们遇到问题时,通过查阅datasheet或上官网基本上都能找到解决方法。然而,这些基本都是英文。所以,英文好对做研发是有很大益处的。不过好在有翻译工具,如:有道(我就是用有道划的)  {% F2 V. E7 J& `+ A+ z
C语言虽然没有class,但有struct。我们可以多用struct。ST库和ucos中就包含许多struct。4 E2 I4 K" f; t, N, I% ~' x7 V& h9 p
程序结构多用状态机或switch。个人感觉,状态机看起来很有条理,很清晰,后期修改维护也方便。
' E* q& x6 {& c3 w9 w; t鄙人浅见,请大家不吝赐教。
8 Q$ `9 L, u6 Y进入正题:UART以DMA方式接收和发送的函数调用顺序:
3 g" J6 q4 d7 B4 u) p+ ^5 O循环模式接收: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)
0 ^+ b8 Q2 g6 K& b. p4 U当然这当中还会调用传输 Half 中断,这里就不讨论了。
+ e3 u% J" U$ I7 m) V1 {
! W2 j  m% a- D1 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)
9 d0 ^- H5 A- {# O9 y1 A
# E( c* i5 R' R  _7 G# @* \/ y这里只分析 HAL_UART_Receive_DMA 的源码,其它的大家自己分析:
4 e- q: x1 z% E' `& pHAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)% U/ a+ V$ G5 N! c
UART以DMA方式接收指定长度数据到指定缓冲区; T- e8 ^' o: P) J6 T/ _( D
{7 h& e* Q' O2 ?% T4 v
  uint32_t *tmp;
; i  A' i8 ?4 d2 A# Z' M" E* ]
  /* Check that a Rx process is not already ongoing */- A0 \9 B0 W1 M& \/ a
  if(huart->RxState == HAL_UART_STATE_READY)   检查huart->RxState是否处于 接收空闲 状态。当这一状态标志非READY时,会跳过DMA接收参数设置,直接返回HAL_BUSY。其它的UART接收函数也会检查这个状态,所以,哪个先调用就执行哪个。
/ c4 t' M. u% T' w" y6 x5 O& G5 R9 c$ r  {3 M! D% u& s& l: j
    if((pData == NULL) || (Size == 0U))4 D" @- Q% f$ ~" P2 V) Z# I0 i
    {
' B% K! K1 s( i1 d      return HAL_ERROR;
6 \, b' }, z; e2 {. o& V$ _0 b    }( c& W  a3 c% H

% [7 X7 f6 T% Y( p    /* Process Locked */
( Q% \# H! \! }0 ~7 R    __HAL_LOCK(huart);( T" s5 I2 _/ t1 e* [- ]8 I
  ^1 H' I  C; D' K) p
    huart->pRxBuffPtr = pData;
$ p3 w7 w6 h0 \    huart->RxXferSize = Size;$ `4 X( @8 Q# I

9 W$ B1 x1 y; Q/ ^4 s    huart->ErrorCode = HAL_UART_ERROR_NONE;
$ d3 c- X" F5 e; W    huart->RxState = HAL_UART_STATE_BUSY_RX;
, [  P5 y/ Y# O" B/ G6 [& r7 |6 Y
    /* Set the UART DMA transfer complete callback */. E7 j3 s; x1 G( t3 K
    huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; 设置DMA传输完成的回调函数。当DMA以循环方式传输时会调用UART接收完成中断的回调函数;以Normal方式传输时会关闭UART的DMA通道,并使能UART传输完成中断,触发UART传输完成中断,设置huart->RxState为READY,并调用0 P7 V' r. k1 A
UART接收完成中断的回调函数。所以,
不管DMA按循环或正常模式传输,到最后都会调用UART接收完成中断的回调函数
$ p9 m9 m; d  w. x    /* Set the UART DMA Half transfer complete callback */! R6 |! t. E% f) b
    huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;
+ R8 ^" ~% P4 u1 U& \( [
8 G# ~4 Z+ W; m7 D) I- n2 f( @    /* Set the DMA error callback */
+ b9 Q$ e7 z2 Z3 e    huart->hdmarx->XferErrorCallback = UART_DMAError;( {2 p" d" U6 ?$ `# k

& k+ c6 c- X0 F% P# o0 g0 ?+ P    /* Set the DMA abort callback *// b+ G# Z  W, _; i3 \) k! @6 X
    huart->hdmarx->XferAbortCallback = NULL;# I5 i( O6 ]  U0 V

+ o. Q$ ]% Y3 C/ I, ?    /* Enable the DMA channel */  C; O' |) Q. L  ?) Y. S8 q
    tmp = (uint32_t*)&pData;
0 {& k3 H4 S5 s) \. o) l- q    HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);以中断方式打开DMA传输。所以CubeMx中DMA中断默认是打开的切不可更改* k/ G) X; M8 y9 N$ g3 U! `
  J* R, i6 I% u  r
    /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */
+ ^2 x- x8 E9 V3 h( G% O; |5 K    __HAL_UART_CLEAR_OREFLAG(huart);
' y( o$ F# k, q, J6 F7 X8 j1 x0 n$ h" n6 E: W
    /* Process Unlocked */
+ M1 O8 Q5 Q) a- D: H    __HAL_UNLOCK(huart);& ^( m$ P+ F3 @7 V5 k
7 m& P/ W: s9 H2 K+ S
    /* Enable the UART Parity Error Interrupt */
6 Z% }5 E7 o. O  y7 C    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);9 z- ], ~8 w7 m, u7 a: I

: h; \8 n# H- x$ {: _# n    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
' S+ Y# u8 ~4 g5 ?5 {3 \/ Z# W, [    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);5 Y- [4 Y5 ~% y! t& D. P0 x# x

7 y  ?0 F' o, H5 y1 a$ q    /* Enable the DMA transfer for the receiver request by setting the DMAR bit : J' o- q/ @1 C2 Q
    in the UART CR3 register */
* I& O% X( q" }7 \    SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);& U8 c, [. q; V* k1 j4 l. j. ]7 d" G
3 Z/ g/ s& T' P8 g- X
    return HAL_OK;
2 J5 H' G4 S* j  }
. B& G9 N( n& v+ `  else
' |* P. G5 m6 k+ w$ }$ ]$ K9 v  {
) @9 x9 q. p* R1 a& M    return HAL_BUSY;
& m% L0 h! [/ Q; w  }1 }6 K8 H2 q: @6 o3 U+ K; a" |
}: c3 b7 [. \8 g' p. X# k: x
下面上我做的测试:一、Cube配置% r- i" r- x( b, \: U
1、DMA接收要用 循环模式,只需调用一次接收函数即可,重复调用也只有第一次调用有效。若是 正常模式,需要在每次接收完成后重复调用。1 T. L+ x+ m: u
2、DMA发送要用 正常模式,需在每次发送时重复调用。还要打开UART中断,否则即使重复调用DMA发送函数也只有第一次发送有效,其余不会执行(UART发送状态一直处于 BUSY)。若是 循环模式,则会连续不断地发送,不会停止。) P  n3 N0 ~8 B$ Q
以上两点大家可以自己改改测试。
$ Q# K( U% v1 u' j# x6 d 2.png
, M6 `% @  e# K 3.png
1 L/ o' H- C% H1 f4 S- T 1.png 4 H3 S4 K7 |# u5 L  v( m
- M/ V! v/ y: ^/ q. H8 V' j; M  w
二、Keil例程编写" G1 K0 Z) f* F
4.png
; r3 M" {: s6 G# k. W* ?( k 6.png 9 P/ p0 ?" z% t+ I
5.png
3 H5 x* q9 @/ y( z' |0 E* U9 @- r7 v5 B' B, P4 v( I
三、测试
4 e" N) E/ E& b# n* N可以看到,发送数 = 接收数。大家可以做更大数据的短间隔长时间测试,看看会不会出错。
8 X4 R0 U) v; o7 ?7 F 7.png
' K7 g8 Z, t" g# l
7 k3 I7 y3 P7 y最后,奉上测试工程: 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库无影响,如果以后串口出现锁死的话,再想想我说的话,或许有点帮助。串口发送前检测下发送状态标志,串口中断函数中,先暂停下发送通道,处理数据,再恢复发送通道。本贴的测试代码还是基于一收一发模式。" j4 b" ?" i. w5 h" R4 h. u. g

" e6 D9 p% q- T- ?
) v3 m3 W7 C- K$ U
xzrsh 回答时间:2019-8-9 20:57:26
不能用,编译路径错误。8 [( Y" f" U! 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月技术原创
8 S8 z+ Q! I& V3 uhttps://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 手机版