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

Cube下实现串口+DMA+空闲中断接收不定长数据  

[复制链接]
wjjontheway 提问时间:2015-7-30 16:38 /

采用STM32cube完成ADC的DMA功能

STM32F051C8T6 STM32CubeMX ADC-DMA-UART教程


一直困惑怎么在Cube上实现串口+DMA+空闲中断接收不定长数据。
使用下来发现Cube好的地方就是你按照图形画方式配置后会帮你省掉很多初始化的工作。但是从标准库走过来略有点别扭,可能用用就习惯了。
想把这几天搞的跟大家聊聊,有不对的地方请指正啊。同时感谢网友baifernlover对本菜的指导。
USART1+DMA配置如下图:

串口1-DMA接收

串口1-DMA接收

生成工程后,在主函数里添加:
  1. __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);    //使能空闲中断
  2.   while (1)
  3.   {
  4.         if(recv_end_flag ==1)                                           //recv_end_flag 结束标志
  5.         {
  6.                 printf("rx_len=%d\r\n",rx_len);                    //rx_len 此次接收到了多少数据
  7.                 for(i=0;i<rx_len;i++)
  8.                 {
  9.                                 printf("%x\r\n",rx_buffer);
  10.                 }
  11.                 rx_len=0;
  12.                 recv_end_flag=0;
  13.         }
  14.         HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);
  15.   }
复制代码

中断函数里面:
  1. void USART1_IRQHandler(void)
  2. {

  3.         uint32_t tmp_flag = 0;
  4.         uint32_t temp;
  5.         tmp_flag =  __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE);
  6.         if((tmp_flag != RESET))
  7.        {
  8.                 __HAL_UART_CLEAR_IDLEFLAG(&huart1);
  9.                 temp = huart1.Instance->SR;  
  10.                 temp = huart1.Instance->DR;
  11.                 HAL_UART_DMAStop(&huart1);
  12.                 temp  = hdma_usart1_rx.Instance->NDTR;            
  13.                 rx_len =  BUFFER_SIZE - temp;                           
  14.                  recv_end_flag = 1;
  15.          }

  16. }
复制代码

之前看到帖子说HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)和HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)接收需要指定接收Size长度值,但是这两个函数的Size有所区别。
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)主要做了两件事:
1.   huart->pRxBuffPtr = pData;       /*!< Pointer to UART Rx transfer Buffer */
      huart->RxXferSize = Size;           /*!< UART Rx Transfer size              */        这个赋值好像没什么用
     huart->RxXferCount = Size;        /*!< UART Rx Transfer Counter           */
2.使能中断
/* Enable the UART Data Register not empty Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
在中断处理函数中,去判断中断标志位;然后再根据相应的中断标志位去处理:
  tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_RXNE);
  tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_RXNE);
  /* UART in mode Receiver ---------------------------------------------------*/
  if((tmp1 != RESET) && (tmp2 != RESET))
  {
    UART_Receive_IT(huart);
  }

UART_Receive_IT函数里感觉做了两件事:
1.*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);  //存数据
2.if(--huart->RxXferCount == 0)                                          //之前赋值的 huart->RxXferCount = Size;
   {
         __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);     //最后一个数据接收完了就关闭中断
    }
   HAL_UART_RxCpltCallback(huart);                                     //调用接收完成回调函数
整个过程中,来一个数据,huart->RxXferCount做一次减法,直到等于0,去调用回调函数。


HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)中:
1. HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size);--->
2.DMA_SetConfig(hdma, SrcAddress, DstAddress, DataLength);这里的DataLength就是之前的Size吧---->
3. /* Configure DMA Stream data length */
   hdma->Instance->NDTR = DataLength;   
对NDTR寄存器描述:This register can be written only
when the stream is disabled. When the stream is enabled, this register is read-only,
indicating the remaining data items to be transmitted. This register decrements after each
DMA transfer.

这里的定义的Size赋给了DMA_SxNDTR。使能之后,只能读取,表示还有多少字节需要被“传送”。所以空闲中断里面
temp  = hdma_usart1_rx.Instance->NDTR;  //读取还没有被传送的个数            
rx_len =  BUFFER_SIZE - temp;                    //DMA缓存大小减去没有被传送的个数,就等于已经被传送的个数,也就是接收到的个数。

先这样子理解了。所以感觉如果是DMA接收,即使外部过来的数据个数不等于设定的Size,那么数据也还是被接收到了指定的buffer中。
DMA中断处理函数HAL_DMA_IRQHandler里面会判断DMA传输的状态,完了去调用各自的回调函数。比如传输完成的话会去调用UART_DMAReceiveCplt()--->HAL_UART_RxCpltCallback(huart);最后还是调用了HAL_UART_RxCpltCallback(huart)函数。

最后附上工程文件,欢迎大家多多讨论。


STM32_USART_DMA_IDLE.rar

下载

6.17 MB, 下载次数: 19826, 下载积分: ST金币 -1

Cube_USART_DMA_IDLE

评分

参与人数 1 ST金币 +1 收起 理由
zcshou + 1 总体思路没错,但是stop后需要HAL_UART_DMA.

