当前位置:首页 > 嵌入式培训 > 嵌入式学习 > 讲师博文 > ARM异常处理设置

ARM异常处理设置 时间:2019-07-11      来源:沈阳中心,杨老师

Arm异常有下列这些

复位异常、数据异常、快速中断异常、外部中断异常、预取异常、软中断异常、未定义指令异常。

那么每种异常有一个相应的异常指令执行地址,即异常向量表

异常                   执行地址

复位异常             0x0000 0000

未定义指令异常       0x0000 0004

软中断异常           0x0000 0008

预取异常             0x0000 000C

数据异常             0x0000 0010

外部中断异常(IRQ)    0x0000 0018

快速中断异常(FIQ)    0x0000 001C

如果想理解异常处理过程,就要理解Arm启动代码, 下面以IRQ中断为例,讲解异常处理过程。

一 启动代码

1) Arm启动先执行一段启动代码(start.S),这段代码是用汇编写的

2) 然后,汇编代码再执行到c的main函数

二 start.S讲解

//////////////////////////////////////////////////////////////////

.text

.global _start                        @@@@  _start  是一个全局标号 (表示在其他函数可以调用_start)

_start:                                           @@@@  通常被指定为0x0000 0000地址 

b reset             @@@@  此段代码放在0x0000 0000地址处 ,命令功能是跳转到reset处去执行

ldr pc,_undefined_instruction

ldr pc,_software_interrupt

ldr pc,_prefetch_abort

ldr pc,_data_abort

ldr pc,_not_used

ldr pc,_irq           @@@@  将_irq地址处的指令地址给PC ,相当于一条跳转命令,这条指令回放在0x0000 0018处

ldr pc,_fiq

_undefined_instruction: .word  _undefined_instruction

_software_interrupt: .word  _software_interrupt

_prefetch_abort: .word  _prefetch_abort

_data_abort: .word  _data_abort

_not_used: .word  _not_used

_irq: .word  irq_handler    @@@@  .word  是一条伪指令,相当于定义一个4字节的变量_irq,值为irq_handler

     @@@@  等价于 int _irq = irq_handler

_fiq: .word  _fiq

      @@@@    伪指令:不是一条实际的汇编指令,变量定义,宏定义,数组定义

reset:

        @@@@  初始化 协处理器

ldr r0,=0x40008000

mcr p15,0,r0,c12,c0,0 @ Vector Base Address Register 

@@@@  设置cpsr的值,为0xd3

mrs   r0,cpsr         

bic r0,r0,#0x1f

orr r0,r0,#0xd3   @@@@   0xd3    1101 0011  禁止FIQ, IRQ, 处于ARM状态 ,模式为管理模式 SVC

msr cpsr,r0         @ Enable svc mode of cpu

init_stack:    @@@@初始化堆栈, svc, irq, fiq.... 每种模式的堆栈大小为512字节

ldr r0,stacktop         /*get stack top pointer*/

/********svc mode stack********/

mov sp,r0

sub r0,#128*4          /*512 byte  for irq mode of stack*/

/****irq mode stack**/

msr cpsr,#0xd2

mov sp,r0

sub r0,#128*4          /*512 byte  for irq mode of stack*/

/***fiq mode stack***/

msr cpsr,#0xd1

mov sp,r0

sub r0,#0

/***abort mode stack***/

msr cpsr,#0xd7

mov sp,r0

sub r0,#0

/***undefine mode stack***/

msr cpsr,#0xdb

mov sp,r0

sub r0,#0

/*** sys mode and usr mode stack ***/

msr cpsr,#0x10

mov sp,r0             /*1024 byte  for user mode of stack*/

@@@此处可以添加自己的初始化代码

b main     @@@@  跳转到main函数,不会回来了

.align 4

/****  swi_interrupt handler  ****/

//到中断时再讲

/****  irq_handler  ****/

irq_handler:    @@@@    一旦IRQ中断会跳转到这,  1) ARM 会将PC  ->  LR

sub  lr,lr,#4 @@@@ 指令流水线         取指   译码   执行   (正在执行的指令是  PC - 8, 下一条要执行的是PC - 4)

@@@@   pc-->0x1108     0x1108  0x1104  0x1100

stmfd sp!,{r0-r12,lr}    @@@@ 将r0-r12, lr寄存器入栈,,,,保护现场

bl do_irq           @@@@ 跳转到中断服务程序do_irq, 执行完再回来

ldmfd sp!,{r0-r12,pc}^   @@@@ 将栈中数据出栈,给r0-r12, lr寄存器,,,,恢复现场

stacktop:    .word stack+4*512 @@@@ .word 定义一个整型变量    int stacktop 

.data

stack: .space  4*512 @@@@.space   相当于定义一个数组   char stack[4 * 512];

/////////////////////////////////下面是C代码//////////////////////////////

void do_irq(void ) //C语言的中断服务程序代码,这段程序是程序员需要重点完成的

