#include <stm32mp1xx_gpio.h>
#include <stm32mp1xx_exti.h>
#include <stm32mp1xx_gic.h>
#include <stm32mp1xx_rcc.h>
void hal_gpio_init(void);
void hal_exti_init(void);
void hal_gicd_init(void);
void hal_gicc_init(void);
extern void printf(const char *fmt, ...);
void mydelay_ms(int ms)
{
int i, j;
while(ms--)
{
for (i = 0; i < 10000; i++)
for (j = 0; j < 10000; j++);
}
}
void do_irq(void)
{
unsigned int irq_num;
irq_num = GICC.IAR & (0x3FF);
switch(irq_num)
{
case 38:
// 中断处理程序,此处只打印一条语句
printf("put on \n");
EXTI.FPR1 |= (0x1 << 0); //下降边沿挂起寄存器 写0防止一直进入
GICD.ICPENDR[1] |= (0x1 << 6); // 清除GICD层中断 挂起清除
break;
case 39:
break;
}
// 清除GICC层中断 将38号写进去结束
GICC.EOIR = (GICC.EOIR & (~0x3FF)) | irq_num;
}
void hal_gpio_init(void)
{
// 使能GPIOA组的时钟使能, 在 RCC_MP_AHB4ENSETR
RCC.MP_AHB4ENSETR |= (0x1 << 0);
GPIOA->MODER &= (~0x3 << 0); //按键配置 输入功能
GPIOA->PUPDR &= ~(0x3 << 0); //无上、下拉
}
/*
* PA0引脚对应的外部中断事件是EXTI[0]
* 对应着Event Input 0
* 具体的外设和EXTI对应的关系在21.3章节
* 按键中断设置为下降沿触发中断事件的检测
* */
void hal_exti_init(void)
{
// 设置为下降沿触发选择寄存器
EXTI.FTSR1 |= (0x1 << 0);
// 设置外部中断选择寄存器 连接到PA0上
EXTI.EXTICR1 &= (~(0xff << 0));
// 设置EXTI CPU1唤醒中断屏蔽寄存器
EXTI.C1IMR1 |= (0x1 << 0);
// 设置EXTI CPU1唤醒事件屏蔽寄存器
// EXTI.C1EMR1 |= (0x1 << 0);
}
/*
* PA0引脚属于EXTI0
* EXTI0 对应的中断ID为 38
* 对应关系在芯片手册的21.2章节
* */
void hal_gicd_init(void)
{
GICD.ISENABLER[1] |= (0x1 << 6); //中断号为38 GICD中断使能
GICD.IPRIORITYR[9] &= ~(0x1f << 19);
GICD.IPRIORITYR[9] |= (0x10 << 19); //中断优先级,设置为16
GICD.ITARGETSR[9] |= (0x3 << 16); //选择处理器处理控制器转发的中断 但是为啥写0x3 //中断处理器目标寄存器
GICD.CTLR |= (0x1 << 0); //全局使能
//由于默认38号外部中断用group 0 处理所以这里不用设置组别
}
void hal_gicc_init(void)
{
GICC.CTLR |= (0x1 << 0); // 设置GICC层 中断全局使能寄存器 enable group 0 interrupts
GICC.PMR |= (0x1f << 3); // 设置GICC层 中断屏蔽寄存器
}
int main()
{
hal_gpio_init();
hal_gicc_init();
hal_gicd_init();
hal_exti_init();
while(1)
{
}
return 0;
}
汇编代码:
.text
.global _start
_start:
b reset
ldr pc,_undefined_instruction
ldr pc,_software_interrupt
ldr pc,_prefetch_abort
ldr pc,_data_abort
ldr pc,_not_used
ldr pc,_irq //跳转到_irq标志处
ldr pc,_fiq
_undefined_instruction: .word _undefined_instruction
_software_interrupt: .word _software_interrupt
_prefetch_abort: .word _prefetch_abort
_data_abort: .word _data_abort
_not_used: .word _not_used
_irq: .word irq_handler //跳转到irq_handler标志处
_fiq: .word _fiq
// mrc p15,0,r0,c1,c1,2
// movw r1, #0x3fff
// movt r1, #0x0004
// orr r0,r0,r1
// mcr p15,0,r0,c1,c1,2
reset:
ldr r0,= _start
mcr p15,0,r0,c12,c0,0 // 通过协处理器将中断向量表地址定向到最前面的_start,如果一有IRQ发生,就跳到_start寻找irq异常
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0 // Enable svc mode of cpu
init_stack:
ldr r0,stacktop /*get stack top pointer*/
/********svc mode stack********/
mov sp,r0
sub r0,#128*4 /*512 byte for irq mode of stack*/
/****irq mode stack**/
msr cpsr,#0xd2
mov sp,r0
sub r0,#128*4 /*512 byte for irq mode of stack*/
/***fiq mode stack***/
msr cpsr,#0xd1
mov sp,r0
sub r0,#0
/***abort mode stack***/
msr cpsr,#0xd7
mov sp,r0
sub r0,#0
/***undefine mode stack***/
msr cpsr,#0xdb
mov sp,r0
sub r0,#0
/*** sys mode and usr mode stack ***/
msr cpsr,#0x10
mov sp,r0 /*1024 byte for user mode of stack*/
bl main
.align 4
/**** swi_interrupt handler ****/
/**** irq_handler ****/
irq_handler:
sub lr,lr,#4 //因为三级流水线的原因,中断模式lr保存的地址的下下条指令的地址,如果我们从中断回来后想跳到下一条指令,那么这里需要人为的
//对lr进行-4操作
stmfd sp!,{r0-r12,lr}//对r0-r12寄存器为IRQ和user模式的共有寄存器,转到IRQ模式之后需要将她们压栈保护现场,对lr进行压栈防止在下面指令中lr被修改就回不去中断之前的地方了
.weak do_irq
bl do_irq //跳到中断处理函数中 这个处理函数是用c语言写的
ldmfd sp!,{r0-r12,pc}^ //1.将r0-r12出栈 2.将lr直接压到PC返回中断前的位置 3.^符号是将spsr寄存器中存好的中断之前的cpsr状态
//给到user模式中的cpsr寄存器
stacktop: .word stack+4*512
.data
stack: .space 4*512
loop:
b loop
.end