stm32 - 中断

stm32 - 中断

概念

stm32 支持的中断资源(都属于外设)

  • EXTI
  • TIM
  • ADC
  • USARt
  • SPI
  • I2C

stm32支持的中断

内核中断

外设中断

中断通道与优先级

一个外设可能占用多个中断通道(一个EXTI外设模块,可以有多个中断通道)

每个中断通道有16个优先级

中断向量表

自定义的中断服务函数,由编译器随机指定函数地址

stm32的中断,由于硬件的限制,只能跳到固定的地址执行程序

为了能让硬件跳转到一个不固定的中断函数中, 需要在内存中定义一个地址列表,这个列表的地址是固定的,中断发生后,先跳到这个固定位置,然后在这个固定位置,由编译器加上一条跳转到中断函数的代码,这样中断就可以跳转到任意位置了

NVIC 嵌套中断向量控制器

用于同一分配中断优先级和管理中断的,NVIC是一个内核外设
一个外设可能占用多个中断通道,所以有n条线

NVIC只有一个输出口,其根据每个中断的优先级分配中断的先后顺序

然后通过仅有的一个输出口,通知CPU应该处理哪个中断

优先级

  • 响应优先级:"插队看病"
  • 抢占优先级:"中断嵌套"

NVIC中断优先级由优先级寄存器的4位(0~15)决定,数值越小优先级越高

高n位为抢占优先级,4-n位为响应优先级

抢占优先级高的可以中断嵌套,响应优先高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队

中断

EXTI

检测指定GPIO的电平信号

电平信号发生变化时,EXTI申请中断

概念

触发方式

上升沿(低->高)触发中断

下降沿(高->低)触发中断

双边沿(上升沿和下降沿都可以触发中断)

软件触发:引脚电平未发生变化,通过在软件中调用执行代码触发中断

GPIO

任意的GPIO口都可以当做外部中断的引脚,但是相同的pin引脚不能同时触发中断(PA0/PB0; PA1/PB1/PC1)

通道数

16个GPIO_pin,外加PVD输出,RTC闹钟,USB唤醒,以太网唤醒

触发响应方式

中断响应:申请中断,让CPU执行中断函数

事件响应: 当外部中断检测到引脚电平变化时,正常的流程是选择触发中断,也可以选择触发事件,那么外部中断的信号就不会通向CPU了,而是通向其他外设,用来触发其他外设的操作

基本结构

EXTI模块支持20个中断通道数,16个pin_脚+PVD+RTC+USB+ETH
针对GPIO,利用AFIO进行中断选择(当使用GPIO实现外部中断时)

PA0/PB0/PC0... 通过AFIO选择器选择一个作为pin_0通道

PA1/PB1/PC1... 通过AFIO选择器选择一个作为pin_1通道

PA2/PB2/PC2... 通过AFIO选择器选择一个作为pin_2通道
每个通道有16可配置优先级,通过NVIC进行优先级配置

NVIC可以进行优先级分组(抢占/响应),每个分组都有取值范围,不同的取值范围,在CPU进行中断服务的时机不同
注意16个引脚输入通道,最终只有7个输入,其中9-5和15-10便成了路两路输出通道,因此需要中断标志位判断哪一个中断过来


PB14引脚的电平信号就可以通过AFIO进入到EXTI电路中

例子- 对射式红外传感器计次

main.c

c 复制代码
#include "stm32f10x.h"
#include "OLED.h"
#include "infrCountSensor.h"

int main()
{
	OLED_Init();
	OLED_ShowString(1,1,"helloworld");
	OLED_ShowString(2,1,"count: ");
	InfrCountSensor_Init(14);
	while (1) 
	{
		OLED_ShowNum(2,8,InfrCountSensor_GetCount(),8);
	}
}

infrCountSensor.h

