STM32学习笔记(三)——STM32的中断

STM32学习笔记(三)——STM32的中断

一、中断编程的原理

1.1 什么是中断

1.1.1 生活中的中断

中断:正在进行的事务被突发事件打断,转而去处理这个突发事件,突发事件处理完成后回到被打断的事务继续执行,这一处理突发事件的过程叫中断

在这里插入图片描述

1.1.2 单片机中的中断

常规程序:main方法和被main方法调用的程序;由于main方法的执行而得到执行的程序,叫常规程序

在这里插入图片描述

用电脑给STM32串口发送程序,只要电脑发来了数据,STM32就必须马上处理这个数据,如果不及时处理,电脑发送新的数据,就会覆盖之前传送的数据,造成数据的丢失

为了实现,来了数据,立马处理这个数据的功能,我们写了一个方法(也就说写了一个函数,函数内部代码的功能为,先接收数据,再处理数据)在这里插入图片描述

流程为:在这里插入图片描述
我们将这样突发的事件,叫中断源
在main中程序叫常规程序
由于中断源触发而调用的程序叫中断响应函数

STM32中的中断概念:
由于中断源的触发,常规程序被打断,CPU转而运行中断响应函数,而后又回到常规程序的执行,这一过程叫做中断。

1.1.3 为什么要使用中断

突发事件有:
突发性:随机的在任意时刻发生、无预兆
紧急性:需要马上被处理

1.2 中断处理过程

保存现场:记录常规程序执行到哪了
清楚中断源:接电话,第一步按接听键挂断电话铃声
还原现场:还原导出常规程序执行的步骤,继续执行

在这里插入图片描述

1.3 中断的优先级

在这里插入图片描述

中断优先级:

  • 优先级的值小,优先级越高
  • 抢占优先级高的中断会对抢占优先级低的发生中断嵌套
  • 发生中断嵌套时,会发生排队,总优先级高的中断排在前面

在这里插入图片描述

例子1
执行A中断的过程总发生了中断B和C.
中断A先发生,而后中断B发生, 但中断B的抢占优先级高,所以发生中断嵌套

在这里插入图片描述

例子2
执行A中断的过程总发生了中断B和C.
中断A先发生,而后中断B和C同时发生, 且B和C的抢占优先级不比A的高,不发生中断嵌套
但需要判断B和C的中断优先级B为1100(12) C为0101(5) 根据优先级数低的优先级高,所以C先发生

在这里插入图片描述

例子3
执行A中断的过程总发生了中断B和C.
中断A先发生,而后中断B和C同时发生, 且B和C的抢占优先级不比A的高,不发生中断嵌套
但需要判断B和C的中断优先级B为0101(5) C为0100(4) 根据优先级数低的优先级高,所以C先发生

在这里插入图片描述

1.4 两种中断源类型

“脉冲型”中断源(罕见):中断提示信号一闪而过
“电平型”中断源(常见):中断信号持续,直到手动关闭,因此必须在中断响应函数结束前清除中断

在这里插入图片描述

在这里插入图片描述

1.5 中断源的四种状态

在这里插入图片描述

二、STM32的NVIC

2.1 NVVIC简介

NVIC(Nested Vectored Interrupt Controller)嵌套中断向量控制器,是一种核心外设,位于Cortex-M3内核中,负责管理中断

在这里插入图片描述

2.2 中断协作模型

中断源:由片上外设产生,同一片上外设可产生多个中断源
NVIC:管理中断,负责中断进入和中断退出,中断优先级设置等。
Flash:等于电脑里硬盘,存储写的中断响应函数程序
在这里插入图片描述

2.3 NVIC的内部结构

开关部分:负责中断屏蔽,将项目中使用不到中断源开关断开,进行屏蔽
中断优先级:由4个bit位进行存储,
中断仲裁:将存储的中断优先级拿出来进行比较,看看谁优先

在这里插入图片描述

中断优先级分组:我们可以根据需要,设置不同的分组方式

在这里插入图片描述

2.4 中断向量表

Flash存储器中,有各种代码,CPU在中断发生时,如何定位是执行这个中断的中断响应函数呢?

