stm32f103/105 串口DMA接收不定长数据,不使用空闲中断

时间:2024-03-14 15:43:41

  最近使用一个2.4G无线模块,做主机从机之间通信,取代原先485有线通信,由于使用的是串口透传模块,简单改了一下标志位,就开始调试程序了。

  1、串口助手连无线模块,能发送接收                                                                   (正常)

   2、主电脑分别当主从机,测试电路板modbus是否正常(不带模块测试)           (正常)

   3、无线模块分别接到主从机上测试                                                                         (不正常)   

发现不正常有些慌了,单独测试都没有问题,无线模块就是串口透传,能有啥问题。果断开始debug。

调试开始---------------------------------------------------------------> 一天过去了,没有找到问题。

第二天调试开始--------------------->发现问题了。

串口接收采用的是DMA+空闲中断,在中断中设置断点。

void USART2_IRQHandler(void)
{
	u8 res=0;	
	u8 cout=0; 
	u8 i = 0;

	if(USART_GetITStatus(USART2, USART_IT_IDLE) == SET){  //接收中断	
	
		USART_ClearITPendingBit(USART2, USART_IT_IDLE);  

		res = USART2->SR;
		res = USART2->DR;																					//清usart_IT_IDLE 标志
		

		DMA_Cmd(DMA1_Channel6,DISABLE);    												 			// 关闭DMA
		cout = MB_UART_RX_LEN -  DMA_GetCurrDataCounter(DMA1_Channel6);      			// 得到接受数据个数
		MB_Uart_Rx[cout] = '\0';
		
		if(MB_Uart_Rx[1] == 0x03)																								 // 保持寄存器
		{
			for(i = 0;i<cout;i++)
			{
				MB_Uart_Rx_hold[i] = MB_Uart_Rx[i];
			}
			mb_receive_hold_flag = 1;
		}

		
		
		DMA_ClearFlag(DMA1_FLAG_GL6 | DMA1_FLAG_TC6 | DMA1_FLAG_TE6 | DMA1_FLAG_HT6); // 清标志

		DMA1_Channel6->CNDTR=MB_UART_RX_LEN;       													 //重新设置接收个数 
		DMA_Cmd(DMA1_Channel6,ENABLE);  													 //开启DMA
		if(USART_GetITStatus(USART2, USART_IT_PE | USART_IT_FE | USART_IT_NE) != RESET)//出错
		{
		USART_ClearITPendingBit(USART2, USART_IT_PE | USART_IT_FE | USART_IT_NE);
		}
		
		mb_receive_flag = 1;
//		receive_flag = 1;           															//接收数据1
//		recflag = 1;
//		
		
	 }		
 
} 

stm32f103/105 串口DMA接收不定长数据,不使用空闲中断

发现全速运行时进入断点,每次只能接收到一个或者2个字节数据,从机反回来的是7个字节的数据,说明接收出现了丢包现象。

分析:

空闲中断+DMA 接收程序在好几个产品中应用,而且有线485也能正常使用,所以怀疑是模块的问题,之前用过其他家的无线模块,遇到过"沾包的现象"即两个数据包发送的时间间隔较短,无线模块认为是一帧数据,从断点接收1-2个字节的数据判断,不是沾包这种情况。进入中断的条件是发生一个字节时间长度空闲,则进入中断。我这里使用的波特率是9600,大概计算一下:

      1s 9600个bit位 一个数据大概在10个bit左右     stm32f103/105 串口DMA接收不定长数据,不使用空闲中断 传输一个字节数据大概在1ms 那么空闲中断的超时时间就是1ms左右。果断查找无线模块的数据手册,--------------------------------------------------------------->时间过去好久,翻遍手册没有找到。多亏前些天加了模块原厂的销售,经过沟通-------------------------------------------------------------->无线模块字节与字节直接的时间间隔在1-5ms之间不确定,不能改下。坑爹啊,小厂的无线模块靠不住啊,技术支持不能解决问题,偷懒DMA+空闲中断用不了,要了老命。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

