||
S3C2440的定时器设置
S3C2440有5个16位定时器,定时器0-3有PWM功能,定时器4有一个没有输出引脚的内部定时器,定时器0有一个用于大电流设备的死区生成器。
有2个8位预分频器和2个4位分频器。定时器0和定时器1共用一个8位预分频器。定时器2,定时器3,定时器4共用另一个8位预分频器。
定时器的时钟源是PCLK,首先经过预分频器降低频率后,进入第二个分频,可以生成5种不同的分频信号(1/2,1/4,1/8,1/16和TCLK)。其中TCLK0,TCLK1是S3C2440的外部时钟信号输入管脚。
所有定时器都是递减计数。
预分频器值(prescaler value)在TCFG0(0X5100 0000)中设置,0-7位设置TIMER0,TIMER1的预分频值。8-15位设置TIMER2,3,4的预分频值。
第二个分频器的值(divider value)在TCFG1(0X5100 0004)中设置
,0-3位设置TIMER0的分频值,4-7位设置TIMER1的分频值,8-11位设置TIMER2的分频值
,12-15位设置TIMER3的分频值,16-19位设置TIMER4的分频值。
定时器输入频率的计算公式是:
Timer input clock Frequency = PCLK / {prescaler value+1} / {divider value}
{prescaler value} = 0~255
{divider value} = 2, 4, 8, 16
TCON(0X5100 0008)是5个定时器的控制位寄存器,以TIMER0为例:
0-4位是TIMER0的控制位
Bit0:停止或开始TIMER0计时
Bit1:手动更新TCNTB0和TCMPB0
Bit2:TOUT0逻辑电平是否翻转
Bit3:0-一次性脉冲模式,1-自动装载模式
Bit4:使能或禁止死区功能
Timer0-3各有TCNTBn,TCNTn,TCMPBn,TCMPn,TCNTOn共5个寄存器,其中TCNTn,TCMPn是内部寄存器,没有对应的地址,通过读TCNTOn的值可以得到TCNTn的值。当定时器计数到0,TCNTBn和TCMPBn的值装入TCNTn和TCMPn,如果中断使能,同时产生中断。在计数过程中,TCNTBn和TCMPBn的值是不变的,变的是TCNTn的值。Timer0-3各有一个对应的输出脚TOUT0-3。
Timer4有TCNTB4,TCNT4,TCNTO4共3个寄存器,其中TCNT4是内部寄存器,Timer4没有对应的输出脚。
按下面的步骤启动一个定时器:
1、 初始化TCNTBn和TCMPBn。
2、 把相应定时器的手动更新位置1,不管是否使用TOUTn极性转换功能,推荐都配置一下极性转换功能位。
3、 设置相应定时器的启动位启动定时器,同时清除手动更新位。
不管定时器是否运行,只要TOUTn极性转换位改变,TOUTn逻辑电平也会变改变。所以推荐极性转换位和人工加载位一起设置。
定时器运行时,当TCNTn等于TCMPBn时,TOUTn输出的电平会翻转,而当TCNTn减为0时,产生中断,TOUTn的电平又会翻转过来。
S3C2440的datasheet里举了一个例子,很好的说明的定时器的工作过程,过程图如下:
1、使能自动装载功能,TCNTBn设为160(50+110),TCMPBn设为110,置为手动更新标志,把TCNTBn,TCMPBn的值装入TCNTn,TCMPn。TOUTn翻转功能关闭,然后把TCNTBn设为80(40+40),TCMPBn设为40,这是为下一次装载设置的。
2、使能定时器开始计时位,清零手动更新位,定时器开始向下计时。
3、当TCNTn的值和TCMPn的值相等时,TOUTn从低变高。
4、当TCNTn等于0时,产生中断,TCNTBn,TCMPBn重新装载进TCNTn,TCMPn,这次的值是80和40,TOUTn从高变低。
5、在定时器中断程序中,TCNTBn和TCMPBn的值设置成80(20+60)和60,这是为下一次装载准备的。
6、当TCNTn的值和TCMPn相等时,TOUTn从低变高。
7、当TCNTn等于0,产生中断,TCNTBn,TCMPBn重新装载进TCNTn,TCMPn,这次的值是80和60.
8、在定时器中断程序中,关闭定时器自动装载和中断功能。
9、当TCNTn的值和TCMPn相等时,TOUTn从低变高。
10、TCNTn等于0,TCNTn不在自动装载,定时器停止计时。
11、不会有中断产生。
经过测试:使用外部时钟输入时输入给定时器的频率信号仅与外部时钟的频率有关,与预分频寄存器的值无关。输出任意频率PWM方波代码如下:
void start_timer2(void)
{
ulong val, tmp, m, n;
val = __raw_readl(S3C2410_TCFG0) & (~S3C2410_TCFG_PRESCALER1_MASK);
val |= (0 << S3C2410_TCFG_PRESCALER1_SHIFT);
__raw_writel(val, S3C2410_TCFG0);
tmp = __raw_readl(S3C2410_TCFG0);
printk("S3C2410_TCFG0=0x%x\n", tmp);
/*设置分频寄存器的值*/
val = __raw_readl(S3C2410_TCFG1) & (~S3C2410_TCFG1_MUX2_MASK);
// val |= S3C2410_TCFG1_MUX2_TCLK1;
val |= S3C2410_TCFG1_MUX2_DIV16;
__raw_writel(val, S3C2410_TCFG1);
tmp = __raw_readl(S3C2410_TCFG1);
printk("S3C2410_TCFG1=0x%x\n", tmp);
/*使能自动重载位*/
tmp = __raw_readl(S3C2410_TCON);
val = __raw_readl(S3C2410_TCON) & (~(0xf << 12));
val |= S3C2410_TCON_T2RELOAD;
__raw_writel(val, S3C2410_TCON);
tmp = __raw_readl(S3C2410_TCON);
printk("S3C2410_TCON22222222222=0x%x\n", tmp);
/*设置TCNTB和TCMPB寄存器*/
// __raw_writel(TIMERCOUNT, S3C2410_TCNTB(2));
// __raw_writel(TIMERCOUNT / 4, S3C2410_TCMPB(2));
__raw_writel(799, S3C2410_TCNTB(2));
__raw_writel(399, S3C2410_TCMPB(2));
m = __raw_readl(S3C2410_TCNTB(2));
n = __raw_readl(S3C2410_TCMPB(2));
printk("S3C2410_TCNTB=0x%x,S3C2410_TCMPB=0x%x\n", m, n);
/*置位手动更新位*/
val = __raw_readl(S3C2410_TCON) & (~(0x3 << 12));
val |= S3C2410_TCON_T2MANUALUPD;
__raw_writel(val, S3C2410_TCON);
tmp = __raw_readl(S3C2410_TCON);
printk("S3C2410_TCON--S3C2410_TCON_T3MANUALUPD=0x%x\n", tmp);
/*设置起始位置位反相位,关闭手动更新位*/
val = __raw_readl(S3C2410_TCON) & (~(0x3 << 12));
val |= (S3C2410_TCON_T2START | S3C2410_TCON_T2INVERT);
// val |= S3C2410_TCON_T2START;
tmp = __raw_writel(val, S3C2410_TCON);
printk("S3C2410_TCON========0x%x\n", tmp);
}
在初始化函数中s3c2410_gpio_cfgpin(S3C2410_GPB2, S3C2410_GPB2_TOUT2);
s3c2410_gpio_pullup(S3C2410_GPB2, 0);
通过设置TCNTB和TCMPB寄存器得到PWM,前者调整频率(pclk经过预分频、分频之后除以(TCNTB+1)即可得到输出频率)。后者调整占空比(当TCNTB的值和TCMPB相等时就会翻转)。