STM32 UART常用的3种中断接收

本文介绍STM32微控制器USART模块的配置方法及三种不同的数据接收完成判断方式,包括缓冲区满标志位法、空闲中断法与状态机法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#include "sys.h"
#include "usart.h"  
#include "main.h"


//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h" //ucos 使用  
#endif




//注意,读取USARTx->SR能避免莫名其妙的错误  
u8 USART1_RX_BUF[USART_RX_LEN];     //接收缓冲,最USART_RX_LEN个字节
u8 USART2_RX_BUF[USART_RX_LEN];      //接收缓冲,最USART_RX_LEN个字节
u8 USART1_TX_BUF[USART_TX_LEN]; //发送缓冲,最大USART_TX_LEN个字节
u8 USART2_TX_BUF[USART_TX_LEN]; //发送缓冲,最大USART_TX_LEN个字节
u8 USART1_RX_STA;       //接收完成状态标记  
u8 USART2_RX_STA;       //接收完成状态标记
u8 USART3_RX_STA;       //接收完成状态标记
//u8 USART1_Count = 0; //USART1接收计数器
//u8 USART2_Count = 0; //USART2接收计数器
u8 TX1_cn,TX1_no,RX1_cn,RX_no;
u8 TX2_cn,TX2_no,RX2_cn,RX2_no;
u8 u8Uart2_FSM;


void usart1_init(u32 baud)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// NVIC_InitTypeDef NVIC_InitStructure;


/* 第1步:打开GPIO和USART部件的时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);


/* 第2步:将USART Tx的GPIO配置为推挽复用模式 */
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);


/* 第3步:将USART Rx的GPIO配置为浮空输入模式
由于CPU复位后,GPIO缺省都是浮空输入模式,因此下面这个步骤不是必须的
但是,我还是建议加上便于阅读,并且防止其它地方修改了这个口线的设置参数
*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/*  第3步已经做了,因此这步可以不做
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
*/
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 第4步:配置USART参数
   - 波特率   = 115200 baud
   - 数据长度 = 8 Bits
   - 1个停止位
   - 无校验
   - 禁止硬件流控(即禁止RTS和CTS)
   - 使能接收和发送
*/
USART_InitStructure.USART_BaudRate = baud;
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 配置
//  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
// NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;//抢占优先级1
// NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
// NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
// NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
    
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
  /* 第5步:使能 USART, 配置完毕 */
USART_Cmd(USART1, ENABLE);
// CPU的小缺陷:串口配置好,如果直接Send,则第1个字节发送不出去
// 如下语句解决第1个字节无法正确发送出去的问题:
// 清发送完成标志,Transmission Complete flag 
USART_ClearFlag(USART1, USART_FLAG_TC); 
}




/*************************************************************************************************************************
* 函数 : void uart2_init(u32 baud) 
* 功能 : USART2做RS485接口使用,
* 参数 : 输入的波特率
* 返回 :
* 依赖 : 底层库函数
* 作者 : li_qcxy@126.com
* 时间 : 2016-12-9
* 最后修改时间 : 
* 说明 : PA1为接收发送使能脚
*************************************************************************************************************************/ 
void usart2_init(u32 baud)     //485通信串口初始化
{
  //GPIO端口设置
  GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
 
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能USART2,GPIOA时钟
  
//USART2_TX   GPIOA.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);//初始化GPIOA.2
   
  //USART2_RX  GPIOA.3初始化
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3
  //GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.3  

//USART2_RX_EN 使能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //PA1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA1
EN_USART2_RX(); //接收模式

  //Usart2 NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;//抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
  
  //USART 初始化设置
USART_InitStructure.USART_BaudRate = baud;//串口波特率
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); //初始化串口2
  USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启串口接受中断
  USART_Cmd(USART2, ENABLE);                    //使能串口2 
  USART2 -> CR1 = USART2 -> CR1;
}






//获取USART2当前接收计数器值
u32 UARTx_GetRxCnt(void)
{
return TX2_no;
}


/*************************************************************************************************************************
* 函数 : void USART1_IRQHandler(void) 
* 功能 : 采用缓冲区满标志位的方法判断数据接收完成
* 参数 :
* 返回 :
* 依赖 : 底层读写函数
* 作者 : li_qcxy@126.com
* 时间 : 2016-12-8
* 最后修改时间 : 
* 说明 :
*************************************************************************************************************************/
void USART1_IRQHandler(void)                 //串口1中断服务程序
{
u8 Res;
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntEnter();    
#endif
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART1); //读取接收到的数据
    USART1_RX_BUF[TX2_no++]=Res; 
     } 
