Appearance
中断
中断有优先级, 数字越小优先级越高
- 系统异常: 内核水平
- 外部中断: 外设水平
有10个系统异常, 60个外设中断, 对应一个中断向量表, 保存在启动文件里面
控制NVIC
NVIC: 嵌套向量中断控制器, 属于内核外设, 支持256个中断(16个内核中断, 240个外部中断), 最多支持256个优先级(可以剪裁), ST只使用了16个
还有一些中断挂起, 解挂, 激活标志位等不常用的功能寄存器
工作原理
首先判断中断是否开启, 之后根据AIRCR寄存器里面的分组方式进行控制进一步操作, 判断运行的顺序
内核中断不通过外部寄存器的开关判断, 但是经过相同的评级
在文件STM32F10xxxx Cortex-M3编程手册中
内核的寄存器定义在core_cm3.h文件
c
typedef struct
{
__IO uint32_t ISER[8]; /*!< Offset: 0x000 Interrupt 使能寄存器 */
uint32_t RESERVED0[24];
__IO uint32_t ICER[8]; /*!< Offset: 0x080 Interrupt 清除 */
uint32_t RSERVED1[24];
__IO uint32_t ISPR[8]; /*!< Offset: 0x100 Interrupt 使能悬起 */
uint32_t RESERVED2[24];
__IO uint32_t ICPR[8]; /*!< Offset: 0x180 Interrupt 清除悬起 */
uint32_t RESERVED3[24];
__IO uint32_t IABR[8]; /*!< Offset: 0x200 Interrupt 有效位寄存器 */
uint32_t RESERVED4[56];
__IO uint8_t IP[240]; /*!< Offset: 0x300 Interrupt 中断优先级寄存器,8位有效 */
uint32_t RESERVED5[644];
__O uint32_t STIR; /*!< Offset: 0xE00 软件触发中断寄存器 */
} NVIC_Type;
经过裁剪之后STM公司把它放在misc.h文件中
优先级设定
实际操作的是NVIC->IPRx寄存器, 一个寄存器8位
使用STM只使用高四位共64种, 又将它分为主优先级, 子优先级, 数字越小优先级越高, 都相等比较硬件中断编号
优先级分为抢占优先级以及响应优先级
抢占优先级可以对低优先级的中断函数进行打断
响应优先级不能进行打断, 抢占优先级相同的时候响应优先级比较高的先执行
优先级相同的时候通过自然优先级进行排序
优先级还会进行分组, 设置为五个组, 控制寄存器表示的方法(主优先级和子优先级分别由几位进行控制)
编程顺序
- 请求使能中断
外设使能, 控制外设相关寄存器 + 总开关NVIC中断使能寄存器
配置优先级分组 有专门的函数进行配置
配置NVIC寄存器
c
typedef struct
{
uint8_t NVIC_IRQChannel; /*!< Specifies the IRQ channel to be enabled or disabled.
This parameter can be a value of @ref IRQn_Type
(For the complete STM32 Devices IRQ Channels list, please
refer to stm32f10x.h file) 配置要使能的中断*/
uint8_t NVIC_IRQChannelPreemptionPriority; /*!< Specifies the pre-emption priority for the IRQ channel
specified in NVIC_IRQChannel. This parameter can be a value
between 0 and 15 as described in the table @ref 抢占优先级 NVIC_Priority_Table 主优先级*/
uint8_t NVIC_IRQChannelSubPriority; /*!< Specifies the subpriority level for the IRQ channel specified
in NVIC_IRQChannel. This parameter can be a value
between 0 and 15 as described in the table @ref NVIC_Priority_Table 次优先级*/
FunctionalState NVIC_IRQChannelCmd; /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel
will be enabled or disabled.
This parameter can be set either to ENABLE or DISABLE 内核控制器 是否同意产生中断*/
} NVIC_InitTypeDef;
- 编写中断服务函数
在中断向量表里面找到对应的函数名称
中断函数写错了会执行启动文件中的, 陷入循环, 函数全部在说stm32F10x_it.h文件中重新定义为空函数
实际使用
使用寄存器SCB->AIRCR:PRIGROUP[10:8](系统控制块(System Control Block))控制分组的模式
实现GPIO中断(外部中断)
EXTI
EXTI(External interupt/event Controller外部中断/事件控制器)控制, 对应的是上面的中断函数中的EXTI触发的中断
在H7里面为Extended interupt/event Controller扩展中断事件控制器
有二十个产生事件.中断请求的边沿检测器, 即有二十个EXTI线(F1)
中断和事件的理解:
中断: 进入NVIC, 有相应的中断处理函数, 需要CPU进行处理
事件: 不进入NVIC, 只用于内部硬件的自动控制, 比如TIM, DMA, ADC
H7里面有89条, 主要是各种的唤醒事件, 为的是降低功耗
主要特性
- F1/F4/F7
每一条线都可以单独的进行配置, 选择触发的是事件还是中断, 触发的方式(上升沿, 下降沿, 双边沿触发), 支持软件触发, 开启/屏蔽, 有挂起状态位(中断)
- H7系列
对EXTI产生的事件分为可配置事件以及直接事件
可配置事件跟上面的类似(来自产生脉冲的I/O或者外设的信号)
直接事件(来自其他的外设中断唤醒源, 需要再外设的中断进行清除): 固定上升沿触发, 不支持软件触发, 无挂起状态位(由其他的外设提供)
工作原理
- F1/F4/F7
中断为上面的一路, 最终的信号通入NVIC控制器
事件为下面的一路
①是输入线, EXTI有十九个中断/事件输入线, 输入线通过寄存器设置为任意一个GPIO, 也可以是一些外设的时间, 一般是存在电平变化的信号
②是一个检测电路, 根据上升沿或下降沿选择寄存器, 对应的位设置控制信号触发, 测到有边沿跳变就左侧输出有效信号 1 给编号 3 电路,否则输出无效信号 0。
③软件中断事件控制器EXTI_SWIER, 设置为1则与门设置为1, 之后会控制EXTI_PR寄存器对应的位为1设置为挂起, 之后会输出1
④EXIT_IMR中断屏蔽寄存器控制位, 设置为1的时候开放请求
⑤发送到内核产生中断
⑥EXTI_EMR事件屏蔽寄存器控制, 写1使能, 来到脉冲发生器, 产生脉冲, 之后会触发ADC产生信号, 或触发定时器等
下降沿触发选择寄存器(EXTI_FTSR)和上面的类似
- H7
PWR: 电源管理模块
EXTI和实际的GPIO的对应(AFIO-F1, SYSCFG-F4/F7/H7)
AFIO: 复用功能I/O和调试配置, 主要是用来进行重映射和外部中断的配置
- 调试IO的配置: AFIO_MAPR[26:24], 配置JTAG和SWD的开关状态
- 重映射配置: AFIO_MAPR, 部分外设IO的重映射配置
- 外部中断配置: AFIO_EXTICR1-4, EXTI的每根线具体对应的IO
配置之前需要使能时钟
SYSCFG: 系统时钟配置控制器, 用于外部中断的配置
使用的是SYSCFG_EXTICR1-4配置实际对应的具体IO
配置之前也需要使能时钟
EXTI可以控制20根外部的终端线, 每个 GPIO 都可以被设置为输入线,占用 EXTI0 至 EXTI15,还有另 外七根用于特定的外设事件,见表 EXTI 中断 _ 事件线 。有十六根外部中断线连接到EXTI的一根线上
固件库
使用EXIT_InitTypeDef函数进行实现
c
typedef struct
{
uint32_t EXTI_Line; /*!< Specifies the EXTI lines to be enabled or disabled.
This parameter can be any combination of @ref EXTI_Lines 设置产生中断的线实 际上设置的是中断屏蔽寄存器*/
EXTIMode_TypeDef EXTI_Mode; /*!< Specifies the mode for the EXTI lines.
This parameter can be a value of @ref EXTIMode_TypeDef设置为事件还是中断 */
EXTITrigger_TypeDef EXTI_Trigger; /*!< Specifies the trigger signal active edge for the EXTI lines.
This parameter can be a value of @ref EXTIMode_TypeDef 设置出发的条件*/
FunctionalState EXTI_LineCmd; /*!< Specifies the new state of the selected EXTI lines.
This parameter can be set either to ENABLE or DISABLE */
}EXTI_InitTypeDef;
这里控制的是上面的框图里面的寄存器
之后使用void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);函数进行设置实际使用的引脚
思路总理
首先设置GPIO的触发中断的引脚使用函数GPIO_EXTILineConfig, 之后产生的信号会进入EXTI, 由EXTI初始化函数确定中断的进一步选择是触发中断还是产生事件, 最后进入NVIC在配置优先级之后会触发中断
- 初始化引脚为输入, 打开时钟
- 选择引脚复用, 由于属于引脚复用打开AFIO时钟(对寄存器AFIO_EVCR,AFIO_MAPR和AFIO_EXTICRX进行读写操作前,应当首先打开AFIO 的时钟。参考第6.3.7节APB2外设时钟使能寄存器(RCC_APB2ENR)。)
- 初始化EXTI
- 初始化NVIC
c
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
//配置优先级分组
/* Check the parameters */
assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
/* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}
在初始化NVIC的通道的时候前四个EXTI有单独的通道, 后面的会使用这种定义
区分不同的中断源需要进行相应的判断,通过取读EXTI->PR寄存器来判
- 中断服务函数
首先检查中断标志位, 之后处理, 最后清除标志位
c
//声明为静态函数, 只能在本文件中使用, 其他文件可以有同名字的函数, 如果声明的是变量之后不可以使用extern在其他文件调用
static void EXTI_NVIC_Config(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void EXTI_Key_config(void)
{
//开启时钟
RCC_APB2PeriphClockCmd(KEY1_GPIO_CLOCK, ENABLE);
//初始化引脚
GPIO_InitTypeDef GPIO_InitStructure;
//初始化NVIC
EXTI_NVIC_Config();
GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);
//复用引脚,选择输入线
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
//初始化引脚
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line = EXTI_Line0; //设置为EXTI0
EXTI_InitStruct.EXTI_LineCmd = ENABLE; //使能
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; //设置为中断
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;//上升沿触发
EXTI_Init(&EXTI_InitStruct);
}
连接多个引脚相同的时候可以检测IDR寄存器