F103有8个系统异常(不包括Reset和HardFault),60个外部中断,大多数异常的优先级是可以编写的。有关于系统异常和外部中断的声明可以在stm32f103.h中查询到,其中在IRQn_Type这个结构体中包含了F103系列的全部的异常声明(异常和中断之间不做区分,基本是一个东西)。
下面开始具体实现使用寄存器控制外部中断(重点重点)
Step1 设置中断源
首先,外部中断从哪里来呢,外部中断由端口获得信号。可是外部引脚不就是输入输出作用吗,怎么样才能成为中断源呢。这里就要用到引脚的复用功能了。对于控制外部中断源,这里要控制AFIO_EXTICRx 。该寄存器结构如下图。

对于每一组I/O口,都有编号为0~15的引脚。其中,编号相同的引脚可以设置为一个中断源,例如可设置PA0、PB0、PC0...PG0中任意一个作为EXTI0的中断源。具体如何设置可以根据下面这个图片。

Step2 设置时钟
既然用到了外部引脚,还用到了复用功能,那么当然就要打开它们相关的时钟了。这里要用到的寄存器是RCC_APB2ENR。该寄存器的结构如下图所示。

根据每一位的英文,我们很容易知道,要打开AFIO的时钟,就要设置第0位为1,要打开GPIOA的时钟就要设置第2位为1。其它的都是一样的道理。
Step3 设置I/O的输入模式了
输入模式可根据外部电路的实际情况来设定,比如如果有上拉电阻或者下拉电阻,我们就可以设置输入模式为浮空输入,或者对应的上拉输入或者下拉输入。如果没有上拉电阻,就根据自己的意愿设定为上拉或者下拉输入。设定输入模式要用到的寄存器是GPIOx_CRL或GPIOx_CRH二者功能类似,只不过是能控制不同部分的引脚。GPIOx_CRL可控制0~7引脚,GPIOx_CRH可控制8~15引脚。


至于具体该怎么设定数值,请参考下表

Step4 设置中断触发方式
我们都知道51单片机的中断只能由下降沿触发。而STM32的中断触发方式是可以设定的。可由上升沿方式触发,也可由下降沿触发。具体由EXTI_RTSR和EXTI_FTSR控制。两个寄存器结构是一样的,如下图所示。

具体操作就是如果设定为1则有效,EXTI_RTSR可控制上升沿触发,EXTI_FTSR可控制下降沿触发。
Step5 设置优先级分组
设置优先级分组要用到SCB_AIRCR寄存器,该寄存器结构如下图

高半字用于作为 密钥 在执行写操作的时候,只有密钥写入为0x5FA,写操作才有效,否则无效。
10~8位作为优先级分组控制,真值表如下

以PRIGROUP[2:0] 为 0b100为例,这时候高三位作为抢占优先级(主优先级)控制位,组合有23种,因此可以设置8种抢占优先级。最低位作为响应优先级(子优先级)控制位,同理,因为组合只有21种,所以可以设置2种响应优先级。至于其它位,不用管,只需要设定为0就可以了。
Step6 设置优先级
设定完毕优先级组之后,我们就可以设置优先级了。这里要设定NVIC_IPRx寄存器。该寄存器结构如下图

看到这么多东西可能有些头大,实际上原理不难,每一个长方形框是一个字节,用来控制一个中断的中断优先级,8位可以表示28=256种优先级。当然对于一般使用而言用不上那么多的中断优先级,因此ST公司对于stm32f10x系列的芯片的中断优先级进行了精简,一个字节中只有高4位有效,因此实际上可表示24=16种优先级。这里和优先级分组并不冲突,因为优先级分组实际上也就是将控制优先级的4位进行分配。值得注意的是对于Cortex-M3内核,设置优先级编号越小,优先级越高至于我们到底要对哪一个字节进行赋值操作,只需要按照自己所要配置的中断的中断编号来就行了,这个是一一对应的。在stm32f10x.h这个文件中,就有中断向量表。
Step7 中断使能
中断使能就像是中断的一个开关,不被使能的中断是不会有响应的,使能中断要用到NVIC_ISERx ,该寄存器结构如下图,只需要和中断向量表相应的位置设置为1即可。

Step8 设置不屏蔽中断
如果不设置的话,中断还是被屏蔽的状态,和未使能差不多,所以这时候就要控制EXTI_IMR(中断屏蔽寄存器),该寄存器结构如图所示。

