CPU在工作的过程中,经常需要与外设进行交互,交互的方式包括”轮询方式”,”中断方式”。
轮询方式:
CPU不断地查询设备的状态。该方式实现比较简单,但CPU利用率很低,不适合多任务的系统。
中断方式:
CPU在告知硬件开始一项工作后,就去做别的事去了,当硬件完成了该项工作后,向CPU发送一个信号,告知CPU它已经完成了这项工作。
事件会送到中断控制器里面,中断控制器对其进行过滤,然后将其送到CPU核
在中断的生命周期中,中断源的作用是负责产生中断信号。
S3C2440支持60个中断源;
S3C6410支持64个中断源;
S5PV210支持93个中断源;
6410的中断:
6410一共有个两个中断组,为VIC0和VIC1,每个VIC支持32个中断源,一共64个中断源,如下图所示
从上面三幅图可以看到6410的64个中断源,同时可以看到有的中断源例如 INT_EINT0 包含了4个中断,主要是为了节约中断,就用这一个中断源,当 这四个中断某一个发生时,中断源INT_EINT0 就会产生中断,然后会查询相应的寄存器来判断是发生了那个中断。
如上图,非向量中断,当中断产生的时候,会跳转到一个统一的入口,然后在这个入口函数中判断是那个中断发生了,然后再去执行相应的中断处理程序处去
向量中断,就是当中断发生的时候,硬件自动判断到是那个中断产生,然后就会跳转到相应的中断寄存器里面,中断寄存器里面存放着这个中断的处理程序的地址,然后根据这个地址,就是我们的中断处理程序了。
从上面两幅图可以看到,ok6410的6个按键对应的GPIO分别为GPN0 ~ GPN5
根据上图,对按键1和按键6进行操作,需要将控制寄存器设为外部中断模式,因此要设置为0b10
#define GPNCON (volatile unsigned long*)0x7f008830
void button_init()
{
*(GPNCON) = 0b10 | (0b10<<10); //设置按键1与按键6 外部中断
}
#define EXT_INT_0_CON *((volatile unsigned int *)0x7f008900)
将EINT0 和 EINT5 设置为下降沿触发
EXT_INT_0_CON = (0b010)|(0b010<<8); /* 配置按键1和按键6为下降沿触发 */
#define EXT_INT_0_MASK *((volatile unsigned int *)0x7f008920)
EXT_INT_0_MASK = 0; /* 取消屏蔽外部中断 */
#define VIC0INTENABLE *((volatile unsigned int *)0x71200010)
根据上面两幅图,使能中断寄存器需要设置其值为1,同时要设置EINT0 EINT5,则需要使能中断源INT_EINT0 INT_EINT1
VIC0INTENABLE |= (0b1)|(0b10); /* 使能外部中断*/
__asm__(
"mrs r0,cpsr\n" /*读出值*/
"bic r0, r0, #0x80\n"
"msr cpsr_c, r0\n" /*写入*/
:
:
向量中断的方式,当中断产生的时候,并没有统一的入口,6410的64个中断有64个相对应的寄存器,每个寄存器里面存放着相对应的中断处理程序的地址,当中断产生的时候,硬件自己判断发生了那个中断,cpu就直接从发生中断对应的寄存器中取出处理程序的地址,从而处理中断。
#define EINT0_VECTADDR *((volatile unsigned int *)0x71200100)
#define EINT5_VECTADDR *((volatile unsigned int *)0x71200104)
EINT0_VECTADDR = (int)key1_handle;
EINT5_VECTADDR = (int)key6_handle;
使能向量中断,6410也可向下兼容2440非向量中断方式,因此需要设置其为向量中断方式
__asm__(
"mrc p15,0,r0,c1,c0,0\n" /*使能向量中断*/
"orr r0,r0,#(1<<24)\n"
"mcr p15,0,r0,c1,c0,0\n"
:
:
);
保存环境变量到栈上
__asm__( "sub lr, lr, #4\n" "stmfd sp!, {r0-r12, lr}\n" : : );
led_on(); //点亮led
此寄存器的功能是当发生中断后,就会产生计数,从0变为1
当处理完中断之后,需要清除掉这次中断,使其恢复到原始的状态。
#define EXT_INT_0_PEND *((volatile unsigned int *)0x7f008924)
EXT_INT_0_PEND = ~0x0;
当产生中断的时候,中断寄存器里面的地址也会放在VIC0ADDRESS 里面,因此在处理完中断的时候,也需要将其清零。
#define VIC0ADDRESS *((volatile unsigned int *)0x71200f00)
#define VIC1ADDRESS *((volatile unsigned int *)0x71300f00)
VIC0ADDRESS = 0;
VIC1ADDRESS = 0;
__asm__( "ldmfd sp!, {r0-r12, pc}^ \n" : : );
中断环境下的栈和SVC模式下的栈有所不同,因此需要设置中断模式下的栈
init_stack:
msr cpsr_c, #0xd2 @进入中断模式
ldr sp, =0x53000000 @初始化r13_irq
msr cpsr_c, #0xd3 @进入svc模式
ldr sp,=0x54000000 @初始化r13_svc 50000000 + 64 MB
mov pc,lr
贴上这次改动过的代码:
button.c
/********************************************
*file name: button.c
*author : stone
*date : 2016.7.3
*function : 按键中断进行相关的操作
*********************************************/
#define GPNCON (volatile unsigned long*)0x7f008830
void button_init()
{
*(GPNCON) = 0b10 | (0b10<<10); //设置按键1与按键6 为外部中断模式
}
interrupt.c
/******************************************** *file name: interrupt.c *author : stone *date : 2016.7.3 *function : 中断处理 *********************************************/
#define EXT_INT_0_CON *((volatile unsigned int *)0x7f008900) //外部中断配置寄存器
#define EXT_INT_0_MASK *((volatile unsigned int *)0x7f008920) //外部中断屏蔽寄存器
#define EXT_INT_0_PEND *((volatile unsigned int *)0x7f008924) //外部中断0悬挂寄存器
#define VIC0INTENABLE *((volatile unsigned int *)0x71200010) //中断使能寄存器
#define EINT0_VECTADDR *((volatile unsigned int *)0x71200100) //向量地址0寄存器
#define EINT5_VECTADDR *((volatile unsigned int *)0x71200104) //向量地址1寄存器
#define VIC0ADDRESS *((volatile unsigned int *)0x71200f00) //向量地址寄存器
#define VIC1ADDRESS *((volatile unsigned int *)0x71300f00)
//按键1处理程序
void key1_handle()
{
__asm__( /*3.1保存环境*/
"sub lr, lr, #4\n"
"stmfd sp!, {r0-r12, lr}\n"
:
:
);
//3.2中断处理程序
led_on();
//3.3清除中断
EXT_INT_0_PEND = ~0x0;
VIC0ADDRESS = 0;
VIC1ADDRESS = 0;
//3.4恢复环境
__asm__(
"ldmfd sp!, {r0-r12, pc}^ \n"
:
:
);
}
//按键6处理程序
void key6_handle()
{
__asm__(
"sub lr, lr, #4\n"
"stmfd sp!, {r0-r12, lr}\n"
:
:
);
led_off();
//清除中断
EXT_INT_0_PEND = ~0x0;
VIC0ADDRESS = 0;
VIC1ADDRESS = 0;
__asm__(
"ldmfd sp!, {r0-r12, pc}^ \n"
:
:
);
}
//中断初始化
void init_irq()
{
EXT_INT_0_CON = (0b010)|(0b010<<8); /* 2.1 配置按键1和按键6为下降沿触发 */
EXT_INT_0_MASK = 0; /* 2.2取消屏蔽外部中断 */
VIC0INTENABLE |= (0b1)|(0b10); /* 2.3使能外部中断*/
EINT0_VECTADDR = (int)key1_handle; /* 2.5用户按下key时,CPU就会自动的将VIC0VECTADDR0的值赋给VIC0ADDRESS并跳转到这个地址去执 */
EINT5_VECTADDR = (int)key6_handle;
__asm__(
"mrc p15,0,r0,c1,c0,0\n" /*2.5使能向量中断*/
"orr r0,r0,#(1<<24)\n"
"mcr p15,0,r0,c1,c0,0\n"
"mrs r0,cpsr\n" /*2.4设置cpsr状态寄存器*/
"bic r0, r0, #0x80\n"
"msr cpsr_c, r0\n"
:
:
);
}
main.c
/********************************************
*file name: main.c
*author : stone
*date : 2016.7.3
*function : 总程序
*********************************************/
int gboot_main()
{
/*mmu 初始化,暂时不用*/
#ifdef MMU_ON
mmu_init();
#endif
/*led初始化*/
led_init();
/*中断初始化*/
init_irq();
/*按键初始化*/
button_init();
//led_off();
while(1);
return 0;
}
mmu.c
/******************************************** *file name: mmu.c *author : stone *date : 2016.7.3 *function : mmu的初始化 *********************************************/
#define MMU_FULL_ACCESS (3 << 10) /* 访问权限 [11:10]*/
#define MMU_DOMAIN (0 << 5) /* 属于哪个域 [8:5]*/
#define MMU_SPECIAL (1 << 4) /* 必须是1 [4]*/
#define MMU_CACHEABLE (1 << 3) /* cacheable [3]*/
#define MMU_BUFFERABLE (1 << 2) /* bufferable [2]*/
#define MMU_SECTION (2) /* 表示这是段描述符 [1:0]*/
#define MMU_SECDESC (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | MMU_SECTION)
#define MMU_SECDESC_WB (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION)
void creat_page_table()
{
unsigned long *ttb = (unsigned long *)0x50000000; //表在内存的基地址处
unsigned long vaddr; //虚拟地址
unsigned long paddr; //物理地址
vaddr = 0xa0000000; //虚拟地址
paddr = 0x7F000000;
*(ttb + (vaddr >> 20)) = (paddr&0xfff00000) | MMU_SECDESC;
//*(ttb + (vaddr >> 20)) 为表项的位置
//(paddr&0xfff00000) 获取高12位数据
//MMU_SECDESC 访问led的gpio很简单,就不需要cache和buffer
//映射内存
vaddr = 0x50000000;
paddr = 0x50000000; //其虚拟地址和物理地址是一致的
while (vaddr < 0x54000000) //映射64mb
{
*(ttb + (vaddr >> 20)) = (paddr & 0xFFF00000) | MMU_SECDESC_WB;
vaddr += 0x100000;
paddr += 0x100000;
}
}
void mmu_enable()
{
__asm__(
/*设置TTB*/
"ldr r0, =0x50000000\n" /* 页表的基地址 */
"mcr p15, 0, r0, c2, c0, 0\n"
/*不进行权限检查*//*cp15 c3 domain 控制权限*/
"mvn r0, #0\n"
"mcr p15, 0, r0, c3, c0, 0\n"
/*使能MMU*/
"mrc p15, 0, r0, c1, c0, 0\n"
"orr r0, r0, #0x0001\n"
"mcr p15, 0, r0, c1, c0, 0\n"
:
:
);
}
void mmu_init()
{
create_page_table();
mmu_enable();
}
菜鸟一枚,如有错误,多多指教。。。