您的位置:首页 > 其它

嵌入式bootloader开发之七---裸机watchdog中断开发(Tiny 6410)

2013-11-05 21:46 489 查看
概要:

本节主要记录watchdog的复位和时钟中断配置方法。

S3C6410中和S3C2410,S3C2440中断管理方法不太一样,它有2个硬件中断控制器VIC0和VIC1来进行中断向量地址的保存和自动跳转,从一定程度上方便了开发者,但是对于我这个小白来说这无疑没什么优势,看的是2440的中断控制方法,却要在6410上使用。而且自己没有实际接触过ARM中断控制。。。

watchdog在嵌入式系统中用的比较多,主要用于防止系统跑飞,同时也可以作定时器使用。在6410中原理图如下:



PCLK是APB总线时钟,经过8-bit的预分频后,再次进行1/16,1/32,1/64或者1/128的分频后,在WTCNT中进行计时,到时后发出RESET或者INTERRUPT信号。

WTDAT在计时器中使用,当超时后自动装入WTCNT中,初始时候不能自动装入WTCNT寄存器,必须指定WTCNT的初始值.

复位信号配置比较简单,不做细述,源码如下:

//watch dog
#define WTCON (*(volatile unsigned long *)0x7E004000 )
#define WTDAT (*(volatile unsigned long *)0x7E004004 )
#define WTCNT (*(volatile unsigned long *)0x7E004008 )
#define WTCLRINT (*(volatile unsigned long *)0x7E00400C )

int main(void)
{
WTCNT = 0x8000;
WTDAT = 0x8000;
WTCON = 1 | (1 << 5) | (0x80 << 8) ;

return 0;
}

但中断的配置就没那么好实现了,需要配置相应的VIC控制器,同时要对中断处理机制有着比较清晰的认识。

首先,由S3C6410开发手册中可以找到有关VIC的相关叙述,查出Watchdog对应的中断号,具体如下:

26 INT_WDT Watchdog timer interrupt VIC0

VIC0处理流程图如下图:



watchdog中断控制中用到的控制寄存器主要有:

#define VIC0_BASE 0x71200000//VIC0基址

#define VIC0VECTADDR ((volatile unsigned *)( VIC0_BASE + 0x100))//中断处理函数表地址

#define VIC0ADDRESS (*(volatile unsigned long *)0x71200F00)//正在处理的中断函数地址

#define VIC0INTENABLE (*(volatile unsigned long *)(VIC0_BASE+0x010))//中断允许

#define VIC0INTSELECT (*(volatile unsigned long *)(VIC0_BASE+0x00C))//中断选择,irq或fiq

#define VIC0INTENCLEAR (*(volatile unsigned long*)(VIC0_BASE+0x014))//中断清除

默认情况下VIC0是不启用的,若要使用必须进行相应的配置,代码如下:

" mrc p15,0,r0,c1,c0,0 \n"
" orr r0,r0,#(1<<24)\n"
" mcr p15,0,r0,c1,c0,0\n" //使能vic0


配置完成后记得要清除IRQ或者FIQ标志,使得系统允许中断。代码如下:

" mrs r0,cpsr \n"
" bic r0,r0,#0x80\n"
" msr cpsr_c ,r0\n"//open the I-bit


另外中断处理中需要进行程序现场的保存和恢复,若在中断处理函数中想要使用uboot提供的函数,就必须使用堆栈,而默认情况下IRQ模式下堆栈是未设置的,所以必须使用sp进行堆栈的正确设置.代码如下:

" bic r0,r0,#0x1F\n"
" orr r1,r0,#0x12\n"
" msr cpsr,r1\n "//switch to IRQ Mode
" ldr sp,=0x52000000\n"//set sp in IRQ Mode

设置完成后,配置watchdog寄存器使其允许向系统发出中断,配置代码如下:

WTCNT = 0x8000;
WTDAT = 0x8000;
WTCON = 0;//关闭watchdog reset
WTCON = (1 << 5) | (0x80 << 8) | (1 << 2);//set watchdog can interrupt and disable the reset function


接着初始化中断控制器,关闭所有中断,清除中断函数寄存器,并设置使用IRQ模式进行中断。

//interrupt init
VIC0INTENCLEAR = 0xFFFFFFFF;//close the interrupt
VIC0INTSELECT = 0 ;//使用IRQ中断
VIC0ADDRESS = 0;//initialize the VIC0 ISR address
VIC0VECTADDR[INT_WDT] = irq_handler;//设置中断处理函数地址




最后允许watchdog中断,并使用一个while循环等待中断的到来。

VIC配置中,设置了中断服务程序的入口地址,中断服务程序主要流程是:

1.修改pc指针

2.保存程序执行现场

3.设置中断处理函数的返回地址

4.跳转到中断处理函数中进行中断服务

5.中断处理函数完成后恢复现场

在中断处理函数中记得要清除相应的中断标志位,否则会一直调用中断。

具体代码如下:

//ISR Entry
void irq_handler(void)
{
__asm__ volatile (
" sub lr,lr,#4\n"//修改返回地址
" stmfd sp,{r0-r14}\n"//保存程序执行现场
" sub sp,sp,#60\n"//修改sp指针,若直接使用stmfd sp!,{r0-r14}会提示warning
" mov lr,pc\n"//设置中断处理程序的返回地址
" ldr pc,=do_irq\n"//调用中断处理函数do_irq
" ldmfd sp,{r0-r13,pc}^\n"//中断处理完毕,恢复程序执行现场
);
}
//interrupt handling function
void do_irq(void)
{
show("watchdog irq.%d\n",cnt++);//打印中断
WTCLRINT &= 0x1;//清除watchdog中断
VIC0ADDRESS = 0;//清除中断处理程序中断
}


如此,便OK了!编写对应的makefile如下:

CC = arm-linux-gcc
LD = arm-linux-ld
OBJDUMP = arm-linux-objdump
OBJCOPY = arm-linux-objcopy
SRC = main.c
OBJ     =  main.o

main.bin : main
$(OBJCOPY) -O binary -I elf32-littlearm main main.bin
cp main.bin /var/lib/tftpboot

main : $(OBJ)
$(LD) -o main -Ttext=0x50000000 $(OBJ)

$(OBJ):
$(CC) -c $(SRC) -o $(OBJ)
clean :
rm $(OBJ) main.bin main


编译并下载到开发板上运行,即可看到每个一段时间输出一行消息,说明中断已经成功。运行情况如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: