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

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

[复制链接]
cuyebiren 发布时间:2018-1-24 13:29
个人感觉,做嵌入式,底层就是datasheet,顶层就是数理逻辑
, p# f. _; W& A* |不管什么芯片,当我们遇到问题时,通过查阅datasheet上官网基本上都能找到解决方法。然而,这些基本都是英文。所以,英文好对做研发是有很大益处的。不过好在有翻译工具,如:有道(我就是用有道划的)
1 n) _4 m/ o, D) FC语言虽然没有class,但有struct。我们可以多用struct。ST库和ucos中就包含许多struct。
8 z  T; [! ]7 {9 D& R程序结构多用状态机或switch。个人感觉,状态机看起来很有条理,很清晰,后期修改维护也方便。
. c' g( d8 a. o8 n- A' o/ l鄙人浅见,请大家不吝赐教。4 W+ n9 h- F! `3 ~, O
进入正题:UART以DMA方式接收和发送的函数调用顺序
* F% M4 q7 k$ ~( w; F/ I: y循环模式接收: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)
) O( y9 v( G5 e) `, t) X4 Y当然这当中还会调用传输 Half 中断,这里就不讨论了。
: a5 {7 g' e% @( k: _8 O; d$ J1 Y% T3 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)1 `9 C$ D/ o& j
0 X4 Z: s1 P* J, o: r; U1 [
这里只分析 HAL_UART_Receive_DMA 的源码,其它的大家自己分析:7 l2 U  m. A5 k. R' X1 ^4 H
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
8 M' K1 `8 f* N+ R; C2 f+ B4 SUART以DMA方式接收指定长度数据到指定缓冲区
: R0 ]/ }% C6 W# t# N6 F5 E) s{
. o; }1 X0 h* W. ?1 a  uint32_t *tmp;. w/ o& {! K2 o

- m- [- O; h' e  /* Check that a Rx process is not already ongoing *// P3 e& k1 w3 D
  if(huart->RxState == HAL_UART_STATE_READY)   检查huart->RxState是否处于 接收空闲 状态。当这一状态标志非READY时,会跳过DMA接收参数设置,直接返回HAL_BUSY其它的UART接收函数也会检查这个状态,所以,哪个先调用就执行哪个。- d6 C2 I5 R" Z  x  c
  {
; K" p0 X$ j8 U+ U" w( Y    if((pData == NULL) || (Size == 0U))8 c5 P. g  F" E! q' A3 W5 q
    {
0 f) q0 L5 i- C9 p: P      return HAL_ERROR;
  W2 c) J- ]$ i) }- H" R/ X+ a    }& ^5 ~! J8 O+ ]

6 ~" o  Z" n! [) k% C( B' [2 F    /* Process Locked */
+ _4 g) V4 w9 Z1 ^2 F    __HAL_LOCK(huart);
8 Q1 x0 x- s( ?* W# b. @- l: `, ]9 \; y2 W4 |
    huart->pRxBuffPtr = pData;
9 a+ S8 y, n0 U& i    huart->RxXferSize = Size;
6 I, u. v) s0 ?
0 ?& ^- {) `4 y% [    huart->ErrorCode = HAL_UART_ERROR_NONE;8 K1 `" R$ c+ T3 @+ O& D
    huart->RxState = HAL_UART_STATE_BUSY_RX;6 C* a7 k* S& b" c# G

1 K6 T5 R! Z/ |/ e( @    /* Set the UART DMA transfer complete callback */6 p/ w9 c2 e" d4 l% |0 b3 a( n7 _1 p
    huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; 设置DMA传输完成的回调函数。当DMA以循环方式传输时会调用UART接收完成中断的回调函数;以Normal方式传输时会关闭UART的DMA通道,并使能UART传输完成中断,触发UART传输完成中断,设置huart->RxStateREADY,并调用, @. [1 O8 C* K
UART接收完成中断的回调函数。所以,
不管DMA按循环或正常模式传输,到最后都会调用UART接收完成中断的回调函数5 t8 l4 s; P6 u; r0 s& D. \
    /* Set the UART DMA Half transfer complete callback */
" x% r0 j4 {! j5 v; x. r  }    huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;9 S" f" B% s$ A
7 `/ _) j- X7 P
    /* Set the DMA error callback */
1 u: u3 B8 j5 c    huart->hdmarx->XferErrorCallback = UART_DMAError;
5 c- ^% k; W- J/ L- ^2 W# E+ r3 N3 u" q; D
    /* Set the DMA abort callback */6 \( m# z- o" B4 {* M# `/ P$ p
    huart->hdmarx->XferAbortCallback = NULL;' L5 ~7 {( I- I; K7 J$ q
7 u. S: s5 Z  x9 P
    /* Enable the DMA channel */
9 ~' x% I9 m/ L- w    tmp = (uint32_t*)&pData;% Y! m) |) P" f. n" l  U# ]
    HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);以中断方式打开DMA传输。所以CubeMx中DMA中断默认是打开的切不可更改# T! S; Z2 T2 N+ l  V0 x+ }) ~

& S/ f9 a1 j+ z) q  Z2 P$ |    /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */) W$ t, O  m! u+ F) C5 x2 W. \
    __HAL_UART_CLEAR_OREFLAG(huart);