中断向量表:看成一个目录,Flash存储器内部从地址0开始的一段区域,按照中断号排列,每4个字节(STM32是32位处理器,1个字节8bit,4字节正好32位)存储一个中断响应函数地址

在这里插入图片描述

表在:中文参考手册9.1.2节
在这里插入图片描述

2.5 NVIC编程

编程接口:
在这里插入图片描述

中断优先级分组:移动中间的虚线,具体分配几个bit位给抢占优先级,分几个bit给子优先级
NVIC初始化:对NVIC某一路中断源进行初始化

在这里插入图片描述

NVIC_PriorityGroupConfig()函数详解:

在这里插入图片描述

NVIC_Init()

在这里插入图片描述

在这里插入图片描述

2.6 NVIC编程实例

根据中断编程模型,将中断编程分为三个部分:

在这里插入图片描述
在这里插入图片描述

问题1:如何让片上外设产生中断源?

  • 不同的片上外设开启方式不同
  • USART1外设开启某中断源的方法:USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);

问题2:如何配置NVIC?

  • 为啥NVIC不用开启时钟?因为NVIC在内核里,只要一上电就有时钟(大脑不能休眠)
  • 1.设置中断优先级分组
  • 2.填写初始化调查问卷(写名,填问题)

问题3:如何编写中断响应函数

  • 弱方法:标注为weak的方法可以被同名的方法覆盖,一开始中断向量表指向的是弱方法进行占位,当我们编写同名函数后,就指向了我们所编写的函数代码
  • 步骤:
    • 在.s文件中找到中断响应函数
    • 写一个同名函数覆盖它 void xxx_IRQHandler(void){}v
      void USART1_IRQHandler(void) { //具体的中断响应代码//主函数外部 }

总结:
在这里插入图片描述

三、STM32的EXTI简介

3.1 EXTI简介

EXTI(External Interrupt/Event Controller)外部中断/事件控制器
EXTI是stm32外设的众多片上外设之一,能够检测外部输入信号的变化边沿,并由此产生中断

在这里插入图片描述

3.2 EXTI的内部结构

在这里插入图片描述

事件和中断区别:
事件:指突发事件本身,一般是一个脉冲信号,事件处理过程不需要CPU介入

在这里插入图片描述

3.3 EXTI的通道

实际项目中有多个外部输入信号,故将此电路复制多份。
EXTI01~19合起来才是一个完整的片上外设

在这里插入图片描述

EXTI这么多通道用来作什么呢?

  • 让所有的IO口都具有触发中断的能力
    使用PA0触发外部中断了 就不能使用PB0、PC0…了
    在这里插入图片描述
  • 其它功能在这里插入图片描述

3.4 EXTI的寄存器组

在这里插入图片描述

其中:
上升沿选择寄存器和下降沿选择寄存器:对应硬件电路的上升沿 下降沿 和双边沿的边沿选择

软件触发寄存器:对应硬件图软件触发
中断屏蔽寄存器:对应开关
挂起寄存器:对应那个框
事件屏蔽寄存器:控制事件屏蔽的开关

3.4 EXTI的标准库编程

3.4.1 EXTI标准库编程接口

在这里插入图片描述

EXTI_Init():

  • 初始化EXTI20个通道中的某一个
  • 参数结构体指针类型(填调查问卷方式)在这里插入图片描述
    例子:
    在这里插入图片描述

EXTI_GenerateSWInterrupt()

  • 产生软件中断在这里插入图片描述

EXTI_GetFlagStatus()

  • 获取中断标志位(硬件图中的4,获取中断挂起的bit位)在这里插入图片描述

EXTI_ClearFlag()

  • 清除标志位(硬件图中的4,清楚中断挂起的bit位)在这里插入图片描述

EXTI_GetITStatus()

  • 中断屏蔽寄存器与上中断挂起寄存器的值,中断屏蔽开关闭合同时中断被触发,中断挂起寄存器值也为1,函数才返回1在这里插入图片描述

EXTI_ClearITPendingBit()

  • 同EXTI_ClearFlag()在这里插入图片描述
3.4.2 EXTI标准库编程实验
3.4.2.1 实验介绍

在这里插入图片描述

曾经我们通过while循环读读取上次和当前的值,变化代表按键发生了动作
现在我们用外部中断的方法,在上升沿到来时,EXTI外设产生中断,NVIC模块对中断进行管理,触发中断响应函数

3.4.2.2 实验思路

在这里插入图片描述

GPIO片上外设:对按键使用的IO进行初始化

AFIO片上外设:EXTI有20个通道,拿出0-15个通道,对应GPIOx的0-15号引脚,引脚组x由AFIO选择,通过复用器选择编号在这里插入图片描述
在这里插入图片描述

EXTI片上外设:使用外部中断,产生中断源

NVIC核内外设:配置中断参数在这里插入图片描述

编写中断响应函数
- 调用函数

  • 找到.s文件在这里插入图片描述
  • 写同名函数覆盖
    在这里插入图片描述
    函数里写:
  • 清除中断源 EXTI_ClearITPendingBit(EXTI_Line1);
  • 编写功能函数
#include "stm32f10x.h"
#include "stm32f10x_pal.h"

int main(void)
{
	//4.1设置中断优先级分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	PAL_Init();
	//1.初始化IO引脚
	//将PA0和PA1分别设置为输入上拉模式
	//开启GPIOA的时钟
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//PA0,PA1
	GPIO_InitTypeDef GPIOInitStruct;
	GPIOInitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
	GPIOInitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GPIOA,&GPIOInitStruct);
	//2.配置EXTI的引脚映射
	//开启AFIO的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	//PA0->EXTI0
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
	//PA1->EXTI1
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1);
	//3.配置EXTI
	//3.1初始化EXTI0
	EXTI_InitTypeDef EXTIInitStruct;
	EXTIInitStruct.EXTI_Line = EXTI_Line0;
	EXTIInitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTIInitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
	EXTIInitStruct.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTIInitStruct);
	//3.2初始化EXTI1
	EXTIInitStruct.EXTI_Line = EXTI_Line1;
	EXTIInitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTIInitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
	EXTIInitStruct.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTIInitStruct);
	
	//4.初始化NVIC
	//4.2初始化某一路的中断
	NVIC_InitTypeDef NVICInitStruct;
	
	//4.2.1EXTI0
	NVICInitStruct.NVIC_IRQChannel = EXTI0_IRQn;
	NVICInitStruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVICInitStruct.NVIC_IRQChannelSubPriority = 2;
	NVICInitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVICInitStruct);
	//4.2.2EXTI1
	NVICInitStruct.NVIC_IRQChannel = EXTI1_IRQn;
	NVICInitStruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVICInitStruct.NVIC_IRQChannelSubPriority = 2;
	NVICInitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVICInitStruct);
	
	//5.初始化PC13
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	
	GPIOInitStruct.GPIO_Mode = GPIO_Mode_Out_OD; //open-drain开漏模式
	GPIOInitStruct.GPIO_Pin = GPIO_Pin_13;
	GPIOInitStruct.GPIO_Speed = GPIO_Speed_2MHz;

	GPIO_Init(GPIOC,&GPIOInitStruct);
	
	
	while(1)
	{
	}
}

