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

HAL库的串口DMA操作方法很难理解,非常的不好用

[复制链接]
tempchar 提问时间:2016-6-12 10:22 /
最近用HAL库做个单工的无线串口,因为无线通信是阻塞的,就用串口的DMA接收函数HAL_UART_Receive_DMA。

实际用的时候发现很多问题:
1.串口接收一旦溢出就会丢数据。
例如串口接收满了,稍等几秒再启动新的DMA接收函数HAL_UART_Receive_DMA时,就丢失数据了,而且是再也收不到串口数据。如果接满后马上启动就没这个问题。
看官方示例代码,停止DMA接收后似乎要DeInit后重新初始化Init和启动DMA接收

2.串口DMA接收不能单独停止。
例如串口同时在DMA发送和DMA接收,DMA接收到一半我要终止DMA的话,只能调用HAL_UART_DMAStop把接收DMA和串口DMA都停止。
换言之,不能单独停止DMA接收。
我理解的接收DMA和发送DMA是两个独立的操作,为什么不能单独停止?


大家有没有类似的经验,应当怎样规避这些坑?
还是说我的做法和理解其实是有问题的?有错误的请大家批评指正,一起学习提高

收藏 2 评论21 发布时间:2016-6-12 10:22

举报

21个回答
任风吹吹 回答时间:2016-6-15 11:28:59
本帖最后由 任风吹吹 于 2016-6-15 17:17 编辑
tempchar 发表于 2016-6-15 11:00
DMA停止后,HAL_UART_Receive_DMA执行前,如果串口有数据接收就会出问题。我测试时就是这种情况。

那我 ...

当你将接收DMA关闭后,此时串口还是激活的,若此时串口来数据,无法触发DMA传输,此时产生上溢错误(ORE),由于串口数据寄存器里的数据不能及时转移走,后面来的数据就进不来。一旦出现这种错误后,就不会再触发DMA请求,即使再开启DMA也不行。要恢复正常的话就只有Deinit后再重新初始化串口,或者使用read接口直接将数据寄存器中的数据读走后,后面的数据才能正常进入,从而正常产生DMA请求,这个DMA请求是指硬件请求。

评分

参与人数 1ST金币 +2 收起 理由
zero99 + 2 很给力!

查看全部评分

龙吟风独恋云 回答时间:2018-5-11 17:04:22
本帖最后由 龙吟风独恋云 于 2018-5-11 17:05 编辑

针对第2点,串口DMA接收不能单独停止。

HAL_UART_DMAStop()函数的确是uart接收和发送同时关闭,找了好久终于找到只关闭接收的函数了。
只关闭接收的函数。HAL_UART_AbortReceive()

下面看一下函数具体实现:
HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart)
{
  uint32_t dmarequest = 0x00U;
  /* The Lock is not implemented on this API to allow the user application
     to call the HAL UART API under callbacks HAL_UART_TxCpltCallback() / HAL_UART_RxCpltCallback():
     when calling HAL_DMA_Abort() API the DMA TX/RX Transfer complete interrupt is generated
     and the correspond call back is executed HAL_UART_TxCpltCallback() / HAL_UART_RxCpltCallback()
     */
  /* Stop UART DMA Tx request if ongoing */
  dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAT);
  if((huart->gState == HAL_UART_STATE_BUSY_TX) && dmarequest)
  {
    CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAT);
    /* Abort the UART DMA Tx channel */
    if(huart->hdmatx != NULL)
    {
      HAL_DMA_Abort(huart->hdmatx);
    }
    UART_EndTxTransfer(huart);
  }
  /* Stop UART DMA Rx request if ongoing */
  dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);
  if((huart->RxState == HAL_UART_STATE_BUSY_RX) && dmarequest)
  {
    CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);
    /* Abort the UART DMA Rx channel */
    if(huart->hdmarx != NULL)
    {
      HAL_DMA_Abort(huart->hdmarx);
    }
    UART_EndRxTransfer(huart);
  }
  return HAL_OK;
}

主要分为2大部分,处理标志,关闭发送和接收DMA。

