32F103提供19个外部中断:
0-15:对应外部IO口输入中断
16:连接到PVD输出
17:连接到RTC闹钟事件
18:连接到USB唤醒事件
IO设备线只有16条,但是GPIO口远远不止16个,所以需要映射
ABCDEFG的0都连接在一个线0,中断线每次只能连接到一个IO口上,这样就需要通过配置来决定对应的中断线配置到哪个GPIO上了。
这种映射关系通过:
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
eg:GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);
中断线2和EXTI2中断线链接,设置好中断线映射后,设置中断线上中断初始化参数来触发IO中断。
初始化通过:
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
同样里面有一个结构体:
EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line=EXTI_Line4; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据 EXTI_InitStruct中指定的参数初始化EXTI寄存器
可以看到是下降沿触发的,第一个参数中断线标号,第二个中断模式,第三个触发方式,第四个是使能中断线。
设置好了中断线和GPIO的映射关系,又设置好了中断的触发模式等参数,依然是外部中断,涉及到中断我们当然还要设置NVIC中断优先级。(和串口实验一样)
配置完中断优先级后,编写终端服务程序,32一共有6个外部中断函数。编写过程中经常用到两个函数:
1、判断中断线上是否有终端发生(标志位是否置位)
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
2、清除某个中断线上的中断表示位:
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
用于终端服务函数结束之前清除中断标志位。
最后总结IO口外部中断一般步骤:
1、初始化IO口为输入
2、开启Io口复用时钟,设置IO口和中断线的映射管理
3、初始化线上中断,设置触发条件
4、配置终端分组NVIC,并使能中断
5、编写中断服务函数
这次我们要实现的功能是通过中断来检测按键,WKUP蜂鸣器按一次叫一次,再按一次停
KEY2控制DS0,按一次亮再按一次灭
KEY1控制DS1,效果和KEY2一样,KEy0同时控制0,1按一次翻转一次
下面这段例程就是完全按照我们所阐述的中断设定方式进行的:
//外部中断初始化函数
void EXTIX_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//外部中断,需要使能AFIO时钟
KEY_Init();//初始化按键对应io模式
//GPIOC.5 中断线以及中断初始化配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource5);
EXTI_InitStructure.EXTI_Line=EXTI_Line5;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
//GPIOA.15 中断线以及中断初始化配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource15);
EXTI_InitStructure.EXTI_Line=EXTI_Line15;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
//GPIOA.0 中断线以及中断初始化配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //使能按键所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02; //子优先级1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; //使能按键所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; //子优先级1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //使能按键所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; //子优先级1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
}
void EXTI0_IRQHandler(void)
{
delay_ms(10); //消抖
if(WK_UP==1)
{
LED0=!LED0;
LED1=!LED1;
}
EXTI_ClearITPendingBit(EXTI_Line0); //清除EXTI0线路挂起位
}
void EXTI9_5_IRQHandler(void)
{
delay_ms(10); //消抖
if(KEY0==0) {
LED0=!LED0;
}
EXTI_ClearITPendingBit(EXTI_Line5); //清除LINE5上的中断标志位
}
void EXTI15_10_IRQHandler(void)
{
delay_ms(10); //消抖
if(KEY1==0) {
LED1=!LED1;
}
EXTI_ClearITPendingBit(EXTI_Line15); //清除LINE15线路挂起位
最后再来看一下主函数:
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "key.h"
#include "usart.h"
#include "exti.h"
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
uart_init(9600); //串口初始化为9600
LED_Init(); //初始化与LED连接的硬件接口
EXTIX_Init(); //外部中断初始化
LED0=0; //点亮LED
while(1)
{
printf("OK\n");
delay_ms(1000);
}
}
效果跟按键输入输出差不多,但是它是通过中断来进行处理的。这就完全不一样了。