中断与事件的区别
我们先来看中断/事件控制器的内部
注意:对某个通道的上升沿和下降沿检测,通过配置上升/下降沿选择寄存器来进行检测哪一种沿。但是,只能在上升沿和下降沿两者中二选一来进行检测。
中断/事件寄存器:选择最后输出的是中断还是事件。
如果选择的是事件寄存器:
路径如图所示:
选择事件寄存器后,路径如图中红色箭头所示,我们由此可以知道事件路径最终输出的是脉冲信号。所以,事件的最终产物是指定频率的脉冲信号。可以输出脉冲信号给外设,使外设做出相应操作。
如果选择的是中断寄存器:
路径如图所示:
选择中断寄存器后,路径如图中红色箭头所示,我们由此可以知道中断路径到达的是中断控制器NVIC,然后触发中断,MCU执行中断服务函数。
所以,中断与事件的区别:
中断,是检测到外部上升或者下降沿,触发中断,进入中断服务函数。
事件,是检测到外部上升或者下降沿,最终产生的是指定频率的脉冲信号。
外部中断/事件的线路映像
由图可知,一共有19个输出,每一组GPIOx通过选择器,选择该GPIO组中的一个I/O口作为EXTI的外部中断/事件源
库函数:
一共19条外部中断线,选其中一个作为外部中断/事件。
选择EXTI最后产生的是中断还是事件
选择上升沿触发还是下降沿触发。
标志位:
如果指定的线路检测到上升沿/下降沿,那么该标志位置SET。注意:它只检测上升沿/下降沿,至于后面发没发生中断它没有办法检测到。
清除标志位
如果指定的线路发生了 中断/事件,那么该标志位位置SET。它就可以检测到中断/事件是否发生。
清除标志位。
配置中断优先级。
上述库函数,可以实现将哪个引脚作为外部中断/事件源。
我们现在用一个按键按下来模拟下降沿作为外部触发。实际上,要产生真正的上升沿/下降沿,需要用信号发生器来产生真正的上升沿/下降沿。
编程步骤:
外部中断-----EXTI
按键模拟外部中断,编程步骤:
//注意:GPIO的时钟与EXTI的时钟是绑在一起的,所以只需要打开GPIO的时钟,EXTI的时钟就会自动被打开了(但是有些单片机需要单独打开,就看手册有没有单独打开EXTI时钟的参数就可以知道GPIO的时钟与EXTI的时钟是不是绑在一起的)
1,打开时钟----GPIOA,EXTI,AFIO
2,初始化GPIO
----GPIO_Pin_0
----上拉输入(当没有下降沿时,输入检测到的是高电平)
3,初始化EXTI
----外部中断线路0
----模式---中断模式
----触发方式---下降沿
----使能
4,配置中断源---将GPIOA0作为外部线路0的中断源
5,配置中断优先级----NVIC初始化
----中断通道号----stm32f10x.h中找
----主优先级
----次优先级
----使能
6,编写中断服务函数
void EXTI0_IRQHandler(void)
{
if(SET==EXTI_GetITStatus( EXTI_Line0)){
EXTI_ClearITPendingBit(EXTI_Line0);
ledflag=~ledflag;
}
}
编写程序:
void EXTI0_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
// 1,打开时钟----GPIOA,EXTI,AFIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
// 2,初始化GPIO
GPIO_InitStruct.GPIO_Pin =GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_IPU;
GPIO_Init(GPIOA,&GPIO_InitStruct);
// 3,初始化EXTI
EXTI_InitStruct.EXTI_Line =EXTI_Line0;
EXTI_InitStruct.EXTI_LineCmd =ENABLE;
EXTI_InitStruct.EXTI_Mode =EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger =EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStruct);
// 4,配置中断源---将GPIOA0作为外部线路0的中断源
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
// 5,配置中断优先级----NVIC初始化
NVIC_InitStruct.NVIC_IRQChannel =EXTI0_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority =1;
NVIC_Init(&NVIC_InitStruct);
}
外部中断通道号:
中断服务函数:
按一下按键,EXTI就会检测到下降沿,那么 ledflag=~ledflag;
void EXTI0_IRQHandler(void)
{
if(SET==EXTI_GetITStatus( EXTI_Line0)){
EXTI_ClearITPendingBit(EXTI_Line0);
ledflag=~ledflag;
}
}
//功能:按一下按键,此时main中小灯进行亮/灭替换
uint8_t ledflag=0;
int main(void)
{
LED_Config();
RCC_ConfigTo72M();//将系统时钟配置成72MHZ
Systick_Config(72);
EXTI0_Config();
// 4,如果是程序的第一个中断,需要设置优先级分钟
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
while(1){
if(0==ledflag){
LED_CTRL(LEDR,LED_OPEN); //开灯
}else{
LED_CTRL(LEDR,LED_CLOSE); //关灯
}
}
}
注意:用外部中断去检测按键是否按下,依然会存在按键抖动导致产生多个下降沿,多次触发外部中断的问题。所以我们需要在中断中加延时,进行按键消抖处理。但是,外部中断触发是会叠加的,上一次的中断触发没有执行完,那么这一次的中断就会等它执行完后立马再执行。而且中断中最好不要使用延时,如果使用了较长的延时(ms级),又恰好main中需要输出PWM波,那么由于进入中断执行时间过长,会导致main中的PWM波无法正常输出。
我们可以使用硬件消抖的方法。
硬件消抖:
方法一:RS触发器为常用的硬件去抖。下图中两个“与非”门构成一个RS触发器。当按键未按下时(开关位于A),输出为0;当键按下时,输出为1。此时即使用按键的机械性能,使按键因弹性抖动而产生瞬时断开(抖动跳开B),只要按键不返回原始状态A,双稳态电路的状态不改变,输出保持为0,不会产生抖动的波形。也就是说,即使B点的电压波形是抖动的,但经双稳态电路之后,其输出为正规的矩形波。
1.当R端无效(1),S端有效时(0),则Q=1,Q非=0,触发器置1。
2.当R端有效(0)、S端无效时(1),则Q=0,Q非=1,触发器置0。
3.当RS端均无效时(0),触发器状态保持不变。
4.当RS端均有效时(1),触发器状态不确定。
方法二:就是在按键上并联一个电容,如下图所示,利用电容的充放电特性来对抖动过程中产生的电压毛刺进行平滑处理,从而实现消抖。