【Bug】STM32串口空闲中断接收不定长数据异常

Bug

使用标准库配置STM32F103C8T6的串口1开启接收中断和空闲中断,通过空闲中断来判断数据发送是否结束,收到数据后切换板载LED灯所接引脚电平,发现LED出现三种情况,熄灭、微亮、正常亮,但是LED灯所接的GPIO引脚为PC13,配置PC13时为推挽输出模式,应该只有高低也就是亮灭两种情况,却出现了微亮的现象,具体串口代码配置如下:

c 复制代码
static void USART1_NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6;	
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

void USART1_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	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);
	
	USART1_NVIC_Configuration();
	
	USART_ITConfig(USART1, USART_IT_RXNE | USART_IT_IDLE, ENABLE);	
	
	USART_Cmd(USART1, ENABLE);	    
}


volatile u8 USART1_BUF[200] = {0};
volatile u8 USART1_BUF_LEN = 0;
volatile u8 USART1_REC_FLAG = 0;
void USART1_IRQHandler(void)
{
	static u8 res = 0;
	if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)
	{
		res = USART_ReceiveData(USART1);
		if(USART1_BUF_LEN < 200)
			USART1_BUF[USART1_BUF_LEN++] = res;
		
		USART_ClearITPendingBit(USART1,USART_IT_RXNE);	
	} 
	if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)
	{
		LED_Trig();			//切换LED灯电平状态函数
		memset((void*)USART1_BUF,0,USART1_BUF_LEN);
		USART_ClearITPendingBit(USART1,USART_IT_IDLE);	
	}
}

LED灯所接GPIO引脚配置函数:

c 复制代码
void LED_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);
}


void LED_Trig(void)
{
	static u8 led = 0;
	if(led == 0){
		GPIO_ResetBits(GPIOC,GPIO_Pin_13);
		led = 1;
	}
	else{                    
		GPIO_SetBits(GPIOC,GPIO_Pin_13);
		led = 0;
	}
}

Reason 1

通过Debug发现空闲中断并未进去,查看了串口1的配置寄存器发现空闲中断并未使能,而空闲中断的使能只有一条函数USART_ITConfig(USART1, USART_IT_RXNE | USART_IT_IDLE, ENABLE);,在仔细查看了该函数后发现,不能通过与运算来同时开启两个中断使能,因为该宏并不是以往像GPIO驱动一样,通过每个位来控制,就能直接通过与运算包含进去直接配置,每个宏的值都是单独的:

c 复制代码
#define USART_IT_PE                          ((uint16_t)0x0028)
#define USART_IT_TXE                         ((uint16_t)0x0727)
#define USART_IT_TC                          ((uint16_t)0x0626)
#define USART_IT_RXNE                        ((uint16_t)0x0525)
#define USART_IT_ORE_RX                      ((uint16_t)0x0325) /* In case interrupt is generated if the RXNEIE bit is set */
#define USART_IT_IDLE                        ((uint16_t)0x0424)
#define USART_IT_LBD                         ((uint16_t)0x0846)
#define USART_IT_CTS                         ((uint16_t)0x096A)
#define USART_IT_ERR                         ((uint16_t)0x0060)
#define USART_IT_ORE_ER                      ((uint16_t)0x0360) /* In case interrupt is generated if the EIE bit is set */
#define USART_IT_NE                          ((uint16_t)0x0260)
#define USART_IT_FE                          ((uint16_t)0x0160)

Resolution 1

因此需要分开进行中断使能,正确的中断使能应该是分开调用使能函数进行:

c 复制代码
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);	
	USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);	

Reason 2

第二个情况,LED灯出现三种亮灭状态的异常情况,明明只是配置成普通的GPIO引脚,却出现微亮的情况,说明程序出现了异常现象,然后再网上偶然看到一个空闲中断中读取串口数据的,在空闲中断中也加入了串口的接收数据读取程序就又正常了 ,一开始以为空闲中断没有数据需要读取所以就没有调用读取函数了,没想到会异常在这里,具体的原因还未知,怀疑是因为没有去读取数据导致中断异常,同时我也发现了串口中断中必须去读取数据,不管数据有用无用,或者是担心保存数据的缓冲区溢出而加入了长度限制或者是其他操作标志位限制而不去读取数据,都会导致这种情况,所以无论什么情况都要去读取串口接收数据,也就是调用USART_ReceiveData()函数。


Resolution 2

在串口中断中,无论是接收中断还是空闲中断,都要去读取串口数据:

c 复制代码
volatile u8 USART1_BUF[200] = {0};
volatile u8 USART1_BUF_LEN = 0;
volatile u8 USART1_REC_FLAG = 0;
void USART1_IRQHandler(void)
{
	static u8 res = 0;
	if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)
	{
		res = USART_ReceiveData(USART1);
		if(USART1_BUF_LEN < 200)
			USART1_BUF[USART1_BUF_LEN++] = res;
		
		USART_ClearITPendingBit(USART1,USART_IT_RXNE);	
	} 
	if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)
	{
		USART_ReceiveData(USART1);	//一定要读
		LED_Trig();
		memset((void*)USART1_BUF,0,USART1_BUF_LEN);
		USART_ClearITPendingBit(USART1,USART_IT_IDLE);	
	}
}

同时,程序也验证了串口的空闲中断能够接收数据不定长的情况,至于如何判断数据是否结束,主要通过起始位后如果全是1,说明没有数据进入,那么在差不多两三个字节都是全1就会认为数据已经发送结束产生空闲中断。

相关推荐
析木不会编程3 小时前
【小白51单片机专用教程】protues仿真独立按键控制LED
单片机·嵌入式硬件·51单片机
枯无穷肉7 小时前
stm32制作CAN适配器4--WinUsb的使用
stm32·单片机·嵌入式硬件
不过四级不改名6777 小时前
基于HAL库的stm32的can收发实验
stm32·单片机·嵌入式硬件
嵌入式科普7 小时前
十一、从0开始卷出一个新项目之瑞萨RA6M5串口DTC接收不定长
c语言·stm32·cubeide·e2studio·ra6m5·dma接收不定长
嵌入式大圣7 小时前
单片机UDP数据透传
单片机·嵌入式硬件·udp
云山工作室8 小时前
基于单片机的视力保护及身姿矫正器设计(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·毕设
嵌入式-老费8 小时前
基于海思soc的智能产品开发(mcu读保护的设置)
单片机·嵌入式硬件
qq_3975623110 小时前
MPU6050 , 设置内部低通滤波器,对于输出数据的影响。(简单实验)
单片机
艺术家天选10 小时前
STM32点亮LED灯
stm32·单片机·嵌入式硬件
向阳逐梦10 小时前
基于STM32F4单片机实现ROS机器人主板
stm32·单片机·机器人