一、时钟体系结构
1.s3c2440能够产生三种时钟,FCLK供给cpu,HCLK供给AHB总线设备,PCLK供给APB总线设备。它还有两个PLL,一个用于FCLK、HCLK、PCLK的时钟产生,另一个用于usb模块时钟。它的时钟源通过引脚OM3和OM2选择,我们外接的是12MH晶振,OM2和OM3都接地。这里需要注意的是上电复位后MPLL不工作,系统时钟为晶振或外部时钟源提供的时钟,必须设置MPLLCON才能启动MPLL.
page237
时钟结构:
page238
时钟启动过程:
page241
上电几毫秒晶振开始工作,FCLK=Fin(晶振频率),通过修改PMS的值,在等待一段时间(Lock-Time),FCLK就可以被改变,在这段时间,FCLK停止震动,cpu不工作。
寄存器配置:
(1)锁定时间
(2)PLL控制寄存器
常用的各个值表格:
注意:当MPLL和UPLL都设置时,应该首先设置UPLL的值,间隔7个NOP指令后再设置MPLL的值。
Mpll = (2 * m * Fin) / (p * 2S)
m = (MDIV + 8), p = (PDIV + 2), s = SDIV
Upll = (m * Fin) / (p * 2S)
m = (MDIV + 8), p = (PDIV + 2), s = SDIV
设置PLL控制寄存器后,经过锁定时间(Lock Time),MPLL输出稳定,CPU工作在心的FCLK频率下。
(3)时钟控制寄存器
(4)时钟分频控制寄存器
(5)相机时钟分频寄存器
注意:如果HDIVN非0,CPU的总线模式不得不从快速总线模式变为异步总线模式,用下面的指令:
#MMU_SetAsyncBusMode
mrc p15,0,r0,c1,c0,0
orr r0,r0,#R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
如果HDIVN非0,cpu的总线工作快速总线模式,则cpu时钟为HCLK。
二、pwm定时器
1.定时器基本结构
s3c2440共有5个16位定时器,定时器0到3可以产生pwm,定时器0有死区产生器,用于大电流的设备。
定时器0和1共享一个8位预分频器,定时器2、3、4共享一个8位预分频器,每个定时还另外有第二级分频器,可以对时钟进行2、4、8、16分频或者使用TCLK1、TCLK0。
page313
2.定时器工作过程
(1). 5个定时器都是向下计数器(down-counter),也就是每个时钟周期“减一”,首先要设置TCNTBn定时器初始值和TCMPBn定时器比较值。
(2).设置TCON启动定时器,TCNTBn和TCMPBn自动被放到定时器内部的TCNTn和TCMPn,TCNTn开始“减一”计数。
(3).当TCNTn等于TCMPn,定时器输出引脚反转。当TCNTn为0时,定时器输出引脚再次反转,此时如果设置了定时中断则产生中断信号。
(4).如果设置了自动重载功能,当当TCNTn为0时,TCNTBn和TCMPBn自动被放到定时器内部的TCNTn和TCMPn,开始下个计数周期。
(5).pwm(pulse width modulation):通过TCMPn与TCNTn比较和TCNTn为0不断产生电平的变化,可以产生不同的占空比,这就是pwm产生机制。
3.寄存器介绍
(1)TCFG0
page322
0~7位决定定时器0和1的预分频值,8~15决定定时器2、3、4预分频值。分频值的范围0~255
(2)TCFG1
page323
二次时钟分频:2、4、8、16分频,定时器0和1还可以选择TCLK0,定时器2、3、4可以选择TCLK1。
定时器工作频率=PCLK / {prescaler value+1} / {divider value}
{prescaler value} = 0~255
{divider value} = 2, 4, 8, 16
(3)TCNTBn和TCMPBn
TCNTBn为定时器初始值,TCMPBn为定时器比较初始值。定时器开始工作时将TCNTBn和TCMPBn装入内部寄存器TCNTn和TCMPn。
(4)TCNTOn
定时器开始工作时,TCNTn开始“减一”,TCNTOn用于读TCNTn的值。
(5)TCON
page324
(1).决定定时器x的启动和停止。
(2).手动更新定时器x,即手动更新TCNTBx和TCPMBx寄存器的值,此位在下次写入前需要清0.
(3)决定定时器输出引脚电平是否反转
(4)决定TCNTBx和TCPMBx是否自动重载
注意:第一次设置定时器时首先设置TCNTBx和TCPMBx寄存器的值,然后设置手动更新位为1,之后就可以启动定时器并清除手动更新为0。
三。实验
led通过定时器中断每隔0.5s亮灭交替。
head.S
.equ SDRAM_BASE, 0x30000000
.text
.global _start
@ 0x00:
_start:
b rest
@ 0x04: 未定义指令中止模式的向量地址
HandleUndef:
b HandleUndef
@ 0x08: 管理模式的向量地址,通过SWI指令进入此模式
HandleSWI:
b HandleSWI
@ 0x0c: 指令预取终止导致的异常的向量地址
HandlePrefetchAbort:
b HandlePrefetchAbort
@ 0x10: 数据访问终止导致的异常的向量地址
HandleDataAbort:
b HandleDataAbort
@ 0x14: 保留
HandleNotUsed:
b HandleNotUsed
@ 0x18: 中断模式的向量地址
b interrupt_server
@ 0x1c: 快中断模式的向量地址
HandleFIQ:
b HandleFIQ
rest:
mov sp, #4096 @设置堆栈
bl disable_watch_dog
bl init_clock @初始化时钟,开启MPLL
bl set_up_mem_ctl @设置存储控制器
mov r0, #0 @steppingstone起始地址
ldr r1, =SDRAM_BASE @SDRAM起始地址
mov r2,#4096 @拷贝代码长度
bl copy_steppingstone_to_sdram
ldr sp ,=0x34000000
ldr pc, =on_sdram
on_sdram:
bl init_led @初始化led
bl init_time0 @初始化定时器0
bl init_interrupt @初始化中断
ldr lr, =halt_loop
ldr pc, =main
halt_loop:
b halt_loop
interrupt_server:
sub lr, lr, #4
stmdb sp!, {r0-r12,lr}
ldr lr, =return
ldr pc, =handle_interrupt
return:
ldmia sp!, {r0-r12,pc}^
init.c
#include "s3c2440.h"
void init_led(void);
void init_time0(void);
void disable_watch_dog(void)
{
WTCON = 0x00;
}
//FCLK = 200MHz,HCLK = 100MHz,PCLK = 50MHz
void init_clock(void)
{
CLKDIVN = 0x03; //应先设置分频值,在设定MPLLCON
__asm__(
"mrc p15,0,r1,c1,c0,0\n"
"orr r1, r1, #0xc0000000\n"
"mcr p15,0,r1,c1,c0,0\n");
MPLLCON = (0x2a << 12) | (0x01 << 4) | 0x01; //MPLL为200MHz
}
void set_up_mem_ctl(void)
{
volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
/* 这个函数之所以这样赋值,而不是像前面的实验(比如mmu实验)那样将配置值
* 写在数组中,是因为要生成”位置无关的代码”,使得这个函数可以在被复制到
* SDRAM之前就可以在steppingstone中运行
*/
/* 存储控制器13个寄存器的值 */
p[0] = 0x22011110; //BWSCON
p[1] = 0x00000700; //BANKCON0
p[2] = 0x00000700; //BANKCON1
p[3] = 0x00000700; //BANKCON2
p[4] = 0x00000700; //BANKCON3
p[5] = 0x00000700; //BANKCON4
p[6] = 0x00000700; //BANKCON5
p[7] = 0x00018005; //BANKCON6
p[8] = 0x00018005; //BANKCON7
/* REFRESH,
* HCLK=12MHz: 0x008C07A3,
* HCLK=100MHz: 0x008C04F4
*/
p[9] = 0x008C04F4;
p[10] = 0x000000B1; //BANKSIZE
p[11] = 0x00000030; //MRSRB6
p[12] = 0x00000030; //MRSRB7
}
void copy_steppingstone_to_sdram(unsigned int *steppingstone_addr, unsigned int *sdram_addr, unsigned int length)
{
unsigned int i;
volatile unsigned int *source_addr = (volatile unsigned int *)steppingstone_addr;
volatile unsigned int *destination_addr = (volatile unsigned int *)sdram_addr;
for(i = 0; i < length; i++){
*(destination_addr + i) = *(source_addr + i);
}
}
void init_interrupt(void)
{
__asm__ __volatile__ (
"msr cpsr_c, #0xd2\n"
"ldr sp, =4096\n"
"msr cpsr_c, #0xd3\n"
"ldr sp, =0x34000000\n"
);
INTMSK &= (~(1<<10));
__asm__ __volatile__ (
"msr cpsr_c, #0x53\n"
);
}
void init_led(void)
{
//GPF4引脚设置为输出模式,高电平
GPFCON &= ~(0X03 << 8);
GPFCON |= 1<<8;
GPFDAT &= (~(1<<4));
}
void init_time0(void)
{
TCFG0 = 99; //预分频99
TCFG1 = 0X03; //16分频
TCNTB0 = 15625; //0.5s触发一次中断
TCON |= (1<<1); //手动更新
TCON = 0X09; //自动加载,清除手动更新,开启定时器
}
interrupt.c
#include "s3c2440.h"
static int flag = 0;
void handle_interrupt(void)
{
if(INTOFFSET == 10)
flag = !flag;
if(flag)
GPFDAT |= 1<<4;
else
GPFDAT &= ~(1<<4) ;
SRCPND = 1 << INTOFFSET;
INTPND = INTPND;
}
main.c
int main(void)
{
while(1);
return 0;
}
s3c2440.h
/* WOTCH DOG register */
#define WTCON (*(volatile unsigned long *)0x53000000)
/* SDRAM regisers */
#define MEM_CTL_BASE 0x48000000
#define SDRAM_BASE 0x30000000
/*GPIO registers*/
#define GPFCON (*(volatile unsigned long *)0x56000050)
#define GPFDAT (*(volatile unsigned long *)0x56000054)
#define GPGCON (*(volatile unsigned long *)0x56000060)
#define GPGDAT (*(volatile unsigned long *)0x56000064)
/*interrupt registes*/
#define SRCPND (*(volatile unsigned long *)0x4A000000)
#define INTMOD (*(volatile unsigned long *)0x4A000004)
#define INTMSK (*(volatile unsigned long *)0x4A000008)
#define PRIORITY (*(volatile unsigned long *)0x4A00000c)
#define INTPND (*(volatile unsigned long *)0x4A000010)
#define INTOFFSET (*(volatile unsigned long *)0x4A000014)
#define SUBSRCPND (*(volatile unsigned long *)0x4A000018)
#define INTSUBMSK (*(volatile unsigned long *)0x4A00001c)
/*external interrupt registers*/
#define EINTMASK (*(volatile unsigned long *)0x560000a4)
#define EINTPEND (*(volatile unsigned long *)0x560000a8)
/*CLOCK registers*/
#define LOCKTIME (*(volatile unsigned long *)0x4C000000)
#define MPLLCON (*(volatile unsigned long *)0x4C000004)
#define UPLLCON (*(volatile unsigned long *)0x4C000008)
#define CLKCON (*(volatile unsigned long *)0x4C00000c)
#define CLKSLOW (*(volatile unsigned long *)0x4C000010)
#define CLKDIVN (*(volatile unsigned long *)0x4C000014)
#define CAMDIVN (*(volatile unsigned long *)0x4C000018)
/*PWM & Timer registers*/
#define TCFG0 (*(volatile unsigned long *)0x51000000)
#define TCFG1 (*(volatile unsigned long *)0x51000004)
#define TCON (*(volatile unsigned long *)0x51000008)
#define TCNTB0 (*(volatile unsigned long *)0x5100000c)
#define TCMPB0 (*(volatile unsigned long *)0x51000010)
#define TCNTO0 (*(volatile unsigned long *)0x51000014)
Makefile:
objs := head.o init.o interrupt.o main.o
timer.bin: $(objs)
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:%.c
arm-linux-gcc -Wall -O2 -c -o $@ $<
%.o:%.S
arm-linux-gcc -Wall -O2 -c -o $@ $<
clean:
rm -f timer.bin timer_elf timer.dis *.o
timer.lds
SECTIONS {
. = 0x30000000;
.text : { *(.text) }
.rodata ALIGN(4) : {*(.rodata)}
.data ALIGN(4) : { *(.data) }
.bss ALIGN(4) : { *(.bss) *(COMMON) }
}