HAL_StatusTypeDef HAL_UART_AbortReceive(UART_HandleTypeDef *huart)
{
  /* Disable RXNE, PE and ERR (Frame error, noise error, overrun error) interrupts */
  CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));
  CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
  /* Disable the UART DMA Rx request if enabled */
  if(HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))
  {
    CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);
    /* Abort the UART DMA Rx channel : use blocking DMA Abort API (no callback) */
    if(huart->hdmarx != NULL)
    {
      /* Set the UART DMA Abort callback to Null.
         No call back execution at end of DMA abort procedure */
      huart->hdmarx->XferAbortCallback = NULL;
      HAL_DMA_Abort(huart->hdmarx);
    }
  }
  /* Reset Rx transfer counter */
  huart->RxXferCount = 0x00U;
  /* Restore huart->RxState to Ready */
  huart->RxState = HAL_UART_STATE_READY;
  return HAL_OK;
}

也是分为2部分,处理标志和关闭接收DMA。

最简单的实现:
1. 先关闭接收DMA,HAL_DMA_Abort(huart->hdmarx);
2. 置位RX ready状态,huart->RxState = HAL_UART_STATE_READY;
忽略了错误标志和IT标志处理,最保险的方式还是使用HAL_UART_AbortReceive()函数。

评分

参与人数 1蝴蝶豆 +4 收起 理由
zero99 + 4

查看全部评分

斜阳 回答时间:2016-6-15 10:21:48
我刚做过用串口DMA接收不定长数据,使能空闲中断,在空闲中断触发的时候停止DMA,取出数据,之后使用HAL_UART_Receive_DMA重新接收没有问题。(没有DeInit和Init)
2,HAl的库函数HAL_UART_DMAStop确实是同时关收发DMA,如果只关一个的话可以直接操作寄存器。至于只关一个会不会出问题就不知道了;
wenyangzeng 回答时间:2016-6-15 10:57:11
你应该用DMA中断将数据移到缓冲区

评分

参与人数 1ST金币 +2 收起 理由
zero99 + 2 很给力!

查看全部评分

tempchar 回答时间:2016-6-15 11:00:33
斜阳__ 发表于 2016-6-15 10:21
我刚做过用串口DMA接收不定长数据,使能空闲中断,在空闲中断触发的时候停止DMA,取出数据,之后使用HAL_UA ...

DMA停止后,HAL_UART_Receive_DMA执行前,如果串口有数据接收就会出问题。我测试时就是这种情况。

那我试试直接操作寄存器。多谢你的建议
tempchar 回答时间:2016-6-15 11:01:01
wenyangzeng 发表于 2016-6-15 10:57
你应该用DMA中断将数据移到缓冲区

的确这也是一种方法
tempchar 回答时间:2016-6-16 10:40:43
任风吹吹 发表于 2016-6-15 11:28
当你将接收DMA关闭后,此时串口还是激活的,若此时串口来数据,无法触发DMA传输,此时产生上溢错误(ORE) ...

这下算是完全明白了,多谢多谢
Ian-392967 回答时间:2016-7-2 06:57:15
谢谢分享,让我明白了
loook 回答时间:2016-10-18 22:42:37
确实我在调试串口的时候也遇到了这个问题,串口dma回调函数只能进入一次,稍后我把问题解决了和大家一起分享吧
ynwscfsfi 回答时间:2016-10-19 01:17:03
解决就好
菜鸟小小 回答时间:2016-11-15 09:07:57
谢谢分享
haifeng-388081 回答时间:2016-11-15 09:43:48
学习了,   
zbber 回答时间:2016-11-15 10:09:59
你应该用DMA中断将数据移到缓冲区
秋水之下 回答时间:2017-5-4 11:36:21
zbber 发表于 2016-11-15 10:09
你应该用DMA中断将数据移到缓冲区

就是啊,本来就应该这样处理呀
ekerV5 回答时间:2017-5-4 13:41:59
斜阳__ 发表于 2016-6-15 10:21
我刚做过用串口DMA接收不定长数据,使能空闲中断,在空闲中断触发的时候停止DMA,取出数据,之后使用HAL_UA ...

哥,请问你怎么做的,能给段代码看看吗
12下一页

所属标签

相似问题

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版