在学校的时候裸跑了2410的PWM来控制直流电动机,之后来到公司先后写了蜂鸣器(有点儿简单,都不好意思提)、CC1100定时器部分的WIN CE驱动,算是对2410的TIMER有所了解了,现记录如下:
在写之前请各位先看一下我转蔡于清老师的一篇文章,因为我也是在看了这篇文章之后才着手弄s3c2410 Timer的,但一定要抱着试试看的态度阅读哟,不然小心中招。嘻嘻^_^先卖个关子。
作者:蔡于清
s3c2410提供了5个
16位的Timer(Timer0~Timer4),其中Timer0~Timer3支持Pulse Width Modulation—— PWM(脉宽调制 )。Timer4是一个内部定时器(internal timer),他没有输出引脚(output pins)。
下面是Timer的工作原理图。
[attach]60[/attach]
如上图所示,
PCLK是Timer的信号源,我们通过设置每个Timer相应的
Prescaler和
Clock Divider把PCLK转换成输入时钟信号传送给各个Timer的逻辑控制单元(Control Logic),事实上每个Timer都有一个称为
输入时钟频率(Timer input clock Frequency)的参数,这个频率就是通过
PCLK,Prescaler和Clock Divider确定下来的,每个Timer 的逻辑控制单元就是以这个频率在工作。下面给出输入时钟频率的公式:
Timer input clock Frequency = PCLK / {prescaler value+1} / {clock divider }
{prescaler value} = 0~255
{ clock divider } = 2, 4, 8, 16然而并不是每一个Timer都有对应的Prescaler和Clock Divider,从上面的原理图我们可以看到Timer0,Timer1共用一对Prescaler和Clock Divider,Timer2,Timer3,Timer4共用另一对Prescaler和Clock Divider,s3c2410的整个时钟系统模块只存在两对Prescaler和Clock Divider。
我曾经在讨论watchdog的文章中提到,watchdog也是一种定时器,他的工作就是在一个单位时间内对一个给定的数值进行递减和比较的操作,而我们这篇文章讨论的定时器他的工作内容和watchdog在本质上是一样的。定时器在一个
工作周期(Timer input clock cycle)内的具体工作内容主要有3个。分别是:
1. 对一个数值进行递减操作
2. 把递减后的数值和另一个数值进行比较操作
3. 产生中断或执行DMA操作在启用Timer之前我们会对Timer进行一系列初始化操作,这些操作包括上面提到的设置Prescaler和Clock Divider,其中还有一个非常重要的就是要给Timer两个数值,我们分别称之为
Counter(变量,用于递减)和
Comparer(定值,用于比较),Counter会被Timer 加载到
COUNT BUFFER REGISTER(TCNTB),而Comparer会被Timer 加载到和
COMPARE BUFFER REGISTER(TCMPB),每个Timer都有这样两个寄存器。当我们设置完毕启动Timer之后,Timer在一个工作周期内所做的就是先把
TCNTB中的数值(Counter)减1,之后把
TCNTB中的数值和
TCMPB中的数值(Comparer)进行对比,若Counter已经被递减到
等于Comparer,
发生计数超出,则Timer产生中断信号(或是执行DMA操作)
并自动把Counter重新装入TCNTB(刷新TCNTB以重新进行递减)。以上就是Timer的工作原理。
下面我们结合代码具体说明如何对Timer0进行初始化并开启它。
首先我假设我的PCLK是50700000Hz
// define Timer register
#define rTCFG0 (*(volatile unsigned int *)0x51000000)
#define rTCFG1 (*(volatile unsigned int *)0x51000004)
#define rTCON (*(volatile unsigned int *)0x51000008)
#define rTCNTB0 (*(volatile unsigned int *)0x5100000C)
#define rTCMPB0 (*(volatile unsigned int *)0x51000010)
#define rTCNTO0 (*(volatile unsigned int *)0x51000014)
#define rTCNTB1 (*(volatile unsigned int *)0x51000018)
#define rTCMPB1 (*(volatile unsigned int *)0x5100001C)
#define rTCNTO1 (*(volatile unsigned int *)0x51000020)
#define rTCNTB2 (*(volatile unsigned int *)0x51000024)
#define rTCMPB2 (*(volatile unsigned int *)0x51000028)
#define rTCNTO2 (*(volatile unsigned int *)0x5100002C)
#define rTCNTB3 (*(volatile unsigned int *)0x51000030)
#define rTCMPB3 (*(volatile unsigned int *)0x51000034)
#define rTCNTO3 (*(volatile unsigned int *)0x51000038)
#define rTCNTB4 (*(volatile unsigned int *)0x5100003C)
#define rTCNTO4 (*(volatile unsigned int *)0x51000040)void timer0_config()
{ rTCFG0=119 rTCFG1=3;
rTCNTB0=26406;
rTCMPB0=0;
}由于我们的
PCLK是50700000Hz, 根据Timer input clock Frequency的计算公式我们如下计算Timer0的时钟输入频率:
prescaler value = 119
divider value = 1/16
PCLK= 50700000
Timer input clock Frequency =50700000/ (119+1)/(1/16)=
26406也就是说通过设置prescaler和divider value之后,
Timer0的工作频率为26406,也就是说一秒内Timer0会进行26406次递减和比较操作,假设我们现在是要让Timer0每1秒产生一次中断的话,我们应该设置Counter=26406和Camparer=0,既:
rTCNTB0=
26406;
rTCMPB0=
0;
如果我们要让Timer0每0.5秒产生一次中断,则我们应该设置Counter=26406/2和Camparer=0,既:
rTCNTB0=
13203;
rTCMPB0=
0;
如果我们要让Timer0每0.25秒产生一次中断,则我们应该设置Counter=26406/4和Camparer=0,既:
rTCNTB0=
6601;
rTCMPB0=
0;
初始化完Timer后我们要开启它。
void timer0_start()
{ rTCON|=1<<1; rTCON=0x09;
}要使你的Timer能够正常的工作,除了调用timer0_config()和timer0_start()之外,我们还必须设置Timer的中断服务例程并取消对Timer的中断的屏蔽.这些操作可以参考<<>>一文.
另外提供的源代码中有更为详细的代码,需要的朋友可以参考,关于Timer的代码存在于timer.c timer.h文件中
If the timer is stopped by force, the TCNTn retains the counter value and is not reloaded from TCNTBn. If a newvalue has to be set, perform manual update.
其实转载的文章已经说明的够清楚了,此处没必要浪费各位看官的时间。下面说一下关于WIN CE的问题
在WIN CE 下我想添加自定义的用户中断,于是要实现的就是ISR代码(在ARMINIT。C中),中断的使能,禁能和DONE代码(在CFW。C中),我以为清中继的代码可以在ISR中也可以在中断的DONE中,但是实践证明如果注释掉ISR中的清中断代码的话中断会一直执行,不知这是何故,按理来说就是出问题也不应该是这样的问题啊,可能是哪儿里又没有注意到,我经常犯各种各样希奇古怪的错误,有时候想一想自己都乐,比如把“&”写成了“|”、在一句之后没有加分号等等等,数之不盛数啊,真叫人发愁,成不了大气,哎!!!