打印
[应用相关]

想问一个串口DMA的问题

[复制链接]
925|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
我是MT|  楼主 | 2015-4-18 20:43 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
问一个关于串口DMA的问题,感觉和串口接收缓冲区有关。
最近在做一个项目,要用串口接收固定40个字节的数据,每隔10ms40个字节的数据。
但是接收数据之前要用串口发命令给设备,请求这些数据,发送请求的时候,设备也会回复几个字节的应答。

当发送完请求的命令后,收到指定的回复(3个字节)后,每隔10ms就会有40个字节的需要的数据通过串口发回来。
但是前面发送的命令和回复的应答都是4、5个字节,所以我没用DMA,而是直接用串口接收中断。
当在串口接收终端中接收到正确的回复后,马上开启DMA模式,用DMA接收数据,DMA接收完成中断一次,就接收到40个字节。
比如发送命令 char command[ ] = { ‘s','e','n','d' }  4个字节, 接收到  char  respond[] = { ' s' , 'e', 'n', 'd', 'o','k'} 6个字节,
然后设备就一直发送40字节的数据。
问题来了,我把DMA的计数设置成40,然后开启的时候,第一次的DMA中断的第一个字节收到的是之前应答的最后一个字节’k',然后才是40字节的数据, 所以接收到39字节数据时就DMA中断了,最后一个字节又到下一次DMA中断才收到。
后面的所有DMA中断都会因为这个原因而错开1字节。但是我在串口中断中已经接收了最后一个字节了(确实接收到了,usart_receive 了最后一个字节了),为什么开启DMA时还会接收到最后一个字节 (我也尝试过开dma前执行一遍 usart_receive(USART),也不行) 。
可能是最后一个字节还留在缓冲区里(我猜想)?,所以有什么办法可以清空串口接收缓冲区吗?
后面我的解决办法是第一次开DMA时,把DMA计数加1 ,即41, 然后在中断中重新设置DMA计数为40,这样是把问题解决了,就是第一组数据丢弃了。
不过我还是想知道是什么原因,是缓冲区问题吗,还是别的?所以来这里求助一下,看看有没有遇到相同问题的,或者有对stm32了解深入一点的大神解答下,谢谢!!
沙发
后会无期1| | 2015-4-18 20:44 | 只看该作者
经楼主这么一分析,我也认为可以是缓冲区的问题,要不这样,用串口收完"K"后再开DMA接收,应该数据就全了吧

使用特权

评论回复
板凳
fhguo1990| | 2015-4-18 20:46 | 只看该作者
能不能看看你的串口配置?DMA的配置。

使用特权

评论回复
地板
我是MT|  楼主 | 2015-4-18 20:46 | 只看该作者
后会无期1 发表于 2015-4-18 20:44
经楼主这么一分析,我也认为可以是缓冲区的问题,要不这样,用串口收完"K"后再开DMA接收,应该数据就全了吧 ...

串口已经收完K了,我还用另一个串口发回来看过了。

使用特权

评论回复
5
我是MT|  楼主 | 2015-4-18 20:47 | 只看该作者
fhguo1990 发表于 2015-4-18 20:46
能不能看看你的串口配置?DMA的配置。


//这个是DMA的配置
void DMA_Configuration()
{
        DMA_InitTypeDef DMA_InitStructure;
        /* USARTy_Tx_DMA_Channel (triggered by USARTy Tx event) Config */
        
        DMA_DeInit(USARTy_Tx_DMA_Channel);
        DMA_InitStructure.DMA_PeripheralBaseAddr = USARTy_DR_Base;
        DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)tmp;
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
        DMA_InitStructure.DMA_BufferSize = 0x40;
        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(USARTy_Tx_DMA_Channel, &DMA_InitStructure);
        /* USARTy RX DMA1 Channel (triggered by USARTy Rx event) Config */
        DMA_DeInit(USARTy_Rx_DMA_Channel);  
        DMA_InitStructure.DMA_PeripheralBaseAddr = USARTy_DR_Base;
        DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer_rx;
        DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
        DMA_InitStructure.DMA_BufferSize = RxBufferSize;
        DMA_Init(USARTy_Rx_DMA_Channel, &DMA_InitStructure);
}

使用特权

评论回复
6
我是MT|  楼主 | 2015-4-18 20:47 | 只看该作者
fhguo1990 发表于 2015-4-18 20:46
能不能看看你的串口配置?DMA的配置。