c 复制代码
#ifndef __INFRCOUNTSENSOR_H__
#define __INFRCOUNTSENSOR_H__
#include "stm32f10x.h"
void InfrCountSensor_Init(unsigned char pin_num);
unsigned int InfrCountSensor_GetCount();
uint16_t GPIO_Pin_Num_Set(uint16_t pin_num);
uint8_t GPIO_AFIO_Pin_Num_Select(uint16_t pin_num);
uint32_t EXTI_Line_Set(unsigned char pin_num);
#endif

infrCountSensor.c

c 复制代码
#include "infrCountSensor.h"

static unsigned int infrCountSensor_count;

uint16_t GPIO_Pin_Num_Set(uint16_t pin_num)
{
	uint16_t GPIO_Pin_num=0;
	switch (pin_num)
	{
		case 0: {return GPIO_Pin_0;break;}
		case 1: {return GPIO_Pin_1;break;}
		case 2: {return GPIO_Pin_2;break;}
		case 3: {return GPIO_Pin_3;break;}
		case 4: {return GPIO_Pin_4;break;}
		case 5: {return GPIO_Pin_5;break;}
		case 6: {return GPIO_Pin_6;break;}
		case 7: {return GPIO_Pin_7;break;}
		case 8: {return GPIO_Pin_8;break;}
		case 9: {return GPIO_Pin_9;break;}
		case 10: {return GPIO_Pin_10;break;}
		case 11: {return GPIO_Pin_11;break;}
		case 12: {return GPIO_Pin_12;break;}
		case 13: {return GPIO_Pin_13;break;}
		case 14: {return GPIO_Pin_14;break;}
		case 15: {return GPIO_Pin_14;break;}
		default: return GPIO_Pin_num;
	}

}

uint8_t GPIO_AFIO_Pin_Num_Select(uint16_t pin_num)
{
	uint8_t AFIO_Pin_num=0;
	switch (pin_num)
	{
		case 0: {return GPIO_PinSource0;break;}
		case 1: {return GPIO_PinSource1;break;}
		case 2: {return GPIO_PinSource2;break;}
		case 3: {return GPIO_PinSource3;break;}
		case 4: {return GPIO_PinSource4;break;}
		case 5: {return GPIO_PinSource5;break;}
		case 6: {return GPIO_PinSource6;break;}
		case 7: {return GPIO_PinSource7;break;}
		case 8: {return GPIO_PinSource8;break;}
		case 9: {return GPIO_PinSource9;break;}
		case 10: {return GPIO_PinSource10;break;}
		case 11: {return GPIO_PinSource11;break;}
		case 12: {return GPIO_PinSource12;break;}
		case 13: {return GPIO_PinSource13;break;}
		case 14: {return GPIO_PinSource14;break;}
		case 15: {return GPIO_PinSource14;break;}
		default: return AFIO_Pin_num;
	}
}

uint32_t EXTI_Line_Set(unsigned char pin_num)
{
	uint32_t EXTI_Line_num=0;
	switch (pin_num)
	{
		case 0: {return EXTI_Line0;break;}
		case 1: {return EXTI_Line1;break;}
		case 2: {return EXTI_Line2;break;}
		case 3: {return EXTI_Line3;break;}
		case 4: {return EXTI_Line4;break;}
		case 5: {return EXTI_Line5;break;}
		case 6: {return EXTI_Line6;break;}
		case 7: {return EXTI_Line7;break;}
		case 8: {return EXTI_Line8;break;}
		case 9: {return EXTI_Line9;break;}
		case 10: {return EXTI_Line10;break;}
		case 11: {return EXTI_Line11;break;}
		case 12: {return EXTI_Line12;break;}
		case 13: {return EXTI_Line13;break;}
		case 14: {return EXTI_Line14;break;}
		case 15: {return EXTI_Line15;break;}
		default: return EXTI_Line_num;
	}
}

