GPIO中断编程
目的:实现KEY1中断,按下、松开按键,串口输出相应信息。
实现流程:
1.1 设置PA0为外部中断输入引脚
//key.c
#include "exti.h"
#include "nvic.h"
#include "string.h"
typedef struct
{
volatile unsigned int EVCR;
volatile unsigned int MAPR;
volatile unsigned int EXTICR[4];
volatile unsigned int RESERVED0;
volatile unsigned int MAPR2;
} AFIO_TypeDef;
void key_Init(void)
{
AFIO_TypeDef *afio = (AFIO_TypeDef *)0x40010000;
unsigned int *pReg;
unsigned int *pRegA;
/* 1.初始化GPIO引脚PA0,设置为输入引脚 */
/* enable GPIOA*/
pReg = (unsigned int *)(0x40021000 + 0x18);
*pReg |= (1 << 3) | (1 << 2);
/* 设置GPIOA0为输入 */
pRegA = (unsigned int *)(0x40010800 + 0x00);
*pRegA &= ~(3); /* mode0 = 0b00 */
*pRegA &= ~(3 << 2); /* cnf0 = 0b00 */
*pRegA |= (1 << 2);
/* 2.设置为EXTI0 */
afio->EXTICR[0] &= ~0xf; //设置PA0作为EXTI0
}
1.2 设置EXTI0
1.触发方式
设置双边沿触发,
设置RTSR寄存器中的bit0 为1:上升沿触发
设置FTSR寄存器中的bit0 为1:下降沿触发
设置IMR寄存器中的bit0 为1 ,不屏蔽中断,即使能中断
//exti.c
typedef struct
{
volatile unsigned int IMR;
volatile unsigned int EMR;
volatile unsigned int RTSR;
volatile unsigned int FTSR;
volatile unsigned int SWIER;
volatile unsigned int PR;
} EXTI_TypeDef;
void exti_Init(void)
{
EXTI_TypeDef *exti = (EXTI_TypeDef *)0x40010400;
/* 1.设置触发方式:双边沿触发 */
exti->RTSR |= (1<<0);
exti->FTSR |= (1<<0);
/* 2.使能中断:能够向NVIC发出中断 */
exti->IMR |= (1<<0);
}
1.3 设置NVIC,使能里面的EXTI0
//nvic.c
/* core_cm3.h */
typedef struct
{
volatile unsigned int ISER[8]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */
volatile unsigned int RESERVED0[24];
volatile unsigned int ICER[8]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */
volatile unsigned int RSERVED1[24];
volatile unsigned int ISPR[8]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */
volatile unsigned int RESERVED2[24];
volatile unsigned int ICPR[8]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */
volatile unsigned int RESERVED3[24];
volatile unsigned int IABR[8]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */
volatile unsigned int RESERVED4[56];
volatile unsigned char IP[240]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */
volatile unsigned int RESERVED5[644];
volatile unsigned int STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */
} NVIC_Type;
void nvic_Init(void)
{
NVIC_Type *nvic = (NVIC_Type *)0xE000E100;
/* 1.使能EXTI0中断 ,bit6对应的就是中断 DCD EXTI0_IRQHandler ; EXTI Line 0*/
nvic->ISER[0] |= (1<<6);
}
ISER0中的bit0对应异常向量表中的第16项(向量表从第0项开始)。根据中断向量表,我们需要设置 ISER[0] 里面的第6位
1.4 设置CPU里面的寄存器
最后,我们设置CPU里面的某个寄存器,让它可以去处理中断。
//cpu_int_enable();
我们在汇编中实现上述操作:
1.向量表中添加中断处理函数
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY
EXPORT __Vectors
IMPORT EXTI0_IRQHandler
__Vectors DCD (0x20000000+0x10000)
DCD Reset_Handler ; Reset Handler
DCD 0 ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD 0 ; MPU Fault Handler
DCD 0 ; Bus Fault Handler
DCD UsageFault_Handler_asm ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD 0 ; Debug Monitor Handler
DCD 0 ; Reserved
DCD 0 ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
; External Interrupts
DCD 0 ; Window Watchdog
DCD 0 ; PVD through EXTI Line detect
DCD 0 ; Tamper
DCD 0 ; RTC
DCD 0 ; Flash
DCD 0 ; RCC
DCD EXTI0_IRQHandler ; EXTI Line 0 ;在这里进行中断处理
AREA |.text|, CODE, READONLY
**2.在Reset_Handler
中使能CPU中断
;使能cpu中断
CPSIE I ; 清除PRIMASK,使能中断
通过操作PRIMASK 寄存器来实现:
把PRIMASK的bit0设置为1,就可以屏蔽所有优先级可配置的中断。
可以使用这些指令来设置它:
CPSIE I ; 清除PRIMASK,使能中断
CPSID I ; 设置PRIMASK,禁止中断
或者:
MOV R0, #1
MSR PRIMASK R0 ; 将1写入PRIMASK禁止所有中断
MOV R0, #0
MSR PRIMASK, R0 ; 将0写入PRIMASK使能所有中断
EXTI0_IRQHandler函数的实现
//在key.c中实现
void EXTI0_IRQHandler(void)
{
/* GPIOA input data register */
unsigned int * pRegA = (unsigned int *)(0x40010800 + 0x08);
if ((*pRegA & (1<<0)) == 0) /* key1被按下 */
{
puts("KEY1 pressed!\n\r");
}
else
{
puts("KEY1 released!\n\r");
}
/* 清除中断 */
exti_clear_int(0);
nvic_clear_int(6);
}
1.5 还要清除相应的寄存器
在设置完上面的一系列寄存器之后,如果没有清除中断,串口将会不断地执行打印操作,所以我们需要从源头开始沿着路线清除中断;即从外部中断控制器EXTI出开始清除
1.先清除EXTI
//exti.c
void exti_clear_int(int bit)
{
EXTI_TypeDef * exti = (EXTI_TypeDef *)0x40010400;
exti->PR |= (1<<bit);
}
//key.c
exti_clear_int(0);
2.再清除NVIC
//nvic.c
void nvic_clear_int(int bit)
{
NVIC_Type *nvic = (NVIC_Type *)0xE000E100;
if (bit <= 31)
nvic->ICPR[0] |= (1<<bit);
}
//key.c
nvic_clear_int(6);
main.c
int mymain()
{
key_Init();
exti_Init();
nvic_Init();
//cpu_int_enable();
}
程序执行结果:
扩展
报错: