稻草人+ 发表于 2019-5-21 16:39:50

stm32f743VIT6 + DMA空闲方式接收不定长度的字节


           最近使用stm32f743VIT6 DMA通过空闲中断接收不定长度的字节。有个现象很奇怪,我接收GPS下发的数据的时候,使用的串口2的DMA的空闲中断来接收,(一般情况下GPS发出来的数据都是每秒钟一帧,美帧数据大约几百个字节),要么接收到1个字节就空闲中断了,要么就根本接收不到。
然后我换成用PC机模拟GPS通过USB转TTL 的线下发数据到串口2,这个时候能完整无误的接收到数据,无论下发的数据字节有多少都能 完整无误的接收到。我把我的串口的配置和中断处理贴出来,希望有知道原因的大牛指点指点。谢谢

   说明一下, 以上我做实验都是通过串口2接收的数据,然后用串口3转发接收的数据到PC机上来观察的。

以下是   串口1 和串口2和串口3 的初始化配置:


   void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
static DMA_HandleTypeDef U1hdma_tx;
static DMA_HandleTypeDef U1hdma_rx;
   
    static DMA_HandleTypeDef U2hdma_tx;
static DMA_HandleTypeDef U2hdma_rx;
   
   
    static DMA_HandleTypeDef U3hdma_tx;
static DMA_HandleTypeDef U3hdma_rx;

GPIO_InitTypeDefGPIO_InitStruct;

