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

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

[复制链接]
cuyebiren 发布时间:2018-1-24 13:29
个人感觉,做嵌入式,底层就是datasheet,顶层就是数理逻辑: {  T# h( B0 b7 t8 y3 q
不管什么芯片,当我们遇到问题时,通过查阅datasheet上官网基本上都能找到解决方法。然而,这些基本都是英文。所以,英文好对做研发是有很大益处的。不过好在有翻译工具,如:有道(我就是用有道划的)8 M3 C# {: }7 _% C
C语言虽然没有class,但有struct。我们可以多用struct。ST库和ucos中就包含许多struct。/ u1 G  }% H- a5 F
程序结构多用状态机或switch。个人感觉,状态机看起来很有条理,很清晰,后期修改维护也方便。
5 ~/ J) Z- m3 ]. W0 l% c鄙人浅见,请大家不吝赐教。3 I6 V  h6 _: L
进入正题:UART以DMA方式接收和发送的函数调用顺序
9 ~: ^# j* M' f  y0 z' |% I循环模式接收: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)
7 ]0 ~. ~2 H: W/ O) r! ]2 k  B当然这当中还会调用传输 Half 中断,这里就不讨论了。
4 U' I" a, N' K& ]) n+ z$ `8 E7 q5 ?4 B: J' w1 V) q- 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)
- T( M1 k2 |' L' {4 [% E2 P8 E) W  U, G' v, A0 W% E
这里只分析 HAL_UART_Receive_DMA 的源码,其它的大家自己分析:
. G2 q' d) c3 g9 R+ AHAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size). c) h/ u( O1 B( t2 g
UART以DMA方式接收指定长度数据到指定缓冲区
0 y' n, j* M; m5 v' a{
6 K# ?7 J. n) i- V  uint32_t *tmp;
) j# y2 B5 C- u! V, a
, j2 G9 T2 q& C+ F% ~  /* Check that a Rx process is not already ongoing */
& Y0 O+ E8 `! |! ]  if(huart->RxState == HAL_UART_STATE_READY)   检查huart->RxState是否处于 接收空闲 状态。当这一状态标志非READY时,会跳过DMA接收参数设置,直接返回HAL_BUSY其它的UART接收函数也会检查这个状态,所以,哪个先调用就执行哪个。* X& D: Q1 y8 e6 M
  {
, k3 j9 x7 {- Z' k: s& `  V  G( Z    if((pData == NULL) || (Size == 0U))
% F4 H  O) U; j: M7 x  v- M. w/ U    {
4 ?6 I7 M' p1 S4 M3 C' O% n      return HAL_ERROR;( h. x2 a+ a) h, A% D6 K
    }& W$ I9 ]3 |9 h

4 }: ^# g+ W4 U  A1 V    /* Process Locked *// k6 S6 }& |; r
    __HAL_LOCK(huart);
+ Q+ J/ ]. q  K( v8 K( Z6 ?7 G% R9 R
    huart->pRxBuffPtr = pData;
& c" W3 e5 d/ r0 W* [2 T4 N3 s    huart->RxXferSize = Size;
8 C$ E0 ^2 y- }4 ]7 e: G$ M2 G) n* @
    huart->ErrorCode = HAL_UART_ERROR_NONE;: P* m1 |3 }% N% {- n
    huart->RxState = HAL_UART_STATE_BUSY_RX;
9 m% P! j$ Z4 ]' N. T; n6 V7 j& w! @+ E& v
    /* Set the UART DMA transfer complete callback */  \0 _! O+ z& l9 G! O
    huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; 设置DMA传输完成的回调函数。当DMA以循环方式传输时会调用UART接收完成中断的回调函数;以Normal方式传输时会关闭UART的DMA通道,并使能UART传输完成中断,触发UART传输完成中断,设置huart->RxStateREADY,并调用/ i; V( `; @( B& a' _
UART接收完成中断的回调函数。所以,
不管DMA按循环或正常模式传输,到最后都会调用UART接收完成中断的回调函数
( v! O4 L) p: A& r1 D; O) Q    /* Set the UART DMA Half transfer complete callback */' z( Y0 j- a& o
    huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;
' u7 Z/ Y+ l$ k; u' R5 L& J
+ v$ W  F) M; [. h/ r4 }0 o    /* Set the DMA error callback */5 G; A: y) `3 U/ T6 N) D2 Y- s9 k
    huart->hdmarx->XferErrorCallback = UART_DMAError;- w( e$ Y9 e5 j7 g- ]2 z% F5 c

