14.1.6  中断处理实现

一、中断介绍
中断的概念:外设和CPU进行数据通信
Chapter fourteen ARM概述(3)

1)通信的两种方式

a.查询方式:CPU循环查询(轮询)
优点:实现简单
缺点:效率低

b.中断方式:外设主动通知CPU机制
优点:效率高
缺点:实现复杂


2)CPU接受中断后的流程
a.保存现场 stmfd
b.查询中断控制器 check interruptor
c.执行相关中断处理函数 excute function 
d.清理中断 clear interruption
e.恢复现场 ldmfd


二、中断控制器工作流程讲解
1)S5PC100_UM_ERV1.02.pdf中解释到,SMART210支持128个中断。
2)中断控制器内部工作流程图(VIC具体流程图)
Chapter fourteen ARM概述(3)

3)流程图解释

a。按键来一个中断通知

b。通知消息首先到达GPIO,我们需要设置相应的寄存器为中断的功能,至于如何设置,请看下面的代码展示。
GPIO有如下几个寄存器:
CON(管脚功能设置寄存器,设置为输出还是输入模式)
DAT(设置当时的输出状态的寄存器)
INTC(中断配置寄存器,设置管脚是高电平还是低电平触发)
PND(记录中断)
MAS(掩码寄存器,也就是用于设置开关的寄存器

c。消息通过GPIO后来到了VIC,我们需要提前设置VIC里的执行函数以及其他参数。

d。中断消息已经通知CPU,CPU再反过来执行VIC里的执行函数,完成相应的中断处理。


三、外部中断实现

1)Makefile和ARM概述(2)中的基本一致,这里不再赘述。


2)start.s:

.global _start
_start:
@这里开始地址为0x41000000

b reset
b undef
b swi
b abt_pre
b abt_dat
b reserved
b irq
b fiq

reset:
@CPU启动进入到reset 

@重新映射异常向量表到0x4000 8000
ldr r0,=0x40008000
mcr p15, 0, r0, c12, c0, 0

@打开CPU的总开关
mrs ro,cpsr
bic r0,r0,#(0x1<<7)
msr cpsr,r0 


@跳转到main函数
bl main

loop:
b loop

undef:
swi:
abt_pre:
abt_dat:
reserved:
irq:
@产生中断
@we need to init the stack pointer,because the mode has changed
ldr sp,=0x42000000

stmfd sp!, {r0-r12, lr}

bl irq_handler
//执行中断处理函数

ldmfd sp!,{r0-r12, lr}
subs pc, lr, #4
//为了防止语句漏执行,我们需要将lr-4后赋给pc

fiq:


3)mian函数:

#define VIC0INTSELECT (*(volatile unsigned int *)0xF200000C)
#define VIC0INTENABLE (*(volatile unsigned int *)0xF2000010)
#define VIC0VECTADDR0 (*(volatile unsigned int *)0xF2000100)
#define GPH0CON (*(volatile unsigned int *)0xE0200C00)
#define EXT_INT_0_CON (*(volatile unsigned int *)0xE0200E00)
#define EXT_INT_0_MASK (*(volatile unsigned int *)0xE0200F00)
#define EXT_INT_0_PEND (*(volatile unsigned int *)0xE0200F40)
#define VICADDRESS (*(volatile unsigned int *)0xF2000F00)

int key1_handler(){

//利用函数指针调用printf,因为裸板没有该函数
int (*printf)(char *format,...);
printf=(void *)0x3ff13e54;

printf("hello key0");

EXT_INT_0_PEND = 0x1;//清零gpio的中断记录
}

int main(){

//初始化中断控制器
VIC0INTSELECT= 0;

//注册中断控制函数
VIC0VECTADDR0=(unsigned int)key1_handler();

//开启0号中断,相当于向第一个地址里放一个函数。
VIC0INTSELECT |=0x1;

//初始化Key0,因为这里中断检测的是 GPH0_0管脚,也就是key0
GPH0CON |=  0xf;
//把gph0_0设置成中断功能

EXT_INT_0_CON &= ~0xf;
EXT_INT_0_CON |= 0x3;
//设置为上升沿触发

//设置掩码,将掩码开关打开
EXT_INT_0_MASK &= ~0x1;

while(1){
;
}
//while死循环等待key按下发生中断。一旦发生中断,CPU就会切换到异常向量表

return 0;
}

void irq_handler(){

void (*handler)();

handler=( void *)VIC0VECTADDR0;

handler();

VIC0ADDRESS = 0;//清零addr0中的中断记录


}//cpu发生中断后,进入到本函数,然后到指定的ADDR0中取得相应的处理函数


=======================================================================================


四、中断程序优化
这里程序优化主要是将main函数中的开启的中断号进行了改进和完善,并与irq_handler封装成了irq.c。这时我们需要修改相应的Makefile。

1)中断处理函数(irq.c)


#define VIC0INTSELECT(*(volatile unsigned int*)0xF200000C)
#define VIC1INTSELECT(*(volatile unsigned int*)0xF210000C)
#define VIC2INTSELECT(*(volatile unsigned int*)0xF220000C)
#define VIC3INTSELECT(*(volatile unsigned int*)0xF230000C) 


