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

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

[复制链接]
cuyebiren 发布时间:2018-1-24 13:29
个人感觉,做嵌入式,底层就是datasheet,顶层就是数理逻辑
) S! E) f0 x7 b6 K5 G" D# l不管什么芯片,当我们遇到问题时,通过查阅datasheet上官网基本上都能找到解决方法。然而,这些基本都是英文。所以,英文好对做研发是有很大益处的。不过好在有翻译工具,如:有道(我就是用有道划的); Z2 i) g- l3 f& }4 W- }0 a, e
C语言虽然没有class,但有struct。我们可以多用struct。ST库和ucos中就包含许多struct。8 F4 k3 N+ x  }- X
程序结构多用状态机或switch。个人感觉,状态机看起来很有条理,很清晰,后期修改维护也方便。
7 T, x. Z; @) K$ C  s鄙人浅见,请大家不吝赐教。! `7 M7 v+ x4 s' ^4 B
进入正题:UART以DMA方式接收和发送的函数调用顺序2 ~: _3 ^$ m, A' 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)* P4 Y5 Q9 B; {% X6 E2 y# J  D. `& j
当然这当中还会调用传输 Half 中断,这里就不讨论了。0 v5 V9 ?# @) R  I! b4 Y
. [0 m( l) L9 @' B* m: Y, G& f
正常模式发送: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)# e+ o  \# D# w" v* U