只需要对应外部中断引脚编号,设定为1即可开放中断。
Step9 编写中断函数
至此,外部中断的获取我们已经设定完成了,接下来就编写中断函数就可以了那么中断从哪里进入呢。我们可能了解过,中断服务其实只是将程序指针跳转到某一之前安排好的代码空间,在STM32的启动文件startup_stm32f10x_hd.s中,就可找到中断入口。这里帮大家列出。
EXPORT WWDG_IRQHandler [WEAK]
EXPORT PVD_IRQHandler [WEAK]
EXPORT TAMPER_IRQHandler [WEAK]
EXPORT RTC_IRQHandler [WEAK]
EXPORT FLASH_IRQHandler [WEAK]
EXPORT RCC_IRQHandler [WEAK]
EXPORT EXTI0_IRQHandler [WEAK]
EXPORT EXTI1_IRQHandler [WEAK]
EXPORT EXTI2_IRQHandler [WEAK]
EXPORT EXTI3_IRQHandler [WEAK]
EXPORT EXTI4_IRQHandler [WEAK]
EXPORT DMA1_Channel1_IRQHandler [WEAK]
EXPORT DMA1_Channel2_IRQHandler [WEAK]
EXPORT DMA1_Channel3_IRQHandler [WEAK]
EXPORT DMA1_Channel4_IRQHandler [WEAK]
EXPORT DMA1_Channel5_IRQHandler [WEAK]
EXPORT DMA1_Channel6_IRQHandler [WEAK]
EXPORT DMA1_Channel7_IRQHandler [WEAK]
EXPORT ADC1_2_IRQHandler [WEAK]
EXPORT USB_HP_CAN1_TX_IRQHandler [WEAK]
EXPORT USB_LP_CAN1_RX0_IRQHandler [WEAK]
EXPORT CAN1_RX1_IRQHandler [WEAK]
EXPORT CAN1_SCE_IRQHandler [WEAK]
EXPORT EXTI9_5_IRQHandler [WEAK]
EXPORT TIM1_BRK_IRQHandler [WEAK]
EXPORT TIM1_UP_IRQHandler [WEAK]
EXPORT TIM1_TRG_COM_IRQHandler [WEAK]
EXPORT TIM1_CC_IRQHandler [WEAK]
EXPORT TIM2_IRQHandler [WEAK]
EXPORT TIM3_IRQHandler [WEAK]
EXPORT TIM4_IRQHandler [WEAK]
EXPORT I2C1_EV_IRQHandler [WEAK]
EXPORT I2C1_ER_IRQHandler [WEAK]
EXPORT I2C2_EV_IRQHandler [WEAK]
EXPORT I2C2_ER_IRQHandler [WEAK]
EXPORT SPI1_IRQHandler [WEAK]
EXPORT SPI2_IRQHandler [WEAK]
EXPORT USART1_IRQHandler [WEAK]
EXPORT USART2_IRQHandler [WEAK]
EXPORT USART3_IRQHandler [WEAK]
EXPORT EXTI15_10_IRQHandler [WEAK]
EXPORT RTCAlarm_IRQHandler [WEAK]
EXPORT USBWakeUp_IRQHandler [WEAK]
EXPORT TIM8_BRK_IRQHandler [WEAK]
EXPORT TIM8_UP_IRQHandler [WEAK]
EXPORT TIM8_TRG_COM_IRQHandler [WEAK]
EXPORT TIM8_CC_IRQHandler [WEAK]
EXPORT ADC3_IRQHandler [WEAK]
EXPORT FSMC_IRQHandler [WEAK]
EXPORT SDIO_IRQHandler [WEAK]
EXPORT TIM5_IRQHandler [WEAK]
EXPORT SPI3_IRQHandler [WEAK]
EXPORT UART4_IRQHandler [WEAK]
EXPORT UART5_IRQHandler [WEAK]
EXPORT TIM6_IRQHandler [WEAK]
EXPORT TIM7_IRQHandler [WEAK]
EXPORT DMA2_Channel1_IRQHandler [WEAK]
EXPORT DMA2_Channel2_IRQHandler [WEAK]
EXPORT DMA2_Channel3_IRQHandler [WEAK]
EXPORT DMA2_Channel4_5_IRQHandler [WEAK]
因此只需要我们写一个函数,函数名为对应的中断入口函数名就可以,比如外部中断为EXTI0,它的函数名应该为EXTI0_IRQHandler。这里值得注意的是外部中断95还有1510分别公用一个中断函数,分别为EXTI9_5_IRQHandler和EXTI15_10_IRQHandler。
中断响应后,会使该中断被挂起,为了使这个中断第二次以及更多次响应,应该再解挂。这里要设置EXTI_PR(挂起寄存器),该寄存器结构如图所示。

当在外部中断线上发生了选择的边沿事件,该位被置’1’。在该位中写入’1’可以清除它,也可以通过改变边沿检测的极性清除。这个操作一般应该直接写在中断服务函数中。
下面举例一些用按键控制EXTI0产生中断
#include "core_cm3.h"
#include "stm32f10x.h"
void NVIC_Config()
{
//前面的是密钥,后面是优先级分组
//优先级分组1
SCB->AIRCR = 0x05FA0000 | 0x0600;
//1抢占优先级,1响应优先级
NVIC->IP[6] = 0x90;
//使能中断
NVIC->ISER[0] = 0x40;
}
void EXTI_Key_Config(void)
{
//打开GPIOA时钟和复用功能时钟
RCC->APB2ENR |= 0x0004 | 0x0001;
NVIC_Config();
/* 配置为浮空输入 */
// 设置按键的引脚为浮空输入
GPIOA->CRL = 0x04;
//中断0不屏蔽
EXTI->IMR = 0x01;
//中断0上升沿触发
EXTI->RTSR = 0x01;
//配置EXTI0中断源为PA0
AFIO->EXTICR[0] = 0x00;
}
void EXTI0_IRQHandler(void)
{
//中断服务函数,我这里是让灯亮起来
LED_R(ON);
//解挂
EXTI->PR |= 0x00001;
}
int main(void)
{
//初始化LED灯引脚
LED_Config();
//设定时钟,这个要自己写,在这个实验中去掉也行
Clock_Config();
//初始化中断按键
EXTI_Key_Config();
while(1)
{
//等待中断到来
}
}
至此完结~~~
由于个人水平有限,难免有错,还望理解。
转发请声明出处。