代码:https://github.com/jimingkang/news5pv210/tree/master/lesson/noOS/6.key_interrupt_stdio4
其他和前面uart修改类似。不过start.S有个重要修改,
bl main 后添加了:
mov r0, #0x53 @进入SVC模式,开cpu的CPSR的中断(把I位设为1)
msr CPSR_cxsf, r0
#define SVC_STACK 0xd0037d80
#define WTCON 0xE2700000
.global _start// 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
// 第1步:关看门狗(向WTCON的bit5写入0即可)
ldr r0, =WTCON
ldr r1, =0x0
str r1, [r0]
// 第2步:初始化时钟
bl clock_init
// 第3步:设置SVC栈
ldr sp, =SVC_STACK
// 第4步:开/关icache
mrc p15,0,r0,c1,c0,0; // 读出cp15的c1到r0中
//bic r0, r0, #(1<<12) // bit12 置0 关icache
orr r0, r0, #(1<<12) // bit12 置1 开icache
mcr p15,0,r0,c1,c0,0;
bl main
mov r0, #0x53 @进入SVC模式,开cpu的CPSR的中断(把I位设为1)
msr CPSR_cxsf, r0
// 从这里之后就可以开始调用C程序了
//bl led_blink // led_blink是C语言实现的一个函数
// 汇编最后的这个死循环不能丢
b .
—————————————
重要提示:
1.在int.h里面定义了srom的中断向量表(应该是中断处理函数才对,当中断发生时应该还是会先跳到irom的BL0,然后BL0找到0xD0037400的对应中断处理函数)位置,这是由srom的BL0规定的地址
#define exception_vector_table_base 0xD0037400
#define exception_irq (exception_vector_table_base + 0x18)
#define r_exception_irq ((volatile unsigned int )exception_irq)
2.在int.c中断初始化会修改对应的函数指针,从而指向自己定义的异常处理函数
// 主要功能:绑定第一阶段异常向量表;禁止所有中断;选择所有中断类型为IRQ;
// 清除VICnADDR为0
void system_init_exception(void)
{
// 第一阶段处理,绑定异常向量表
r_exception_reset = (unsigned int)reset_exception;
r_exception_undef = (unsigned int)undef_exception;
r_exception_sotf_int = (unsigned int)sotf_int_exception;
r_exception_prefetch = (unsigned int)prefetch_exception;
r_exception_data = (unsigned int)data_exception;
r_exception_irq = (unsigned int)IRQ_handle;
r_exception_fiq = (unsigned int)IRQ_handle;
// 初始化中断控制器的基本寄存器
intc_init();
}
// 初始化中断控制器
//(1)VIC0INTENCLEAR为禁止中断(属于中断控制器范围,不是cpu范围CPSR),与VIC1INTENABLE对应
//(2)VICnINTSELECT表示何种方式:上边缘,下边缘..触发
void intc_init(void)
{
// 禁止所有中断
// 为什么在中断初始化之初要禁止所有中断?
// 因为中断一旦打开,因为外部或者硬件自己的原因产生中断后一定就会寻找isr
// 而我们可能认为自己用不到这个中断就没有提供isr,这时它自动拿到的就是乱码
// 则程序很可能跑飞,所以不用的中断一定要关掉。
// 一般的做法是先全部关掉,然后再逐一打开自己感兴趣的中断。一旦打开就必须
// 给这个中断提供相应的isr并绑定好。
VIC0INTENCLEAR = 0xffffffff;
VIC1INTENCLEAR = 0xffffffff;
VIC2INTENCLEAR = 0xffffffff;
VIC3INTENCLEAR = 0xffffffff;
// 选择中断类型为IRQ
VIC0INTSELECT = 0x0;
VIC1INTSELECT = 0x0;
VIC2INTSELECT = 0x0;
VIC3INTSELECT = 0x0;
// 清VICxADDR
intc_clearvectaddr();
}
// 清除需要处理的中断的中断处理函数的地址
//(4)VICnADDR存储当前正在处理的中断的中断处理函数的地址
void intc_clearvectaddr(void)
{
// VICxADDR:当前正在处理的中断的中断处理函数的地址
VIC0ADDR = 0;
VIC1ADDR = 0;
VIC2ADDR = 0;
VIC3ADDR = 0;
}
3.绑定我们写的isr到VICnVECTADDR寄存器
//(5)VICnVECTADDR提前存储好我们要调用的中断处理函数,这s3c2440不同
// 绑定过之后我们就把isr地址交给硬件了,剩下的我们不用管了,硬件自己会处理
// 等发生相应中断的时候,我们直接到相应的VICnADDR中去取isr地址即可。
// 参数:intnum是int.h定义的物理中断号,handler是函数指针,就是我们写的isr
// VIC0VECTADDR定义为VIC0VECTADDR0寄存器的地址,就相当于是VIC0VECTADDR0~31这个 数组(这个数组就是一个函数指针数组)的首地址,然后具体计算每一个中断的时候,只需要首地址+偏移量即可。
void intc_setvectaddr(unsigned long intnum, void (*handler)(void))
{
//VIC0
if(intnum<32)
{
( (volatile unsigned long )(VIC0VECTADDR + 4*(intnum-0)) ) = (unsigned)handler;
}
…
return;
}
4.main函数中调用,初始化,绑定中断处理,始能:
key_init_interrupt();
intc_setvectaddr
intc_enable
int main(void)
{
uart_init();
//key_init();
key_init_interrupt();
// 如果程序中要使用中断,就要调用中断初始化来初步初始化中断控制器
system_init_exception();
printf("-------------key interrypt test--------------");
// 绑定isr到中断控制器硬件
intc_setvectaddr(KEY_EINT2, isr_eint2);
intc_setvectaddr(KEY_EINT3, isr_eint3);
intc_setvectaddr(KEY_EINT16_19, isr_eint16171819);
// 使能中断
intc_enable(KEY_EINT2);
intc_enable(KEY_EINT3);
intc_enable(KEY_EINT16_19);
// 在这里加个心跳
/*while (1)
{
printf("B ");
delay(10);
}*/
return 0;
}
5.真正的key的处理函数在key.c中,里面有第三层次(一为cpu,二为中断控制器组)的具体的关于中断的开关(比如外部中断允许),触发模式等设置,这是和具体硬件相关,与前面中断控制器组配合使用:
//———————–中断方式处理按键———————————–
// 以中断方式来处理按键的初始化
void key_init_interrupt(void)
{
// 1. 外部中断对应的GPIO模式设置
rGPH0CON |= 0xFF<<8; // GPH0_2 GPH0_3设置为外部中断模式
rGPH2CON |= 0xFFFF<<0; // GPH2_0123共4个引脚设置为外部中断模式
// 2. 中断触发模式设置
rEXT_INT_0_CON &= ~(0xFF<<8); // bit8~bit15全部清零
rEXT_INT_0_CON |= ((2<<8)|(2<<12)); // EXT_INT2和EXT_INT3设置为 下降沿触发
rEXT_INT_2_CON &= ~(0xFFFF<<0);
rEXT_INT_2_CON |= ((2<<0)|(2<<4)|(2<<8)|(2<<12));
// 3. 中断允许
rEXT_INT_0_MASK &= ~(3<<2); // 外部中断允许
rEXT_INT_2_MASK &= ~(0x0f<<0);
// 4. 清挂起,清除是写1,不是写0
rEXT_INT_0_PEND |= (3<<2);
rEXT_INT_2_PEND |= (0x0F<<0);
}
// EINT2通道对应的按键,就是GPH0_2引脚对应的按键,就是开发板上标了LEFT的那个按键
void isr_eint2(void)
{
// 真正的isr应该做2件事情。
// 第一,中断处理代码,就是真正干活的代码
printf(“isr_eint2_LEFT.\n”);
**// 第二,清除中断挂起
rEXT_INT_0_PEND |= (1<<2);
intc_clearvectaddr();**
}
6.几个重要寄存器:
1)VIC0INTENCLEAR为禁止中断(属于中断控制器范围,不是cpu范围CPSR),与VIC1INTENABLE对应
2)VICnINTSELECT表示何种方式:上边缘,下边缘..触发
3)VICnADDR存储当前正在处理的中断的中断处理函数的地址
4)VICnVECTADDR提前存储好我们要调用的中断处理函数
5)key相关寄存器
rGPH0CON
rEXT_INT_0_CON
rEXT_INT_0_MASK
rEXT_INT_0_PEND