/ @; M7 V+ }0 n' ^7 Q! p这里只分析 HAL_UART_Receive_DMA 的源码,其它的大家自己分析:; B0 M5 r/ u$ G  Y
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
% ]) W' ?8 F8 t, {% s7 i) HUART以DMA方式接收指定长度数据到指定缓冲区1 ^, I3 p* I; u3 e
{
& D, F- [0 q" k& e8 w  uint32_t *tmp;- L5 b8 c/ U: a6 o! I2 u  S; e; {
: S2 {9 q7 G: M2 J# ~
  /* Check that a Rx process is not already ongoing */
4 `, e* u6 d7 @& U0 W! I1 L4 A  if(huart->RxState == HAL_UART_STATE_READY)   检查huart->RxState是否处于 接收空闲 状态。当这一状态标志非READY时,会跳过DMA接收参数设置,直接返回HAL_BUSY其它的UART接收函数也会检查这个状态,所以,哪个先调用就执行哪个。
0 U  M% O6 r1 U& n/ E* u' s  X  {2 }; G1 ]: p, W0 Q, W
    if((pData == NULL) || (Size == 0U))
6 Z7 h3 c0 ~! z9 @: ]1 w    {' Y/ l2 t" o/ _5 z: _
      return HAL_ERROR;
5 I; B- c; ?- J! n    }# I: `1 ]: D- m( U5 c, Q: p
/ V/ x4 o5 ]/ R1 g' b. |7 v+ w4 S
    /* Process Locked */
2 i( I  ?9 `; c    __HAL_LOCK(huart);
% S: Z  o9 t  T! k- J2 X4 c2 E8 [6 r6 ]% w0 f) R: s
    huart->pRxBuffPtr = pData;
4 A* |/ b9 A% s  _; G* H" R    huart->RxXferSize = Size;
3 C3 w. O. _, {/ x  k" a) h$ K4 i& ~# Y- {% s
    huart->ErrorCode = HAL_UART_ERROR_NONE;
& n4 b: E' ~" p3 i6 a2 _* D    huart->RxState = HAL_UART_STATE_BUSY_RX;* M  w1 S3 Z- p8 ^6 I, B
7 E, _* D' s( _2 e
    /* Set the UART DMA transfer complete callback */
  x/ f3 F( j" }6 @/ m( A" z    huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; 设置DMA传输完成的回调函数。当DMA以循环方式传输时会调用UART接收完成中断的回调函数;以Normal方式传输时会关闭UART的DMA通道,并使能UART传输完成中断,触发UART传输完成中断,设置huart->RxStateREADY,并调用) K" n3 n5 F. N; u: E8 h  q
UART接收完成中断的回调函数。所以,
不管DMA按循环或正常模式传输,到最后都会调用UART接收完成中断的回调函数
7 J1 b. N/ k& q8 P% R2 M% t1 ^    /* Set the UART DMA Half transfer complete callback */+ ^' {, u' s0 a5 q3 _+ u5 S
    huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;  Q' U% W( m2 p/ y5 a1 P# j

6 E. P" p: `' a# O+ H$ \. H    /* Set the DMA error callback */, p+ L0 W/ ?) d7 G
    huart->hdmarx->XferErrorCallback = UART_DMAError;7 n+ P2 Z  r0 C$ f& H* G
* j" \7 N1 e+ L
    /* Set the DMA abort callback */
/ v; h- D$ l. y; l$ g2 A7 }    huart->hdmarx->XferAbortCallback = NULL;+ z0 Y, ?- H2 F6 E6 i7 j

2 \/ Q, |6 H3 ~3 C: |    /* Enable the DMA channel */( m" w/ r2 ~. {% x1 O2 s
    tmp = (uint32_t*)&pData;
: g" O6 ^* X2 ^3 |5 m' C    HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);以中断方式打开DMA传输。所以CubeMx中DMA中断默认是打开的切不可更改. q" ^4 Z0 `, z+ I. t8 X& F5 y

; c" K! c5 j2 Y/ B' k% v* a    /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */! X: I4 r1 k, v
    __HAL_UART_CLEAR_OREFLAG(huart);
0 \9 Y% R; J' O" f2 B
0 L- i& |: e! m1 @& L7 [    /* Process Unlocked */! L, L6 S0 E/ U1 I9 }- w
    __HAL_UNLOCK(huart);
: a8 Y8 m0 A+ e5 W* A& n8 L- H1 L6 M6 M( t# W
    /* Enable the UART Parity Error Interrupt */1 Y6 _( O+ Z0 u. X0 c5 g& c- R& j
    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);/ J/ K. w/ q; [6 P( N8 W4 a6 G5 e( G
, n2 S* C# v& H
    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
6 _! a' C' \! Q    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
+ ]) Z8 l2 n4 \0 n1 ~: ~0 o$ K' l3 O7 v
    /* Enable the DMA transfer for the receiver request by setting the DMAR bit
8 H! ?$ n5 e; p3 v( ?, U3 t( }    in the UART CR3 register */
1 z$ d  q  }* |7 D& |0 P0 N    SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);5 _8 f8 j# U- H- i1 ]7 a! A6 n" b3 K

' d5 [; c7 [4 T" q0 ?+ d8 N, K    return HAL_OK;! R; l, s& M1 C* E
  }
/ y+ o0 C4 l6 }3 q: S; n  else
! }3 D- a" O  c: v- d, \& g$ u3 O  {
% |, f  ]' y4 h/ A    return HAL_BUSY;
6 V2 q9 l8 e0 x1 K7 n/ J- ^) v( x  }! S& L9 `9 ~  l: B& @* T
}
' [- H  P$ T, j5 e下面上我做的测试:一、Cube配置; d: @8 s' K0 w% G# Q' C6 A: j
1、DMA接收要用 循环模式,只需调用一次接收函数即可,重复调用也只有第一次调用有效。若是 正常模式,需要在每次接收完成后重复调用。
  o9 Y# g3 l& o. k; ~/ v2、DMA发送要用 正常模式,需在每次发送时重复调用。还要打开UART中断,否则即使重复调用DMA发送函数也只有第一次发送有效,其余不会执行(UART发送状态一直处于 BUSY)。若是 循环模式,则会连续不断地发送,不会停止。% W: N; \2 Y! a: _( y/ W
以上两点大家可以自己改改测试。$ }% o2 w! [1 h; M
2.png
' _  R& i0 z2 A7 m6 Q* h" w 3.png % L' L" C; E" f( J- l! {5 y3 }7 t8 T
1.png ! N9 c- @: [' r( @8 u

. J, P$ T; ^- R+ @二、Keil例程编写% b' G7 v) z0 l, x
4.png / m* Q8 D. n$ B, y; c) O, e
6.png
; p  Z( K  p6 m% t% @5 | 5.png 1 w. O9 N. i0 a
$ a) V% {$ r1 }6 Y! Q+ D
三、测试/ Q" L) Q0 F" t' u0 K+ [
可以看到,发送数 = 接收数。大家可以做更大数据的短间隔长时间测试,看看会不会出错。) Q' W1 B/ S: i0 w6 I/ a+ r
7.png ) S/ i- u( }# e
/ I! e% N# S5 s2 S) s% I
最后,奉上测试工程: 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库无影响,如果以后串口出现锁死的话,再想想我说的话,或许有点帮助。串口发送前检测下发送状态标志,串口中断函数中,先暂停下发送通道,处理数据,再恢复发送通道。本贴的测试代码还是基于一收一发模式。
  D2 C& Q' a2 r  Z* o, v* ]5 U' B) a" ]! Q. u. _
  a5 _7 Q7 \% L+ k
xzrsh 回答时间:2019-8-9 20:57:26
不能用,编译路径错误。1 _# G/ b9 j2 E) W  Q
"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月技术原创
4 u& z9 V- E9 {" t& h8 `8 Thttps://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 手机版