void InfrCountSensor_Init(unsigned char pin_num)
{
	// 打开GPIO/AFIO时钟
	// GPIO和AFIO是APB2总线的外设,需要手动开启时钟,RCC用于配置内核外的外设时钟
	// EXIT中断默认开始时钟,NVIC是内核外设(内核外设也无需开启时钟)
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
		 
	// 配置GPIO 输入模式
	uint16_t GPIO_Pin_num=0;
	GPIO_Pin_num=GPIO_Pin_Num_Set(pin_num);
	
	GPIO_InitTypeDef GPIO_InitStructure; 
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;		// 上拉输入
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_num;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	// 配置AFIO 选择器(中断引脚选择)
	// GPIO_AFIODeInit(); 		// 清空配置
	// GPIO_PinLockConfig(); 	// 锁定引脚,防止意外被更改
	// GPIO_PinRemapConfig(); 		// 引脚重映射
	// 选择指定的GPIO / pin 作为外部中断源
	uint8_t AFIO_Pin_num=0;
	AFIO_Pin_num=GPIO_AFIO_Pin_Num_Select(pin_num);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,AFIO_Pin_num); // 配置数据选择器,
															// AFIO_Pin_num 是AFIO的中断选择器号,这里是第14个数据选择器
	
	// 配置EXTI中断,触发方式,触发响应方式
	// EXTI_DeInit(); 		// 清除配置
	// EXTI_StructInit(); 	// 结构体赋值
	// EXTI_GenerateSWinterrupt(); // 软件触发
	// 外部中断的外部状态寄存器会设置对应的标志位
	// 在主程序中读写外部中断标志位,在中断服务函数中读写外部中断标志位
	uint32_t EXTI_Line_num=0;
	EXTI_Line_num=EXTI_Line_Set(pin_num);
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line=EXTI_Line_num;				// 指定要配置的中断线,中断EXTI输入通道
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;					// 中断线的新状态,开启中断
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt ;		// 中断模式interrupt或事件模式event
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;	//触发方式,下降沿
	EXTI_Init(&EXTI_InitStructure); 						// 初始化
	
	// 配置NVIC,中断优先级,进入CPU再执行中断程序
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断分组,两位抢占,两位响应
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn;		// 中断EXTI输出通道,15-10占用一个通道,在中断服务函数中需要判断中断标志位
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;	// 抢占优先级(在抢占中断中)
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;		// 响应优先级 (在响应中断中)
	NVIC_Init(&NVIC_InitStructure);
}

void EXTI15_10_IRQHandler(void) // 中断函数
{
	// 中断标志位的判断
	if (EXTI_GetITStatus(EXTI_Line14)==SET)
	{
		// 清楚中断标志位
		EXTI_ClearITPendingBit(EXTI_Line14);
		
		infrCountSensor_count++;
	}
}


unsigned int InfrCountSensor_GetCount()
{
	return infrCountSensor_count;
}

例子 - 旋转编码器

对于两个中断,AFIO、EXTI、NVIC的code方式

main.c

c 复制代码
#include "stm32f10x.h"
#include "Delay.h"
#include "LED.h"
#include "key.h"
#include "Buzzer.h"
#include "PhotoSensor.h"
#include "OLED.h"
#include "infrCountSensor.h"
#include "encoder.h"

int main()
{
	OLED_Init();
	OLED_ShowString(1,1,"helloworld");
	OLED_ShowString(2,1,"count: ");
	Encoder_Init();
	while (1) 
	{
		OLED_ShowSignedNum(2,8,Encoder_Get(),8);
	}
}

encoder.h

c 复制代码
#ifndef __ENCODER_H__
#define __ENCODER_H__

#include "stm32f10x.h"
void Encoder_Init();
int16_t Encoder_Get();
#endif

encoder.c

c 复制代码
#include "encoder.h"

int16_t encoder_rotate;

