s5pv210——中斷實戰
阿新 • • 發佈:2019-01-10
一、s5pv210的中斷步驟
(1)建立異常向量表;
(2)中斷初始化;
(3)使能(如外部中斷,寫中斷處理函式);
(4)建立中斷號與中斷處理函式的聯絡,使能。
當中斷髮生時,中斷處理函式會自動處理中斷;
流程如下圖:
二、程式碼
1、建立異常向量表
/* * s5pv210 裸機 * * 異常向量表初始化 * */ #define VECTOR_TABLE_BASE 0xD0037400 #define Reset_offset 0x0 #define Undef_offset 0x4 #define SVC_offset 0x8 #define Prectch_offset 0xC #define Data_Abort_offset 0x10 #define IRQ_offset 0x18 #define FIQ_offset 0x1C #define _PFUNC_Reset (*(unsigned int*)(VECTOR_TABLE_BASE+Reset_offset)) #define _PFUNC_Undef (*(unsigned int*)(VECTOR_TABLE_BASE+Undef_offset)) #define _PFUNC_SVC (*(unsigned int*)(VECTOR_TABLE_BASE+SVC_offset)) #define _PFUNC_Prectch (*(unsigned int*)(VECTOR_TABLE_BASE+Prectch_offset)) #define _PFUNC_Data_Abort (*(unsigned int*)(VECTOR_TABLE_BASE+Data_Abort_offset)) #define _PFUNC_IRQ (*(unsigned int*)(VECTOR_TABLE_BASE+IRQ_offset)) #define _PFUNC_FIQ (*(unsigned int*)(VECTOR_TABLE_BASE+FIQ_offset)) extern void IRQ_handle(void); void Reset_handle(void) { } void Undef_handle(void) { } void SVC_handle(void) { } void Prectch_handle(void) { } void Data_Abort_handle(void) { } void vector_table_init(void) { _PFUNC_Reset = (unsigned int)Reset_handle; _PFUNC_Undef = (unsigned int)Undef_handle; _PFUNC_SVC = (unsigned int)SVC_handle; _PFUNC_Prectch = (unsigned int)Prectch_handle; _PFUNC_Data_Abort = (unsigned int)Data_Abort_handle; _PFUNC_IRQ = (unsigned int)IRQ_handle;//但凡是中斷,都會進入到此模式 _PFUNC_FIQ = (unsigned int)IRQ_handle;//FIQ、IRQ都是採用IRQ中斷 }
其中,IRQ_handle要寫在彙編IRQ_handle.S中(因為要設定棧,以及儲存現場 ),如下
#define IRQ_STACK 0xD0037F80 .global IRQ_handle IRQ_handle: //設定IRQ的棧 ldr sp, =IRQ_STACK //由於三級流水線的存在,pc為此時的程式語句+8,儲存的時候要把下一句儲存到lr中 sub lr, lr, #4 //儲存現場 stmd sp! {r0-r12, lr} //跳轉到中斷處理函式 bl isr_handler //恢復現場 ldmfd sp! {r0-r8, pc}^//這裡為什麼不是恢復r0~r12
ARM儲存中斷時為什麼使用 sub lr, lr, #4?
(1)首先要談流水線,在arm執行過程中一般分為取指,譯碼,執行階段;
- 假設當前第一條指令在執行階段,第二條指令在譯碼階段,第三條指令在取指階段;
- 若當前正在執行的指令地址為pc-8,那第二條就為pc-4,而pc指向取址。
(2)一般pc在發生中斷時lr儲存的是當前的pc值,這裡pc值是多少呢?
- 當發生中斷時,肯定儲存的pc是第三條指令,而我們從中斷返回肯定不是執行第三條指令,而是緊接著的第二條指令,所以應該儲存的 lr = pc - 4,(但是當執行到此位置時pc值已經改變,肯定不對,還好發生中斷時 mov lr,pc ,)所以這裡就可以直接使用 sub lr,lr,#4,即lr=pc-4。
2、中斷初始化
//清除4箇中斷處理函式
void clean_vicaddress(void)
{
_REG_VIC0ADDRESS = 0x0;
_REG_VIC1ADDRESS = 0x0;
_REG_VIC2ADDRESS = 0x0;
_REG_VIC3ADDRESS = 0x0;
}
void interrupt_init(void)
{
//第一步初始化中斷之前要關閉所有中斷
_REG_VIC0INTENCLEAR = 0xFFFFFFFF;
_REG_VIC1INTENCLEAR = 0xFFFFFFFF;
_REG_VIC2INTENCLEAR = 0xFFFFFFFF;
_REG_VIC3INTENCLEAR = 0xFFFFFFFF;
//第三步:設定中斷為IRQ中斷
_REG_VIC0INTSELECT = 0x0;
_REG_VIC1INTSELECT = 0x0;
_REG_VIC2INTSELECT = 0x0;
_REG_VIC3INTSELECT = 0x0;
//第三步:清中斷處理函式地址
clean_vicaddress();
}
void int_disable(unsigned int num)
{
if (num < 32) {
_REG_VIC0INTENCLEAR = (0x1<<num);
}
else if (num < 64) {
_REG_VIC1INTENCLEAR = (0x1<<(num-32));
}
else if (num < 96) {
_REG_VIC2INTENCLEAR = (0x1<<(num-64));
}
else if (num < 128) {
_REG_VIC3INTENCLEAR = (0x1<<(num-96));
}
else {
}
}
void int_enable(unsigned int num)
{
if (num < 32) {
_REG_VIC0INTENABLE = (0x1<<num);
}
else if (num < 64) {
_REG_VIC1INTENABLE = (0x1<<(num-32));
}
else if (num < 96) {
_REG_VIC2INTENABLE = (0x1<<(num-64));
}
else if (num < 128) {
_REG_VIC3INTENABLE = (0x1<<(num-96));
}
else {
_REG_VIC0INTENABLE = 0xFFFFFFFF;
_REG_VIC1INTENABLE = 0xFFFFFFFF;
_REG_VIC2INTENABLE = 0xFFFFFFFF;
_REG_VIC3INTENABLE = 0xFFFFFFFF;
}
}
void creat_israddr(unsigned int num, void (*PIRQ_handler)(void))
{
if (num < 32) {
//*( (void (*)(void))(VIC0VECTADDR + 4*num) )= PIRQ_handler;
*( (volatile unsigned long *)(VIC0VECTADDR + 4*(num-0)) ) = (unsigned)PIRQ_handler;
}
else if (num < 64) {
//(void (*)(void))(VIC1VECTADDR + 4*(num-32))= PIRQ_handler;
*( (volatile unsigned long *)(VIC1VECTADDR + 4*(num-32)) ) = (unsigned)PIRQ_handler;
}
else if (num < 96) {
//(void (*)(void))(VIC2VECTADDR + 4*(num-64))= PIRQ_handler;
*( (volatile unsigned long *)(VIC2VECTADDR + 4*(num-64)) ) = (unsigned)PIRQ_handler;
}
else {
//(void (*)(void))(VIC3VECTADDR + 4*(num-96))= PIRQ_handler;
*( (volatile unsigned long *)(VIC3VECTADDR + 4*(num-96)) ) = (unsigned)PIRQ_handler;
}
}
//判斷中斷在哪個address中
static int check_int_addr(void)
{
if (_REG_VIC0IRQSTATUS) {
return 0;
}
else if (_REG_VIC1IRQSTATUS) {
return 1;
}
else if (_REG_VIC2IRQSTATUS) {
return 2;
}
else if (_REG_VIC3IRQSTATUS) {
return 3;
}
else {
return -1;
}
}
void isr_handler(void)
{
void (*p_isr)(void) = NULL;
int i;
i = check_int_addr();
switch (i) {
case 0 :
p_isr = (void (*)(void))_REG_VIC0ADDRESS;
break;
case 1 :
p_isr = (void (*)(void))_REG_VIC1ADDRESS;
break;
case 2 :
p_isr = (void (*)(void))_REG_VIC2ADDRESS;
break;
case 3 :
p_isr = (void (*)(void))_REG_VIC2ADDRESS;
break;
default :
break;
}
p_isr();
}
3、使能外部中斷中斷處理函式
void int_led_blink(void)
{
//中斷處理函式
led_blink();
//清楚外部中斷掛起,注意寫1清掛起
clean_int_pend();
//清vicaddress
clean_vicaddress();
}
4、建立中斷號與中斷函式聯絡,使能中斷
#include "interrupt.h"
#include "stdio.h"
extern void led_blink(void);
extern void led1_on(void);
extern void vector_table_init(void);
extern void key_init(void);
extern void uart_init(void);
int main(void)
{
//按鍵初始化
key_inter_init();
//異常向量表初始化
vector_table_init();
//中斷初始化
interrupt_init();
//建立函式
creat_israddr(NUM_EINT2, int_led_blink);
creat_israddr(NUM_EINT3, int_led_blink);
creat_israddr(NUM_EINT16_31, int_led_blink);
//使能中斷
int_enable(NUM_EINT2);
int_enable(NUM_EINT3);
int_enable(NUM_EINT16_31);
while (1) {
printf("a");
}
}
小總結:
一、S5PV210中斷處理的程式設計實踐
2、中斷控制器初始化
- 主要工作有:第一階段繫結異常向量表到異常處理程式;禁止所有中斷源;選擇所有中斷型別為IRQ;清理VICnADDR暫存器為0.
3、中斷的使能與禁止
- 思路是先根據中斷號判斷這個中斷屬於VIC幾,然後在用中斷源減去這個VIC的偏移量,得到這個中斷號在本VIC中的偏移量,然後1<<x位,寫入相應的VIC的INTENABLE/INTENCLEAR暫存器即可。
4、繫結自己實現的isr到VICnVECTADDR
- 搞清楚2個暫存器的區別:VICnVECTADDR和VICnADDR
- VICVECTADDR暫存器一共有4×32個,每個中斷源都有一個VECTADDR暫存器,將自己為這個中斷源寫的isr地址丟到這個中斷源對應的VECTADDR暫存器中即可。
5、真正的中斷處理程式如何獲取isr
- 發生中斷時,硬體自動把相應中斷源的isr地址從VICnVECTADDR暫存器推到VICnADDR暫存器中,所以只需要到相應的VICnADDR中去拿出isr地址,呼叫執行即可。
- 第4步繫結isr地址到VICnVECTADDR,以及第5步中斷髮生時第二階段的第二階段如何獲取isr地址,這兩步是相關的。這兩個的結合技術,就是x210的硬體自動尋找isr的機制。
二、整個中斷的流程梳理:
整個中斷的工作分為2部分:
第一部分是我們為中斷響應而做的預備工作:
1. 初始化中斷控制器
2. 繫結寫好的isr到中斷控制器
3. 相應中斷的所有條件使能
第二部分是當硬體產生中斷後如何自動執行isr:
1. 第一步,經過異常向量表跳轉入IRQ/FIQ的入口
2. 第二步,做中斷現場保護(在start.S中),然後跳入isr_handler
3. 第三步,在isr_handler中先去搞清楚是哪個VIC中斷了,然後直接去這個VIC的ADDR暫存器中取isr來執行即可。
4. 第四步,isr執行完,中斷現場恢復,直接返回繼續做常規任務。