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

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

[复制链接]
cuyebiren 发布时间:2018-1-24 13:29
个人感觉,做嵌入式,底层就是datasheet,顶层就是数理逻辑7 z5 R" A2 b# Q3 r
不管什么芯片,当我们遇到问题时,通过查阅datasheet上官网基本上都能找到解决方法。然而,这些基本都是英文。所以,英文好对做研发是有很大益处的。不过好在有翻译工具,如:有道(我就是用有道划的)* o. u7 a& ^+ J0 ?$ N/ Z2 m( l
C语言虽然没有class,但有struct。我们可以多用struct。ST库和ucos中就包含许多struct。& k; S$ R0 m; o, W3 G& \
程序结构多用状态机或switch。个人感觉,状态机看起来很有条理,很清晰,后期修改维护也方便。; a& n/ B9 J9 S6 S( U& ?# j9 X
鄙人浅见,请大家不吝赐教。
) x  {) Z2 }# t# \# _进入正题:UART以DMA方式接收和发送的函数调用顺序( Q- H3 O: x% f/ W  j0 T
循环模式接收: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 n4 _- S2 G% t  ~3 y4 A2 Y0 B+ c+ e
当然这当中还会调用传输 Half 中断,这里就不讨论了。  N5 U+ }' l  B0 N1 ?

0 a! A2 j" N0 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)$ E2 D/ u) |6 ]: D& t9 O5 ]5 r8 o

% R4 c' k0 g7 O这里只分析 HAL_UART_Receive_DMA 的源码,其它的大家自己分析:
" ]1 H6 F, p. G- m1 @HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
. C( g6 V/ J! O; FUART以DMA方式接收指定长度数据到指定缓冲区
5 d# A- T3 t# n; u{
: `( \* n1 }: a) e* ~  uint32_t *tmp;
9 m% q, [! F" B( M0 g9 j& \7 s$ X9 B
  /* Check that a Rx process is not already ongoing */
  n! d  ^6 j& j) `3 [) |  if(huart->RxState == HAL_UART_STATE_READY)   检查huart->RxState是否处于 接收空闲 状态。当这一状态标志非READY时,会跳过DMA接收参数设置,直接返回HAL_BUSY其它的UART接收函数也会检查这个状态,所以,哪个先调用就执行哪个。
1 ], F2 u, g( N; v6 N& b/ ?& g  {2 M: n1 O3 V$ n3 j# D
    if((pData == NULL) || (Size == 0U))8 ?, W* E" L% z  ]
    {
; k  ^: V% T. k; f) [4 Y      return HAL_ERROR;
  r4 s6 U$ R& t' j) Q7 q( c    }
, u9 [& _1 |& u3 T/ C, u% z" x! U
* q7 C2 h# t# a  m    /* Process Locked */
5 k; p2 b) x+ I    __HAL_LOCK(huart);
6 ?5 w+ ?2 b1 O+ E
- t- O! j. m/ n/ i    huart->pRxBuffPtr = pData;
/ u  g* F7 @, C  m6 F7 ~    huart->RxXferSize = Size;: H  E! x' v  A( P5 L6 X  Y
; `. v# ?, l2 m- `
    huart->ErrorCode = HAL_UART_ERROR_NONE;
. ?) B4 d; C7 J    huart->RxState = HAL_UART_STATE_BUSY_RX;4 A* v& U; w4 i9 I, h: A
7 S- w1 v6 A6 d: N; Q& c
    /* Set the UART DMA transfer complete callback */
; }/ L% x8 E% s    huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; 设置DMA传输完成的回调函数。当DMA以循环方式传输时会调用UART接收完成中断的回调函数;以Normal方式传输时会关闭UART的DMA通道,并使能UART传输完成中断,触发UART传输完成中断,设置huart->RxStateREADY,并调用+ E' @8 q: ]' G* x1 d+ u. C
UART接收完成中断的回调函数。所以,
不管DMA按循环或正常模式传输,到最后都会调用UART接收完成中断的回调函数
, u) m% l8 F1 V# N    /* Set the UART DMA Half transfer complete callback */
& L/ f! n  y. ~# m& T# N* a& a    huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;: U- Y7 R1 d, X) C$ l; `! N8 @

/ E4 M9 m/ ?9 ~9 @6 m) |0 L$ _    /* Set the DMA error callback */
! B# y3 J1 M6 X( O# D    huart->hdmarx->XferErrorCallback = UART_DMAError;
0 ^! G  P8 c4 k8 E2 k( q
7 g' u6 u+ |5 \0 G    /* Set the DMA abort callback */+ ]( ^- w/ o/ y% T: ?' B
    huart->hdmarx->XferAbortCallback = NULL;
) m+ [3 U2 x5 V- S( R
" z# W3 L- M0 k. S1 V0 @    /* Enable the DMA channel */
4 b) F2 |$ h  F  V    tmp = (uint32_t*)&pData;
; L* N; S4 V, d$ L* c6 ]: m    HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);以中断方式打开DMA传输。所以CubeMx中DMA中断默认是打开的切不可更改9 z9 |" N- ?( J, |, `- {
. T; C6 h0 O1 ?
    /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */
! b* \; h  C' [" |+ O    __HAL_UART_CLEAR_OREFLAG(huart);8 ]/ {8 S8 \/ S- _9 U7 {0 }

2 d* R3 W7 Z, |1 T* k    /* Process Unlocked */4 k2 r: P. B# u+ o
    __HAL_UNLOCK(huart);/ J5 w4 r( P5 k/ o$ ?& `
, n: P! |' h) M+ u1 R
    /* Enable the UART Parity Error Interrupt */& ?" I. }# u& o4 \
    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);
$ U. f' H0 f# U- w: j  }
! }5 f& h- W0 {& t    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
3 C0 x4 t/ v- b7 R. k8 P    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
8 Z- ~; u$ V: M
1 n6 g$ f7 U$ C1 r  `9 N    /* Enable the DMA transfer for the receiver request by setting the DMAR bit 5 b: ~1 E+ M/ J' u2 w& C
    in the UART CR3 register */
4 W; A: g/ n9 e/ {  U6 k# x    SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);/ g9 _, h. a9 `1 O5 m

/ D# E" Y% }8 ^4 v' ]) \; L    return HAL_OK;
8 Q3 J! s& q& M( u/ i# G" p  }
) f! F5 ]: K9 m* |1 L  else
9 c- E  i5 O: ^! [  {* i8 G4 u5 Q& ~- h) [' ?: m
    return HAL_BUSY;
( w+ b" U" [0 h9 i  Y; S& W6 t7 b  }
/ G. s% R% J4 q4 r5 u- L# m}
9 W3 [9 t- ~  [- Q3 o7 x下面上我做的测试:一、Cube配置
) I( k# D/ ~' I# W1、DMA接收要用 循环模式,只需调用一次接收函数即可,重复调用也只有第一次调用有效。若是 正常模式,需要在每次接收完成后重复调用。
4 ~' E1 z' G0 s, j+ T9 ^2、DMA发送要用 正常模式,需在每次发送时重复调用。还要打开UART中断,否则即使重复调用DMA发送函数也只有第一次发送有效,其余不会执行(UART发送状态一直处于 BUSY)。若是 循环模式,则会连续不断地发送,不会停止。1 m3 J" w, C# c( n# G& d0 ~1 `4 a+ s
以上两点大家可以自己改改测试。
5 [' b. a( T& M0 T8 P 2.png ; \; e* P; J( S* a: u& U
3.png 1 y* }6 H, v0 c5 ^1 k  H
1.png
" z+ d& C6 B8 X5 F. j1 }1 n6 A
( Z& ~/ K1 Q) P+ }' |二、Keil例程编写: g2 Y( x% D7 u; E4 ?( a/ L9 \
4.png
8 R4 B" t1 w) ?  v6 c4 W 6.png
% m3 ?  L  ^' b: X 5.png
9 ?- M  L- m  G" @( O/ z7 d: n
4 x5 P; P1 S& M三、测试
* s5 T, n+ m6 H/ {  Z; g( z+ A可以看到,发送数 = 接收数。大家可以做更大数据的短间隔长时间测试,看看会不会出错。
" X' @* s  j% F( }. z 7.png
2 P( L! m; K$ N: o' E0 G& L- h$ R8 |5 w% ~$ A
最后,奉上测试工程: 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库无影响,如果以后串口出现锁死的话,再想想我说的话,或许有点帮助。串口发送前检测下发送状态标志,串口中断函数中,先暂停下发送通道,处理数据,再恢复发送通道。本贴的测试代码还是基于一收一发模式。
/ ^. i4 x7 j% O" T1 o6 ]* Q( \# Q9 F% G0 y. d8 H, D/ X: O- b6 w
" g3 R3 F( n; L1 \
xzrsh 回答时间:2019-8-9 20:57:26
不能用,编译路径错误。3 H- j1 \, U0 N) x& 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月技术原创
, p' t; c+ o$ O. r' n: Uhttps://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管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版