天津做做网站,企业的网站开发费用摊销几年,网站制作的服务机构,有路由器做网站中断:在主程序运行过程中#xff0c;出现了特定的中断触发条件(中断源)#xff0c;使得CPU暂停当前正在运行的程序#xff0c;转而去处理中断程序处理完成后又返回原来被暂停的位置继续运行
中断优先级:当有多个中断源同时申请中断时#xff0c;CPU会根据中断源的轻重缓急…中断:在主程序运行过程中出现了特定的中断触发条件(中断源)使得CPU暂停当前正在运行的程序转而去处理中断程序处理完成后又返回原来被暂停的位置继续运行
中断优先级:当有多个中断源同时申请中断时CPU会根据中断源的轻重缓急进行裁决优先响应更加紧急的中断源
中断嵌套:当一个中断程序正在运行时又有新的更高优先级的中断源申请中断CPU再次暂停当前中断程序转而去处理新的中断程序处理完成后依次进行返回 STM32中断 68个可屏蔽中断通道包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设 使用NVIC统一管理中断每个中断通道都拥有16个可编程的优先等级可对优先级进行分组进一步设置抢占优先级和响应优先级
中断由于硬件限制只能跳到固定的地址去执行程序所以要一个存地址的表就叫中断向量表
NVIC嵌套中断向量控制器基本结构 统一分配中断优先级和管理中断是一个内核外设是CPU的一个小助手。n代表一个外设可能会占用多个中断通道但 CPU某时只看一个想象成CPU是医生左侧的中断是排队的病人NVIC是叫号系统分配看病优先级并负责管理谁去见医生后来的更紧急也可以抢占优先
每个中断有16个优先级为了把这个优先级再区分为抢占优先级和响应优先级就需要对这16个优先级进行分组。NVIC的中断优先级由优先级寄存器的4位四位二进制可以表示0~15的数对应16个优先级这个优先级的数是值越小优先级越高0就是最高优先级决定这4位可以进行切分分为高n位的抢占优先级和低4-n位的响应优先级 抢占优先级高的可以中断嵌套响应优先级高的可以优先排队。 当两个中断同时响应时抢占优先级谁高谁先中断; 当抢占优先级相同时谁的响应优先级高谁先响应; 当抢占优先级相同时低响应优先级已经进入中断函数高响应优先级不可打断低优先级 抢占优先级和响应优先级均相同的按中断号排队 STM32的中断不存在先来后到的排队方式在任何时候都是优先级高的先响应
// EXTI 学习 EXTI(Extern Interrupt)外部中断 EXTI可以监测指定GPI0口的电平信号当其指定的GPIO口产生电平变化时EXTI将立即向NVIC发出中断申请经过NVIC裁决后即可中断CPU主程序使CPU执行EXTI对应的中断程序
支持的触发方式: 上升沿/下降沿/双边沿/软件触发 支持的GPIO口: 所有GPIO口都可以触发中断但相同的Pin不能同时触发中断 通道数: 16个GPIO Pin外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒 触发响应方式: 中断响应/事件响应
EXTI基本结构 AFIO主要用于引脚复用功能的选择和重定义 在STM32中AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择 此处应结合点路结构深入学习暂且先到这插个眼先学会用再杀回来
一点补充 AFIO中断引脚选择 我们刚才这里说了EXTI模块只有16个GPIO的通道。但这里每个GPIO外设都有16个引脚如果每个引脚占用一个通道EXTI的16个通道显然就不够用了。所以在这里会有一个AFIO中断引脚选择的电路模块。
这个AFIO就是一个数据选择器它可以在这前面3个GPIO外设各自的16个引脚里选择其中一个连接到后面的EXTI的通道里所以这前面说相同的Pin不能同时触发中断。因为对于PA0、PB0、PC0这些通过AFIO选择之后只有其中一个能接到EXTI的通道0上。同理PA1、PB1、PC1这些也只能有一个接到通道1上。这就是所有GPIO口都能触发中断但相同的pin不能同时触发中断的原因
/// 对外式红外传感器 DO数字输出端接到B14端口当挡光片或者编码盘在这个对射式红外传感器中间经过时DO就会输出电平跳变的信号然后这个电平跳变的信号触发STM32 PB14号口的中断我们在中断函数里执行变量的程序然后主循环里调用OLED显示这个变量 记住上面提到的EXIT结构图从左往右一条路 具体步骤就是 第一步配置RCC把我们这里涉及的外设的时钟都打开不打开时钟外设是没法工作的。 第二步配置GPIO 选择我们的端口为输入模式。 第三步配置AFIO 选择我们用的这一路GPIO 连接到后面的EXTI。 第四步配置EXTI选择边沿触发方式比如上升沿、下降沿或者双边沿还有选择触发响应方式可以选择中断响应和事件响应。当然我们一般都是中断响应。 第五步配置NVIC给这个中断选择一个合适的优先级。通过NVIC外部中断信号就能进入CPU。了。这样CPU才能收到中断信号才能跳转到中断函数里执行中断程序。 这五步就是外部中断的配置流程这里涉及的外设比较多
要点强调 GPIO_EXTILineConfig就是我们本节外部中断需要用的函数调用这个函数就可以配置AFIO 的数据选择器来选择我们想要的中断引脚这个函数虽然是GPIO 开头但实际上里面操作的是AFIO 的寄存器 GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14); //将外部中断的14号线映射到GPIOB即选择PB14为外部中断引脚 第一个参数GPIO_PortSource选择某个GPIO外设作为外部中断源这个参数可以是GPIO_PortSourceGPIOx其中x可以是A到G。 第二个参数是GPIO_PinSource指定配置的外设中断线。这个参数可以是GPIO_PinSourcex其中x可以是0到15。
当执行完这个函数后AFIO 的第14个数据选择器就拨好了。其中输入端被拨到了GPIOB 外设上对应的就是PB14号引脚输出端固定连接的是EXTI的第14个中断线路 直接复制上节OLED工程 在Hardware目录下添加CountSensor.c/.h
#ifndef __COUNT_SENSOR_H
#define __COUNT_SENSOR_Hvoid CountSensor_Init(void);
uint16_t CountSensor_Get(void);#endif
#include stm32f10x.h // Device headeruint16_t CountSensor_Count; //全局变量用于计数/*** 函 数计数传感器初始化* 参 数无* 返 回 值无*/
void CountSensor_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//开启GPIOB的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//开启AFIO的时钟外部中断必须开启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); //将PB14引脚初始化为上拉输入/*AFIO选择中断引脚*/GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//将外部中断的14号线映射到GPIOB即选择PB14为外部中断引脚//AFIO外设中断引脚选择配置就完成了/*EXTI初始化*/EXTI_InitTypeDef EXTI_InitStructure;//定义结构体变量EXTI_InitStructure.EXTI_Line EXTI_Line14; //选择配置外部中断的14号线EXTI_InitStructure.EXTI_LineCmd ENABLE; //指定外部中断线使能EXTI_InitStructure.EXTI_Mode EXTI_Mode_Interrupt; //指定外部中断线为中断模式EXTI_InitStructure.EXTI_Trigger EXTI_Trigger_Falling; //指定外部中断线为下降沿触发EXTI_Init(EXTI_InitStructure); //将结构体变量交给EXTI_Init配置EXTI外设/*NVIC中断分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//配置NVIC为分组2//即抢占优先级范围0~3响应优先级范围0~3//此分组配置在整个工程中仅需调用一次//若有多个中断可以把此代码放在main函数内while循环之前//若调用多次配置分组的代码则后执行的配置会覆盖先执行的配置/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure;//定义结构体变量NVIC_InitStructure.NVIC_IRQChannel EXTI15_10_IRQn;//选择配置NVIC的EXTI15_10线NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE;//指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1;//指定NVIC线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority 1;//指定NVIC线路的响应优先级为1NVIC_Init(NVIC_InitStructure);//将结构体变量交给NVIC_Init配置NVIC外设
}/*** 函 数获取计数传感器的计数值* 参 数无* 返 回 值计数值范围0~65535*/
uint16_t CountSensor_Get(void)
{return CountSensor_Count;
}/*** 函 数EXTI15_10外部中断函数* 参 数无* 返 回 值无* 注意事项此函数为中断函数无需调用中断触发后自动执行* 函数名为预留的指定名称可以从启动文件复制* 请确保函数名正确不能有任何差异否则中断函数将不能进入*/
void EXTI15_10_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line14) SET) //判断是否是外部中断14号线触发的中断{/*如果出现数据乱跳的现象可再次判断引脚电平以避免抖动*/if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) 0){CountSensor_Count ; //计数值自增一次}EXTI_ClearITPendingBit(EXTI_Line14); //清除外部中断14号线的中断标志位//中断标志位必须清除//否则中断将连续不断地触发导致主程序卡死}
}
int main(void)
{/*模块初始化*/OLED_Init(); //OLED初始化CountSensor_Init(); //计数传感器初始化/*显示静态字符串*/OLED_ShowString(1, 1, Count:); //1行1列显示字符串Count:while (1){OLED_ShowNum(1, 7, CountSensor_Get(), 5); //OLED不断刷新显示CountSensor_Get的返回值}
}我们用的是下降沿触方法在移开挡光片的时候触发中断数值加1 改下这里变为上升沿出发遮挡的时候数字加1 如果这里改成上升沿下降沿都触发就是遮挡和移开的时候都加1。
/ 旋转编码器 #ifndef __ENCODER_H
#define __ENCODER_Hvoid Encoder_Init(void);
int16_t Encoder_Get(void);#endif
#include stm32f10x.h // Device headerint16_t Encoder_Count; //全局变量用于计数旋转编码器的增量值/*** 函 数旋转编码器初始化* 参 数无* 返 回 值无*/
void Encoder_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //开启GPIOB的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //开启AFIO的时钟外部中断必须开启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); //将PB0和PB1引脚初始化为上拉输入/*AFIO选择中断引脚*/GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//将外部中断的0号线映射到GPIOB即选择PB0为外部中断引脚GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);//将外部中断的1号线映射到GPIOB即选择PB1为外部中断引脚/*EXTI初始化*/EXTI_InitTypeDef EXTI_InitStructure; //定义结构体变量EXTI_InitStructure.EXTI_Line EXTI_Line0 | EXTI_Line1; //选择配置外部中断的0号线和1号线EXTI_InitStructure.EXTI_LineCmd ENABLE; //指定外部中断线使能EXTI_InitStructure.EXTI_Mode EXTI_Mode_Interrupt; //指定外部中断线为中断模式EXTI_InitStructure.EXTI_Trigger EXTI_Trigger_Falling; //指定外部中断线为下降沿触发EXTI_Init(EXTI_InitStructure); //将结构体变量交给EXTI_Init配置EXTI外设/*NVIC中断分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC为分组2//即抢占优先级范围0~3响应优先级范围0~3//此分组配置在整个工程中仅需调用一次//若有多个中断可以把此代码放在main函数内while循环之前//若调用多次配置分组的代码则后执行的配置会覆盖先执行的配置/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量NVIC_InitStructure.NVIC_IRQChannel EXTI0_IRQn; //选择配置NVIC的EXTI0线NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; //指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; //指定NVIC线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; //指定NVIC线路的响应优先级为1NVIC_Init(NVIC_InitStructure); //将结构体变量交给NVIC_Init配置NVIC外设NVIC_InitStructure.NVIC_IRQChannel EXTI1_IRQn; //选择配置NVIC的EXTI1线NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; //指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; //指定NVIC线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority 2; //指定NVIC线路的响应优先级为2NVIC_Init(NVIC_InitStructure); //将结构体变量交给NVIC_Init配置NVIC外设
}/*** 函 数旋转编码器获取增量值* 参 数无* 返 回 值自上此调用此函数后旋转编码器的增量值*/
int16_t Encoder_Get(void)
{/*使用Temp变量作为中继目的是返回Encoder_Count后将其清零*//*在这里也可以直接返回Encoder_Count但这样就不是获取增量值的操作方法了也可以实现功能只是思路不一样*/int16_t Temp;Temp Encoder_Count;Encoder_Count 0;return Temp;
}/*** 函 数EXTI0外部中断函数* 参 数无* 返 回 值无* 注意事项此函数为中断函数无需调用中断触发后自动执行* 函数名为预留的指定名称可以从启动文件复制* 请确保函数名正确不能有任何差异否则中断函数将不能进入*/
void EXTI0_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line0) SET) //判断是否是外部中断0号线触发的中断{/*如果出现数据乱跳的现象可再次判断引脚电平以避免抖动*/if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) 0){if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) 0) //PB0的下降沿触发中断此时检测另一相PB1的电平目的是判断旋转方向{Encoder_Count --; //此方向定义为反转计数变量自减}}EXTI_ClearITPendingBit(EXTI_Line0); //清除外部中断0号线的中断标志位//中断标志位必须清除//否则中断将连续不断地触发导致主程序卡死}
}/*** 函 数EXTI1外部中断函数* 参 数无* 返 回 值无* 注意事项此函数为中断函数无需调用中断触发后自动执行* 函数名为预留的指定名称可以从启动文件复制* 请确保函数名正确不能有任何差异否则中断函数将不能进入*/
void EXTI1_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line1) SET) //判断是否是外部中断1号线触发的中断{/*如果出现数据乱跳的现象可再次判断引脚电平以避免抖动*/if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) 0){if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) 0) //PB1的下降沿触发中断此时检测另一相PB0的电平目的是判断旋转方向{Encoder_Count ; //此方向定义为正转计数变量自增}}EXTI_ClearITPendingBit(EXTI_Line1); //清除外部中断1号线的中断标志位//中断标志位必须清除//否则中断将连续不断地触发导致主程序卡死}
}