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

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

[复制链接]
cuyebiren 发布时间:2018-1-24 13:29
个人感觉,做嵌入式,底层就是datasheet,顶层就是数理逻辑7 F4 r2 o( T  G, \! i
不管什么芯片,当我们遇到问题时,通过查阅datasheet上官网基本上都能找到解决方法。然而,这些基本都是英文。所以,英文好对做研发是有很大益处的。不过好在有翻译工具,如:有道(我就是用有道划的)# E( T! K: d; o) R3 l
C语言虽然没有class,但有struct。我们可以多用struct。ST库和ucos中就包含许多struct。
! c4 ^6 t1 w# v, B程序结构多用状态机或switch。个人感觉,状态机看起来很有条理,很清晰,后期修改维护也方便。% b8 G4 n) G- P* E; J
鄙人浅见,请大家不吝赐教。
8 c; Q, Y; I% S  q. Y9 i1 O' K进入正题:UART以DMA方式接收和发送的函数调用顺序, U: U* a  }5 Q- H- }
循环模式接收: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)/ K0 \+ U( b* H' ~$ L! c
当然这当中还会调用传输 Half 中断,这里就不讨论了。
7 u$ c' I# e, }3 Z2 y8 t6 {' F0 Z" o- ?2 w, H  Z9 ?5 k
正常模式发送: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). X! h6 D. h" }8 ?  d% ]

+ h) O: p* e" G# N' ~5 t这里只分析 HAL_UART_Receive_DMA 的源码,其它的大家自己分析:
& W, p# t7 P8 B% f  G6 A$ B/ WHAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
7 B5 `5 n! ]) m/ J! QUART以DMA方式接收指定长度数据到指定缓冲区4 N: p* y& z8 Z* D* b* U1 n9 Z3 q
{
6 h- b) f" L, Q  m7 g/ {1 |  uint32_t *tmp;
+ }$ O" j- H3 Q& Q
1 x, L! [  J# o! l7 \  /* Check that a Rx process is not already ongoing */
" m- q( ], f. B8 N  if(huart->RxState == HAL_UART_STATE_READY)   检查huart->RxState是否处于 接收空闲 状态。当这一状态标志非READY时,会跳过DMA接收参数设置,直接返回HAL_BUSY其它的UART接收函数也会检查这个状态,所以,哪个先调用就执行哪个。
: Q" _1 Y! u2 O; q* z1 |6 H  {
2 B% `- h8 K7 H% l8 ]- @' M    if((pData == NULL) || (Size == 0U))
5 I& e, v. e' F& n7 W2 @0 ^: j    {' p3 B3 t1 C8 o! @
      return HAL_ERROR;