{

int irq_num;

irq_num = (CPU0.ICCIAR & 0x1FF);

switch (irq_num) {

case 58: //turn on LED2; turn off LED3

GPX2.GPX2DAT = 0x1 << 7;

GPX1.GPX1DAT &= ~0x1;

printf("IRQ interrupt !! turn on LED2; turn off LED3\n");

//Clear Pend

EXT_INT41_PEND |= 0x1 << 2;

ICDICPR.ICDICPR1 |= 0x1 << 26;

break;

}

// End of interrupt

CPU0.ICCEOIR = (CPU0.ICCEOIR & ~(0x1FF)) | irq_num;

}

int main(void)

{

//.....

}

三 中断处理

Arm, Cortex A9是4核处理器

1 读懂寄存器

Cortex A9采用的是ARM官方规定的中断处理机制

有两大类寄存器决定了中断工作状态

1) exynos 4412 特有的寄存器(在第26章)

2) Cortex A9 规定的工作寄存器(在第9章和第10章)


2 中断服务程序注意事项

1)  没有参数,没有返回值

2)  执行时间要短

delay(1000); //延时1秒

3)  中断服务程序中变量要少

4)  中断服务程序要考虑重入的问题

中断源:

外部中断EINT(按键...)

定时器TIMER2

串口接收数据

网卡接收数据

3 GIC控制器初始化, ARM提供(Generic Interrupt Controller)

////////4412 的中断控制器部分,采用了ARM公司的GIC中断控制器架构

GIC中断共152个, 分为三部分

SGI   16个    Software Generated Interrupt (SGI)

PPI   8个     Private Peripheral Interrupt (PPI)

SPI   128     Shared Peripheral Interrupt (SPI)   外部中断  定时器中断  串口中断

每个中断都被编了一个ID

可以根据GIC Interrupt Table 来查找

exynos4412 已经连接了两个外部中断(EINT)

 中断源     SPI Port No      ID

EINT[9]      25              57

练习:查找EINT9,EINT10,  TIMER0 和 WDT的中断源编号

 中断源     SPI Port No      ID

EINT[9]      25              57    K2

EINT[10]      26              58        K3

TIMER0      37              69

WDT          43              75

GIC 设置流程:

1 设置GIC中的某个SPI 中断允许还是禁止

////////////////

ICDISER(n)   ICDISER1,  ICDISER2, ICDISER3, ICDISER4

SPI中断共128个,分成了4组

ICDISER1  ---- > 控制 0-31 (第1组) (控制此中断编号是否可以产生中断 1 允许,0 禁止)

ICDISER2  ---- > 控制 32-63 (第2组)

ICDISER3  ---- > 控制 64-95 (第3组)

ICDISER4  ---- > 控制 96-127 (第4组)

EINT9  ---- 25号    EINT10 ---- 26号    

ICDISER1 (GIC的通道号0-31)    如果某一位为1 那么中断允许 

允许EINT9 产生中断  

ICDISER.ICDISER1 |= (0x1 << 25);            //中断使能寄存器

允许INT10 产生中断  

ICDISER.ICDISER1 |= (0x1 << 26);            //中断使能寄存器

练习:

允许WDT产生中断(43)

ICDISER.ICDISER2 |= 0x1 << 11;   (43 % 32)

2 设置 某个CPU是否允许响应中断(默认情况,4个CPU都不响应中断)

ICCICR:  对应于每个CPU,都有一个ICCICR,他的功能是   控制这个CPU的总的中断开关,1表示这个CPU响应中断, 0表示不响应中断

CPU0.ICCICR = 0x1;   (把它值1,表明CPU0响应中断) 

3 设置 某个CPU响应的中断的最高优先级

CPU0.ICCPMR = 0xFF;    //上面允许了中断,ICCPMR中断优先级小于设置值的中断允许进入  0xFF最低

4 设置 ICDDCR  GIC控制寄存器,控制GIC是否工作,也就是是否响应中断

ICDDCR 

ICDDCR = 1;   //总的中断允许位

中断产生时的中断标志位, 具体中断标志在哪参照ICDICPR

ICDICPR1   每组32位,对应相应的中断,如果产生了中断,那么ICDICPR相应位为0

ICDICPR2

ICDICPR3

ICDICPR4

5 设置某个中断是由哪个CPU来响应(815页)

ICDIPTR0(0-7, 8-15, 16-23, 24-31) ,  四组代表4个中断源,设置某个中断源由哪个CPU来处理

假设中断源0, 由CPU1来处理

ICDIPTR0 = 0x00 00 00 02

假设中断源1, 由CPU2来处理

ICDIPTR0 = 0x00 00 04 00 //0x04 ---> 0000 0100

ICDIPTR  有很多组:4个中断为1组(中断ID号最大159,   160 / 4  = 40,   ICDIPTR  有40个编号从0-39   )

如果设置EINT9由CPU0来响应(EINT9 是57号,所以在编号14的寄存器中(56, 57, 58, 59))

ICDIPTR.ICDIPTR14 = 0x00000100; //表示总中断号为   57的中断由CPU0来处理

如果设置EINT9由CPU1来响应

