您的位置:首页 > 运维架构 > Linux

[S5PV210 Linux字符驱动之PWM蜂鸣器驱动

2016-08-31 09:16 337 查看
在SMDK210.C中添加如下beeper_device 结构体

static struct platform_device beeper_device = {
.name = "pwm_buzzer",
.id         =  1,
.dev = {
.parent
= &s3c_device_timer[1].dev, //PWM1是定时器1
.platform_data
= 0,
},

};

然后在smdkv210_devices中添加该结构体

static struct platform_device *smdkv210_devices[] __initdata = {

&s3c_device_fb,
&s3c_device_adc,
&s3c_device_cfcon,
&s3c_device_hsmmc0,
&s3c_device_hsmmc1,
&s3c_device_hsmmc2,
&s3c_device_hsmmc3,
&s3c_device_i2c0,
&s3c_device_i2c1,
&s3c_device_i2c2,

&s3c_device_timer[1], //add
&beeper_device, //add

}

一、硬件分析
对蜂鸣器的操作,主要是通过S5PV210的PWM来实现的,因为在OK210上,连接的是一个无源蜂鸣器,必须通过外部的驱动信号,才能控制其的“鸣叫”。
首先从OK210的底板原理图中可知,OK210开发板上的蜂鸣器连接通过一个三极管组成的放大电路连接到了核心板的XpwmTOUT[1]引脚上,如下图所示:



而XpwmTOUT[1]引脚由S5PV210用户手册,可知,该引脚位于GPD0[1]引脚上,默认为GPI,即当作通用输入端口使用,如下图所示:


 

但的第一功能名为TOUT_0,继续查阅,可知,该功能可作为PWM输出使用,如下所示,


 

所以,我们要对蜂鸣器进行操作,就是通过对XpwmTOUT[1]引脚的设置,即将其设置为TOUT_0功能,通过配置PWM的波形来实现蜂鸣器的鸣叫。

二、软件基础

如上所述,无源蜂鸣器没有自带震荡电路,必须外部提供2-5Khz左右的方波,才能驱动其发声,而要想产生方波,就会用到S5PV210的PWM模块。
S5PV210共有5个32bit的PWM定时器,其中定时器0、1、2、3有PWM功能,定时器4没有输出引脚。这些定时器都可产生中断。每个定时器可选择输入时钟为PCLK或SCLK_PWM。对PWM的操作,主要通过几个寄存器来完成,操作步骤如下:

1、设置TCFG0寄存器:配置定时器的一级分频值

2、设置TCFG1寄存器:配置定时器的二级分频值

3、设置TCNTBn寄存器:递减计数器缓冲寄存器

4、设置TCMPBn寄存器:比较缓冲寄存器

5、设置TCON寄存器:

(1)手动更新on(执行后,CPU会把TCNTBn的值加载到递减计数器中)

(2)手动更新off、自动重载、启动定时器

不过在Linux内核中,三星公司和飞凌公司已经为我们配置好了对这些PWM模块的使用,具体参见源码目录下 arch/arm/plat-s3c/的pwm.c文件,在驱动编程中,主要使用pwm_config()、pwm_enable()、pwm_disable(pwm4buzzer)这三个函数。另外,也可参见另一篇博文【S5PV210
PWM】。

驱动编程

有图有真相,按照如下运行后,即可听到“鸣叫”。



1
驱动程序

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/init.h>

#include <linux/platform_device.h>

#include <linux/fb.h>

#include <linux/backlight.h>

#include <linux/err.h>

#include <linux/pwm.h>

#include <linux/slab.h>

#include <linux/miscdevice.h>

#include <linux/delay.h>

#include <mach/gpio.h>

#include <mach/regs-gpio.h>

#include <plat/gpio-cfg.h>

#define DEVICE_NAME                                "pwm_buzzer"

#define PWM_IOCTL_SET_FREQ                1

#define PWM_IOCTL_STOP                        0

#define NS_IN_1HZ                                (1000000000UL)

#define BUZZER_PWM_ID                        0

#define BUZZER_PMW_GPIO                        S5PV210_GPD0(0)

static struct pwm_device *pwm4buzzer;

static struct semaphore lock;

static void pwm_set_freq(unsigned long freq) {

        int period_ns = NS_IN_1HZ / freq;

        pwm_config(pwm4buzzer, period_ns / 2, period_ns);

        pwm_enable(pwm4buzzer);

}

static void pwm_stop(void) {

        pwm_config(pwm4buzzer, 0, NS_IN_1HZ / 100);

        pwm_disable(pwm4buzzer);

}

static int my_pwm_open(struct inode *inode, struct file *file) {

    if (!down_trylock(&lock))

         return 0;

        else

       return -EBUSY;

}

static int my_pwm_close(struct inode *inode, struct file *file) {

        up(&lock);

        return 0;

}

static long my_pwm_ioctl(struct file *filep, unsigned int cmd,unsigned long arg)

{

        switch (cmd) {

                case PWM_IOCTL_SET_FREQ:

                        if (arg == 0)

                                return -EINVAL;

                        pwm_set_freq(arg);

                        break;

                case PWM_IOCTL_STOP:

                default:

                        pwm_stop();

                        break;

        }

        return 0;

}

static struct file_operations my_pwm_ops = {

        .owner                        = THIS_MODULE,

        .open                        = my_pwm_open,

        .release                = my_pwm_close,

        .unlocked_ioctl        = my_pwm_ioctl,

};

static struct miscdevice my_misc_dev = {

        .minor = MISC_DYNAMIC_MINOR,

        .name = DEVICE_NAME,

        .fops = &my_pwm_ops,

};

static int __init my_pwm_dev_init(void) {

        int ret;

    printk(DEVICE_NAME " my_pwm_dev_init\n");

        ret = gpio_request(BUZZER_PMW_GPIO, DEVICE_NAME);

        if (ret) {

                printk("request GPIO %d for pwm failed\n", BUZZER_PMW_GPIO);

                return ret;

        }

        gpio_set_value(BUZZER_PMW_GPIO, 0);

        s3c_gpio_cfgpin(BUZZER_PMW_GPIO, S3C_GPIO_OUTPUT);

        pwm4buzzer = pwm_request(BUZZER_PWM_ID, DEVICE_NAME);

        if (IS_ERR(pwm4buzzer)) {

                printk("request pwm %d for %s failed\n", BUZZER_PWM_ID, DEVICE_NAME);

                return -ENODEV;

        }

        pwm_stop();

        s3c_gpio_cfgpin(BUZZER_PMW_GPIO, S3C_GPIO_SFN(2));

        gpio_free(BUZZER_PMW_GPIO);

        init_MUTEX(&lock);

        ret = misc_register(&my_misc_dev);

        return ret;

}

static void __exit my_pwm_dev_exit(void) {

    printk(DEVICE_NAME " my_pwm_dev_exit\n");

        pwm_stop();

        misc_deregister(&my_misc_dev);

}

module_init(my_pwm_dev_init);

module_exit(my_pwm_dev_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("gjianw217@163.com");

MODULE_DESCRIPTION("PWM Driver");

复制代码
2 应用程序

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <sys/ioctl.h>

#include <fcntl.h>

#define PWM_IOCTL_SET_FREQ 1

#define PWM_IOCTL_STOP 0

int main(int argc ,char* argv[])

{

   int m_fd=0;//

   m_fd = open("/dev/pwm", O_RDONLY);

   int freq=1000;

   if(argc>1)

   freq=atoi(argv[1]);

   printf("%d \t\t",freq);

   ioctl(m_fd, PWM_IOCTL_STOP);

   ioctl(m_fd, PWM_IOCTL_SET_FREQ,freq);

   getchar();

   ioctl(m_fd, PWM_IOCTL_STOP);

   close(m_fd);

   return 0;

}

复制代码
3 Makefile文件

#pwm Makefile

ARCH=arm

CROSS_COMPILE=/home/ok210/arm-2009q3/bin/arm-none-linux-gnueabi-

APP_COMPILE=/home/ok210/arm-2009q3/bin/arm-none-linux-gnueabi-

#obj-m := app-drv.o

obj-m := pwm-drv.o

#KDIR := /path/to/kernel/linux/

KDIR := /home/ok210/android-kernel-samsung-dev/

PWD := $(shell pwd)

default:

        make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules

app:pwm-app.c

        $(APP_COMPILE)gcc -o app pwm-app.c

clean:

        $(MAKE) -C $(KDIR) M=$(PWD) clean

复制代码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: