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

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

[复制链接]
cuyebiren 发布时间:2018-1-24 13:29
个人感觉,做嵌入式,底层就是datasheet,顶层就是数理逻辑。) g9 a: R9 r  b9 v/ u/ K6 G
不管什么芯片,当我们遇到问题时,通过查阅datasheet或上官网基本上都能找到解决方法。然而,这些基本都是英文。所以,英文好对做研发是有很大益处的。不过好在有翻译工具,如:有道(我就是用有道划的)/ \* e; ^" H. s# ~6 C4 x* u
C语言虽然没有class,但有struct。我们可以多用struct。ST库和ucos中就包含许多struct。- @$ H4 A- m: v4 x5 B
程序结构多用状态机或switch。个人感觉,状态机看起来很有条理,很清晰,后期修改维护也方便。
& }( g( j0 E9 v9 K% w* y鄙人浅见,请大家不吝赐教。; P9 ~+ d, s( r. g, K8 ~) ?
进入正题:UART以DMA方式接收和发送的函数调用顺序:# U- X# J6 E! _( h) E* C
循环模式接收: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)
, q3 t$ B& g# @6 X当然这当中还会调用传输 Half 中断,这里就不讨论了。6 T' {. I" `, y, B" k7 b+ h. @) s& m
, i/ u/ ?% o, ?( }
正常模式发送: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)! o) N6 d4 i, N! R) p; |) S5 H
# @1 r9 d- e  T
这里只分析 HAL_UART_Receive_DMA 的源码,其它的大家自己分析:# N7 ]3 p  L9 p1 a" s1 U8 @
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
/ N) V# h" E- p. C$ N- RUART以DMA方式接收指定长度数据到指定缓冲区
1 Z* H) E$ S9 S4 ~% u, D{
- M6 X: o, f/ n% P1 a" Q  uint32_t *tmp;
7 v: s3 ?" U% L5 V7 y# x0 [- t, n1 b8 ?2 Y) K2 ]5 a
  /* Check that a Rx process is not already ongoing */9 J0 c* ^& h! e+ n2 E$ v9 {
  if(huart->RxState == HAL_UART_STATE_READY)   检查huart->RxState是否处于 接收空闲 状态。当这一状态标志非READY时,会跳过DMA接收参数设置,直接返回HAL_BUSY。其它的UART接收函数也会检查这个状态,所以,哪个先调用就执行哪个。3 x- d6 r& E( F# K
  {
' L2 d4 t( S. @- A- ]  L1 j    if((pData == NULL) || (Size == 0U)): d8 ]* ?& ?* x% R
    {
  N3 `4 @" |4 {" m      return HAL_ERROR;
5 O5 X1 c. N% z# {) I( t! W    }. k2 s8 @( K$ \# ?

' M# I2 @& s. P    /* Process Locked */
( w" k  J! L& p2 y    __HAL_LOCK(huart);
# g# X3 S; _# L  s" k7 ~2 ]' }3 P* X# y  W' T' x
    huart->pRxBuffPtr = pData;6 H0 R* a( a6 _0 C& [" {, ^& i7 l
    huart->RxXferSize = Size;
; l- t" {! U3 P- z- D. [9 e* ?8 D) J
7 ]) B2 t) [3 T. U$ [& S    huart->ErrorCode = HAL_UART_ERROR_NONE;, S" `( o2 X( _  J: h
    huart->RxState = HAL_UART_STATE_BUSY_RX;- X2 O1 A2 U& d$ i) t

1 _/ L( x/ }9 Y( v4 f8 ^. F: y- f    /* Set the UART DMA transfer complete callback */
; ^  F6 Q- f, E. B1 _0 H6 w4 f    huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; 设置DMA传输完成的回调函数。当DMA以循环方式传输时会调用UART接收完成中断的回调函数;以Normal方式传输时会关闭UART的DMA通道,并使能UART传输完成中断,触发UART传输完成中断,设置huart->RxState为READY,并调用
  z0 a6 [! Q" B3 V' M2 cUART接收完成中断的回调函数。所以,
不管DMA按循环或正常模式传输,到最后都会调用UART接收完成中断的回调函数4 H. Y; j1 U5 `* I6 V' E+ ]
    /* Set the UART DMA Half transfer complete callback */
7 g! x  c! H; l    huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;  W5 e. y6 j3 Y
* q6 x: {; K* P% U. a( `
    /* Set the DMA error callback */$ E* c7 Z1 q) f6 U
    huart->hdmarx->XferErrorCallback = UART_DMAError;: G) o) r7 u* k3 Q2 J$ ~4 ^' C/ U

$ Y/ J+ |" K7 r    /* Set the DMA abort callback */
+ l  T; _4 Q2 {    huart->hdmarx->XferAbortCallback = NULL;' j5 ~6 Y. n0 a# V9 R

) I4 C* V1 a; g% n$ p    /* Enable the DMA channel */
* _2 ?  Z* F0 J/ N* p    tmp = (uint32_t*)&pData;
4 T+ H. X0 W6 c" q7 S! |    HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);以中断方式打开DMA传输。所以CubeMx中DMA中断默认是打开的切不可更改
4 a9 i% D  `8 D) ]/ e3 l0 [: v9 q; P; H1 G6 w: d8 R8 ?3 f
    /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */
! i0 Q2 f9 \3 x2 N0 u& O    __HAL_UART_CLEAR_OREFLAG(huart);
) h$ s$ |8 }0 q7 |3 B; E
5 u& o6 p$ i  j7 H1 U8 y4 _. T    /* Process Unlocked */
- u" l2 x: \9 L; r- Z$ U, V- y    __HAL_UNLOCK(huart);
6 y- g" q8 p) y' p' ^- j# t
( a5 `- N8 n4 u2 Z    /* Enable the UART Parity Error Interrupt */; s/ z9 ]! [7 R+ v4 Q
    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);
: G' y" I: e, Z# T, r
( [: r: j7 q$ j! A    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */8 K( J1 |2 r; e0 B7 c7 U1 N5 \8 L
    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);( u0 y# E( R( m! l6 b
. D( |* G1 c6 W0 B: g. b" t) {
    /* Enable the DMA transfer for the receiver request by setting the DMAR bit
# H4 [- P9 B( T& P4 E% R/ s    in the UART CR3 register */
; P9 R; t  a2 k0 i" G9 I    SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);. @. [6 M6 E# m/ n( P, {% V
5 I. }$ Z+ e: t0 |2 W  Z
    return HAL_OK;: _. I% B% b- h' n8 L
  }# |" O* P. F7 r2 V  V' ?
  else+ m. J. `9 {6 B: k% }! H$ B
  {
: c& E5 Y4 X5 R. m    return HAL_BUSY;
( w% @4 p: A  M! z  }
1 I! c7 ^6 k: |3 i2 @2 ?, v- o}
& q" Z. F% `* a: s1 j5 k; O下面上我做的测试:一、Cube配置
, {- W: L2 B8 \$ h/ X& n6 w' |1、DMA接收要用 循环模式,只需调用一次接收函数即可,重复调用也只有第一次调用有效。若是 正常模式,需要在每次接收完成后重复调用。
( Q7 B$ @8 V5 h' h; J" d$ D7 K2、DMA发送要用 正常模式,需在每次发送时重复调用。还要打开UART中断,否则即使重复调用DMA发送函数也只有第一次发送有效,其余不会执行(UART发送状态一直处于 BUSY)。若是 循环模式,则会连续不断地发送,不会停止。
4 Y% p2 _4 n& p8 Q/ |2 C以上两点大家可以自己改改测试。
9 T, }  l9 b6 I% s2 ~8 U 2.png
% _' t9 U/ V; J$ d. |9 H- m* f 3.png " T6 j6 o5 V; F4 w1 _! J- j
1.png   t) d' V1 o5 F
  q. ^$ V) s7 J/ o) t
二、Keil例程编写) e  u! C  H( n1 B5 b* k
4.png
: z2 |# G, }, h: F2 \ 6.png / v% ~3 [+ U- D4 B4 Y3 W% H/ O
5.png 4 @# e+ l  @' [, X

  Y8 B) i+ R9 M9 I! g) S三、测试  Z6 @4 ]' x& ?4 U
可以看到,发送数 = 接收数。大家可以做更大数据的短间隔长时间测试,看看会不会出错。! z: l: q& T, m5 G' m  F
7.png . ?0 Z' S1 Q% }9 u$ J) G

/ u' M$ B8 H. e) m% n6 l% m最后,奉上测试工程: 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库无影响,如果以后串口出现锁死的话,再想想我说的话,或许有点帮助。串口发送前检测下发送状态标志,串口中断函数中,先暂停下发送通道,处理数据,再恢复发送通道。本贴的测试代码还是基于一收一发模式。
4 n4 w3 s9 [: y/ |2 G
( X) [+ }0 F( B  R! Q2 w# W8 e% e" ]6 l4 i( }8 o/ L' a
xzrsh 回答时间:2019-8-9 20:57:26
不能用,编译路径错误。' q3 W. r% ^; D2 E
"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月技术原创) i: E6 T' E2 F0 Y
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管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版