但是问题还是要解决的!!!!!!!

方案1、采用最原始的一个字节一中断,但是32个从机,一直发数据,那么就是一直进中断,效率极其低下

方案2、继续研究新的接收方法,继续专研技术(偷懒技术)

经过一段时间研发论证分析翻了一堆帖子。总结了新的方法。

方法如下:

     串口DMA接收+超时查询 (不用中断)

在原先的程序上小改动一下即可。程序如下

void uart2_init(u32 bound)
{
    //GPIO端口设置
    GPIO_InitTypeDef GPIO_InitStructure;
		USART_InitTypeDef USART_InitStructure;
		NVIC_InitTypeDef NVIC_InitStructure;
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);	//使能USART2,GPIOA时钟
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//使能USART2,GPIOA时钟
	 	USART_DeInit(USART2);  //复位串口2
	 //USART2_TX   PA.2
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA.2
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
    GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA2
   
    //USART2_RX	  PA.3
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);  //初始化PA3
   //USART 初始化设置
		USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;
		USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
		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(USART2, &USART_InitStructure); //初始化串口
   //Usart1 NVIC 配置
		NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级1
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;		//子优先级0
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
		NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
   
//		USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);//总线空闲 产生中断
		USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE);
//		USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);

		USART_Cmd(USART2, ENABLE);                    //使能串口 



   /*uart2DMA配置*/
	DMA_DeInit(DMA1_Channel6);

   DMA_Initstructure.DMA_PeripheralBaseAddr =  (u32)(&USART2->DR);;
   DMA_Initstructure.DMA_MemoryBaseAddr     = (u32)MB_Uart_Rx;
   DMA_Initstructure.DMA_DIR = DMA_DIR_PeripheralSRC;
   DMA_Initstructure.DMA_BufferSize = MB_UART_RX_LEN;
   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_High;
   DMA_Initstructure.DMA_M2M = DMA_M2M_Disable;
   DMA_Init(DMA1_Channel6,&DMA_Initstructure);
  
	DMA_ITConfig(DMA1_Channel6, DMA_IT_TC, ENABLE);
	DMA_ITConfig(DMA1_Channel6, DMA_IT_TE, ENABLE);

	/* Enable USART2 DMA RX request */
	USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE);
   //启动DMA
   DMA_Cmd(DMA1_Channel6,ENABLE);








}


void wl24GreceiveData(void)
{
		u8 cout=0; 
		u8 i=0; 

		DMA_Cmd(DMA1_Channel6,DISABLE);    												 								// 关闭DMA
		cout = MB_UART_RX_LEN -  DMA_GetCurrDataCounter(DMA1_Channel6);      			// 得到接受数据个数
		MB_Uart_Rx[cout] = '\0';
		
		if(MB_Uart_Rx[1] == 0x03)																								 	// 保持寄存器
		{
			for(i = 0;i<cout;i++)
			{
				MB_Uart_Rx_hold[i] = MB_Uart_Rx[i];
			}
			mb_receive_hold_flag = 1;
		}
			MB_Uart_Rx[1] = 0;

		DMA_ClearFlag(DMA1_FLAG_GL6 | DMA1_FLAG_TC6 | DMA1_FLAG_TE6 | DMA1_FLAG_HT6); // 清标志

		DMA1_Channel6->CNDTR=MB_UART_RX_LEN;       													 //重新设置接收个数 
		DMA_Cmd(DMA1_Channel6,ENABLE);  													 //开启DMA


}

 

 

stm32f103/105 串口DMA接收不定长数据,不使用空闲中断

超时时间设置成100MS 能成才接收数据 。超时读取数据,能正常玩耍了。

最后说一句,使用小厂家的模块虽然便宜,但是即使支持不好,有各种各样的bug,出问题自己解决,如果没有实力精力时间,不要碰便宜的模块,便宜没好货,好货不便宜。

研究无线modbus去了。