void Encoder_Init()
{
	// 打开GPIO/AFIO时钟
	// GPIO和AFIO是APB2总线的外设,需要手动开启时钟,RCC用于配置内核外的外设时钟
	// EXIT中断默认开始时钟,NVIC是内核外设(内核外设也无需开启时钟)
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
		 
	// 配置GPIO 输入模式
	GPIO_InitTypeDef GPIO_InitStructure; 
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;		// 上拉输入
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	// 配置AFIO 选择器(中断引脚选择)
	// 选择指定的GPIO / pin 作为外部中断源
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0); // 配置数据选择器,
															// AFIO_Pin_num 是AFIO的中断选择器号,这里是第14个数据选择器
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1); // 配置数据选择器,
															// AFIO_Pin_num 是AFIO的中断选择器号,这里是第14个数据选择器


	// 配置EXTI中断,触发方式,触发响应方式
	// 外部中断的外部状态寄存器会设置对应的标志位
	// 在主程序中读写外部中断标志位,在中断服务函数中读写外部中断标志位;
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line=EXTI_Line0 | EXTI_Line1;				// 指定要配置的中断线,中断EXTI输入通道
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;					// 中断线的新状态,开启中断
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt ;		// 中断模式interrupt或事件模式event
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;	//触发方式,下降沿
	EXTI_Init(&EXTI_InitStructure); 						// 初始化
	
	// 对两个通道分别设置优先级
	// 配置NVIC,中断优先级,进入CPU再执行中断程序
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断分组,两位抢占,两位响应
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;		// 中断EXTI输出通道
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;	// 抢占优先级(在抢占中断中)
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;		// 响应优先级 (在响应中断中)
	NVIC_Init(&NVIC_InitStructure);
	
	NVIC_InitStructure.NVIC_IRQChannel=EXTI1_IRQn;		// 中断EXTI输出通道
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;	// 抢占优先级(在抢占中断中)
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;		// 响应优先级 (在响应中断中)
	NVIC_Init(&NVIC_InitStructure);
}

void EXTI0_IRQHandler()
{
	if (EXTI_GetITStatus(EXTI_Line0)==SET)
	{
		EXTI_ClearITPendingBit(EXTI_Line0);
		
		if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0)
		{
			encoder_rotate--;
		} 
	}
}

void EXTI1_IRQHandler()
{
	if (EXTI_GetITStatus(EXTI_Line1)==SET)
	{
		EXTI_ClearITPendingBit(EXTI_Line1);
		
		if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0)
		{
			encoder_rotate++;
		} 
	}
}

int16_t Encoder_Get()
{
	return encoder_rotate;
}
相关推荐
m0_748254091 小时前
STM32--超声波模块(HC—SR04)(标准库+HAL库)
stm32·单片机·嵌入式硬件
南城花随雪。1 小时前
单片机:实现FFT快速傅里叶变换算法(附带源码)
单片机·嵌入式硬件·算法
逝灮1 小时前
【蓝桥杯——物联网设计与开发】基础模块8 - RTC
stm32·单片机·嵌入式硬件·mcu·物联网·蓝桥杯·rtc
LXL_242 小时前
模拟——郑益慧_笔记1_绪论
嵌入式硬件
weixin_452600698 小时前
串行时钟保持芯片D1380/D1381,低功耗工作方式自带秒、分、时、日、日期、月、年的串行时钟保持芯片,每个月多少天以及闰年能自动调节
科技·单片机·嵌入式硬件·时钟·白色家电电源·微机串行时钟
森旺电子11 小时前
51单片机仿真摇号抽奖机源程序 12864液晶显示
单片机·嵌入式硬件·51单片机
不过四级不改名67713 小时前
蓝桥杯嵌入式备赛教程(1、led,2、lcd,3、key)
stm32·嵌入式硬件·蓝桥杯
小A15913 小时前
STM32完全学习——SPI接口的FLASH(DMA模式)
stm32·嵌入式硬件·学习
Rorsion13 小时前
各种电机原理介绍
单片机·嵌入式硬件
善 .16 小时前
单片机的内存是指RAM还是ROM
单片机·嵌入式硬件