主机环境:UBUNTU10.04LTS+arm-linux-gcc 2.95.3
开发板环境:EdukitIII实验箱+s3c2410子板
问题描述:首先初始化S3C2410系统时钟,然后通过定时器中断来控制LED的点亮、熄灭情况
【1.系统时钟硬件原理】
EdukitIII实验箱上一共有两个时钟,都是通过外接晶振实现,一个是实时时钟RTC,主要为系统计时使用,其晶振X101频率为32.768kHz;另一个是系统时钟,为硬件设备提供时钟信号使用,其晶振X102为12MHz,系统时钟都是在X102的基础上通过时钟寄存器的控制来生成不同的时钟信号,为不同的硬件设备提供工作时钟信号。
S3C2410的时钟控制逻辑可以外接晶振,然后通过内部的电路产生时钟源,也可以直接使用外部提供的时钟源,通过引脚设置来选择。时钟逻辑为整个系统提供3种时钟:FCLK用于CPU核;HCLK用于AHB总线上的设备,如存储控制器、中断控制器、LCD控制器、DMA、USB主机模块等;PCLK用于APB总线上的设备,如WATCHDOG、IIS、I2C、PWM定时器、MMC接口、ADC、UART、GPIO、RTC、SPI等。
开发板上的外接时钟(晶振X102为12MHz)通过相位锁相环(PLL)电路来提高频率,S3C2410有两个PLL,一个MPLL,用于设置FCLK、HCLK、PCLK;另一个为UPLL,用于USB设备。
上电时,PLL没有启动,FCLK等于外部输入时钟Fin,若要提高系统频率,通过软件来启用PLL(设置相关寄存器),图1为PLL上电后的启动过程
图1 上电后MPLL的启动过程
图中的OSC即是外接晶振X102,频率为12MHz,上电后需要等待一段时间(Lock Time),MPLL才能输出稳定,Lock Time的值由寄存器LOCITIME设置。Lock Time之前,FCLK=Fin,Lock Time之后,MPLL输出正常,CPU工作在新的FCLK之下。
启动S3C2410的MPLL,要设置3个相关寄存器:
1.LOCITIME寄存器,用于设置Lock Time的长度,地址和各位含义如图2所示
图2 LOCKTIME地址和各位含义
LOCKTIME[0:11]用于设置MPLL的Lock Time,LOCKTIME[12:23]用于设置UPLL的Lock Time,使用默认值0x00FFFFFF即可。
2.MPLLCON寄存器(Main PLL Control):用于设置FCLK与Fin的倍数,地址和各位含义如图3所示
图3 MPLLCON地址和各位含义
MPLLCON[0:1]称为SDIV,MPLLCON[4:9]称为PDIV,MPLLCON[12:19]称为MDIV,FCLK的计算公式如下:
S3C2410: MPLL(FCLK) = (m * Fin)/(p * 2^s)
S3C2410: MPLL(FCLK) = (2 * m * Fin)/(p * 2^s)
其中: m = MDIV + 8, p = PDIV + 2, s = SDIV
对于本开发板,Fin = 12MHz(晶振X102的频率)
本例要产生的FCLK为200MHz,因此MDIV=0x5C=92;PDIV=0x04=4;SDIV=0x00=0,所以m=100,p=6,s=0,把Fin=12MHz代入S3C2410的MPLL(FCLK)计算公式,可以得到FCLK=200MHz,因此该寄存器的值可以设置为((0x5c<<12)|(0x04<<4)|(0x00))。图4是一些常用的PLL值,设置MPLLCON寄存器时可以参考该表。
图4:MPLLCON常用设置值
3.CLKDIVN寄存器,用于设置FCLK、HCLK、PCLK的比例,地址和各位含义如图5所示
![110526130654.jpg](http://blogimg.chinaunix.net/blog/upfile2/110526130654.jpg)
转存失败重新上传取消
图5 CLKDIVN地址和各位含义
HDIVN1=CLKDIVN[2]=0,表示保留,HDIVN1=CLKDIVN[2]=1,表示FCLK:HCLK:PCLK=1:4:4,此时HDIVN和PDIVN均要设置为0。HDIVN=CLKDIVN[1]=0,表示HCLK=FCLK,HDIVN=CLKDIVN[1]=1,表示HCLK=FCLK/2;PDIVV=CLKDIVN[0]=0,表示PCLK=HCLK,PDIVV=CLKDIVN[0]=1,表示PCLK=FCLK/2。对于本例程,要求FCLK:HCLK:PCLK=1:2:4,所以CLKDIVN[0:2]=0b110=0x3。如果HDIVN=1,那么CPU要从fast bus mode转换为asynchronous bus mode,可以通过如下指令完成:
![110526130654.jpg](http://blogimg.chinaunix.net/blog/upfile2/110526130654.jpg)
![uploading.4e448015.gif](https://i-blog.csdnimg.cn/blog_migrate/750a96f1213fde619ab85ab1c8d2e3e5.gif)
![110526130654.jpg](http://blogimg.chinaunix.net/blog/upfile2/110526130654.jpg)
图5 CLKDIVN地址和各位含义
HDIVN1=CLKDIVN[2]=0,表示保留,HDIVN1=CLKDIVN[2]=1,表示FCLK:HCLK:PCLK=1:4:4,此时HDIVN和PDIVN均要设置为0。HDIVN=CLKDIVN[1]=0,表示HCLK=FCLK,HDIVN=CLKDIVN[1]=1,表示HCLK=FCLK/2;PDIVV=CLKDIVN[0]=0,表示PCLK=HCLK,PDIVV=CLKDIVN[0]=1,表示PCLK=FCLK/2。对于本例程,要求FCLK:HCLK:PCLK=1:2:4,所以CLKDIVN[0:2]=0b110=0x3。如果HDIVN=1,那么CPU要从fast bus mode转换为asynchronous bus mode,可以通过如下指令完成:
mrc p15, 0, r1, c1, c0, 0\n /* 读出控制寄存器 */ |
【2.PWM定时器硬件原理】
S3C2410共有5个16位的定时器,其中定时器0、1、2、3具有脉宽调制PWM(Pulse Width Modulation)功能,它们都有一个输出引脚TOUTi,可以通过定时器来控制输出引脚周期性的高、低电平变化,定时器4没有输出引脚,定时器结构如图6所示
![110526134937.jpg](http://blogimg.chinaunix.net/blog/upfile2/110526134937.jpg)
![uploading.4e448015.gif](https://i-blog.csdnimg.cn/blog_migrate/750a96f1213fde619ab85ab1c8d2e3e5.gif)
![110526134937.jpg](http://blogimg.chinaunix.net/blog/upfile2/110526134937.jpg)
图6 PWM定时器结构图
PWM定时器使用的时钟是PCLK,先通过两个8位的预分频器(使用TCFG0寄存器来设置)降低频率,定时器0、1公用一个预分频器,定时器2、3、4公用第二个预分频器。预分频器的输出进入到第二级分频器,可以产生2分频、4分频、8分频、16分频或者外部时钟TCLK0/TCLK1,每个定时器的工作频率可以从这5种频率中选择(使用TCFG1寄存器来设置)。
PWM定时器工作流程如下:参考图7
![110526140126.jpg](http://blogimg.chinaunix.net/blog/upfile2/110526140126.jpg)
![uploading.4e448015.gif](https://i-blog.csdnimg.cn/blog_migrate/750a96f1213fde619ab85ab1c8d2e3e5.gif)
![110526140126.jpg](http://blogimg.chinaunix.net/blog/upfile2/110526140126.jpg)
图7 PWM定时器操作流程
(1).定时器有TCMPBn、TCNTBn寄存器,用于设置定时器n的初始比较值和初始计数值
(2).然后设置TCONn寄存器启动定时器n,TCMPBn、TCNTBn寄存器的值分别装入TCMPn、TCNTn寄存器,TCMPn、TCNTn是定时器内部寄存器,在定时器n的工作频率下,TCNTn开始减1计数,其值可以通过读取TCNTOn得到
(3).当TCNTn的值等于TCMPn的值时,定时器n的输出TOUTn电平反转,TCNTn继续减数
(4).当TCNTn的值到达0时,输出引脚TOUTn电平再次反转(这样就实现了PWM),并触发定时器n的定时中断(如果定时中断使能)
(5).TCNTn到达0时,如果在TCON寄存器中将定时器n设为“自动加载”,则TCMPBn和TCNTBn自动重新装入TCMPn和TCNTn寄存器,下一个计数流程开始,
PWM定时器常用寄存器,以定时器0为例
1.TCFG0寄存器(TIMER CONFIGURATION),用于控制预分频器,其地址和各位含义如图8所示
![11829250_1306401073Ubi5.jpg](/attachment/201105/26/11829250_1306401073Ubi5.jpg)
![uploading.4e448015.gif](https://i-blog.csdnimg.cn/blog_migrate/750a96f1213fde619ab85ab1c8d2e3e5.gif)
![11829250_1306401073Ubi5.jpg](/attachment/201105/26/11829250_1306401073Ubi5.jpg)
图8 TCFG0寄存器地址和各位含义
定时器的输入频率计算公式为:定时器工作频率=PCLK/(prescaler value+1)/(divider value),其中prescaler value的值(预分频器)通过TCFG0寄存器设置,TCFG0[0:7]设置定时器0和定时器1的prescaler value值,TCFG0[8:15]设置定时器2、3、4的prescaler value的值,该值的范围为0~255,本例程选择定时器0的该值为99,因此该寄存器的值设置为99。divider value的值(第二级分频器)为2、4、8、16,由TCFG1寄存器设置
2.TCFG1寄存器(TIMER CONFIGURATION),经预分频器得到的时钟被输入到第二级分频器,可以再次被2分频、4分频、8分频和16分频,由图6可知,定时器0、1还可以工作在外接时钟TCLK0下,定时器2、3、4还可以工作在外接时钟TCLK1下,使用TCFG1寄存器来设置这5个定时器的第二级分频器的分频数,其地址和各位含义如图9所示
![11829250_1306402110ZjnW.jpg](/attachment/201105/26/11829250_1306402110ZjnW.jpg)
![uploading.4e448015.gif](https://i-blog.csdnimg.cn/blog_migrate/750a96f1213fde619ab85ab1c8d2e3e5.gif)
![11829250_1306402110ZjnW.jpg](/attachment/201105/26/11829250_1306402110ZjnW.jpg)
图9 TCFG1寄存器地址和各位含义
TCFG1[20:23]用于设置5个定时器的DMA模式,TCFG1[0:3]设置定时器0的分频数。本例程中不使用定时器的DMA模式,定时器0的第二级分频选择1/16,所以该寄存器的值为0x3。
3.TCON寄存器(TIMER CONTROL),其地址和各位含义如图10所示
![11829250_1306408480A3qz.jpg](/attachment/201105/26/11829250_1306408480A3qz.jpg)
![uploading.4e448015.gif](https://i-blog.csdnimg.cn/blog_migrate/750a96f1213fde619ab85ab1c8d2e3e5.gif)
![11829250_1306408480A3qz.jpg](/attachment/201105/26/11829250_1306408480A3qz.jpg)
图10 TCON地址和各位含义
TCON[0]设置定时器0的开启和停止;TCON[1]设置定时器0“手动更新”,将TCMPB0/TCNTB0的值装入内部寄存器TCMP0/TCNT0中;TCON[2]设置TOUT0是否反转;TCON[3]设置自动加载。对于本例程,使用定时器0,开启定时器、手动更新、反转、自动加载。
4.TCNTB0/TCMPB0/TOUT0寄存器,TCNTB0用于保存定时器初始计数值,TCMPB0用于保存比较值,TOUT0用来观察TCNT0的数值;其地址和各位含义如图11所示
![11829250_1306409033EaQe.jpg](/attachment/201105/26/11829250_1306409033EaQe.jpg)
![uploading.4e448015.gif](https://i-blog.csdnimg.cn/blog_migrate/750a96f1213fde619ab85ab1c8d2e3e5.gif)
![11829250_1306409033EaQe.jpg](/attachment/201105/26/11829250_1306409033EaQe.jpg)
图11 TCNTB0/TCMPB0/TOUT0寄存器地址和各位含义
对与本例程,TCMPB0=0,TCNTB0=15625。
【3.程序实现】
该例程一共包含8个文件,描述如下:
文件名 | 描述 |
Head.s | ARM启动代码,设置异常向量,只实现了复位异常和普通中断异常;调用存储器设置函数、调用关闭看门狗定时器函数、调用时钟初始化函数、调用定时器初始化函数、调用LED初始化函数、调用中断初始化函数,调用把代码从Step Stone拷贝到SDRAM的拷贝函数,最后调用C语言的Main函数。 |
init.c | 初始化函数的实现,包括关闭看门狗函数的实现、时钟初始化函数的实现、存储器初始化函数的实现、把代码从Step Stone拷贝到SDRAM函数的实现、LED初始化函数的实现、定时器初始化函数的实现、中断初始化函数的实现 |
interrupt.c | 定时器中断的中断服务程序的实现,中断服务程序的功能是每次中断改变4个LED灯的状态 |
interrupt.h | 定时器中断的中断服务程序的头文件 |
s3c24xx.h | s3c2410A的头文件,主要是相关寄存器地址的定义。 |
main.c | 主函数 |
Makefile | Makefile文件 |
timer.lds | 链接脚本文件 |
- @******************************************************************************
- @Name: head.S
- @Desc: 初始化,设置中断模式、系统模式的栈,设置好中断处理函数
- @Parameter:
- @Return:
- @Author: yoyoba(stuyou@126.com)
- @Date: 2011-5-26
- @Modify: 2011-5-26
- @*******************************************************************************
- .extern main
- .text
- .global _start
- _start:
- @******************************************************************************
- @ 中断向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用
- @******************************************************************************
- b Reset
- @ 0x04: 未定义指令中止模式的向量地址
- HandleUndef:
- b HandleUndef
- @ 0x08: 管理模式的向量地址,通过SWI指令进入此模式
- HandleSWI:
- b HandleSWI
- @ 0x0c: 指令预取终止导致的异常的向量地址
- HandlePrefetchAbort:
- b HandlePrefetchAbort
- @ 0x10: 数据访问终止导致的异常的向量地址
- HandleDataAbort:
- b HandleDataAbort
- @ 0x14: 保留
- HandleNotUsed:
- b HandleNotUsed
- @ 0x18: 中断模式的向量地址
- b HandleIRQ
- @ 0x1c: 快中断模式的向量地址
- HandleFIQ:
- b HandleFIQ
- Reset:
- ldr sp, =4096 @ 设置栈指针,以下都是C函数,调用前需要设好栈
- bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启
- bl clock_init @ 设置MPLL,改变FCLK、HCLK、PCLK
- bl memsetup @ 设置存储控制器以使用SDRAM
- bl copy_steppingstone_to_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的GPIO管脚
- bl timer0_init @ 初始化定时器0
- bl init_irq @ 调用中断初始化函数,在init.c中
- msr cpsr_c, #0x5f @ 设置I-bit=0,开IRQ中断
- ldr lr, =halt_loop @ 设置返回地址
- ldr pc, =Main @ 调用Main主函数
- halt_loop:
- b halt_loop
- HandleIRQ:
- sub lr, lr, #4 @ 计算返回地址
- stmdb sp!, { r0-r12,lr } @ 保存使用到的寄存器
- @ 注意,此时的sp是中断模式的sp
- @ 初始值是上面设置的4096
- ldr lr, =int_return @ 设置调用ISR即EINT_Handle函数后的返回地址
- ldr pc, =Timer0_Handle @ 调用中断服务函数,在interrupt.c中
- int_return:
- ldmia sp!, { r0-r12,pc }^ @ 中断返回, ^表示将spsr的值复制到cpsr
- /****