之前在友善之臂论坛上发表了一篇文章 从GPB1输出PWM,我打算以后集中在CSDN上发,所以这算是复制了以前写的东西。
/* 因为需要输出两路PWM,示例代码中只写了从GPB0输出,也就是蜂鸣器。
折腾了好几天才知道怎么从GPB1也输出PWM。
在原先的mini2440_pwm.c里面
只需要修改PWM_Set_Freq( unsigned long freq )
和PWM_Stop( void )这两个函数就可以了 */
static void PWM_Set_Freq( unsigned long freq )
{
unsigned long tcon;
unsigned long tcnt;
unsigned long tcfg1;
unsigned long tcfg0;
struct clk *clk_p;
unsigned long pclk;
//set GPB0 as tout0, pwm output
/* s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_TOUT0); */
/*这里是设置pwm输出口的地方,之前一直不成功,有一方面原因就是只修改了这里*/
s3c2410_gpio_cfgpin(S3C2410_GPB1, S3C2410_GPB1_TOUT1);
tcon = __raw_readl(S3C2410_TCON);
tcfg1 = __raw_readl(S3C2410_TCFG1);
tcfg0 = __raw_readl(S3C2410_TCFG0);
//prescaler = 50
/*先前以为这里也要改,但是实际上是没有必要改的。
* 因为预分频寄存器(TCFG0)的0~7位是timer0和timer1共用的*/
tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;
tcfg0 |= (50 - 1);
//mux = 1/16
/* tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK; */
/* tcfg1 |= S3C2410_TCFG1_MUX0_DIV16; */
/*这里是比较关键的地方,因为分频寄存器中,timer0和timer1使用的数据位不一样
* 分频器的0~3位是给timer0用的, 4~7位是给timer1用的,所以对于timer1要左移4位
* #define S3C2410_TCFG1_MUX0_DIV16 (3<<0)
* #define S3C2410_TCFG1_MUX0_MASK (15<<0)
* #define S3C2410_TCFG1_MUX1_DIV16 (3<<4)
* #define S3C2410_TCFG1_MUX1_MASK (15<<4)
* 从它们的宏定义中就可以看出来了 */
tcfg1 &= ~S3C2410_TCFG1_MUX1_MASK;
tcfg1 |= S3C2410_TCFG1_MUX1_DIV16;
__raw_writel(tcfg1, S3C2410_TCFG1);
__raw_writel(tcfg0, S3C2410_TCFG0);
clk_p = clk_get(NULL, "pclk");
pclk = clk_get_rate(clk_p);
tcnt = (pclk/50/16)/freq;
__raw_writel(tcnt, S3C2410_TCNTB(1));
__raw_writel(tcnt/2, S3C2410_TCMPB(1));
/* tcon &= ~0x1f; */
/* tcon |= 0xb;
*disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0 */
/* __raw_writel(tcon, S3C2410_TCON); */
/* tcon &= ~2; //clear manual update bit */
/* __raw_writel(tcon, S3C2410_TCON); */
/*这里是最后一个关键的地方
*TCON是定时器位置控制寄存器,0~4位是timer0的,8~11位是timer1的
* 这就是为什么原来的代码是 tcon &= ~0x1f,而现在是tcon &= ~0x0f00
既然是位置控制寄存器,当然要说说它们的作用了:
对于timer0:
bit0:停止或开始timer0计时
bit1:手动更新TCNTB0和TCMPB0
bit2:TOUT0是否进行电平翻转
bit3:使用脉冲模式还是自动装载模式
bit4:是否使用死区功能
对于timer1:
8~11的功能分别对应了0~3
这就是我们这里用tcon |= 0x0b00的原因*/
tcon &= ~0x0f00;
tcon |= 0x0b00;
__raw_writel(tcon, S3C2410_TCON);
/*这里是我一直没有想明白的地方,为什么要把这一位清掉?还请知道的兄弟解答一下*/
/*后来有一位网友的回答:那个之所以要设置第二位为0是因为不需要更新TCNTB0 & TCMPB0的值*/
tcon &= ~0x0200;
__raw_writel(tcon, S3C2410_TCON);
}
void PWM_Stop( void )
{
/*这个里面的不用说了吧*/
/* s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_OUTP); */
s3c2410_gpio_cfgpin(S3C2410_GPB1, S3C2410_GPB1_OUTP);
/* s3c2410_gpio_setpin(S3C2410_GPB0, 0); */
s3c2410_gpio_setpin(S3C2410_GPB1, 0);
}
/* 改完这些你就可以用示波器在GPB1(也就是文档上说的CON4的32口)上看到波形了。
*关于PWM定时器方面的内容在光盘上的s3c2440a_um_rev014_040712.pdf里面都有介绍,可以参考一下 */