void EXTI0_IRQHandler(void)
{
	//清除中断
	EXTI_ClearITPendingBit(EXTI_Line0);
	//点亮LED-向PC13写0
	GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
	
}

void EXTI1_IRQHandler(void)
{
	//清除中断
	EXTI_ClearITPendingBit(EXTI_Line1);
	//熄灭LED
	GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);

}

若是PA5和PA6产生中断,其中断处理函数为同一个,在调用中断处理函数时,怎么知道是哪个中断产生的呢?

  • 只需要判断其中断挂起寄存器的值,若为1,则代表是这个中断源产生的中断
#include "stm32f10x.h"
#include "stm32f10x_pal.h"

int main(void)
{
	//4.1设置中断优先级分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	PAL_Init();
	//1.初始化IO引脚
	//将PA0和PA1分别设置为输入上拉模式
	//开启GPIOA的时钟
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOA,ENABLE);
	//PA0,PA1
	GPIO_InitTypeDef GPIOInitStruct;
	GPIOInitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6;
	GPIOInitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GPIOA,&GPIOInitStruct);
	//2.配置EXTI的引脚映射
	//开启AFIO的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	//PA0->EXTI0
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource5);
	//PA1->EXTI1
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource6);
	//3.配置EXTI
	//3.1初始化EXTI0
	EXTI_InitTypeDef EXTIInitStruct;
	EXTIInitStruct.EXTI_Line = EXTI_Line5;
	EXTIInitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTIInitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
	EXTIInitStruct.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTIInitStruct);
	//3.2初始化EXTI1
	EXTIInitStruct.EXTI_Line = EXTI_Line6;
	EXTIInitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTIInitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
	EXTIInitStruct.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTIInitStruct);
	
	//4.初始化NVIC
	//4.2初始化某一路的中断
	NVIC_InitTypeDef NVICInitStruct;
	
	//4.2.1EXTI0
	NVICInitStruct.NVIC_IRQChannel = EXTI9_5_IRQn;
	NVICInitStruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVICInitStruct.NVIC_IRQChannelSubPriority = 2;
	NVICInitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVICInitStruct);
	//4.2.2EXTI1

	
	//5.初始化PC13
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	
	GPIOInitStruct.GPIO_Mode = GPIO_Mode_Out_OD; //open-drain开漏模式
	GPIOInitStruct.GPIO_Pin = GPIO_Pin_13;
	GPIOInitStruct.GPIO_Speed = GPIO_Speed_2MHz;

	GPIO_Init(GPIOC,&GPIOInitStruct);
	
	
	while(1)
	{
	}
}

