mini2440驱动学习第五课————PWM

来源:互联网 发布:格斗漫画知乎 编辑:程序博客网 时间:2024/04/28 02:42

    转眼间,已到了mini2440厂家友善所提供驱动的最后一课——PWM。本驱动用定时器0产生PWM脉冲信号,GPB0管脚输出接蜂鸣器。贴出代码如下:

 

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>

 

#include <asm/irq.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <plat/regs-timer.h>
#include <mach/regs-irq.h>
#include <asm/mach/time.h>
#include <linux/clk.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/miscdevice.h>

 

#define DEVICE_NAME     "pwm"                  //设备名

 

#define PWM_IOCTL_SET_FREQ  1               //宏变量,用于ioctl的cmd变量
#define PWM_IOCTL_STOP   0

 

static struct semaphore lock;                      //定义信号量 lock

 

/* freq:  pclk/50/16/65536 ~ pclk/50/16
  * if pclk = 50MHz, freq is 1Hz to 62500Hz
  * human ear : 20Hz~ 20000Hz
  */
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;

 

       s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPB0_TOUT0);

                                                                       //设置GPB0为PWM输出模式

       tcon  = __raw_readl(S3C2410_TCON);            //读取寄存器TCON
       tcfg1 = __raw_readl(S3C2410_TCFG1);           //读取寄存取TCFG1
       tcfg0 = __raw_readl(S3C2410_TCFG0);           //读取寄存器TCFG0

 

       tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;   //预分频掩码:TCFG0[0:7]

       tcfg0 |= (50 - 1);                                            //设置50分频

 

       tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;        //分割值掩码:TCFG1[0:3]
       tcfg1 |= S3C2410_TCFG1_MUX0_DIV16;          //定时器0进行16分割

 

       __raw_writel(tcfg1, S3C2410_TCFG1);             //把设置后数据存入TCFG1、TCFG0
       __raw_writel(tcfg0, S3C2410_TCFG0);

 

       clk_p = clk_get(NULL, "pclk");                       

       pclk  = clk_get_rate(clk_p);               //得到pclk时钟
       tcnt  = (pclk/50/16)/freq;                  //得到定时器初始化数值大小
 
       __raw_writel(tcnt, S3C2410_TCNTB(0));          //tcnt写入到定时器计数寄存器
       __raw_writel(tcnt/2, S3C2410_TCMPB(0));       //占空比50%
    
       tcon &= ~0x1f;          //disable deadzone, auto-reload, inv-off, 

       tcon |= 0xb;              //update TCNTB0&TCMPB0, start timer 0                           

       __raw_writel(tcon, S3C2410_TCON);
 
       tcon &= ~2;              //clear manual update bit
       __raw_writel(tcon, S3C2410_TCON);
}

 

static void PWM_Stop(void)
{               //设置GPB0的为输出模式,并输出低电平,停止PWM工作
     s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPIO_OUTPUT);           

     s3c2410_gpio_setpin(S3C2410_GPB(0), 0); 
}

 

static int s3c24xx_pwm_open(struct inode *inode, struct file *file)
{
     if (!down_trylock(&lock))        //信号量-1,若成功,down_trylock(&lock)返回0
          return 0;
     else
          return -EBUSY;
}


static int s3c24xx_pwm_close(struct inode *inode, struct file *file)
{
     PWM_Stop();              //调用Stop函数,停用PWM
     up(&lock);                  //释放信号量
     return 0;
}


static int s3c24xx_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{                //根据不同的cmd,选择相应的操作              
     switch (cmd)

     {
          case PWM_IOCTL_SET_FREQ: if (arg == 0)
                                                        return -EINVAL;
                                                    PWM_Set_Freq(arg);
                                                    break;

 

          case PWM_IOCTL_STOP:  PWM_Stop();
                                              break;
      }

      return 0;
}


static struct file_operations dev_fops =

{                 //本驱动所提供给上层的funtion
    .owner   =   THIS_MODULE,
    .open    =   s3c24xx_pwm_open,
    .release =   s3c24xx_pwm_close,
    .ioctl   =   s3c24xx_pwm_ioctl,
};

 

static struct miscdevice misc =

{
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &dev_fops,
};

 

static int __init dev_init(void)
{
     int ret;

     init_MUTEX(&lock);                 //初始化互斥锁
     ret = misc_register(&misc);     //注册mise设备

     printk (DEVICE_NAME"/tinitialized/n");
     return ret;
}

 

static void __exit dev_exit(void)
{
      misc_deregister(&misc);          //注销mise设备
}

 

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");
MODULE_DESCRIPTION("S3C2410/S3C2440 PWM Driver");

 

结语:

     通过这一系列驱动的研读和测试,我对Linxu驱动这一块有了更深刻的认识,或许例子比较简单、肤浅,但想必这样更符合初学者的水平,为我们广大的初学者提供了一个很好的切入点。本系列文章不足的地方有很多,个人感觉最不好的就是没有贴出上层应用程序出来与大家分享,看一下上层的应用程序究竟是怎么调用底层驱动所提供的功能。(还是因为自己有点懒~~)  若有需要查看应用程序代码的,随时与鄙人联系,乐意共同研究。

     由于本系列的驱动学习是一课紧接一课的,所以在程序的注释上,后面的课程就没有重复前面已经讲解过的,如果有朋友看到不明白的地方,可以翻翻鄙人前面同一个专题的博文,若还没找到满意的答案,欢迎与鄙人取得联系,共同学习进步!

 

原创粉丝点击