版主,请教个uart接收中断的问题

chokee   2009-3-23 17:58 楼主
                                 问一下,我现在是一个byte接收产生一次接收中断 。 但是这样中断进入太频繁,那我打算比方8个bytes接收进入一次中断, 是不是此时我必须要用DMA 的方式,多缓冲器 ?  

回复评论 (38)

是的,必须要用DMA的方式

                                 “多缓冲器”是什么意思?每个缓冲器有多大?
点赞  2009-3-23 18:07

如果uart有FIFO加上有超时功能就太好了

ATMEL的ARM7就有,用着很爽,省了我一个定时器

不知STM能不能加上去呢?呵呵
点赞  2009-3-23 19:07

usart1的RX 的dma通道是channle5

现在我用DMA方式来进行USART1的数据接收。

我想请问的是: 我最终接收判断应该是在DMA中断里面做的喽,因为DMA_TC表示DMA传输指定的buffer大小完成了,对吧。
那这样的话,我USART1的接收中断还要打开吗?

我现在测试的代码里面是打开的,但是我在DMA中断DMA1_FLAG_TC5=1的情况下,并没有在RX的buffer中看到我从串口发出来的数据,这是为什么呢?

我这样做有问题吗? : 请看下面代码

DMA中断:=========================
void DMA1_Channel5_IRQHandler(void)
{
  if(DMA_GetFlagStatus(DMA1_FLAG_TC5) != RESET)
  {
    RxCounter = 0;
  }
  
  if(DMA_GetFlagStatus(DMA1_FLAG_GL5) != RESET)
  {
    
  }
  
  if(DMA_GetFlagStatus(DMA1_FLAG_HT5) != RESET)
  {
    
  }
}

USART1中断:=====================
void USART1_IRQHandler(void)
{
  if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
  {
    /* Read one byte from the receive data register */
    RxBuffer1[RxCounter++] = USART_ReceiveData(USART1);         

    if(RxCounter == NbrOfDataToRead)
    {
      /* Disable the USART2 Receive interrupt */
      //USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
      RxCounter = 0;
    }
  }
}

DMA 初始化: =========================
DMA_DeInit(DMA1_Channel5);
  DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;
  DMA_InitStructure.DMA_MemoryBaseAddr = (u32)RxBuffer1;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
  DMA_InitStructure.DMA_BufferSize = TxBufferSize1;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(DMA1_Channel5, &DMA_InitStructure);

main函数中的功能使能:=================
USART_InitStructure.USART_BaudRate = 115200;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
 
  USART_Init(USART1, &USART_InitStructure);
  USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
  DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
  DMA_Cmd(DMA1_Channel5, ENABLE);
  USART_Cmd(USART1, ENABLE);

点赞  2009-3-24 11:52

user manual上写--->

在DMA中断程序里,USART1_CR3寄存器的DMAR位应该被软件清零。
这句如果我DMAR清零了,那么接收时的DMA模式就被禁止了啊。这句没想通。

另外,关于DMA中断函数,我参考了I2C的DMA的中断函数,现在DMA中断这么修改的:
void DMA1_Channel5_IRQHandler(void)
{
  u32 i;
  
  if(DMA_GetFlagStatus(DMA1_FLAG_TC5) != RESET)
  {
    DMA_ClearFlag(DMA1_FLAG_TC5);
    //USART1->CR3 = 0<<6;
  }
  
  if(DMA_GetFlagStatus(DMA1_FLAG_GL5) != RESET)
  {
    DMA_ClearFlag( DMA1_FLAG_GL5);
  }
  
  if(DMA_GetFlagStatus(DMA1_FLAG_HT5) != RESET)
  {
    DMA_ClearFlag( DMA1_FLAG_HT5);
  }
}

但是有个问题,我向串口第一次发一组数据,会进入DMA中断,当我再次发送的时候,便不再进入DMA中断了,我期间并没有做什么关闭DMA中断功能的代码啊。

当我main中初始化,使能DMA,USART1后,便一直while(1);了,实在是想不通。

麻烦版主赐教一下。
点赞  2009-3-24 15:09

再次发送的时候,也需要再次初始化DMA

请看下面这个文档的第14~19页:
相关链接:http://www.stmicroelectronics.com.cn/mcu/images/STM32_Technical_Slide_PDF.pdf
点赞  2009-3-24 20:50

谢谢香主答复

