在阅读此文时候需要将上一篇博客完全搞懂。
ARM一共有7种模式
1. usr模式 1用户模式是给写应用程序的人使用的,防止他们破坏操作系统。
这六种模式称为特权模式(privileged mode),在这6种模式之下,可任意切换到其它模式(通过编程操作cpsr寄存器直接进入到其他模式)。 此外,在linux中,不会使用FIQ模式。
一共有两种state:
1. ARM state:ARM指令集,每个指令占据4字节 2. Thumb state:Thumb 指令集,每个指令占据2个字节 12在嵌入式系统中,Nor Flash或者NAND Flash很大,不用节省这一点空间,所以使用ARM state即可。
CPSR寄存器:
关于r0-r12的说明:
关于r13-r15的说明:
r13 : sp寄存器 r14:lr寄存器 r15:pc程序进入异常是硬件所操作的
下一条指令的地址保存在lr寄存器中,以便恢复现场:LR_异常 = 下一条指令地址把CPSR保存到spr_异常:SPSR_异常 = CPSR修改cpsr的模式为进入异常跳转到向量表(硬件完成)ARM异常向量表: 配置中断向量代码: 这里两种配置方式: <1>: <2>: 两种配置方式本质是一样一样的,具体实现原理可以参考上一篇博客
step1:设置新mode下的栈 只要这个栈地址不予其他模式下的栈地址重合就可以了
step2:保存现场 ① 是否需要对lr值进行处理 例如对于irq模式,需要先将lr-4,在存入栈中 具体可以参考下表:
② 保存r0-r12,lr
stmdb sp!,{r0-r12,lr} //先减后存 1step3:中断服务函数
bl interrupt_service_function 1step4:恢复现场
ldmia sp!,{r0-r12,pc}^ /* 先读后加,^ 表示恢复`CPSR`的值:CPSR = SPCR_异常 */ 12使用c语言实现print_un即可
void print_un(void) { putstr("\n\r"); putstr("\n\r"); putstr("\n\r"); putstr("\n\rException:undefined!\n\r"); }所有过程参考以上undefined异常处理过程,几乎一毛一样
在start.S中配置CPSR,清除其I位,打开总中断
配置引脚为中断引脚
使能外部中断屏蔽寄存器就可以了,即清除相对应的中断允许位
/* SRCPND 用来显示哪个中断产生了, 需要清除对应位 * bit0-eint0 * bit2-eint2 * bit5-eint8_23 *//* INTMSK 用来屏蔽中断, 1-masked
bit0-eint0bit2-eint2bit5-eint8_23 *//* INTPND 用来显示当前优先级最高的、正在发生的中断, 需要清除对应位
bit0-eint0bit2-eint2bit5-eint8_23 *//* INTOFFSET : 用来显示INTPND中哪一位被设置为1 / / 初始化中断控制器 */ void interrupt_init(void) { INTMSK &= ~((1<<0) | (1<<2) | (1<<5)|(1<<10));//bit10:定时器0的中断 }
分别清除:EINTPEND、SRCPND、INTPND
定时器时钟计算公式: timer clk = pclk / (precaler value + 1)/ (divider value) TCFG0 : 设置预分频系数 TCFG1 : 设置分频系数
TCFG0 : TCFG1:
定时器时钟框图:
注意:在手动加载初值之后需要在下一次写入之前将改位清除
对TCON相应位进行配置就可以了,先清除再配置为相应的位
定时器初始化函数:timer0_init.c
void time0_init(void) { //设置时钟 TCFG0 = 99; TCFG1 &= ~(15<<0); TCFG1 |= (3<<0); //设置初始 TCNTB0 = 31250;//Time0周期为1S //加载初值 TCON &= ~(1<<1); TCON |= (1<<1); //清除手动加载位 TCON &= ~(1<<1); //启动自动重加载、启动定时器 TCON &= ~((1<<0)|(1<<3)); TCON |= ((1<<0)|(1<<3));}
这个部分可以参考外部中断进行配置
</div> <link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-e9f16cbbc2.css" rel="stylesheet"> </div>