使用linux内核hrtimer高精度定时器实现GPIO口模拟PWM,【原创】
2017-07-01 11:52
871 查看
关键词: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
驱动程序对应的Makefile,自己修改交叉工具链还有内核的位置
写一个简单的APP去控制设备代码如下buzzer_write.c:
对应的Android.mk
编译驱动:错误和警告可以忽略,之后使用adb工具将buzzer_driver.ko推送进板子。
编译应用程序
实验操作如下:
查看相应的LOG
这里无法展示你们看,蜂鸣器的叫声,经过测试改程序勉强还是可以使用的啊。
平台信息:
内核: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");
驱动程序对应的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_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
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推送进板子。
编译应用程序
实验操作如下:
查看相应的LOG
这里无法展示你们看,蜂鸣器的叫声,经过测试改程序勉强还是可以使用的啊。
相关文章推荐
- AVR使用范例--定时器实现PWM功能
- 如何在Oracle中使用游标来实现多重循环?[原创]
- [原创]使用RMI编写客户端-服务器应用程序,实现分布式计算
- 使用Microsoft Visual Studio .NET 2003 可以轻松实现对JavaScript的调试【原创】
- 使用java简单模拟ping和telnet的实现
- 使用Timer实现定时器
- 『原创』使用ASP与JAVASCRIPT配合实现多个复选框数据关联显示
- java中使用线程实现Timer(定时器)原理和源码
- 使用AjaxPro框架实现无刷新用户登录验证【原创】
- 【原创】Asp中使用Ajax实现无刷新调用页面。
- java中使用线程实现Timer(定时器)原理和源码
- 使用TreeView实现无限级扩展节点(原创)
- [原创]JAAS 实现in Struts Web App,使用XMLPolicy文件,不改变VM安全文件(2)授权
- [原创]JAAS 实现in Struts Web App,使用XMLPolicy文件,不改变VM安全配置(1)认证
- 使用AjaxPro框架实现无刷新用户登录验证【原创】
- [原创] 又有新瓶装旧酒——使用Delphi实现图片界面换肤色 (2004-8)
- [原创]使用自定义类库实现中间件的功能
- 使用" 参数化基类" 和" 成员函数指针" 模拟实现虚函数--在实际中的应用
- VC++实现非窗口类中使用定时器的方法
- java中使用线程实现Timer(定时器)原理和源码