else if(TX2_no == USART_RX_LEN) //接收缓冲区接收满,即接收完成
{
TX2_no = 0;
USART1_RX_STA = 1;
}
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntExit();    
#endif
}
/*************************************************************************************************************************
* 函数 : void USART2_IRQHandler(void) 
* 功能 : 采用空闲中断的方法判断数据接收完成
* 参数 :
* 返回 :
* 依赖 : 底层读写函数
* 作者 : li_qcxy@126.com
* 时间 : 2016-12-8
* 最后修改时间 : 
* 说明 :
*************************************************************************************************************************/
void USART3_IRQHandler(void)                 //串口2中断服务程序
{  
  u8 Res;
 if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
    Res =USART_ReceiveData(USART2);//(USART2->DR); //读取接收到的数据
  USART2_RX_BUF[TX2_no++]=Res;
}
else if((USART_GetITStatus(USART2, USART_IT_IDLE) != RESET)) //使用空闲中断判断接收不定长数据完成
{  
USART3_RX_STA = 1; //数据接收完成标志位
//
}
}


/*************************************************************************************************************************
* 函数 : void USART2_IRQHandler(void) 
* 功能 : 采用状态机的方法判断数据接收完成
* 参数 :
* 返回 :
* 依赖 : 底层读写函数
* 作者 : li_qcxy@126.com
* 时间 : 2016-12-8
* 最后修改时间 : 
* 说明 :
*************************************************************************************************************************/
void USART2_IRQHandler(void)             
{
 u8 u8Temp;
 /*****************发送中断************************************/
 if (USART_GetITStatus(USART2, USART_IT_TC) == SET)
 { 
  if(TX2_cn >= (TX2_no))
  {
  EN_USART2_TX();
  USART_ITConfig(USART2, USART_IT_TC, DISABLE);
   USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
   u8Uart2_FSM = U_FSM_ADR;
  }
  else
  {
  u8Temp = USART2_TX_BUF[TX2_cn++];
  USART2->DR = (u16)u8Temp;
  }
 }
 
 /******************接收中断*************************************/
 if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET)
 {
  u8Temp = (u8)USART2->DR;
  switch(u8Uart2_FSM)
  {
   case U_FSM_ADR:
   {
    if(u8Temp == local_info.id)  //本地地址
    {
     USART2_RX_BUF[0] = u8Temp;
     u8Uart2_FSM = U_FSM_FUN;
    }
break;
   }
   case U_FSM_FUN:
   {
    if((u8Temp == 0x05)||(u8Temp == 0x01)||(u8Temp == 0x03)||(u8Temp == 0x04)) //本单元值接受0x05和0x01的指令,遥信和遥控
    {
     USART2_RX_BUF[1] = u8Temp;
     u8Uart2_FSM = U_FSM_DATA;
     RX2_no = 8; 
     RX2_cn = 2;
    }
else if(u8Temp == 0x10)
{
USART2_RX_BUF[1] = u8Temp;
     u8Uart2_FSM = U_FSM_FUN16;
//     RX2_no = 15; 
     RX2_cn = 2;
}
    else
{
     u8Uart2_FSM = U_FSM_ADR;
}
break;
}
case U_FSM_FUN16:
{
USART2_RX_BUF[RX2_cn++] = u8Temp;
if(RX2_cn == 6)
{
USART2_RX_BUF[6] = u8Temp;
u8Uart2_FSM = U_FSM_DATA16;
RX2_cn = 7;
RX2_no = 9 + USART2_RX_BUF[6];
}
// else
// {
// USART2_RX_BUF[RX2_cn++] = u8Temp;
// }
break;
}
   case U_FSM_DATA:
   {
    USART2_RX_BUF[RX2_cn++] = u8Temp;
    if(RX2_no <= RX2_cn)                 //数据接收完毕
   
    u8Uart2_FSM = U_FSM_ADR;  
USART_ITConfig(USART2, USART_IT_RXNE, DISABLE);  //一帧数据收完,不再接受数据
    USART2_RX_STA = 1;//置一帧数据接收完标志
//   GPIOA->ODR |= GPIO_Pin_8;
//   GPIO_SetBits(GPIOA,GPIO_Pin_11); 
    }
  break; 
   }
case U_FSM_DATA16:
{
USART2_RX_BUF[RX2_cn++] = u8Temp;
    if(RX2_no <= RX2_cn)                 //数据接收完毕
   
    u8Uart2_FSM = U_FSM_ADR;  
USART_ITConfig(USART2, USART_IT_RXNE, DISABLE);  //一帧数据收完,不再接受数据
    USART2_RX_STA = 1;//置一帧数据接收完标志
}
   break;
   }
default:{u8Uart2_FSM = U_FSM_ADR; break;}
  }
 } 
}



