查看: 2719|回复: 3

OK6410 linux PWM驱动设计

[复制链接]
  • TA的每日心情

    2014-4-10 13:56
  • 签到天数: 5 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    发表于 2014-1-16 14:46:14 | 显示全部楼层 |阅读模式
    分享到:

    今天我为大家讲解下OK6410关于PWM蜂鸣器驱动的软件和硬件方面的一些知识点。首先我们为了写pwm的驱动程序我们先来了解下它的硬件电路和关于PWM的一些知识点。
    在飞凌嵌入式OK6410开发板中,蜂鸣器的IO口为GPF15
    图片7.png 图片8.png
    关于GPF15GPIO详细参考S3C6410PDF手册
    GPF14 [29:28] 00 = Input       01 = Output   10 = PWM TOUT[0]  11 = CLKOUT[0]
    GPF15 [31:30] 00 = Input        01 = Output  10 = PWM TOUT[1]  11 = Reserved
    下面我们重点介绍PWM定时器工作:
    S3C6410X中有5个定时器,这些定时器产生内部中断。其中,Timer0和Timer1具有PWM功能,而Timer2,3,4没有此功能。
    PWM具有两种操作模式:自动装载模式,一次触发模式。为实现PWM功能,芯片提供了16个功能寄存器。这些功能寄存器都连接APB总线。
    定时器具有双缓冲特性,这样就能在不停止当前定时器操作的情况下,为下次定时器运行装入新的数值。尽管为定时器设置了新数值,但当前的定时操作能够成功完成。定时器从TCNTBn读取的值是为下次延时定时用的,并不影响当前定时器的运行。当TCNTn减小到0的时候,TCNTBn的值会自动复制到TCNTn中,这就是说的自动装载操作。定时器的当前计数值可以从定时计数观察寄存器中TCNTOn读取。如果TCNTn为0且从装载也为0的话则TCNTn不在进行下次操作。
    在S3C6410的PDF手册中介绍PWM的工作流程:
    The Pulse Width Modulation function (PWM) uses the value of the TCMPBn register. 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 a PWM output.
    The TCNTBn and TCMPBn registers are double buffered to allow the timer parameters to be updated in the
    middle of a cycle. The new values will not take effect until the current timer cycle completes.
    A simple example of a PWM cycle is shown in the figure below.  
    file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps_clip_image-23666.png

    1.  Initialize the TCNTBn with 159(50+109) and the TCMPBn with 109.  
    2.  Start Timer by setting the start bit and manual update bit off.
    The TCNTBn value of 159 is loaded into the down-counter, the output is driven low.
    3.  When down-counter counts down to the value in the TCMPBn register 109,
    the output is changed from low to high
    4.  When the down-counter reaches 0, the interrupt request is generated.  
    5.  The down-counter is automatically reloaded with TCNTBn, which restarts the cycle.
    对于PWM 功能,要用到寄存器TCMPBn,当递减计数器down-counter的值和比较寄存器TCMPBn的值相同时,定时控制逻辑模块就会改变输出电平。因此比较寄存器TCMPBn决定了PWM的输出。
    定时器0工作寄存器:
    file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps_clip_image-2011.png
    file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps_clip_image-17848.png
    TCFG1 (Timer Configuration Register)
    file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps_clip_image-7879.png
    TCON (Timer Control Register)
    file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wps_clip_image-8913.png
    通过对上述内容的了解现在我们来写下PWM的驱动程序
    1 #include <linux/module.h>  
    2 #include <linux/kernel.h>  
    3 #include <linux/fs.h>  
    4 #include <linux/init.h>  
    5 #include <linux/delay.h>  
    6 #include <linux/poll.h>  
    7 #include <asm/irq.h>  
    8 #include <asm/io.h>  
    9 #include <linux/interrupt.h>  
    10 #include <asm/uaccess.h>  
    11 #include <mach/hardware.h>  
    12 #include <plat/regs-timer.h>  
    13 #include <mach/regs-irq.h>  
    14 #include <asm/mach/time.h>  
    15 #include <linux/clk.h>  
    16 #include <linux/cdev.h>  
    17 #include <linux/device.h>  
    18 #include <linux/miscdevice.h>  
    19   
    20 #include <mach/map.h>  
    21 #include <mach/regs-clock.h>  
    22 #include <mach/regs-gpio.h>  
    23   
    24 #include <plat/gpio-cfg.h>  
    25 #include <mach/gpio-bank-e.h>  
    26 #include <mach/gpio-bank-f.h>  
    27 #include <mach/gpio-bank-k.h>  
    28   
    29 #define DEVICE_NAME     "pwm"  
    30   
    31 #define PWM_IOCTL_SET_FREQ      1  
    32 #define PWM_IOCTL_STOP          0  
    33   
    34 static struct semaphore lock; //信号量  
    35   
    36 /* freq:  pclk/50/16/65536 ~ pclk/50/16   
    37   * if pclk = 50MHz, freq is 1Hz to 62500Hz  
    38   * human ear : 20Hz~ 20000Hz  
    39   */  
    40 static void PWM_Set_Freq( unsigned long freq )  
    41 {  
    42     unsigned long tcon;  
    43     unsigned long tcnt;  
    44     unsigned long tcfg1;  
    45     unsigned long tcfg0;  
    46   
    47     struct clk *clk_p;  
    48     unsigned long pclk;  
    49   
    50     unsigned tmp;  
    51   
    52     tmp = readl(S3C64XX_GPFCON);   
    53     //tmp &= ~(0x3U << 28);  
    54     //tmp |=  (0x2U << 28);  
    55   
    56          tmp &=~(0x3U << 30);  //GPF15  [31:30]    10 = PWM TOUT[1]   
    57          tmp |=  (0x2U << 30);  
    58   
    59     writel(tmp, S3C64XX_GPFCON);  
    60   
    61     tcon = __raw_readl(S3C_TCON);  //定时器控制寄存器  
    62     tcfg1 = __raw_readl(S3C_TCFG1); //时钟多路复用器和 DMA模式的选择  
    63     tcfg0 = __raw_readl(S3C_TCFG0); //时钟预定标器和死区结构  
    64   
    65     //prescaler = 50   Prescaler 0 [7:0] R/W Prescaler 0 value for timer 0 & 1   
    66     tcfg0 &= ~S3C_TCFG_PRESCALER0_MASK;  //{prescaler value} = 1~255   
    67     tcfg0 |= (50 - 1);  //prescaler value=50  
    68   
    69     //mux = 1/16  TCFG1  Divider MUX0 [3:0] R/W Select Mux input for PWM Timer 0   
    70     tcfg1 &= ~S3C_TCFG1_MUX0_MASK;  
    71     tcfg1 |= S3C_TCFG1_MUX0_DIV16; // 0100: 1/16   
    72   
    73     __raw_writel(tcfg1, S3C_TCFG1);  
    74     __raw_writel(tcfg0, S3C_TCFG0);  
    75   
    76   
    77 /* clk_get获取一个名为id的时针  
    78  * 输入参数dev:   可以为NULL  
    79  * 输入参数id:    时针名称,fclkhclkpclk等  
    80  * 返回值:        返回该时钟的clk结构体  
    81  *  
    82  *再将clk_get返回的clk结构体传递给clk_get_rate,获取该时钟的频率  
    83  */  
    84 /*PCLK is used for APB bus, which is used by the peripherals   
    85  *such as WDT, IIS, I2C, PWM timer, MMC */  
    86   
    87     clk_p = clk_get(NULL, "pclk");  //获取一个名为id的时针  
    88     pclk  = clk_get_rate(clk_p);  //获取该时钟的频率  
    89     //定时器输入时钟频率   
    90     //Timer input clock Frequency = PCLK / ( {prescaler value + 1} ) / {divider value}   
    91     tcnt  = (pclk/50/16)/freq;   
    92   
    93     __raw_writel(tcnt, S3C_TCNTB(0)); //TCNTB0:定时器0计数缓冲器。   
    94     __raw_writel(tcnt/2, S3C_TCMPB(0));//TCMPB0:定时器0比较缓冲寄存器。   
    95                  
    96     tcon &= ~0x1f;  
    97     tcon |= 0xb;        //disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0  
    98     __raw_writel(tcon, S3C_TCON);  
    99      
    100 /*Note: Manual update bit must be 1b0 before Start/Stop bit is 1b1.   
    101    If Manual update bit is 1b1 and Start/Stop bit is 1b1,   
    102    timer counter is not update by new value.   
    103    Timer counter value is last value. */  
    104      
    105     tcon &= ~2;         //clear manual update bit  
    106     __raw_writel(tcon, S3C_TCON);  
    107 }  
    108   
    109 void PWM_Stop( void )  
    110 {  
    111     unsigned tmp;  
    112     tmp = readl(S3C64XX_GPFCON);  
    113     //tmp &= ~(0x3U << 28);  
    114         tmp &= ~(0x3U << 30);  
    115   
    116     writel(tmp, S3C64XX_GPFCON);  
    117 }  
    118   
    119 /* 该函数尝试获得信号量sem,如果能够立刻获得,  
    120 它就获得该信号量并返回0,否则,返回非0值。  
    121 它不会导致调用者睡眠,可以在中断上下文使用。  
    122   
    123 */  
    124 static int s3c64xx_pwm_open(struct inode *inode, struct file *file)  
    125 {  
    126     if (!down_trylock(&lock))  
    127         return 0;  
    128     else  
    129         return -EBUSY;  
    130 }  
    131   
    132   
    133 static int s3c64xx_pwm_close(struct inode *inode, struct file *file)  
    134 {  
    135     up(&lock);  //该函数释放信号量sem,唤醒等待者  
    136     return 0;  
    137 }  
    138   
    139   
    140 static long s3c64xx_pwm_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)  
    141 {  
    142     switch (cmd) {  
    143         case PWM_IOCTL_SET_FREQ:  
    144             if (arg == 0)  
    145                 return -EINVAL;  
    146             PWM_Set_Freq(arg);  
    147             break;  
    148   
    149         case PWM_IOCTL_STOP:  
    150         default:  
    151             PWM_Stop();  
    152             break;  
    153     }  
    154   
    155     return 0;  
    156 }  
    157   
    158   
    159 static struct file_operations dev_fops = {  
    160     .owner          = THIS_MODULE,  
    161     .open           = s3c64xx_pwm_open,  
    162     .release        = s3c64xx_pwm_close,   
    163     .unlocked_ioctl = s3c64xx_pwm_ioctl,  
    164 };  
    165   
    166 static struct miscdevice misc = {  
    167     .minor = MISC_DYNAMIC_MINOR,  
    168     .name = DEVICE_NAME,  
    169     .fops = &dev_fops,  
    170 };  
    171   
    172 static int __init dev_init(void)  
    173 {  
    174     int ret;  
    175   
    176 /* 该函数用于初始化一个互斥锁,即它把信号量sem的值设置为1,  
    177 等同于sema_init (struct semaphore *sem, 1)*/  
    178     init_MUTEX(&lock);  
    179     ret = misc_register(&misc);  
    180   
    181     printk (DEVICE_NAME"\tinitialized\n");  
    182         return ret;  
    183 }  
    184   
    185 static void __exit dev_exit(void)  
    186 {  
    187     misc_deregister(&misc);  
    188 }  
    189   
    190 module_init(dev_init);  
    191 module_exit(dev_exit);  
    192 MODULE_LICENSE("GPL");  
    193 MODULE_AUTHOR("FORLINX Inc.");  
    194 MODULE_DESCRIPTION("S3C6410 PWM Driver");  

    回复

    使用道具 举报

  • TA的每日心情
    开心
    2015-11-30 08:37
  • 签到天数: 220 天

    连续签到: 1 天

    [LV.7]常住居民III

    发表于 2014-1-16 21:17:42 | 显示全部楼层
    linux难吗?           
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2023-12-3 18:51
  • 签到天数: 772 天

    连续签到: 1 天

    [LV.10]以坛为家III

    发表于 2014-1-17 08:06:24 | 显示全部楼层
    楼主真是牛人
    回复 支持 反对

    使用道具 举报

  • TA的每日心情

    2014-4-10 13:56
  • 签到天数: 5 天

    连续签到: 1 天

    [LV.2]偶尔看看I

     楼主| 发表于 2014-1-17 10:18:29 | 显示全部楼层
    ddllxxrr 发表于 2014-1-17 08:06
    楼主真是牛人

                                                                                             
    回复 支持 反对

    使用道具 举报

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

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-12-12 10:55 , Processed in 0.147270 second(s), 22 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.