PWM Timer的使用---S3C2440的数据手册

OVERVIEW

       The S3C2410A has five 16-bit timers. Timer 0, 1, 2, and 3 have Pulse Width Modulation (PWM) function. Timer 4 has an internal timer only with no output pins. The timer 0 has a dead-zone generator, which is used with a large current device.

       The timer 0 and 1 share an 8-bit prescaler, while the timer 2, 3 and 4 share other 8-bit prescaler. Each timer has a clock divider which 5 different divided signals (1/2, 1/4, 1/8, 1/16, and TCLK). Each timer block receives its own clock signals from the clock divider, which receives the clock from the corresponding 8-bit prescaler. The 8-bit prescaler is programmable and divides the PCLK according to the loading value, which is stored in TCFG0 and TCFG1 registers.

       The timer count buffer register (TCNTBn) has an initial value which is loaded into the down-counter when the timer is enabled. The timer compare buffer register (TCMPBn) has an initial value which is loaded into the compare register to be compared with the down-counter value. This double buffering feature of TCNTBn and TCMPBn makes the timer generate a stable output when the frequency and duty ratio are changed.

       Each timer has its own 16-bit down counter, which is driven by the timer clock. When the down counter reaches zero, the timer interrupt request is generated to inform the CPU that the timer operation has been completed. When the timer counter reaches zero, the value of corresponding TCNTBn is automatically loaded into the down counter to continue the next operation. However, if the timer stops, for example, by clearing the timer enable bit of TCONn during the timer running mode, the value of TCNTBn will not be reloaded into the counter.

       The value of TCMPBn is used for pulse width modulation (PWM). The timer control logic changes the output level when the down-counter value matches the value of the compare register in the timer control logic. Therefore, the compare register determines the turn-on time (or turn-off time) of an PWM output.

[ARM学习笔记]S3c2410之PWM Timer - Fantity Wei - Footprints

 

       由S3C2410的技术手册和上面这幅结构图,我们来总结一下2410内部定时器模块的特性吧:

1)共5个16位的定时器,定时器0、1、2、3都带有脉冲宽度调制功能(PWM),定时器4是一个没有输出引脚的内部定时器,定时器0有一个用于大电流设备的死区生成器。

2)每个定时器都有一个比较缓存寄存器(TCMPB)和一个计数缓存寄存器(TCNTB);

3)定时器0、1共享一个8位的预分频器(预定标器),定时器2、3、4共享另一个8位的预分频器(预定标器),其值范围是0~255;

4)定时器0、1共享一个时钟分频器,定时器2、3、4共享另一个时钟分频器,这两个时钟分频器都能产生5种不同的分频信号值(即:1/2、1/4、1/8、1/16和TCLK);

5)两个8位的预分频器是可编程的且根据装载的值来对PCLK进行分频,装载的值分别存储在定时器配置寄存器TCFG0和TCFG1中;

6)有一个TCON控制寄存器控制着所有定时器的属性和状态,TCON的第0~7位控制着定时器0、第8~11位控制着定时器1、第12~15位控制着定时器2、第16~19位控制着定时器3、第20~22位控制着定时器4。

 

       还是根据S3C2440手册的描述和上图的结构,要开始一个PWM定时器功能的步骤如下(假设使用的是第一个定时器):

1)分别设置定时器0的预分频器值和时钟分频值,以供定时器0的比较缓存寄存器和计数缓存寄存器用;

2)设置比较缓存寄存器TCMPB0和计数缓存寄存器TCNTB0的初始值(即定时器0的输出时钟频率);

3)关闭定时器0的死区生成器(设置TCON的第4位);

4)开启定时器0的自动重载(设置TCON的第3位);

5)关闭定时器0的反相器(设置TCON的第2位);

6)开启定时器0的手动更新TCNTB0&TCMPB0功能(设置TCON的第1位);

7)启动定时器0(设置TCON的第0位);

8)清除定时器0的手动更新TCNTB0&TCMPB0功能(设置TCON的第1位)。

 

