问题18:GetFlagStatus,ClearFlag,GetITStatus,ClearITPendingBit四个函数的区别
本文最后更新时间:2025.4.18
问题解释
在STM32中,有一类寄存器,叫做状态寄存器,状态寄存器中的某一位(简称状态位)可以指示硬件电路的状态(比如定时时间到,ADC转换完成,串口收到数据了等等),我们读取状态位,就可以得知电路的工作状况。
同时,如果这个状态是比较紧急的,我们希望状态位置1了,可以立即申请中断,则此时,状态位就化身为中断标志位。如果开启了对应的中断并配置了NVIC,则状态位/中断标志位为1时,外设就会向CPU申请中断。
例如,下图所示,状态寄存器里的UIF位,表示定时器发生更新,即定时时间到,如果不开启中断,我们可以手动查询此位,来判断定时时间到没到。如果开启了中断,则此位化身为更新中断标志位,此位为1,且开启了定时器更新中断并配置了NVIC,那么定时器就会向CPU申请中断,进而使程序跳转到中断函数执行。

UIF为定时器更新状态位
STM32中还有一类寄存器的位,叫做中断使能位,顾名思义,中断使能位为1,表示开启中断,中断使能位为0,表示关闭中断。
例如,下图所示,定时器使能寄存器DIER中,UIE位,表示使能更新中断,此位置1,表示允许更新中断,此位置0,表示禁止更新中断。

UIE为定时器更新中断使能位
综上所示,状态位表示设备运行状态,可以通过查询的方式获取状态位的值,如果此状态比较紧急,可以进一步使用中断使能位开启中断,此时状态位化身为中断标志位,为1时,会申请中断。
注意,不同外设的寄存器和库函数的设计可能有区别,但是它们的基本执行流程都是一样的。例如,对于ADC外设,中断使能位位于控制寄存器CR1中;对于外部中断EXTI,中断使能位被中断屏蔽位代替,位于中断屏蔽寄存器IMR中,而状态位被挂起位代替,位于挂起寄存器PR中。这可能是因为每个外设的设计者不一样,所以寄存器的安排和设计也有所区别。
GetFlagStatus,ClearFlag,GetITStatus,ClearITPendingBit这4个函数,就是对状态位和中断使能位的读写函数。
以定时器为例,TIM_GetFlagStatus的程序源码如下:
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG) { ITStatus bitstatus = RESET; /* Check the parameters */ assert_param(IS_TIM_ALL_PERIPH(TIMx)); assert_param(IS_TIM_GET_FLAG(TIM_FLAG)); if ((TIMx->SR & TIM_FLAG) != (uint16_t)RESET) { bitstatus = SET; } else { bitstatus = RESET; } return bitstatus; }
显而易见,它就是直接读取定时器的状态寄存器SR,返回对应状态位的值,如果指定的状态位不为0,则返回SET(1),否则,返回RESET(0)。例如调用函数TIM_GetFlagStatus(TIM2, TIM_FLAG_Update),则返回SET表示状态位UIF为1,返回RESET表示状态位UIF为0。
TIM_ClearFlag的程序源码如下:
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG) { /* Check the parameters */ assert_param(IS_TIM_ALL_PERIPH(TIMx)); assert_param(IS_TIM_CLEAR_FLAG(TIM_FLAG)); /* Clear the flags */ TIMx->SR = (uint16_t)~TIM_FLAG; }
可以看出,它通过在指定状态位里写0,来清除此状态位,例如调用函数TIM_ClearFlag(TIM2, TIM_FLAG_Update),表示清除UIF状态位。
总结:GetFlagStatus和ClearFlag是对状态位的直接读写函数,GetFlagStatus用于读取状态位,ClearFlag用于清除状态位。
既然GetFlagStatus和ClearFlag就已经包含了读写状态位,那还要GetITStatus和ClearITPendingBit干啥呢?
前面说了,状态位可以直接进行读写,通过查询的方式来获知对应的状态有没有发生,同时,如果配置了中断,状态位还可以进一步去申请中断,此时,我们就有判断中断是否发生的需求了,因此GetITStatus和ClearITPendingBit孕育而生。
注意,状态位为1,并不代表中断一定发生、中断函数一定执行,因为,还要使能中断和配置NVIC,才能触发中断函数执行。没有开启中断的时候,状态位也是可以正常工作的,这时可以使用查询的方式用到状态位。
TIM_GetITStatus的程序源码如下:
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT) { ITStatus bitstatus = RESET; uint16_t itstatus = 0x0, itenable = 0x0; /* Check the parameters */ assert_param(IS_TIM_ALL_PERIPH(TIMx)); assert_param(IS_TIM_GET_IT(TIM_IT)); itstatus = TIMx->SR & TIM_IT; itenable = TIMx->DIER & TIM_IT; if ((itstatus != (uint16_t)RESET) && (itenable != (uint16_t)RESET)) { bitstatus = SET; } else { bitstatus = RESET; } return bitstatus; }
可以看出,它同时读取了状态寄存器SR和中断使能寄存器DIER,判断条件是,如果指定的状态位为1,且中断被使能了,才返回SET(1),否则,就返回RESET(0)。例如调用函数TIM_GetITStatus(TIM2, TIM_IT_Update),只有UIF和UIE同时为1,函数才返回SET,否则,就是返回RESET。
ClearITPendingBit的程序源码如下:
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT) { /* Check the parameters */ assert_param(IS_TIM_ALL_PERIPH(TIMx)); assert_param(IS_TIM_IT(TIM_IT)); /* Clear the IT pending Bit */ TIMx->SR = (uint16_t)~TIM_IT; }
可以说,它的执行逻辑和TIM_ClearFlag完全一样,也是通过在指定状态位里写0,来清除此状态位,或者说叫中断标志位。
总结:GetITStatus和ClearITPendingBit是与中断相关的读写函数,GetITStatus相比较GetFlagStatus,增加了对中断使能位的判断,如果中断没使能,即使状态位为1,返回的也是RESET。ClearITPendingBit意为清除中断挂起标志位,其实也就是清除状态位,内部执行逻辑,与ClearFlag没有区别。
最后,GetFlagStatus和ClearFlag相比较GetITStatus和ClearITPendingBit,它们的参数取值范围可能不同,因为状态位有很多,但并不是所有的状态位都支持申请中断,因此GetITStatus和ClearITPendingBit的参数,只能是涉及中断的标志位,参数取值范围,小于或等于,GetFlagStatus和ClearFlag的参数取值范围。
简单理解:如果只想单纯地查询状态位,用GetFlagStatus和ClearFlag,如果想判断中断有没有发生,用GetITStatus和ClearITPendingBit。
当然,在大多数情况下,GetFlagStatus可以与GetITStatus混用,ClearFlag也可与ClearITPendingBit混用。至于为什么相似的逻辑出两套函数,那可能就要看设计者的意思了。
< 返回