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

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

[复制链接]
cuyebiren 发布时间:2018-1-24 13:29
个人感觉,做嵌入式,底层就是datasheet,顶层就是数理逻辑2 j9 }# A" @: e! U. ^  S) b
不管什么芯片,当我们遇到问题时,通过查阅datasheet上官网基本上都能找到解决方法。然而,这些基本都是英文。所以,英文好对做研发是有很大益处的。不过好在有翻译工具,如:有道(我就是用有道划的)
! b; H4 i7 m4 d: t( b9 {6 t+ l: QC语言虽然没有class,但有struct。我们可以多用struct。ST库和ucos中就包含许多struct。
* N, t2 q, T6 C; ?) D程序结构多用状态机或switch。个人感觉,状态机看起来很有条理,很清晰,后期修改维护也方便。
  z2 W& a1 x7 ^$ O3 G鄙人浅见,请大家不吝赐教。
' w: g% I% l! R3 E2 V5 J$ V进入正题:UART以DMA方式接收和发送的函数调用顺序0 z/ K. R+ a8 ]: x2 \+ Q% X3 j
循环模式接收: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). |! @% D# B( k: v2 n0 y2 w
当然这当中还会调用传输 Half 中断,这里就不讨论了。
" L* S2 [, {& H" G6 b! S, m" |( [+ h! k3 p9 n$ ]
正常模式发送: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)
4 T. f% ~8 t" L- m& k( z9 \& d
9 h2 a! O. {! j! S: w: J这里只分析 HAL_UART_Receive_DMA 的源码,其它的大家自己分析:- w! r" ^: P/ b# ?
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)/ Z' c4 ?" B( _7 w! [
UART以DMA方式接收指定长度数据到指定缓冲区
5 U$ b) F; w' K{3 G+ M9 R. c6 W0 I
  uint32_t *tmp;& F- C5 O& a# Z1 c& n( m. {
# v: a# f( ~1 `
  /* Check that a Rx process is not already ongoing */) p: F9 y6 z) b  i: G% ]
  if(huart->RxState == HAL_UART_STATE_READY)   检查huart->RxState是否处于 接收空闲 状态。当这一状态标志非READY时,会跳过DMA接收参数设置,直接返回HAL_BUSY其它的UART接收函数也会检查这个状态,所以,哪个先调用就执行哪个。
8 k8 C8 _: Q) d) T% w  j) B% _  {7 i+ M, F; I3 X. Z$ ]) e/ k3 s
    if((pData == NULL) || (Size == 0U))
; x: c) J2 o' T* A    {% d! W( d: A" i. Z% k
      return HAL_ERROR;* I! ?* L7 ?4 t: z9 [
    }
- E7 _& Y& v2 z) T# T7 F+ Y3 }, r0 q/ z- w6 L1 \7 x
    /* Process Locked */
