STM32_08_中断(☆☆☆)

中断介绍

中断和轮询

中断的硬件连接

EXTI0

  • 类似这种梯形就是多选一,例如PB1这个引脚的电平变化会映射到EXTI1硬联。
  • STM32 通过SYSCFG(或 F1 系列的 AFIO)的 EXTICR 配置寄存器,将特定 GPIO 引脚(如 PA0、PB1 等)"绑定" 到对应的 EXTI 中断线(EXTIn);当该引脚发生电平变化(上升沿、下降沿等)时,就会触发对应 EXTI 线,进而产生中断请求或事件信号。
  • 通过AFIO_EXTICRx配置GPIO线上的外部中断/事件,而如果想让GPIO线映射过来,首先要使能AFIO时钟。
  1. GPIO变化过程映射到EXTI线上
    打开AFIO时钟
  2. EXTI控制器内部
    上升沿/下降沿
    中断/事件
    清除中断到来位
    使能中断 - 不能屏蔽

NVIC介绍

NVIC中断优先级

速记:抢占式优先级是主优先级,响应优先级是子优先级。

  1. 中断A:主1子0
    中断B:主0子2
    -> 同时到 -> B
  2. 中断A:主1子0
    中断B:主0子2
    -> A先到,执行, B到了 -> B会抢占A的资源
  3. 中断A:主1子0
    中断B:主1子2
    -> 同时到 -> A先执行
  4. 中断A:主1子0
    中断B:主1子2
    -> B先到, 执行, A到了 -> A不会抢占B的资源
  5. 中断A:主1子2
    中断B:主1子2
    -> 同时到 -> 看表(谁在前谁先运行)

4bit:

主4子0

主:0-15子: ( < 2的n次方)
主3子1
主:0-7子:0-1
主2子2
主:0-3子:0-3
主1子3
主:0-1子:0-7

主0子4

主: 子:0-15

外部EXTI配置

中断处理

思考:

PA0 - EXTI0 - EXTI - NVIC - CPU

谁触发的中断?

EXTI0触发的中断 ,并不是PA0,是PA0映射到了EXTI0,EXTI0一步步触发的中断。

stm32 - 中断处理函数 - 名字定好了 - 启动文件中

打开启动文件: XXX_IRQHandler

EXTI0_IRQHandler

1.中断处理函数

2.EXIT0触发的中断调用该函数

中断处理函数没有定义, 需要开发人员自定义

中断处理流程

中断处理函数

1.APP/EXTI目录

2.APP/EXTI

新建exti.c exti.h

3.添加工程组/ C/C++头文件路径

4.工程组添加 stm32f10x_exti.c - EXTI库函数

KEY0按键

相关库函数和结构体

代码示例:

KEY0按键

思路:

KEY0一旦按下,GPIOE4就映射到了EXTI4上,然后会有一个下降沿,EXTI4设置中断模式,又能检测下降沿,然后发送一个中断信号到NVIC,NVIC发现是EXTI4的中断,给你分配了一个主优先级和子优先级,然后NVIC把中断信号发送到CPU里。

相关库函数和结构体

代码示例:

注意:

EXTI和NVIC有时钟,但是它们自动使能。IRQn在stm32f10x.h头文件的第167行,下面就是各个中断源。

xxxx_IRQn - 中断源 - 谁触发的中断 -

NVIC_InitTypeDef - IRQChannel - 赋值

接下来调用中断处理函数

实战演练

代码示例:

exti.h:

cpp 复制代码
// exti.h
#ifndef __EXTI_H__
#define __EXTI_H__

#include "stm32f10x.h"

// KEY0
extern void My_EXTI_Init(void);
#endif

exti.c:

cpp 复制代码
// exti.c
#include "exti.h"
#include "systick.h"
#include "beep.h"
#include "key.h"
// 
// 中断的初始化 - 发送中断的流程
// KEY0 - GPIOE4
// KEY0 - GPIOE4 - EXTI4 - EXTI控制器 - NVIC - CPU核
//
void My_EXTI_Init(void){
	// 1.KEY0 - 配置为上拉输入
	
	// 2.GPIOE4映射到EXTI4上
	// 2.1打开AFIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	// 2.2将GPIOE3映射到EXTI4上
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);

	// 3.EXTI控制器的配置
	// EXTI4 - 中断模式 - 下降沿触发
	EXTI_InitTypeDef EXTI_Config;
	EXTI_Config.EXTI_Line = EXTI_Line4;							 // EXTI4
	EXTI_Config.EXTI_Mode = EXTI_Mode_Interrupt;		 // 中断模式
	EXTI_Config.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿
	EXTI_Config.EXTI_LineCmd = ENABLE;							 // 使能
	EXTI_Init(&EXTI_Config);
	
	// 4.NVIC控制器的配置
	// EXTI4触发的中断 - 主1 - 子2 - 使能
	NVIC_InitTypeDef NVIC_Config; 
	NVIC_Config.NVIC_IRQChannel = EXTI4_IRQn; // EXTI4触发的中断
	NVIC_Config.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_Config.NVIC_IRQChannelSubPriority = 2;
	NVIC_Config.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_Config);
}
//
// 中断处理函数到启动文件(.s)中找
// 中断处理函数
// 触发该函数 - KEY0按下 - 蜂鸣器控制
//
void EXTI4_IRQHandler(void){
	// 判断是否是EXTI4触发的中断
	if(EXTI_GetITStatus(EXTI_Line4) == SET){
		// 业务处理
		delay_ms(10); // 去抖动
		if(KEY0 == 0)
			BEEP = !BEEP;
	}
	// 清除中断到来位
	EXTI_ClearITPendingBit(EXTI_Line4);
}

