STM32外部中断驱动按键,实现按键控制LED灯的开关

STM32F407ZE开发板外部中断驱动按键,实现按键控制LED灯的开关.

main.c部分:

#include <stm32f4xx.h> //该头文件作用和reg51.h是一样的
#include "sys.h"
#include "led.h"
#include "key.h"
#include "delay.h"


int main()
{
    
    
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	//中断优先级分组  2分组
	
	LED_Init();										//LED灯初始化
	KEYEXTI_Init1();								//按键PA0的外部中断初始化
	
	while(1)
	{
    
    
		
	}
}

sys.h部分:

#ifndef __SYS_H
#define __SYS_H	 
#include "stm32f4xx.h" 

//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr & 0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+20) //0x40020014
#define GPIOB_ODR_Addr    (GPIOB_BASE+20) //0x40020414 
#define GPIOC_ODR_Addr    (GPIOC_BASE+20) //0x40020814 
#define GPIOD_ODR_Addr    (GPIOD_BASE+20) //0x40020C14 
#define GPIOE_ODR_Addr    (GPIOE_BASE+20) //0x40021014 
#define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414    
#define GPIOG_ODR_Addr    (GPIOG_BASE+20) //0x40021814   
#define GPIOH_ODR_Addr    (GPIOH_BASE+20) //0x40021C14    
#define GPIOI_ODR_Addr    (GPIOI_BASE+20) //0x40022014     

#define GPIOA_IDR_Addr    (GPIOA_BASE+16) //0x40020010 
#define GPIOB_IDR_Addr    (GPIOB_BASE+16) //0x40020410 
#define GPIOC_IDR_Addr    (GPIOC_BASE+16) //0x40020810 
#define GPIOD_IDR_Addr    (GPIOD_BASE+16) //0x40020C10 
#define GPIOE_IDR_Addr    (GPIOE_BASE+16) //0x40021010 
#define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410 
#define GPIOG_IDR_Addr    (GPIOG_BASE+16) //0x40021810 
#define GPIOH_IDR_Addr    (GPIOH_BASE+16) //0x40021C10 
#define GPIOI_IDR_Addr    (GPIOI_BASE+16) //0x40022010 
 
//STM32中  对寄存器的访问  是不能单独访问寄存器的单个bit  只能以32bit地址访问寄存器
//这些位为只写形式,只能在字(word)--4byte、半字2byte 或字节模式下访问 
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

#define PHout(n)   BIT_ADDR(GPIOH_ODR_Addr,n)  //输出 
#define PHin(n)    BIT_ADDR(GPIOH_IDR_Addr,n)  //输入

#define PIout(n)   BIT_ADDR(GPIOI_ODR_Addr,n)  //输出 
#define PIin(n)    BIT_ADDR(GPIOI_IDR_Addr,n)  //输入


#endif

led.h部分:

#ifndef _LED_H_
#define _LED_H_

#include <stm32f4xx.h>
#include "sys.h"

void LED_Init(void);

#endif

led.c部分:

#include "led.h"

void LED_Init(void)
{
    
    
	GPIO_InitTypeDef aaa;
	
	//1、先开启对应用到的模块时钟节拍
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);//PE组时钟

	//2、可以初始化配置GPIO  F组的9号引脚
	aaa.GPIO_Pin   = GPIO_Pin_9 | GPIO_Pin_10;
	aaa.GPIO_Mode  = GPIO_Mode_OUT;//输出模式
	aaa.GPIO_Speed = GPIO_Fast_Speed;//快速  点灯和引脚速度无关
	aaa.GPIO_OType = GPIO_OType_PP;//推挽输出
	aaa.GPIO_PuPd  = GPIO_PuPd_UP;//内部上拉
	GPIO_Init(GPIOF,&aaa);
	
	
	aaa.GPIO_Pin   = GPIO_Pin_13 | GPIO_Pin_14;
	GPIO_Init(GPIOE,&aaa);
	
	//初始化完成  灭掉4盏灯
	PFout(9)  = 1;
	PFout(10) = 1;
	PEout(13) = 1;
	PEout(14) = 1;

}

key.h部分:

#ifndef _KEY_H_
#define _KEY_H_

#include <stm32f4xx.h>
#include "sys.h"

void KEY_Init(void);
void KEYEXTI_Init1(void);
#endif

key.c部分:

#include "key.h"
#include "delay.h"