#define VIC0VECTADDR  (( volatile unsigned int *)0xF2000100)
#define VIC1VECTADDR  (( volatile unsigned int *)0xF2100100)
#define VIC2VECTADDR  (( volatile unsigned int *)0xF2200100)
#define VIC3VECTADDR  (( volatile unsigned int *)0xF2300100)


#define VIC0INTENABLE (*(volatile unsigned int *)0xF2000010)
#define VIC1INTENABLE (*(volatile unsigned int *)0xF2100010)
#define VIC2INTENABLE (*(volatile unsigned int *)0xF2200010)
#define VIC3INTENABLE (*(volatile unsigned int *)0xF2300010)


#define VIC0ADDRESS   (*(volatile unsigned int *)0xF2000F00)
#define VIC1ADDRESS   (*(volatile unsigned int *)0xF2100F00)
#define VIC2ADDRESS   (*(volatile unsigned int *)0xF2200F00)
#define VIC3ADDRESS   (*(volatile unsigned int *)0xF2300F00)


int request_irq(int irq,void (*handler)())
{
//初始化中断控制器
//选择irq中断

VIC0INTSELECT = 0;
VIC1INTSELECT = 0;
VIC2INTSELECT = 0;
VIC3INTSELECT = 0;


if(irq>= 0 && irq<= 31)
VIC0VECTADDR[irq]=(unsigned int)handler;

else if(irq>= 32 && irq<= 63){
irq-=32;
VIC1VECTADDR[irq]=(unsigned int)handler;
}


else if(irq>= 64 && irq<= 95){
irq-=64;
VIC2VECTADDR[irq]=(unsigned int)handler;
}


else if(irq>= 96 && irq<= 128){
irq-=96;
VIC3VECTADDR[irq]=(unsigned int)handler;
}


return 0;
}

void irq_handler()
{
void(*handler)();


if(VIC0ADDRESS !=0)//如果C0发生中断
handler=(void*)VIC0ADDRESS();
else if(VIC1ADDRESS !=0)
handler=(void*)VIC1ADDRESS();
else if(VIC2ADDRESS !=0)
handler=(void*)VIC2ADDRESS();
else if(VIC3ADDRESS !=0)
handler=(void*)VIC3ADDRESS();

handler();

VIC0ADDRESS = 0;
VIC1ADDRESS = 0;
VIC2ADDRESS = 0;
VIC3ADDRESS = 0;
}


2)main函数

#define GPH0CON (*(volatile unsigned int *)0xE0200C00)
#define EXT_INT_0_CON (*(volatile unsigned int *)0xE0200E00)
#define EXT_INT_0_MASK (*(volatile unsigned int *)0xE0200F00)
#define EXT_INT_0_PEND (*(volatile unsigned int *)0xE0200F40)
#define VICADDRESS (*(volatile unsigned int *)0xF2000F00)

int key1_handler(){

//利用函数指针调用printf,因为裸板没有该函数
int (*printf)(char *format,...);
printf=(void *)0x3ff13e54;

printf("hello key0");

EXT_INT_0_PEND = 0x1;//清零gpio的中断记录
}

int main(){

//初始化中断控制器
request_irq(0,key1_handler);

//初始化Key0,因为这里中断检测的是 GPH0_0管脚,也就是key0
GPH0CON |=  0xf;
//把gph0_0设置成中断功能

EXT_INT_0_CON &= ~0xf;
EXT_INT_0_CON |= 0x3;
//设置为上升沿触发

//设置掩码,将掩码开关打开
EXT_INT_0_MASK &= ~0x1;

while(1){
;
}
//while死循环等待key按下发生中断。一旦发生中断,CPU就会切换到异常向量表

return 0;
}



五、看门狗(WDT)

1)看门狗的简单介绍
CPU的一个外设,保护CPU的,防止其死机。如果死机就会发送有一个复位信号进行复位。
看门狗有2个功能,分别由WTCON【2】和WTCON【0】控制。前者控制是否产生中断;后者控制是否复位。
计数器:(Down Counter)必须有一个数值限制。设定计数器为固定值的操作为喂狗操作。
Chapter fourteen ARM概述(3)

2)WDT驱动具体实现

vi main.c

#define WTCON (*(volatile unsigned int *)0xE2700000)
#define WTDAT (*(volatile unsigned int *)0xE2700004)
#define WTCNT    (*(volatile unsigned int *)0xE2700008)
#define WTCLRINT (*(volatile unsigned int *)0xE270000C)
//828 配置寄存器

void wat_handler(){

int (*printf)(char *format,...);
printf=(void*)0x3ff13e54;

printf("hello watchdog!\n\r");

WTCLRINT = 1;
//清理中断
}

