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

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

[复制链接]
cuyebiren 发布时间:2018-1-24 13:29
个人感觉,做嵌入式,底层就是datasheet,顶层就是数理逻辑
2 L' K' _( R4 \3 `3 a不管什么芯片,当我们遇到问题时,通过查阅datasheet上官网基本上都能找到解决方法。然而,这些基本都是英文。所以,英文好对做研发是有很大益处的。不过好在有翻译工具,如:有道(我就是用有道划的)  ]4 C2 |1 P( R! X" d) C
C语言虽然没有class,但有struct。我们可以多用struct。ST库和ucos中就包含许多struct。
! ?- |; u3 T0 O" k( s程序结构多用状态机或switch。个人感觉,状态机看起来很有条理,很清晰,后期修改维护也方便。
0 Q. H* u7 w/ l2 g鄙人浅见,请大家不吝赐教。: L! D  C7 L" z6 l" M, {3 B
进入正题:UART以DMA方式接收和发送的函数调用顺序- c# G8 ^3 g1 A% @. c' C( r
循环模式接收: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): |3 j# X8 \/ b4 z, o* |4 B( O
当然这当中还会调用传输 Half 中断,这里就不讨论了。
  V8 ^8 J* m- s+ x- n0 |2 ?0 ]1 C5 Q3 V3 W
正常模式发送: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)
5 M- |# Z) q' G  ?$ n7 I& F$ @# q3 ~  [8 h) S/ Z; i9 h
这里只分析 HAL_UART_Receive_DMA 的源码,其它的大家自己分析:
6 L7 P/ V7 r* t! ]HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)$ h  j  y9 \7 \* i; W1 [3 q
UART以DMA方式接收指定长度数据到指定缓冲区
" }6 O3 d. O) d2 |{
' E. H$ D3 D0 o" }1 D0 Q# K  uint32_t *tmp;  C, G" O$ V- l& P. Z- V- B, C
  B0 [1 H- Q1 ]
  /* Check that a Rx process is not already ongoing */
. d9 F2 f3 h; x6 J' i: p+ Q5 ^  if(huart->RxState == HAL_UART_STATE_READY)   检查huart->RxState是否处于 接收空闲 状态。当这一状态标志非READY时,会跳过DMA接收参数设置,直接返回HAL_BUSY其它的UART接收函数也会检查这个状态,所以,哪个先调用就执行哪个。1 t- o- _) @) S) n  Q: G! g4 k( l
  {
5 f# C* y- ?# b' X    if((pData == NULL) || (Size == 0U))% y7 \7 Q, p0 F, U) \0 {
    {
9 ]+ A) @0 V9 z# \9 F" B5 D      return HAL_ERROR;
: ~7 f  d0 T3 O& Q    }6 R8 e5 Q& ]9 \) ?
: L% R& ]! T$ M$ x7 R, u
    /* Process Locked */
+ T( P  g$ f0 e$ Z7 L. g* W    __HAL_LOCK(huart);
0 g3 g7 P3 n( j2 P  L& d! Z. P  \0 c& t; V2 q2 S, E
    huart->pRxBuffPtr = pData;
: W6 W2 }8 c8 Z$ ?8 @    huart->RxXferSize = Size;" S6 B7 k6 Y/ x. P2 p0 p3 ?

, K3 R: n1 [6 Z& t. Y# v3 v    huart->ErrorCode = HAL_UART_ERROR_NONE;
9 G4 L; S- S8 y5 x0 y4 Z0 b* D+ C4 t    huart->RxState = HAL_UART_STATE_BUSY_RX;& r( A. r! c9 ~( F

/ C; J/ y. V7 ^! O5 O5 K    /* Set the UART DMA transfer complete callback */( v, G2 h1 I. ^9 p. X
    huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; 设置DMA传输完成的回调函数。当DMA以循环方式传输时会调用UART接收完成中断的回调函数;以Normal方式传输时会关闭UART的DMA通道,并使能UART传输完成中断,触发UART传输完成中断,设置huart->RxStateREADY,并调用/ T4 b0 Q. t7 I
UART接收完成中断的回调函数。所以,
不管DMA按循环或正常模式传输,到最后都会调用UART接收完成中断的回调函数
) r3 Z) {; o: Z/ L    /* Set the UART DMA Half transfer complete callback */
% F5 J0 y, y0 J8 j  ~4 M    huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;
, D8 e, M2 j0 [" g8 n' P
* z. ~" t" ~& y! b) V    /* Set the DMA error callback */. O" o, U! Z  {+ {4 l
    huart->hdmarx->XferErrorCallback = UART_DMAError;' J* j; o' U" q

! ]1 D: h9 x  k1 o% |2 A    /* Set the DMA abort callback */
! x* e/ l2 l2 }7 f4 H: p" U1 _* B    huart->hdmarx->XferAbortCallback = NULL;
7 d4 g8 p6 f5 c+ Y, D5 x* Q
& [2 `& Z. v( s' i# s    /* Enable the DMA channel */
+ l) O2 S' B4 K3 ~    tmp = (uint32_t*)&pData;: z& S- N8 L1 {5 C
    HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);以中断方式打开DMA传输。所以CubeMx中DMA中断默认是打开的切不可更改