void KEY_Init()
{
    
    
	GPIO_InitTypeDef KEY1,KEY2;
	//先开启对应用到的模块时钟节拍PA、PE
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
	
	//初始化KEY1
	KEY1.GPIO_Pin 		= GPIO_Pin_0 ;
	KEY1.GPIO_Mode 		= GPIO_Mode_IN;
	KEY1.GPIO_Speed 	= GPIO_Fast_Speed;
	KEY1.GPIO_OType 	= GPIO_OType_PP;
	KEY1.GPIO_PuPd  	= GPIO_PuPd_UP;
	GPIO_Init(GPIOA,&KEY1);
	
	//初始化KEY2、KEY3、KEY4
	KEY2.GPIO_Pin 	= GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
	KEY2.GPIO_Mode 	= GPIO_Mode_IN;
	KEY2.GPIO_Speed = GPIO_Fast_Speed;
	KEY2.GPIO_OType = GPIO_OType_PP;
	KEY2.GPIO_PuPd  = GPIO_PuPd_UP;
	GPIO_Init(GPIOE,&KEY2);
}


void KEYEXTI_Init1(void)
{
    
    
	EXTI_InitTypeDef Keyexti_Struct1;
	NVIC_InitTypeDef Keynvic_Struct1;
	
	//0、使能SYSCFG  EXTI相关时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
	
	//1、先初始化按键引脚
	KEY_Init();

	//2、将PA0、PE2、PE3、PE4引脚用作为外部中断引脚
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource2);
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource3);
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);
	
	//3、初始化EXTI(设置对应的中断线0,2,3,4)
	Keyexti_Struct1.EXTI_Line 		= EXTI_Line0 | EXTI_Line2 | EXTI_Line3 | EXTI_Line4;
	Keyexti_Struct1.EXTI_Mode 		= EXTI_Mode_Interrupt;		//中断模式
	Keyexti_Struct1.EXTI_Trigger 	= EXTI_Trigger_Falling;		//上升沿--按键松开触发
	Keyexti_Struct1.EXTI_LineCmd 	= ENABLE;					//外部中断开启使能设置
	EXTI_Init(&Keyexti_Struct1);
	
	//4、NVIC初始化
	Keynvic_Struct1.NVIC_IRQChannel = EXTI0_IRQn;				//中断线0对应的函数
	Keynvic_Struct1.NVIC_IRQChannelPreemptionPriority = 0;		//抢占优先级设置
	Keynvic_Struct1.NVIC_IRQChannelSubPriority = 0;				//响应优先级
	Keynvic_Struct1.NVIC_IRQChannelCmd   = ENABLE;				//开启NVIC管理
	NVIC_Init(&Keynvic_Struct1);
	
	Keynvic_Struct1.NVIC_IRQChannel = EXTI2_IRQn;				//中断线2对应的函数
	NVIC_Init(&Keynvic_Struct1);
	
	Keynvic_Struct1.NVIC_IRQChannel = EXTI3_IRQn;				//中断线3对应的函数
	NVIC_Init(&Keynvic_Struct1);
	
	Keynvic_Struct1.NVIC_IRQChannel = EXTI4_IRQn;				//中断线4对应的函数
	NVIC_Init(&Keynvic_Struct1);
}

void EXTI0_IRQHandler(void)
{
    
    
	delay(80);													//延时消抖
	PFout(9) = !PFout(9);										//LED1置位取反
	
	EXTI_ClearITPendingBit(EXTI_Line0);							//标志位清除
}

void EXTI2_IRQHandler(void)
{
    
    
	delay(80);													//延时消抖
	PFout(10) = !PFout(10);										//LED2置位取反
	
	EXTI_ClearITPendingBit(EXTI_Line2);							//标志位清除
}

void EXTI3_IRQHandler(void)
{
    
    	
	delay(80);													//延时消抖
	PEout(13) = !PEout(13);										//LED3置位取反
	
	EXTI_ClearITPendingBit(EXTI_Line3);							//标志位清除
}

void EXTI4_IRQHandler(void)
{
    
    
	delay(80);													//延时消抖
	PEout(14) = !PEout(14);										//LED4置位取反
	
	EXTI_ClearITPendingBit(EXTI_Line4);							//标志位清除
}

delay.h部分:

#ifndef _DELAY_H_
#define _DELAY_H_


void delay(int tim);

#endif

delay.c部分:

#include "delay.h"

void delay(int tim)
{
    
    
	int i;
	while(tim--)
	{
    
    
		for(i =38400;i>0;i--);
	}
}

