1.实验说明
通过使用定时器0来实现1秒的定时功能 ,通过对定时器中断处理函数对灯光的状态进行取反来演示.
2.相关寄存器介绍
定时器配置相关寄存器
1)TCFG0
注:(PCLK =101.25MHz)
prescaler 0 = 249,divider value = 16
这样定时器输入时钟频率 = 101,250,000Hz/250/16 = 25,312Hz
综上所述 , 对于TCFG0的配置如下:
TCFG0 |= 249 =0xF9;
2)TCFG1
注:
由TCFG0的讨论可以得知 , 这里MUX 0 = 0011b
所以对TCFG1的配置如下:
TCFG1 |= 3 ;
3)TCON
注:
这个寄存器留在最后配置;
禁止dead zone操作 , 使能自动重载 , 关闭输出反相器 , 第一次要手动更新TCNTB0 ,开启定时器0功能 .
(定时器启动之后要把手动更新TCNTB0和TCMPB0禁止掉) .
综上所述对TCON的配置如下 :
启动定时器前 : TCON |=0x0B ;
启动定时器后 : TCON&= ~(1<<1) ;
4)TCNTB0和TCMPB0
注:
使计数1秒产生中断
TCNTB0 = 25312;
TCMPB0 = 0 ;
5)TCNTO0
注:
通过读取这个寄存器可以获得当前计数器值.
定时器中断相关介绍及寄存器
1)相关仲裁器
2)SRCPND
注:
写1清零 ,在中断服务子程序里 .
3)INTPND
注:
写1清零,在中断服务子程序里 ,并且要在对SRCPND清零之后.
4)INTMOD
注:
INTMOD &= ~(1<<10) 直接采用复位默认值就行
5)INTMSK
注:
INTMSK &= ~(1<<10)
6)INTOFFSET
注:
在中断服务子程序里判断是否为定时器0中断 .
3.程序流程图设计
1)主流程图:
2)中断服务程序流程图:
3)中断服务处理子函数流程图:
4.程序设计
1)Makefile
timer.bin : head.o led_display.o
arm-linux-ld -Ttimer.lds -o timer_elf $^
arm-linux-objcopy -O binary -S timer_elf $@
arm-linux-objdump -D -m arm timer_elf > timer.dis
%.o : %.S
arm-linux-gcc -Wall -O2 -c -o $@ $<
%.o : %.c
arm-linux-gcc -Wall -O2 -c -o $@ $<
clean:
rm -f timer.dis timer.bin timer_elf *.o *.bak
2)timer.lds
SECTIONS {
first 0x00000000 : { head.o }
second 0x30000000 : AT(2048) { led_display.o }
}
3)head.S
@与内存相关
.equ BWSCON , 0x48000000
.equ BANKCON6 , 0x4800001C
.equ REFRESH , 0x48000024
.equ BANKSIZE , 0x48000028
.equ MRSRB6 , 0x4800002C
@与中断相关
.equ INTMSK , 0x4A000008
.equ SRCPND , 0x4A000000
.equ INTPND , 0x4A000010
@与看门狗相关
.equ WTCON , 0x53000000
@与灯光配置相关的
.equ GPBCON , 0x56000010
.equ GPBDAT , 0x56000014
@与定时器配置相关的
.equ TCFG0 , 0x51000000
.equ TCFG1 , 0x51000004
.equ TCON , 0x51000008
.equ TCNTB0 , 0x5100000C
.equ TCMPB0 , 0x51000010
.equ TCNTO0 , 0x51000014 @read-only
@时钟相关寄存器
.equ MPLLCON , 0x4C000004
.equ UPLLCON , 0x4C000008
.equ CLKDIVN , 0x4C000014
.equ CAMDIVN , 0x4C000018
.text
.global _start
_start:
/***********设置中断向量表*************/
b ResetInit @复位异常入口
HandlerUndef:
b HandlerUndef @未定义异常入口
HandlerSWI:
b HandlerSWI @软中断异常入口
HandlerPabort:
b HandlerPabort @取指中止异常入口
HandlerDabort:
b HandlerDabort @数据中止异常入口
HandlerNotUsed:
b HandlerNotUsed @保留
b HandlerIRQ @中断异常入口
HandlerFIQ:
b HandlerFIQ @快中断异常入口
/************END设置中断向量表***********/
ResetInit:
/*************关闭看门狗****************/
ldr r0 , =WTCON
mov r1 , #0x0
str r1 , [r0]
/************END关闭看门狗**************/
/**********初始化LED灯管脚************/
@把LED1-4管脚置为输出
ldr r0 , =GPBCON
ldr r1 , [r0] @把GPBCON里的内容加载到r1里
ldr r2 , =(0xFF<<10)
bic r1 , r1 ,r2 @操作数取反码或上r1,用于清零工作
ldr r2 , =(0x55<<10)
orr r1 , r1 , r2
str r1 , [r0]
/***********END***************/
/***********配置内存相关寄存器***********/
ldr lr , =InitSystemSp
ldr pc , =MemConfigure
/**********END配置内存相关寄存器*********/
/***********设置系统模式下的sp***********/
InitSystemSp:
@复位默认进入系统模式
ldr sp , =0x34000000
/********END设置系统模式下的sp***********/
/********配置定时器0中断相关寄存器*******/
ldr r0 , =INTMSK
ldr r1 , [r0]
bic r1 , r1 , #(0x01<<10)
str r1 , [r0]
/******END配置定时器0中断相关寄存器******/
/*进入中断模式设置中断模式下的sp退出到系统模式
*使能IRQ中断*/
msr cpsr_c , 0xd2 @进入中断模式,禁止中断,其中cpsr后的_c表示cpsr[7:0]
ldr sp , =0x33500000 @设置sp
msr cpsr_c , 0x5f @退出到系统模式,使能IRQ中断
/*END进入中断模式设置中断模式下的sp退出到系统模式*/
/***********配置时钟相关寄存器***********/
ldr lr , =CopyToSdram
ldr pc , =ClkConfigure
/**********END配置时钟相关寄存器*********/
/************拷贝中断处理子函数到内存****/
CopyToSdram:
ldr lr , =InitTimer0
ldr pc , =copy_bootsram_to_sdram
/*******************END******************/
/*配置定时器0相关寄存器使定时1秒进入中断*/
InitTimer0:
ldr lr , =halt_loop
ldr pc , =Timer0Configure
/*******************END******************/
/*************等待中断******************/
halt_loop:
b halt_loop
/*********************END****************/
/*************IRQ中断服务子程序**********/
HandlerIRQ:
@因为在产生中断异常时,lr存的是当前指令的下一条指令,所以要减四
sub lr , lr , #4
@把相关寄存器压入中断模式下的栈
@db表示sp每次传送内容前减1
stmdb sp! , {r0-r12 , lr}
@禁止IRQ中断
mrs r1 , cpsr_all
orr r1 , r1 , #(1<<7)
msr cpsr_all , r1
@调用中断服务处理函数
ldr lr , =IRQ_Return
ldr pc , =main
IRQ_Return:
@把栈里面的内容推出到相应寄存器里,并把lr推到pc寄存器实现跳转
@ia表示每次传送后加1 , 当寄存器列表中包含了pc寄存器,选用^为后缀,就会把spsr拷贝到cpsr
ldmia sp! , {r0-r12 , pc}^
/********END IRQ中断服务子程序***********/
/***********配置时钟相关寄存器***********/
ClkConfigure:
@UPLL选择MDIV=0x38,PDIV=2,SDIV=1.得UPLL clock=96MHz
@这里先设置UPLL是为了与设置MPLL隔开至少7个NOP的时间间隔
ldr r0 , =UPLLCON
ldr r1 , =(0x38<<12)|(0x02<<4)|(0x01<<0)
str r1 , [r0]
@分频比FCLK:HCLK:PCLK=1:4:4 , UCLK=UPLL_clock/2=48MHz
ldr r0 , =CLKDIVN
mov r1 , #(0x01<<3)|(0x02<<1)|(0x00<<0)
str r1 , [r0]
ldr r0 , =CAMDIVN
ldr r1 , [r0]
bic r1 , r1 , #(0x03<<8)
str r1 , [r0]
@因为HDIVN不是0 , 所以CPU总线模式要从高速总线模式改变
@为异步总线模式
mrc p15 , 0 , r1 , c1 , c0 , 0
orr r0 , r0 , #0xC0000000
mcr p15 , 0 , r0 , c1 , c0 , 0
@MPLL选择MDIV=0x7f,PDIV=2,SDIV=1,得MPLL clock=405MHz
ldr r0 , =MPLLCON
ldr r1 , =(0x7f<<12)|(0x02<<4)|(0x01<<0)
str r1 , [r0]
bx lr
/***************END*************************/
/*配置定时器0相关寄存器使定时1秒进入中断*/
Timer0Configure:
ldr r0 , =TCFG0
ldr r1 , =0xF9
str r1 , [r0]
ldr r0 , =TCFG1
ldr r1 , =0x03
str r1 , [r0]
ldr r0 , =TCNTB0
ldr r1 , =0x62E0
str r1 , [r0]
ldr r0 , =TCMPB0
mov r1 , #0x0
str r1 , [r0]
@第一次手动更新TCNTB0和TCMPB0
ldr r0 , =TCON
mov r1 , #(0x01<<1)
str r1 , [r0]
@手动更新TCNTB0和TCMPB0禁止,使能自动重载,启动定时器0
mov r1 , #0x09
str r1 , [r0]
bx lr
/******************END******************/
/*******内存初始化子程序*********/
MemConfigure:
@BWSCON[27:24] = 0 0 10B
ldr r0 , =BWSCON
ldr r1 , [r0]
ldr r2 , =(0x0F<<24)
bic r1 , r1 , r2
ldr r2 , =(0x02<<24)
orr r1 , r1 , r2
str r1 , [r0]
@BANKCON6[16:15]=11B;BANKCON6[3:0]=00 01B
ldr r0 , =BANKCON6
ldr r1 , [r0]
ldr r2 , =(0x03<<15)
bic r1 , r1 , r2
orr r1 , r1 , r2
ldr r2 , =0x0F
bic r1 , r1 , r2
ldr r2 , = 0x01
orr r1 , r1 , r2
str r1 , [r0]
@这里的Trp要大于20ns , Trc要大于70ns,HCLK=101.25MHz
@故时钟周期=1s/HCLK=9.8ns,Trp*9.8>20ns ==> Trp>=3 ==> Trp域=01b
@Trp*9.8+Tsrc*9.8>70ns ==> Tsrc>=5 ==> Tsrc域=01b
@REFRESH[23:18] = 1 0 01 01B;REFRESH[10:0] = 0x4E8
ldr r0 , =REFRESH
ldr r1 , [r0]
ldr r2 , =(0x3F<<18)
bic r1 , r1 , r2
ldr r2 , =(0x25<<18)
orr r1 , r1 , r2
ldr r2 , =0x7FF
bic r1 , r1 , r2
ldr r2 , =0x4E9
orr r1 , r1 , r2
str r1 , [r0]
@BANKSIZE[7:0] = 1 0 1 1 0 001 B
ldr r0 , =BANKSIZE
ldr r1 , [r0]
ldr r2 , =0xFF
bic r1 , r1 , r2
ldr r2 , =0xB1
orr r1 , r1 , r2
str r1 , [r0]
@MRSRB6[11:0] = 0 00 011 0 000 B
ldr r0 , =MRSRB6
ldr r1 , [r0]
ldr r2 , =0x3FF
bic r1 , r1 , r2
ldr r2 , =0x030
orr r1 , r1 , r2
str r1 , [r0]
bx lr @函数返回
/******END内存初始化子程序*******/
/******拷贝后2048到4096之间的代码到sdram*******/
copy_bootsram_to_sdram:
ldr r0 , =0x800
ldr r1 , =0x30000000
ldr r2 , =0x1000
copy:
ldr r3 , [r0] , #4
str r3 , [r1] , #4
cmp r0 , r2
bne copy
bx lr
/*****END拷贝前4K代码到sdram*****/
4)led_display.c
#define GPBCON (*(volatile unsigned long *)0x56000010)
#define GPBDAT (*(volatile unsigned long *)0x56000014)
#define INTOFFSET (*(volatile unsigned long *)0x4A000014)
#define SRCPND (*(volatile unsigned long *)0x4A000000)
#define INTPND (*(volatile unsigned long *)0x4A000010)
int main()
{
if(INTOFFSET == 10){
GPBDAT ^= (0xF<<5); //LED灯状态取反
}
SRCPND = SRCPND;
INTPND = INTPND;
return 0;
}