目录
一:中断
1:简历
2:AFIO
3:EXTI
编辑
4:NVIC基本结构
5:使用步骤
二:中断的应用
A:对外式红外传感计数器
1:连接图编辑
2:函数介绍
3:硬件介绍
4:计数代码
B;旋转编码计数器
1:连接图
2:硬件介绍
3:旋转编码器代码:
一:中断
1:简历
中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行
中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源
中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回
STM32中断: 68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设 使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级 (EXTI可以产生中断的,众多外设之一)
2:AFIO
使用这个函数GPIO_EXTILineConfig配置那个引脚为中断引脚: 调用这个函数,就可以配置AFIO的数据选择器,来选择我们想要的中断引脚
AFIO主要用于引脚复用功能的选择和重定义
在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择
3:EXTI
EXTI_Init:
调用这个函数,就可以根据这个结构体里的参数配置EXTI外设, 使用方法和GPIO Init也是一样;
选择那个IO口为中断IO口(和前面的AFIO引脚配置保持一致),
选择那个方式触发中断(中断模式,事件模式)
选择触发中断的方式 (上升沿触发,下降沿触发,上升沿和下降沿触发)
EXTI简介:
EXTI(Extern Interrupt)外部中断
EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
支持的触发方式:上升沿/下降沿/双边沿/软件触发
支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断 (PA1、PB1、PC1这样的,PAO和PBO这样的相同的Pin只能选1个作为中断引脚) PA6和PA7、PA9和PB15、PBO和PB1这样的都可以
原因:AFOI会在APIOA,APIO,APIOC中选择一个GPIO的16个引脚连接到后面的EXTI
通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
触发响应方式:中断响应/事件响应
基本结构:
EXTI框图
4:NVIC基本结构
嵌套中断向量控制器: 用来统一分配断优先级和管理中断的
A:使用NVIC_PriorityGroupConfig函数先分组;分组完成后抢占优先级和响应优先级的号不能超过表中的范围
B:使用NVIC_Init初始化 (1:选择通道,选择抢占优先级和响应优先级)
NVIC优先级分组:
NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级(更加紧急)和低4-n位的响应优先级(紧急)抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队 (中断号:EXTI简历中的优先级)
抢占优先级>响应优先级
优先级的数是值越小,优先级越高,0就是最高优先级
5:使用步骤
1:第一步,配置RCC,把我们这里涉及的外设的时钟都打开(GPIO,AFIO)
2:第二步,配置GPIO,选择我们的端口为输入模式
3:第三步,配置AFIO,选择我们用的这一路GPIO,连接到后面的EXT
4:第四步,配置EXTI,选择边沿触发方式比如上升沿、下降沿或者双边沿, 还有选择触发响应方式,可以选择中断响应和事件响应
5:第五步,配置NVIC,给我们这个中断选择一个合适的优先级
最后,通过NVIC,外部中断信号就能进入CPU了
1:EXIT和NVIC的时钟一直是打开状态不需要开启
二:中断的应用
A:对外式红外传感计数器
1:连接图
2:函数介绍
配置AFIO在文件中stm32f10x_gpio.h
void GPIO_AFIODeInit(void);
void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
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);
void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);
GPIO_AFIODeInit: 用来复位AFIO外设, 调用这个函数AFIO外设的配置就会全部清除
GPIO_PinLockConfig: 参数指定位那个引脚, 那这个引脚的配置就会被锁定,防止意外更改
GPIO_EventOutputConfig 和GPIO_EventOutputCmd : 配置AFIO的事件输出功能的
GPIO_PinRemapConfig: 引脚重新映射, 第一个参数可以选择你要重映射的方式,第二个参数是新的状态
GPIO_EXTILineConfig: 调用这个函数,就可以配置AFIO的数据选择器,来选择我们想要的中断引脚
GPIO_ETH_MediaInterfaceConfig:关于以太网的
配置EXTI在stm32f10x exti.h文件中
void EXTI_DeInit(void);
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
void EXTI_ClearFlag(uint32_t EXTI_Line);
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
EXTI_DeInit: EXTI的配置都清除,恢复成上电默认的状态
EXTI_Init:调用这个函数,就可以根据这个结构体里的参数配置EXTI外设, 使用方法和GPIO Init也是一样
EXTI_StructInit: 可以把参数传递的结构体变量赋一个默认值
EXTI_GenerateSWInterrupt:软件触发外部中断
在主函数中你想查看标志位和清除标志位使用下面的函数
EXTI_GetFlagStatus:可以获取指定的标志位是否被置1了
EXTI_ClearFlag: 可以对置1的标志位进行清除
在中断函数里,如果你想查看标志位和清除标志位使用下面的函数
EXTI_GetITStatus: 获取中断标志位是否被置1了(检测外部中断的状态)
EXTI_ClearITPendingBit: 清除中断挂起标志位
配置NMIC在misc.h文件中的函数
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource);
NVIC_PriorityGroupConfig:用来中断分组的,参数是中断分组的方式
NVIC_Init: 根据结构体里面指定的参数初始化NMIC
NVIC_SetVectorTable: 设置中断向量表
NVIC_SystemLPConfig :系统低功耗配置
3:硬件介绍
4:计数代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"
uint16_t CountSensor_count;
void CountSensor_Init(void){
//1:配置RCC,把我们这里涉及的外设的时钟都打开(GPIO,AFIO)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//2:配置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);
//3:配置AFIO,选择我们用的这一路GPIO,连接到后面的EXT
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
//4: 第四步,配置EXTI,选择边沿触发方式比如上升沿、下降沿或者双边沿, 还有选择触发响应方式,可以选择中断响应和事件响应
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line14;//14IO口作为中断
EXTI_InitStructure.EXTI_LineCmd=ENABLE;
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//中断模式
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;//下降沿触发
EXTI_Init(&EXTI_InitStructure);
//5:配置NVIC,给我们这个中断选择一个合适的优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn;//到NVIC的通道
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;
}
//STM32的中断函数名字不能变,在启动文件里面查看
void EXTI15_10_IRQHandler(void){
//在中断函数里,查看中断标志位是否被置1了,看是不是PB14口引发的中断(通道10~15都可以进去)
if (EXTI_GetITStatus(EXTI_Line14)==SET){
/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
//读取PB14IO口是否为低电频
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==0){
CountSensor_count++;
}
//清除中断挂起标志位
EXTI_ClearITPendingBit(EXTI_Line14);
}
}
int main(void)
{
//OLED屏幕代码省略
OLED_Init();
CountSensor_Init();
OLED_ShowString(1, 1, "Count:");
while (1)
{
OLED_ShowNum(1, 7, CountSensor_Get(), 5);
}
}
B;旋转编码计数器
1:连接图
2:硬件介绍
我们使用判断正反转的条件:
正转-----B相下降沿和A相低由平时同时满足时;
反转----在A相下降沿和B相低电频同时满足时;
本次使用了两个中断注意:
PB0和PB1设置为中断:
A:配置AFIO时:需要配置两个引脚
B:配置EXTI时:X.EXTI_Line需要配置两个 (X.EXTI_Line = EXTI_Line0 | EXTI_Line1)
C:配置NVIC时: 一次分组,2次初始化
中断函数分析:
当A为中断IO口时: A为下降沿触发中断,B1和B0同时为低电频时满足正转
当B为中断IO口时: A为下降沿触发中断,B0和B1同时为低电频时满足反转
(中断函数处理全局变量时,可以改变全局变量的值)
3:旋转编码器代码:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"
int16_t Encoder_Count;
void Encoder_Init(void)
{
//1:配置RCC,把我们这里涉及的外设的时钟都打开(GPIO,AFIO)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//2:配置GPIO,选择我们的端口为输入模式 注意打开了2个中断
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);
//3:配置AFIO,选择我们用的这一路GPIO,连接到后面的EXT
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
//4: 第四步,配置EXTI,选择边沿触发方式比如上升沿、下降沿或者双边沿, 还有选择触发响应方式,可以选择中断响应和事件响应
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);
//5:配置NMIC,给我们这个中断选择一个合适的优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; 到NVIC的通道
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的通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStructure);
}
//返回每次调用这个Get函数之后,返回Count的变化值
//每次调用这个函数都会加1或者减1
int16_t Encoder_Get(void)
{
int16_t Temp;
Temp = Encoder_Count;
/*返回的temp是局部变量,在栈区,也就是该函数一旦执行完,
就会被系统释放,当再次循环调用到该函数时,
get里的temp就需要被重新赋值,temp被赋值0,
无论主函数如何循环都是变成了NUM+=0*/
/*如果返回函数不清零的话,
数字就变成等差数列求和,清零的话就疯狂加或者减一*/
Encoder_Count = 0;
return Temp;
}
//void EXTI0_IRQHandler(void)
//{检测外部中断状态,如果状态为SET,会返回真(true),否则返回假(false)。
// if (EXTI_GetITStatus(EXTI_Line0) == SET)
// {
// /*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
// if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)
// {//中断函数的处理可以改变全局变量的值
// if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
// {
// Encoder_Count --;
// }
// }
// EXTI_ClearITPendingBit(EXTI_Line0);
// }
//}
void EXTI0_IRQHandler(void)
{检测外部中断状态,如果状态为SET,会返回真(true),否则返回假(false)。
if (EXTI_GetITStatus(EXTI_Line0) == SET)
{
/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
{
//中断函数的处理可以改变全局变量的值
Encoder_Count --;
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
//void EXTI1_IRQHandler(void)
// { //检测外部中断状态,如果状态为SET,会返回真(true),否则返回假(false)。
// if (EXTI_GetITStatus(EXTI_Line1) == SET)
// {
// /*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
//
// if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) //PB1为低电频
// {
// if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) //PB0为低电频
// {//中断函数的处理可以改变全局变量的值
// Encoder_Count ++;
// }
// }
// //清除中断挂起标志位
// EXTI_ClearITPendingBit(EXTI_Line1);
// }
//}
void EXTI1_IRQHandler(void)
{ //检测外部中断状态,如果状态为SET,会返回真(true),否则返回假(false)。
if (EXTI_GetITStatus(EXTI_Line1) == SET)
{
/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) //PB0为低电频
{//中断函数的处理可以改变全局变量的值
Encoder_Count ++;
}
//清除中断挂起标志位
EXTI_ClearITPendingBit(EXTI_Line1);
}
}
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);
}
}