查看全部评分

4 收藏 22 评论81 发布时间:2015-7-30 16:38

举报

81个回答
kylongmu 回答时间:2018-4-9 14:02:28
本帖最后由 kylongmu 于 2018-4-9 14:12 编辑

我觉得大家把这个路走歪了点,
__HAL_DMA_DISABLE_IT(huart1.hdmarx, DMA_IT_TC | DMA_IT_HT | DMA_IT_TE);//关闭DMA 错误 传输一半 全部完成 中断

这里如果将DMA配置成一锤子买卖,肯定会丢东西。
--------------------------------------------------------------------------------------------------------------------------------------------------------------
不如将DMA配置成Circular模式,不要停止DMA,另外开启
“传输一半、全部完成” 中断,然后把DMA缓冲区当作乒乓结构来用,当“传输一半”中断时处理前半部分数据,当“全部完成”中断时处理后半部分数据。处理方法:依据上次读指针读出到当前点数据至一个缓冲区,并更新读指针。

那么在“空闲中断”时,依据上次读指针读出到当前点数据,追加写入缓冲区,并将缓冲区数据整体复制为未处理数据包(带有效数据长度),然后更新缓冲区写入指针至头部。

这样就能解决DMA缓冲区必须设置为很大的问题,还能解决关闭DMA造成数据丢失的问题,仅是维护一个读DMA缓冲区指针(这个指针与Circula模式一样无限循环)+一个写缓冲区指针(每次拷贝数据后复位)。

DMA缓冲区可以设置为256字节,这样缓冲区读指针可以采用基地址+一个UINT8偏移实现,UINT8自动溢出循环256。
jtc_88 回答时间:2017-6-2 11:28:17
这个 需要配合 串口数据 收发 协议来,否则无法确认 到底接收多少数据。
                HAL_UART_Receive_DMA(&huart1, RX_DATA, RX_DATA_NUM);
                __HAL_DMA_DISABLE_IT(huart1.hdmarx, DMA_IT_TC | DMA_IT_HT | DMA_IT_TE);//关闭DMA 错误 传输一半 全部完成 中断

                __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);//使能 串口 空闲中断

                HAL_NVIC_SetPriority(USART1_IRQn, 1, 0);
                HAL_NVIC_EnableIRQ(USART1_IRQn);                        //使能中断

void USART1_IRQHandler(void)
{
        if((__HAL_DMA_GET_COUNTER(huart1.hdmarx) + RX_DATA[3]) == (RX_DATA_NUM - 6))
        {
                Flag_RX = 1;
        }
       
        __HAL_UART_CLEAR_IT(&huart1, UART_CLEAR_IDLEF);
       
        __HAL_DMA_DISABLE(huart1.hdmarx);
        huart1.hdmarx->Instance->CNDTR = RX_DATA_NUM;
        __HAL_DMA_ENABLE(huart1.hdmarx);                                //DMA接收数据 必须在 DMA 禁止 时 重新写入
}
Paderboy 回答时间:2015-7-30 16:52:30
多谢分享,学习了
沐紫 回答时间:2015-7-30 17:07:38
抱歉,图片右下角因为当时正在调整水印功能,覆盖了,楼主可以重新上传一下,麻烦了
Tension 回答时间:2015-7-30 17:12:44
我也差不多的逻辑,不过,我按HAL的回调方式写的。
wjjontheway 回答时间:2015-7-30 17:16:59
Tension 发表于 2015-7-30 17:12
我也差不多的逻辑,不过,我按HAL的回调方式写的。

按HAL的回调方式写,是指在HAL_UART_IRQHandler()中添加   
tmp1 = __HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE);
  tmp2 = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_IDLE);
  /* UART in mode Receiver ---------------------------------------------------*/
  if((tmp1 != RESET) && (tmp2 != RESET))
  {
    UART_XXX_IT(huart);
  }
这个意思吗?
还有我在while(1)里面一直HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);这样子会不会有额外的开销。这里一点我有点不确定,在while一直这样调用会不会有问题。
wjjontheway 回答时间:2015-7-30 17:20:05
DMA串口配置图片
DMA_串口配置.jpg
Brady 回答时间:2015-7-30 17:41:58
支持楼主, 这个功能很有用,
你好我好大家好! 回答时间:2015-7-30 21:44:52
谢楼主              
hooke 回答时间:2015-7-30 23:20:29
收藏个

lkl0305 回答时间:2015-7-30 23:38:23
多谢分享!!!
a208hlc-57526 回答时间:2015-7-31 00:21:27
空闲中断只有一个字节的时长吧?怎么能调节时长呢?modbus里规定的好像是3.5字节
mark0668 回答时间:2015-7-31 01:23:53
多谢分享!!!
wjandsq 回答时间:2015-7-31 09:32:11
这种方式弥补ST串口没有接收FIFO的缺陷,赞一个!
JackieLaura 回答时间:2015-7-31 10:04:31
谢谢楼主分享。。。水印透明的还是好点。。。
smare 回答时间:2015-7-31 20:27:15
正需要这个。明天测试一下楼主的方法。谢谢

所属标签

相似问题

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版