嵌入式开发十六:外部中断实验1:通过外部中断控制灯亮灭

        中断在嵌入式应用中占有非常重要的地位,几乎每个控制器都有中断功能。中断对保证紧急事件得到第一时间处理是非常重要的,本篇博客主要是外部中断的一个简单应用,我们设计使用按键来作为触发源,使得控制器产生中断,并在中断服务函数中实现控制LED灯的任务。通过这样一个简单的中断实验来熟悉中断的配置过程,为后续使用中断打好基础。

目录

一、实现的功能

二、硬件设计

三、程序设计

3.1创建工程模板

3.2工程中创建对应的文件

3.3添加文件路径

3.4添加相应的外设固件库和所需文件

3.5程序流程图

​3.5.1 编写LED0和LED1初始化函数驱动代码

3.5.2 初始化外部中断,包括:EXTI初始化、NVIC初始化

3.5.3 编写中断服务函数

3.5.5 编写延时函数

3.5.6 程序主函数

四、下载验证


一、实现的功能

       我们设计使用按键Key0来作为触发源,使得控制器产生中断,并在中断服务函数中实现控制LED灯的任务。即当按键按下时,对应的 IO 口电平会发生变化,这时只要配置好对应端口的外部中断就可以触发中断,从而执行中断服务函数。

二、硬件设计

本实验用到的硬件资源有:

LED0和LED1 以及独立按键硬件部分的连接方式的原理图在前面博客已经详细介绍过,如图所示

在本次实验中,我们只用到了Key0,为了练习,这里详细讲解如果这四个按键作为中断源/中断通道,按键是连在GPIO口的,即当按键按下时,对应的 GPIO 口电平会发生变化,这时只要配置好对应端口的外部中断就可以触发中断,从而执行中断服务函数。我们应将如何配置对应的端口模式?

       首先分析PA0,按键未按下时是低电平(下拉),当按键按下时是高电平,因此,当PA口的电平变化由低电平变化为高电平时,触发中断,也就是PA0需要配置为:输入下拉,上升沿触发模式;

       再看PE4、PE3、PE2,按键未按下时是高电平(上拉),当按键按下时是低电平,因此,当PA口的电平变化由高电平变化为低电平时,触发中断,也就是PA0需要配置为:输入上拉,下降沿触发模式;

三、程序设计

3.1创建工程模板

       直接复制创建好的库函数模板, 在此模板上进行程序开发。将复制过来的模板文件夹重新命名为“实验四:外部中断实验:中断控制LED灯”。打开此文件夹,在Project目录下新建一个文件夹,命名为:MyExti,用于存放按键中断的驱动程序,因为本次实验涉及到LED,因此,同样需要建立MyDelay和MyLed文件夹,存放对应的驱动程序。创建好后,目录如下所示:

3.2工程中创建对应的文件

      在工程下创建对应的文件夹,同时新建对应的源文件和头文件,创建好后如下图所示:

3.3添加文件路径

       要想工程在编译阶段能够找到创建的文件,必须要加入文件路径,详细步骤之前已经介绍过,路径添加完毕后,如下图所示:

3.4添加相应的外设固件库和所需文件

      本次实验是外部中断输入,有用到额外的外设SYSCFG和EXTI,因此,需要勾选这两个外设,从而在工程中引入相应的固件库源文件。与之前相比多了这两个。

3.5程序流程图

 3.5.1编写LED0和LED1初始化函数驱动代码

      由于后面我们需要利用按键中断来控制灯的开和关,所以我们必须要首先对它们进行初始化,设置PF9和PF10的GPIO口的端口模式。这与我们之前跑马灯实验初始化函数相同。

在 myled.h文件夹下编写如下代码:

#ifndef __MYLED_H
#define __MYLED_H

void LED_Init(void);

#endif

在 myled.c文件夹下编写如下代码:

#include "stm32f4xx.h"                  // Device header
#include "myled.h"


void LED_Init(void)
{
    //第一步:使能GPIOF两个口的时钟 
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能 GPIOF 时钟
	
   //第二步:GPIOF9,F10 初始化设置
   GPIO_InitTypeDef GPIO_InitStructure;
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;//LED0对应 IO 口
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
   GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化 GPIO

   //第三步:设置灯的初始状态
   GPIO_ReSetBits(GPIOF,GPIO_Pin_9 );//GPIOF9,设置低电平,灯亮
}

3.5.2  初始化外部中断,包括:EXTI初始化、NVIC初始化

        本小节是配置外部中断的关键部分,在上篇博客已经详细介绍过过程,这里针对这个实验复述整个过程,这也是程序编码的逻辑,务必掌握!

第一步:使能GPIO时钟和使能SYSCFG时钟

       首先,我们要使用GPIOE作为中断输入,同时LED所在的GPIOF,所以我们要使能相应的 GPIOE和GPIOF的时钟, EXTI和 NVIC的时钟默认是打开的,因此,我们不需要使能,但是需要使能SYSCFG的时钟。

