本文是讲解直接用寄存器的外部中断实验代码,所以关于概念,不熟悉的可以点击下方连接学习中断概念。
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文件里的代码简洁明了。