7 ?  K7 h3 _. b, f* o    }" F0 d( [- D4 Q% n
  Z+ l2 @( X, d; V8 o7 h
    /* Process Locked */- C3 G# w( l/ x; f% E) T
    __HAL_LOCK(huart);0 G- V5 Y" I& J7 u# m! C& p$ t: y- E
8 ?' K+ L  ]2 Z+ \
    huart->pRxBuffPtr = pData;; r9 ^/ W7 c/ p0 y3 p
    huart->RxXferSize = Size;
1 A! |+ g( x8 o" g$ |, o
2 W4 v/ e$ _9 Y- H, N- S    huart->ErrorCode = HAL_UART_ERROR_NONE;
- C: c( y5 @8 y0 Q# P7 H; m    huart->RxState = HAL_UART_STATE_BUSY_RX;" K5 V9 E! z* W' D4 I( U
* }3 U- x- L$ R1 y1 Q$ S
    /* Set the UART DMA transfer complete callback */
# Y1 ?7 z8 A$ n' ?    huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; 设置DMA传输完成的回调函数。当DMA以循环方式传输时会调用UART接收完成中断的回调函数;以Normal方式传输时会关闭UART的DMA通道,并使能UART传输完成中断,触发UART传输完成中断,设置huart->RxStateREADY,并调用( L! p# ?2 O' T# ~9 f2 }5 f/ q4 `
UART接收完成中断的回调函数。所以,
不管DMA按循环或正常模式传输,到最后都会调用UART接收完成中断的回调函数
: K) j, ?, T' @* v7 g    /* Set the UART DMA Half transfer complete callback */3 y" I% `! [  y) T
    huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;+ S9 M8 y. [9 ~( P

+ c# k  |6 c+ H( V- `% {% x  k    /* Set the DMA error callback */2 H$ R9 g7 Q, y
    huart->hdmarx->XferErrorCallback = UART_DMAError;2 Z9 a1 \; i7 V; s% t' p* i
! n+ Y# e7 C7 h4 Y  {4 f. ?
    /* Set the DMA abort callback */
9 R9 R7 S9 @: `9 p: g4 n* k    huart->hdmarx->XferAbortCallback = NULL;5 Q2 I/ d& ]2 ^) S/ @2 \. r4 U' V

) h/ H1 |4 b' E) T    /* Enable the DMA channel */" T9 i) z0 e/ `7 \
    tmp = (uint32_t*)&pData;
6 K  N/ E: J, k& |/ d9 u    HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);以中断方式打开DMA传输。所以CubeMx中DMA中断默认是打开的切不可更改  o- H8 y( m( a8 r: s

/ ^( g. o. F( Y1 H    /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */6 F* V- A7 k1 j# Z$ Y
    __HAL_UART_CLEAR_OREFLAG(huart);" d# a' \! T& [1 K; m1 [0 d9 a" d3 F( ?
$ o2 ^1 f: x! s6 e
    /* Process Unlocked */$ W5 {0 X3 V8 Y* l6 h
    __HAL_UNLOCK(huart);
1 }, u# t2 H& }# l$ B* M/ V$ D
' x; L, ?9 x$ n$ A$ [    /* Enable the UART Parity Error Interrupt */
' _' v9 c+ Z3 a0 M; ^    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);8 Q2 z9 O  h0 ~% K
6 a3 H5 m; }- ]
    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
8 V: M* R1 L8 q0 W) ~& O    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
2 _3 q- @9 n- b6 v8 t; V
) ?( F- Q+ y+ m( C    /* Enable the DMA transfer for the receiver request by setting the DMAR bit
* k" b% b1 R' a    in the UART CR3 register */
0 b( I: Z: `3 N) {    SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);2 w; u: |' a' z7 o0 z2 I3 {7 t

0 \- ]. }0 o; Q    return HAL_OK;$ w' c4 `9 W2 |. i; d) _; \
  }, m: q  h% g7 x( \) |
  else$ }  v4 |! ^. G! O
  {' b6 Q9 U, v$ M' E0 f- C
    return HAL_BUSY;. Y' `4 [6 n. E% [. T7 Z
  }
. x$ D6 x- o7 r7 f4 f% K& E. V}, }) A  `+ J" S6 t
下面上我做的测试:一、Cube配置
7 n7 n( e2 J+ L, _" m9 o1、DMA接收要用 循环模式,只需调用一次接收函数即可,重复调用也只有第一次调用有效。若是 正常模式,需要在每次接收完成后重复调用。
: g! f& U: `8 S. u2、DMA发送要用 正常模式,需在每次发送时重复调用。还要打开UART中断,否则即使重复调用DMA发送函数也只有第一次发送有效,其余不会执行(UART发送状态一直处于 BUSY)。若是 循环模式,则会连续不断地发送,不会停止。
9 A5 C9 l6 K" P: x6 X以上两点大家可以自己改改测试。/ \- y4 D4 w, a
2.png
$ U( P! Z2 [8 _" m 3.png
6 Q$ l! F, N4 {: q. z 1.png
& j1 \9 L/ `2 @. F1 `3 [
, A: d3 ?* G0 Z% T! r& B二、Keil例程编写* v) h' D8 y( i1 v5 e* Q* g
4.png ( O" s$ G! h5 v! \" H. A' {
6.png
& E8 d6 K  [5 b0 K% u7 ^/ a' a 5.png 2 p% H9 _3 [' v8 C% @: z# U! M$ Q

' G7 I6 a- I& C9 E' T( Z3 K三、测试4 B. \5 t9 x) G5 J7 R6 X+ a9 j0 L
可以看到,发送数 = 接收数。大家可以做更大数据的短间隔长时间测试,看看会不会出错。5 ~9 Y. t+ G! Z/ X1 [/ o
7.png 0 D( @2 K8 T, k" K& f1 D; H8 u" `1 b

' o" Z. B+ y, B8 ~2 q7 W9 s9 |" x最后,奉上测试工程: uart_dma_test.rar (3.7 MB, 下载次数: 2111)

评分

参与人数 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库无影响,如果以后串口出现锁死的话,再想想我说的话,或许有点帮助。串口发送前检测下发送状态标志,串口中断函数中,先暂停下发送通道,处理数据,再恢复发送通道。本贴的测试代码还是基于一收一发模式。0 _# q8 B% O6 l' X+ }1 H9 w

% f$ {" ]( D! W# O4 R2 Z* F% H
  L2 l# J( m( V* p
xzrsh 回答时间:2019-8-9 20:57:26
不能用,编译路径错误。
  Q* s5 n$ A$ `( w"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月技术原创- s6 F* `$ J" q
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 手机版