Android震動vibrator系統開發全過程

1、前言java

本人剛學習安卓驅動開發,水平不能說菜,是根本沒有水平,在這裏把學習過程貼出來,跟你們一塊兒學習交流,還望你們多多指正,轉載的請標明出處。linux

2、android驅動介紹android

安卓整體架構是在 Linux內核基礎上,增長硬件抽象層(HAL),運行庫,java虛擬機,程序框架等組成的,具體以下圖。多線程


 


安卓的應用程序是從application framework層架構上創建的。全部APK應用程序都是經過framework層來運行的。application framework是google寫好的,除非本身深度定製,通常是不會更改這個層的。對於驅動開發來說,咱們要作的就是讓framework層能認識並操做咱們的硬件設備就OK了。所以咱們關心主要有3個層面:架構

linux Kernel層app

HAL層框架

JNI層ide

1.       linuxKernel:是google在linux內核基礎上,專門爲移動設備優化後的內核,增長修改一些東西,擔修改的很少,對於內核驅動來說,基本沒有修改,作過linux驅動開發的人應該很容易理解。函數

2.       HAL,硬件抽象層:簡單來講,就是對Linux 內核驅動程序的封裝,向上提供接口,屏蔽低層的實現細節。也就是說,把對硬件的支持分紅了兩層,一層放在用戶空間(User Space),一層放在內核空間(Kernel Space),其中,硬件抽象層運行在用戶空間。用戶空間不屬於內核沒必要遵照GPL協議,各個廠商能夠把與本身硬件設備相關,具備商業機密的一些代碼放在HAL層。學習

3.       JNI層:提供java和底層C、C++的動態連接庫的接口。我理解的是JNI就是一個代理,能夠把C和C++生成的接口函數翻譯成Java可用,提供給framework層。

3、振動系統開發過程

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層:這一層我找到了具體的代碼咱們會好分析不少,其代碼是:

android\frameworks\base\services\jni\ 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\n");  

  24.     vibrator_on(timeout_ms);  

  25. }  

  26.    

  27. static voidvibratorOff(JNIEnv *env, jobject clazz)//關閉振動器  

  28. {  

  29.     // LOGI("vibratorOff\n");  

  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層代碼:

\android40\hardware\libhardware_legacy\vibrator\vibrator.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\n",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框架實現的函數在:

\kernel\drivers\staging\android\timed_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\n",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\n",  

  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");  

\kernel\drivers\staging\android\timed_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_imx\drivers\vibrator\vibrator.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\n",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\n",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!\n");  

  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

這裏總結一下:經過這個例程學會了安卓驅動開發的通常步驟,對安卓每一個層的認識都有深刻。是個很是好的開始。這種從上往下的分析方法只適合於簡單的系統,和項目時間要求不高的狀況下,我在分析上就浪費了很多時間。項目比較緊張的話,直接百度一個歷程按照說明改一下就成了,複雜的系統涉及的東西太多,好比視頻之類的,一路分析下去的話可能半個月不必定能搞定。這個驅動屬於很是簡單,可是實際動手時候,仍是多參考別人的例程,畢竟水平不高,再簡單的驅動也不必定能想的周全。

相關文章
相關標籤/搜索