【嵌入式实训】STM32中断处理机制及外部中断使用方法

简介: 理论知识 STM32系列处理器外部中断/事件控制器的原理 共19个外部中断线,其中GPIO端口以下图的方式连接到16个外部中断/事件线上: 另外三种其他的外部中断/事件控制器的连接如下:EXTI 线 
本文首发于稀土掘金。该平台的作者 逐光而行 也是本人。

理论知识

STM32系列处理器外部中断/事件控制器的原理

image.png

共19个外部中断线,其中GPIO端口以下图的方式连接到16个外部中断/事件线上:

image.png

image.png

image.png

  • 另外三种其他的外部中断/事件控制器的连接如下:EXTI 线 16 连接到 PVD 输出,EXTI 线 17 连接到 RTC 闹钟事件,EXTI 线 18 连接到 USB 唤醒事件
  • 每个输入线可以独立地配置输入类型(脉冲或挂起)和对应的触发事件(上升沿或下降沿触发, 或者双边沿都触发)。
  • 每个输入线都可以被独立屏蔽。挂起寄存器保持着状态线的中断要求。

STM32系列处理器外部中断/事件控制器的相关寄存器

(1)中断屏蔽寄存器(EXTI_IMR);

(2)事件屏蔽寄存器(EXTI_EMR);

(3)上升沿触发选择寄存器(EXTI_RTSR);

(4)下降沿触发选择寄存器(EXTI_FTSR);

(5)软件中断事件寄存器(EXTI_SWIER);

(6)挂起寄存器(EXTI_PR)

寄存器映像:

image.png

实验电路原理图

image.png
核心板上一共有四个普通按键S1-S4,分别通过上拉电阻与STM32的PD0-PD3相连,另一端接地,每次按键将在引脚上产生一个负脉冲。

实验要求

  • 在四个中断按键中任意选择两个实现控制功能,控制8位流水灯旋转的方向,一个使流水灯左转,一个使流水灯右转。
  • 剩下两个按键控制流水灯的转速,一个加速,一个减速。

程序主要代码记录如下:

main.c

#include "stm32f10x.h"
#include "delay.h"
#include "led.h"
#include "exti.h"

uint16_t time=500;
int flag=1;

int main(void)
{
    Delay_Init();               //延时初始化

    LED_Hardware_Init();        //LED初始化

    EXTI_Software_Init();     //外部中断初始化

    while(1)

    {

            //__WFI();              // cpu sleep , wait for interrupt to wake up

        //LED状态取反程序在exti.c中

      if(flag==1){

        LED_TOGGLE(LED1_PIN);
        Delay_ms(time);
        LED_TOGGLE(LED1_PIN);
        Delay_ms(time);

        LED_TOGGLE(LED2_PIN);
        Delay_ms(time);
        LED_TOGGLE(LED2_PIN);
        Delay_ms(time);

        LED_TOGGLE(LED3_PIN);
        Delay_ms(time);
        LED_TOGGLE(LED3_PIN);
        Delay_ms(time);

        LED_TOGGLE(LED4_PIN);
        Delay_ms(time);
        LED_TOGGLE(LED4_PIN);
        Delay_ms(time);

        LED_TOGGLE(LED5_PIN);
        Delay_ms(time);
        LED_TOGGLE(LED5_PIN);
        Delay_ms(time);
        
        LED_TOGGLE(LED6_PIN);
        Delay_ms(time);
        LED_TOGGLE(LED6_PIN);
        Delay_ms(time);

       
        LED_TOGGLE(LED7_PIN);
        Delay_ms(time);
        LED_TOGGLE(LED7_PIN);
        Delay_ms(time);

        LED_TOGGLE(LED8_PIN);
        Delay_ms(time);
        LED_TOGGLE(LED8_PIN);
        Delay_ms(time);
    }

        if(flag==2){

        LED_TOGGLE(LED8_PIN);
        Delay_ms(time);
        LED_TOGGLE(LED8_PIN);
        Delay_ms(time);

        LED_TOGGLE(LED7_PIN);
        Delay_ms(time);
        LED_TOGGLE(LED7_PIN);
        Delay_ms(time);

        LED_TOGGLE(LED6_PIN);
        Delay_ms(time);
        LED_TOGGLE(LED6_PIN);
        Delay_ms(time);

        LED_TOGGLE(LED5_PIN);
        Delay_ms(time);
        LED_TOGGLE(LED5_PIN);
        Delay_ms(time);

        LED_TOGGLE(LED4_PIN);
        Delay_ms(time);
        LED_TOGGLE(LED4_PIN);
        Delay_ms(time);

        LED_TOGGLE(LED3_PIN);
        Delay_ms(time);
        LED_TOGGLE(LED3_PIN);
        Delay_ms(time);

        LED_TOGGLE(LED2_PIN);
        Delay_ms(time);
        LED_TOGGLE(LED2_PIN);
        Delay_ms(time);

        LED_TOGGLE(LED1_PIN);
        Delay_ms(time);
        LED_TOGGLE(LED1_PIN);
        Delay_ms(time);
    }

    if(flag==3){
        time+=200;
    }
    if(flag==4){ 
        time-=200;
    }
    }     
}

