使用linux内核hrtimer高精度定时器实现GPIO口模拟PWM,「原创」[亲测有效]

使用linux内核hrtimer高精度定时器实现GPIO口模拟PWM,「原创」[亲测有效]关键词:Android linux hrtimer 蜂鸣器 等待队列 信号量 字符设备平台信息:内核:linux3.4.39 系统:androi

欢迎大家来到IT世界,在知识的湖畔探索吧!

关键词:Android linux hrtimer 蜂鸣器 等待队列 信号量 字符设备

平台信息:
内核:linux3.4.39
系统:android/android5.1
平台:S5P4418

作者:庄泽彬(欢迎转载,请注明作者)

邮箱:2760715357@qq.com

程序描述:本文控制的设备是无源蜂鸣器,由于无源蜂鸣器是需要产生一定的频率的PWM才能够控制蜂鸣器,不像有源蜂鸣器,只需要提供高低电平就可以控制蜂鸣器。linux内核普通的定时器,由于具有一定的局限性,不能达到纳秒级别的定时,使用普通的定时器模拟GPIO口产生PWM会导致蜂鸣器出现杂音,因此要使用hrtimer高精度定时器模拟GPIO口产生PWM可以极大的改善性能。使用信号量sem只是为了避免多个应用程序打开设备,使用等待队列是为了让程序可以按照指定的方式去运行,如果不加等待队列,在启动hrtimer定时器之后就会不会等待定时器完成之后在关闭设备,因此需要加入等待队列,等待定时器定时的工作完成之后再唤醒等待队列。虽然程序可以使用GPIO模拟PWM产生一定频率的信号去控制无源蜂鸣器,但是还是存在一定的局限性,因为定时器也是加入内核进行调度,所以有可能杂定时的时候会导致输出的PWM被打断,导致杂音的出现。因此严格来说不能完整的输出一段不被打断模拟PWM信号。如果有什么好方法可以实现的话,可以共同探讨。好吧不说了上代码吧。

无源蜂鸣器的驱动程序(代码写的一般般如有不对,欢迎指点)

buzzer_driver.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/input.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/semaphore.h>

#include <asm/uaccess.h>
#include <asm/io.h>

#include <mach/platform.h>

#define BUZZER_DEVICE_NAME   "mybuzzer"
#define BUZZER_CLASS_NAME   "mybuzzer"    //sys/class/mybuzzer
#define BUZZER_DEVICE_NUM     1         //设备节点的编号最终生成节点的名字为/dev/buzzer-1


#define BUZZER_GPIO_SWITCH  1    //是否设置buzzer的gpio的初始化,另一个驱动已经初始化GPIO
#define BUZZER_DELAY_TIME_HIGHT (190000) //2.7KHZ
#define BUZZER_DELAY_TIME_LOW (190000) //2.7KHZ
#define DE_BUG 1

#if DE_BUG
#define prdebug(fmt,arg...)      printk("zbzhuang"KERN_ERR fmt"\n",##arg)
#else
#define prdebug(fmt,arg...)      do{}while(0);
#endif



typedef enum {
    BUZZER_DISABLE = 0,
    BUZZER_ENABLE,
}BUZZER_STATUS_t;

//buzzer的设备对象
struct buzzer_chip{
    dev_t devno;
    struct cdev *cdev;
    struct class *cls;
    struct device *dev;
    unsigned long count; //从应用空间读取的数据
    struct semaphore sem;
    struct hrtimer mytimer;
    ktime_t kt;     //设置定时时间
    wait_queue_head_t wait_queue;
    BUZZER_STATUS_t status;
};




static int count = 1000;
struct buzzer_chip *buzzer_dev;

static void buzzer_test(void);
static void buzzer_gpio_start(void);
static enum hrtimer_restart    hrtimer_handler(struct hrtimer *timer);


