一、中断系统

中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行程序,转而去处理中断程序处理完成后又返回原来被暂停的位置继续运行

中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源

中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回

二、STM32中断

 68个可屏蔽中断通道包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设

使用NVIC统一管理中断,每个中断通道都拥有16个可编程优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先

三、NVIC(嵌套中断向量控制器基本结构

作用:统一分配中断优先级和管理中断

四、NVIC优先级分组

NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为n位的抢占优先级和低4-n位的响应优先级

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

五、EXTI外部中断

5.1 外部中断基本知识

①EXTI(Extern Interrupt外部中断

②EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
支持的触发方式:上升沿/下降沿/双边沿/软件触发
支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断
通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒以太网唤醒
⑥触发响应方式:中断响应/事件响应

中断响应:引脚电平变化触发中断

事件响应:不触发中断,触发其他外设操作

5.2 外部中断(EXTI)基本结构

5.2.1开发步骤: 

配置RCC,把涉及到的外设时钟打开

配置GPIO,选择端口输入模式

配置AFIO,选择用的某一路GPIO,连接到后面的EXTI

配置EXTI,选择边沿触发方式、触发响应方式(中断响应/事件响应)

配置NVIC,给中断选择合适的优先级

5.3 AFIO复用IO口

AFIO主要用于引脚复用功能的选择和重定义

在STM32中,AFIO主要完成两个任务复用功能引脚映射中断引脚选择

5.4 EXTI内部框图

六、AFIO库函数

void GPIO_AFIODeInit(void);//复位AFIO

//配置AFIO事件输出功能

void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
void GPIO_EventOutputCmd(FunctionalState NewState);

/*下面两个重要*/
void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);

//引脚重映射第一个参数:重映射的方式;第二个参数:新的状态
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);

//配置AFIO的数据选择器本节中断用到函数

void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);

//以太网相关

AFIO中断选择函数详解

void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource); 

第一个参数:GPIO_PortSourceGPIOx where x can be (A..G)

第二个参数:GPIO_PinSourcex where x can be (0..15)

七、EXTI库函数 

void EXTI_DeInit(void);//清除所有EXTI配置,复位
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);//结构体方式初始化EXTI
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);//给结构变量默认值

void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);//软件触发外部中断

/*主函数查看清除标志位*/
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);//获取指定标志位是否被置1
void EXTI_ClearFlag(uint32_t EXTI_Line);//对置1的标志位清除

/*中断函数查看和清除标志位*/
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);

八、NVIC库函数

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);//中断分组的方式,整个芯片只能用一种,最好放在主函数中
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);//结构体方式初始化NVIC

/*下面两个常用*/
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);//设置中断向量表
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);//系统低功耗配置

九、中断函数

每个通道对应一个中断函数,在启动文件中可查看

开发技巧

在中断函数中,先进行中断标志位的判断,确保是我们想要的中断源触发的函数;然后使用清除标志位函数清除,否则会一直执行中断函数,在中断函数里卡死

以上函数具体使用参考”第十章、实验“ 

十、实验

10.1对射式红外传感器计次

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"

int main(void)
{
	OLED_Init();
	CountSensor_Init();
	
	OLED_ShowString(1,1 ,"Count:");
	
	
	while (1)
	{
		OLED_ShowNum(1,7,CountSensor_Get(),5);
	}
}

CountSensor.c

#include "stm32f10x.h"                  // Device header

uint16_t CountSensor_Count;

void CountSensor_Init(void)
{
	/*一、开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//开启APB2时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启AFIO时钟
	
	/*二、GPIO配置*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	/*三、AFIO配置*/
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
	
	/*四、EXTI第14个线路配置为中断模式,下降沿触发,开启中断*/
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line = EXTI_Line14;//EXTI线
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;//开启或关闭EXTI
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式或者事件模式
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//指定触发信号有效边沿
	EXTI_Init(&EXTI_InitStructure);
	
	/*五、配置NVIC*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//优先级分组
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//选择中断通道
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能或失能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级
	NVIC_Init(&NVIC_InitStructure);
}

uint16_t CountSensor_Get(void)
{
	return CountSensor_Count;
}

void EXTI15_10_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line14) == SET)    //判断标志位,是否是EXTI_Line14触发的中断
	{
		CountSensor_Count++;  
		EXTI_ClearITPendingBit(EXTI_Line14);//清除标志位
	}
	
}

CountSensor.h

#ifndef __COUNTSENSOR_H
#define __COUNTSENSOR_H

void CountSensor_Init(void);
uint16_t CountSensor_Get(void);

#endif

10.2旋转编码器计次

 mian.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"

int16_t Num;

int main(void)
{
	OLED_Init();
	Encoder_Init();

	OLED_ShowString(1,1,"Num:");
	
	
	while (1)
	{
		Num += Encoder_Get();
		OLED_ShowSignedNum(1,5,Num,5);  
	}
}

Encoder.c

#include "stm32f10x.h"                  // Device header

int16_t Encoder_Count;//无符号变量

/*初始操作*/
void Encoder_Init(void)
{
	/*一、开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//开启APB2时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启AFIO时钟
	
	/*二、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_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource0);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);
	
	/*四、EXTI第0/1个线路配置为中断模式,下降沿触发,开启中断*/
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
	EXTI_Init(&EXTI_InitStructure);
	
	/*五、配置NVIC*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//优先级分组
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
	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;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//响应优先级
	NVIC_Init(&NVIC_InitStructure);
}

int16_t Encoder_Get(void)
{
	int16_t Temp;
	Temp = Encoder_Count;
	Encoder_Count = 0;
	return Temp;
}

/*正转中断函数*/
void EXTI1_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line1) == SET)					//B相下降沿(中断)
	{
		if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0) == 0)	//A相低电平
		{
			Encoder_Count ++;
		}
		EXTI_ClearITPendingBit(EXTI_Line1);
	}
}

/*反转中断函数*/
void EXTI0_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line0) == SET)					//A相下降沿(中断)
	{
		if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) == 0)	//B相低电平
		{
			Encoder_Count --;
		}
		EXTI_ClearITPendingBit(EXTI_Line0);
	}
}

Encoder.h

#ifndef __ENCODER_H
#define __ENCODER_H

void Encoder_Init(void);
int16_t Encoder_Get(void);

#endif

十一、中断编程建议

①在中断函数中,不要执行耗时过长代码,中断函数要简短快速,不要执行Delay函数,防止主程序受到严重阻塞

②不要在主函数和中断函数中调用相同的函数或者操作同一个硬件

原文地址:https://blog.csdn.net/qq_52902991/article/details/134783246

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_41438.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注