exti.c

#include "stm32f10x.h"
#include "delay.h"
#include "led.h"

extern uint16_t time;
extern int flag;

/*******************************************************

函数名称:Exti_Init

输入参数:无

函数作用:外部中断初始化

返回参数:无

********************************************************/

void EXTI_Software_Init(void)

{

    //定义结构体变量
    GPIO_InitTypeDef GPIO_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    //开启时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_AFIO,ENABLE);

    //端口初始化

    GPIO_InitStructure.GPIO_Pin  =     GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;//选择端口

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;   //选择工作模式  浮空输入

    GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化GPIOD

    //中断线配置
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOD,GPIO_PinSource0);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOD,GPIO_PinSource1);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOD,GPIO_PinSource2);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOD,GPIO_PinSource3);
    
    //中断触发方式
    EXTI_InitStructure.EXTI_Line = EXTI_Line0;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
    EXTI_InitStructure.EXTI_Line=EXTI_Line1;
    EXTI_Init(&EXTI_InitStructure);
    EXTI_InitStructure.EXTI_Line=EXTI_Line2;
    EXTI_Init(&EXTI_InitStructure);
    EXTI_InitStructure.EXTI_Line=EXTI_Line3;
    EXTI_Init(&EXTI_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;            //使能按键所在的外部中断通道

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;//先占优先级0
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;       //子优先级0
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             //使能外部中断通道
    NVIC_Init(&NVIC_InitStructure);    
    NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;            //使能按键所在的外部中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;//先占优先级0
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;       //子优先级1
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             //使能外部中断通道
    NVIC_Init(&NVIC_InitStructure);
    NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;            //使能按键所在的外部中断通道

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;//先占优先级0
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;       //子优先级2
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             //使能外部中断通道
    NVIC_Init(&NVIC_InitStructure);
    NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;            //使能按键所在的外部中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;//先占优先级0
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;       //子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             //使能外部中断通道
    NVIC_Init(&NVIC_InitStructure);

}

 

/**********************************************************

*功  能:外部中断0响应函数

*参  数:无

*返回值:无

**********************************************************/

void EXTI0_IRQHandler(void)

{

    //from left to right

    //if(EXTI_GetITStatus(EXTI_Line0) != RESET)   //检查指定的EXTI0线路触发请求发生与否

if(EXTI_GetITStatus(EXTI_Line0) != RESET)    //检查指定的EXTI1线路触发请求发生与否

    {    
        flag=1;
    }
    EXTI_ClearITPendingBit(EXTI_Line0);         //清除EXTI0线路挂起位
}

 

/**********************************************************

*功  能:外部中断1响应函数

*参  数:无

*返回值:无

**********************************************************/

 

void EXTI1_IRQHandler(void)

