最终效果描述
按键按下触发外部中断, 点亮灯, 再次按下熄灭灯.
RCC配置
需要打开外设GPIO, AFIO, 两个. 查看源代码竟然发现EXTI和NVIC竟然不属于外设, 不过想想也对, 因为中断不光是为用户来准备的, 还有一部分是系统需要的.
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
AFIO介绍
AFIO是数据选择器, 用于引脚的复用功能的选择和重定义. stm32最多只有16个外部中断通道, 为了实现每个io口都可以作为一个触发引脚, 使用数据选择器来选择, 但编号0的只能选择一个. 这里AFIO配置就是将IO口配置为一个外部中断引脚. 硬件原理图样子如下所示.
将IO口配置为一个外部中断引脚代码如下.
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource1);
GPIO配置
因为需要读取外面的信号, 所以模式需要设置为输入模式, 防止引脚悬空选择上拉模式.
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
EXTI配置
通过框图可以知道, 可以外部触发, 也可以配置软件中断事件寄存器来触发.
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line1; //选择中断通道
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //开启中断通道
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
EXTI_Init(&EXTI_InitStructure);
NVIC(向量中断控制器)配置
NVIC是中断的大闸门, 总开关, 可以配置中断的优先级.
抢占优先级: 抢占低抢占优先级的中断, 实现中断嵌套.
响应优先级: 同时响应时, 高响应优先级的先响应.
根据优先级分组的配置来选择抢占和响应优先级的个数, 数字越低, 优先级越高.
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStructure);
中断向量表与中断服务函数
中断服务函数返回值皆为void, 参数为void. 函数名在中断向量表中, 名字唯一, 不可以更改.
中断向量表为start up汇编文件中, 这是单片机真正的入口, 里面的Vector Table Mapped.
void EXTI1_IRQHandler()
{
if(EXTI_GetITStatus(EXTI_Line1) == SET) //判断中断信号是否触发
{
flag++;
Delay_ms(250);
EXTI_ClearITPendingBit(EXTI_Line1); //清空中断标志位
}
}
按键消除抖动
按键按下会触发抖动, 原因就是两个金属弹片快速碰撞来的抖动, 造成信号的抖动, 旋转式的老式风扇开关会出火花也是这个原因, 不能瞬间的充分接触导致的. 软件消除抖动就是使用延时函数, 硬件的消除抖动并联电容, 利用电容的滤波特性. 这里选择延时消除抖动.
在中断服务函数中可以看出延时了250ms, 在使用逻辑分析仪来反复监测按键的抖动, 发现最高是240多毫秒, 250ms之后才可以消除抖动带来的影响, 但是大大增加了按键的触发延迟.
程序主逻辑
上面的配置完成了, 任务的主逻辑就很简单了, 利用flag变量, 按键按下触发flag+1, 偶数次熄灭, 奇数次点亮LED.
uint8_t flag = 0;
int main(void)
{
//灯
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
extiInit();
while (1)
{
if(flag % 2 == 1){
GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_RESET);
}else{
GPIO_WriteBit(GPIOA, GPIO_Pin_8, Bit_SET);
}
}
}