1.s3c2440模式与状态
在阅读此文时候需要将上一篇博客完全搞懂。
1.1.模式
ARM一共有7种模式
1. usr模式
用户模式是给写应用程序的人使用的,防止他们破坏操作系统。
2. sys模式
3. undefined模式
4. svc管理模式
5. abort中止模式:① 指令预取中止;② 数据访问中止
6. IRQ中断模式
7. FIQ快中断模式
这六种模式称为特权模式(privileged mode),在这6种模式之下,可任意切换到其它模式(通过编程操作cpsr寄存器直接进入到其他模式)。
此外,在linux中,不会使用FIQ模式。
1.2.CPU State
一共有两种state:
1. ARM state:ARM指令集,每个指令占据4字节
2. Thumb state:Thumb 指令集,每个指令占据2个字节
在嵌入式系统中,Nor Flash或者NAND Flash很大,不用节省这一点空间,所以使用ARM state即可。
1.3寄存器
1.3.1CPSR寄存器(程序状态寄存器)
CPSR寄存器:
T位:state bits(ARM state、Thumb state)
F位:FIQ disable,为1时所用的FIQ禁止
I位:IRQ disable,为1时,禁止所有IRQ
1.3.2.其余寄存器
关于r0-r12的说明:
关于r13-r15的说明:
r13 : sp寄存器
r14:lr寄存器
r15:pc
2.异常处理流程
2.1.进入异常操作
程序进入异常是硬件所操作的
- 下一条指令的地址保存在lr寄存器中,以便恢复现场:
LR_异常 = 下一条指令地址
- 把CPSR保存到spr_异常:
SPSR_异常 = CPSR
- 修改cpsr的模式为进入异常
- 跳转到向量表(硬件完成)
2.2.退出异常操作
- lr寄存器减去某个值赋值给
pc
:pc = LR_异常 - offset
- 恢复
CPSR
的值:CPSR = SPCR_异常
- 清中断(其余异常不用管)
3.undefined异常处理实例
1. 在start.S中配置异常向量表
2. 配置响应函数
3. 配置异常服务函数
①. 配置异常向量表
ARM异常向量表:
配置中断向量代码:
这里两种配置方式:
<1>:
<2>:
两种配置方式本质是一样一样的,具体实现原理可以参考上一篇博客
②. 配置响应函数
step1:设置新mode下的栈
只要这个栈地址不予其他模式下的栈地址重合就可以了
step2:保存现场
① 是否需要对lr值进行处理
例如对于irq模式,需要先将lr-4,在存入栈中
具体可以参考下表:
② 保存r0-r12,lr
stmdb sp!,{r0-r12,lr} //先减后存
step3:中断服务函数
bl interrupt_service_function
step4:恢复现场
ldmia sp!,{r0-r12,pc}^
/* 先读后加,^ 表示恢复`CPSR`的值:CPSR = SPCR_异常 */
③. 配置异常服务函数
使用c语言实现print_un即可
void print_un(void)
{
putstr("\n\r");
putstr("\n\r");
putstr("\n\r");
putstr("\n\rException:undefined!\n\r");
}
4.SWI(software interrupt)异常处理实例
1. 在start.S中配置异常向量表
2. 配置响应函数
3. 配置异常服务函数
①. 配置异常向量表
②. 配置响应函数
③. 配置异常服务函数
所有过程参考以上undefined异常处理过程,几乎一毛一样
5.IRQ(外部中断)处理实例
5.1. 初始化设置
① 设置CPU,CPSR的I位,它是中断的总开关
在start.S中配置CPSR,清除其I位,打开总中断
② 设置中断源,让它能够发出中断信号
配置引脚为中断引脚
③ 设置外部中断源的触发方式
④ 设置中断控制寄存器,让它能发出中断给CPU
使能外部中断屏蔽寄存器就可以了,即清除相对应的中断允许位
/* SRCPND 用来显示哪个中断产生了, 需要清除对应位
* bit0-eint0
* bit2-eint2
* bit5-eint8_23
*/
/* INTMSK 用来屏蔽中断, 1-masked
* bit0-eint0
* bit2-eint2
* bit5-eint8_23
*/
/* INTPND 用来显示当前优先级最高的、正在发生的中断, 需要清除对应位
* bit0-eint0
* bit2-eint2
* bit5-eint8_23
*/
/* INTOFFSET : 用来显示INTPND中哪一位被设置为1
*/
/* 初始化中断控制器 */
void interrupt_init(void)
{
INTMSK &= ~((1<<0) | (1<<2) | (1<<5)|(1<<10));//bit10:定时器0的中断
}
5.2. 响应中断,分辨中断源,对终端进行处理
5.3. 处理完要清除中断
分别清除:EINTPEND、SRCPND、INTPND
6.定时器中断
① 配置时钟
定时器时钟计算公式:
timer clk = pclk / (precaler value + 1)/ (divider value)
TCFG0 : 设置预分频系数
TCFG1 : 设置分频系数
TCFG0 :
TCFG1:
定时器时钟框图:
② 设置定时器初值(TCNTBn、TCMPBn)
③ 加载初始值(配置TCON)
注意:在手动加载初值之后需要在下一次写入之前将改位清除
④ 设置为自动加载并启动
对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));
}
⑤ 配置定时器中断
- 开启总中断
- 配置中断向量表以及响应函数
- 配置中断服务函数
这个部分可以参考外部中断进行配置