STM32中断式按键输入[直接用寄存器]

本文是讲解直接用寄存器的外部中断实验代码,所以关于概念,不熟悉的可以点击下方连接学习中断概念。
https://blog.csdn.net/gelad_w/article/details/115800545

本次实验目标:

  • 按下KEY2红灯LED0反转
  • 按下KEY1绿灯LED1反转
  • 按下KEY0蜂鸣器反转
  • 按下WK_UP LED0,LED1同时反转

一、配置中断步骤

1、开启复用时钟,设置映射关系(选择具体IO口引脚为中断输入源)
2、开放来自线x的中断请求
3、设置触发条件(上升沿、下降沿)
4、分配中断向量控制器(分组和设置优先级并使能)
5、编写中断服务函数(编写中断要处理的事情)

二、代码

先来看一下四个按键的中断配置

//------------------------外部中断设置--------------------------
void NVIC(unsigned int EXTI)
{
	RCC_APB2ENR|=1<<0;			//复用时钟
	switch(EXTI)
	{
		case	6:								//WK_UP
				AFIO_EXTICR1|=0x00000000;		//PA0映射
				EXTI_IMR|=1<<0;					//开放来自线0的中断
				EXTI_RTSR|=1<<0;				//上升沿触发
				AIRCR|=0X05FA0500; 				//写入钥匙和分组2
				NVIC_ISER0|=0x0000040;			//PA0中断使能
				NVIC_IP1|=0x00800000;			//PA0 主优先级2 次优先级0
				break;
		case	8:								//KEY2
				AFIO_EXTICR1|=0x00000400;		//PE2映射
				EXTI_IMR|=1<<2;					//开放来自线2的中断
				EXTI_FTSR|=1<<2;				//下降沿触发
				AIRCR|=0X05FA0500; 				//写入钥匙和分组2
				NVIC_ISER0|=0x00000100;			//PE2中断使能
				NVIC_IP2|=0x00000090;			//PE2 主优先级2 次优先级1
				break;
		case	9:								//KEY1
				AFIO_EXTICR1|=0x00004000;		//PE3映射
				EXTI_IMR|=1<<3;					//开放来自线3的中断
				EXTI_FTSR|=1<<3;				//下降沿触发
				AIRCR|=0X05FA0500; 				//写入钥匙和分组2
				NVIC_ISER0|=0x00000200;			//PE3中断使能
				NVIC_IP2|=0x0000A000;			//PE3 主优先级2 次优先级2
				break;
		case	10:								//KEY0
				AFIO_EXTICR2|=0x00000004;		//PE4映射
				EXTI_IMR|=1<<4;					//开放来自线4的中断
				EXTI_FTSR|=1<<4;				//下降沿触发
				AIRCR|=0X05FA0500; 				//写入钥匙和分组2
				NVIC_ISER0|=0x00000400;			//PE4中断使能
				NVIC_IP2|=0x00B00000;			//PE2 主优先级2 次优先级3
				break;
	}
}

接着看看中断服务程序,中断服务程序里就是我们进入中断要做的事情,比如我们设置EXTI0线上就是两个LED灯反转结果。

//---------------------中断服务程序-----------------------------
void EXTI0_IRQHandler(void)
{
		SysTick_ms(150);	//消抖
		PE5=~(PE5);			//LED1反转
		PB5=~(PB5);			//LED0反转
		EXTI_PR=1<<0;       //清除LINE0上的中断标志位  
}
void EXTI2_IRQHandler(void)
{
		SysTick_ms(150);	//消抖
		PB5=~(PB5);			//LED0反转
		EXTI_PR=1<<2;       //清除LINE2上的中断标志位 	
}
void EXTI3_IRQHandler(void)
{
		SysTick_ms(150);	//消抖
		PE5=~(PE5);			//LED1反转
		EXTI_PR=1<<3; 		//清除LINE3上的中断标志位  
}
void EXTI4_IRQHandler(void)
{
		SysTick_ms(150);	//消抖
		BEEP=~(BEEP);		//蜂鸣器反转
		EXTI_PR=1<<4;  		//清除LINE4上的中断标志位  
}

在这里插入图片描述
在这里要注意一下,我们的中断服务程序的函数名和启动代码中的要一致,否则中断无法进入服务程序,自然也就无法执行服务程序里命令。

三、主程序