<think>好的,我现在需要帮用户解决关于串口接收空闲中断的实现方法或者示例代码的问题。用户提到了STM32的串口空闲中断,以及相关的引用资料,我需要结合这些信息来构建一个清晰的回答。 首先,我应该回顾一下用户提供的引用内容。引用1解释了空闲中断的触发条件:当总线上在一个字节的时间内没有接收到数据时触发。这通常发生在数据帧发送完成后。引用2提到了在没有特定结束字符或固定长度时如何判断接收完成,这可能就是空闲中断的应用场景。引用3给出了如何配置STM32接收中断和空闲中断,包括寄存器的设置和标准库函数的使用。 接下来,我需要组织回答的结构。用户可能希望了解如何配置空闲中断、如何处理中断服务函数,以及如何结合DMA来提高效率。根据引用3中的代码示例,配置CR1寄存器的RXNEIE和IDLEIE位,或者使用标准库函数USART_ITConfig来开启这两个中断。同时,空闲中断接收中断共享同一个中断通道,所以中断服务函数需要区分这两个中断源。 然后,我需要考虑示例代码的结构。可能包括初始化部分,中断服务函数,以及数据处理的部分。在初始化时,启用接收中断和空闲中断,并可能开启DMA传输。在中断处理中,当接收到数据时,RXNE中断将数据存入缓冲区,当检测到空闲中断时,清除中断标志,并设置数据接收完成的标志,以便主程序处理。 另外,用户可能关心如何清除空闲中断标志。根据引用3,需要先读取SR和DR寄存器来清除IDLE标志,否则会不断进入中断。这点需要特别注意,否则可能导致程序卡死。 还需要考虑DMA的使用,因为空闲中断结合DMA可以高效接收不定长数据。例如,当空闲中断触发时,可以计算DMA传输的数据量,从而确定接收到的数据长度,然后进行处理。这种方法避免了频繁进入中断,提高了效率。 此外,用户的问题可能涉及如何判断接收完成,特别是在没有特定结束符的情况下。空闲中断正好解决了这个问题,一旦检测到总线空闲,就认为一帧数据接收完毕,触发处理流程。 最后,需要确保回答的结构清晰,分步骤说明实现方法,并提供示例代码。同时,生成相关问题,帮助用户进一步理解相关概念和应用场景。</think>### 串口接收空闲中断实现方法 #### 1. 硬件配置原理 串口空闲中断用于检测接收总线上的数据帧结束状态。当接收完一帧数据后,总线在$1$个字节传输时间内无新数据,将触发空闲中断[^1]。实现需结合**接收中断(RXNE)**和**空闲中断(IDLE)**,二者共用同一个中断通道[^3]。 #### 2. STM32实现步骤 ##### (1) 初始化配置 ```c // 标准库配置示例 USART_InitTypeDef USART_InitStruct; USART_InitStruct.USART_BaudRate = 115200; USART_InitStruct.USART_WordLength = USART_WordLength_8b; USART_InitStruct.USART_StopBits = USART_StopBits_1; USART_InitStruct.USART_Parity = USART_Parity_No; USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStruct); // 开启接收中断和空闲中断 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 每收到1字节触发中断 USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); // 检测到总线空闲触发中断 USART_Cmd(USART1, ENABLE); ``` ##### (2) 中断服务函数 ```c volatile uint8_t rx_buffer[256]; volatile uint16_t rx_index = 0; volatile bool data_ready = false; void USART1_IRQHandler(void) { if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { // 接收单个字节 rx_buffer[rx_index++] = USART_ReceiveData(USART1); USART_ClearITPendingBit(USART1, USART_IT_RXNE); } if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) { // 必须读取SR和DR寄存器清除IDLE标志 volatile uint32_t temp = USART1->SR; // 读状态寄存器 temp = USART1->DR; // 读数据寄存器 (void)temp; // 防止编译器优化 data_ready = true; // 通知主程序处理数据 rx_index = 0; // 重置接收索引 } } ``` ##### (3) 主程序处理 ```c while(1) { if(data_ready) { process_data(rx_buffer); // 用户自定义数据处理函数 data_ready = false; } } ``` #### 3. 结合DMA优化 对于高速数据传输,推荐使用**DMA+空闲中断**方案: ```c // 初始化DMA接收 DMA_InitTypeDef DMA_InitStruct; DMA_InitStruct.DMA_BufferSize = 256; DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; DMA_Init(DMA1_Channel5, &DMA_InitStruct); // 中断中获取接收长度 if(USART_GetITStatus(USART1, USART_IT_IDLE)) { uint16_t recv_len = 256 - DMA_GetCurrDataCounter(DMA1_Channel5); process_dma_data(rx_buffer, recv_len); } ``` #### 关键点说明 1. **空闲标志清除**:必须通过读取$SR$和$DR$寄存器清除,否则会持续触发中断 2. **缓冲区管理**:需设置环形缓冲区或动态内存防止溢出 3. **中断优先级**:建议设置为较高优先级(如分组2)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值