s3c2440时钟系统

一、时钟体系结构

1.s3c2440能够产生三种时钟,FCLK供给cpu,HCLK供给AHB总线设备,PCLK供给APB总线设备。它还有两个PLL,一个用于FCLK、HCLK、PCLK的时钟产生,另一个用于usb模块时钟。它的时钟源通过引脚OM3和OM2选择,我们外接的是12MH晶振,OM2和OM3都接地。这里需要注意的是上电复位后MPLL不工作,系统时钟为晶振或外部时钟源提供的时钟,必须设置MPLLCON才能启动MPLL.

s3c2440时钟系统page237

时钟结构:

s3c2440时钟系统page238

时钟启动过程:

s3c2440时钟系统page241

上电几毫秒晶振开始工作,FCLK=Fin(晶振频率),通过修改PMS的值,在等待一段时间(Lock-Time),FCLK就可以被改变,在这段时间,FCLK停止震动,cpu不工作。

寄存器配置:

(1)锁定时间

s3c2440时钟系统

(2)PLL控制寄存器

s3c2440时钟系统

常用的各个值表格:

s3c2440时钟系统

注意:当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)时钟控制寄存器

s3c2440时钟系统

(4)时钟分频控制寄存器

s3c2440时钟系统

(5)相机时钟分频寄存器

s3c2440时钟系统



注意:如果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。

s3c2440时钟系统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

s3c2440时钟系统page322

0~7位决定定时器0和1的预分频值,8~15决定定时器2、3、4预分频值。分频值的范围0~255

(2)TCFG1

s3c2440时钟系统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

s3c2440时钟系统page324

s3c2440时钟系统

(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 [email protected]
    arm-linux-objdump -D -m arm timer_elf > timer.dis
    
%.o:%.c
    arm-linux-gcc -Wall -O2 -c -o [email protected] $<

%.o:%.S
    arm-linux-gcc -Wall -O2 -c -o [email protected] $<

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) }
}