分为保存现场和恢复现场两部分
CPSR寄存器:当前程序状态寄存器
此步骤由电脑自动完成,分为四大步三小步
1:保存CPSR寄存器中的值到SPSR_
2:修改CPSR寄存器中的值
1> 修改CPSR寄存器的T位(状态位),修改为ARM状态
2> 根据需要,禁止相应的中断位 I位/F位(分别对应IRQ、FIQ)
3> 修改CPSR寄存器的模式位,切换到对应的异常模式
3:保存函数返回地址到LR_
4:PC指针指向异常向量表
T[5]:状态位
T=0:ARM状态,执行arm指令集
T=1:thumb状态,执行thumb指令集
M[4:0]:模式位
10000 User mode;
10001 FIQ mode;
10011 SVC mode;
10111 Abort mode;
11011 Undef mode;
11111 System mode;
10110 Monitor mode;
10010 IRQ mode;
1:把SPSR_
2:LR_
再去主板上找它们是什么引脚,以KEY1为例
KEY1为 PF9引脚,所以是GPIO控制器
至此我们可以确定,我们需要的是:
GIC控制器:通用的全局中断控制器
EXTI控制器:外部中断事件控制器
GPIO控制器:输入输出控制器,控制引脚
1.通过框图分析可知:RCC章节/GPIOF章节/EXTI章节/GIC章节
2.需要分析GPIOF章节:设置引脚为输入模式
3.需要分析EXTI章节:检测中断的触发方式
4.需要分析GIC章节:设置GPIOF引脚对应的中断号
寻找对应总线
略过前几篇文章做一次找一次的GPIOF,它在AHB4总线
EXTI也在AHB4总线(下面还能看到GPIOF)
GIC中断控制器在CA7总线
意味着,该控制器(GIC、EXTI)系统会自动使能,不需要我们通过RCC手动使能
查找RCC_MP_AHB4ENSETR
0x50000A28地址的第5位置1,设置GPIOF控制器使能
RCC_MP_AHB4ENSETR[5] = 1 -------->设置GPIOF控制器使能工作
这次GPIO章节比较简单,只需要设置为输入模式即可
GPIOF_MODER[19:18] = 00------>KEY1输入模式
由该图知,PF9引脚对应的控制器为:EXTI_EXTICR1.EXTI9
EXTI_EXTICRn寄存器每8位管理一个EXTI,一个寄存器32位,最多管理4个EXTI
因为KEY1引脚为PF9,所以,EXTICR寄存器编号为3(范围为8~11引脚)
分析可知:
key1------>PF9------>EXTI9--->9 / 4 = 2.....1--->EXTI_EXTICR3[15:8] = 0x05
由此(寻找过程)得出公式
EXTI编号 / 4 = 商 ...... 余数
商+1 :对应的哪一个寄存器
余数*8:对应寄存器8位中的最低位
key1------>PF9------>EXTI9--->对应事件9--->EXTI_FTSR1[9] = 0x1
key1------>PF9------>EXTI9--->对应事件9--->EXTI_IMR1[9] = 0x1--->中断不屏蔽
rc_w1: rc--->可读 w1--->写1清除中断挂起标志位
读0:中断没有发生
读1:中断发生
写0:表示没有清除中断挂起标志位
写1:表示清除中断挂起标志位
所以:
key1------>PF9------>EXTI9--->对应事件9--->EXTI_FPR1[9] = 0x1--->清除中断挂起标志位
GIC控制器分为GICC与GICD两个
Software generated interrupts (SGI):软件中断号(ID:0~15)
Private peripheral interrupts (PPI):私有的外设中断号(ID:16~31)
Shared peripheral interrupts (SPI):共享外设中断号(ID:32~287)
设置GICD层CPU0组使能
GICD_CTLR[0] = 1
GIC层一共管理288个中断号(16个SGI,16个PPI,256个SPI)
GICD_ISENABLERx每一位管理一个中断号,所以一个寄存器最多管理32个中断号
要想管理288个中断号,需要 288 / 32 = 9个这样的寄存器
key1------>PF9------>EXTI9--->对应事件9--->中断号99--->GICD_ISENABLER3[3] = 1
因此判断,计算公式为:
中断号 / 32 = 商 ...... 余数
商:中断号对应要操作的寄存器
余数:中断号对应操作寄存器的位数
公式: 中断号 / 4 = 商 ...... 余数
商:中断号对应要操作的寄存器
余数*8+3:中断号对应操作寄存器的位数
key1------>PF9------>EXTI9--->
对应事件9--->中断号99---> 99 / 4 = 24 .... 3--->GICD_IPRIORITYR24[31:27]
公式: 中断号 / 4 = 商 ...... 余数
商:中断号对应要操作的寄存器
余数*8:中断号对应操作寄存器的位数
key1------>PF9------>EXTI9--->
对应事件9--->中断号99---> 99 / 4 = 24 .... 3--->GGICD_ITARGETSR24[25:24] = 0bx1
0bx1----->分配给CPU0
0b1x----->分配给CPU1
0b11----->分配给CPU0和CPU1
公式: 中断号 / 32 = 商 ...... 余数
商:中断号对应要操作的寄存器
余数:中断号对应操作寄存器的位数
key1------>PF9------>EXTI9--->
对应事件9--->中断号99---> 99 / 32 = 3 .... 3--->GICD_ICPENDR3[3] = 1
CTRL[0] = 1 cpu组0使能
该寄存器的值随意设置,只需要比GICD层设计的中断优先级的值要大(GICD_IPRIORITYRx)
GICC_EOIR作用:清除中断号
GICC_EOIR[9:0]:清除按键的中断号
直接根据前文分析出来的内容,填写代码即可,头文件内采用现成的库、
//初始化EXTI层
void pf9_exti_init()
{
/***********RCC初始化**************/
//使能GPIOF
RCC->MP_AHB4ENSETR = (0x1 << 5);
/*****GPIO章节初始化*****/
//设置PF9引脚为输入模式
//GPIOF_MODER[19:18] = 0b00
GPIOF->MODER &= (~(0x3 << 18));
/*******EXTI章节初始化********/
//1.设置PF9引脚和EXTI9控制器进行链接
//EXIT_EXTICR3[15:8] = 0x05
EXTI->EXTICR3 &= (~(0xff << 8)); //清零
EXTI->EXTICR3 |= (0x05 << 8); //置1
//2.设置PF9引脚检测方式为下降沿
//EXTI_FTSR1[9] = 0x01
EXTI->FTSR1 |= (0x1 << 9);
//3.设置PF9引脚中断不屏蔽
//EXTI_IMR1[9] = 0x01
EXTI->C1IMR1 |= (0x01 << 9);
}
//初始化GICD层
void pf9_gicd_init()
{
//设置GICD层全局中断使能寄存器
GICD->CTRL |= (0X01 << 0);
//设置GICD层中断使能寄存器
//EXTI9-->99号-->GICD_ISENABLER[3][3] = 1
GICD->ISENABLER[3] |= (0x1 << 3);
//设置GICD层中断优先级寄存器
//99号中断-->GICD_IPRIORITYR24[31:27] = 10 //数字随意
GICD->IPRIORITYR[24] &= (~(0x1F << 27)); //清零
GICD->IPRIORITYR[24] |= (0x1 << 27);
//设置GICD层中断目标分配器
//99号中断 --> 99/4 = 24....3 --> GICD_ITARGETSR24[25:24] = 0bx1(x代表0、1任意值)
GICD->ITARGETSR[24] &= (~(0x3 << 24));
GICD->ITARGETSR[24] |= (0x1 << 24);
}
//初始化GICC
void pf9_gicc_init()
{
//设置GICC层全局中断使能寄存器
GICC->CTRL |= (0x1 << 0);
//设置GICC层中断优先级寄存器
GICC->PMR &= (~(0x1F << 3));
GICC->PMR |= (0xF << 3); //优先级设置为15
}
为了初始化方便,整合到一起
//包裹pf9
void PF9_init()
{
pf9_exti_init();
pf9_gicd_init();
pf9_gicc_init();
}
还要编写中断文件
#include "key.h"
extern void delay_ms(int ms);
extern void printf(const char *fmt, ...);
//unsigned int i = 0;
void do_irq(void)
{
unsigned int num = 0;
//获取中断信号
num = GICC->IAR;
switch(num)
{
/* 此处为省略的key2与key3的中断事件
case 97:
delay_ms(500); //防按键抖动
printf("key2######\n");
//清除EXTI中断挂起标志位
EXTI->FPR1 |= (0x1 << 7);
//清除GICD层中断挂起标志位
//key1-->PF9-->EXTI9-->事件9-->中断号99
//97 / 32 = 3...1
GICD->ICPENDR[3] |= (0x1 << 1);
break;
case 98:
delay_ms(500); //防按键抖动
printf("key3######\n");
//清除EXTI中断挂起标志位
EXTI->FPR1 |= (0x1 << 8);
//清除GICD层中断挂起标志位
//key1-->PF9-->EXTI9-->事件9-->中断号99
//98 / 32 = 3...2
GICD->ICPENDR[3] |= (0x1 << 2);
break;
*/
case 99:
delay_ms(500); //防按键抖动
printf("key1######\n");
//清除EXTI中断挂起标志位
EXTI->FPR1 |= (0x1 << 9);
//清除GICD层中断挂起标志位
//key1-->PF9-->EXTI9-->事件9-->中断号99
//99 / 32 = 3...3
GICD->ICPENDR[3] |= (0x1 << 3);
break;
}
//清除中断信号
GICC->EOIR &= (~(0x3FF << 0));
GICC->EOIR |= num;
}
#ifndef __KEY_H__
#define __KEY_H__
#include "stm32mp1xx_gpio.h"
#include "stm32mp1xx_exti.h"
#include "stm32mp1xx_rcc.h"
#include "stm32mp1xx_gic.h"
//初始化
/******KEY1----->PF9******/
//初始化EXTI层
void pf9_exti_init();
//初始化GICD层
void pf9_gicd_init();
//初始化GICC层
void pf9_gicc_init();
//包裹pf9
void PF9_init();
//此处本还有key2、key3的初始化,此处略
#endif
实验运行效果:
当按下开发板的对应按钮后,会在串口界面输出对应的按键号