AUTO RELOAD & DOUBLE BUFFERING

       The timer value can be written into Timer Count Buffer register (TCNTBn) and the current counter value of the timer can be read from Timer Count Observation register (TCNTOn). If the TCNTBn is read, the read value does not indicate the current state of the counter but the reload value for the next timer duration.

       The auto reload operation copies the TCNTBn into TCNTn when the TCNTn reaches 0. The value, written into the TCNTBn, is loaded to the TCNTn only when the TCNTn reaches 0 and auto reload is enabled. If the TCNTn becomes 0 and the auto reload bit is 0, the TCNTn does not operate any further.

 

TIMER INITIALIZATION USING MANUAL UPDATE BIT AND INVERTER BIT

       An auto reload operation of the timer occurs when the down counter reaches 0. So, a starting value of the TCNTn has to be defined by the user in advance. In this case, the starting value has to be loaded by the manual update bit. The following steps describe how to start a timer:

1) Write the initial value into TCNTBn and TCMPBn.

2) Set the manual update bit of the corresponding timer. It is recommended that you configure the inverter on/off bit. (whether use inverter or not).

3) Set start bit of the corresponding timer to start the timer (and clear the manual update bit).

       If the timer is stopped by force, the TCNTn retains the counter value and is not reloaded from TCNTBn. If a new value has to be set, perform manual update.

      

NOTE

       Whenever TOUT inverter on/off bit is changed, the TOUTn logic value will also be changed whether the timer runs. Therefore, it is desirable that the inverter on/off bit is configured with the manual update bit.

 

OUTPUT LEVEL CONTROL

       The following procedure describes how to maintain TOUT as high or low (assume the inverter is off):

1)Turn off the auto reload bit. And then, TOUTn goes to high level and the timer is stopped after the TCNTn reaches0 (recommended).

2)Stop the timer by clearing the timer start/stop bit to 0. If TCNTn < TCMPn, the output level is high. If TCNTn>TCMPn, the output level is low.

3)The TOUTn can be inverted by the inverter on/off bit in TCON. The inverter removes the additional circuit to adjust the output level.

 

寄存器的设置步骤:

1)使能自动导入功能。设置TCNTBn和TCMPBn的值。设置自动更新位并且配置反转位(on/off)。手动更新位会将TCNTBn和TCMPBn的值分别导入TCNTn和TCMPn。然后,设置TCNTBn和TCMPBn的值以供下次自动导入的时候使用。

2)置位开始位,手动更新位设为0,置位自动更新位。

3)当TCNTn的值等于TCMPn时,TOUTn的值会反转。

4)当TCNTn为0时,中断请求产生并且TCNTBn的值被装载入临时寄存器。在下一个时间隙,TCNTn重新载入TCNTB的值。

5)在ISR中,TCNTn和TCMPBn的值被重新设置,以供下次自动导入时使用。

 

       参见如下驱动程序:

/*

 ================================================

 Name        : my2440_pwm.c

 Author      : Huang Gang

 Date        : 25/11/09

 Copyright   : GPL

 Description : my2440 pwm driver

 ================================================

 */

#include <linux/module.h>

#include <linux/init.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/errno.h>

#include <linux/clk.h>

#include <linux/device.h>

#include <asm/io.h>

#include <mach/hardware.h>

#include <mach/regs-gpio.h>

#include <plat/regs-timer.h>

 

#define PWM_MAJOR 0                  //主设备号

#define PWM_NAME "my2440_pwm"        //设备名称

 

 

 

static int device_major = PWM_MAJOR; //系统动态生成的主设备号

 

//打开设备

static int pwm_open(struct inode *inode, struct file *file)

{

    //对GPB0复用口进行复用功能设置,设置为TOUT0 PWM输出

    s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_TOUT0);

    return 0;

}

 

//关闭设备

static int pwm_close(struct inode *inode, struct file *file)

{

    return 0;

}

 

//对设备进行控制

static int pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)

 

 

