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

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管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版