首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

嵌入式Linux之我行——PWM在ARM Linux中的原理和蜂鸣器驱动实例开发(2)(转)

嵌入式Linux之我行——PWM在ARM Linux中的原理和蜂鸣器驱动实例开发(2)(转)

3. 编写合适开发板的蜂鸣器驱动程序,文件名:my2440_pwm.c

/*
================================================
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, unsignedlong 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比较缓存寄存器的值

        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("PGL");
MODULE_AUTHOR("Huang Gang");
MODULE_DESCRIPTION("my2440 pwm driver");


4. 将PWM蜂鸣器驱动代码部署到内核中。

#cp -f my2440_pwm.c /linux-2.6.30.4/drivers/char //把驱动源码复制到内核驱动的字符设备下

#gedit /linux-2.6.30.4/drivers/char/Kconfig //添加PWM蜂鸣器设备配置
config MY2440_PWM_BEEP
    tristate "My2440 PWM Beep Device"
    depends on ARCH_S3C2440
    default y
    ---help---
      My2440 PWM Beep


#gedit /linux-2.6.30.4/drivers/char/Makefile //添加PWM蜂鸣器设备配置
obj-$(CONFIG_MY2440_PWM_BEEP) += my2440_pwm.o

5.配置内核,选择PWM蜂鸣器设备选项

#make menuconfig
Device Drivers --->
    Character devices --->
        <*> My2440 PWM Beep Device (NEW)
继承事业,薪火相传
返回列表