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

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

[复制链接]
cuyebiren 发布时间:2018-1-24 13:29
个人感觉,做嵌入式,底层就是datasheet,顶层就是数理逻辑
7 F9 k% _. l, x不管什么芯片,当我们遇到问题时,通过查阅datasheet上官网基本上都能找到解决方法。然而,这些基本都是英文。所以,英文好对做研发是有很大益处的。不过好在有翻译工具,如:有道(我就是用有道划的)
2 I8 I, [0 g' t" m" EC语言虽然没有class,但有struct。我们可以多用struct。ST库和ucos中就包含许多struct。
; B# i" T) T; S2 T$ d* I程序结构多用状态机或switch。个人感觉,状态机看起来很有条理,很清晰,后期修改维护也方便。
. U6 U6 ~  {5 b) ]; P; L鄙人浅见,请大家不吝赐教。2 o8 n! R  y+ Y& ?$ N' h6 x$ p0 s
进入正题:UART以DMA方式接收和发送的函数调用顺序
4 \8 L$ b" G: C& o6 q3 \, 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)
  u; o5 s$ M/ M5 E3 ^当然这当中还会调用传输 Half 中断,这里就不讨论了。6 w, E* e, K" B

+ d" k1 ?) ]7 a正常模式发送: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)- D! f3 Z* q5 l% P2 c
$ u7 _- G' `1 S# s0 r/ d# q
这里只分析 HAL_UART_Receive_DMA 的源码,其它的大家自己分析:
; Y- a5 j% F( N) K; q7 cHAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
$ O. y' }5 `% S& I' U3 @6 O7 t4 CUART以DMA方式接收指定长度数据到指定缓冲区
* D5 F& O& U- Z5 Z{
7 ~' X8 j1 b5 c3 {  g3 f; M# d9 c  uint32_t *tmp;
7 ?. w8 N1 v. Q) |* G4 _* {7 H. I3 O& h' p
  /* Check that a Rx process is not already ongoing */" p" y  F& I/ Q
  if(huart->RxState == HAL_UART_STATE_READY)   检查huart->RxState是否处于 接收空闲 状态。当这一状态标志非READY时,会跳过DMA接收参数设置,直接返回HAL_BUSY其它的UART接收函数也会检查这个状态,所以,哪个先调用就执行哪个。
5 M+ I& ?$ I! p" ]: p; S/ N  {2 V  a/ D7 d. _! c
    if((pData == NULL) || (Size == 0U))
# o1 {0 o1 s2 C5 A    {
$ v8 E7 @8 g5 X0 m: N7 ]      return HAL_ERROR;
1 z  b/ r4 s4 L    }& W4 i: u3 N' }& c6 L# H
# ^) q$ B5 P8 ?2 f- T# D3 o
    /* Process Locked */' F: u4 m6 I" o' A. J
    __HAL_LOCK(huart);