void EXTI9_5_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line5) == SET)//EXTI 
	{
		EXTI_ClearITPendingBit(EXTI_Line5);	//清除中断
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
	}
	//不写if else原因:可能同时为1 两个都执行
	if(EXTI_GetITStatus(EXTI_Line6) == SET)
	{
		EXTI_ClearITPendingBit(EXTI_Line6);//清除中断
		GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
	}
}

3.5 EXTI的标准库编程方法总结

在这里插入图片描述

  • 18
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
根据引用内容,STM32C8T6是一款通用增强型的48脚单片机,具有64K闪存和LQFP封装。它适用于工业级温度范围-40~85度。下面是一些关于STM32C8T6学习笔记: 1. 学习资料:可以从ST官方网站下载STM32C8T6的数据手册和参考手册,这些手册包含了该单片机的详细信息和使用方法。 2. 开发环境:为了开始学习STM32C8T6,你需要安装相应的开发环境。ST官方提供了一款免费的集成开发环境(IDE)——STM32CubeIDE,它可以帮助你进行代码编写、调试和下载。 3. 编程语言:STM32C8T6可以使用多种编程语言进行开发,包括C语言和汇编语言。C语言是最常用的编程语言,它可以通过STM32CubeIDE进行编写和调试。 4. 引脚配置:在使用STM32C8T6之前,你需要了解每个引脚的功能和配置。数据手册中有一张引脚功能表,可以帮助你了解每个引脚的用途和配置方法。 5. 时钟配置:STM32C8T6具有多个时钟源和时钟分频器,你需要根据自己的需求配置正确的时钟。时钟配置对于外设的正常工作非常重要。 6. 中断和定时器:STM32C8T6支持中断和定时器功能,这些功能可以帮助你实现各种任务和功能。你可以通过配置中断和定时器来实现外设的响应和定时操作。 7. 外设驱动:STM32C8T6具有丰富的外设,包括GPIO、UART、SPI、I2C等。你可以根据自己的需求选择合适的外设,并学习如何配置和驱动这些外设。 8. 调试和下载:在开发过程中,你可以使用STM32CubeIDE提供的调试功能来调试你的代码。一旦代码调试完成,你可以使用ST-Link或其他下载器将代码下载到STM32C8T6上运行。 希望以上笔记对你学习STM32C8T6有所帮助!如果你有任何进一步的问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值