[ZLG-ARM] s3c2410 Timer的工作原理

[复制链接]
 楼主| arminfo 发表于 2009-6-22 14:09 | 显示全部楼层 |阅读模式
&nbsp;&nbsp;&nbsp;s3c2410提供了5个16位的Timer(Timer0~Timer4),其中Timer0~Timer3支持Pulse&nbsp;Width&nbsp;Modulation——&nbsp;PWM(脉宽调制&nbsp;)。Timer4是一个内部定时器(internal&nbsp;timer),他没有输出引脚(output&nbsp;pins)。<br />&nbsp;&nbsp;&nbsp;&nbsp;下面是Timer的工作原理图。<br /><br /><br /><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;如上图所示,PCLK是Timer的信号源,我们通过设置每个Timer相应的Prescaler和Clock&nbsp;Divider把PCLK转换成输入时钟信号传送给各个Timer的逻辑控制单元(Control&nbsp;Logic),事实上每个Timer都有一个称为输入时钟频率(Timer&nbsp;input&nbsp;clock&nbsp;Frequency)的参数,这个频率就是通过PCLK,Prescaler和Clock&nbsp;Divider确定下来的,每个Timer&nbsp;的逻辑控制单元就是以这个频率在工作。下面给出输入时钟频率的公式:<i>Timer&nbsp;input&nbsp;clock&nbsp;Frequency&nbsp;=&nbsp;PCLK&nbsp;/&nbsp;{prescaler&nbsp;value+1}&nbsp;/&nbsp;{clock&nbsp;divider&nbsp;}<br />{prescaler&nbsp;value}&nbsp;=&nbsp;0~255<br />{&nbsp;clock&nbsp;divider&nbsp;}&nbsp;=&nbsp;2,&nbsp;4,&nbsp;8,&nbsp;16</i><br />&nbsp;&nbsp;&nbsp;&nbsp;然而并不是每一个Timer都有对应的Prescaler和Clock&nbsp;Divider,从上面的原理图我们可以看到Timer0,Timer1共用一对Prescaler和Clock&nbsp;Divider,Timer2,Timer3,Timer4共用另一对Prescaler和Clock&nbsp;Divider,s3c2410的整个时钟系统模块只存在两对Prescaler和Clock&nbsp;Divider。<br />&nbsp;&nbsp;&nbsp;&nbsp;我曾经在讨论watchdog的**中提到,watchdog也是一种定时器,他的工作就是在一个单位时间内对一个给定的数值进行递减和比较的操作,而我们这篇**讨论的定时器他的工作内容和watchdog在本质上是一样的。定时器在一个工作周期(Timer&nbsp;input&nbsp;clock&nbsp;cycle)内的具体工作内容主要有3个。分别是:<br /><br />对一个数值进行递减操作<br />把递减后的数值和另一个数值进行比较操作<br />产生中断或执行DMA操作<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在启用Timer之前我们会对Timer进行一系列初始化操作,这些操作包括上面提到的设置Prescaler和Clock&nbsp;Divider,其中还有一个非常重要的就是要给Timer两个数值,我们分别称之为Counter(变量,用于递减)和Comparer(定值,用于比较),Counter会被Timer&nbsp;加载到COUNT&nbsp;BUFFER&nbsp;REGISTER(TCNTB),而Comparer会被Timer&nbsp;加载到和COMPARE&nbsp;BUFFER&nbsp;REGISTER(TCMPB),每个Timer都有这样两个寄存器。当我们设置完毕启动Timer之后,Timer在一个工作周期内所做的就是先把TCNTB中的数值(Counter)减1,之后把TCNTB中的数值和TCMPB中的数值(Comparer)进行对比,若Counter已经被递减到等于Comparer,发生计数超出,则Timer产生中断信号(或是执行DMA操作)并自动把Counter重新装入TCNTB(刷新TCNTB以重新进行递减)。以上就是Timer的工作原理。<br />下面我们结合代码具体说明如何对Timer0进行初始化并开启它。<br />首先我假设我的PCLK是50700000Hz<br />//&nbsp;define&nbsp;Timer&nbsp;register<br />#define&nbsp;rTCFG0&nbsp;(*(volatile&nbsp;unsigned&nbsp;int&nbsp;*)0x51000000)<br />#define&nbsp;rTCFG1&nbsp;(*(volatile&nbsp;unsigned&nbsp;int&nbsp;*)0x51000004)<br />#define&nbsp;rTCON&nbsp;(*(volatile&nbsp;unsigned&nbsp;int&nbsp;*)0x51000008)<br />#define&nbsp;rTCNTB0&nbsp;(*(volatile&nbsp;unsigned&nbsp;int&nbsp;*)0x5100000C)<br />#define&nbsp;rTCMPB0&nbsp;(*(volatile&nbsp;unsigned&nbsp;int&nbsp;*)0x51000010)<br />#define&nbsp;rTCNTO0&nbsp;(*(volatile&nbsp;unsigned&nbsp;int&nbsp;*)0x51000014)<br />#define&nbsp;rTCNTB1&nbsp;(*(volatile&nbsp;unsigned&nbsp;int&nbsp;*)0x51000018)<br />#define&nbsp;rTCMPB1&nbsp;(*(volatile&nbsp;unsigned&nbsp;int&nbsp;*)0x5100001C)<br />#define&nbsp;rTCNTO1&nbsp;(*(volatile&nbsp;unsigned&nbsp;int&nbsp;*)0x51000020)<br />#define&nbsp;rTCNTB2&nbsp;(*(volatile&nbsp;unsigned&nbsp;int&nbsp;*)0x51000024)<br />#define&nbsp;rTCMPB2&nbsp;(*(volatile&nbsp;unsigned&nbsp;int&nbsp;*)0x51000028)<br />#define&nbsp;rTCNTO2&nbsp;(*(volatile&nbsp;unsigned&nbsp;int&nbsp;*)0x5100002C)<br />#define&nbsp;rTCNTB3&nbsp;(*(volatile&nbsp;unsigned&nbsp;int&nbsp;*)0x51000030)<br />#define&nbsp;rTCMPB3&nbsp;(*(volatile&nbsp;unsigned&nbsp;int&nbsp;*)0x51000034)<br />#define&nbsp;rTCNTO3&nbsp;(*(volatile&nbsp;unsigned&nbsp;int&nbsp;*)0x51000038)<br />#define&nbsp;rTCNTB4&nbsp;(*(volatile&nbsp;unsigned&nbsp;int&nbsp;*)0x5100003C)<br />#define&nbsp;rTCNTO4&nbsp;(*(volatile&nbsp;unsigned&nbsp;int&nbsp;*)0x51000040)<br />void&nbsp;timer0_config()<br />{<br />/*<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Timer0的prescaler由rTCFG0&nbsp;的&nbsp;0~7&nbsp;bit决定<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Prescaler=119<br />*/<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rTCFG0=119&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />/*<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Timer0的divider&nbsp;value由TCFG1的&nbsp;0~3&nbsp;bit决定,设置为3表示divider&nbsp;value&nbsp;=&nbsp;1/16<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rTCFG1的第20~23bit用于决定Timer计数超出后所采取的响应,我们使用了中断模式(20~23bit全部为0),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;即计数超出后产生中断<br />*/<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rTCFG1=3;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rTCNTB0=26406;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rTCMPB0=0;<br />}<br />由于我们的PCLK是50700000Hz,&nbsp;根据Timer&nbsp;input&nbsp;clock&nbsp;Frequency的计算公式我们如下计算Timer0的时钟输入频率:<br /><br /><i>prescaler&nbsp;value&nbsp;=&nbsp;119<br />divider&nbsp;value&nbsp;=&nbsp;1/16<br />PCLK=&nbsp;50700000<br />Timer&nbsp;input&nbsp;clock&nbsp;Frequency&nbsp;=50700000/&nbsp;(119+1)/(1/16)=26406<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;</i>也就是说通过设置prescaler和divider&nbsp;value之后,Timer0的工作频率为26406,也就是说一秒内Timer0会进行26406次递减和比较操作,假设我们现在是要让Timer0每1秒产生一次中断的话,我们应该设置Counter=26406和Camparer=0,既:<br /><br />rTCNTB0=26406;<br />rTCMPB0=0;<br /><br />如果我们要让Timer0每0.5秒产生一次中断,则我们应该设置Counter=26406/2和Camparer=0,既:<br /><br />rTCNTB0=13203;<br />rTCMPB0=0;<br /><br />如果我们要让Timer0每0.25秒产生一次中断,则我们应该设置Counter=26406/4和Camparer=0,既:<br /><br />rTCNTB0=6601;<br />rTCMPB0=0;<br /><br />初始化完Timer后我们要开启它。<br /><br />void&nbsp;timer0_start()<br />{<br />/*<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Update&nbsp;TCNTB0&nbsp;&&nbsp;TCMPB0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rTCON寄存器的第1位是刷新Timer0的COUNT&nbsp;BUFFER&nbsp;REGISTER(TCNTB)和<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;COMPARE&nbsp;BUFFER&nbsp;REGISTER(TCMPB),由于是第一次加载Counter和Comparer,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;所以我们需要手动刷新它们<br />*/<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rTCON|=1&lt&lt1;<br />/*<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;置rTCON第0位为1,开启Timer0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;把rTCON第1位置为0,停止刷新TCNTB0&nbsp;和&nbsp;TCMPB0<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;置rTCON第3位为1,设置Counter的加载模式为自动加载(auto&nbsp;reload),这样每当<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Timer计数超出之后(此时TCNTB的值等于TCMPB的值),Timer会自动把原来我们给<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;定的Counter重新加载到TCNTB中<br />*/<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rTCON=0x09;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />}<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;要使你的Timer能够正常的工作,除了调用timer0_config()和timer0_start()之外,我们还必须设置Timer的中断服务例程并取消对Timer的中断的屏蔽.这些操作可以参考&lt&lts3c2410&nbsp;中断异常处理&gt&gt一文.<br /><br />&nbsp;<br /> &nbsp;&nbsp;<br />
miclinux 发表于 2009-6-24 13:13 | 显示全部楼层

  
tmake 发表于 2009-7-2 18:37 | 显示全部楼层

搞操作系统否

  
lpc2410 发表于 2009-7-3 12:56 | 显示全部楼层

定时器嘛,应该都一样的咯

  
您需要登录后才可以回帖 登录 | 注册

本版积分规则

17

主题

47

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部