一、EXTI各函数作用
void EXTI_DeInit(void); //调用它就可以把EXTI的配置全部清除,恢复成上电默认状态
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct); //EXTI初始化函数
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct); //调用这个函数,可以把函数传递的结构体变量赋一个默认值
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);//这个函数用来软件触发外部中断,调用这个函数,参数给一个指定的中断线,就能软件触发一次这个外部中断
如果想在主程序里查看或清除标志位就用这两个函数
{
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line); //可以获取指定的标志位是否被置一了
void EXTI_ClearFlag(uint32_t EXTI_Line); //可以对置一的标志位进行清除
}
如果想在中断函数里查看或清除标志位就用这两个函数
{
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line); //获取中断标志位是否被置一了
void EXTI_ClearITPendingBit(uint32_t EXTI_Line); //清除中断挂起标志位
}
最后这四个函数总的来说都是读取状态寄存器
二、NVIC各函数作用
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
//★★★★用来中断分组,参数是中断分组的方式
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
//★★★★根据结构体里的参数初始化NVIC
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);
//设置中断向量表
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);
//系统低功耗配置
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource);
//这个函数用于配置 SysTick(系统定时器) 的时钟源
总的来说NVIC只要记住前两个函数就可以,后面三个用的不多
//先用第一个函数指定分组,然后用第二个函数初始化NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//指定所选通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//打开通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//指定所选通道的抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//指定所选通道的响应优先级
NVIC_Init(&NVIC_InitStructure);
三、如何用外部中断按键控制灯
如图所示,我们就用SW2按键来控制下面这个灯的亮灭
1.首先对按键进行初始化
#include "stm32f10x.h"
#include "key.h"
void key_Init(void)
{
GPIO_InitTypeDef key_initstruct;
EXTI_InitTypeDef exti_initstruct;
NVIC_InitTypeDef nvic_initstruct;
//声明 GPIO、EXTI 和 NVIC 的初始化结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO,ENABLE);
//使能 GPIOA 端口和 AFIO(用于外部中断配置)时钟,必须在使用 GPIOA 和 EXTI 之前先打开时钟,否则外设无法工作。
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//设置中断优先级分组为 2,即:2 位抢占优先级 2 位子优先级
key_initstruct.GPIO_Mode = GPIO_Mode_IPU;
//因为按键按下后为低电平,所以我们这里设置按键为上拉输入,也就是默认为高电平,以此来检查按键是否按下
key_initstruct.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA,&key_initstruct);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
//使能 PA0 作为 EXTI0 的输入信号源,使得 PA0 触发外部中断 EXTI0(说白了就是把这个GPIO口变为中断的作用)
exti_initstruct.EXTI_Line = EXTI_Line0; //选择通道(PA0,PB0...PG0全都是通道0)
exti_initstruct.EXTI_Trigger = EXTI_Trigger_Falling; //按键按下后高电平变为低电平,所以选择下降沿触发中断
exti_initstruct.EXTI_Mode = EXTI_Mode_Interrupt; //配置为中断模式(而不是事件模式)
exti_initstruct.EXTI_LineCmd = ENABLE; //使能该外部中断通道
EXTI_Init(&exti_initstruct);
nvic_initstruct.NVIC_IRQChannel = EXTI0_IRQn; //指定中断通道,即 EXTI0(对应 PA0)
nvic_initstruct.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级 0(最高优先级)
nvic_initstruct.NVIC_IRQChannelSubPriority = 0; //子优先级0
nvic_initstruct.NVIC_IRQChannelCmd = ENABLE; //使能该中断
NVIC_Init(&nvic_initstruct);
}
2.对LED灯进行初始化
#include "led.h"
#include "stm32f10x.h"
//初始化LED三部曲 定义(.c文件),声明(.h文件),调用(main.c)
void LED_Init(void)
{
//1.初始化GPIOA
GPIO_InitTypeDef led_initstruct;//定义GPIO结构体
//2.初始化APB2下的GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
led_initstruct.GPIO_Pin = GPIO_Pin_1;
led_initstruct.GPIO_Speed = GPIO_Speed_2MHz;
led_initstruct.GPIO_Mode = GPIO_Mode_Out_PP;//初始化为推挽输出模式
GPIO_Init(GPIOA,&led_initstruct);//初始化GPIO结构体
}
3.编写主函数
#include "stm32f10x.h" // Device header
#include "led.h"
#include "key.h"
void Delay(uint16_t time)//延时1ms
{
uint16_t i = 0;
while(time--)
{
i = 12000;
while(i--);
}
}
int main(void)
{
LED_Init();
key_Init();
GPIO_SetBits(GPIOA,GPIO_Pin_1);//先把灯初始为熄灭状态
while(1)
{
}
}
void EXTI0_IRQHandler() //中断函数
{
if(EXTI_GetITStatus(EXTI_Line0) != RESET) //判断按键是否被按下(判断是否在PA0检测到下降沿),也可以说是检测中断标志位是否被置1
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);//亮灯
Delay(1000); //延时1s
GPIO_SetBits(GPIOA,GPIO_Pin_1); //熄灭灯
Delay(1000); //延时1s
EXTI_ClearITPendingBit(EXTI_Line0); //★★★一定要记得在中断函数中清除掉标志位,要不然下次检测到下降沿将不会触发中断
}
}
注意:本次实验有几个不严谨的地方
1.没有进行按键消抖
2.Delay延时函数并不是那么精确
3.在中断函数中使用了Delay延时函数(本实验是为了现象比较明显才使用延时函数,一般在中断函数中不要使用延时函数,容易导致程序堵塞)
希望大家可以将此程序更加优化!