第二步:初始化口为输入模式

        按键Key0所在的GPIO口为GPIOE,因此使用 GPIOE_Pin_4口作为中断输入,初始化相应的GPIOE_Pin_4口为输入上拉;

第三步:配置SYSCFG,设置GPIO口与中断线的映射关系。

         KEY0、KEY1、KEY2 和 KEY_UP 分别连接 PE4、PE3、PE2 和 PA0,即对应了 EXTI4、 EXTI3、EXTI2 和 EXTI0 这四条外部中断线。这里需要注意的是 EXTI0 到 EXTI4 都是有单独 的 中 断 向 量 , EXTI5 到 EXTI9 是公用 EXTI9_5_IRQn , EXTI10 到 EXTI15 是公用 EXTI15_10_IRQn。

        本次实验只用了GPIOE_Pin_4(PE4),因此应该使用SYSCFG将其映射到EXTI4中断通道

第四步:初始化线上中断EXTI

       定义EXTI外设结构体,对成员变量初始化(对寄存器操作),对于本实验就是中断通道为:EXTI4、中断响应、下降沿触发模式(前面分析过);然后调用初始化函数EXTI_Init()。

第五步:初始化中断分组(NVIC) , 并使能中断。

         既然是外部中断,涉及到中断我们当然还要设置 NVIC 中断分组和优先级。

(1)NVIC中断分组:直接调用函数:NVIC_ PriorityGroupConfig();//整个工程只设置一次

(2)此外部中断的设置:首先,定义内核外设NVIC的结构体,对成员变量初始化(对寄存器操作),对于本实验就是中断通道为:EXTI4_IRQn、抢占优先级、响应优先级、使能外部中断通道,然后调用初始化函数NVIC_Init()。

在 myexti.h文件夹下编写如下代码:

#ifndef _MY_EXTI_H_
#define _MY_EXTI_H_

void My_ExtiInit(void);  //初始化中断

#endif

在 myexti.c文件夹下编写如下代码:

#include"stm32f4xx.h"


/*******************************************
    第一步:开时钟
    按键 ---GPIOE_Pin_4
    NVIC  不需要开时钟!!!(在内核里面)
    SYSCFG ---APB2总线上
    EXTI的时钟是默认打开的
	第二步:初始化GPIO
	第三步:配置SYSCFG,映射通道
	第四步:初始化EXTI:边沿检测控制
	第五步:初始化NVIC,中断响应优先级,优先处理哪一个中断
	第六步:编写中断函数:第1步:查看函数名称:启动文件中定义好的中断处理函数。第二步:重写中断函数
*********************************************/

//初始化方法统一方法:定义外设对应的结构体,对结构体变量的成员赋初值(配置寄存器),调用初始化函数


void My_ExtiInit(void)
{
	//开启GPIOE的时钟
	 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
	//开启SYSCFG时钟
	 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
	
	//初始化GPIOE
	GPIO_InitTypeDef Struct;
	Struct.GPIO_Pin= GPIO_Pin_4;
	Struct.GPIO_Mode= GPIO_Mode_IN;   //输入
	Struct.GPIO_PuPd=GPIO_PuPd_UP;	 //配置按键,默认上拉
	GPIO_Init(GPIOE,&Struct);  
	
	
	//映射通道 将GPIOE_Pin4映射到EXTI的通道4上	
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);
	
	
	
	//初始化EXTI
	EXTI_InitTypeDef Struct2;
	Struct2.EXTI_Line=EXTI_Line4;             //设置中断通道	
	Struct2.EXTI_Mode=EXTI_Mode_Interrupt;   //中断模式:中断响应和事件响应
	Struct2.EXTI_Trigger=EXTI_Trigger_Falling; //中断触发方式:下降沿触发
	Struct2.EXTI_LineCmd=ENABLE;             //中断使能
	EXTI_Init(&Struct2);
	
	
	//设置中断分组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	
	//初始化NVIC
	NVIC_InitTypeDef Struct3;
	Struct3.NVIC_IRQChannel=EXTI4_IRQn;             //设置中断源/中断通道
	Struct3.NVIC_IRQChannelPreemptionPriority=0;  //设置抢占优先级
	Struct3.NVIC_IRQChannelSubPriority=0;        //设置响应优先级
	Struct3.NVIC_IRQChannelCmd=ENABLE;             //使能中断
	NVIC_Init(&Struct3);
	
}

3.5.3编写中断服务函数

         初始化 EXTI 后,中断就已经开启了,当任意按键按下后会触发一次中断, 这时程序就会进入中断服务函数执行,所以我们还需要编写对应的 EXTI 中断服务函数.实现一些控制。  因为PE4管脚对应的中断线是EXTI4,打开启动文件查看对应的中断服务函数名为EXTI4_IRQHandler, 这个从名字来看就很好理解。一般我们把中断服务函数和相应的中断初始化函数放在一个文件。

        一般为确保中断确实发生,我们会在中断服务函数调用中断标志位状态读取函数读取外设中断标志位并判断标志位状态。 EXTI_GetITStatus 函数用来获取 EXTI 的中断标志位状态,如果 EXTI 线有中断发生函 数返回“SET”否则返回“RESET”。实际上EXTI_GetITStatus 函数是通过读取 EXTI_PR 寄存器值来判断 EXTI 线状态的。 执行任务后需要调用 EXTI_ClearITPendingBit 函数清除 EXTI 线的中断标志 位。

 在 myexti.c文件夹下编写如下代码:

