标志位和中断位的区别:USART_ClearFlag和USART_ClearITPendingBit

本文详细解析了STM32中ADC模块的中断位与标志位的区别及联系,阐述了它们在程序中的作用,并提供了具体的代码示例。

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

在STM32的函数库中,这两个函数实现的功能其实是一样的,都是清除对应的标志位,但是标志位和中断位的含义不一样,不是所有的标志位都可以产生中断。查看源代码如下:
#define ADC_IT_EOC                                 ((uint16_t)0x0220)
#define ADC_IT_AWD                                 ((uint16_t)0x0140)
#define ADC_IT_JEOC                                ((uint16_t)0x0480)
这是定义的中断位,可以产生中断
#define ADC_FLAG_AWD                               ((uint8_t)0x01)
#define ADC_FLAG_EOC                               ((uint8_t)0x02)
#define ADC_FLAG_JEOC                              ((uint8_t)0x04)
#define ADC_FLAG_JSTRT                             ((uint8_t)0x08)
#define ADC_FLAG_STRT                              ((uint8_t)0x10)
这是定义的标志位,二者对比可以发现有的标志位不能产生中断
标志位在程序中可以作为判定条件,支持程序的运行,中断则是跳转到中断函数执行。两个函数实现的功能是一样的,在中断程序中可以用两个中的任一个。区分两个函数是为了更清晰的显示函数库的严谨。
再具体分析下面的中断位和标志位:
#define ADC_IT_EOC                                 ((uint16_t)0x0220)
#define ADC_FLAG_EOC                               ((uint8_t)0x02)
这两个数值不同是因为标志位只是为了清除标志位而设的,而中断位设置成这个值是因为在其他函数中这一位还有其他用途。而且还要注意:
void ADC_ClearFlag(ADC_TypeDef* ADCx, uint8_t ADC_FLAG)
{
     /* Check the parameters */
     assert_param(IS_ADC_ALL_PERIPH(ADCx));
     assert_param(IS_ADC_CLEAR_FLAG(ADC_FLAG));
     /* Clear the selected ADC flags */
     ADCx->SR = ~(uint32_t)ADC_FLAG;
}
这一句ADCx->SR = ~(uint32_t)ADC_FLAG;应该是ADCx->SR &= ~(uint32_t)ADC_FLAG;
不过状态位只能由硬件置位,软件可以读和清零,所以这样写也是可以的。
#include "stm32f10x.h" #include "main.h" #include "usart.h" #include "led.h" #include "stdio.h" //#include "bear.h" //#include "key.h" void delay(uint16_t time) //ѓʱ1msº¯ʽ { uint16_t i = 0; while(time --) { i = 2000; while(i --); } } int main() { //1、串口初始化函数 my_usart_init(); // my_Usart_Send_Byte(USART1,'A'); // my_Usart_Send_Byte(USART1,'B'); // my_Usart_Send_Byte(USART1,'C'); // // My_Usart_Send_String(USART1,"moumouge\r\n"); printf("hello moumouge\r\n"); while(1) { } } #include "stm32f10x.h" #include "usart.h" #include "stdio.h" void my_usart_init(void) { //1、GPIOA外设时钟使能 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1,ENABLE); //10、NVIC的分组设置 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //2、定义串口对应引脚的结构体 GPIO_InitTypeDef usart_gpio_initstruct; //3、初始化TX A9串口引脚信息 usart_gpio_initstruct.GPIO_Pin = GPIO_Pin_9; usart_gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP; usart_gpio_initstruct.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(GPIOA,&usart_gpio_initstruct); //3、初始化RX A10串口引脚信息 usart_gpio_initstruct.GPIO_Pin = GPIO_Pin_10; usart_gpio_initstruct.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOA,&usart_gpio_initstruct); //4、定义usart外设的结构体 USART_InitTypeDef usart_initStruct; //5、初始化usart外设的信息 usart_initStruct.USART_BaudRate = 115200;//波特率 usart_initStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//是否有硬件流控制 usart_initStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//串口输出模式 usart_initStruct.USART_Parity = USART_Parity_No;//奇偶校验 usart_initStruct.USART_StopBits = USART_StopBits_1;//停止 usart_initStruct.USART_WordLength = USART_WordLength_8b;//数据字节数 //6、初始USART化结构体 USART_Init(USART1,&usart_initStruct); //7、使能串口外设 USART_Cmd(USART1,ENABLE); //11、串口中断输出使能函数 USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //8、定义NVIC结构体指针 NVIC_InitTypeDef Nvic_InitStruct; Nvic_InitStruct.NVIC_IRQChannel = USART1_IRQn;//对应的NVIC通道 Nvic_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级 Nvic_InitStruct.NVIC_IRQChannelSubPriority = 0;//响应优先级 Nvic_InitStruct.NVIC_IRQChannelCmd = ENABLE;//通道使能 //9、NVIC初始化 NVIC_Init(&Nvic_InitStruct); } void my_Usart_Send_Byte(USART_TypeDef* USARTx, uint16_t Data) { USART_SendData(USARTx,Data); while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE) == RESET); } void My_Usart_Send_String(USART_TypeDef* USARTx, char * Str) { uint16_t i = 0; while((*(Str+i)) != '\0') { my_Usart_Send_Byte(USARTx,*(Str+i)); i++; } while(USART_GetFlagStatus(USARTx,USART_FLAG_TC) == RESET); } int fputc(int ch, FILE * p) { USART_SendData(USART1,(u8)ch); while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET); return ch; } void USART1_IRQHandler() { char str; if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET) { str = USART_ReceiveData(USART1); printf("receive data is: %c \r\n",str); if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { USART_ClearITPendingBit(USART1, USART_IT_RXNE); } } } 为什么我用串口工具第一次发送了一个a,可以接收到,第二次发送了1,串口工具接受不到了, 日志信息:hello moumouge receive data is: a
06-23
<think>嗯,用户问的是USART_GetITStatusUSART_GetFlagStatus的区别及作用。这个问题在嵌入式开发中很关键,因为串口通信是单片机应用的基础。用户可能是正在调试STM32串口程序时遇到了疑惑,或者在学习中断机制时需要明确这两个函数的使用场景。 从引用资料来看,用户已经查阅过一些博客资料[^1][^2],但可能缺乏系统性理解。我需要从底层寄存器操作的角度解释清楚本质区别,避免让用户停留在表面认知。 首先USART_GetFlagStatus函数更底层,它直接读取状态寄存器(SR)的物理标志位。比如用户需要查询RXNE(接收缓冲区非空)这种状态时,无论是否开启中断都能用这个函数检测。而USART_GetITStatus是带中断使能校验的封装,它在读取SR寄存器前会先检查CR寄存器里对应的中断使能是否打开。 举个典型场景:当用户使用中断服务函数时,应该用USART_GetITStatus判断事件来源,因为它同时验证了“事件发生”中断使能”两个条件。而如果用户只是轮询发送完成标志TC,用USART_GetFlagStatus就够了。 特别要注意TXETC标志的区别。发送数据寄存器空(TXE)表示可以写入新数据,发送完成(TC)是整个帧包括停止都发送完毕。很多初学者会混淆这两个状态,导致程序卡死或者数据丢失。 用户可能还需要知道,在STM32标准库中这两个函数的实现差异很大。USART_GetITStatus有额外的中断使能判断逻辑,而USART_GetFlagStatus就是简单的操作。这个设计体现了硬件抽象层的封装思想。 考虑到用户实际调试需求,应该补充典型用法示例。比如在中断服务函数里必须用USART_GetITStatus清除挂起标志,否则会反复触发中断。而DMA传输时查询TC标志用USART_GetFlagStatus更高效。 最后要提醒临界保护问题。在RTOS环境下查询标志位时,如果高优先级任务可能操作同一个串口,就需要关中断保护状态读取的原子性。这个细节很多资料会忽略,但对用户实际项目很重要。</think>理解USART_GetITStatusUSART_GetFlagStatus的区别及作用是STM32串口编程的关键。它们的核心差异在于**是否检查中断使能状态**。下面详细说明: --- ### 1. **USART_GetFlagStatus** * **作用**:**直接读取硬件状态寄存器(SR)** 中的某个标志位(Flag),判断某个特定事件是否发生。 * **原理**:仅查询状态寄存器`USARTx->SR`的指定(如`USART_FLAG_RXNE`、`USART_FLAG_TXE`、`USART_FLAG_TC`等)。 * **返回值**:`FlagStatus`(`SET`或`RESET`),表示该标志位的当前状态。 * **适用场景**: * 轮询模式(Polling):在主循环中不断查询标志位状态以决定下一步操作(例如:检查`USART_FLAG_RXNE`判断是否有数据收到)。 * 需要了解串口硬件状态,但不一定与中断相关的情况。 * **特点**:**简单直接,只关心事件本身是否发生,不关心该事件是否配置了中断。** --- ### 2. **USART_GetITStatus** * **作用**:**检查某个中断源是否处于“挂起(Pending)”状态**,即该中断是否已发生且**被使能**。 * **原理**:这是一个两步检查过程: 1. **检查中断使能**:查询控制寄存器`USARTx->CR1`/`CR2`/`CR3`中对应的中断使能(如`USART_IT_RXNE`)是否被置中断是否已开启)。 2. **检查状态标志位**:查询状态寄存器`USARTx->SR`中对应的状态标志位(如`USART_FLAG_RXNE`)是否被置(事件是否发生)。 * **返回值**:`ITStatus`(`SET`或`RESET`),仅当**中断使能状态标志位同时为`SET`**时,才返回`SET`。 * **适用场景**: * **中断服务程序(ISR)**内部:用于确定是哪个中断源触发了本次中断。例如,在USART全局中断服务函数中,使用此函数判断是接收中断(`USART_IT_RXNE`)、发送完成中断(`USART_IT_TC`)还是其他中断。 * **特点**:**不仅关心事件是否发生,还关心该事件是否被配置为可以触发中断**。这是判断中断源的**标准方法**。 --- ### 关键区别总结 | 特性 | `USART_GetFlagStatus` | `USART_GetITStatus` | | :-------------------- | :----------------------------------------------- | :--------------------------------------------------- | | **查询对象** | 状态寄存器(`SR`)中的**标志位(Flag)** | 状态寄存器(`SR`)中的**标志位(Flag)** + 控制寄存器(`CRx`)中的**中断使能(IT Enable)** | | **核心目的** | 检测**事件是否发生** | 检测**中断是否挂起**(事件发生 **且** 中断已使能) | | **返回值含义** | 事件发生(`SET`) / 未发生(`RESET`) | 中断挂起(`SET`) / 未挂起(`RESET`) | | **典型应用场景** | 轮询模式(Polling) | **中断服务程序(ISR)** | | **是否检查中断使能** | **否** | **是** | | **底层操作** | 直接读`USARTx->SR` & `Flag` | 读`USARTx->CRx` & `IT_Enable` AND 读`USARTx->SR` & `Flag` | --- ### 实际应用举例 1. **轮询接收数据 (使用 FlagStatus):** ```c while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET); // 等待数据到达 uint8_t data = USART_ReceiveData(USART1); // 读取接收到的数据 ``` 2. **中断服务程序 (使用 ITStatus):** ```c void USART1_IRQHandler(void) { // 检查是否是接收中断触发 if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { uint8_t data = USART_ReceiveData(USART1); // 读取数据 // ... 处理数据 ... USART_ClearITPendingBit(USART1, USART_IT_RXNE); // 清除中断挂起 (通常也清除标志位) } // 检查是否是发送完成中断触发 if (USART_GetITStatus(USART1, USART_IT_TC) != RESET) { // ... 处理发送完成 ... USART_ClearITPendingBit(USART1, USART_IT_TC); // 清除中断挂起 } } ``` --- ### 重要说明 * **清除标志位/中断挂起:** 在中断服务程序中处理完中断后,**必须清除相应的中断挂起**(通常使用`USART_ClearITPendingBit`)。这个操作通常也会清除对应的状态标志位(如`USART_FLAG_RXNE`)。如果不清除,中断会持续触发。`USART_ClearFlag`函数则只清除状态标志位,不处理中断挂起状态。 * **中断配置:** 要使用`USART_GetITStatus`有意义,必须先通过`USART_ITConfig`函数使能相应的中断(如`USART_ITConfig(USART1, USART_IT_RXNE, ENABLE)`)并配置好NVIC。 * **库函数差异:** 标准外设库(SPL)HAL/LL库的函数名实现细节可能略有不同,但核心概念(检查标志位 vs 检查中断源)是一致的。 **总结:** 在轮询模式下使用`USART_GetFlagStatus`查询事件状态;在中断服务程序(ISR)中,使用`USART_GetITStatus`来判断具体是哪个**已使能的中断源**触发了中断[^1][^2]。 --- ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值