32单片机串口数据接收、空闲IDLE中断详解

一、前提说明

一开始写单片机程序的时候不太清楚空闲中断这个东西,每次用串口接收数据,都要再开一个定时器,在定时器内进行倒计时,每次接收数据就重置计时时间,计时结束就触发中断,再判断所有接收的数据,当然这种方法也并不过时,因为不是所有单片机都有空闲中断这个东西的,空闲中断实际是为开发者串口接收数据提供了部分便利而已,无论是用定时器还是空闲中断原理实际都是一样的。另外我们要知道很多32单片机都是使用的Arm核心,而外设部分是单片机厂商自己设计的,所以各家的设计思想和实现方式也各有不同,切勿以固定思维去判断。

这里我只对比两款常用单片机的空闲中断,只是为了说明不同品牌实现空闲中断的方式也不同,但作用都是差不多的。

1、GD32单片机

设计思想:只在接收完一组数据之后触发一次空闲

用法1:

初始化直接打开接收中断和空闲中断,在一组串口数据接收完成就会触发一次空闲中断,然后在空闲中断内对数据进行处理,特别注意不但要清标志位还要再额外读一下数据寄存器。

用法2:

初始化关闭空闲中断,打开接收中断,在串口接收中断内开启空闲中断,在空闲中断内关闭空闲中断,这样就可以保证不会一直进入空闲中断,然后在空闲中断内对数据进行处理。

2、STM32单片机

设计思想:只在接收完一组数据之后触发一次空闲

用法1:

初始化直接打开接收中断和空闲中断,在一组串口数据接收完成就会触发一次空闲中断,然后在空闲中断内对数据进行处理,特别注意不但要清标志位还要再额外读一下数据寄存器。

注意:gd32单片机的方法二并不适用与stm32

3、其他单片机

另外还有一些单片机品牌连空闲中断都没有设计的,完全不考虑开发者的感受,这里抨击一下,我就不点名了。

二、示例代码

1、GD32

cpp 复制代码
//串口初始化
void DW_PortCfg(void)
{
	rcu_periph_clock_enable(RCU_GPIOB);
	rcu_periph_clock_enable(RCU_AF);
	
	gpio_init(DW_MODULE_PORT,DW_URXD_MODE,GPIO_OSPEED_50MHZ,DW_URXD_PIN);
	gpio_init(DW_MODULE_PORT,DW_UTXD_MODE,GPIO_OSPEED_50MHZ,DW_UTXD_PIN);
	
	rcu_periph_clock_enable(DW_UART_RCU);
	usart_deinit(DW_UART);
	usart_baudrate_set(DW_UART,DW_BUAD_RATE);
	usart_stop_bit_set(DW_UART,USART_STB_1BIT);
	usart_word_length_set(DW_UART,USART_WL_8BIT);
	usart_parity_config(DW_UART,USART_PM_NONE);
	usart_hardware_flow_rts_config(DW_UART, USART_RTS_DISABLE);
	usart_hardware_flow_cts_config(DW_UART, USART_CTS_DISABLE);
	usart_receive_config(DW_UART, USART_RECEIVE_ENABLE);
	usart_transmit_config(DW_UART, USART_TRANSMIT_ENABLE);
	usart_enable(DW_UART);
	usart_interrupt_enable(DW_UART,USART_INT_RBNE);
	nvic_irq_enable(DW_UART_IRQn,0,1);
}
//中断服务函数
void USART2_IRQHandler(void){
	if(usart_interrupt_flag_get(DW_UART,USART_INT_FLAG_RBNE)==SET){
		dwRecvChar(usart_data_receive(DW_UART));
		usart_interrupt_flag_clear(DW_UART,USART_INT_FLAG_RBNE);
		usart_interrupt_enable(DW_UART,USART_INT_IDLE);
	}
	else if(usart_interrupt_flag_get(DW_UART,USART_INT_FLAG_IDLE)){
		dwRecvFinish();
		usart_interrupt_flag_clear(DW_UART,USART_INT_FLAG_IDLE);
		usart_interrupt_disable(DW_UART,USART_INT_IDLE);
	}
}

2、STM32

cpp 复制代码
void uart_init(u32 bound){
  //GPIO端口设置
  GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
  
	//USART1_TX   GPIOA.9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
   
  //USART1_RX	  GPIOA.10初始化
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

  //Usart1 NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
  
   //USART 初始化设置

	USART_InitStructure.USART_BaudRate = bound;//串口波特率
	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(USART1, &USART_InitStructure); //初始化串口1
	
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
	USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//开启空闲中断
	USART_Cmd(USART1, ENABLE);
}

void USART1_IRQHandler(void)                	//串口1中断服务程序
{
		uint16_t data;  
        if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)  
        { 
            USART2_RX_BUF[length++] = USART2->DR & 0x0FF;    //接收数据
          
        }
        if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)  
        { 
  
            data = USART1->SR;  //清空空闲中断标志位操作
            data = USART1->DR;  
          
        }  
}
相关推荐
代码游侠5 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法
CODECOLLECT9 小时前
京元 I62D Windows PDA 技术拆解:Windows 10 IoT 兼容 + 硬解码模块,如何降低工业软件迁移成本?
stm32·单片机·嵌入式硬件
BackCatK Chen10 小时前
STM32+FreeRTOS:嵌入式开发的黄金搭档,未来十年就靠它了!
stm32·单片机·嵌入式硬件·freertos·低功耗·rtdbs·工业控制
全栈游侠12 小时前
STM32F103XX 02-电源与备份寄存器
stm32·单片机·嵌入式硬件
Lsir10110_12 小时前
【Linux】中断 —— 操作系统的运行基石
linux·运维·嵌入式硬件
深圳市九鼎创展科技15 小时前
瑞芯微 RK3399 开发板 X3399 评测:高性能 ARM 平台的多面手
linux·arm开发·人工智能·单片机·嵌入式硬件·边缘计算
辰哥单片机设计15 小时前
STM32项目分享:车辆防盗报警系统
stm32·单片机·嵌入式硬件
小龙报16 小时前
【51单片机】从 0 到 1 玩转 51 蜂鸣器:分清有源无源,轻松驱动它奏响新年旋律
c语言·数据结构·c++·stm32·单片机·嵌入式硬件·51单片机
范纹杉想快点毕业16 小时前
嵌入式与单片机开发核心学习指南——从思维转变到第一性原理的深度实践
单片机·嵌入式硬件
Industio_触觉智能16 小时前
瑞芯微RK3566开发板规格书,详细参数配置,型号EVB3566-V1,基于RK3566核心板SOM3566邮票孔封装
嵌入式硬件·开发板·rk3568·rk3566·核心板·瑞芯微