总代码:
之后只需要在主函数中将初始化代码调用即可。
#include "stm32f10x.h" // Device header void CountSensor_Init(void) { //打开外设时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //初始化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 EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line = EXTI_Line14; 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; //指定中断通道,EXTI和NVIC之间的部分 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); } void EXTI15_10_IRQHandler (void) { if(EXTI_GetITStatus(EXTI_Line14) == SET) { } }
复制
一、STM32中断资源介绍:
1.STM32中有68个可屏蔽的中断通道,包含EXTI、TIM、ADC、USART等等,每个中断通道拥有16个可编程的优先等级。
2.由于硬件的限制,中断跳转只能跳转到固定的地址去执行程序,所以一般在那个固定地址写一个跳转函数让程序跳到用户设置的地方去执行。
二、STM32中的NVIC:
NVIC简介:
1.STM32的NVIC(Nested Vectored Interrupt Controller,嵌套向量中断控制器)是STM32微控制器中的一个重要组件,它用于管理和协调处理器的中断请求。NVIC提供了灵活、高效、可扩展的中断处理机制,支持多级优先级、多向中断、嵌套向量中断等特性。
2.当STM32在程序运行过程中触发中断条件时,NVIC会暂停当前正在运行的程序,并转去处理中断程序。中断处理分为三个阶段:中断响应、中断处理、中断返回。当中断事件发生时,STM32首先会对中断请求进行检测;当中断处理程序执行完成时,CPU会返回到原来的执行状态,继续执行之前中断事件发生时的程序。
3.NVIC可以管理多个中断请求,并按优先级处理它们。在STM32中,每个中断源都需要指定两种优先级:抢占优先级和响应优先级。抢占优先级决定了哪个中断可以打断其他中断的执行;响应优先级则决定了在相同抢占优先级下,哪个中断会先被执行。
4.NVIC的配置方法通常包括设置中断优先级分组和配置中断优先级。在STM32CubeMX或手动配置的情况下,可以选择合适的中断优先级分组方式,通常有四种模式可供选择。在初始化中断时,可以使用HAL库提供的函数或直接写寄存器的方式对中断的优先级进行配置。
5.NVIC的中断控制及状态寄存器中,有一个VECTACTIVE位段和特殊功能寄存器ISR,它们都记录了当前正服务异常的编号。如果一个中断不能立即响应,就称它被“悬起”(pending)。
在嵌入式系统中,理解和使用NVIC对于确保系统能够正确处理中断请求、提高系统的性能和可靠性至关重要。
NVIC优先级分组:
这里的分组方式的意思是,优先级一共只有四位,那么如果你选择只给1位的抢占优先级那么你的响应优先级配置就只能用剩下的三位寄存器,那么就是分组1。
三、EXTI:
EXTI可以监测GPIO口的电平信号,当指定的GPIO口发生电平
1.支持的触发方式:上升沿/下降沿/双边沿/软件触发
2.支持所有的GPIO口,但是相同的Pin不能同时触发中断,意思就是不能是PA1和PB1同时设置位中断位,但是可以是 PA1和PA2或者PA1和PB2。
3.有20个通道数:16个GPIO,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒。
4.触发响应方式:中断响应/事件响应。
5.外部中断能换个从低功耗模式的停止模式下唤醒STM32。
四、步骤:
所有步骤就基于上图:
1.先配置好要作为接收外设数据的GPIO口,然后再通过AFIO将接收数据的GPIO口设置为中断引脚,
2.(这个过程参考下图)然后再通过EXTI将该中断引脚对应的中断通道打开,让其跟NVIC相连通,再进行配置触发方式和触发后的响应模式(产生中断/或者产生事件)EXTI中的边缘检测电路会根据上放的上升沿下降沿的两个寄存器的配置,当该引脚电平变化满足设置好的触发方式时,边缘检测电路会向外发送1,到左边的或门
3.经过NVIC为该通道配置好优先级之后,如果设置为下降沿触发,那么如果当GPIO口接收的电平数据有高电平降低为低电平时,那么就会产生中断,通过中断通道向NVIC发送中断申请,如果有多个中断申请一起来,NVIC会通过他们的优先级,选择优先级最高的中断通道发送给CPU,
4.然后CPU就会跳到指定的地址,那个地址上已经写好一个跳转函数,他会将程序跳转到我们的中断程序的位置让CPU去执行我们的中断程序。
第一步:配置RCC,把涉及的外设的时钟打开
- 涉及的外设有GPIO、AFIO、EXTI、NVIC,其中EXTI的时钟是一直打开的不需要我们自己打开,还有NVIC是内核中的外设,内核的外设都不需要开启时钟。
1.代码:
//打开外设时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
复制
第二步:在GPIO文件中查找函数配置GPIO,选择端口为输入模式
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);
复制
第三步:在GPIO文件中查找函数配置AFIO,将GPIO连接到后面的EXTI
1.AFIO主要负责复用功能引脚重映射、引脚复用功能的选择和重定义、中断引脚选择。
引脚复用功能重定义如下图:
2.红色圈中是告知配置哪一个寄存器就能够选择蓝色部分中的引脚接到黄色部分的EXTI1~15这几个数据选择器中,然后作为外部中断引脚
3.代码:
//初始化AFIO GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
复制
第四步:到EXTI文件中查看函数并调用配置EXTI,选择边沿触发方式和触发响应模式(产生中断/事件)
4.代码:
EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line = EXTI_Line14; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_Init(&EXTI_InitStructure);
复制
以下四个函数都可以读写状态寄存器,上面的两个适合在普通程序中进行调用,下面的两个适合在中断函数中进行调用,因为下面两个函数只能读取与中断有关的标志位,并且对中断是否允许进行了判断,上面的两个函数只是一般的读写标志位,不进行额外的读取,能不能触发中断的标志位都能读取。
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);
复制
第五步:到misc文件中查看函数,配置NVIC,给中断选择一个合适的优先级
5.代码:
//指定中断通道,EXTI和NVIC之间的部分 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);
复制
第六步:中断函数:到启动文件中查看中断向量表,确认中断程序名
6.代码:
void EXTI15_10_IRQHandler (void) { if(EXTI_GetITStatus(EXTI_Line14) == SET) { } }
复制
首先,每个中断程序的名字都是确定的,要去启动文件中找中断向量表:
; External Interrupts DCD WWDG_IRQHandler ; Window Watchdog DCD PVD_IRQHandler ; PVD through EXTI Line detect DCD TAMPER_IRQHandler ; Tamper DCD RTC_IRQHandler ; RTC DCD FLASH_IRQHandler ; Flash DCD RCC_IRQHandler ; RCC DCD EXTI0_IRQHandler ; EXTI Line 0 DCD EXTI1_IRQHandler ; EXTI Line 1 DCD EXTI2_IRQHandler ; EXTI Line 2 DCD EXTI3_IRQHandler ; EXTI Line 3 DCD EXTI4_IRQHandler ; EXTI Line 4 DCD DMA1_Channel1_IRQHandler ; DMA1 Channel 1 DCD DMA1_Channel2_IRQHandler ; DMA1 Channel 2 DCD DMA1_Channel3_IRQHandler ; DMA1 Channel 3 DCD DMA1_Channel4_IRQHandler ; DMA1 Channel 4 DCD DMA1_Channel5_IRQHandler ; DMA1 Channel 5 DCD DMA1_Channel6_IRQHandler ; DMA1 Channel 6 DCD DMA1_Channel7_IRQHandler ; DMA1 Channel 7 DCD ADC1_2_IRQHandler ; ADC1_2 DCD USB_HP_CAN1_TX_IRQHandler ; USB High Priority or CAN1 TX DCD USB_LP_CAN1_RX0_IRQHandler ; USB Low Priority or CAN1 RX0 DCD CAN1_RX1_IRQHandler ; CAN1 RX1 DCD CAN1_SCE_IRQHandler ; CAN1 SCE DCD EXTI9_5_IRQHandler ; EXTI Line 9..5 DCD TIM1_BRK_IRQHandler ; TIM1 Break DCD TIM1_UP_IRQHandler ; TIM1 Update DCD TIM1_TRG_COM_IRQHandler ; TIM1 Trigger and Commutation DCD TIM1_CC_IRQHandler ; TIM1 Capture Compare DCD TIM2_IRQHandler ; TIM2 DCD TIM3_IRQHandler ; TIM3 DCD TIM4_IRQHandler ; TIM4 DCD I2C1_EV_IRQHandler ; I2C1 Event DCD I2C1_ER_IRQHandler ; I2C1 Error DCD I2C2_EV_IRQHandler ; I2C2 Event DCD I2C2_ER_IRQHandler ; I2C2 Error DCD SPI1_IRQHandler ; SPI1 DCD SPI2_IRQHandler ; SPI2 DCD USART1_IRQHandler ; USART1 DCD USART2_IRQHandler ; USART2 DCD USART3_IRQHandler ; USART3 DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 DCD RTCAlarm_IRQHandler ; RTC Alarm through EXTI Line DCD USBWakeUp_IRQHandler ; USB Wakeup from suspend
复制
然后:
我的例子中是用到下面这个,而这个中断程序当中断通道15~10被触发时都能进来,我用的是14中断通道,所以必须在中断函数中对该通道的标志位进行判断,然后执行对应的程序。
DCD EXTI15_10_IRQHandler ; EXTI Line 15..10 当外部中断线15到10中的任何一条被触发时,处理器会跳转到EXTI15_10_IRQHandler这个中断服务程序。
复制
中断标志位判断和清除:
- 以下四个函数都可以读写状态寄存器,上面的两个适合在普通程序中进行调用,下面的两个适合在中断函数中进行调用,因为下面两个函数只能读取与中断有关的标志位,并且对中断是否允许进行了判断,上面的两个函数只是一般的读写标志位,不进行额外的读取,能不能触发中断的标志位都能读取。
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_GetITStatus函数来读取中短线14的标志位是否有效,进行if判断之后就可以在if内编写自己的中断程序了,并且在程序的结尾要用EXTI_ClearITPendingBit函数清除中断标志位,否则会一直触发中断。