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

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

[复制链接]
cuyebiren 发布时间:2018-1-24 13:29
个人感觉,做嵌入式,底层就是datasheet,顶层就是数理逻辑% f2 C8 j8 V# `' w6 l# N
不管什么芯片,当我们遇到问题时,通过查阅datasheet上官网基本上都能找到解决方法。然而,这些基本都是英文。所以,英文好对做研发是有很大益处的。不过好在有翻译工具,如:有道(我就是用有道划的)5 Y% G8 [, H4 i+ z2 B2 e1 Q
C语言虽然没有class,但有struct。我们可以多用struct。ST库和ucos中就包含许多struct。
. F. t. d/ l) N6 ~, K3 g% }程序结构多用状态机或switch。个人感觉,状态机看起来很有条理,很清晰,后期修改维护也方便。
0 f2 I: X) ?$ ]& B/ }% ]鄙人浅见,请大家不吝赐教。
/ Z5 Z& k: g+ r" {! p4 ~. D进入正题:UART以DMA方式接收和发送的函数调用顺序
7 T! O- T9 J+ X6 `+ X: E循环模式接收: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)7 w! Z0 A7 h! E. M" {# |
当然这当中还会调用传输 Half 中断,这里就不讨论了。
' N/ k9 M! P/ @& J' C  u8 F( [8 x2 R. e- h% u; D
正常模式发送: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)- j8 `5 S4 M( S+ O
4 C  L( \5 Z% _4 g2 Y
这里只分析 HAL_UART_Receive_DMA 的源码,其它的大家自己分析:. F$ _( M# a0 h) ]$ p% Y3 P
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
1 }/ e( [1 l3 o; MUART以DMA方式接收指定长度数据到指定缓冲区
7 A5 g/ r& J; f, N$ {{! J6 h' i8 k: P6 {1 \- F# d# e
  uint32_t *tmp;
# x! z- t1 l7 [: L4 t
3 x5 ^$ x- N  a2 U& |* U4 k: g. x  /* Check that a Rx process is not already ongoing */+ W/ X; d4 l1 G' t" a
  if(huart->RxState == HAL_UART_STATE_READY)   检查huart->RxState是否处于 接收空闲 状态。当这一状态标志非READY时,会跳过DMA接收参数设置,直接返回HAL_BUSY其它的UART接收函数也会检查这个状态,所以,哪个先调用就执行哪个。
4 l7 K6 v; t% D9 z) G% n  {8 d- n, O; \+ D4 D8 t. Y7 O
    if((pData == NULL) || (Size == 0U))
' l( g4 V* |% Q$ r! a- k: b6 m: ^0 |9 K    {5 I4 R5 e/ T4 t! _+ s2 s
      return HAL_ERROR;  \' A( h9 B. f6 F
    }0 s5 m. O: _0 u1 ^/ p6 V1 }
8 x  B8 Z' R; d% \  h& O6 O/ q
    /* Process Locked */# v, F3 [7 `0 |8 u- R# p- l
    __HAL_LOCK(huart);
7 j# ^: w1 O: d0 G  s) A. `# X# z2 ^/ R; k4 S
    huart->pRxBuffPtr = pData;  f4 K- j; s& s) ^. m
    huart->RxXferSize = Size;
6 c  n% N7 L& K) |) u. F
- U& x4 l# X9 X6 I. v    huart->ErrorCode = HAL_UART_ERROR_NONE;
5 j1 ^& c8 o5 \    huart->RxState = HAL_UART_STATE_BUSY_RX;
) b" d3 T4 ^  e2 p( q" U7 ~3 x# v. F; o# Z. E
    /* Set the UART DMA transfer complete callback */
% |8 d! z1 |9 y1 k* Q  u/ U6 c    huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; 设置DMA传输完成的回调函数。当DMA以循环方式传输时会调用UART接收完成中断的回调函数;以Normal方式传输时会关闭UART的DMA通道,并使能UART传输完成中断,触发UART传输完成中断,设置huart->RxStateREADY,并调用
; v/ ^3 _* X2 y* Q' T0 }UART接收完成中断的回调函数。所以,
不管DMA按循环或正常模式传输,到最后都会调用UART接收完成中断的回调函数
+ J  A. t$ @. J7 J0 t    /* Set the UART DMA Half transfer complete callback */' s# ^/ |2 \+ B3 h
    huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;
9 K- S/ o; q" C& }9 Y( Y, T' O, r0 A
    /* Set the DMA error callback */
; W7 }: ?+ I. d0 ~/ d    huart->hdmarx->XferErrorCallback = UART_DMAError;# j/ p! r* R) y/ A' }# e