//编写中断函数
void EXTI4_IRQHandler(void)//为什么中断函数没有返回值和参数?
{	
	if(EXTI_GetITStatus(EXTI_Line4) == SET) //判断中断是否产生,对中断标志位进行检测
	{	
		GPIO_ToggleBits (GPIOF,GPIO_Pin_9 );   //电平翻转实现对LED的控制
	}
	EXTI_ClearITPendingBit(EXTI_Line4);      //中断触发后,清零操作!!!
}

3.5.5编写延时函数

在 mydelay.h文件夹下编写如下代码:

#ifndef __MYDELAY_H__
#define __MYDELAY_H__
#include <stdint.h>
 
void My_Delay_us(uint32_t num); //延时任意微秒
void My_Delay_ms(uint32_t num); //延时任意毫秒
void My_Delay_s(uint32_t num);  //延时任意秒
 
#endif

在 mydelay.c文件夹下编写如下代码: 

#include "stm32f4xx.h"                  // Device header
#include "mydelay.h"
 
 
//延时num微秒
void My_Delay_us(uint32_t num)
{
    while(num--)
    {
        SysTick ->CTRL = (1 << 0);
    
        SysTick ->CTRL &= ~(1<<2);
    
        SysTick ->CTRL &= ~(1<<1);
    
        SysTick ->VAL = 0x0;
    
        SysTick ->LOAD = 21;   //1秒 21000000HZ,1毫秒 21000HZ  1微秒 21 HZ
    
        while(!(SysTick ->CTRL & (1<<16)));
        SysTick ->CTRL = ~(1<<0);
    }
}
 
 
//延时num毫秒,1毫秒等于1000微秒
void My_Delay_ms(uint32_t num)
{
    while(num--)
    {
        My_Delay_us(1000);
    }
}
 
 
//延时num秒,1秒等于1000毫秒
void My_Delay_s(uint32_t num)
{
    while(num--)
    {
        My_Delay_ms(1000);
    }
}


3.5.6程序主函数

      首先是调用初始化函数主要有LED的初始化函数和外部中断初始化函数接下来。

#include "stm32f4xx.h"                  // Device header
#include "myexti.h"
#include "myled.h"


int main(void)
{
	 LED_Init();//LED初始化
	 My_ExtiInit(); //外部中断初始化
	
     while(1)
	 {
		 
	 }
}

四、下载验证

         我们先来看看编译结果,如图所示:

 可以看到 0 错误,0 警告,编译通过,接下来,大家就可以下载验证了。这里我们使用 DAP 仿真器下载。下载完之后,运行结果如图所示:

VID_20240516_123328

      可以看到我们按 KEY0 可以控制 LED 灯的打开和关闭(每按一次Key0都会触发一次中断,将LED的PF9电平翻转,从而实现对LED灯的控制),符合预期设计。至此,我们的本次的学习就结束了。主要学习了 STM32F407 外部中断的使用方法。这一节我们就讲解到这里,希望能对大家的开发有帮助。 如有兴趣,感谢点赞、关注、收藏,若有不正地方,还请各位大佬多多指教!

  • 19
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
STM32是一款基于ARM Cortex-M系列内核的32位微控制器,可用于开发嵌入式系统。按键中断和LED灯亮是嵌入式系统中非常常见的功能,下面我将用300字回答如何使用STM32的按键中断控制LED灯的亮。 首先,我们需要连接一个按键和一个LED灯到STM32微控制器的相应引脚上。假设按键连接到PA0引脚,LED灯连接到PC13引脚。 接下来,我们需要配置STM32的GPIO外设来控制这些引脚。首先,开启相关引脚的时钟。然后,将PA0引脚配置为输入模式,PC13引脚配置为输出模式。可以使用STM32的寄存器或者开发环境提供的库函数来完成这些配置。 然后,我们需要配置外部中断。配置PA0引脚所对应的外部中断线,使其可以检测到按键的状态变化。可以使用STM32的寄存器或者库函数来完成这个配置。 紧接着,我们编写中断处理函数。当按键状态发生变化,中断触发时,中断处理函数被调用。在该函数中,我们可以读取按键引脚的状态,如果按键被按下,我们将PC13引脚设置为高电平,LED灯亮起;如果按键被释放,我们将PC13引脚设置为低电平,LED灯熄。 最后,我们需要在主函数中启用中断。启用中断后,当按键状态发生变化时,中断处理函数将被调用。 以上就是使用STM32的按键中断来控制LED灯亮的步骤。通过配置GPIO外设和外部中断,编写中断处理函数,我们能够实现按下按键时,LED灯亮起;释放按键时,LED灯熄的功能。这样就完成了按键中断控制LED灯亮的任务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未来可期,静待花开~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值