{

    if(cmd <= 0)//如果输入的参数小于或等于0的话,就让蜂鸣器停止工作

    {

        //这里又恢复GPB0口为IO口输出功能,由原理图可知直接给低电平可让蜂鸣器停止工作

        s3c2410_gpio_cfgpin(S3C2410_GPB0, S3C2410_GPB0_OUTP);

        s3c2410_gpio_setpin(S3C2410_GPB0, 0);

    }

    else//如果输入的参数大于0,就让蜂鸣器开始工作,不同的参数,蜂鸣器的频率也不一样

    {

        //定义一些局部变量

        unsigned long tcon;

        unsigned long tcnt;

        unsigned long tcfg1;

        unsigned long tcfg0;

 

        struct clk *clk_p;

        unsigned long pclk;

 

        //以下对各寄存器的操作结合上面讲的开始一个PWM定时器的步骤和2440手册PWM寄存器操作部分来看就比较容易理解

        tcfg1 = __raw_readl(S3C2410_TCFG1);     //读取定时器配置寄存器1的值

        tcfg0 = __raw_readl(S3C2410_TCFG0);     //读取定时器配置寄存器0的值

 

        tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;

        tcfg0 |= (50 - 1);                      //设置tcfg0的值为49

 

        tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;

        tcfg1 |= S3C2410_TCFG1_MUX0_DIV16;      //设置tcfg1的值为0x0011即:1/16

 

        __raw_writel(tcfg1, S3C2410_TCFG1);     //将值tcfg1写入定时器配置寄存器1中

        __raw_writel(tcfg0, S3C2410_TCFG0);     //将值tcfg0写入定时器配置寄存器0中

 

        clk_p = clk_get(NULL, "pclk");

        pclk = clk_get_rate(clk_p);   //从系统平台时钟队列中获取pclk的时钟频率,在include/linux/clk.h中定义

        tcnt = (pclk/50/16)/cmd;      //计算定时器0的输出时钟频率(pclk/{prescaler0 + 1}/divider value)

 

        __raw_writel(tcnt, S3C2410_TCNTB(0));   //设置定时器0计数缓存寄存器的值

        __raw_writel(tcnt/2, S3C2410_TCMPB(0)); //设置定时器0比较缓存寄存器的值

 /*种TCNBn和TCMPBn 的双缓冲特点使得定时器在频率和占空比变化时输出的信号更加稳定。*/

        tcon = __raw_readl(S3C2410_TCON);       //读取定时器控制寄存器的值

                  

        tcon &= ~0x1f;

        tcon |= 0xb;  //关闭死区、自动重载、关反相器、更新TCNTB0&TCMPB0、启动定时器0

        __raw_writel(tcon, S3C2410_TCON);  //设置定时器控制寄存器的0-4位,即对定时器0进行控制

       

        tcon &= ~2;

        __raw_writel(tcon, S3C2410_TCON); //清除定时器0的手动更新位

    }

 

    return 0;

}

 

//设备操作结构体

static struct file_operations pwm_fops =

{

    .owner   = THIS_MODULE,

    .open    = pwm_open,

    .release = pwm_close,

    .ioctl   = pwm_ioctl,

};

 

//定义一个设备类

static struct class *pwm_class;

 

static int __init pwm_init(void)

{

    //注册为字符设备,主设备号为0让系统自动分配,设备名为my2440_pwm,注册成功返回动态生成的主设备号

    device_major = register_chrdev(PWM_MAJOR, PWM_NAME, &pwm_fops);

 

    if(device_major < 0)

    {

        printk(PWM_NAME " register falid!\n");

        return device_major;

    }

 

    //注册一个设备类,使mdev可以在/dev/目录下自动建立设备节点

    pwm_class = class_create(THIS_MODULE, PWM_NAME);

 

    if(IS_ERR(pwm_class))

    {

        printk(PWM_NAME " register class falid!\n");

        return -1;

    }

 

    //创建一个设备节点,设备名为PWM_NAME,即:my2440_pwm

    device_create(pwm_class, NULL, MKDEV(device_major, 0), NULL, PWM_NAME);

 

    return 0;

}

 

static void __exit pwm_exit(void)

{

    //注销设备

    unregister_chrdev(device_major, PWM_NAME);

 

    //删除设备节点

    device_destroy(pwm_class, MKDEV(device_major, 0));

 

    //注销设备类

    class_destroy(pwm_class);

}

 

module_init(pwm_init);

module_exit(pwm_exit);

 

MODULE_LICENSE("GPL");

MODULE_AUTHOR("Huang Gang");

MODULE_DESCRIPTION("my2440 pwm driver");

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值