• 使用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"
    ",##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("
    major = %d minor = %d
    n",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

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

  • 相关阅读:
    IT小小鸟读书笔记2
    第五周读书笔记
    JSON Schema 入门指南【Java后端使用】
    win10装多个MySQL(MySQL 8.0免安装版)
    记一些实习生问我的问题
    JAVA项目(maven)使用钉钉SDK(获取token、用户等)
    从项目开始的前端开发学习
    从项目开始的Java开发学习
    HBuilderX 5+APP MUI 入门
    项目部署各种配置
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/7101880.html
Copyright © 2020-2023  润新知