S5P4418 64个中断源,按键的GPIO口是GPIOB28-30,中断号为54;
Interrupt Controller相关寄存器
寄存器 | 功能 |
---|---|
VICIRQSTATUS | R,相关位为1表示该IRQ中断发生且未被屏蔽 |
VICFIQSTATUS | R,相关位为1表示该FIQ中断发生且未被屏蔽 |
VICRAWINTR | R,表示屏蔽之前的中断状态 |
VICINTSELECT | 选择中断请求类型 |
VICINTENABLE | 写1中断使能 |
VICINTENCLEAR | 写1中断失能 |
VICSOFTINT | 软中断失能,我理解为可以模拟按键 |
VICSOFTINTCLEAR | 软中断失能 |
VICPROTECTION | 使能或失能Protection mode |
VICSWPRIORITYMASK | 某些优先级的中断是否屏蔽 |
VICVECTPRIORITYDAISY | 设置中断优先级 |
VICVECTADDR0-31 | 表示各个中断源的ISR(Interrupt service routine) |
VICVECTPRIORITY0 | 各个中断源的优先级 |
VICADDRESS | 中断发生时,对应的ISR函数地址会复制到这个寄存器中 |
VICPERIPHID0-4 | 返回固定的值 |
VICPCELLID1-4 | 返回固定的值 |
GPIO Controller中相关寄存器
寄存器 | 功能 |
---|---|
GPIOxOUTENB | 写0设置输入模式 |
GPIOxDETMODE0-1 | 设置事件检测方式,也可以说中断触发方式 |
GPIOxDETMODEEX | 事件检测方式的扩展位 |
GPIOxINTENB | 中断使能 |
GPIOxDET | 检测模式下1表示事件发生,Set “1” to clear the relevant bit. GPIOx[31:0] is used as a Pending register when an interrupt occurs. |
GPIOxDETENB | detected mode使能与使能 |
中断配置与处理流程
CPSR
的I
位清0,允许IRQ中断- 设置检测方式,使能IO口检测,
GPIOxDETMODE,GPIOxDETENB
- 使能IO口中断,
GPIOxINTENB
- 选择IO口中断类型,
VICINTSELECT
- 设置中断处理函数地址,
VICVECTADDR
- 使能中断,
VICINTENABLE
当中断发生后,跳到异常向量表,CPSR
的I
位会被置1,硬件将优先级最高的中断ISR地址放到了VICADDRESS
中。
IRQ模式只能被FIQ模式打断,FIQ模式下谁也打不断。所以这款芯片没有中断嵌套的情况。
- 设置返回地址,保存现场
- 进入IRQ后取
VICADDRESS
的值,进入中断处理函数 - 通过
GPIOxDET
判断是哪个引脚发生了中断 - 退出中断函数时,将
GPIOxDET
的值写回GPIOxDET
以清除相应位,否则会重复进去ISR - 最后向
VICADDRESS
中写任意值,清除当前中断
使能GPIO外部中断
// 首先要修改CPSR寄存器,允许IRQ中断
void exti_init(){ // 配置中断
// K1 GPIOB28; K2 GPIOB29; K3 GPIOB30; 按下为低电平
GPIOBALTFN1 &= ~((3 << 24) | (3 << 26) | (3 << 28)); // 交替函数功能选择
GPIOBALTFN1 |= ((1 << 24) | (1 << 26) | (1 << 28));
GPIOBOUTENB &= ~((1 << 24) | (1 << 26) | (1 << 28)); // 输入模式
GPIOBDETMODEEX &= ~((1 << 30) | (1 << 29) | (1 << 28));
GPIOBDETMODE1 &= ~((3 << 28) | (3 << 26) | (3 << 24));
GPIOBDETMODE1 |= ((2 << 28) | (2 << 26) | (2 << 24)); // 下降沿触发
GPIOBDETENB |= ((1 << 30) | (1 << 29) | (1 << 28)); // 事件检测使能
GPIOBINTENB |= ((1 << 30) | (1 << 29) | (1 << 28)); // 中断使能
VICINTENCLEAR_CH1 = 0xFFFFFFFF;
VICINTSELECT_CH1 &= ~(1 << 22); // IRQ
VICVECTADDR_GPIOB = (u32)exti54_ISR;
VICINTENABLE_CH1 = (1 << 22);
// VICSOFTINT_CH1 = (1 << 22); // 生成软中断,相当于模拟按键
}
void exti54_ISR(){ // ISR
print_cpsr();
if(GPIOBDET & (1 << 28)){ // 按下k1点亮led1
led1_on();
}
if(GPIOBDET & (1 << 29)){ // 按下k2点亮led2
led2_on();
}
if(GPIOBDET & (1 << 30)){ // 按下k3熄灭led
led1_off();
led2_off();
}
GPIOBDET = GPIOBDET; // 相当于清除挂起寄存器
VICADDRESS_CH1 = 0x0; // 必须清除
}
// .starts 启动文件中保护和恢复现场的操作
_irq:
sub lr, lr, #4
stmdb sp!, {r0-r12, lr}
sub r0, lr, #4 // 传参
bl except_irq
ldmia sp!, {r0-r12, pc}^
void except_irq(u32 addr){
printf("[%x] Triggered IRQ exceptions\r\n", addr);
((void (*)(void))VICADDRESS_CH1)(); // 具体的中断函数
}
编译烧写上电,死活进不了IRQ,纠结了几天,了解到还有一个叫GIC
的东西。
帖子面向Cortex-A9的ucos移植 中有这样一句话
因此,ICCICR寄存器的地址为0XF0000000+0X100,往这个地址写0则绕过GIC,这样,多核处理器的中断就跟我们以前用的单核没有区别了。
不纠结那么多,先出效果再说;
#define GICC_CTLR __REG(0xF0000100 + 0x00)
void other_init(){
// cpsie i
__asm__(
"MRS r0, cpsr\n"
"BIC r0, r0, #(1 << 7)\n"
"MSR cpsr, r0\n"
); // 开启IRQ
GICC_CTLR = 0; // 绕过GIC
}
编译烧写上电,OK。成功进入IRQ模式;
下一贴纠结GIC;
顺便把串口也改成中断模式
// 配置中断
UARTIMSC |= (1 << 4);
VICINTENCLEAR_CH0 = (1 << 7);
VICINTSELECT_CH0 &= ~(1 << 7); // IRQ
VICVECTADDR_UART0 = (u32)uart0_ISR;
VICINTENABLE_CH0 = (1 << 7);