裸机系列代码地址:链接:http://pan.baidu.com/s/1pLHOd0v 密码:4x5s
S3C2440时钟控制逻辑给整个芯片提供了3种时钟:
FCLK:用于CPU核HCLK:用于AHB总线上设备,比如存储控制器,中断控制器,LCD控制器,DMA和USB主机模块。
PCLK:用于APB总线上的设备,比如WATCHDOG,IIC,PWM定时器,MMC接口,ADC,UART,GPIO,RTC,SPI
开发板上的晶振为12MHZ,为了提高频率,需要时钟控制逻辑PLL提高系统时钟,S3C2440有两个PLL
MPLL:用于设置FCLK,HCLK,PCLK。
UPLL: 专用于USB设备。
MPLL的设置,一般使用三个寄存器 LOCKTIME,MPLLCON,CLKDINV寄存器来设置FCLK,HCLK,PCLK的时钟频率
(1)设置MPLL的几个寄存器后,需要等待一段时间,MPLL的输出才稳定,这个等待的时间由LOCKTIME设置
(2)MPLLCON设置FCLK的频率相对于Fin的倍数关系,Fin为板上晶振频率,所以通过MPLLCON可以得到FCLK的频率
(3)CLKDIVN用来设置FCLK:HCLK:PCLK的比例关系,已知FCLK,又得到这个比例关系,则HCLK,PCLK频率可以确定
注意:当FCLK:HCLK!=1:1时,系统总线模式应从“fast bus mode”变为“asynchronous bus mode”
__asm__(
"mrc p15, 0, r1, c1, c0, 0\n" /* 读出控制寄存器 */
"orr r1, r1, #0xc0000000\n" /* 设置为“asynchronous bus mode” */
"mcr p15, 0, r1, c1, c0, 0\n" /* 写入控制寄存器 */
);
PWM定时器
S3C2440有5个16位的定时器,其中定时器0,1,2,3有PWM功能,有输出引脚,定时器4无输出引脚
定时器部件的时钟源为PCLK,首先通过1个8位的预分频器(TCFG0)降低频率,然后进入第二级分频器(TCFG1)分频,
这就得到定时器使用的频率。
确定定时器的工作频率后,在看看怎么控制定时器。
TCMPBn和TCNTBn用来设置定时器的PWM比例,定时器启动时,TCMPBn、TCNTBn的值被自动装入定时器内部寄存器TCMPn、TCNTn。
当定时器的TCNTn的值等于TCMPn的值时,定时器n的输出引脚的电平第一次翻转,当定时器的TCNTn的值等于0时,
TCON(TIMER CONTROL)
定时器的控制寄存器,用来决定定时器的启动,停止;当计数为0时是否自动将TMPPBn和TCNPBn的值装入定时器内部寄存器;
定时器的输出引脚是否翻转;第一次启动时是否将TMPPBn和TCNPBn的值装入定时器内部寄存器。
下面是一个定时器的实例
Makefile文件
objs := head.o init.o irq.o main.o
time.bin: $(objs)
arm-linux-ld -Ttime.lds -o time_linux $^
arm-linux-objcopy -O binary -S time_linux $@
arm-linux-objdump -D -m arm time_linux > time.dis
%.o:%.c
arm-linux-gcc -Wall -O2 -c -o $@ $<
%.o:%.S
arm-linux-gcc -Wall -O2 -c -o $@ $<
clean:
rm -f time.bin time_linux time.dis *.o
链接文件time.lds
SECTIONS {
. = 0x30000000;
.text : { *(.text) }
.rodata ALIGN(4) : {*(.rodata)}
.data ALIGN(4) : { *(.data) }
.bss ALIGN(4) : { *(.bss) *(COMMON) }
}
head.S文件
.text
.global _start
_start:
b reset
HandleUndef:
b HandleUndef
HandlerSWI:
b HandlerSWI
HandlePrefetchAbort:
b HandlePrefetchAbort
HandleDataAbort:
b HandleDataAbort
HandleNotUsed:
b HandleNotUsed
b HandleIRQ /*中断向量表的位置必须固定,即 b HandleIRQ运行位置必须在0x18处*/
HandleFIQ:
b HandleFIQ
reset:
ldr sp,=4096
bl disable_watch_dog
bl init_clock /*初始化系统时钟,可以提高系统频率,加快执行速度*/
bl memsetup /*存储控制器初始化,使得SDRAM可用,注意这里HCLK=100HZ,*/
bl copy_steppingstone_to_sdram /*将代码复制到SDRAM中执行,因为链接地址是SDRAM起始地址,所以必须复制到SDRAM中*/
ldr pc,=on_sdram /*这一句是地址相关代码,从这里开始代码在SDRAM中执行*/
on_sdram:
msr cpsr_c,#0xd2 /*进入中断模式*/
ldr sp,=4096 /*设置中断模式栈指针*/
msr cpsr_c,#0xdf /*进入用户模式/
ldr sp,=0x34000000 /*设置用户模式栈指针*/
bl init_led /*初始化led,即使得led引脚为输出引脚*/
bl init_irq /*初始化中断,不屏蔽TOU0中断*/
bl init_time0 /*定时器初始化*/
msr cpsr_c,#0x5f /*开中断*/
ldr lr,=halt_loop
ldr pc,=main /*进入死循环的main函数,等待中断*/
halt_loop:
b halt_loop
HandleIRQ: /*有IRQ中断到来就跳到此处*/
sub lr,lr,#4 /*计算中断返回的地址*/
stmdb sp!,{r0-r12,lr} /*保存中断现场*/
ldr lr,=int_return /*计算具体中断的返回地址*/
ldr pc,=EINT_Handle /*具体中断的函数*/
int_return:
ldmia sp!,{r0-r12,pc}^ /*返回到中断前的模式中*/
各初始化函数所在文件
#include "s3c2440.h"
void disable_watch_dog(void)
{
WTCON = 0x00000000;
}
void init_clock(void)
{
#define MPLL_200MHZ (0x5c<<12)|(0x01<<4)|(0x02)
CLKDIVN =0x03;/*FCLK:HCLK:PCLK = 4:2:1*/
__asm__(
"mrc p15, 0, r1, c1, c0, 0\n" /* 读出控制寄存器 */
"orr r1, r1, #0xc0000000\n" /* 设置为“asynchronous bus mode” */
"mcr p15, 0, r1, c1, c0, 0\n" /* 写入控制寄存器 */
);
MPLLCON = MPLL_200MHZ;
}
void copy_steppingstone_to_sdram(void)
{
unsigned int *pdest = (unsigned int *)0x30000000;
unsigned int *psrc = (unsigned int *)0x00000000;
while(psrc<(unsigned int *)4096) //片内存储大小为4k字节,即4096字节
{
*pdest=*psrc; //将源地址内容复制到目的地址处
pdest++;
psrc++;
}
}
void init_time0(void)
{
TCFG0 = (99); /*一级预分频器*/
TCFG1 = (0x03); /*二级预分频器16分频*/
TCNTB0 = 3125; /*计数初始值*/
TCON = (1<<1); /*第一次手动加载*/
TCON = (0x09); /*自动加载,清除手动更新,启动定时器0*/
}
void init_irq(void)
{
/*允许TOU0中断*/
INTMSK &= ~(0x1<<10);
}
void init_led(void)
{
GPFCON &= ~( (3<<8) | (3<<10) | (3<<12));
GPFCON |= ( (0b01<<8) | (0b01<<10) | (0b01<<12) );
GPFDAT |= (0xf<<4);
}
void memsetup(void)
{
volatile unsigned long *p = (volatile unsigned long *)0x48000000;
/* 这个函数之所以这样赋值,而不是像前面的实验(比如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
}
irq.c文件
#include "s3c2440.h"
void EINT_Handle(void)
{
int oft=INTOFFSET;
if(10 == oft)
{
if((GPFDAT>>4)&1 )
GPFDAT &=~(1<<4);
else
GPFDAT |= (1<<4);
break;
}
/*清除中断*/
SRCPND = 1<<oft;
INTPND = 1<<oft;
}
main.c文件
int main(void)
{
while(1);
return 0;
}