驱动程序实例(三):蜂鸣器驱动程序(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:\r\n");
            if (arg == 0)
                return -EINVAL;
            PWM_Set_Freq(arg);
            break;

        case PWM_IOCTL_STOP:
        default:
            printk("PWM_IOCTL_STOP:\r\n");
            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\n"); 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.\n");
        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;
}

猜你喜欢

转载自www.cnblogs.com/linfeng-learning/p/9443740.html