噢,这样,那我就用循环模式,这样比较方便。


但是我还是想请教一下版主,4楼的问题。
点赞  2009-3-25 10:18

没有看明白4楼的问题,请再叙述一遍

                                 谢谢
点赞  2009-3-25 10:21

好的

现在是这样的: 我USART中断接受本来是一个byte产生一次接收中断。 现在我要用DMA方式来进行USART的接收。 所以我现在启动的USART_DMA_RX的功能,我用的是3210E的开发板,USART1。

例子库的USART DMA INTERRUPT的例子是用的DMA发送功能。 所以我在此基础上稍作修改,改成DMA接收的功能。 我设定的RXBuffer大小是24个字节。 

我的问题是:
1)初始化的时候,我还需要使能USART1_RX的中断功能吗? 因为我的理解是:USART1_RX的中断启动应该是不需要了。

因为USART1_DR已经跟DMA关联了,当有了DR,RXNE置位后,这个DR的数据会自动传输到RXBuffer上去,一直到24个字节收满后,DMA的TCIF5会置位,进入DMA中断服务程序,此时我应该在RXBuffer上看到这收到的24个字节,是吗?

但是现在当我发送24个字节,从PC到USART1后,进入了DMA中断,并且是TCIF5=1,但是RXBuffer我看到的都是00,这是为何?

2)另外,昨天我说DMA进了一次中断后,就再也不进,那是因为设置了普通模式,所以今天我改成了循环模式,是可以反复进DMA中断了。 但是此时我看到我的串口助手在连续不停的收到数据,即使我debug在暂停状态。
点赞  2009-3-25 10:31

使用DMA操作USART

关于第1个问题:如果使用DMA方式接收,不需要再使能USART1_RX的中断功能。至于RXBuffer中看到的都是00,请检查DMA初始化是否正确,尤其是设备地址。

关于第2个问题:debug在暂停状态时,PC在连续不停的收到数据,是否是因为你发送很多数据并被PC接收到缓冲区,Debug暂停时PC还没有刷新显示完,耐心等一会儿看看。
点赞  2009-3-25 10:47

我看了下,没有什么设置错误啊

1)
这就是我的DMA初始化================================
void DMA_Configuration(void)
{
  DMA_InitTypeDef DMA_InitStructure;

  /* DMA1 Channel4 (triggered by USART1 Tx event) Config */
  DMA_DeInit(DMA1_Channel5);  // usart1 rx dma use dmachannle5
  DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;
  DMA_InitStructure.DMA_MemoryBaseAddr = (u32)RxBuffer1;  //size is 24
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
  DMA_InitStructure.DMA_BufferSize = RxBufferSize1;       //size is 24
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  //DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(DMA1_Channel5, &DMA_InitStructure);
}

DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
  DMA_Cmd(DMA1_Channel5, ENABLE);

这是USART1的:=================================
USART_InitStructure.USART_BaudRate = 115200;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  USART_InitStructure.USART_StopBits = USART_StopBits_1;
  USART_InitStructure.USART_Parity = USART_Parity_No;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
 
  USART_Init(USART1, &USART_InitStructure);
  USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
  //USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
  USART_Cmd(USART1, ENABLE);

2)我PC只向USART1发了一次24个字节,但是PC接收到的都是好几万次了,导致我的串口助手崩溃了。并且接收到的都是00
点赞  2009-3-25 11:42

你选的是DMA循环模式,请先在DMA完成中断中计数

                                 例如计数5次后停止DMA传输。如果不停止DMA传输,当然PC端会不断地收到数据了。
点赞  2009-3-25 11:52

香主的意思是在DMA中断出来的时候我要DISABLE DMA 功能?

                                 如果是这样的话,还是要后面再打开DMA功能,那对我来说,只能在DMA中断函数里面去进行ENABLE/DISABLE  DMA 了
点赞  2009-3-25 13:21

香主能否看一下我11楼的代码和下面的DMA中断程序