int buzzer_drv_open (struct inode * inode, struct file *filp)
{
    int minor;
    int major;


    prdebug("--------------%s----------------",__func__);

    minor = iminor(inode);
    major = imajor(inode);

    prdebug("\r\nmajor = %d minor = %d\rn",major,minor);
    filp->private_data = (void *)minor;

    if(down_interruptible(&buzzer_dev->sem))
        return -ERESTARTSYS;

    return 0;
}
ssize_t buzzer_drv_read (struct file *filp, char __user *userbuf, size_t count, loff_t *fpos)
{
    int ret;

    prdebug("--------------%s----------------",__func__);

    if(filp->f_flags & O_NONBLOCK){
        return -EAGAIN;
    }

    ret = copy_to_user(userbuf,&buzzer_dev->count,count);
    if(ret > 0){
        prdebug("error copy_to_user");
        return -EFAULT;
    }
    prdebug("%s :read count = %ld",__func__,buzzer_dev->count);

    return count;
}
ssize_t buzzer_drv_write (struct file *filp, const char __user *userbuf, size_t count, loff_t *fpos)
{
    int ret;

    prdebug("--------------%s----------------",__func__);
    prdebug("task pid[%d] context[%s]",current->pid,current->comm);

    ret = copy_from_user(&buzzer_dev->count,userbuf,count);
    if(ret > 0){
        prdebug("error copy_from_user");
        return -EFAULT;
    }

    prdebug("%s :write count = %ld",__func__,buzzer_dev->count);




    if(buzzer_dev->count){
        if(buzzer_dev->status == BUZZER_DISABLE){
            //启动定时器
            prdebug("-----------start hrtimer timer-------------");
            buzzer_dev->status = BUZZER_ENABLE;
            buzzer_gpio_start();
            wait_event(buzzer_dev->wait_queue, buzzer_dev->status == BUZZER_DISABLE);
            prdebug("------------wake up queue-------------------------------");
        }else{
            prdebug("buzzer_aready work");
        }

    }else{

    }


    return count;

}
int buzzer_drv_close (struct inode *inode, struct file *filp)
{

    prdebug("--------------%s----------------",__func__);
    up(&buzzer_dev->sem);

    return 0;

}

const struct file_operations buzzer_fops = {
    .open = buzzer_drv_open,
    .write = buzzer_drv_write,
    .read = buzzer_drv_read,
    .release = buzzer_drv_close,

};

static int buzzer_gpio_init(void)
{
    int ret = -1;

    if(gpio_request(BUZZER_IO, "BUZZER_GPIO")){
        prdebug("error buzzer_gpio_init");
        return ret;

    }else{
        gpio_direction_output(BUZZER_IO, 1);
        gpio_set_value(BUZZER_IO, 1);
        buzzer_dev->status = BUZZER_DISABLE;
    }

    return 0;

}

static void buzzer_gpio_exit(void)
{
    gpio_set_value(BUZZER_IO, 1);
    gpio_free(BUZZER_IO);
}

static void buzzer_gpio_start(void)
{
    prdebug("-----------buzzer_gpio_start------------");



    //高精度定时器
    hrtimer_init(&buzzer_dev->mytimer,CLOCK_MONOTONIC,HRTIMER_MODE_REL);
    buzzer_dev->mytimer.function = hrtimer_handler;
    buzzer_dev->kt = ktime_set(0, BUZZER_DELAY_TIME_LOW);
    hrtimer_start(&buzzer_dev->mytimer,buzzer_dev->kt,HRTIMER_MODE_REL);



}

static void buzzer_test(void)
{
    unsigned long i;

    prdebug("-----------start test buzzer------------");
        for(i = 0;i < 10000;i ++){
            gpio_set_value(BUZZER_IO, 0);
            udelay(150);
            gpio_set_value(BUZZER_IO, 1);
            udelay(150);
        }
    prdebug("-----------end test buzzer------------");
}

static enum hrtimer_restart    hrtimer_handler(struct hrtimer *timer)
{


    //prdebug("--------------%s----------------",__func__);

    if(buzzer_dev->count != 1){

        if (gpio_get_value(BUZZER_IO) == 1) {
            gpio_set_value(BUZZER_IO, 0);

            buzzer_dev->kt = ktime_set(0, BUZZER_DELAY_TIME_LOW);
            hrtimer_forward_now(&buzzer_dev->mytimer, buzzer_dev->kt);

        } else {
            gpio_set_value(BUZZER_IO, 1);

            buzzer_dev->kt = ktime_set(0, BUZZER_DELAY_TIME_HIGHT);
            hrtimer_forward_now(&buzzer_dev->mytimer, buzzer_dev->kt);
        }
        buzzer_dev->count --;
        return HRTIMER_RESTART;
    }else{
        buzzer_dev->count --;
        buzzer_dev->status = BUZZER_DISABLE;
        prdebug("buzzer_dev->count = %d",buzzer_dev->count);
        prdebug("-----------finsh hrtimer timer-------------");
        wake_up(&buzzer_dev->wait_queue);
        return HRTIMER_NORESTART;
    }


}


