根据框图以及里关于NVIC中断向量表,可以知道EXTI0-EXTI15分别挂着PA\B\C\D\E\F\G0-PA\B\C\D\E\F\G15,也就是说不同的GPIO相同的引脚号挂载在同一条EXTI线上,他们只能选择其中一个,比如EXTI0里只能在PA0-PG0里选择一个作为中断源。
在NVIC管理中,EXTI0 EXTI1 EXTI2 EXTI3 EXTI4都是独立的,而EXTI5-EXTI9产生的是同一个中断,需要通过中断标志位来判断到底是EXTI几产生的中断,同样的,EXTI10-EXTI15也是同一个中断。
显然,在相应位置1可以开放来自EXTIx上的中断请求。
1.通过中断屏蔽寄存器,可以配置开放哪一条线产生中断
2.通过上升&下降沿触发选择寄存器可以配置外部中断的触发方式
3.通过读取挂起寄存器就可以读出哪一条线产生了触发请求
注:通过AFIO_EXTICRx配置GPIO线上的外部中断/事件,必须先使能AFIO时钟。
AFIO的配置主要是配置选择EXTIx的外部中断的输入源
为了产生外部中断,需要先将对应的引脚配置成上下拉输入的模式,在AFIO里选择输入源,配置好EXTI寄存器,最后在NVIC里配置好响应的优先级
外部中断的配置代码可以是这样:
void EXTI_init(void)
{
RCC->APB2ENR |= (1<<2); //GPIOA
RCC->APB2ENR |= (1<<0); //AFIO
//EXTI和NVIC两个外设时钟是一直开着的,不需要开
//NVIC是内核外设,和CPU一起运行,RCC管的是内核以外
GPIOA->CRL &= 0x00000000; //清零
GPIOA->CRL |= 0x88881111; //高四位输入,低四位输出
GPIOA->ODR |= 0xFF; //上拉输入,和输出高电平
//外部中断配置寄存器
AFIO->EXTICR[1] = 0x0000; //配置PA4 PA5 PA6 PA7
//配置EXTI寄存器
EXTI->IMR |= (0xF<<4); //开放来自4567线上的中断请求
EXTI->FTSR |= (0xF<<4); //允许输入线4567上的下降沿触发
MY_NVIC_Init(0,0,EXTI4_IRQn,0);
MY_NVIC_Init(0,0,EXTI9_5_IRQn,0);
}
EXTI4_IRQn和EXTI9_5_IRQn可以再stm32f10x.h头文件中找到,这是中断编号。函数MY_NVIC_Init()在里提到是整点原子提供在sys.c里的函数,直接利用了,在手册中并没有详细说明。
PA4 PA5 PA6 PA7接按键,PA0 PA1 PA2 PA3接LED灯,用外部中断去控制灯的亮灭
#include "stm32f10x.h"
#include "sys.h"
#include "MY_LED.H"
#include "delay.h"
void EXTI_init(void)
{
RCC->APB2ENR |= (1<<2); //GPIOA
RCC->APB2ENR |= (1<<0); //AFIO
//EXTI和NVIC两个外设时钟是一直开着的,不需要开
//NVIC是内核外设,和CPU一起运行,RCC管的是内核以外
GPIOA->CRL &= 0x00000000; //清零
GPIOA->CRL |= 0x88881111; //高四位输入,低四位输出
GPIOA->ODR |= 0xFF; //上拉输入,和输出高电平
//外部中断配置寄存器
AFIO->EXTICR[1] = 0x0000; //配置PA4 PA5 PA6 PA7
//配置EXTI寄存器
EXTI->IMR |= (0xF<<4); //开放来自4567线上的中断请求
EXTI->FTSR |= (0xF<<4); //允许输入线4567上的下降沿触发
MY_NVIC_Init(0,0,EXTI4_IRQn,0);
MY_NVIC_Init(0,0,EXTI9_5_IRQn,0);
}
int main(void)
{
delay_init(8);
EXTI_init();
MY_LED_Init();
while(1)
{
}
}
void EXTI4_IRQHandler(void)
{
delay_ms(10); //消抖
GPIOA->ODR ^= (1<<0); //取反第0位,让LED灯状态翻转
while(!(GPIOA->IDR&(1<<4))); //松开按键的消抖
delay_ms(10);
EXTI->PR=1<<4; //清除LINE4上的中断标志位
}
void EXTI9_5_IRQHandler(void)
{
if(EXTI->PR&(1<<5))
{
delay_ms(10); //消抖
GPIOA->ODR ^= (1<<1);
while(!(GPIOA->IDR&(1<<5))); //松开按键的消抖
delay_ms(10);
EXTI->PR=1<<5;
}else if(EXTI->PR&(1<<6))
{
delay_ms(10); //消抖
GPIOA->ODR ^= (1<<2);
while(!(GPIOA->IDR&(1<<6))); //松开按键的消抖
delay_ms(10);
EXTI->PR=1<<6;
}else if(EXTI->PR&(1<<7))
{
delay_ms(10); //消抖
GPIOA->ODR ^= (1<<3);
while(!(GPIOA->IDR&(1<<7))); //松开按键的消抖
delay_ms(10);
EXTI->PR=1<<7;
}
}
由于EXTI5-EXTI9是合并成一个中断号,产生的中断是同一个,所以要在中断里判断到底是哪一条线产生的中断,读PR寄存器。主要看EXIT4_IRQHandler的部分
#include "stm32f10x.h" // Device header
#include "delay.h"
void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetFlagStatus(EXTI_Line5) == SET)
{
Delay_ms(5); //按键消抖,按下的消抖
while(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_5) == RESET); //消抖,等待按键松开
Delay_ms(5); //按键消抖,松开的消抖
GPIO_WriteBit(GPIOA , GPIO_Pin_1 , (BitAction)!GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_1));
EXTI_ClearFlag(EXTI_Line5);
}
if(EXTI_GetFlagStatus(EXTI_Line6) == SET)
{
Delay_ms(5); //按键消抖,按下的消抖
while(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_6) == RESET); //消抖,等待按键松开
Delay_ms(5); //按键消抖,松开的消抖
GPIO_WriteBit(GPIOA , GPIO_Pin_2 , (BitAction)!GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_2));
EXTI_ClearFlag(EXTI_Line6);
}
if(EXTI_GetFlagStatus(EXTI_Line7) == SET)
{
Delay_ms(5); //按键消抖,按下的消抖
while(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_7) == RESET); //消抖,等待按键松开
Delay_ms(5); //按键消抖,松开的消抖
GPIO_WriteBit(GPIOA , GPIO_Pin_3 , (BitAction)!GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_3));
EXTI_ClearFlag(EXTI_Line7);
}
}
void EXTI4_IRQHandler(void)
{
Delay_ms(5); //按键消抖,按下的消抖
while(GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_4) == RESET); //消抖,等待按键松开
Delay_ms(5); //按键消抖,松开的消抖
GPIO_WriteBit(GPIOA , GPIO_Pin_0 , (BitAction)!GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_0));
EXTI_ClearFlag(EXTI_Line4);
}
void EXTI_init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line4 | EXTI_Line5 | EXTI_Line6 | EXTI_Line7; //PA4/5/6/7挂在线4/5/6/7上
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能外部中断
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //产生中断而不是事件
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
EXTI_Init(&EXTI_InitStructure);
}
void NVIC_init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级和响应优先级优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_Init(&NVIC_InitStructure);
}
void GPIOA_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //没啥用,填吧
GPIO_Init(GPIOA , &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA , &GPIO_InitStructure);
GPIO_SetBits(GPIOA , GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3);
}
int main(void)
{
GPIOA_init();
EXTI_init();
NVIC_init();
while(1);
}
因篇幅问题不能全部显示,请点此查看更多更全内容