RCC_PeriphCLKInitTypeDef RCC_PeriphClkInit;

       if(huart->Instance==USART1)
                {      
                USARTx_TX_GPIO_CLK_ENABLE();
            
                RCC_PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART16;
                RCC_PeriphClkInit.Usart16ClockSelection = RCC_USART16CLKSOURCE_D2PCLK2;
                HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInit);

                USARTx_CLK_ENABLE();

                DMAx_CLK_ENABLE();

                GPIO_InitStruct.Pin       = USARTx_TX_PIN;
                GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
                GPIO_InitStruct.Pull      = GPIO_PULLUP;
                GPIO_InitStruct.Speed   = GPIO_SPEED_FREQ_VERY_HIGH;
                GPIO_InitStruct.Alternate = USARTx_TX_AF;

                HAL_GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStruct);

                GPIO_InitStruct.Pin = USARTx_RX_PIN;
                GPIO_InitStruct.Alternate = USARTx_RX_AF;

                HAL_GPIO_Init(USARTx_RX_GPIO_PORT, &GPIO_InitStruct);
            
                U1hdma_tx.Instance               = USARTx_TX_DMA_STREAM;
                U1hdma_tx.Init.Request             = USARTx_TX_DMA_CHANNEL;
                U1hdma_tx.Init.Direction         = DMA_MEMORY_TO_PERIPH;
                U1hdma_tx.Init.PeriphInc         = DMA_PINC_DISABLE;
                U1hdma_tx.Init.MemInc            = DMA_MINC_ENABLE;
                U1hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
                U1hdma_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
                U1hdma_tx.Init.Mode                = DMA_NORMAL;
                U1hdma_tx.Init.Priority            = DMA_PRIORITY_LOW;
                U1hdma_tx.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
                U1hdma_tx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
                U1hdma_tx.Init.MemBurst            = DMA_MBURST_INC4;
                U1hdma_tx.Init.PeriphBurst         = DMA_PBURST_INC4;

                HAL_DMA_Init(&U1hdma_tx);
            
                __HAL_LINKDMA(huart, hdmatx, U1hdma_tx);

                HAL_NVIC_SetPriority(USARTx_DMA_TX_IRQn, 3, 1);
                HAL_NVIC_EnableIRQ(USARTx_DMA_TX_IRQn);

                HAL_NVIC_SetPriority(USARTx_IRQn, 3, 1);
                HAL_NVIC_EnableIRQ(USARTx_IRQn);   
            }
       if(huart->Instance==USART2)
             {   
               UsartG_RX_GPIO_CLK_ENABLE();

                RCC_PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2;
                RCC_PeriphClkInit.Usart234578ClockSelection = RCC_USART2CLKSOURCE_D2PCLK1;
                HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInit);

               __HAL_RCC_USART2_CLK_ENABLE();

               __HAL_RCC_DMA1_CLK_ENABLE();

                GPIO_InitStruct.Pin       = UsartG_TX_PIN;
                GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
                GPIO_InitStruct.Pull      = GPIO_PULLUP;
                GPIO_InitStruct.Speed   = GPIO_SPEED_FREQ_HIGH;
                GPIO_InitStruct.Alternate = UsartG_TX_AF;
                HAL_GPIO_Init(UsartG_TX_GPIO_PORT, &GPIO_InitStruct);
               
                GPIO_InitStruct.Pin = UsartG_RX_PIN;
                GPIO_InitStruct.Alternate = UsartG_RX_AF;
                HAL_GPIO_Init(UsartG_RX_GPIO_PORT, &GPIO_InitStruct);

            U2hdma_rx.Instance               = UsartG_RX_DMA_STREAM;
            U2hdma_rx.Init.Request             = UsartG_RX_DMA_CHANNEL;
            U2hdma_rx.Init.Direction         = DMA_PERIPH_TO_MEMORY;
            U2hdma_rx.Init.PeriphInc         = DMA_PINC_DISABLE;
            U2hdma_rx.Init.MemInc            = DMA_MINC_ENABLE;
            U2hdma_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
            U2hdma_rx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
            U2hdma_rx.Init.Mode                = DMA_NORMAL;
            U2hdma_rx.Init.Priority            = DMA_PRIORITY_HIGH;
            U2hdma_rx.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
            U2hdma_rx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
            U2hdma_rx.Init.MemBurst            = DMA_MBURST_INC4;
            U2hdma_rx.Init.PeriphBurst         = DMA_PBURST_INC4;

            HAL_DMA_Init(&U2hdma_rx);
               
            __HAL_LINKDMA(huart, hdmarx, U2hdma_rx);

                HAL_NVIC_SetPriority(UsartG_DMA_RX_IRQn, 0, 0);
                HAL_NVIC_EnableIRQ(UsartG_DMA_RX_IRQn);

                HAL_NVIC_SetPriority(UsartG_IRQn, 0, 0);
                HAL_NVIC_EnableIRQ(UsartG_IRQn);
             }
            
      if(huart->Instance==USART3)
            {   
                UsartX_TX_GPIO_CLK_ENABLE();

                RCC_PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART3;
                RCC_PeriphClkInit.Usart234578ClockSelection = RCC_USART3CLKSOURCE_D2PCLK1;
                HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInit);

                UsartX_CLK_ENABLE();
   
                DMAX_CLK_ENABLE();
   
                GPIO_InitStruct.Pin       = UsartX_TX_PIN;
                GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
                GPIO_InitStruct.Pull      = GPIO_PULLUP;
                GPIO_InitStruct.Speed   = GPIO_SPEED_FREQ_HIGH;
                GPIO_InitStruct.Alternate = UsartX_TX_AF;

                HAL_GPIO_Init(UsartX_TX_GPIO_PORT, &GPIO_InitStruct);

   
   
                U3hdma_tx.Instance               = UsartX_TX_DMA_STREAM;
                U3hdma_tx.Init.Request             = UsartX_TX_DMA_CHANNEL;
                U3hdma_tx.Init.Direction         = DMA_MEMORY_TO_PERIPH;
                U3hdma_tx.Init.PeriphInc         = DMA_PINC_DISABLE;
                U3hdma_tx.Init.MemInc            = DMA_MINC_ENABLE;
                U3hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
                U3hdma_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
                U3hdma_tx.Init.Mode                = DMA_NORMAL;
                U3hdma_tx.Init.Priority            = DMA_PRIORITY_LOW;
                U3hdma_tx.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
                U3hdma_tx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
                U3hdma_tx.Init.MemBurst            = DMA_MBURST_INC4;
                U3hdma_tx.Init.PeriphBurst         = DMA_PBURST_INC4;

                HAL_DMA_Init(&U3hdma_tx);
            
                __HAL_LINKDMA(huart, hdmatx, U3hdma_tx);
               
            HAL_NVIC_SetPriority(UsartX_DMA_TX_IRQn, 1, 1);
            HAL_NVIC_EnableIRQ(UsartX_DMA_TX_IRQn);

                HAL_NVIC_SetPriority(UsartX_IRQn, 1, 1);
                HAL_NVIC_EnableIRQ(UsartX_IRQn);   
      }      
}


       以下是串口2的中断处理:
注:GPS_UartHandle是处理处理GPS 接收数据的一个Handle ,也就是串口2