{

    if(EXTI_GetITStatus(EXTI_Line1) != RESET)    //检查指定的EXTI1线路触发请求发生与否
    {    
        flag=2;
    }
    EXTI_ClearITPendingBit(EXTI_Line1);          //清除EXTI1线路挂起位
}

 

/**********************************************************

*功  能:外部中断2响应函数

*参  数:无

*返回值:无

**********************************************************/

void EXTI2_IRQHandler(void)

{

    if(EXTI_GetITStatus(EXTI_Line2) != RESET)     //检查指定的EXTI2线路触发请求发生与否

    {    
        flag=3;
    }
    EXTI_ClearITPendingBit(EXTI_Line2);           //清除EXTI2线路挂起位
}

 

/**********************************************************

*功  能:外部中断3响应函数

*参  数:无

*返回值:无

**********************************************************/

void EXTI3_IRQHandler(void)

{

    if(EXTI_GetITStatus(EXTI_Line3) != RESET)     //检查指定的EXTI3线路触发请求发生与否
    {    
        flag=4;
    }
    EXTI_ClearITPendingBit(EXTI_Line3);
}

代码思路说明

  • 执行流水灯相应操作的程序我写在了main.c中的main函数中,并通过设置全局变量,使中断响应函数改变该全局变量的值,从而触发操作。

(一开始我想把操作程序直接写在中断响应函数中,经老师指导,认为这样是复用性很差的写法,于是修改了)

  • 关于多个函数间全局变量的使用,可在一个函数的声明语句之后声明该变量,并在同一文件夹下的其他文件中通过extern引入该变量。

(复习c语言)

  • 关于流水灯左转右转的代码我写的这一版还稍显冗余。也许有一种能在for循环中调用传入参数的函数的写法,各位小伙伴自行摸索了哈。因为精力有限,我也不细究太多了。

一点感悟:

  • 代码其实不是我们从0创造的,老师提供了整一个工程项目文件,我们只需要把一些关键文件的关键函数进行修改即可。不过在这个改代码的过程中我也学会了很多东西。
  • 嵌入式讲求软硬结合,在实际连接到开发板上进行操作尝试的过程中,我突然理解了很多步骤的意图,这是我单纯看代码时迟迟领悟不到的。所以说“纸上得来终觉浅,绝知此事要躬行”。
相关文章
|
6月前
|
物联网 开发者 智能硬件
STM32:引领嵌入式系统新时代的微控制器
STM32:引领嵌入式系统新时代的微控制器
|
5月前
|
Web App开发 传感器 Linux
【嵌入式软件工程师面经】STM32单片机
【嵌入式软件工程师面经】STM32单片机
148 1
|
5月前
|
存储 数据安全/隐私保护 芯片
【STM32】详解嵌入式中FLASH闪存的特性和代码示例
【STM32】详解嵌入式中FLASH闪存的特性和代码示例
|
6月前
|
传感器
STM32标准库外部中断和定时器知识点总结-2
STM32标准库外部中断和定时器知识点总结
|
6月前
|
传感器
STM32标准库外部中断和定时器知识点总结-1
STM32标准库外部中断和定时器知识点总结
|
6月前
|
传感器 数据采集 物联网
基于STM32的光敏传感器数据采集系统-嵌入式系统与设计课程设计2
基于STM32的光敏传感器数据采集系统-嵌入式系统与设计课程设计
701 0
|
6月前
|
缓存 网络协议 算法
[蓝桥杯嵌入式]hal库 stm32 PWM的使用(随时修改占空比,随时修改频率)
[蓝桥杯嵌入式]hal库 stm32 PWM的使用(随时修改占空比,随时修改频率)
|
6月前
|
传感器 开发者
【STM32基础 CubeMX】外部中断
【STM32基础 CubeMX】外部中断
239 44
|
6月前
|
缓存 编译器 程序员
嵌入式开发环境Vscode开发STM32单片机程序
嵌入式开发环境Vscode开发STM32单片机程序
162 0