void wdt_init(){

WTCON = (65<<8) | (0x1<<5) | (0x1<<3) | (0x1<<2) | 0
//   Prescaler value=65;打开看门狗定时器;clock division factor=32;打开中断开关;禁止复位操作


//- t_watchdog = 1/( PCLK / (Prescaler value + 1) / Division_factor ) 看门狗周期计算
// t= 1/66/66/32= 32us

WTCNT= 1000*1000/32;//1s
WTDAT= 1000*1000/32;
//每次减完1s后,WTDAT自动赋值给WTCNT

}

int main(){

request_irq(27,wat_handler);//526

while(1){
;
}

return 0;

}

===========================================================================================================================

14.1.7  时钟系统


一、时钟的概念
时钟主要是用于同步工作系统的同步信号。
SOC内部有很多控制器,例如CPU,串口等外设,这些外设需要协同工作,互相通信,就需要同步的时钟系统来指挥,这个时候就需要SOC的时钟系统。


时钟源的获得方式:
a)外部直接输入,用于IC传感器。
b)外部晶振+内部时钟发生器产生时钟,一般用于低频单片机。
c)外部晶振+内部时钟发生器+倍频电路+内部分频电路得到各种频率的时钟,用于高速芯片(SOC)。

二、210时钟讲解

APLL、MPLL、EPLL、VPLL:锁相环
DIV:分频器
MUX:或门
XXTI:晶振
Chapter fourteen ARM概述(3)Chapter fourteen ARM概述(3)Chapter fourteen ARM概述(3)

三、210时钟编码测试


vi start.s
start.s:


.global _start
_start:
@这里开始地址为0x41000000


b reset
b undef
b swi
b abt_pre
b abt_dat
b reserved
b irq
b fiq

reset:
@CPU启动进入到reset 


@重新映射异常向量表到0x4000 8000
ldr r0,=0x40008000
mcr p15, 0, r0, c12, c0, 0


@打开CPU的总开关
mrs ro,cpsr
bic r0,r0,#(0x1<<7)
msr cpsr,r0 


@跳转到初始化函数
bl clock_init

@跳转到main函数
bl main

loop:
b loop

undef:
swi:
abt_pre:
abt_dat:
reserved:
irq:
@产生中断
@we need to init the stack pointer,because the mode has changed
ldr sp,=0x42000000

stmfd sp!, {r0-r12, lr}

bl irq_handler
//执行中断处理函数

ldmfd sp!,{r0-r12, lr}
subs pc, lr, #4
//为了防止语句漏执行,我们需要将lr-4后赋给pc

fiq:


vi clock.s

.global clock_init
clock_init:


@关闭使用PLL
ldr r0, =0xE0100200
ldr r1, =0x0;
str r1, [r0]
@将0写入0xe……,此时CPU为24M


@设置PLL
@(APLL_LOCK,0xE0100000)
@(MPLL_LOCK,0xE0100008)
@(EPLL_LOCK,0xE0100010)
@(VPLL_LOCK,0xE0100020)
Chapter fourteen ARM概述(3)

ldr r0, =0xE0100000
ldr r1, =30*24
str r1,  [r0]
@每个周期是24分之1us。现在我的locktime是30us,那么要经过多少个周期?其实就是30除以24分之一。


ldr r0, =0xE0100008
ldr r1, =200*24
str r1,  [r0]


ldr r0, =0xE0100010
ldr r1, =375*24
str r1,  [r0]


ldr r0, =0xE0100020
ldr r1, =100*24
str r1,  [r0]


 
@设置倍频因子
@APLL_CON0
ldr r0, =0xE0100100
ldr r1, =(1<<31)|(125<<16)|(3<<8)|(1<<0)
str r1, [r0]
Chapter fourteen ARM概述(3)

@MPLL_CON0
ldr r0, =0xE0100108
ldr r1, =(1<<31)|(667<<16)|(12<<8)|(1<<0)
str r1, [r0]


@配置分频器
@CLK_DIV0
ldr r0, =0xE0100300
ldr r1, =(1<<28)|(4<<24)|(1<<20)|(3<<16)|(1<<12)|(4<<8)|(4<<4)|0
str r1, [r0]

Chapter fourteen ARM概述(3)

Chapter fourteen ARM概述(3)
@使用PLL
ldr r0, =0xE0100200
ldr r1, =0x1111;
str r1, [r0]
@将0写入0xe……,此时为锁相环输出
Chapter fourteen ARM概述(3)

delay:
mov r0, 0x100000
d:
subs r0,r0, #1
bne d

mov pc,lr
@回去


四:210时钟设置程序测试程序

测试原理:如果我们使得输出为66MHZ时,而控制串口输出的时钟也是的66MHZ,如果
printf能用我们设置的频率 进行数据打印,那么我们就算是设置成功。


我们在上面程序的基础上,再加上之前wtd的main.c和irq.c就可以进行相应测试。

 

相关文章:

  • 2021-05-12
  • 2021-12-22
  • 2021-11-08
  • 2021-08-08
  • 2021-11-03
  • 2021-11-17
  • 2021-07-04
猜你喜欢
  • 2021-06-01
  • 2021-12-08
  • 2021-12-06
  • 2021-10-10
  • 2022-12-23
  • 2021-12-24
  • 2021-07-17
相关资源
相似解决方案