void Usart2_IRQHandler(void)
{
uint32_t tmp_flag = 0;
uint32_t temp;
//HAL_UART_IRQHandler(&GPS_UartHandle);
tmp_flag = __HAL_UART_GET_FLAG(&GPS_UartHandle,UART_FLAG_IDLE);                  // 这里是获取空闲标志
    if((tmp_flag ==SET))                                                            
{               
         __HAL_UART_CLEAR_IDLEFLAG(&GPS_UartHandle);                                          // 清除空闲标志      
          temp=__HAL_DMA_GET_COUNTER(GPS_UartHandle.hdmarx);                        // 获取接收缓存的剩余字节数
          HAL_UART_DMAStop(&GPS_UartHandle);                                                            /// 停止 串口2的DMA接收
          rx_len = (RXBUFFERSIZE - temp);                                                                     // 计算 接收到的数据长度   
         recv_end_flag = 1;                                                                                              // 置位接收完成标志,在main中提示处理接收到的数据
          temp =0;                  
}
    __HAL_UART_CLEAR_IT(&GPS_UartHandle,UART_CLEAR_PEF                              //清除 其他的中断标志 等
                                     |UART_CLEAR_FEF
                                     |UART_CLEAR_NEF
                                     |UART_CLEAR_OREF
                                 //|UART_CLEAR_IDLEF
                                    |UART_CLEAR_TCF
                                    |UART_CLEAR_LBDF
                                    |UART_CLEAR_CTSF
                                    |UART_CLEAR_RTOF
                                    |UART_CLEAR_WUF
                                    |UART_CLEAR_CMF
                                    |UART_CLEAR_TXFECF);
   
}


恳请 知道原因的大牛指点一二,谢谢


tgw860910 发表于 2019-5-21 16:39:51

空闲中断,只要连续的两个字节的等待时间超过一个bit的间隔就会产生中断,用电脑模拟的串口信号应该是很好的,两个字节之间间隔很短,但是实际从GPS模块出来的串口波形是不是两个字节很连贯就要你自己量一下波形看看了。
还有一个,你在串口中断处理函数里只有停止DMA的操作,怎么没有再次打开DMA的操作呢?不再次打开怎么能够接收新数据呢?

tanic 发表于 2019-5-21 17:15:55

本帖最后由 tanic 于 2019-5-21 17:21 编辑

说了多少次不要用空闲中断+DMA方式
能这么用的必须保证发送方也是DMA方式发送,且DMA优先级最高。否则一旦发送被打断,你接收端就认为空闲中断了,于是错了。
能用空闲+DMA的应用场景在于大数据传输之前,先通过简单串口协议交互,把数据信息准备好,然后启用DMA定长传输数据,传输完成后切换到普通模式,亦或者两个串口,一个走命令一个走大数据

tanic 发表于 2019-5-21 17:25:58

另外你的GPS能有多大精度,设备运动速度有多快,需要这么搞。别说节约CPU,大多数限制程序性能的是代码逻辑,很多时候CPU都在空跑。
串口中断接收数据放入fifo,再找合适的时机处理即可。

edmundlee 发表于 2019-5-21 19:47:28

DMA+IDLE中断不是问题
DMA的两个中断 TC及HT都要用上
这三个中断只应用作, 告诉你buf里有新来的数据, 而不能以IDLE中断来代替数据包的解析

稻草人+ 发表于 2019-5-22 09:13:03

tgw860910 发表于 2019-5-21 17:13
空闲中断,只要连续的两个字节的等待时间超过一个bit的间隔就会产生中断,用电脑模拟的串口信号应该是很好 ...

GPS是标准的串口输出,我看了两个字节是连贯的,另外我开DMA的操作是在 main里面对数据处理了以后再开的

稻草人+ 发表于 2019-5-22 09:15:40

tanic 发表于 2019-5-21 17:15
说了多少次不要用空闲中断+DMA方式
能这么用的必须保证发送方也是DMA方式发送,且DMA优先级最高。否则一旦 ...

接收不定长度的是字节不用DMA+空闲还有其他方式吗,主要是我通过USB转TTL 的线在PC上模拟下发数据的时候也是正常的,如果是DMA被中断了的话那么说这种方式应该是不正常或者不能够接收到完整的数据。

稻草人+ 发表于 2019-5-22 09:37:29

edmundlee 发表于 2019-5-21 19:47
DMA+IDLE中断不是问题
DMA的两个中断 TC及HT都要用上
这三个中断只应用作, 告诉你buf里有新来的数据, 而 ...

你的意思是说我再空闲中断的处理函数中判断DMA 通道的TC 中断标志是不是置位,如果置位才表示一帧数据接收完毕了对了?还要判断HT 这个中断标志,这个标志是半传输完成标志,?这个怎么操作,您有合适的例子吗,能给我发一个吗?   4094359@qq.com

稻草人+ 发表于 2019-5-22 09:38:21

tanic 发表于 2019-5-21 17:25
另外你的GPS能有多大精度,设备运动速度有多快,需要这么搞。别说节约CPU,大多数限制程序性能的是代码逻辑 ...

谢谢,中断接收放入FIFO 这个不错我可以试试。

秦秦秦 发表于 2019-5-22 10:04:05

tanic 发表于 2019-5-21 17:15
说了多少次不要用空闲中断+DMA方式
能这么用的必须保证发送方也是DMA方式发送,且DMA优先级最高。否则一旦 ...

空闲+DMA被打断也没有什么问题吧
页: [1] 2
查看完整版本: stm32f743VIT6 + DMA空闲方式接收不定长度的字节