ICDIPTR.ICDIPTR14 = 0x00000200; //表示总中断号为   57的中断由CPU1来处理

如果设置EINT9由CPU2来响应

ICDIPTR.ICDIPTR14 = 0x00000400; //表示总中断号为   57的中断由CPU2来处理

如果设置EINT9由CPU3来响应

ICDIPTR.ICDIPTR14 = 0x00000800; //表示总中断号为   57的中断由CPU3来处理

如果设置EINT9由CPU4来响应

ICDIPTR.ICDIPTR14 = 0x00001000; //表示总中断号为   57的中断由CPU4来处理

//上面的寄存器设置我的中断由哪个CPU来处理

ICDIPTR 有很多(设置某个中断由哪个CPU来执行)

ICDIPTR1  ICDIPTR1 ICDIPTR2  ICDIPTR3 ICDIPTR4  ICDIPTR5........

有 160 / 4 个,  共40个, 每个寄存器控制4个中断

每个寄存器是32位的,8位为1组,     每组代表某种中断由哪个CPU来执行

0000 0001  表示由 CPU0来处理

0000 0010  表示由 CPU1来处理

0000 0100  表示由 CPU2来处理

0000 1000  表示由 CPU3来处理

0001 0000  表示由 CPU4来处理

0010 0000  表示由 CPU5来处理

0100 0000  表示由 CPU6来处理

1000 0000  表示由 CPU7来处理

一个32位寄存器可以分四组

ICDIPTR.ICDIPTR14 = 0x01010101;   //编号56,57,58,59四个中断,由CPU0来处理

14  是 总的中断编号 / 4

练习1:设置EINT10 由CPU1 响应(58)   ICDIPTR.ICDIPTR14 = 0x00020000;

练习2:设置WDT 由CPU0 响应(75)     ICDIPTR.ICDIPTR18 = 0x01000000;

如果设置EINT9 (57) 中断优先级10

ICDIPR14 = 0x00000A00;

定时中断

练习:

   完成定时功能, LED2 往复 亮,灭, 时间间隔两秒钟(精确延时),

如果实现WDT中断程序

1 工程中添加start.S文件(中断相应的过程, 是一段汇编代码)

2 修改Makefile(当前的可以不改)

3 start.lds (也可以不改)

4 start.c改成main.c

#include <exynos_4412.h>

/**********************************************************************

 * @brief IRQ Interrupt Service Routine program body

 * @param[in] None

 * @return None

 **********************************************************************/

int flag = 0;

void do_irq(void)

{

int irq_num;

irq_num = (CPU0.ICCIAR & 0x3FF); //获取中断编号  0000 0000 0011 1111 1111

printf("\n ******* WDT  interrupt !!********  irqno %d\n", irq_num); //75

if(flag == 0)

{

//点亮

flag = 1;

}

else

{

//点灭

flag = 0;

}

//换成点亮或点灭LED3    GPX1_0

WDT.WTCLRINT = 1; //清exynos4412中断标志位

// End of interrupt

CPU0.ICCEOIR = (CPU0.ICCEOIR & ~(0x3FF)) | irq_num; //向GIC控制器通知,此中断已经处理完

}

void wdt_init()

{

WDT.WTCNT = 6000; //initial value  ,, 延时大概有两秒

WDT.WTDAT = 6000; //initial value  ,, 延时大概有两秒

WDT.WTCON = 0xff<<8 | 1<<5 | 3<<3 | 1<<2 ;

}

void mydelay_ms(int time)

{

int i, j;

while(time--)

{

for (i = 0; i < 5; i++)

for (j = 0; j < 514; j++);

}

}

int main(void)

{

/*

* GIC interrupt controller:

* */

// Enables the corresponding interrupt SPI43, WDT

ICDISER.ICDISER2 |= 1<<11;  //ICDISER2:spi 32[bit0] ~ 63[bit31], 43 - 32 = [bit11]

CPU0.ICCICR |= 0x1; //Global enable for signaling of interrupts

CPU0.ICCPMR = 0xFF; //The priority mask level.Priority filter. threshold

ICDDCR = 1; //Bit1:  GIC monitors the peripheral interrupt signals and

// forwards pending interrupts to the CPU interfaces2

//ICDIPTR18:SPI40~SPI43; SPI43  interrupts are sent to processor 0

ICDIPTR.ICDIPTR18 = (ICDIPTR.ICDIPTR18 & ~(0xFF<<24)) | 1<<24;

wdt_init();

printf("\n****************WDT Interrupt test!!***************\n");

while(1)

{

mydelay_ms(200);

mydelay_ms(200);

printf("working...\n");

}

return 0;

}

上一篇:STM32之外设DMA

下一篇:单链表

热点文章推荐
华清学员就业榜单
高薪学员经验分享
热点新闻推荐
前台专线:010-82525158 企业培训洽谈专线:010-82525379 院校合作洽谈专线:010-82525379 Copyright © 2004-2022 北京华清远见科技集团有限公司 版权所有 ,京ICP备16055225号-5京公海网安备11010802025203号

回到顶部