6 |5 l1 Z1 {5 l1 M    __HAL_LOCK(huart);. e4 F$ G; {  [& i5 J' m5 @( t7 i
, o6 r( Z- z7 a3 k9 W: k# H
    huart->pRxBuffPtr = pData;6 C, _; P! f' E( x3 e" A6 [* C
    huart->RxXferSize = Size;) D  T2 Q: w  t5 n# `  Z  ]
+ ]" B6 a# r. |1 L4 ^
    huart->ErrorCode = HAL_UART_ERROR_NONE;6 f5 y: I% q# L
    huart->RxState = HAL_UART_STATE_BUSY_RX;
* Q7 z% Z+ ]9 e6 s& u
, I, `, v! ^' i. x, G    /* Set the UART DMA transfer complete callback */3 ^4 q& N7 `" W* T0 T6 p' ^
    huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; 设置DMA传输完成的回调函数。当DMA以循环方式传输时会调用UART接收完成中断的回调函数;以Normal方式传输时会关闭UART的DMA通道,并使能UART传输完成中断,触发UART传输完成中断,设置huart->RxStateREADY,并调用
) Y) k2 H8 O1 V3 BUART接收完成中断的回调函数。所以,
不管DMA按循环或正常模式传输,到最后都会调用UART接收完成中断的回调函数
( R2 H7 z" Y; t! a! }+ u    /* Set the UART DMA Half transfer complete callback */
, u# }" Q: j( q7 g2 J$ t* F    huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;
3 n* x  @% R" I: k% a
8 X) T% S( W+ v9 X( @2 I    /* Set the DMA error callback */' R: C/ V2 P6 K" q2 e  Q+ v
    huart->hdmarx->XferErrorCallback = UART_DMAError;8 k5 {0 m2 U* E" J9 S& v

3 x+ K, n5 d" X- F    /* Set the DMA abort callback */
' n, q' i; I8 `5 G" p8 w6 F* V    huart->hdmarx->XferAbortCallback = NULL;9 Z7 m7 Q1 g* C9 x, d: i9 E
2 Y+ z+ j1 h; R% Q3 O. J4 F
    /* Enable the DMA channel */
; c" F4 F% G4 j" }+ |    tmp = (uint32_t*)&pData;. K+ _# |* w* C' ^. R4 e; A( y5 C
    HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);以中断方式打开DMA传输。所以CubeMx中DMA中断默认是打开的切不可更改1 a: A/ O1 g  y8 U) ~# p
# U; `6 ~; l5 H
    /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */: t# c9 U( A8 b7 ]$ |$ t: ?
    __HAL_UART_CLEAR_OREFLAG(huart);
9 _( p# X& I  E8 U" u) T- {! z3 E& W
    /* Process Unlocked */. e, V6 l/ }) E9 u5 ?: ~
    __HAL_UNLOCK(huart);
. j6 b' c, d. Z% J8 U. n( L$ Q9 @8 u- }2 ^, }7 N0 R* u
    /* Enable the UART Parity Error Interrupt */
. W2 e* c# }/ E) E/ ^0 x    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);8 P% w3 ]$ [7 X

: C9 ~7 J" |% s8 G    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */6 o8 q# j' V0 C: j7 N) a( J
    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
9 s5 u( G" V- _( u) E* @  S9 y" ^; q% b
    /* Enable the DMA transfer for the receiver request by setting the DMAR bit 1 b7 U, Q. I. F
    in the UART CR3 register */! U6 Q, s' {8 f, R1 Y
    SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);' L- m* s  w3 C' @9 b8 ]4 N! t! ~/ r

) N# U  D- f( d: t    return HAL_OK;
% b* N. C5 H4 e, {: U6 s/ L  }
. [0 E0 y6 n8 c* z, k1 |) E  else1 _4 r9 ]0 m& V) S1 b$ V) H7 Q" U
  {7 C* t0 H2 j/ j9 n* |
    return HAL_BUSY;: C& o$ t" O6 a+ _1 h6 L# y+ X
  }, b$ @  [/ Q% V( e, ~& L4 U
}
9 n" R/ P% S: X( g下面上我做的测试:一、Cube配置
* q8 i& O# ?9 p6 x1、DMA接收要用 循环模式,只需调用一次接收函数即可,重复调用也只有第一次调用有效。若是 正常模式,需要在每次接收完成后重复调用。7 l0 N, E" F, K) L" @8 R
2、DMA发送要用 正常模式,需在每次发送时重复调用。还要打开UART中断,否则即使重复调用DMA发送函数也只有第一次发送有效,其余不会执行(UART发送状态一直处于 BUSY)。若是 循环模式,则会连续不断地发送,不会停止。
9 Y4 @. _6 n* {以上两点大家可以自己改改测试。
; T, t! m0 f; @: c" F 2.png
! I1 L9 V) z( r  ]( \) \ 3.png ! Q9 z' r) ~' @* U. E3 K$ d6 O
1.png
9 N7 K" T* H0 c+ u5 m: T0 \' k9 ?/ G& x
二、Keil例程编写
, N5 b7 ?' K2 [% D" E$ C) o  b 4.png
$ J) r: `3 C1 @. M3 q; J+ Y 6.png : b0 w2 K" U8 A" L. O( f
5.png ) R0 g) t, o9 \0 o

3 y; D0 E5 L/ G- W7 C0 O" v三、测试
- J+ g# E6 T& ^' ~" F' B可以看到,发送数 = 接收数。大家可以做更大数据的短间隔长时间测试,看看会不会出错。
5 \" g1 b2 Z, j8 O2 t1 r% r6 @0 n. a 7.png . i$ E) Z. N2 f' N/ C  I
2 @4 m% s2 R# H8 j+ X9 F2 N- g
最后,奉上测试工程: 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库无影响,如果以后串口出现锁死的话,再想想我说的话,或许有点帮助。串口发送前检测下发送状态标志,串口中断函数中,先暂停下发送通道,处理数据,再恢复发送通道。本贴的测试代码还是基于一收一发模式。/ g' M  \" G! F! ~

7 E+ ~9 |/ @' G2 b% k$ |+ m; [* ]; g( u: {% f% G3 k
xzrsh 回答时间:2019-8-9 20:57:26
不能用,编译路径错误。- b2 w7 T5 m: G* c4 {
"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月技术原创- R' g5 E1 e! Q8 q0 e
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 手机版