8 b% K1 t4 Y) ]" O9 h( [! b    /* Set the DMA abort callback */
8 j3 M& c! A1 W; m% C5 r6 ^    huart->hdmarx->XferAbortCallback = NULL;) [; R  w+ B/ ~% c9 V7 M

, r  {! I9 y% y5 `2 }4 }% X    /* Enable the DMA channel */4 [2 p9 A, B! B' ~, h1 i
    tmp = (uint32_t*)&pData;3 Z3 a8 j* e9 M6 H( Z) a4 _, a' S
    HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);以中断方式打开DMA传输。所以CubeMx中DMA中断默认是打开的切不可更改. S8 n3 ]9 L8 ~, X3 W* ?
$ p; m; }2 M+ e
    /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */4 o6 N( h6 v& Y  V' e+ O4 R0 F. q
    __HAL_UART_CLEAR_OREFLAG(huart);
& O. d  Z+ m( a( Q% r  y3 d; {) C' w& ~; b9 F7 t4 O9 u6 J7 I: {; M) U
    /* Process Unlocked */. O) {7 B# D! U3 l; V! r
    __HAL_UNLOCK(huart);
& t4 r$ J0 `: y; I4 T' K9 v
9 ?% P# I6 S4 x2 f, i$ f    /* Enable the UART Parity Error Interrupt */
: W% q9 z" a, Y* P$ D& L8 }    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);
& |$ q; l" ?4 i" Q$ \2 ~$ t2 }" ?
. }" {. S! E0 p8 h. R9 G    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
" a1 `6 W+ }+ r" T    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);5 [# Q4 ^" F# w  m7 G7 |
6 x! G# z! F( R+ q+ l9 H
    /* Enable the DMA transfer for the receiver request by setting the DMAR bit
" d( |4 C% G# L9 l    in the UART CR3 register */
( x3 E  t% d5 v5 f    SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);, j4 Q6 I8 X4 w

. _1 @& Q* Z% G2 L, N5 D5 n- Q    return HAL_OK;+ _2 {( k- w+ G6 b: H; N
  }
' R1 D7 o: s5 e8 P, f8 Y" v+ Q% d  else
6 L0 O: u" {3 d0 k0 w- d4 P4 N+ R  {" G  O  r6 e: h6 Y/ j2 N
    return HAL_BUSY;
5 C/ t- ^! B  ?& A. w  }& F. o" `6 x* v+ v2 ^
}
0 Y& D0 L/ o; H6 v. ~# B" J下面上我做的测试:一、Cube配置
- [% A4 F0 a% ^0 f) ^1、DMA接收要用 循环模式,只需调用一次接收函数即可,重复调用也只有第一次调用有效。若是 正常模式,需要在每次接收完成后重复调用。
1 k( N/ ]  E$ N- o; n" b! ~2、DMA发送要用 正常模式,需在每次发送时重复调用。还要打开UART中断,否则即使重复调用DMA发送函数也只有第一次发送有效,其余不会执行(UART发送状态一直处于 BUSY)。若是 循环模式,则会连续不断地发送,不会停止。
* Z8 z3 r2 z6 x" O以上两点大家可以自己改改测试。
  a8 c2 p3 z+ S- Q3 C! q/ U 2.png
7 d# H' h5 P* l2 K. Z$ l% [ 3.png
; \4 Y0 K! o8 v- M& S. p 1.png
6 K. `( G: f: ], B# w+ x3 @4 n) h8 E2 g: ?/ }3 ?( o% N' q5 l+ w
二、Keil例程编写& A' \2 c  W8 O0 G" ?4 X
4.png
, H$ |) L0 I& m; s" w) f6 H, F 6.png . w) \/ G" }0 c; [, o$ @$ l
5.png 9 H0 ?/ W" F3 Y5 C
. u' G% g9 U) H3 n* a% a& A
三、测试
, g# C' X! N: @* ^  Z; V$ \可以看到,发送数 = 接收数。大家可以做更大数据的短间隔长时间测试,看看会不会出错。9 z( z' T! C1 @6 h9 Q
7.png
) [1 c* z6 g3 R* z& C0 m9 Y" `7 c* c1 M2 |
最后,奉上测试工程: 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库无影响,如果以后串口出现锁死的话,再想想我说的话,或许有点帮助。串口发送前检测下发送状态标志,串口中断函数中,先暂停下发送通道,处理数据,再恢复发送通道。本贴的测试代码还是基于一收一发模式。
; `8 F) ^9 E& u' P+ j/ _  h- s; `( r, |9 s* c5 z. s
. t+ p2 c1 v- j9 W4 [
xzrsh 回答时间:2019-8-9 20:57:26
不能用,编译路径错误。. q5 X. y8 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月技术原创$ }; }+ ~" o4 ]5 u% y! A
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 手机版