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

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

[复制链接]
cuyebiren 发布时间:2018-1-24 13:29
个人感觉,做嵌入式,底层就是datasheet,顶层就是数理逻辑- V: k6 d% s; s) t
不管什么芯片,当我们遇到问题时,通过查阅datasheet上官网基本上都能找到解决方法。然而,这些基本都是英文。所以,英文好对做研发是有很大益处的。不过好在有翻译工具,如:有道(我就是用有道划的)1 u/ f) B2 b' }9 o: D. N
C语言虽然没有class,但有struct。我们可以多用struct。ST库和ucos中就包含许多struct。
# R2 \9 N! M: ~: H+ H1 k# o, r程序结构多用状态机或switch。个人感觉,状态机看起来很有条理,很清晰,后期修改维护也方便。
* S+ D( u% u7 A+ I3 n* p1 V* R鄙人浅见,请大家不吝赐教。8 L0 q# A4 w: g) c( ?# @2 v
进入正题:UART以DMA方式接收和发送的函数调用顺序: d. p3 L9 j& a; h0 p7 P
循环模式接收: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)1 i% t4 ~" I8 i3 H
当然这当中还会调用传输 Half 中断,这里就不讨论了。
6 x  e- V5 C& d  C; j# W$ m* e! ?6 }) a5 t5 w' v
正常模式发送: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)
0 j' q8 z9 }1 w" Y' H' ~' Y4 s3 F. V) Y
这里只分析 HAL_UART_Receive_DMA 的源码,其它的大家自己分析:! O% D# \* T5 F( `; _) v
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
+ e6 M  [+ p  `2 ]1 k+ sUART以DMA方式接收指定长度数据到指定缓冲区% s' a: Q* H' I5 |7 z
{9 Z0 D; ^/ k2 @' r" {! e
  uint32_t *tmp;" n, P; R. B9 F5 ^1 J% c
2 \4 k3 S/ D4 D+ X+ T+ m- Y5 g9 c
  /* Check that a Rx process is not already ongoing */
3 Z/ |* i" C/ Z. J! ~4 B6 |  if(huart->RxState == HAL_UART_STATE_READY)   检查huart->RxState是否处于 接收空闲 状态。当这一状态标志非READY时,会跳过DMA接收参数设置,直接返回HAL_BUSY其它的UART接收函数也会检查这个状态,所以,哪个先调用就执行哪个。
' L3 j; b& N$ _% H  {6 z7 z7 t& F& v8 N
    if((pData == NULL) || (Size == 0U))
5 r* N9 w1 t3 I$ [% U9 ?    {
/ p) Q8 v  ]" T9 M+ b! G      return HAL_ERROR;* U$ H2 _: l6 y  r
    }; v  m4 h2 S0 m0 Y
* _( }3 h0 e& q
    /* Process Locked */
8 f4 j0 C4 ?, X& a, M* m    __HAL_LOCK(huart);
- d3 y* c* W# I7 T) A7 V- _- J. l5 H; o& h" J' w. t. ?0 V9 `
    huart->pRxBuffPtr = pData;
+ f+ n( ~, s2 m8 t" \* |    huart->RxXferSize = Size;" B2 C/ C" o- k: E4 u
" ~7 f3 ]! ?" _  {1 q* X! P
    huart->ErrorCode = HAL_UART_ERROR_NONE;
+ V- ^. O- o. c+ e4 z    huart->RxState = HAL_UART_STATE_BUSY_RX;6 z7 _: [% h. _9 r: ?+ D( q5 o

7 L' i. q5 Y1 Z* n3 P2 a* d    /* Set the UART DMA transfer complete callback */
1 c) X1 S# ?: C+ x! E. }- h( {    huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt; 设置DMA传输完成的回调函数。当DMA以循环方式传输时会调用UART接收完成中断的回调函数;以Normal方式传输时会关闭UART的DMA通道,并使能UART传输完成中断,触发UART传输完成中断,设置huart->RxStateREADY,并调用
! m7 ~- p2 `5 LUART接收完成中断的回调函数。所以,
不管DMA按循环或正常模式传输,到最后都会调用UART接收完成中断的回调函数7 X  O1 W( P) U: q8 ^) B
    /* Set the UART DMA Half transfer complete callback */$ v+ b- g3 T: R: p( _: d* K
    huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;
: L% H9 c  e9 j' D* v/ c0 A5 F3 c8 e
    /* Set the DMA error callback */
- Y  I* {; z" J% z% Z5 n    huart->hdmarx->XferErrorCallback = UART_DMAError;/ B9 ?3 ?9 D1 J+ z3 ]' i- o. F3 `

0 Q- H2 [& k- C6 y+ v    /* Set the DMA abort callback */
  l1 N& g2 X9 H; t1 T4 R    huart->hdmarx->XferAbortCallback = NULL;' B  |" T! ]7 X, |
7 e' F1 ]& z6 @. b* L
    /* Enable the DMA channel */5 P# s' j. T0 N& h. }
    tmp = (uint32_t*)&pData;0 r$ i- l' }6 I! t# t0 q
    HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);以中断方式打开DMA传输。所以CubeMx中DMA中断默认是打开的切不可更改0 q8 ]0 H. c# H2 O

) G1 ~5 [. m  f- r% L& n    /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer *// r- V. S& g3 T7 N+ y& t3 d
    __HAL_UART_CLEAR_OREFLAG(huart);; O1 l1 [3 U+ l, M* t& G

  f3 F5 O+ ?$ |    /* Process Unlocked */6 j  ?$ C. u! @
    __HAL_UNLOCK(huart);; \2 B5 o7 ^: D4 K& A/ q
1 \' _6 l0 @( v2 u
    /* Enable the UART Parity Error Interrupt */4 t0 ~* s8 l9 Y& x: @) q8 f( @6 P
    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);
3 T; ^5 c; L- C* @
# [; F9 e) O; J& O  k4 r    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
1 s0 r) T& B- T8 b6 i2 x    SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
, _6 u) U. Y8 r. B( j
! W" D& u/ y$ r/ e0 q, C    /* Enable the DMA transfer for the receiver request by setting the DMAR bit - ?/ h2 X# \1 E* A1 y8 z) L) I
    in the UART CR3 register */; u$ B5 T9 o4 G6 N
    SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);" G, ~+ U' x! q+ P
7 c6 {- I# W$ T- T, s
    return HAL_OK;$ V# b  C( o- S. o. I
  }. c8 ^' z( i1 m/ @5 A- w8 W
  else
. `: i4 f" a6 [# o4 c0 f: Q# Q  {
9 @3 `" n; X" N5 u; n1 m- e, x    return HAL_BUSY;
; C% m6 [! e$ O$ Y  }
1 a; A5 s5 o, L8 W# ~}9 L% ?" U# s6 ]! I. \4 S
下面上我做的测试:一、Cube配置
( Y5 }( q, p) O2 D1、DMA接收要用 循环模式,只需调用一次接收函数即可,重复调用也只有第一次调用有效。若是 正常模式,需要在每次接收完成后重复调用。
: u; T- v/ o/ {! x5 X/ m2、DMA发送要用 正常模式,需在每次发送时重复调用。还要打开UART中断,否则即使重复调用DMA发送函数也只有第一次发送有效,其余不会执行(UART发送状态一直处于 BUSY)。若是 循环模式,则会连续不断地发送,不会停止。4 B# o# }) R. Q' \  C* E
以上两点大家可以自己改改测试。
- s$ A: |4 L  h& J! S4 j) j% U1 U 2.png - l( n* l& \$ f! f9 c; O; u
3.png / I5 ~4 x$ e; K2 ]
1.png
+ D" R: C3 E* m
, d" `6 ^' \7 @% T8 V二、Keil例程编写
6 @$ [9 i1 D/ l" w3 M5 n) N 4.png ! z7 j, c' D+ F
6.png 6 p" f2 D. b" b& _
5.png ' c" M) ~/ s: ?9 ]% b$ u
3 e2 m& E) o2 ?
三、测试* {. q( A, H  c' F) L* V% `
可以看到,发送数 = 接收数。大家可以做更大数据的短间隔长时间测试,看看会不会出错。
9 O" f7 F4 v5 d2 s0 u 7.png # [9 N/ I' d; ^( n/ C7 T

  X. _1 y, b( r6 E* i  U最后,奉上测试工程: uart_dma_test.rar (3.7 MB, 下载次数: 2086)

评分

参与人数 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库无影响,如果以后串口出现锁死的话,再想想我说的话,或许有点帮助。串口发送前检测下发送状态标志,串口中断函数中,先暂停下发送通道,处理数据,再恢复发送通道。本贴的测试代码还是基于一收一发模式。
) h: S, E3 ~0 j; Y+ T8 k8 H. ?7 E  o' d& [
" f2 y8 h4 z' G3 ?  I1 W9 T0 ?$ v
xzrsh 回答时间:2019-8-9 20:57:26
不能用,编译路径错误。
( d* e: x! d/ N" }# \+ Q"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月技术原创" }' U' J) b) 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管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版