目录
中断是什么?
例:在公司上班打代码,然后经理打电话过去谈话,谈话到一半,老婆打电话要去医院,然后回来医院继续谈话,然后继续工作打代码。
打断原来做的事情 -> 打断工作
保留现场 -> 保留工作进度
处理中断 -> 谈话
回到现场 -> 继续工作
如何配置中断
1.初始化原来中断的GPIO口
2.初始化EXTI(什么是EXTI?)
边沿检测电路 -> 上升沿/下降沿触发中断
3.配置NVIC(中断优先级)
配置中断的优先级,中断嵌套
4.编写中断服务函数
EXTI 初始化结构体
typedef struct {
uint32_t EXTI_Line; // 中断/事件线
EXTIMode_TypeDef EXTI_Mode; // EXTI 模式
EXTITrigger_TypeDef EXTI_Trigger; // 触发类型
FunctionalState EXTI_LineCmd; // EXTI 使能
} EXTI_InitTypeDef;
- EXTI_Line:EXTI中断/事件线选择,可选 EXTI0 至 EXTI19
- EXTI_Mode:EXTI 模式选择,可选为产生中断(EXTI_Mode_Interrupt)或者产生事件(EXTI_Mode_Event)。
- EXTI_Trigger:EXTI 边沿触发事件,可选上升沿触发(EXTI_Trigger_Rising)、下降沿触发 (EXTI_Trigger_Falling) 或者上升沿和下降沿都触发
- ( EXTI_Trigger_Rising_Falling)。 EXTI_LineCmd:控制是否使能 EXTI 线,可选使能 EXTI线(ENABLE)或禁用 (DISABLE)。
NVIC 初始化结构体
typedef struct
{
uint8_t NVIC_IRQChannel; //设置中断通道
uint8_t NVIC_IRQChannelPreemptionPriority; //设置抢占优先级
uint8_t NVIC_IRQChannelSubPriority; //设置响应优先级
FunctionalState NVIC_IRQChannelCmd; //设置是否使能或禁用
} NVIC_InitTypeDef;
本文章是学习笔记,部分内容转载自https://blog.csdn.net/zxh1592000/article/details/80280715
此文章讲的非常详细。
震动感应灯
上篇文章做了感应灯,现在来用中断做震动感应灯。
按照如何上面如何配置中断的四个步骤来编写。
1.初始化原来中断的GPIO口
GPIO口配置我直接复制了shake.c(震动传感器)内容
2.初始化EXTI
EXTI_Line
因为震动传感器为PA1,所以exti_init.EXTI_Line = EXTI_Line1;
GPIO1的中断挂在line1上了,硬件决定的,没法改
EXTI_Mode_Interrupt
EXTIMode有两种,一种是Interrupt(中断模式),另一种是Event(事件模式)
如果想深入了解这两种模式有什么区别可以参考下面文章
http://blog.sina.com.cn/s/blog_4d1854230101dcui.html
EXTI_Trigger_Falling
震动传感器
未发生震动 - - 高电平
发生震动 - - - -低电平
所以我们需要在下降沿的时候中断
exti_init.EXTI_Trigger = EXTI_Trigger_Falling;
3.配置NVIC(中断优先级)
NVIC_IRQChannel
可以去stm32f10x.h中找到,因为EXTI_Line是1
所以nvic_init.NVIC_IRQChannel=EXTI1_IRQn;
NVIC_IRQChannelPreemptionPriority(抢占优先级)
说到优先级,可以去misc.h中找到图下代码
我们需要配置一个优先级组,因为我们就一个中断,所以随便选择,我写的是第三种
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
根据组的不同抢占优先级和子优先级的范围就不同,这就随便写了。
nvic_init.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_IRQChannelSubPriority(子优先级)
nvic_init.NVIC_IRQChannelSubPriority = 1;
配置完NVIC我们需要配置中断时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
还需要将GPIO1设置为外部中断源
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1);
4.编写中断服务函数
在main.c中编写中断函数
中断函数在启动文件中
我们需要判断中断是否发生
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
这个函数中有个标识位RESET也就是0的意思
发生了中断开灯就完了
我们还需要写一个清除中断标志,要不中断创建了一个标志不清除,那么这个标志一直存在,就像一直申请内存,用完不释放,一直被占用导致内存泄漏一样,所以需要清除一下。
void EXTI_ClearFlag(uint32_t EXTI_Line);
提示:发生中断时中断函数会自动执行
代码
exti.h
#include "stm32f10x.h"
void exti_init(void);
exti.c
#include "stm32f10x.h"
#include "exti.h"
void exti_init(void)
{
//1.ÅäÖÃGPIO
GPIO_InitTypeDef shake_init;
EXTI_InitTypeDef exti_init;
NVIC_InitTypeDef nvic_init;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
shake_init.GPIO_Mode = GPIO_Mode_IPD;
shake_init.GPIO_Pin = GPIO_Pin_1;
shake_init.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOA, &shake_init);
//2.ÅäÖÃEXTIÍⲿÖжÏ
exti_init.EXTI_Line = EXTI_Line1;
exti_init.EXTI_Mode = EXTI_Mode_Interrupt;
exti_init.EXTI_Trigger = EXTI_Trigger_Falling;
exti_init.EXTI_LineCmd = ENABLE;
EXTI_Init(&exti_init);
//3.ÅäÖÃNVICÖжϿØÖÆÆ÷
nvic_init.NVIC_IRQChannel = EXTI1_IRQn;
nvic_init.NVIC_IRQChannelPreemptionPriority = 1;
nvic_init.NVIC_IRQChannelSubPriority = 1;
nvic_init.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic_init);
}
main.c
#include "stm32f10x.h"
#include "led.h"
#include "relay.h"
#include "shake.h"
#include "exti.h"
void delay(uint16_t time)
{
uint16_t i =0;
while(time--){
i=12000;
while(i--);
}
}
int main()
{
Led_init();
Relay_Init();
Shake_init();
exti_init();
GPIO_SetBits(GPIOA, GPIO_Pin_3);
GPIO_SetBits(GPIOC, GPIO_Pin_13);
while(1)
{
}
}
void EXTI1_IRQHandler(void)
{
if (EXTI_GetITStatus( EXTI_Line1 ) != RESET){// ÅжÏÊÇ·ñ·¢ÉúÖжÏ
GPIO_ResetBits(GPIOA, GPIO_Pin_3);
delay(1000);
GPIO_SetBits(GPIOA, GPIO_Pin_3);
}
EXTI_ClearFlag(EXTI_Line1);
}