一、中断原理
单片机的中断原理涉及CPU在执行程序过程中对紧急事件的响应机制。
假如有一个程序B必须要在程序A执行完毕后才能执行。这样在实际情况中可能会遇到一个问题。就是程序B有的时候需要先处理。比如,假设程序B是个按键程序,用户按下按键时,需要优先处理按键程序,而这个时候程序A还没有处理完,假设还需要3秒钟才能处理完。这个时候,用户就得等3秒才能得到程序响应,这样用户就会觉得程序卡顿。这种情况显然是要避免的.要解决这种情况,也就是当按键按下时,要暂停程序A,先去处理程序B,等响应完成后,再回头去处理程序A。这就是所谓的中断机制。
单片机的中断机制允许CPU暂停当前正在执行的任务,转而去处理一个紧急事件或称为中断源的特殊请求,处理完毕后再返回继续执行原来的任务。这个过程大致可以分为以下步骤:
1.中断请求:这是中断过程的开始,由中断源发起。中断源可以是外围设备、定时器溢出、外部信号变化等。
2.中断响应:CPU识别到中断请求后,如果条件允许(例如,当前没有执行更高优先级的中断服务程序),会暂停当前的程序执行,准备响应中断。
3.中断处理:CPU跳转到对应的中断服务程序去处理中断请求。这个过程中,CPU会保存当前执行状态(断点)以便之后能够恢复执行原程序。
4.中断返回:中断服务程序执行完成后,CPU从中断返回到之前被中断的程序继续执行。返回的过程中需要恢复之前保存的执行环境。
二、中断实例实验
利用中断控制LED灯的亮灭
要求:用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关(用杜邦线模拟代替)。采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。
项目配置流程参考:手把手教你STM32入门教程(标准库)
代码实现:
led.h头文件
#ifndef __LED_H
#define __LED_H
#include "stm32f10x.h"
void LED_Init(void);
#endif
led.c文件(初始化LED)
#include "led.h"
void LED_Init(void) // 定义LED初始化函数
{
GPIO_InitTypeDef GPIO_InitStructure; // 定义GPIO初始化结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 设置GPIO引脚为PA0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 设置GPIO模式为推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 设置GPIO速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); // 根据初始化结构体配置GPIOA
}
中断函数配置:exti_key.h
#ifndef __EXTI_KEY_H
#define __EXTI_KEY_H
#include "stm32f10x.h"
void EXTI_Key_Init(void);
#endif
exti_key.c
#include "exti_key.h"
#include "misc.h"
void EXTI_Key_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; // 定义GPIO初始化结构体
// 配置GPIOB的PB0引脚为输入模式
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能GPIOB时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 设置GPIO引脚为PB0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 设置GPIO模式为上拉输入模式
GPIO_Init(GPIOB, &GPIO_InitStructure); // 根据初始化结构体配置GPIOB
EXTI_InitTypeDef EXTI_InitStructure; // 定义外部中断初始化结构体
NVIC_InitTypeDef NVIC_InitStructure; // 定义NVIC中断初始化结构体
// 使能AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// 将GPIOB的PB0引脚映射到外部中断线0
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
// 配置外部中断线0的触发方式为下降沿触发
EXTI_InitStructure.EXTI_Line = EXTI_Line0; // 设置外部中断线为0
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; // 设置外部中断模式为中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; // 设置外部中断触发方式为下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE; // 使能外部中断线0
EXTI_Init(&EXTI_InitStructure); // 根据初始化结构体配置外部中断
// 配置NVIC中断优先级分组2
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 配置外部中断线0的中断优先级
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; // 设置中断通道为外部中断线0
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 设置抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 设置子优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断通道
NVIC_Init(&NVIC_InitStructure); // 根据初始化结构体配置NVIC中断
}
主函数main.c
#include "stm32f10x.h"
#define unsigned int uint8_t
#include "led.h"
#include "exti_key.h"
int main(void)
{
LED_Init();
EXTI_Key_Init();
while(1)
{
}
}
void EXTI0_IRQHandler(void)
{
if (EXTI_GetITStatus(EXTI_Line0) != RESET)
{
// 切换LED的状态
GPIOA->ODR ^= GPIO_Pin_0;
// 清除中断标志位
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
实物运行结果
按键中断点灯