一、中断系统介绍
众所周知,轮询是 CPU 通过不断地查询某个外部设备的状态,如果外部设备准备好,就可以向其发送数据或者读取数据,这种方式由于CPU不断查询总线,导致指令执行受到影响,效率非常低。
而与之相对应的就是中断,正常情况 CPU 会处理其他的事情,如果设备有需要 CPU 处理的事情就产生一个中断,CPU 就会停下正在做的事情来处理中断。
中断的执行流程如下:
STM32 中断包含很多中断源(中断通道),并且使用 NVIC 统一管理中断,由左边的地址组成的表称为中断向量表,表中的内容为中断入口的地址:
NVIC 为嵌套向量中断控制器(Nested Vectored Interrupt Controller),在 STM32 中是用来统一分配中断优先级和管理中断的,它是一个内核外设
STM32 的 NVIC 可以对优先级进行分组,抢占优先级(pre-emption priority)高的中断源可以打断当前中断,来执行自己的中断,而响应优先级(subpriority)高的中断源可以优先被 CPU 响应执行,在 STM32 官方的编程手册中有 NVIC 相关的介绍。
二、EXTI(外部中断)
中断系统是管理和执行中断的逻辑结构,外部中断(EXTI)是众多能产生中断的外设之一。在触发响应方式中,中断响应是正常的流程,引脚电平变化触发中断;而事件响应不会触发中断,而是触发别的外设操作,如 DMA 等。
外部中断的基本结构如下图。相同的 Pin 不能同时触发中断,比如 pA0、pB0... 只有一个能接到后面的 16 个 GPIO_Pin 上,所以需要经过 AFIO 数据选择器。在经过 EXTI 边沿检测控制模块后,输出中断输出 EXTI0~15,还有 20 个引向其他外设,即事件响应。
AFIO 结构,本质上是一系列数据选择器,可以用于引脚复用功能的选择和重定义,或者中断引脚的选择。
EXTI 内部框图如下:
需要使用外部中断的设备一般是外部驱动的很快的突发信号,比如旋转编码器:
2.1 对射式红外传感器计数
接线图:
初始化函数 CountSensor_Init(),首先要把从左到又涉及到的外设的时钟打开;第二步把 GPIO 配置为输入模式;第三步配置 AFIO,将选择的 GPIO 连接到后面的 EXTI;第四步,配置 EXTI,选择边沿触发方式,如上升沿、下降沿和双边沿等,并且选择触发相应方式,可以选择中断响应或者事件响应;第五步配置 NVIC,给中断选择一个合适的优先级。这里涉及到的外设比较多,有 RCC、GPIO、AFIO、EXTI、NVIC 等。
EXTI 相关函数:
/* stm32f10x_exti.h */
/* 可以将EXTI的配置清除,恢复成上电默认的状态 */
void EXTI_DeInit(void);
/* 根据结构体初始化EXTI外设,和GPIO的差不多 */
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
/* 可以给结构体赋予一个默认值 */
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);
/* 软件触发一次外部中断 */
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);
/* 获取指定的flag是否置1 */
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
/* 对标志位进行清除 */
void EXTI_ClearFlag(uint32_t EXTI_Line);
/* 获取中断标志位是否被置1,在中断程序中使用 */
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
NVIC 相关函数:
/* misc.h */
/* 用来中断优先级分组,参数为中断分组的方式 */
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
/* 根据指定结构体初始化NVIC */
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
/* 设置中断向量表 */
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);
/* 系统