linux应用程序使用时钟中断,《嵌入式linux应用程序开发完全手册》系统时钟和定时器学习笔记...

系统时钟和定时器

一.系统时钟

(1) FCLK:用于CPU核

HCLK:用于AHB总线上设备:CPU核、存储器控制器、中断控制器、LCD控制器、DMA和USB主机模块

PCLK:用于APB总线上设备:WATCHDOG、IIS、I2C、PWM定时器、MMC接口、ADC、UART、GPIO、RTC和SPI

(2 )开发板时钟频率为12 MHZ,通过PLL提高系统时钟:S3C2440包括MPLL和UPLL,UPLL用于USB设备,MPLL用于FCLK、HCLK、PLCK,他们的设置方法类似。

(3 )上电→FCLK=Fin(外部输入时钟)→设置MPLL相关寄存器→等待(Lock Time:长短由寄存器LOCKTIME设定)→MPLL输出稳定,CPU工作在新的时钟FCLK下。

(4)设置MPLL需要设置下面几个重要寄存器:

LOCKTIME寄存器(LOCK TIME COUNT)用于设置lock time的长度。

MPLLCON(Main PLL Control)寄存器用于设置FCLK与Fin的倍数

CLKDIVN(CLOCK DIVIDER CONTROL)寄存器用于设置FCLK、HCLK、PCLK三者的比例

CAMDIVN某些时钟比例需要设置。

二. PWM(pulse width modulation)定时器

(1)S3C2440共有5个16位的定时器,其中定时器0、1、2、3有PWM功能,即它们都有一个输出引脚,可以通过定时器来控制引脚周期性的高、低电平变化;定时器4没有输出引脚。

(2)PLCK→2个8位预分频器(定时器0、1共用第一个定时器,2、3、4共用第二个);→第二级分频(输出2分频,4分频,8分频,16分频或者外部时钟TCLK0/TCLK1)。

这两次预分频都是通过设置TCFG0寄存器完成的。每个定时器工作在哪种频率下可以通过TCFG1寄存器来选择的。定时器内部控制逻辑的详细原理可参考数据文档。

定时器的使用主要涉及两个寄存器:

TCFG0寄存器:位[7:0],位[15:8]分别用于控制预分频器0,1;它们的值为0~255。经过分频器出来的时钟频率:PLCK/(TCFG0[7:0]+1或TCFG0[15:8]+1)。

TCFG1寄存器:设定相应定时器为经过分频器出来的时钟频率的几分频。

定时器工作频率= PLCK/(TCFG0[7:0]或TCFG0[15:8]+1)/几分频

TCNTBn/TCMPBn寄存器:这两个寄存器都只用到位[15:0]。TCNTBn中保存定时器的初始计数值,TCMPBn中保存比较值。它们的在启动定时器时,被传到定时器内部寄存器TCNTn,TCMPn中。

TCNTOn寄存器:n为0~4,内部寄存器TCNTn在其工作时钟下不断减1计数,可以通过读取TCNTOn寄存器得知其值。

TCON寄存器:它的功能如下:A.第一次启动定时器时,手动将TCNTBn/TCMPBn寄存器的值装入内部寄存器TCNTn,TCMPn中。B.启动,停止定时器。C.决定在定时器计数到达0时是否自动装入初值。D.决定定时器的管脚TOUTn的输出电平是否反转。

三. WATCHDOG定时器

(1)工作原理:PLCK→2个8位预分频器(输出16分频,32分频,64分频,128分频或者外部时钟TCLK0/TCLK1)

初始计数值写入WTCNT→while(WTCNT==0)自动重新装载WTCNT=WTDAT,并可以产生中断信号,可以输出复位信号。WATDOG定时器工作频率=PCLK/(WTCON[15:8]+1)/几分频。大部分功能都在WTCON中设定

(2)其相关寄存器:

WTCON:用于设置预分频系数,选择工作频率,决定是否使能中断,是否启用WATDOG功能等,其寄存器具体操作可见数据手册。

WTDAT:用以决定WATCHDOG定时器的超时周期。

WTCNT:在启动WATDOG定时器前,必须往这个寄存器写入初始计数值,启动定时器后,它做减1操作,当计数器值达到0时,如果中断被使能的话,就发出中断,如果WATCHDOG功能被使能的话就发出复位信号,装载WTDAT寄存器的值并重新计数。

四. MPLL的定时器实验

首先启动MPLL,提高系统时钟,初始化存储控制器,使SDRAM工作在新的HCLK下,然后将定时器0设为0.5s产生一次,在中断程序里改变LED状态。

(1)设置/启动MPLL

clock_init函数用于设置MPLL,S3C2440一般输入时钟频率Fin为12MHZ,将FCLK,HCLK,PCLK分别设为200MHZ,100MHZ和50MHZ,三者比例为1:2:4。其中MPLLCON地址为0x4c000004,MDIV[19:12],PDIV[9:4],SDIV[1:0]。

代码如下:

#define S3C2440_MPLL_200MHZ ((0x5c<<12)|(0x01<<4)|(0x02)) /* * 对于MPLLCON寄存器,[19:12]为MDIV,[9:4]为PDIV,[1:0]为SDIV * 有如下计算公式: * S3C2440: MPLL(FCLK) = (2 * m * Fin)/(p * 2^s) * 其中: m = MDIV + 8, p = PDIV + 2, s = SDIV * 对于本开发板,Fin = 12MHz * 设置CLKDIVN,令分频比为:FCLK:HCLK:PCLK=1:2:4, * FCLK=200MHz,HCLK=100MHz,PCLK=50MHz */ void clock_init(void) { // LOCKTIME = 0x00ffffff; // 使用默认值即可 CLKDIVN = 0x03; // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1 /* 如果HDIVN非0,CPU的总线模式应该从“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" /* 写入控制寄存器 */ ); MPLLCON = S3C2440_MPLL_200MHZ; /* 现在,FCLK=200MHz,HCLK=100MHz,PCLK=50MHz */ }

(2)设置存储控制器

/* * 设置存储控制器以使用SDRAM */ void memsetup(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 }

(3)初始化定时器0

/* * Timer input clock Frequency = PCLK / {prescaler value+1} / {divider value} * {prescaler value} = 0~255 * {divider value} = 2, 4, 8, 16 * 本实验的Timer0的时钟频率=100MHz/(99+1)/(16)=62500Hz * 设置Timer0 0.5秒钟触发一次中断: */ void timer0_init(void) { TCFG0 = 99; // 预分频器0 = 99 TCFG1 = 0x03; // 选择16分频 TCNTB0 = 31250; // 0.5秒钟触发一次中断 TCON |= (1<<1); // 手动更新 TCON = 0x09; // 自动加载,清“手动更新”位,启动定时器0 }

(4)定时器中断

前面调用timer0_init函数后,定时器0就开始工作,调用init_irq函数使能定时器0中断,设置CPSR寄存器,开启IRQ中断后,每当定时器0计数达到0时就触发中断。

/* * 定时器0中断使能 */ void init_irq(void) { // 定时器0中断使能 INTMSK &= (~(1<<10)); }

(5)ISP定时器0计数到达时就调用其服务程序:

void Timer0_Handle(void) { /* * 每次中断令4个LED改变状态 */ if(INTOFFSET == 10) { GPBDAT = ~(GPBDAT & (0xf << 5)); } //清中断 SRCPND = 1 << INTOFFSET; INTPND = INTPND; }

定时器0的中断使用SRCPND,INTPND寄存器中的位10来表示,中断服务程序Timer0_Handle先判断是否定时器0的中断,若是则反转LED状态。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值