$ K5 E( K/ n* C2 D7 T
' a+ A# L+ G: u$ m7 x6 W    /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer *// c# r; g  N% S# V
    __HAL_UART_CLEAR_OREFLAG(huart);
5 n; O( G9 y' _& w; G  f8 F, u0 P
, z  g3 b  H' A' g# _2 j. L' c    /* Process Unlocked */
( _4 F: G2 N" T  z( `- b    __HAL_UNLOCK(huart);# K5 O% H0 x/ v8 B) u$ A8 C

. k; n$ B) h, f- G5 U: M: q# _    /* Enable the UART Parity Error Interrupt */% S0 z0 a- C: _* b
    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);
) G9 s. L+ d/ A
  j5 q4 G. P0 f1 O  W/ v    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */$ g* C" J2 `7 p' U
    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);0 o2 }" F" p5 Z4 H8 I
( d" S  U' T- U0 y4 v% ]
    /* Enable the DMA transfer for the receiver request by setting the DMAR bit / ?' Q# ]/ Q5 ?7 E/ H" p# ~
    in the UART CR3 register */
6 C4 N, ?- ?* T+ M: p* o    SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);  s! t" D* ^& x0 K; _

# }+ O% [8 j. S7 ?    return HAL_OK;. Z( v. t' I, c& T5 S
  }- d7 N( `5 h5 V' m/ n7 V& ?
  else: ]6 ~$ Z( p+ A
  {
$ ^9 o! S6 c1 y    return HAL_BUSY;) b( _7 b% l7 }: |" q1 P
  }8 c5 n, z) }: u. j. D/ ?& J9 r) Z
}: U8 ?- ?  Z8 m" Y
下面上我做的测试:一、Cube配置4 [% s1 h( z; w" j# T
1、DMA接收要用 循环模式,只需调用一次接收函数即可,重复调用也只有第一次调用有效。若是 正常模式,需要在每次接收完成后重复调用。( D  z' x9 U8 m- g0 V# [- |  \# s
2、DMA发送要用 正常模式,需在每次发送时重复调用。还要打开UART中断,否则即使重复调用DMA发送函数也只有第一次发送有效,其余不会执行(UART发送状态一直处于 BUSY)。若是 循环模式,则会连续不断地发送,不会停止。; j8 y9 k7 J  a2 P
以上两点大家可以自己改改测试。7 U2 G+ k9 H$ \" i' V/ m' S
2.png
9 W  r" h+ g6 u) h' L7 K" ~& B 3.png ; E4 l/ R4 Q' `/ G5 j9 G
1.png & M3 R4 d% f# \( g" N
7 a! d% k% b" l" c$ b
二、Keil例程编写
/ @" C/ E; f- V5 o1 I* `/ d4 v 4.png 3 I% V3 w6 ]3 O3 u" m# q
6.png 7 a+ |! N) j1 ]5 {7 z
5.png . R3 G4 ]6 f; |3 n9 v

. v4 v8 X% q6 A+ u三、测试/ J8 W: m' t( S" j: ^( y) i
可以看到,发送数 = 接收数。大家可以做更大数据的短间隔长时间测试,看看会不会出错。1 K4 s5 c# E$ J2 {5 A4 ?
7.png & h5 r; }1 {3 A) |2 d' A1 ^% I
, O$ N8 |7 h  p' E
最后,奉上测试工程: 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库无影响,如果以后串口出现锁死的话,再想想我说的话,或许有点帮助。串口发送前检测下发送状态标志,串口中断函数中,先暂停下发送通道,处理数据,再恢复发送通道。本贴的测试代码还是基于一收一发模式。# W* w8 L' C) a
  `$ A1 f9 }2 \! ?
7 ?0 P0 w6 [2 H1 t2 Y# F& H; ~- s; S
xzrsh 回答时间:2019-8-9 20:57:26
不能用,编译路径错误。; ]) S6 l. S* X, t" `2 Q1 M; R4 u
"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月技术原创% \% C, W1 I$ Y7 X7 g) P
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管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版