main.c:

cpp 复制代码
// main.c 
#include "stm32f10x.h"
#include "led.h"
#include "beep.h"
#include "system.h"
#include "systick.h"
#include "key.h"
#include "exti.h"

int main(void)
{
	// |--|--|
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	// LED初始化 
	LED_Init();
	// BEEP初始化 
	BEEP_Init();
	// systick初始化 
	Systick_Init();
	// KEY初始化
	KEY_Init();
	// EXTI初始化
	// 作用:按下KEY0 - EXTI4 - EXTI -NVIC -> 中断信号 -> CPU核
	My_EXTI_Init();
	
	while(1)
	{
		// 业务逻辑 - 做其他事情
		LED1 = !LED1;
		delay_ms(1000);
	}
	// 启动文件 - main函数 - 初始化 - while(1)
	// while(1) -------(1)-----------------> 执行
	//								 ^CPU核
	// 								/
	// 按下KEY0->中断信号
	//										CPU核从(1)停止 -> 执行中断处理函数
	//																		EXTI4_IRQHandler
	//																    执行完返回(1)继续
}

补充:

  1. 和中断相关的三类函数:
    xxx_ITConfig - 打开/关闭控制器的某个中断
    xxx_GetITStatus - 判断某个中断是否到来
    xxx_ClearITPendingBit - 中断中断到来位
  2. EXTI_GetITStatus(EXTI_Line4) == SET的作用
    例如:
    EXTI6触发了中断, 调用其中断处理函数
    // EXTI5 6 7 8 9 触发的中断调用该函数
    // 问 - 是哪个硬件线触发的中断调用的该函数
    void EXTI9_5_IRQHandler(void){
    if(EXTI_GetITStatus(EXTI_Line5) == SET){...}
    else if(EXTI_GetITStatus(EXTI_Line6) == SET){...}
    else if(EXTI_GetITStatus(EXTI_Line7) == SET){...}
    else if(EXTI_GetITStatus(EXTI_Line8) == SET){...}
    else if(EXTI_GetITStatus(EXTI_Line9) == SET){...}
    }

拓展:

循环开关灯,按下key0触发中断,开启蜂鸣器;按下key_up触发中断,关闭蜂鸣器。

KEY_UP使用中断来触发:

PA0 - 下拉输入

低电平 - 高电平 - 低电平

检测 - 上升沿

谁触发的中断 - EXTI0

初始化函数:

1.PA0 映射到EXTI0

2.EXTI配置 - EXTI0, 中断模式, 上升沿, 使能

3.NVIC配置 - EXTI0, 主1, 子0, 使能

中断处理函数:

EXTI0_IRQHandler

exti.c:

