首页 > 代码库 > 使用linux内核hrtimer高精度定时器实现GPIO口模拟PWM,【原创】

使用linux内核hrtimer高精度定时器实现GPIO口模拟PWM,【原创】

关键词: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

  1 #include <linux/init.h>  2 #include <linux/module.h>  3 #include <linux/fs.h>  4 #include <linux/device.h>  5 #include <linux/slab.h>  6 #include <linux/cdev.h>  7 #include <linux/interrupt.h>  8 #include <linux/gpio.h>  9 #include <linux/input.h> 10 #include <linux/sched.h> 11 #include <linux/wait.h> 12 #include <linux/delay.h> 13 #include <linux/semaphore.h> 14  15 #include <asm/uaccess.h> 16 #include <asm/io.h> 17  18 #include <mach/platform.h> 19  20 #define BUZZER_DEVICE_NAME   "mybuzzer"      21 #define BUZZER_CLASS_NAME   "mybuzzer"    //sys/class/mybuzzer 22 #define BUZZER_DEVICE_NUM     1         //设备节点的编号最终生成节点的名字为/dev/buzzer-1 23  24  25 #define BUZZER_GPIO_SWITCH  1    //是否设置buzzer的gpio的初始化,另一个驱动已经初始化GPIO 26 #define BUZZER_DELAY_TIME_HIGHT (190000) //2.7KHZ 27 #define BUZZER_DELAY_TIME_LOW (190000) //2.7KHZ 28 #define DE_BUG 1 29  30 #if DE_BUG  31 #define prdebug(fmt,arg...)      printk("zbzhuang"KERN_ERR fmt"\n",##arg) 32 #else 33 #define prdebug(fmt,arg...)      do{}while(0); 34 #endif 35  36  37  38 typedef enum { 39     BUZZER_DISABLE = 0, 40     BUZZER_ENABLE, 41 }BUZZER_STATUS_t; 42  43 //buzzer的设备对象 44 struct buzzer_chip{ 45     dev_t devno; 46     struct cdev *cdev; 47     struct class *cls; 48     struct device *dev; 49     unsigned long count; //从应用空间读取的数据 50     struct semaphore sem; 51     struct hrtimer mytimer; 52     ktime_t kt;     //设置定时时间 53     wait_queue_head_t wait_queue; 54     BUZZER_STATUS_t status; 55 }; 56  57  58  59  60 static int count = 1000; 61 struct buzzer_chip *buzzer_dev; 62  63 static void buzzer_test(void); 64 static void buzzer_gpio_start(void); 65 static enum hrtimer_restart    hrtimer_handler(struct hrtimer *timer); 66  67  68 int buzzer_drv_open (struct inode * inode, struct file *filp) 69 { 70     int minor; 71     int major; 72      73  74     prdebug("--------------%s----------------",__func__); 75  76     minor = iminor(inode); 77     major = imajor(inode); 78  79     prdebug("\r\nmajor = %d minor = %d\rn",major,minor); 80     filp->private_data = http://www.mamicode.com/(void *)minor; 81  82     if(down_interruptible(&buzzer_dev->sem)) 83         return -ERESTARTSYS; 84      85     return 0; 86 } 87 ssize_t buzzer_drv_read (struct file *filp, char __user *userbuf, size_t count, loff_t *fpos) 88 { 89     int ret; 90  91     prdebug("--------------%s----------------",__func__); 92  93     if(filp->f_flags & O_NONBLOCK){ 94         return -EAGAIN; 95     } 96  97     ret = copy_to_user(userbuf,&buzzer_dev->count,count); 98     if(ret > 0){ 99         prdebug("error copy_to_user");100         return -EFAULT;101     }102     prdebug("%s :read count = %ld",__func__,buzzer_dev->count);103 104     return count;105 }106 ssize_t buzzer_drv_write (struct file *filp, const char __user *userbuf, size_t count, loff_t *fpos)107 {108     int ret;109 110     prdebug("--------------%s----------------",__func__);111     prdebug("task pid[%d] context[%s]",current->pid,current->comm);112 113     ret = copy_from_user(&buzzer_dev->count,userbuf,count);114     if(ret > 0){115         prdebug("error copy_from_user");116         return -EFAULT;117     }118 119     prdebug("%s :write count = %ld",__func__,buzzer_dev->count);120 121 122 123     124     if(buzzer_dev->count){125         if(buzzer_dev->status == BUZZER_DISABLE){126             //启动定时器  127             prdebug("-----------start hrtimer timer-------------");128             buzzer_dev->status = BUZZER_ENABLE;129             buzzer_gpio_start();130             wait_event(buzzer_dev->wait_queue, buzzer_dev->status == BUZZER_DISABLE);131             prdebug("------------wake up queue-------------------------------");132         }else{133             prdebug("buzzer_aready work");134         }135         136     }else{137         138     }139 140 141     return count;142 143 }144 int buzzer_drv_close (struct inode *inode, struct file *filp)145 {146 147     prdebug("--------------%s----------------",__func__);148     up(&buzzer_dev->sem);149 150     return 0;151 152 }153 154 const struct file_operations buzzer_fops = {155     .open = buzzer_drv_open,156     .write = buzzer_drv_write,157     .read = buzzer_drv_read,158     .release = buzzer_drv_close,159 160 };161 162 static int buzzer_gpio_init(void)163 {164     int ret = -1;165 166     if(gpio_request(BUZZER_IO, "BUZZER_GPIO")){167         prdebug("error buzzer_gpio_init");168         return ret;169 170     }else{171         gpio_direction_output(BUZZER_IO, 1);172         gpio_set_value(BUZZER_IO, 1);173         buzzer_dev->status = BUZZER_DISABLE;174     }175 176     return 0;177 178 }179 180 static void buzzer_gpio_exit(void)181 {182     gpio_set_value(BUZZER_IO, 1);183     gpio_free(BUZZER_IO);184 }185 186 static void buzzer_gpio_start(void)187 {188     prdebug("-----------buzzer_gpio_start------------");189     190     191 192     //高精度定时器193     hrtimer_init(&buzzer_dev->mytimer,CLOCK_MONOTONIC,HRTIMER_MODE_REL);194     buzzer_dev->mytimer.function = hrtimer_handler;195     buzzer_dev->kt = ktime_set(0, BUZZER_DELAY_TIME_LOW);196     hrtimer_start(&buzzer_dev->mytimer,buzzer_dev->kt,HRTIMER_MODE_REL);197     198     199     200 }201 202 static void buzzer_test(void)203 {204     unsigned long i;205 206     prdebug("-----------start test buzzer------------");207         for(i = 0;i < 10000;i ++){208             gpio_set_value(BUZZER_IO, 0);209             udelay(150);210             gpio_set_value(BUZZER_IO, 1);211             udelay(150);212         }213     prdebug("-----------end test buzzer------------");214 }215 216 static enum hrtimer_restart    hrtimer_handler(struct hrtimer *timer)217 {218     219 220     //prdebug("--------------%s----------------",__func__);221     222     if(buzzer_dev->count != 1){223     224         if (gpio_get_value(BUZZER_IO) == 1) {225             gpio_set_value(BUZZER_IO, 0);226 227             buzzer_dev->kt = ktime_set(0, BUZZER_DELAY_TIME_LOW);228             hrtimer_forward_now(&buzzer_dev->mytimer, buzzer_dev->kt);229 230         } else {231             gpio_set_value(BUZZER_IO, 1);232 233             buzzer_dev->kt = ktime_set(0, BUZZER_DELAY_TIME_HIGHT);234             hrtimer_forward_now(&buzzer_dev->mytimer, buzzer_dev->kt);235         }236         buzzer_dev->count --;237         return HRTIMER_RESTART;238     }else{239         buzzer_dev->count --;240         buzzer_dev->status = BUZZER_DISABLE;241         prdebug("buzzer_dev->count = %d",buzzer_dev->count);242         prdebug("-----------finsh hrtimer timer-------------");243         wake_up(&buzzer_dev->wait_queue);244         return HRTIMER_NORESTART;245     }246 247     248 }249 250 251 static int __init buzzer_drv_init(void)252 {253     int ret;254     255 256     prdebug("--------------%s----------------",__func__);257 258     buzzer_dev = kzalloc(sizeof(struct buzzer_chip),GFP_KERNEL);259     if(buzzer_dev == NULL){260         prdebug("kzalloc error");261         return -ENOMEM;262     }263 264     //动态的申请设备号265     ret = alloc_chrdev_region(&buzzer_dev->devno,0,1,BUZZER_DEVICE_NAME);266     if(ret != 0){267         prdebug("error alloc_chrdev_region");268         goto err_free;269     }270 271     //分配cdev对象272     buzzer_dev->cdev = cdev_alloc();273     cdev_init(buzzer_dev->cdev,&buzzer_fops);274     cdev_add(buzzer_dev->cdev,buzzer_dev->devno,1);275 276     //自动创建设备节点277     buzzer_dev->cls = class_create(THIS_MODULE,BUZZER_CLASS_NAME);278     if(IS_ERR(buzzer_dev->cls)){279         prdebug("error class_create");280         ret = PTR_ERR(buzzer_dev->cls);281         goto err_unregister;282     }283 284     buzzer_dev->dev = device_create(buzzer_dev->cls,NULL,buzzer_dev->devno,NULL,"buzzer-%d",BUZZER_DEVICE_NUM);285     if(IS_ERR(buzzer_dev->dev)){286         prdebug("error device_create");287         ret = PTR_ERR(buzzer_dev);288         goto err_class_error;289     }290 291     //信号量292     sema_init(&buzzer_dev->sem,1);293     294     init_waitqueue_head(&buzzer_dev->wait_queue);295     296     297 298     299     300 #if BUZZER_GPIO_SWITCH301     //初始化Buzzer的GPIO302     ret = buzzer_gpio_init();303     if(ret !=0){304         prdebug("error buzzer_gpio_init");305         goto err_device_create;306     }307 308 #endif309     310     311     312     return 0;313 314 #if BUZZER_GPIO_SWITCH315 err_device_create:316     device_destroy(buzzer_dev->cls,buzzer_dev->devno);317 #endif318 319 err_class_error:320     class_destroy(buzzer_dev->cls);321 322 err_unregister:    323     cdev_del(buzzer_dev->cdev);324     unregister_chrdev_region(buzzer_dev->devno,1);325 326 err_free:327     kfree(buzzer_dev);328     return ret;329     330 331 }332 333 static void __exit buzzer_drv_exit(void)334 {335     prdebug("--------------%s----------------",__func__);336 #if BUZZER_GPIO_SWITCH337     buzzer_gpio_exit();338 #endif339     device_destroy(buzzer_dev->cls,buzzer_dev->devno);340     class_destroy(buzzer_dev->cls);341     cdev_del(buzzer_dev->cdev);342     unregister_chrdev_region(buzzer_dev->devno,1);343     kfree(buzzer_dev);344 345 }346 347 module_init(buzzer_drv_init);348 module_exit(buzzer_drv_exit);349 MODULE_LICENSE("GPL");350 MODULE_AUTHOR("zhuang zebin@qq.com");

 

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

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_appMODULE_NAME = buzzer_driver#内核源码路径#KERNEL_DIR = /home/zsf/rk3188_5.1/android/kernelKERNEL_DIR = /home/zsf/u4209/s5p4418-5.1-android/trunk/kernelCUR_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

 1 LOCAL_PATH := $(call my-dir) 2  3 include $(CLEAR_VARS) 4  5 LOCAL_MODULE_TAGS := optional 6  7 LOCAL_MODULE := buzzer_write 8  9 LOCAL_SRC_FILES := buzzer_write.cpp10 11 include $(BUILD_EXECUTABLE)

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

技术分享

编译应用程序

技术分享

 

实验操作如下:

技术分享

查看相应的LOG

技术分享

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

 

使用linux内核hrtimer高精度定时器实现GPIO口模拟PWM,【原创】