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

Posted 请给我倒杯茶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用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 = (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_SWITCH
301     //初始化Buzzer的GPIO
302     ret = buzzer_gpio_init();
303     if(ret !=0){
304         prdebug("error buzzer_gpio_init");
305         goto err_device_create;
306     }
307 
308 #endif
309     
310     
311     
312     return 0;
313 
314 #if BUZZER_GPIO_SWITCH
315 err_device_create:
316     device_destroy(buzzer_dev->cls,buzzer_dev->devno);
317 #endif
318 
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_SWITCH
337     buzzer_gpio_exit();
338 #endif
339     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_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

 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.cpp
10 
11 include $(BUILD_EXECUTABLE)

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

编译应用程序

 

实验操作如下:

查看相应的LOG

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

 

以上是关于使用linux内核hrtimer高精度定时器实现GPIO口模拟PWM,原创的主要内容,如果未能解决你的问题,请参考以下文章

Linux时间子系统之六:高精度定时器(HRTIMER)的原理和实现

Linux时间子系统之六:高精度定时器(HRTIMER)的原理和实现

Linux时间子系统之六:高精度定时器(HRTIMER)的原理和实现

Linux 高精度定时器hrtimer 使用示例

Linux时间子系统之七:定时器的应用--msleep(),hrtimer_nanosleep()

Linux时间子系统之七:定时器的应用--msleep(),hrtimer_nanosleep()