控制寄存器实现外部中断

F103有8个系统异常(不包括Reset和HardFault),60个外部中断,大多数异常的优先级是可以编写的。有关于系统异常和外部中断的声明可以在stm32f103.h中查询到,其中在IRQn_Type这个结构体中包含了F103系列的全部的异常声明(异常和中断之间不做区分,基本是一个东西)。

下面开始具体实现使用寄存器控制外部中断(重点重点)

Step1 设置中断源

首先,外部中断从哪里来呢,外部中断由端口获得信号。可是外部引脚不就是输入输出作用吗,怎么样才能成为中断源呢。这里就要用到引脚的复用功能了。对于控制外部中断源,这里要控制AFIO_EXTICRx x\in[1,4]。该寄存器结构如下图。

AFIO_EXTICRx

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

Step2 设置时钟

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

RCC_APB2ENR

根据每一位的英文,我们很容易知道,要打开AFIO的时钟,就要设置第0位为1,要打开GPIOA的时钟就要设置第2位为1。其它的都是一样的道理。

Step3 设置I/O的输入模式了

输入模式可根据外部电路的实际情况来设定,比如如果有上拉电阻或者下拉电阻,我们就可以设置输入模式为浮空输入,或者对应的上拉输入或者下拉输入。如果没有上拉电阻,就根据自己的意愿设定为上拉或者下拉输入。设定输入模式要用到的寄存器是GPIOx_CRLGPIOx_CRH二者功能类似,只不过是能控制不同部分的引脚。GPIOx_CRL可控制0~7引脚,GPIOx_CRH可控制8~15引脚。

GPIOx_CRL

GPIOx_CRH

至于具体该怎么设定数值,请参考下表
输入输出模式设定逻辑

Step4 设置中断触发方式

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

EXTI_RTSR

具体操作就是如果设定为1则有效,EXTI_RTSR可控制上升沿触发,EXTI_FTSR可控制下降沿触发。

Step5 设置优先级分组

设置优先级分组要用到SCB_AIRCR寄存器,该寄存器结构如下图

SCB_AIRCR

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

PRIGROUP

以PRIGROUP[2:0] 为 0b100为例,这时候高三位作为抢占优先级(主优先级)控制位,组合有23种,因此可以设置8种抢占优先级。最低位作为响应优先级(子优先级)控制位,同理,因为组合只有21种,所以可以设置2种响应优先级。至于其它位,不用管,只需要设定为0就可以了。

Step6 设置优先级

设定完毕优先级组之后,我们就可以设置优先级了。这里要设定NVIC_IPRx寄存器。该寄存器结构如下图

NVIC_IPRx

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

Step7 中断使能

中断使能就像是中断的一个开关,不被使能的中断是不会有响应的,使能中断要用到NVIC_ISERx x\in[0,2],该寄存器结构如下图,只需要和中断向量表相应的位置设置为1即可。

NVIC_ISERx

Step8 设置不屏蔽中断

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

image.png

只需要对应外部中断引脚编号,设定为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(挂起寄存器),该寄存器结构如图所示。

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)
    {
         //等待中断到来
    }
}

至此完结~~~
由于个人水平有限,难免有错,还望理解。
转发请声明出处。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容