这是我的DMA中断处理
void DMA1_Channel5_IRQHandler(void)
{
  u32 i;

  if(DMA_GetFlagStatus(DMA1_FLAG_TC5) != RESET)
  {
    //RxBuffer1[RxCounter++] = USART_ReceiveData(USART1);    
  /*  for(i=0;i<24;i++)
      hjh=RxBuffer1;
    
      /* Disable the USART2 Receive interrupt */
      //USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);*/
    DMA_ClearFlag(DMA1_FLAG_TC5);
    //USART1->CR3 = 0<<6;
  }
  
  if(DMA_GetFlagStatus(DMA1_FLAG_GL5) != RESET)
  {
    DMA_ClearFlag( DMA1_FLAG_GL5);
  }
  
  if(DMA_GetFlagStatus(DMA1_FLAG_HT5) != RESET)
  {
    DMA_ClearFlag( DMA1_FLAG_HT5);
  }

}
点赞  2009-3-25 13:26

6楼给的文档中已经说明了如何重新初始化DMA通道

                                 我在12楼的建议是针对DMA循环模式。
点赞  2009-3-25 13:29

我就是说的你的12楼的建议

首先我想问一下,我这样理解对不对?
比如现在我PC要发24个字节的数据。 对DMA中断来说,我设置的是全部长度接收完产生一次中断,那么DMA中断应该就进入一次,当我进入这个DMA中断后,if(DMA_GetFlagStatus(DMA1_FLAG_TC5) != RESET) 肯定是1,因为TC5这时候应该是1,那么进入这个if里面,我应该可以在原来设置的RXBuffer中看到接收到的24个字节的数据。 

其次,你说DMA完成中断中计数。并且计数值到了后要停止DMA传输,这个停止是不是就是DISABLE DMA ? 如果是的话,那我什么时候去打开呢? 如果DISABLE了,我以后的数据怎么进入?  因为是中断触发的,所以如果要打开,只能DMA中断函数了? 那么这个关闭还有什么意义呢?

我贴出来的代码,希望版主能check一下,我是根据例子代码去修改的USART DMA接收功能。 我这么设置对不对? 如果是对的,为何现在进入DMA中断了,TC5=1了,RXBuffer里面还都是00(我PC发送的数据是1234...)
点赞  2009-3-25 13:49

我现在buffer里面是收到了

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; 这句话改了一改

我现在还是用循环模式,并且DIR改了后,也不会导致之前我说的PC狂接到数据了。 这是我自己的问题,是我不好。

但是现在还有一个问题,就是如果发送超时了,怎么办,我看过论坛里面的一些帖子,版主建议过使用TIM来定时,但问题现在我TIM都已经用掉了,没有空闲TIM, Systick也用掉了。 那我这个超时处理如何处理?

点赞  2009-3-25 14:22

如果设置为循环模式,你会在每个循环周期结束时收到一次

因为没有循环次数控制寄存器,所以你需要在每次处理DMA完成中断中计数,当到达规定的循环次数时,关闭DMA通道,即Disable。如果不用循环模式,则完成一次DMA传输后(在你的设置中是24字节),将收到一次DMA完成中断,同时DMA通道会自动停止,不再循环。


还有你的DMA初始化有问题,既然是DMA接收,为什么是从存储器至设备的DMA传输?总不能把发送的例子直接抄过来就用于接收吧?
点赞  2009-3-25 14:32

香主,我们的理解有点差异

我们来捋一下:
我现在测试的时候是24个字节的长度buffer,循环模式,当我PC发送24个字节,没问题。
当我PC发送3个字节,要发8次进入一次DMA中断。这个没问题,因为长度是24个字节,3个字节没满长度值,所以前几次没进,无所谓。

现在如果我发送25个字节(我的消息长度不是唯一的,所以牵涉到超时的问题),那么肯定由于是循环模式,所以前面的数据会被覆盖。 所以对我来说,没有规定的循环次数。 

所以我想知道,超时问题如何解决、? (我看到论坛有说用TIM定时的,但是我TIM现在都占用了。有没有不耗其他资源的方法?)

点赞  2009-3-25 15:33

记得以前讨论过这个问题

你的数据包中总应该有一个字段标识了数据包的长度,你应该把这个字段放在数据包中一个固定的位置,即使最短的数据包也应该有这个字段。

这样STM32端的接收过程需要分解为2个阶段,第1个阶段是用DMA或中断接收包括数据包长度的若干个字节,第2个阶段是用DMA接收剩余的字节。这种方式下,第1个阶段的接收长度始终是已知的,在收到第1个阶段的数据后,第2个阶段的接收长度也是已知了,根本不需要什么超时处理,也不需要使用DMA的循环模式。
点赞  2009-3-25 15:53
12下一页
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复