这个是串口配置。
void My_Usart_Init()
{
      USART_InitTypeDef USART_InitStructure;
      
      RCC_Configuration();
      DMA_Configuration();
      GPIO_Configuration();
      NVIC_Configuration();
  
      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;
      
      //³õʼ»¯´®¿Ú2
      USART_Init(USARTy,&USART_InitStructure);   
      //ʹÄÜ´®¿Ú½ÓÊÕÖжÏ
      USART_ITConfig(USARTy,USART_IT_RXNE ,ENABLE);
      //ʹÄÜ´®¿Ú2
      USART_Cmd(USARTy,ENABLE);
      //ʹÄÜ´®¿ÚYµÄDMA
      USART_DMACmd(USARTy,USART_DMAReq_Tx | USART_DMAReq_Rx,ENABLE);
//      DMA_ITConfig(USARTy_Tx_DMA_Channel, DMA_IT_TC, ENABLE);
//      Periph_NVIC_config(USARTy_Tx_DMA_IRQn,0,5,ENABLE);
           
        /////* Enable the USARTz Interrupt */     
      DMA_ITConfig(USARTy_Rx_DMA_Channel, DMA_IT_TC, ENABLE);
      Periph_NVIC_config(USARTy_Rx_DMA_IRQn,0,3,ENABLE);

使用特权

评论回复
7
我是MT|  楼主 | 2015-4-18 20:48 | 只看该作者
我是MT 发表于 2015-4-18 20:47
这个是串口配置。
void My_Usart_Init()
{

刚刚一直用快速回复栏,格式没搞好。 重复上一贴。
这个是串口的配置。
因为有时要改串口,所以代码里面用了宏,但是这些定义应该是没问题的,因为都能正常使用了(只是多一位)。我想没有必要贴上来吧。
void My_Usart_Init()
{
      USART_InitTypeDef USART_InitStructure;
      
      RCC_Configuration();
      DMA_Configuration();
      GPIO_Configuration();
      NVIC_Configuration();
  
      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(USARTy,&USART_InitStructure);   
      //开接收中断
      USART_ITConfig(USARTy,USART_IT_RXNE ,ENABLE);
      //使能串口
      USART_Cmd(USARTy,ENABLE);
      //使能串口DMA
      USART_DMACmd(USARTy,USART_DMAReq_Tx | USART_DMAReq_Rx,ENABLE);
//      DMA_ITConfig(USARTy_Tx_DMA_Channel, DMA_IT_TC, ENABLE);
//      Periph_NVIC_config(USARTy_Tx_DMA_IRQn,0,5,ENABLE);
           

        /////* Enable the USARTz Interrupt */     
      DMA_ITConfig(USARTy_Rx_DMA_Channel, DMA_IT_TC, ENABLE);     使能DMA接收中断
      Periph_NVIC_config(USARTy_Rx_DMA_IRQn,0,3,ENABLE);   //开NVIC中断,自己写的函数,这样代码好看点。

使用特权

评论回复
8
我是MT|  楼主 | 2015-4-18 20:49 | 只看该作者
我的代码是这样的,先是开串口接收中断来接收回复,一旦接收到了第一个应带,马上开启一个定时器,当然串口接收的数据也会保存起来。当定时器到时间时(3ms),我就开始检查串口接收的数据(4、5字节)。
volatile char buffer_rx[60];
volatile u8 num = 0;
volatile u8 commandflag = 0;
void USART3_IRQHandler(void)
{   
        USART_ClearITPendingBit(USART3,USART_IT_RXNE);
        if( !commandflag ) {       //第一次接收到串口回复后开定时器,后面的字节直接跳过。
            commandflag = 1;
            TIM_SetCounter( TIM3, 0 );
            TIM_Cmd(TIM3,ENABLE);
        }

        buffer_rx[num++] = USART_ReceiveData(USART3);   //保存数据。
        
     
                //memset(RxBuffer1,0,RxBufferSize1);
}

volatile char commandWaitingFlag = 0;
/**
* @brief This function handles TIM2 global interrupt request by resuming the
* iNemoData task.
*/
void TIM3_IRQHandler( void ) {
  if(TIM_GetITStatus(TIM3, TIM_IT_Update))
  {
        TIM_ClearITPendingBit( TIM3, TIM_IT_Update );
        TIM_Cmd(TIM3,DISABLE);          //关定时器。
        commandflag = 0;                        //下次接收回复后开定时器。
        commandWaitingFlag = 1;    //   这个标志位用于在发送命令后,检查回复的有效性。
        printf("%d: %x %x %x\r\n", num, buffer_rx[0], buffer_rx[1], buffer_rx[2]);  //用USART1发送出回复,串口中断是USART3。这行可省略,调试用的。
        num = 0;
        GPIOA->ODR ^= 1 CNDTR = RxBufferSize + 1;            //这里其实是41,理想值当然是40啦
                USARTy_Rx_DMA_Channel->CMAR  = (uint32_t)buffer_rx;          //接收数据的buffer
                DMA_Cmd(USARTy_Rx_DMA_Channel,ENABLE);                        //开启串口DMA
        }
        res = true;
      } else {
        res = false;
      }
      return res;
}
嗯上面就是所有的代码了。还有,很感谢你们看完这些东西。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

28

主题

278

帖子

1

粉丝