//-----------------外部中断寄存器--------------------------
#define AFIO_EXTICR1	*((unsigned volatile int*)0x40010008)
#define AFIO_EXTICR2	*((unsigned volatile int*)0x4001000C)
#define EXTI_IMR		*((unsigned volatile int*)0x40010400)
#define EXTI_RTSR		*((unsigned volatile int*)0x40010408)
#define EXTI_FTSR		*((unsigned volatile int*)0x4001040C)
#define EXTI_PR			*((unsigned volatile int*)0x40010414)
//------------------内部中断寄存器-------------------------
#define NVIC_ISER0		*((unsigned volatile int*)0xE000E100)
#define NVIC_IP1		*((unsigned volatile int*)0xE000E404)
#define NVIC_IP2		*((unsigned volatile int*)0xE000E408)
#define AIRCR			*((unsigned volatile int*)0xE000ED00)
//--------------APB2使能时钟寄存器------------------------
#define RCC_APB2ENR		*((unsigned volatile int*)0x40021018)
//----------------GPIOA配置寄存器-------------------------
#define GPIOA_CRL		*((unsigned volatile int*)0x40010800)
#define GPIOA_IDR		*((unsigned volatile int*)0x40010808)
//----------------GPIOB配置寄存器 ------------------------
#define GPIOB_CRL		*((unsigned volatile int*)0x40010C00)
#define GPIOB_CRH		*((unsigned volatile int*)0x40010C04)
#define	GPIOB_ODR		*((unsigned volatile int*)0x40010C0C)
//----------------GPIOE配置寄存器 ------------------------
#define GPIOE_CRL		*((unsigned volatile int*)0x40011800)
#define	GPIOE_ODR		*((unsigned volatile int*)0x4001180C)
#define	GPIOE_IDR		*((unsigned volatile int*)0x40011808)	
//------------------RCC时钟寄存器-------------------------
#define RCC_CR			*((unsigned volatile int*)0x40021000)
#define RCC_CFGR		*((unsigned volatile int*)0x40021004)
//--------------FLASH闪存存储器接口-----------------------
#define FLASH_ACR		*((unsigned volatile int*)0x40022000)
//----------------------位带定义--------------------------
#define PB5				*((unsigned volatile int*)0x42218194)
#define PE5				*((unsigned volatile int*)0x42230194)
#define BEEP			*((unsigned volatile int*)0x422181A0)
//-----------------SysTick寄存器地址----------------------
#define SysTick_Base	0xE000E010
#define SysTick	((SysTick_Typedef*)SysTick_Base)
//-----------------SysTick寄存器定义----------------------
typedef struct
{
	volatile unsigned int CTRL;			//控制和状态寄存器
	volatile unsigned int RELOAD;		//重装载寄存器
	volatile unsigned int VAL;			//当前值寄存器
	volatile unsigned int CALIB;		//校准寄存器
}SysTick_Typedef;
//------------------系统时钟配置---------------------------
void System_clock(unsigned char PLL)
{
	unsigned int Clock_OK;
	RCC_CR|=1<<16;				//开启HSE高速外部时钟
	while(!(RCC_CR&(1<<17)));	//等待HSE开启成功
	RCC_CFGR|=4<<8;				//0x00000400 AHB不分频;APB2不分频;APB1二分频
	FLASH_ACR|=0x2;				//FLASH缓冲
	RCC_CFGR|=1<<16;			//PREDIV1输出作为PLL输入时钟
	PLL=PLL-2;					//选择PLL倍频2--9
	RCC_CFGR|=PLL<<18;			//PLL九倍频输出
	RCC_CR|=1<<24;				//PLL使能
	while(!(RCC_CR&(1<<25)));	//等待PLL使能成功
	RCC_CFGR|=2<<0;				//选择PLL为系统时钟
	do							//等待系统时钟设置成功
	{
		Clock_OK=RCC_CFGR&0x0c;
	}
	while(Clock_OK!=0x08);
}
//----------------------滴答定时器---------------------------
void SysTick_ms(unsigned int time)
{
	unsigned long num;
	SysTick->VAL=0;				//计数器清零
	SysTick->RELOAD=9000*time;	//重装载计数值
	SysTick->CTRL|=1<<0;		//定时器使能,打开定时器
	do
	{
		num=SysTick->CTRL;
	}
	while((num&0x01)&&!(num&(1<<16)));//等待计数器到0
	SysTick->CTRL&=~(1<<0);		//关闭计数器
	SysTick->VAL=0;				//计数器清零
}
//设置完成后系统时钟:SYSCLK=72MHZ;AHB:HCLK=72MHZ;APB2:PCLK2=72MHZ;APB1:PCLK1=36MHZ
//------------------------LED灯设置---------------------------
void LED(void)
{
	RCC_APB2ENR|=1<<3;			//APB2-GPIOB外设时钟使能	
	RCC_APB2ENR|=1<<6;			//APB2-GPIOE外设时钟使能
	
	GPIOB_CRL&=0xFF0FFFFF;		//设置位清零	
	GPIOB_CRL|=0x00200000;		//PB5推挽输出
	GPIOB_ODR|=1<<5;			//设置初始灯为灭
	
	GPIOE_CRL&=0xFF0FFFFF;		//设置位清零
	GPIOE_CRL|=0x00200000;   	//PE5推挽输出
	GPIOE_ODR|=1<<5;			//设置初始灯为灭	
	
	GPIOB_CRH&=0xFFFFFFF0;		//引脚初始化
	GPIOB_CRH|=0x00000002;		//PB8推挽
	GPIOB_ODR&=~(1<<8);			//蜂鸣器不工作
}
//-------------------------按键设置---------------------------
void key(void)
{
	RCC_APB2ENR|=1<<2;			//GPIOA
	RCC_APB2ENR|=1<<6;			//GPIOE
	
	GPIOA_CRL&=0xFFFFFFF0;		//引脚初始化
	GPIOA_CRL|=0x00000008;		//PA0下拉
	
	GPIOE_CRL&=0xFFF000FF;		//引脚初始化
	GPIOE_CRL|=0x00088800; 		//PE2、PE3、PE4上拉	   
	GPIOE_ODR|=7<<2;			//PE2上拉
}
//------------------------外部中断设置--------------------------
void NVIC(unsigned int EXTI)
{
	RCC_APB2ENR|=1<<0;			//复用时钟
	switch(EXTI)
	{
		case	6:								//WK_UP
				AFIO_EXTICR1|=0x00000000;		//PA0映射
				EXTI_IMR|=1<<0;					//开放来自线0的中断
				EXTI_RTSR|=1<<0;				//上升沿触发
				AIRCR|=0X05FA0500; 				//写入钥匙和分组2
				NVIC_ISER0|=0x0000040;			//PA0中断使能
				NVIC_IP1|=0x00800000;			//PA0 主优先级2 次优先级0
				break;
		case	8:								//KEY2
				AFIO_EXTICR1|=0x00000400;		//PE2映射
				EXTI_IMR|=1<<2;					//开放来自线2的中断
				EXTI_FTSR|=1<<2;				//下降沿触发
				AIRCR|=0X05FA0500; 				//写入钥匙和分组2
				NVIC_ISER0|=0x00000100;			//PE2中断使能
				NVIC_IP2|=0x00000090;			//PE2 主优先级2 次优先级1
				break;
		case	9:								//KEY1
				AFIO_EXTICR1|=0x00004000;		//PE3映射
				EXTI_IMR|=1<<3;					//开放来自线3的中断
				EXTI_FTSR|=1<<3;				//下降沿触发
				AIRCR|=0X05FA0500; 				//写入钥匙和分组2
				NVIC_ISER0|=0x00000200;			//PE3中断使能
				NVIC_IP2|=0x0000A000;			//PE3 主优先级2 次优先级2
				break;
		case	10:								//KEY0
				AFIO_EXTICR2|=0x00000004;		//PE4映射
				EXTI_IMR|=1<<4;					//开放来自线4的中断
				EXTI_FTSR|=1<<4;				//下降沿触发
				AIRCR|=0X05FA0500; 				//写入钥匙和分组2
				NVIC_ISER0|=0x00000400;			//PE4中断使能
				NVIC_IP2|=0x00B00000;			//PE2 主优先级2 次优先级3
				break;
	}
}
//---------------------中断服务程序-----------------------------
void EXTI0_IRQHandler(void)
{
		SysTick_ms(150);	//消抖
		PE5=~(PE5);			//LED1反转
		PB5=~(PB5);			//LED0反转
		EXTI_PR=1<<0;  		//清除LINE0上的中断标志位  
}
void EXTI2_IRQHandler(void)
{
		SysTick_ms(150);	//消抖
		PB5=~(PB5);			//LED0反转
		EXTI_PR=1<<2;  		//清除LINE2上的中断标志位 	
}
void EXTI3_IRQHandler(void)
{
		SysTick_ms(150);	//消抖
		PE5=~(PE5);			//LED1反转
		EXTI_PR=1<<3; 	    //清除LINE3上的中断标志位  
}
void EXTI4_IRQHandler(void)
{
		SysTick_ms(150);	//消抖
		BEEP=~(BEEP);		//蜂鸣器反转
		EXTI_PR=1<<4; 	    //清除LINE4上的中断标志位  
//-----------------------主函数-----------------------------------
int main(void)
{
	System_clock(9);	//系统时钟72MHZ
	LED();				//LED初始化 
	key();				//按键设置
	GPIOB_ODR&=~(1<<5);	//先让LED0亮起来,做指示灯
	NVIC(6);			//中断6,按键WK_UP在中断向量表里是第6位
	NVIC(8);			//中断8,按键KEY2在中断向量表里是第8位
	NVIC(9);			//中断9,按键KEY1在中断向量表里是第9位
	NVIC(10);			//中断10,按键KEY0在中断向量表里是第10位
	while(1)
	{
		 
	}
}

慢慢的可以发现,我们使用的寄存器越来越多了,前期为了大家能理解寄存器,所以把寄存器地址一个一个列了出来,后面会教大家用官方定义的寄存器头文件,就不用再一个一个找寄存器的地址了,只需要知道它怎么用就可以,然后来进行模块化的编写程序,让main.c文件里的代码简洁明了。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值