0 x9 u# W% F( z7 `9 z    /* Set the DMA abort callback */- F6 c$ y, S+ y! U$ x
    huart->hdmarx->XferAbortCallback = NULL;1 f  f0 W+ X/ s& p3 w7 `

2 B! J5 V/ N0 j" t1 U+ I    /* Enable the DMA channel */6 W% j7 d! H* p) ?& M& r& y
    tmp = (uint32_t*)&pData;) R0 R4 w4 y8 G, X4 W5 h
    HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);以中断方式打开DMA传输。所以CubeMx中DMA中断默认是打开的切不可更改3 X6 s4 T+ a! ^/ k  V- F5 C; h

' Y; {, a  a1 g/ I7 _    /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */
1 m. o% j6 j# j0 O; b    __HAL_UART_CLEAR_OREFLAG(huart);
2 s6 _6 D! }; E8 z9 L0 a3 B/ D& u5 c/ s7 Y  }
    /* Process Unlocked *// t. e0 E6 m0 ?" p) p! ?$ |
    __HAL_UNLOCK(huart);
- M5 ^$ ]! j% V2 T6 T9 V4 V$ i* u+ p8 m/ }
    /* Enable the UART Parity Error Interrupt */
& I( _8 Y8 \8 G    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);
" G0 P. [0 ?- \$ q; h* u
! w3 S* k" |9 R! L    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
: B8 D  \; ^' B! r8 F, f! f    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);* \' R0 U# K7 t+ K1 ]2 W$ u% w' L
+ Q: f0 W+ J2 M
    /* Enable the DMA transfer for the receiver request by setting the DMAR bit
/ ^( d6 `1 q: V6 Z  w2 P: o    in the UART CR3 register */
/ U- ~! N4 t% G7 C" P    SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);: `7 V, D& \6 b$ a/ B

# j" C: M7 ?% w5 Z    return HAL_OK;
8 y5 G7 Z6 r: g0 E4 u- o  }
. L( o% k( m9 Q  else
  U" d" b2 F/ V, @  {$ k  s8 l/ F  O/ K- x
    return HAL_BUSY;2 R* b3 u) o* h# ~6 k$ C
  }) f2 @: H5 x8 [8 d$ J2 i
}
, l: S5 g  S3 f9 f0 J下面上我做的测试:一、Cube配置
  H3 S2 s' I& s' w* V# b1、DMA接收要用 循环模式,只需调用一次接收函数即可,重复调用也只有第一次调用有效。若是 正常模式,需要在每次接收完成后重复调用。
$ K7 r5 c4 K: y: C$ b2、DMA发送要用 正常模式,需在每次发送时重复调用。还要打开UART中断,否则即使重复调用DMA发送函数也只有第一次发送有效,其余不会执行(UART发送状态一直处于 BUSY)。若是 循环模式,则会连续不断地发送,不会停止。+ N; v9 V) }8 o+ s, {3 K; Q
以上两点大家可以自己改改测试。
# L. e6 Q: k& y+ S' w5 L7 C 2.png ; E2 P: ]+ B* T) h
3.png ! V! G/ S- S% @. s% Y
1.png ) \, ?: A1 M. f$ ?' m- `7 e

8 x; [$ m0 |9 ]4 k# [二、Keil例程编写, x/ L3 f7 I+ z: L2 q
4.png
9 g4 ?3 ?) p7 w8 k8 O# o 6.png
$ K7 Z2 @7 D( p6 f5 M 5.png 4 }  g8 c# f0 E5 i7 y9 [

$ K& S( H9 v+ u9 r7 T三、测试" O% Y8 \: z/ r1 A
可以看到,发送数 = 接收数。大家可以做更大数据的短间隔长时间测试,看看会不会出错。% |3 m+ k+ V2 u9 S: n1 [, a: Y9 W
7.png
# z, y8 |, c0 R  d( V6 f) v( D
, ]2 g6 F+ ?- J0 p最后,奉上测试工程: 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库无影响,如果以后串口出现锁死的话,再想想我说的话,或许有点帮助。串口发送前检测下发送状态标志,串口中断函数中,先暂停下发送通道,处理数据,再恢复发送通道。本贴的测试代码还是基于一收一发模式。
/ o/ j, v; o3 d2 @  V6 ^  ^0 D3 c, q( u  a: B
1 B0 g- \) ^) X, J- m
xzrsh 回答时间:2019-8-9 20:57:26
不能用,编译路径错误。
$ f& v) n9 Y& A4 n. C"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月技术原创  q# _4 g' c2 L" x9 m3 z% x$ J
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 手机版