《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
中断框图
Request sources : 中断请求源 发起中断信号
SUBSRCPND :子中断源挂起标记
SUBMASK:子中断屏蔽寄存器 相当于中断开关
SRCPND:中断源挂起标记 有些中断源单独占据一位,有些中断源需要经过子中断源,子中断源汇总后占据SRCPND上的一位。
MASK:中断屏蔽寄存器 功能类似于SUBMASK ,它是总的一个开关
Priority: 优先级配置 暂时不用 保持默认值,与兴趣可以自己研究
INTPND: 发生的中断 通过优先级筛选后的中断
MODE:中断模式,IRQ、FIQ
CPSR: bit7 IRQ 总开关
-----------------------------------------------------
下面介绍 外部中断需要配置的寄存器
GPFCON : IO工作模式配置为中断模式 P292
EXTINT0:配置外部中断的边沿触发方式 P301
EINTMASK:外部中断屏蔽,我们把对应的位设置为不屏蔽,打开中断源开关。属于上图中的SUBMASK 一级开关
INTMSK:中断屏蔽,我们把对应的位设置为不屏蔽,打开中断源开关。属于上图中的MASK 二级开关
CPSR: bit7 打开中断总开关
修改start.S文件 在中断向量表指定的位置添加IRQ入口及对应的现场保存,栈指针设置,中断函数调用,恢复现场,具体可以看我的上一遍文章。
通过这些配置,就可以触发中断。
然后在中断函数中通过分析对应的寄存器,来判断是什么触发的中断,然后再调用特定的函数。
EINTPEND: 外部中断挂起标志,写1清除中断;用于指示哪些外部引脚触发的中断(1个或多个),属于上图中的 SUBSRCPND
SRCPND:中断挂起标志,写1清除中断;用于指示哪些类型的中断(1个或多个);属于上图中的 SRCPND
INTPND:当前发生的中断,写1清除标志,经过中断优先级筛选出来的中断(唯一);用于判断当前发生的是什么中断
INTOFFSET:当前发生中断的偏移,是INTPND对应位的下标,用于快速判断中断源,不用手动清除,清除INTPND时,INTOFFSET也会清除;
下面是代码
----------------------------------------------------------------
start.S
.text
.global _start
_start:
/* 中断向量表 对应工作模式 参考 P82 */
b reset /* vector 0x00 : reset svc 复位 管理模式*/
ldr pc, _undefined_instruction /* vector 0x04 : und 无定义指令模式 */
ldr pc, _software_interrupt /* vector 0x08 : swi svc 软中断 管理模式 */
b reset /* vector 0x0C : abt(prefetch) 终止模式 预取终止 */
b reset /* vector 0x10 : abt(data) 终止模式 数据终止 */
b reset /* vector 0x14 : Reserved 保留 */
ldr pc, _irq /* vector 0x18 : IRQ 中断模式 */
b reset /* vector 0x1C : FIQ 快速中断模式 */
/* 将函数的入口放置在前面,如果不定义默认放置在.text 段的
* 末尾 ,start.S 汇编后超过4k 在NAND启动时CPU有可能无法
* 读取到入口
*/
_undefined_instruction:
.word undefined_instruction /* 保存入口地址。运行时地址,会跳转到SDRAM执行 */
_software_interrupt:
.word software_interrupt
_irq:
.word irq
undefined_instruction:
/* 执行到这里之前:
* 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
* 2. SPSR_und保存有被中断模式的CPSR
* 3. CPSR中的M4-M0被设置为11011, 进入到und模式
* 4. 跳到0x04的地方执行程序
*/
/* sp_und未设置, 先设置它 */
ldr sp, =0x34000000
/* 保存现场 */
/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
/* lr是异常处理完后的返回地址, 也要保存 */
stmdb sp!, {r0-r12, lr}
/* 处理und异常 */
mrs r0, cpsr // 获取程序状态寄存器 传递给 printState
bl printState
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
software_interrupt:
/* 执行到这里之前:
* 1. lr_swi保存有被中断模式中的下一条即将执行的指令的地址
* 2. SPSR_swi保存有被中断模式的CPSR
* 3. CPSR中的M4-M0被设置为10011, 进入到swi模式
* 4. 跳到0x08的地方执行程序
*/
/* sp_swi未设置, 先设置它 */
ldr sp, =0x33f00000
/* 保存现场 */
/* 在swi异常处理函数中有可能会修改r0-r12, 所以先保存 */
/* lr是异常处理完后的返回地址, 也要保存 */
stmdb sp!, {r0-r12, lr}
/* 处理swi异常 */
/* C函数调用时会用到 r4 - r11 所以在函数调用时会被保存,函
* 数返回时恢复,所以我们可以用r4来获取lr的值 参考第8章内
* 容 ATPCS 寄存器使用规则
*/
mov r4, lr // 将lr 返回地址 保存到 r4 sub r0, r4, #4 获取触发SWI异常的指令 ,根据指令获取传递进来的软中断号
mrs r0, cpsr // 获取程序状态寄存器 传递给 printState
bl printState
/* 获取软中断号 */
sub r0, r4, #4
bl getSWIVal
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
irq:
/* 执行到这里之前:
* 1. lr_irq保存有被中断模式中的下一条即将执行的指令的地址
* 2. SPSR_irq保存有被中断模式的CPSR
* 3. CPSR中的M4-M0被设置为10010, 进入到irq模式
* 4. 跳到0x18的地方执行程序
*/
/* sp_irq未设置, 先设置它 */
ldr sp, =0x33d00000
/* 保存现场 */
/* 在irq异常处理函数中有可能会修改r0-r12, 所以先保存 */
/* lr-4是异常处理完后的返回地址, 也要保存 参考 P80 */
sub lr, lr, #4
stmdb sp!, {r0-r12, lr}
mrs r0, cpsr // 获取程序状态寄存器 传递给 printState
bl printState
/* 处理irq异常 */
bl handle_irq_c
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr_irq的值恢复到cpsr里 */
/* 上面打印字符串长度有可能不是4字节对齐,所以这里要强
* 制4字节对齐,否则程序可能会出错
*/
.align 4
reset:
/* 关闭看门狗 */
ldr r0,=0x53000000
ldr r1,=0
str r1,[r0]
/* PLL配置---------------------------- */
/* 设置MPLL,FCLK:HCLK:PCLK=400M:100M:50M */
/* 设置lock time 详见手册P255
* LCOKTIME(0x4c000000)=0xFFFFFFFF
*/
ldr r0,=0x4c000000
ldr r1,=0xFFFFFFFF
str r1,[r0]
/* 设置HCLK、PCLK 相对于FCLK的分频系数 详见手册P260
* CLKDIVN(0x4c000014)=0x05 tFCLK:tHCLK:tPCLK=1:4:8
*/
ldr r0,=0x4c000014
ldr r1,=0x05
str r1,[r0]
/* 设置CPU工作于异步模式
* 详见手册P244
*/
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
/* 配置MPLLCON 来设置FCLK频率 详见手册P255~256
* 设置MPLLCON(0x4c000004)=(92<<12)|(1<<4)|(1<<0)
* m = MDIV+8 = 92+8 =100
* p = PDIV+2 =1+2 =3
* s = SDIV = 1
* FCLK = 2*m*Fin/(p*2^s)=2*100*12/(3*2^1)=400MHz
*/
ldr r0,=0x4c000004
ldr r1,=(92<<12)|(1<<4)|(1<<0)
str r1,[r0]
/* 一旦设置PLL,就会锁定 lock time 直到PLL输出稳定 */
/* PLL配置--END-------------------------- */
/* 设置内存:sp 栈 */
/* 分辨是nor/nand启动
* 写0到0地址,再读出来;如果读出来是0,表示
* 操作是内部RAM,是nand启动;否则就是nor启动
* 因为nor flash 不向内存那样能直接读写
*/
mov r1,#0
ldr r0,[r1] /* 备份0地址数据 */
str r1,[r1] /* 将0写入0地址 */
ldr r2,[r1] /* 读取0地址数据到r2 */
cmp r1,r2 /* 判断r1 r2是否相等 */
ldr sp,=0x40000000+4096 /* 假设nor启动 */
moveq sp,#4096 /* 如果r1==r2 nand启动 */
streq r0,[r1] /* 恢复原来的值 */
/* 整个程序重定位 */
bl sdram_init /* 初始化SDRAM 否则下面无法搬运数据 */
bl copy2sdram /* .text .rodata .data 段数据拷贝 */
bl clean_bss /* .bss 段清除 */
/* 复位之后, cpu处于svc模式
* 现在, 切换到usr模式
*/
mrs r0, cpsr /* 读出cpsr */
bic r0, r0, #0xf /* 修改M4-M0为0b10000, 进入usr模式 */
bic r0, r0, #(1<<7) /* 清除I位, 使能中断 开总中断 */
msr cpsr, r0
/* 设置 sp_usr */
ldr sp, =0x33e00000
bl uart0_init /* 故障代码中调用了串口打印,所以先初始化串口 */
und_code:
.word 0xeeadc0de /* 未定义指令 P86 指令集格式 原代码 0xdeadc0de 有误 参考:http://www.100ask.org/bbs/forum.php?mod=viewthread&tid=20299&highlight=und%D2%EC%B3%A3 */
/* 获取当前状态的CPSR */
mrs r0, cpsr // 获取程序状态寄存器 传递给 printState
bl printState // 打印当前状态信息
swi 0x123 /* 执行此命令, 触发SWI异常, 进入0x8执行 */
.align 4
/* 调用main函数 */
ldr pc, =main /* 绝对跳转, 跳到SDRAM 运行 */
/* 死循环 */
halt:
b halt
interrupt.c
#include "interrupt.h"
/* 初始化按键, 设为中断源 */
void key_eint_init(void)
{
/* 配置GPIO为中断引脚 */
GPFCON &= ~((3<<0) | (3<<4));
GPFCON |= ((2<<0) | (2<<4)); /* S2,S3被配置为中断引脚 */
GPGCON &= ~((3<<6) | (3<<22));
GPGCON |= ((2<<6) | (2<<22)); /* S4,S5被配置为中断引脚 */
/* 设置GPFCON让GPF4/5/6配置为输出引脚 */
GPFCON &= ~((3<<8) | (3<<10) | (3<<12));
GPFCON |= ((1<<8) | (1<<10) | (1<<12));
/* 设置中断触发方式: 双边沿触发 */
EXTINT0 |= (7<<0) | (7<<8); /* S2,S3 */
EXTINT1 |= (7<<12); /* S4 */
EXTINT2 |= (7<<12); /* S5 */
/* 设置EINTMASK使能eint11,19 */
EINTMASK &= ~((1<<11) | (1<<19));
INTMSK &= ~((1<<0) | (1<<2) | (1<<5));
}
/* 读EINTPEND分辨率哪个EINT产生(eint4~23)
* 清除中断时, 写EINTPEND的相应位
*/
void key_eint_irq(int irq)
{
unsigned int val = EINTPEND;
unsigned int val1 = GPFDAT;
unsigned int val2 = GPGDAT;
if (irq == 0) /* eint0 : s2 控制 D12 */
{
if (val1 & (1<<0)) /* s2 --> gpf6 */
{
/* 松开 */
GPFDAT |= (1<<6);
}
else
{
/* 按下 */
GPFDAT &= ~(1<<6);
}
}
else if (irq == 2) /* eint2 : s3 控制 D11 */
{
if (val1 & (1<<2)) /* s3 --> gpf5 */
{
/* 松开 */
GPFDAT |= (1<<5);
}
else
{
/* 按下 */
GPFDAT &= ~(1<<5);
}
}
else if (irq == 5) /* eint8_23, eint11--s4 控制 D10, eint19---s5 控制所有LED */
{
if (val & (1<<11)) /* eint11 */
{
if (val2 & (1<<3)) /* s4 --> gpf4 */
{
/* 松开 */
GPFDAT |= (1<<4);
}
else
{
/* 按下 */
GPFDAT &= ~(1<<4);
}
}
else if (val & (1<<19)) /* eint19 */
{
if (val2 & (1<<11))
{
/* 松开 */
/* 熄灭所有LED */
GPFDAT |= ((1<<4) | (1<<5) | (1<<6));
}
else
{
/* 按下: 点亮所有LED */
GPFDAT &= ~((1<<4) | (1<<5) | (1<<6));
}
}
}
EINTPEND = val;
}
void handle_irq_c(void)
{
/* 分辨中断源 */
int bit = INTOFFSET;
/* 调用对应的处理函数 */
if (bit == 0 || bit == 2 || bit == 5) /* eint0,2,eint8_23 */
{
key_eint_irq(bit); /* 处理中断, 清中断源EINTPEND */
}
/* 清中断 : 从源头开始清 */
SRCPND = (1<<bit);
INTPND = (1<<bit);
}
最近编辑记录 xinxiaoci (2018-05-17 09:20:01)
离线