zoukankan      html  css  js  c++  java
  • 驱动程序实例(三):蜂鸣器驱动程序(misc类设备驱动框架)

    结合之前对Linux内核的misc类设备驱动框架的分析 ,本文将编写基于misc类设备驱动框架的Buzzer设备的实例代码并对其进行分析。

    misc类设备驱动框架的分析,详见Linux字符设备驱动框架(三):Linux内核的misc类设备驱动框架

    硬件接口:

      CPU:s5pv210;

      Buzzer的GPIO:GPIO_D0_2 ;

      LED的工作方式:低电平停,高电平响。

    P.S.:无源蜂鸣器声音频率可控,一般采用PWM控制;有源蜂鸣器用普通IO控制方式即可。

    1. 驱动程序分析

    本代码取自,九鼎科技x210开发板的BSP。

    #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 <asm/irq.h>
    #include <asm/io.h>
    #include <linux/interrupt.h>
    #include <asm/uaccess.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>
    #include <linux/gpio.h>
    #include <plat/gpio-cfg.h>
    
    #define DEVICE_NAME     "buzzer"
    
    #define PWM_IOCTL_SET_FREQ        1
    #define PWM_IOCTL_STOP            0
    
    static struct semaphore lock;
    
    // TCFG0在Uboot中设置,这里不再重复设置
    // Timer0输入频率Finput=pclk/(prescaler1+1)/MUX1
    //                     =66M/16/16
    // TCFG0 = tcnt = (pclk/16/16)/freq;
    // PWM0输出频率Foutput =Finput/TCFG0= freq
    static void PWM_Set_Freq( unsigned long freq )
    {
        unsigned long tcon;
        unsigned long tcnt;
        unsigned long tcfg1;
    
        struct clk *clk_p;
        unsigned long pclk;
        
        //设置GPD0_2为PWM输出
        s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(2));
    
        tcon = __raw_readl(S3C2410_TCON);
        tcfg1 = __raw_readl(S3C2410_TCFG1);
    
        //mux = 1/16
        tcfg1 &= ~(0xf<<8);
        tcfg1 |= (0x4<<8);
        __raw_writel(tcfg1, S3C2410_TCFG1);
        
        clk_p = clk_get(NULL, "pclk");
        pclk  = clk_get_rate(clk_p);
    
        tcnt  = (pclk/16/16)/freq;
        
        __raw_writel(tcnt, S3C2410_TCNTB(2));
        __raw_writel(tcnt/2, S3C2410_TCMPB(2));//占空比为50%
    
        tcon &= ~(0xf<<12);
        tcon |= (0xb<<12);        //disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0
        __raw_writel(tcon, S3C2410_TCON);
        
        tcon &= ~(2<<12);         //clear manual update bit
        __raw_writel(tcon, S3C2410_TCON);
    }
    
    void PWM_Stop( void )
    {
        //将GPD0_2设置为input
        s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(0));    
    }
    
    static int x210_pwm_open(struct inode *inode, struct file *file)
    {
        if (!down_trylock(&lock))//上锁
            return 0;
        else
            return -EBUSY;
        
    }
    
    static int x210_pwm_close(struct inode *inode, struct file *file)
    {
        up(&lock);//解锁
        return 0;
    }
    
    // PWM:GPF14->PWM0
    static int x210_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
    {
        switch (cmd) 
        {
            case PWM_IOCTL_SET_FREQ:
                printk("PWM_IOCTL_SET_FREQ:
    ");
                if (arg == 0)
                    return -EINVAL;
                PWM_Set_Freq(arg);
                break;
    
            case PWM_IOCTL_STOP:
            default:
                printk("PWM_IOCTL_STOP:
    ");
                PWM_Stop();
                break;
        }
        return 0;
    }
    
    //buzzer设备操作集
    static struct file_operations dev_fops = {
        .owner   =   THIS_MODULE,
        .open    =   x210_pwm_open,
        .release =   x210_pwm_close, 
        .ioctl   =   x210_pwm_ioctl,
    };
    
    //misc类设备信息初始化
    static struct miscdevice misc = 
    {
        .minor = MISC_DYNAMIC_MINOR,
        .name = DEVICE_NAME,
        .fops = &dev_fops,
    };
    
    //buzzer设备注册
    static int __init dev_init(void)
    {
        int ret;
    
        init_MUTEX(&lock);           //上锁
        ret = misc_register(&misc);  //将buzzer注册到misc类下
        
        /* GPD0_2 (PWMTOUT2) */
        ret =
    gpio_request(S5PV210_GPD0(2), "GPD0"); //申请GPIO if(ret) printk("buzzer-x210: request gpio GPD0(2) fail"); s3c_gpio_setpull(S5PV210_GPD0(2), S3C_GPIO_PULL_UP);//将GPIO_D0_2设置为上拉 s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(1)); //将GPIO_D0_2设置为输出模式 gpio_set_value(S5PV210_GPD0(2), 0); //将GPIO_D0_2设置为低电平 printk ("x210 "DEVICE_NAME" initialized "); return ret; } //buzzer设备注销 static void __exit dev_exit(void) { init_MUTEX_LOCKED(&lock); //解锁 gpio_free(S5PV210_GPD0(2));//释放GPIO misc_deregister(&misc); //注销buzzer设备 } module_init(dev_init); module_exit(dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("www.9tripod.com"); MODULE_DESCRIPTION("x210 PWM Driver");

    2. 测试

    Buzzer驱动模块被装载进内核之后,可运行如下应用程序测试该驱动是否工作正常。若运行应用程序之后,蜂鸣器连响3声,则说明驱动工作正常。

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <sys/ioctl.h>
    
    #define FILE_BUZZER "/dev/buzzer"
    
    #define PWM_IOCTL_SET_FREQ        1
    #define PWM_IOCTL_STOP            0
    
    int main(void)
    {
        int fd = -1;
        int i = 0;
        
        fd = open(FILE_BUZZER, O_RDWR);
        if (fd < 0)
        {
            printf("open buzzer file error.
    ");
            return -1;
        }
        
        for (i=0; i<3; i++)
        {
            ioctl(fd, PWM_IOCTL_SET_FREQ, 10000);
            sleep(3);
            ioctl(fd, PWM_IOCTL_STOP);
            sleep(3);
        }
        
        return 0;
    }
  • 相关阅读:
    说明因 Active Directory 冲突导致的 NTDS 复制警告 ID 1083 和 1061 以及 SAM 错误 ID 12294
    KB817701:可用于解决帐户锁定问题的 Service Pack 和修复程序
    log check
    AD account 锁定问题
    VS.2005 中比较有用的快捷键
    用户帐户意外锁定, 以及 Windows Server 2003 中记录事件 ID 12294
    xp 重新安装MDAC
    [学习收藏]业界三种架构优缺点比较
    [收藏学习]Linux内核虚拟机 学习KVM架构及其优点
    两个命令:hdparm和iozone参数解释
  • 原文地址:https://www.cnblogs.com/linfeng-learning/p/9443740.html
Copyright © 2011-2022 走看看