% ]! G  R* j! |' Y! a1 L
0 ?: @9 Y5 x9 T    huart->pRxBuffPtr = pData;
# ~% u7 C5 ^5 m9 [  _6 U; U3 E+ k    huart->RxXferSize = Size;
4 K4 ]7 X  Z; \4 U# D
& I! |5 t( R2 |% N& L! P* B    huart->ErrorCode = HAL_UART_ERROR_NONE;
# e0 q+ g2 o2 o/ _    huart->RxState = HAL_UART_STATE_BUSY_RX;1 ]& S( M: u: Z

/ I2 _( V8 V' X  G2 G    /* Set the UART DMA transfer complete callback */
1 K5 M: N  R% i) [/ J7 I- m( u6 @    huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; 设置DMA传输完成的回调函数。当DMA以循环方式传输时会调用UART接收完成中断的回调函数;以Normal方式传输时会关闭UART的DMA通道,并使能UART传输完成中断,触发UART传输完成中断,设置huart->RxStateREADY,并调用
( Y* S5 j$ U! h- V) ~* Q% \UART接收完成中断的回调函数。所以,
不管DMA按循环或正常模式传输,到最后都会调用UART接收完成中断的回调函数- S# _4 K/ F0 }  f  d* j: J7 P; R, |
    /* Set the UART DMA Half transfer complete callback */+ F/ L; N0 I, \; \
    huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;  o6 ~$ d4 Z/ e5 o

6 p- {2 [1 p3 C8 k: q1 b+ z; k: X    /* Set the DMA error callback */
# E3 p* v- s0 F- e. v+ ^* `6 @6 ~    huart->hdmarx->XferErrorCallback = UART_DMAError;
/ l/ Z0 W  U8 n& ^* @( `/ o, z! a# }
9 M' `  U" \$ j+ L    /* Set the DMA abort callback */8 i- D8 s. L; I+ E
    huart->hdmarx->XferAbortCallback = NULL;
1 m4 o. J9 x( m' B0 b9 \/ c9 t  l+ I5 O
    /* Enable the DMA channel */
9 g, Z/ f3 W! m$ ^" `) ?    tmp = (uint32_t*)&pData;
5 A3 z; G/ [# O0 m- G. M8 `3 s    HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);以中断方式打开DMA传输。所以CubeMx中DMA中断默认是打开的切不可更改: }0 y; Q3 a& Z" z, N
' [) y4 K: N* K/ l  }3 ^7 u$ U) X) s
    /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */' U2 ?: r1 a/ S- s, |% k
    __HAL_UART_CLEAR_OREFLAG(huart);: ]: u4 q, G, f' N8 G  D

7 a6 r8 r0 o) q# o- }8 Y    /* Process Unlocked */
( {  Y! C8 d6 A4 A7 M# H    __HAL_UNLOCK(huart);: M4 M0 X: w, D8 F
6 ]1 x- p- q, }1 ^" @
    /* Enable the UART Parity Error Interrupt */
4 Z; A9 Z( Z+ U' v9 o7 |    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);
7 P/ g  p3 l3 Z6 `( t$ c1 h0 ?- X2 h0 C" H+ E, E+ Q* j
    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */1 z' b6 T& ^9 o4 q0 @" d* H
    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);1 \+ F  h7 @9 R, U

% Z- [* t) R. R( u* Z    /* Enable the DMA transfer for the receiver request by setting the DMAR bit
8 r& [* `& \: I7 R    in the UART CR3 register */
  d/ s: X7 q  q) [4 o% C6 Q    SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);
8 q* t% e; u% k' q
3 N( v# K  p; z3 p0 V* c    return HAL_OK;
* ?" z5 a, b  M, R, J# g  }" O; C  a0 R8 j9 f2 Z1 Y2 j
  else
- F3 Y+ l, {% A  {
- h4 {; S, m! ~8 L    return HAL_BUSY;
  w9 ]  k( x0 t0 i  }
0 \+ A4 Z7 [% b3 ]2 l& R$ j* [}
" Z4 I6 I$ S# z1 u8 F下面上我做的测试:一、Cube配置
: ^/ P3 P4 l" o1、DMA接收要用 循环模式,只需调用一次接收函数即可,重复调用也只有第一次调用有效。若是 正常模式,需要在每次接收完成后重复调用。
7 }+ B5 ]0 _+ D$ ?9 r. ^0 s2、DMA发送要用 正常模式,需在每次发送时重复调用。还要打开UART中断,否则即使重复调用DMA发送函数也只有第一次发送有效,其余不会执行(UART发送状态一直处于 BUSY)。若是 循环模式,则会连续不断地发送,不会停止。
! _+ C. A2 Q5 a以上两点大家可以自己改改测试。& s- ?4 `: L( h( Q4 _+ c
2.png
6 ]5 m6 `/ _5 p# d: u5 F9 g 3.png 8 i+ a& Z! G9 B( l
1.png 2 s2 a- u; \, M
% ^! m" a3 ^, j6 k2 e& n8 O
二、Keil例程编写
  `5 `- s" |& M) a' y 4.png 1 v( s. P( j% Q% O! j1 e/ [0 ~" y
6.png
  R5 _  x/ ?7 C8 ]+ J( T" W% J 5.png 7 }1 O7 L! i$ a/ a. e

: D/ Z  T& f5 x. Y三、测试
6 b( L  P# U) u5 J" Z- D可以看到,发送数 = 接收数。大家可以做更大数据的短间隔长时间测试,看看会不会出错。) {+ A; p, A+ P% n  n- _  J1 }" _
7.png
6 w8 o4 U1 F2 |3 `7 e( I
" _, D- H+ j; k) F9 h' |$ {最后,奉上测试工程: 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库无影响,如果以后串口出现锁死的话,再想想我说的话,或许有点帮助。串口发送前检测下发送状态标志,串口中断函数中,先暂停下发送通道,处理数据,再恢复发送通道。本贴的测试代码还是基于一收一发模式。
- S, z% t( {# [& T1 H
# Z9 Y! ]6 B, `' k+ ]' y
0 {8 w' y7 W9 t
xzrsh 回答时间:2019-8-9 20:57:26
不能用,编译路径错误。5 @, V4 \% Q  p
"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月技术原创
: z! x. w8 z! S8 [$ I' t$ 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管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版