static int __init buzzer_drv_init(void)
{
    int ret;


    prdebug("--------------%s----------------",__func__);

    buzzer_dev = kzalloc(sizeof(struct buzzer_chip),GFP_KERNEL);
    if(buzzer_dev == NULL){
        prdebug("kzalloc error");
        return -ENOMEM;
    }

    //动态的申请设备号
    ret = alloc_chrdev_region(&buzzer_dev->devno,0,1,BUZZER_DEVICE_NAME);
    if(ret != 0){
        prdebug("error alloc_chrdev_region");
        goto err_free;
    }

    //分配cdev对象
    buzzer_dev->cdev = cdev_alloc();
    cdev_init(buzzer_dev->cdev,&buzzer_fops);
    cdev_add(buzzer_dev->cdev,buzzer_dev->devno,1);

    //自动创建设备节点
    buzzer_dev->cls = class_create(THIS_MODULE,BUZZER_CLASS_NAME);
    if(IS_ERR(buzzer_dev->cls)){
        prdebug("error class_create");
        ret = PTR_ERR(buzzer_dev->cls);
        goto err_unregister;
    }

    buzzer_dev->dev = device_create(buzzer_dev->cls,NULL,buzzer_dev->devno,NULL,"buzzer-%d",BUZZER_DEVICE_NUM);
    if(IS_ERR(buzzer_dev->dev)){
        prdebug("error device_create");
        ret = PTR_ERR(buzzer_dev);
        goto err_class_error;
    }

    //信号量
    sema_init(&buzzer_dev->sem,1);

    init_waitqueue_head(&buzzer_dev->wait_queue);





#if BUZZER_GPIO_SWITCH
    //初始化Buzzer的GPIO
    ret = buzzer_gpio_init();
    if(ret !=0){
        prdebug("error buzzer_gpio_init");
        goto err_device_create;
    }

#endif



    return 0;

#if BUZZER_GPIO_SWITCH
err_device_create:
    device_destroy(buzzer_dev->cls,buzzer_dev->devno);
#endif

err_class_error:
    class_destroy(buzzer_dev->cls);

err_unregister:
    cdev_del(buzzer_dev->cdev);
    unregister_chrdev_region(buzzer_dev->devno,1);

err_free:
    kfree(buzzer_dev);
    return ret;


}

static void __exit buzzer_drv_exit(void)
{
    prdebug("--------------%s----------------",__func__);
#if BUZZER_GPIO_SWITCH
    buzzer_gpio_exit();
#endif
    device_destroy(buzzer_dev->cls,buzzer_dev->devno);
    class_destroy(buzzer_dev->cls);
    cdev_del(buzzer_dev->cdev);
    unregister_chrdev_region(buzzer_dev->devno,1);
    kfree(buzzer_dev);

}

module_init(buzzer_drv_init);
module_exit(buzzer_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhuang zebin@qq.com");

欢迎大家来到IT世界,在知识的湖畔探索吧!

驱动程序对应的Makefile,自己修改交叉工具链还有内核的位置

欢迎大家来到IT世界,在知识的湖畔探索吧!CROSS_COMPILE = /home/zsf/u4209/s5p4418-5.1-android/trunk/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-
#CROSS_COMPILE = /home/zsf/book/toolchain-4.5.1-farsight/bin/arm-none-linux-gnueabi-
CC = $(CROSS_COMPILE)gcc
#APP_NAME = led_app
MODULE_NAME = buzzer_driver


#内核源码路径
#KERNEL_DIR = /home/zsf/rk3188_5.1/android/kernel
KERNEL_DIR = /home/zsf/u4209/s5p4418-5.1-android/trunk/kernel

CUR_DIR = $(shell pwd)

all :
    make -C $(KERNEL_DIR) M=$(CUR_DIR) modules 
    #$(CC) $(APP_NAME).c -o  $(APP_NAME)

clean : 
    make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
    #rm -rf $(APP_NAME) 


install:
    cp -raf  *.ko $(APP_NAME)  /opt/rootfs/drv_module/


#指定编译哪个源文件
obj-m = $(MODULE_NAME).o


~

写一个简单的APP去控制设备代码如下buzzer_write.c:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc,char **argv)
{
    int fd = -1;
    unsigned long  on = -1;

    fd = open("/dev/buzzer-1",O_RDWR);
    if(fd < 0){
        perror("open");
        exit(1);
    }

    on = atol(argv[1]);
    write(fd,&on,4);

    close(fd);



    return 0;
}

对应的Android.mk

欢迎大家来到IT世界,在知识的湖畔探索吧!LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_MODULE := buzzer_write

LOCAL_SRC_FILES := buzzer_write.cpp

include $(BUILD_EXECUTABLE)

编译驱动:错误和警告可以忽略,之后使用adb工具将buzzer_driver.ko推送进板子。

使用linux内核hrtimer高精度定时器实现GPIO口模拟PWM,「原创」[亲测有效]

编译应用程序

使用linux内核hrtimer高精度定时器实现GPIO口模拟PWM,「原创」[亲测有效]

实验操作如下:

使用linux内核hrtimer高精度定时器实现GPIO口模拟PWM,「原创」[亲测有效]

查看相应的LOG

使用linux内核hrtimer高精度定时器实现GPIO口模拟PWM,「原创」[亲测有效]

这里无法展示你们看,蜂鸣器的叫声,经过测试改程序勉强还是可以使用的啊。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/18521.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信