. [/ F& b  `5 J; \1 Z6 Z) G" }5 S9 s- |$ t2 l
    /* Process Unlocked */
! L1 D$ ~4 ^- ~- h9 v    __HAL_UNLOCK(huart);' v, O- M# I3 I8 ]4 r

, d- c, N1 y) v) A! ^    /* Enable the UART Parity Error Interrupt */
6 B2 o# C& z/ {/ q4 G    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);/ E3 A4 q# n$ f6 T2 i

' E! D: Q$ W7 g8 [    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
( h7 a4 |! R7 j7 Q    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
! L1 o  u0 q1 @3 d
9 m, Q- N% Q% D3 G1 l- M+ @    /* Enable the DMA transfer for the receiver request by setting the DMAR bit
* _0 v) F4 Q  B    in the UART CR3 register */
2 D" v% `' E" u  `3 w% I    SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);$ p9 j: l. V6 z/ c8 r& Q
$ _9 r3 a. N. ]. z# F7 X0 @9 V9 ~' N
    return HAL_OK;; q7 \+ q0 Y( n
  }
9 x2 R6 F  r! G1 L  else4 z. R- S, D6 ~1 a1 ^, J; O$ S
  {
# V5 j8 h' u: _2 u6 K- G/ x    return HAL_BUSY;
' b, D% R. n8 O! w0 [  }) H6 `: `  T% c5 @0 u1 _
}7 f, @- C& b# n6 L
下面上我做的测试:一、Cube配置
* ?: K2 N. L7 ]; o5 T- @1、DMA接收要用 循环模式,只需调用一次接收函数即可,重复调用也只有第一次调用有效。若是 正常模式,需要在每次接收完成后重复调用。* t3 R( W* Z# g9 `
2、DMA发送要用 正常模式,需在每次发送时重复调用。还要打开UART中断,否则即使重复调用DMA发送函数也只有第一次发送有效,其余不会执行(UART发送状态一直处于 BUSY)。若是 循环模式,则会连续不断地发送,不会停止。1 f; d  ?( K; {) l
以上两点大家可以自己改改测试。
* h( A, k' d  n9 B' A: e2 d; q3 G 2.png
( F' n( k4 `) y 3.png
9 w( D$ u0 ^4 p0 u0 Y6 v8 E4 A( | 1.png
* s" Y/ d1 j0 Z, g, B  u+ l4 t$ N0 c8 v0 H: {
二、Keil例程编写
6 ~5 k0 v2 O8 R) k) [2 u 4.png
( c9 Z; V$ f; k  x" `( ? 6.png : \+ H! ~% r" d! _4 q( {
5.png
: {6 u8 `. }* @: a( I, W, h" V) l% C- C% L9 A- _
三、测试
' ^* [. P4 x* r$ i  O! w可以看到,发送数 = 接收数。大家可以做更大数据的短间隔长时间测试,看看会不会出错。
8 Q  }% X& y5 O# ? 7.png
9 W4 a2 U8 V" X9 A, V+ S
1 @, D% _  \+ [# v& N+ a最后,奉上测试工程: 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库无影响,如果以后串口出现锁死的话,再想想我说的话,或许有点帮助。串口发送前检测下发送状态标志,串口中断函数中,先暂停下发送通道,处理数据,再恢复发送通道。本贴的测试代码还是基于一收一发模式。
! L# g! R% v5 Y/ @# X- s- f+ W! {! ^: @' z) C  w( _; x
' X; c, J6 @8 F: s( M
xzrsh 回答时间:2019-8-9 20:57:26
不能用,编译路径错误。
  z! r6 ?" @+ |7 b9 N3 K* q  n7 ^8 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月技术原创' y1 e! T/ T  v4 m4 T. }
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 手机版