cpp 复制代码
// exti.c
#include "exti.h"
#include "systick.h"
#include "beep.h"
#include "key.h"
// 
// 中断的初始化 - 发送中断的流程
// KEY0 - GPIOE4
// KEY0 - GPIOE4 - EXTI4 - EXTI控制器 - NVIC - CPU核
// KEY_UP - GPIOA0 - EXTI0 - EXTI控制器 - NVIC - CPU核
//
void My_EXTI_Init(void){
	// 1.KEY0 - 配置为上拉输入
	// 1.2KEY_UP配置为下拉输入
	// 2.GPIOE4映射到EXTI4上
	// 2.1打开AFIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	// 2.2将GPIOE3映射到EXTI4上
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4);
	// 2.2将GPIOA0映射到EXTI0上
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
	
	// 3.EXTI控制器的配置
	// EXTI4 - 中断模式 - 下降沿触发
	EXTI_InitTypeDef EXTI_Config;
	EXTI_Config.EXTI_Line = EXTI_Line4;							 // EXTI4
	EXTI_Config.EXTI_Mode = EXTI_Mode_Interrupt;		 // 中断模式
	EXTI_Config.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿
	EXTI_Config.EXTI_LineCmd = ENABLE;							 // 使能
	EXTI_Init(&EXTI_Config);
	// 3.2EXTI0 - 中断模式 - 上升沿触发
	EXTI_Config.EXTI_Line = EXTI_Line0;
	EXTI_Config.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_Config.EXTI_Trigger = EXTI_Trigger_Rising;
	EXTI_Config.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_Config);
	
	// 4.NVIC控制器的配置
	// EXTI4触发的中断 - 主1 - 子2 - 使能
	NVIC_InitTypeDef NVIC_Config; 
	NVIC_Config.NVIC_IRQChannel = EXTI4_IRQn; // EXTI4触发的中断
	NVIC_Config.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_Config.NVIC_IRQChannelSubPriority = 2;
	NVIC_Config.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_Config);
	
	// 4.2EXTI0触发的中断 - 主1 - 子0 - 使能
	NVIC_Config.NVIC_IRQChannel = EXTI0_IRQn;
	NVIC_Config.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_Config.NVIC_IRQChannelSubPriority = 0;
	NVIC_Config.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_Config);
}
//
// 中断处理函数到启动文件(.s)中找
// 中断处理函数
// 触发该函数 - KEY0按下 - 蜂鸣器控制
//
void EXTI4_IRQHandler(void){
	// 判断是否是EXTI4触发的中断
	if(EXTI_GetITStatus(EXTI_Line4) == SET){
		// 业务处理
		delay_ms(10); // 去抖动
		if(KEY0 == 0)
			BEEP = 1;
	}
	// 清除中断到来位
	EXTI_ClearITPendingBit(EXTI_Line4);
}
	
void EXTI0_IRQHandler(void){
	// 判断是否是EXTI0触发的中断
	if(EXTI_GetITStatus(EXTI_Line0) == SET){
		// 业务处理
		delay_ms(10);
		if(KEY_UP == 1)
			BEEP = 0;
	}
		// 清除中断到来位
	EXTI_ClearITPendingBit(EXTI_Line0);
}

	

main.c:

cpp 复制代码
// main.c 
#include "stm32f10x.h"
#include "led.h"
#include "beep.h"
#include "system.h"
#include "systick.h"
#include "key.h"
#include "exti.h"

int main(void)
{
	// |--|--|
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	// |--|--|
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	// LED初始化 
	LED_Init();
	// BEEP初始化 
	BEEP_Init();
	// systick初始化 
	Systick_Init();
	// KEY初始化
	KEY_Init();
	// EXTI初始化
	// 作用:按下KEY0 - EXTI4 - EXTI -NVIC -> 中断信号 -> CPU核
	My_EXTI_Init();
	
	while(1)
	{
		// 业务逻辑 - 做其他事情
		LED1 = !LED1;
		delay_ms(1000);
	}
	// 启动文件 - main函数 - 初始化 - while(1)
	// while(1) -------(1)-----------------> 执行
	//								 ^CPU核
	// 								/
	// 按下KEY0->中断信号
	//										CPU核从(1)停止 -> 执行中断处理函数
	//																		EXTI4_IRQHandler
	//																    执行完返回(1)继续
}
相关推荐
蓝斯49710 小时前
从.到实现一个Windows上的虚拟hid键盘设备
windows·单片机·计算机外设
熙芯XiChip11 小时前
硬实时与软实时RTOS核心区别
嵌入式硬件
伟大的前程11 小时前
unsigned int 定义的变量 在 电脑的 64 位系统中 和 32 位 cortex-m0 和 8 位的 51 单片机
单片机·嵌入式硬件
小渔村的拉线工11 小时前
1.HPM6E80 解析芯片整体工作原理和存储架构
单片机·嵌入式硬件·mcu·架构·hpm6e80
熙芯XiChip12 小时前
CAN与CANopen的本质区别
单片机
lilihewo13 小时前
Altium Designer 封装库的创建(分享pcb 3D封装库)
嵌入式硬件·51单片机
神罚天下ET14 小时前
典型arm位单片机启动流程(从上电到main.c)
c语言·arm开发·单片机
碧海银沙音频科技研究院14 小时前
蓝牙耳机使用WSDF5361锂保芯片进入船运模式实现方法
嵌入式硬件·算法
Zevalin爱灰灰15 小时前
模拟信号链电路——基于TL431的电压基准电路
单片机·嵌入式硬件
csdn杰哥16 小时前
瑞萨单片机开发教程[二十七] DTC 外部中断触发传输实验
单片机·嵌入式硬件·mongodb·瑞萨·ra6m5