----------------------------------------------------------分割线----------------------------------------------------------
设置外部中断,主要由下面函数设定:

void KEYEXTI_Init1(void)
{
    
    
	EXTI_InitTypeDef Keyexti_Struct1;
	NVIC_InitTypeDef Keynvic_Struct1;
	
	//0、使能SYSCFG  EXTI相关时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
	
	//1、先初始化按键引脚
	KEY_Init();

	//2、将PA0、PE2、PE3、PE4引脚用作为外部中断引脚
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource2);
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource3);
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);
	
	//3、初始化EXTI(设置对应的中断线0,2,3,4)
	Keyexti_Struct1.EXTI_Line 		= EXTI_Line0 | EXTI_Line2 | EXTI_Line3 | EXTI_Line4;
	Keyexti_Struct1.EXTI_Mode 		= EXTI_Mode_Interrupt;		//中断模式
	Keyexti_Struct1.EXTI_Trigger 	= EXTI_Trigger_Falling;		//上升沿--按键松开触发
	Keyexti_Struct1.EXTI_LineCmd 	= ENABLE;					//外部中断开启使能设置
	EXTI_Init(&Keyexti_Struct1);
	
	//4、NVIC初始化
	Keynvic_Struct1.NVIC_IRQChannel = EXTI0_IRQn;				//中断线0对应的函数
	Keynvic_Struct1.NVIC_IRQChannelPreemptionPriority = 0;		//抢占优先级设置
	Keynvic_Struct1.NVIC_IRQChannelSubPriority = 0;				//响应优先级
	Keynvic_Struct1.NVIC_IRQChannelCmd   = ENABLE;				//开启NVIC管理
	NVIC_Init(&Keynvic_Struct1);
	
	Keynvic_Struct1.NVIC_IRQChannel = EXTI2_IRQn;				//中断线2对应的函数
	NVIC_Init(&Keynvic_Struct1);
	
	Keynvic_Struct1.NVIC_IRQChannel = EXTI3_IRQn;				//中断线3对应的函数
	NVIC_Init(&Keynvic_Struct1);
	
	Keynvic_Struct1.NVIC_IRQChannel = EXTI4_IRQn;				//中断线4对应的函数
	NVIC_Init(&Keynvic_Struct1);
}
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
参数1:EXTI_InitStruct
typedef struct
{
    
    
  uint32_t EXTI_Line;  				// EXTI_Line0          
  EXTIMode_TypeDef EXTI_Mode;   	// EXTI_Mode_Interrupt   
  EXTITrigger_TypeDef EXTI_Trigger; 
  FunctionalState EXTI_LineCmd;  
}EXTI_InitTypeDef;
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
参数1:NVIC_InitStruct
typedef struct
{
    
    
	uint8_t NVIC_IRQChannel;   					//选择你要配置的中断的全局中断编号             
             									//EXTI_Line0   该编号去stm32f4xx.h头文件
           										//查找  Line176  EXTI0_IRQn   6号
	uint8_t NVIC_IRQChannelPreemptionPriority;  //抢占优先级(第一)0~15
  
	uint8_t NVIC_IRQChannelSubPriority;    		//响应优先级  (第二)0~15
                             					//数字越小 优先级越高
	规则1:当两个中断的抢占优先级不同时  响应优先级不考虑
	        	 ①如果两个中断同时出发   限制性高抢占的
	       		 ②如果高抢占先触发  低抢占后触发 低抢占只能排队等待高抢占的处理完  再做低抢占
	         	 ③当低抢占先触发 并先被CPU响应了 ,但高抢占后触发 ,就会出现高抢占把低抢占的处理过程抢占了(中断嵌套)
	规则2:当两个中断的抢占优先级相同    则去考虑响应优先级规则和上面一样 ,但是不能发生抢占
	规则3: 关于设置这两个优先级的问题:	
	           		关于某个中断的优先级设置 ,只给记录优先级的数值提供了4bit的记录空间----分配问题
		            404bit给抢占 0000 ~1111  0bit给响应
		            313bit给抢占    1bit 给响应
		            220~3:抢占    0~3:响应
		            13
		            04
	              这个分组设置最好在我们的工程一开始就设置好,而且只要设置一次即可!!

	FunctionalState NVIC_IRQChannelCmd;      	//ENABLE  DISABLE   

} NVIC_InitTypeDef;

猜你喜欢

转载自blog.csdn.net/weixin_43793181/article/details/109056730