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");

 

發佈了49 篇原創文章 · 獲贊 30 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章