• Android震动vibrator(马达)--系统到驱动的流程【转】


    本文转载自:https://blog.csdn.net/tianshiyalin/article/details/17136723

    一、前言

    本人刚学习安卓驱动开发,水平不能说菜,是根本没有水平,在这里把学习过程贴出来,跟大家一起学习交流,还望大家多多指正,转载的请标明出处。http://blog.csdn.net/angle_birds/article/details/16801533

    二、android驱动介绍

    安卓总体架构是在 Linux内核基础上,增加硬件抽象层(HAL),运行库,java虚拟机,程序框架等组成的,具体如下图。

     

    安卓的应用程序是从application framework层架构上建立的。所有APK应用程序都是通过framework层来运行的。application framework是google写好的,除非自己深度定制,一般是不会更改这个层的。对于驱动开发来讲,我们要做的就是让framework层能认识并操作我们的硬件设备就OK了。因此我们关心主要有3个层面:

    linux Kernel层

    HAL层

    JNI层

    1.       linuxKernel:是google在linux内核基础上,专门为移动设备优化后的内核,增加修改一些东西,担修改的不多,对于内核驱动来讲,基本没有修改,做过linux驱动开发的人应该很容易理解。

    2.       HAL,硬件抽象层:简单来说,就是对Linux 内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节。也就是说,把对硬件的支持分成了两层,一层放在用户空间(User Space),一层放在内核空间(Kernel Space),其中,硬件抽象层运行在用户空间。用户空间不属于内核不必遵守GPL协议,各个厂商可以把与自己硬件设备相关,具有商业机密的一些代码放在HAL层。

    3.       JNI层:提供java和底层C、C++的动态链接库的接口。我理解的是JNI就是一个代理,可以把C和C++生成的接口函数翻译成Java可用,提供给framework层。

    三、振动系统开发过程

    1.       硬件平台

               CPU:IMX6Q4核1G

               RAM:1G

               FLASH:8G板载

       这次开发用的代码都是google和飞思卡尔提供的具体的就不再说明了,因为每个平台代码都有所不同,而且买开发板时候都会带相应的资料。

    2.       震动系统是android里面比较简单的一个系统, 我采用的是从高层到底层的学习方式。因为我们的驱动最终是给应用程序用的,从application的需求分析JNI,然后分析HAL最后在我们写linux kernel驱动时候,很容易理解为什么要这么写。好了开始正式分析。

    3.       Application层:通过google我找到关于APK访问震动的如下说明:

    A、通过系统服务获得手机震动服务,Vibrator vibrator =(Vibrator)getSystemService(VIBRATOR_SERVICE); 

    B、得到震动服务后检测vibrator是否存在:

    vibrator.hasVibrator();

    检测当前硬件是否有vibrator,如果有返回true,如果没有返回false。

    C、根据实际需要进行适当的调用,

    vibrator.vibrate(longmilliseconds);

    开始启动vibrator持续milliseconds毫秒。    

    vibrator.vibrate(long[]pattern, int repeat);

    以pattern方式重复repeat次启动vibrator。

    (pattern的形式为new long[]{arg1,arg2,arg3,arg4......},其中以两个一组的如arg1 和arg2为一组、arg3和arg4为一组,每一组的前一个代表等待多少毫 秒启动vibrator,后一个代表vibrator持续多少毫秒停止,之后往复即 可。Repeat表示重复次数,当其为-1时,表示不重复只以pattern的方 式运行一次)。

    D、vibrator.cancel();

    Vibrator停止。

    从上面的说明,可以看出应用程序调用震动系统,是调用一个叫VIBRATOR_SERVICE的服务,这个服务有3个函数,分别是hasVibrator(),r.vibrate,.cancel();当然这个三个函数可能在framework层进行的另一层的封装,我没有去深究。但可以推测出JNI层要做的是与注册VIBRATOR_SERVICE服务和实现这三个函数相关的.

    4.       HAL层:这一层我找到了具体的代码我们会好分析很多,其代码是:

    androidframeworksaseservicesjni com_android_server_VibratorService.cpp

    [cpp] view plaincopy
     
     
     
    1. #define LOG_TAG"VibratorService"  
    2.    
    3. #include"jni.h"  
    4. #include"JNIHelp.h"  
    5. #include"android_runtime/AndroidRuntime.h"  
    6.    
    7. #include<utils/misc.h>  
    8. #include<utils/Log.h>  
    9. #include<hardware_legacy/vibrator.h>  
    10.    
    11. #include<stdio.h>  
    12.    
    13. namespace android  
    14. {  
    15.    
    16. static jbooleanvibratorExists(JNIEnv *env, jobject clazz)       //判断振动器是否存在  
    17. {  
    18.     return vibrator_exists() > 0 ? JNI_TRUE: JNI_FALSE;  
    19. }  
    20.    
    21. static voidvibratorOn(JNIEnv *env, jobject clazz, jlong timeout_ms)//打开振动器  
    22. {  
    23.     // LOGI("vibratorOn ");  
    24.     vibrator_on(timeout_ms);  
    25. }  
    26.    
    27. static voidvibratorOff(JNIEnv *env, jobject clazz)//关闭振动器  
    28. {  
    29.     // LOGI("vibratorOff ");  
    30.     vibrator_off();  
    31. }  
    32.    
    33. staticJNINativeMethod method_table[] = {  
    34.     { "vibratorExists","()Z", (void*)vibratorExists },  
    35.     { "vibratorOn", "(J)V",(void*)vibratorOn },  
    36.     { "vibratorOff", "()V",(void*)vibratorOff }  
    37. };  
    38.    
    39. intregister_android_server_VibratorService(JNIEnv *env)    //注册vibrator服务  
    40. {  
    41.     return jniRegisterNativeMethods(env,"com/android/server/VibratorService",  
    42.             method_table, NELEM(method_table));  
    43. }  

    从上面代码可以看出,JNI做了两件事:其一注册vibrator服务,其二,实现了vibratorExists,vibratorOn,vibratorOff三个服务函数。 进而我们可以分析出HAL层主要的就是实现次代码里调用的三个函数vibrator_exists(),vibrator_on(timeout_ms),vibrator_off()。

    5.       HAL层:经过各种查找我们找到了vibrator的hal层代码:

    android40hardwarelibhardware_legacyvibratorvibrator.c

    [cpp] view plaincopy
     
     
     
    1. #include<hardware_legacy/vibrator.h>  
    2. #include"qemu.h"  
    3.    
    4. #include<stdio.h>  
    5. #include<unistd.h>  
    6. #include<fcntl.h>  
    7. #include<errno.h>  
    8.    
    9. #define THE_DEVICE"/sys/class/timed_output/vibrator/enable"  
    10.    
    11. intvibrator_exists()         //判断 振动器是否存在  
    12. {  
    13.     int fd;  
    14.    
    15. #ifdefQEMU_HARDWARE                 //模拟器情况下实现此功能  
    16.     if (qemu_check()) {  
    17.         return 1;  
    18.     }  
    19. #endif  
    20.    
    21.     fd = open(THE_DEVICE, O_RDWR);  
    22.     if(fd < 0)  
    23.         return 0;  
    24.     close(fd);  
    25.     return 1;  
    26. }  
    27.    
    28. static intsendit(int timeout_ms)       //打开振动器 timeout_ms 毫秒  
    29. {  
    30.     int nwr, ret, fd;  
    31.     char value[20];  
    32.    
    33. #ifdefQEMU_HARDWARE        //模拟器情况下实现次功能  
    34.     if (qemu_check()) {  
    35.         return qemu_control_command("vibrator:%d", timeout_ms );  
    36.     }  
    37. #endif  
    38.    
    39.     fd = open(THE_DEVICE, O_RDWR);  
    40.     if(fd < 0)  
    41.         return errno;  
    42.    
    43.     nwr = sprintf(value, "%d ",timeout_ms);  
    44.     ret = write(fd, value, nwr);  
    45.    
    46.     close(fd);  
    47.    
    48.     return (ret == nwr) ? 0 : -1;  
    49. }  
    50.    
    51. intvibrator_on(int timeout_ms)  
    52. {  
    53.     /* constant on, up to maximum allowed time*/  
    54.     return sendit(timeout_ms);  
    55. }  
    56.    
    57. int vibrator_off()     //关闭振动器就是设置振动器打开时间为0  
    58. {  
    59.     return sendit(0);        
    60. }  

    分析上面代码可以看出,HAL访问这个设备是打开/sys/class/timed_output/vibrator/enable,这个设备文件,然后向文件中写入打开时间来完成设备操作的。因此很容易我们可以推断出,linux kernel层是要生成这个设备文件然后,实现相应的函数。

    6.       Linuxkernel层:通过上面分析我们大概了解了内核驱动所要实现的功能。通过各种参考资料,我查到了这个设备驱动是通过timed_output框架来实现的,有框架在又简单了不少,我们找到timed_output框架实现的函数在:

    kerneldriversstagingandroid imed_output.c

    [cpp] view plaincopy
     
     
     
    1. #include<linux/module.h>  
    2. #include<linux/types.h>  
    3. #include<linux/device.h>  
    4. #include<linux/fs.h>  
    5. #include<linux/err.h>  
    6.    
    7. #include"timed_output.h"  
    8.    
    9. static structclass *timed_output_class;  
    10. static atomic_tdevice_count;  
    11.    
    12. static ssize_t enable_show(structdevice *dev, struct device_attribute *attr,  
    13.              char *buf)  
    14. {  
    15.     struct timed_output_dev *tdev =dev_get_drvdata(dev);  
    16.     int remaining = tdev->get_time(tdev);  
    17.    
    18.     return sprintf(buf, "%d ",remaining);  
    19. }  
    20.    
    21. static ssize_tenable_store(  
    22.              struct device *dev, structdevice_attribute *attr,  
    23.              const char *buf, size_t size)  
    24. {  
    25.     struct timed_output_dev *tdev =dev_get_drvdata(dev);  
    26.     int value;  
    27.    
    28.     if (sscanf(buf, "%d", &value)!= 1)  
    29.              return -EINVAL;  
    30.    
    31.     tdev->enable(tdev, value);  
    32.    
    33.     return size;  
    34. }  
    35.    
    36. static DEVICE_ATTR(enable,S_IRUGO | S_IWUSR, enable_show, enable_store);  
    37.    
    38. static intcreate_timed_output_class(void)  
    39. {  
    40.     if (!timed_output_class) {  
    41.              timed_output_class =class_create(THIS_MODULE, "timed_output");  
    42.              if (IS_ERR(timed_output_class))  
    43.                        return PTR_ERR(timed_output_class);  
    44.              atomic_set(&device_count, 0);  
    45.     }  
    46.    
    47.     return 0;  
    48. }  
    49.    
    50. inttimed_output_dev_register(struct timed_output_dev *tdev)  
    51. {  
    52.     int ret;  
    53.    
    54.     if (!tdev || !tdev->name ||!tdev->enable || !tdev->get_time)  
    55.              return -EINVAL;  
    56.    
    57.     ret = create_timed_output_class();  
    58.     if (ret < 0)  
    59.              return ret;  
    60.    
    61.     tdev->index =atomic_inc_return(&device_count);  
    62.     tdev->dev =device_create(timed_output_class, NULL,  
    63.              MKDEV(0, tdev->index), NULL,tdev->name);  
    64.     if (IS_ERR(tdev->dev))  
    65.              return PTR_ERR(tdev->dev);  
    66.    
    67.     ret = device_create_file(tdev->dev,&dev_attr_enable);  
    68.     if (ret < 0)  
    69.              goto err_create_file;  
    70.    
    71.     dev_set_drvdata(tdev->dev, tdev);  
    72.     tdev->state = 0;  
    73.     return 0;  
    74.    
    75. err_create_file:  
    76.     device_destroy(timed_output_class, MKDEV(0,tdev->index));  
    77.     printk(KERN_ERR "timed_output: Failedto register driver %s ",  
    78.                        tdev->name);  
    79.    
    80.     return ret;  
    81. }  
    82. EXPORT_SYMBOL_GPL(timed_output_dev_register);  
    83.    
    84. voidtimed_output_dev_unregister(struct timed_output_dev *tdev)  
    85. {  
    86.     device_remove_file(tdev->dev,&dev_attr_enable);  
    87.     device_destroy(timed_output_class, MKDEV(0,tdev->index));  
    88.     dev_set_drvdata(tdev->dev, NULL);  
    89. }  
    90. EXPORT_SYMBOL_GPL(timed_output_dev_unregister);  
    91.    
    92. static int __inittimed_output_init(void)  
    93. {  
    94.     return create_timed_output_class();  
    95. }  
    96.    
    97. static void __exittimed_output_exit(void)  
    98. {  
    99.     class_destroy(timed_output_class);  
    100. }  
    101.    
    102. module_init(timed_output_init);  
    103. module_exit(timed_output_exit);  
    104.    
    105. MODULE_AUTHOR("MikeLockwood <lockwood@android.com>");  
    106. MODULE_DESCRIPTION("timedoutput class driver");  
    107. MODULE_LICENSE("GPL");  

    kerneldriversstagingandroid imed_output.h

    [cpp] view plaincopy
     
     
     
    1. #ifndef _LINUX_TIMED_OUTPUT_H  
    2. #define _LINUX_TIMED_OUTPUT_H  
    3.    
    4. struct timed_output_dev {  
    5.          constchar  *name;  
    6.    
    7.          /* enablethe output and set the timer */  
    8.          void   (*enable)(struct timed_output_dev *sdev, inttimeout);  
    9.    
    10.          /*returns the current number of milliseconds remaining on the timer */  
    11.          int              (*get_time)(structtimed_output_dev *sdev);  
    12.    
    13.          /*private data */  
    14.          structdevice       *dev;  
    15.          int              index;  
    16.          int              state;  
    17. };  
    18.    
    19. extern int timed_output_dev_register(struct timed_output_dev*dev);  
    20. extern void timed_output_dev_unregister(structtimed_output_dev *dev);  
    21.    
    22. #endif  
    23.    

    分析上面代码可以看出,我们的驱动是要实现timed_output_dev结构体,然后注册这个结构体就行了。下面我们开始真正动手。由于本人水平有限,参考了samung一个公开kernel的代码里的马达驱动。写出了自己的驱动:

    本人硬件的马达通过P4.17脚控制 高打开 低关闭

    kernel_imxdriversvibratorvibrator.c

    [cpp] view plaincopy
     
     
     
    1. #include <linux/hrtimer.h>  
    2. #include <linux/err.h>  
    3. #include <linux/gpio.h>  
    4. #include <linux/wakelock.h>  
    5. #include <linux/mutex.h>  
    6. #include <linux/clk.h>  
    7. #include <linux/workqueue.h>  
    8. #include <asm/mach-types.h>  
    9. #include <linux/kernel.h>  
    10. #include <linux/module.h>  
    11.    
    12. #include<../drivers/staging/android/timed_output.h>  
    13.    
    14. #define IMX_GPIO_NR(bank, nr)             (((bank) - 1) * 32 + (nr))        //IO定义  
    15. #define SABRESD_VIBRATOR_CTL                   IMX_GPIO_NR(4, 17)   //电机通过P4.17脚控制 高打开 低关闭  
    16. #define MAX_TIMEOUT        10000/* 10s */  //最长可打开10s  
    17.    
    18.  static structvibrator {  
    19.          structwake_lock wklock;      //wake_lock 防止震动过程中系统休眠,线程不释放此设备,造成不必要错误  
    20.          structhrtimer timer;    //高精度定时器  
    21.          structmutex lock;        //互斥锁,防止多线程同时访问这个设备.  
    22.          structwork_struct work; //设备操作队列,用于一次操作完成和下一次开始同步用 (三星这么用的,具体为什么不直接用回调函数,我也不懂,还望大神们私信给个说明 感激不尽)  
    23. } vibdata;  
    24.    
    25. static void mx6_vibrator_off(void)  
    26. {  
    27.    
    28.          gpio_direction_output(SABRESD_VIBRATOR_CTL,0);         
    29.          wake_unlock(&vibdata.wklock);              //震动关闭就可以释放 wake_lock锁  
    30.           
    31. }  
    32. void mx6_motor_enable(struct timed_output_dev *sdev,int value)  
    33. {  
    34.          mutex_lock(&vibdata.lock);                     //关键代码段,同一时间只允许一个线程执行  
    35.           
    36.          /* cancelprevious timer and set GPIO according to value */  
    37.          hrtimer_cancel(&vibdata.timer);            //当先前定时器完成后 关闭这个定时器  
    38.          cancel_work_sync(&vibdata.work);         //当上次震动完成后 关闭这次动作  
    39.          if(value)  
    40.          {  
    41.                    wake_lock(&vibdata.wklock);         //开始震动打开wake lock锁不允许休眠  
    42.                    gpio_direction_output(SABRESD_VIBRATOR_CTL,1);  
    43.           
    44.                    if(value > 0)  
    45.                    {  
    46.                             if(value > MAX_TIMEOUT)  
    47.                                      value= MAX_TIMEOUT;  
    48.                             value+= 45;                                    //为了使震动变得明显,固定增加一个时间.跟硬件有关系  
    49.                             hrtimer_start(&vibdata.timer,                 //开始定时器  
    50.                                      ns_to_ktime((u64)value* NSEC_PER_MSEC),  
    51.                                      HRTIMER_MODE_REL);  
    52.                    }  
    53.          }  
    54.          else  
    55.                    mx6_vibrator_off();  
    56.    
    57.          mutex_unlock(&vibdata.lock);                 //关键代码段执行完成,释放互斥锁  
    58.    
    59.    
    60. }  
    61. int     mx6_get_time(structtimed_output_dev *sdev)  
    62. {  
    63.          if(hrtimer_active(&vibdata.timer))  
    64.          {  
    65.                    ktime_tr = hrtimer_get_remaining(&vibdata.timer);                 //读取剩余时间按并返回  
    66.                    returnktime_to_ms(r);  
    67.          }  
    68.           
    69.          return 0;  
    70. }  
    71. struct timed_output_dev mx6_motot_driver={  
    72. .name ="vibrator",                                  //注意这个名字,由于HAL层里面的设备为//"/sys/class/timed_output/vibrator/enable"  
    73.                                                                  //因此这个名字必须为"vibrator"  
    74.                    .enable= mx6_motor_enable,  
    75.                    .get_time= mx6_get_time,  
    76. };  
    77.    
    78. static enum hrtimer_restartmx6_vibrator_timer_func(struct hrtimer * timer) //定时器结束时候的回调函数  
    79. {  
    80.          schedule_work(&vibdata.work);              //定时器完成了 执行work队列回调函数来关闭电机  
    81.          returnHRTIMER_NORESTART;  
    82. }  
    83. static void mx6_vibrator_work(struct work_struct *work)//工作队列处理函数,当工作队列执行 当  
    84. //schedule_work时候执行  
    85. {  
    86.          mx6_vibrator_off();  
    87. }  
    88.    
    89.    
    90. void __init mx6_motor_init()  
    91. {  
    92.          int ret =0;  
    93.          hrtimer_init(&vibdata.timer,CLOCK_MONOTONIC, HRTIMER_MODE_REL);//初始化定时器  
    94.          vibdata.timer.function= mx6_vibrator_timer_func;           //设置回调函数  
    95.          INIT_WORK(&vibdata.work,mx6_vibrator_work);    //初始化工作队列  
    96.          ret =gpio_request(SABRESD_VIBRATOR_CTL, "vibrator-en");    //申请IO  
    97.          if (ret< 0)  
    98.          {  
    99.                    printk("vibratorrequest IO err!:%d ",ret);  
    100.                    returnret;  
    101.          }  
    102.          wake_lock_init(&vibdata.wklock,WAKE_LOCK_SUSPEND, "vibrator"); //初始化 wake_lock  
    103.          mutex_init(&vibdata.lock);             //初始化 互斥锁  
    104.          ret=timed_output_dev_register(&mx6_motot_driver);//注册timed_output 设备  
    105.          if (ret< 0)  
    106.                    gotoerr_to_dev_reg;  
    107.          return 0;  
    108. err_to_dev_reg:           //错误了 就释放所有资源  
    109.          mutex_destroy(&vibdata.lock);  
    110.          wake_lock_destroy(&vibdata.wklock);  
    111.    
    112.          gpio_free(SABRESD_VIBRATOR_CTL);  
    113.          printk("vibrator   err!:%d ",ret);  
    114.          returnret;  
    115.           
    116. }  
    117. void mx6_motor_exit()  
    118. {  
    119.          mutex_destroy(&vibdata.lock);  
    120.          wake_lock_destroy(&vibdata.wklock);  
    121.          gpio_free(SABRESD_VIBRATOR_CTL);  
    122.          printk("vibrator  exit! ");  
    123.          timed_output_dev_register(&mx6_motot_driver);  
    124. }  
    125. module_init(mx6_motor_init);  
    126. module_exit(mx6_motor_exit);  
    127.    
    128. MODULE_AUTHOR("<lijianzhang>");  
    129. MODULE_DESCRIPTION("Motor Vibrator driver");  
    130. MODULE_LICENSE("GPL");  
    131.    

    自此完成了驱动的所有内容,编译,烧写!

    有两种方法可以测试是否成功:

    其一就是 系统启动后,打开一个带振动的APP看能否实现震动功能。

    其二调试口中 向设备文件中写数据.列如:

    echo "1000">>/sys/class/timed_output/vibrator/enable          //震动1S中

    试验成功! 大功告成!

     这里补充一下,关于android 应用程序中震动的的调用方法:在别人博客看到了一个写的很好贴出网址,供大家参考

    http://blog.csdn.net/czh4869623/article/details/8956370

    这里总结一下:通过这个例程学会了安卓驱动开发的一般步骤,对安卓每个层的认识都有深入。是个非常好的开始。这种从上往下的分析方法只适合于简单的系统,和项目时间要求不高的情况下,我在分析上就浪费了不少时间。项目比较紧张的话,直接百度一个历程按照说明改一下就成了,复杂的系统涉及的东西太多,比如视频之类的,一路分析下去的话可能半个月不一定能搞定。这个驱动属于非常简单,但是实际动手时候,还是多参考别人的例程,毕竟水平不高,再简单的驱动也不一定能想的周全。

  • 相关阅读:
    如何证明Application Domain的隔离性
    Programming clojure – Multimethods
    Kafka: a Distributed Messaging System for Log Processing
    Practical Clojure Parallel Programming
    The Joy of Clojure – Clojure philosophy(1)
    Columnar Storage
    Programming clojure – Concurrency
    Practical clojure – Macros and Metaprogramming
    Linkedin Databus
    Leiningen
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/9389282.html
Copyright © 2020-2023  润新知