linux PWM蜂鳴器移植以及驅動程序分析【轉】


本文轉載自:https://blog.csdn.net/lxllinux/article/details/80885331

一、關於PWM:
        PWM(Pulse Width Modulation)——脈寬調制,它是利用微控制器的數字輸出來對模擬電路進行控制的一種非常有效的技術,廣泛應用於測量、通信、功率控制與變換等許多領域。

        通過S3C2440底板原理圖可知蜂鳴器接2440GPB0/TOUT0.即我們的蜂鳴器是通過GPB0 IO口使用PWM信號驅動工作的,而且GPB0口是一個復用的IO口,要使用它得把他設置成TOUT0 PWM輸出模式。

       蜂鳴器可以發聲,靠的不僅僅是蜂鳴器硬件的驅動,還有Linux下的PWM(脈沖寬度調制)這種變頻技術,靠改變脈沖寬度來控制輸出電壓,通過改變周期來控制其輸出頻率。通過改變頻率可以使蜂鳴器發出不同的聲音

二、修改設備樹

  1. beeper {
  2. compatible = "pwm-beeper";
  3. pwms = <&pwm 0 1000000 0>;
  4. pinctrl-names = "default";
  5. pinctrl -0 = <&pwm0_pin>;
  6. };

四、修改配置文件:

[yangni@yangni linux-3.0.54]$make menuconfig
SystemType  --->
         [ * ] PWM device support
DeviceDrivers  --->
           Input device support  --->
                       [* ] Miscellaneous devices  --->
                                    < * > PWM beeper support
 
     配置完成以后就可以make了,然后將重新編譯好的內核下載到開發板上,開發板啟動的時候會聽到“滴”的一聲響。並且,在開發板會打印出如下相關的信息:
 
五、測試程序:
     首先得查找出事件編號。因為后來移植過按鍵驅動,所以按鍵驅動被分配為input0了,導致后來再運行打開不了,所以測試程序寫的時候,open的時候要open對。
   cat proc/bus/input/devices

  1.  
  1. /*********************************************************************************
  2. * Copyright: (C) 2017 qicheng
  3. * All rights reserved.
  4. *
  5. * Filename: beer_test.c
  6. * Description: This file
  7. *
  8. * Version: 1.0.0(04/19/2017)
  9. * Author: yangni <497049229@qq.com>
  10. * ChangeLog: 1, Release initial version on "04/19/2017 11:44:58 AM"
  11. *
  12. ********************************************************************************/
  13. #include<stdint.h>
  14. #include<string.h>
  15. #include<fcntl.h>
  16. #include<unistd.h>
  17. #include<stdio.h>
  18. #include<linux/input.h>
  19. #include<unistd.h>
  20. int main(int argc, char *argv[])
  21. {
  22. int fd, version, ret;
  23. int i;
  24. struct input_event event;
  25. if ((fd = open( "/dev/event1", O_RDWR)) < 0) {
  26. perror( "beep test");
  27. return 1;
  28. }
  29. event.type = EV_SND;
  30. event.code = SND_TONE;
  31. if(! strcmp (argv[ 1], "1"))
  32. {
  33. for(i= 0;i<= 7;i++)
  34. {
  35. event.value = 2000; //打開蜂鳴器
  36. ret = write(fd, &event, sizeof(struct input_event));
  37. sleep( 0.5);
  38. event.value = 0; //關閉蜂鳴器
  39. ret = write(fd, &event, sizeof(struct input_event));
  40. }
  41. }
  42. else if(! strcmp (argv[ 1], "0"))
  43. {
  44. event.value = 0;
  45. ret = write(fd, &event, sizeof(struct input_event));
  46. }
  47. close(fd);
  48. return 0;
  49. }
從應用程序中得到的input_event.code只能為SND_BELL或SND_TONE,否則退出。
如果為SND_BELL,不管input_event.value為多少,最終的value只能為1000或0,即不能改變蜂鳴器的頻率。1000表示蜂鳴器打開,0表示蜂鳴器關閉。
      如果為SND_TONE,則可以通過改變input_event.value的值來調整蜂鳴器的頻率,從而發出各種不同的音調。
六、驅動程序分析:
     該驅動程序位於:drivers/input/misc/pwm-beeper.c
[yangni@yangni linux-3.0.54]$ vim drivers/input/misc/pwm-beeper.c
  1. /* Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
  2. * PWM beeper driver
  3. *
  4. * This program is free software; you can redistribute it and/or modify it
  5. * under the terms of the GNU General Public License as published by the
  6. * Free Software Foundation; either version 2 of the License, or (at your
  7. * option) any later version.
  8. *
  9. * You should have received a copy of the GNU General Public License along
  10. * with this program; if not, write to the Free Software Foundation, Inc.,
  11. * 675 Mass Ave, Cambridge, MA 02139, USA.
  12. *
  13. */
  14. #include <linux/input.h>
  15. #include <linux/module.h>
  16. #include <linux/kernel.h>
  17. #include <linux/platform_device.h>
  18. #include <linux/pwm.h>
  19. #include <linux/slab.h>
  20. struct pwm_beeper {
  21. struct input_dev *input;
  22. struct pwm_device *pwm;
  23. unsigned long period;
  24. };
  25. #define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
  26. //事件處理函數
  27. static int pwm_beeper_event(struct input_dev *input,
  28. unsigned int type, unsigned int code, int value)
  29. {
  30. int ret = 0;
  31. struct pwm_beeper *beeper = input_get_drvdata(input);
  32. unsigned long period;
  33. if (type != EV_SND || value < 0)
  34. return -EINVAL; //若input_event.type不是EV_SND或input_event.value小於0,則退出
  35. switch (code) {
  36. case SND_BELL:
  37. value = value ? 1000 : 0;
  38. break;
  39. case SND_TONE:
  40. break;
  41. default:
  42. return -EINVAL;
  43. }
  44. if (value == 0) {
  45. pwm_config(beeper->pwm, 0, 0); //配置PWM函數
  46. pwm_disable(beeper->pwm); //關閉PWM函數
  47. } else {
  48. period = HZ_TO_NANOSECONDS(value);
  49. ret = pwm_config(beeper->pwm, period / 2, period);
  50. if (ret)
  51. return ret;
  52. ret = pwm_enable(beeper->pwm);
  53. if (ret)
  54. return ret;
  55. beeper->period = period;
  56. }
  57. return 0;
  58. }
  59. static int __ devinit pwm_beeper_probe(struct platform_device *pdev)
  60. {
  61. unsigned long pwm_id = ( unsigned long)pdev->dev.platform_data;
  62. struct pwm_beeper *beeper;
  63. int error;
  64. //為蜂鳴器設備開辟一段內存空間,並清零
  65. beeper = kzalloc( sizeof(*beeper), GFP_KERNEL);
  66. if (!beeper)
  67. return -ENOMEM;
  68. beeper->pwm = pwm_request(pwm_id, "pwm beeper"); //申請一個PWM設備
  69. if (IS_ERR(beeper->pwm)) {
  70. error = PTR_ERR(beeper->pwm);
  71. dev_err(&pdev->dev, "Failed to request pwm device: %d\n", error);
  72. goto err_free;
  73. }
  74. beeper->input = input_allocate_device(); //為輸入設備結構體分配一個空間,並對其主要的成員進行了初始化.
  75. if (!beeper->input) {
  76. dev_err(&pdev->dev, "Failed to allocate input device\n");
  77. error = -ENOMEM;
  78. goto err_pwm_free;
  79. }
  80. beeper->input->dev.parent = &pdev->dev; //將輸入信息賦值到輸入子系統
  81. beeper->input->name = "pwm-beeper";
  82. beeper->input->phys = "pwm/input0";
  83. beeper->input->id.bustype = BUS_HOST;
  84. beeper->input->id.vendor = 0x001f;
  85. beeper->input->id.product = 0x0001;
  86. beeper->input->id.version = 0x0100;
  87. beeper->input->evbit[ 0] = BIT(EV_SND);
  88.  
  89. beeper->input->sndbit[ 0] = BIT(SND_TONE) | BIT(SND_BELL);
  90. beeper->input->event = pwm_beeper_event; //給出 事件處理函數
  91. input_set_drvdata(beeper->input, beeper);
  92. error = input_register_device(beeper->input);
  93. if (error)
  94. {
  95. dev_err(&pdev->dev, "Failed to register input device: %d\n", error);
  96. goto err_input_free;
  97. }
  98. platform_set_drvdata(pdev, beeper);
  99. return 0;
  100. err_input_free: input_free_device(beeper->input);
  101. err_pwm_free: pwm_free(beeper->pwm);
  102. err_free: kfree(beeper); return error;
  103. }
  104. //移除pwm設備
  105. static int __ devexit pwm_beeper_remove(struct platform_device *pdev)
  106. {
  107.      struct pwm_beeper *beeper = platform_get_drvdata(pdev);
  108.      platform_set_drvdata(pdev, NULL);
  109.      input_unregister_device(beeper->input); //卸載輸入子系統
  110.      pwm_disable(beeper->pwm);
  111.     pwm_free(beeper->pwm);
  112.     kfree(beeper);
  113.      return 0;
  114. }
  115. #ifdef CONFIG_PM
  116. static int pwm_beeper_suspend(struct device *dev)
  117. {
  118.      struct pwm_beeper *beeper = dev_get_drvdata(dev);
  119.      if (beeper->period)
  120.          pwm_disable(beeper->pwm);
  121.      return 0;
  122. }
  123. static int pwm_beeper_resume(struct device *dev)
  124. {
  125.      struct pwm_beeper *beeper = dev_get_drvdata(dev);
  126.      if (beeper->period)
  127.      { pwm_config(beeper->pwm, beeper->period / 2, beeper->period);
  128.          pwm_enable(beeper->pwm);
  129. }
  130. return 0;
  131. }
  132. static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops, pwm_beeper_suspend, pwm_beeper_resume);
  133. #define PWM_BEEPER_PM_OPS (&pwm_beeper_pm_ops)
  134. #else
  135. #define PWM_BEEPER_PM_OPS NULL
  136. #endif
  137. //創建平台驅動,此處name與上面我們在mach-smdk2440.c添加的名字相同,用於匹配
  138. static struct platform_driver pwm_beeper_driver = {
  139.     .probe = pwm_beeper_probe,
  140.     .remove = __devexit_p(pwm_beeper_remove),
  141.     .driver = { .name = "pwm-beeper",
  142.             .owner = THIS_MODULE,
  143.             .pm = PWM_BEEPER_PM_OPS,
  144.            },
  145.      };
  146. //驅動入口
  147. static int __ init pwm_beeper_init(void)
  148. {
  149.     return platform_driver_register(&pwm_beeper_driver); //注冊平台驅動
  150. }
  151. module_init(pwm_beeper_init);
  152. static void __ exit pwm_beeper_exit(void)
  153. {
  154.    platform_driver_unregister(&pwm_beeper_driver);
  155. }
  156. module_exit(pwm_beeper_exit);
  157. MODULE_AUTHOR( "Lars-Peter Clausen <lars@metafoo.de>");
  158. MODULE_DESCRIPTION( "PWM beeper driver");
  159. MODULE_LICENSE( "GPL");
  160. MODULE_ALIAS( "platform:pwm-beeper");
 內核使能的是驅動,而設備是我們自己添加的。該驅動程序工作流程與之前的類似:
1、創建平台驅動  static struct platform_driver pwm_beeper_driver()
        平台驅動的成員 name要與上面我們在mach-smdk2440.c添加的設備機構體成員name名字相同,方便用於匹配。
2、注冊平台驅動
pwm_beeper_init(void)中的函數platform_driver_register()函數用於驅動注冊。
3、調用pwm_beeper_probe()函數
設備與驅動匹配后,會調用pwm_beeper_probe()函數。該驅動中probe函數主要是用於申請pwm設備以及給輸入子系統賦值(指定事件處理函數)。
          beeper->input->evbit[ 0] = BIT(EV_SND);
  1. beeper->input->sndbit[ 0] = BIT(SND_TONE) | BIT(SND_BELL);
  2.  PWM蜂鳴器的 事件類型 EV_SND 聲音的類型 SND_TONE或SND_BELL
  3.   這兩項內容就是我們在應用程序中要用到的 input_event中的type和code
4、事件處理函數:             
  1. static int pwm_beeper_event(struct input_dev *input,
  2. unsigned int type, unsigned int code, int value)
  3. {
  4. int ret = 0;
  5. struct pwm_beeper *beeper = input_get_drvdata(input);
  6. unsigned long period;
  7. if (type != EV_SND || value < 0)
  8. return -EINVAL;
  9. switch (code) {
  10. case SND_BELL:
  11. value = value ? 1000 : 0;
  12. break;
  13. case SND_TONE:
  14. break;
  15. default:
  16. return -EINVAL;
  17. }
  18. if (value == 0) {
  19. pwm_config(beeper->pwm, 0, 0); //配置PWM函數
  20. pwm_disable(beeper->pwm); //關閉PWM函數
  21. } else {
  22. period = HZ_TO_NANOSECONDS(value);
  23. ret = pwm_config(beeper->pwm, period / 2, period);
  24. if (ret)
  25. return ret;
  26. ret = pwm_enable(beeper->pwm);
  27. if (ret)
  28. return ret;
  29. beeper->period = period;
  30. }
  31. return 0;
  32. }
     這里是應用程序調用的關鍵。首先通過判斷 事件類型event是不是 EV_SND,不是則退出。
     如果是event是EV_SND類型,則接着判斷聲音類型code的值:
  •      如果code為SND_BELL,不管input_event.value為多少,最終的value只能為1000或0,即不能改變蜂鳴器的頻率。0表示關閉。
  •      如果code為SND_TONE,則可以通過改變input_event.value的值來調整蜂鳴器的頻率,從而發出各種不同的音調。
5、卸載平台驅動
pwm_beeper_exit(void)中的函數platform_driver_unregister函數用於驅動退出。
 
6、輸入子系統簡介:

Linux輸入子系統包括三個層次:事件處理層(Event Handler)核心層(Input Core)驅動層(Input Driver)

1.事件處理層負責與用戶程序打交道,將核心層傳來的事件報告給用戶程序。

2.核心層是鏈接其他兩個層之間的紐帶與橋梁,向下提供驅動層的接口,向上提供事件處理層的接口。

3.驅動層負責操作具體的硬件設備,這層的代碼是針對具體的驅動程序的,鍵盤、鼠標、觸摸屏等字符設備驅動功能的實現工作主要就在這層。

         輸入子系統有三個核心結構體:input_dev,input_handleinput_handler。input_dev表示一個輸入設備,包含輸入設備的一些相關信息;input_handler表示對輸入事件的具體處理,它為輸入設備的功能實現了一個接口;input_handle是用來連接輸入設備和輸入事件。輸入子系統主要的任務就是把這三個結構體連接在一起。

總結:
該驅動新的知識點主要涉及申請pwm設備以及對事件處理函數的理解,還有就是輸入子系統的使用,輸入子系統詳細使用可參考博客:
   

 

一、關於PWM:
        PWM(Pulse Width Modulation)——脈寬調制,它是利用微控制器的數字輸出來對模擬電路進行控制的一種非常有效的技術,廣泛應用於測量、通信、功率控制與變換等許多領域。

        通過S3C2440底板原理圖可知蜂鳴器接2440GPB0/TOUT0.即我們的蜂鳴器是通過GPB0 IO口使用PWM信號驅動工作的,而且GPB0口是一個復用的IO口,要使用它得把他設置成TOUT0 PWM輸出模式。

       蜂鳴器可以發聲,靠的不僅僅是蜂鳴器硬件的驅動,還有Linux下的PWM(脈沖寬度調制)這種變頻技術,靠改變脈沖寬度來控制輸出電壓,通過改變周期來控制其輸出頻率。通過改變頻率可以使蜂鳴器發出不同的聲音

二、修改設備樹

  1. beeper {
  2. compatible = "pwm-beeper";
  3. pwms = <&pwm 0 1000000 0>;
  4. pinctrl-names = "default";
  5. pinctrl -0 = <&pwm0_pin>;
  6. };

四、修改配置文件:

[yangni@yangni linux-3.0.54]$make menuconfig
SystemType  --->
         [ * ] PWM device support
DeviceDrivers  --->
           Input device support  --->
                       [* ] Miscellaneous devices  --->
                                    < * > PWM beeper support
 
     配置完成以后就可以make了,然后將重新編譯好的內核下載到開發板上,開發板啟動的時候會聽到“滴”的一聲響。並且,在開發板會打印出如下相關的信息:
 
五、測試程序:
     首先得查找出事件編號。因為后來移植過按鍵驅動,所以按鍵驅動被分配為input0了,導致后來再運行打開不了,所以測試程序寫的時候,open的時候要open對。
   cat proc/bus/input/devices

  1.  
  1. /*********************************************************************************
  2. * Copyright: (C) 2017 qicheng
  3. * All rights reserved.
  4. *
  5. * Filename: beer_test.c
  6. * Description: This file
  7. *
  8. * Version: 1.0.0(04/19/2017)
  9. * Author: yangni <497049229@qq.com>
  10. * ChangeLog: 1, Release initial version on "04/19/2017 11:44:58 AM"
  11. *
  12. ********************************************************************************/
  13. #include<stdint.h>
  14. #include<string.h>
  15. #include<fcntl.h>
  16. #include<unistd.h>
  17. #include<stdio.h>
  18. #include<linux/input.h>
  19. #include<unistd.h>
  20. int main(int argc, char *argv[])
  21. {
  22. int fd, version, ret;
  23. int i;
  24. struct input_event event;
  25. if ((fd = open( "/dev/event1", O_RDWR)) < 0) {
  26. perror( "beep test");
  27. return 1;
  28. }
  29. event.type = EV_SND;
  30. event.code = SND_TONE;
  31. if(! strcmp (argv[ 1], "1"))
  32. {
  33. for(i= 0;i<= 7;i++)
  34. {
  35. event.value = 2000; //打開蜂鳴器
  36. ret = write(fd, &event, sizeof(struct input_event));
  37. sleep( 0.5);
  38. event.value = 0; //關閉蜂鳴器
  39. ret = write(fd, &event, sizeof(struct input_event));
  40. }
  41. }
  42. else if(! strcmp (argv[ 1], "0"))
  43. {
  44. event.value = 0;
  45. ret = write(fd, &event, sizeof(struct input_event));
  46. }
  47. close(fd);
  48. return 0;
  49. }
從應用程序中得到的input_event.code只能為SND_BELL或SND_TONE,否則退出。
如果為SND_BELL,不管input_event.value為多少,最終的value只能為1000或0,即不能改變蜂鳴器的頻率。1000表示蜂鳴器打開,0表示蜂鳴器關閉。
      如果為SND_TONE,則可以通過改變input_event.value的值來調整蜂鳴器的頻率,從而發出各種不同的音調。
六、驅動程序分析:
     該驅動程序位於:drivers/input/misc/pwm-beeper.c
[yangni@yangni linux-3.0.54]$ vim drivers/input/misc/pwm-beeper.c
  1. /* Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
  2. * PWM beeper driver
  3. *
  4. * This program is free software; you can redistribute it and/or modify it
  5. * under the terms of the GNU General Public License as published by the
  6. * Free Software Foundation; either version 2 of the License, or (at your
  7. * option) any later version.
  8. *
  9. * You should have received a copy of the GNU General Public License along
  10. * with this program; if not, write to the Free Software Foundation, Inc.,
  11. * 675 Mass Ave, Cambridge, MA 02139, USA.
  12. *
  13. */
  14. #include <linux/input.h>
  15. #include <linux/module.h>
  16. #include <linux/kernel.h>
  17. #include <linux/platform_device.h>
  18. #include <linux/pwm.h>
  19. #include <linux/slab.h>
  20. struct pwm_beeper {
  21. struct input_dev *input;
  22. struct pwm_device *pwm;
  23. unsigned long period;
  24. };
  25. #define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
  26. //事件處理函數
  27. static int pwm_beeper_event(struct input_dev *input,
  28. unsigned int type, unsigned int code, int value)
  29. {
  30. int ret = 0;
  31. struct pwm_beeper *beeper = input_get_drvdata(input);
  32. unsigned long period;
  33. if (type != EV_SND || value < 0)
  34. return -EINVAL; //若input_event.type不是EV_SND或input_event.value小於0,則退出
  35. switch (code) {
  36. case SND_BELL:
  37. value = value ? 1000 : 0;
  38. break;
  39. case SND_TONE:
  40. break;
  41. default:
  42. return -EINVAL;
  43. }
  44. if (value == 0) {
  45. pwm_config(beeper->pwm, 0, 0); //配置PWM函數
  46. pwm_disable(beeper->pwm); //關閉PWM函數
  47. } else {
  48. period = HZ_TO_NANOSECONDS(value);
  49. ret = pwm_config(beeper->pwm, period / 2, period);
  50. if (ret)
  51. return ret;
  52. ret = pwm_enable(beeper->pwm);
  53. if (ret)
  54. return ret;
  55. beeper->period = period;
  56. }
  57. return 0;
  58. }
  59. static int __ devinit pwm_beeper_probe(struct platform_device *pdev)
  60. {
  61. unsigned long pwm_id = ( unsigned long)pdev->dev.platform_data;
  62. struct pwm_beeper *beeper;
  63. int error;
  64. //為蜂鳴器設備開辟一段內存空間,並清零
  65. beeper = kzalloc( sizeof(*beeper), GFP_KERNEL);
  66. if (!beeper)
  67. return -ENOMEM;
  68. beeper->pwm = pwm_request(pwm_id, "pwm beeper"); //申請一個PWM設備
  69. if (IS_ERR(beeper->pwm)) {
  70. error = PTR_ERR(beeper->pwm);
  71. dev_err(&pdev->dev, "Failed to request pwm device: %d\n", error);
  72. goto err_free;
  73. }
  74. beeper->input = input_allocate_device(); //為輸入設備結構體分配一個空間,並對其主要的成員進行了初始化.
  75. if (!beeper->input) {
  76. dev_err(&pdev->dev, "Failed to allocate input device\n");
  77. error = -ENOMEM;
  78. goto err_pwm_free;
  79. }
  80. beeper->input->dev.parent = &pdev->dev; //將輸入信息賦值到輸入子系統
  81. beeper->input->name = "pwm-beeper";
  82. beeper->input->phys = "pwm/input0";
  83. beeper->input->id.bustype = BUS_HOST;
  84. beeper->input->id.vendor = 0x001f;
  85. beeper->input->id.product = 0x0001;
  86. beeper->input->id.version = 0x0100;
  87. beeper->input->evbit[ 0] = BIT(EV_SND);
  88.  
  89. beeper->input->sndbit[ 0] = BIT(SND_TONE) | BIT(SND_BELL);
  90. beeper->input->event = pwm_beeper_event; //給出 事件處理函數
  91. input_set_drvdata(beeper->input, beeper);
  92. error = input_register_device(beeper->input);
  93. if (error)
  94. {
  95. dev_err(&pdev->dev, "Failed to register input device: %d\n", error);
  96. goto err_input_free;
  97. }
  98. platform_set_drvdata(pdev, beeper);
  99. return 0;
  100. err_input_free: input_free_device(beeper->input);
  101. err_pwm_free: pwm_free(beeper->pwm);
  102. err_free: kfree(beeper); return error;
  103. }
  104. //移除pwm設備
  105. static int __ devexit pwm_beeper_remove(struct platform_device *pdev)
  106. {
  107.      struct pwm_beeper *beeper = platform_get_drvdata(pdev);
  108.      platform_set_drvdata(pdev, NULL);
  109.      input_unregister_device(beeper->input); //卸載輸入子系統
  110.      pwm_disable(beeper->pwm);
  111.     pwm_free(beeper->pwm);
  112.     kfree(beeper);
  113.      return 0;
  114. }
  115. #ifdef CONFIG_PM
  116. static int pwm_beeper_suspend(struct device *dev)
  117. {
  118.      struct pwm_beeper *beeper = dev_get_drvdata(dev);
  119.      if (beeper->period)
  120.          pwm_disable(beeper->pwm);
  121.      return 0;
  122. }
  123. static int pwm_beeper_resume(struct device *dev)
  124. {
  125.      struct pwm_beeper *beeper = dev_get_drvdata(dev);
  126.      if (beeper->period)
  127.      { pwm_config(beeper->pwm, beeper->period / 2, beeper->period);
  128.          pwm_enable(beeper->pwm);
  129. }
  130. return 0;
  131. }
  132. static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops, pwm_beeper_suspend, pwm_beeper_resume);
  133. #define PWM_BEEPER_PM_OPS (&pwm_beeper_pm_ops)
  134. #else
  135. #define PWM_BEEPER_PM_OPS NULL
  136. #endif
  137. //創建平台驅動,此處name與上面我們在mach-smdk2440.c添加的名字相同,用於匹配
  138. static struct platform_driver pwm_beeper_driver = {
  139.     .probe = pwm_beeper_probe,
  140.     .remove = __devexit_p(pwm_beeper_remove),
  141.     .driver = { .name = "pwm-beeper",
  142.             .owner = THIS_MODULE,
  143.             .pm = PWM_BEEPER_PM_OPS,
  144.            },
  145.      };
  146. //驅動入口
  147. static int __ init pwm_beeper_init(void)
  148. {
  149.     return platform_driver_register(&pwm_beeper_driver); //注冊平台驅動
  150. }
  151. module_init(pwm_beeper_init);
  152. static void __ exit pwm_beeper_exit(void)
  153. {
  154.    platform_driver_unregister(&pwm_beeper_driver);
  155. }
  156. module_exit(pwm_beeper_exit);
  157. MODULE_AUTHOR( "Lars-Peter Clausen <lars@metafoo.de>");
  158. MODULE_DESCRIPTION( "PWM beeper driver");
  159. MODULE_LICENSE( "GPL");
  160. MODULE_ALIAS( "platform:pwm-beeper");
 內核使能的是驅動,而設備是我們自己添加的。該驅動程序工作流程與之前的類似:
1、創建平台驅動  static struct platform_driver pwm_beeper_driver()
        平台驅動的成員 name要與上面我們在mach-smdk2440.c添加的設備機構體成員name名字相同,方便用於匹配。
2、注冊平台驅動
pwm_beeper_init(void)中的函數platform_driver_register()函數用於驅動注冊。
3、調用pwm_beeper_probe()函數
設備與驅動匹配后,會調用pwm_beeper_probe()函數。該驅動中probe函數主要是用於申請pwm設備以及給輸入子系統賦值(指定事件處理函數)。
          beeper->input->evbit[ 0] = BIT(EV_SND);
  1. beeper->input->sndbit[ 0] = BIT(SND_TONE) | BIT(SND_BELL);
  2.  PWM蜂鳴器的 事件類型 EV_SND 聲音的類型 SND_TONE或SND_BELL
  3.   這兩項內容就是我們在應用程序中要用到的 input_event中的type和code
4、事件處理函數:             
  1. static int pwm_beeper_event(struct input_dev *input,
  2. unsigned int type, unsigned int code, int value)
  3. {
  4. int ret = 0;
  5. struct pwm_beeper *beeper = input_get_drvdata(input);
  6. unsigned long period;
  7. if (type != EV_SND || value < 0)
  8. return -EINVAL;
  9. switch (code) {
  10. case SND_BELL:
  11. value = value ? 1000 : 0;
  12. break;
  13. case SND_TONE:
  14. break;
  15. default:
  16. return -EINVAL;
  17. }
  18. if (value == 0) {
  19. pwm_config(beeper->pwm, 0, 0); //配置PWM函數
  20. pwm_disable(beeper->pwm); //關閉PWM函數
  21. } else {
  22. period = HZ_TO_NANOSECONDS(value);
  23. ret = pwm_config(beeper->pwm, period / 2, period);
  24. if (ret)
  25. return ret;
  26. ret = pwm_enable(beeper->pwm);
  27. if (ret)
  28. return ret;
  29. beeper->period = period;
  30. }
  31. return 0;
  32. }
     這里是應用程序調用的關鍵。首先通過判斷 事件類型event是不是 EV_SND,不是則退出。
     如果是event是EV_SND類型,則接着判斷聲音類型code的值:
  •      如果code為SND_BELL,不管input_event.value為多少,最終的value只能為1000或0,即不能改變蜂鳴器的頻率。0表示關閉。
  •      如果code為SND_TONE,則可以通過改變input_event.value的值來調整蜂鳴器的頻率,從而發出各種不同的音調。
5、卸載平台驅動
pwm_beeper_exit(void)中的函數platform_driver_unregister函數用於驅動退出。
 
6、輸入子系統簡介:

Linux輸入子系統包括三個層次:事件處理層(Event Handler)核心層(Input Core)驅動層(Input Driver)

1.事件處理層負責與用戶程序打交道,將核心層傳來的事件報告給用戶程序。

2.核心層是鏈接其他兩個層之間的紐帶與橋梁,向下提供驅動層的接口,向上提供事件處理層的接口。

3.驅動層負責操作具體的硬件設備,這層的代碼是針對具體的驅動程序的,鍵盤、鼠標、觸摸屏等字符設備驅動功能的實現工作主要就在這層。

         輸入子系統有三個核心結構體:input_dev,input_handleinput_handler。input_dev表示一個輸入設備,包含輸入設備的一些相關信息;input_handler表示對輸入事件的具體處理,它為輸入設備的功能實現了一個接口;input_handle是用來連接輸入設備和輸入事件。輸入子系統主要的任務就是把這三個結構體連接在一起。

總結:
該驅動新的知識點主要涉及申請pwm設備以及對事件處理函數的理解,還有就是輸入子系統的使用,輸入子系統詳細使用可參考博客:
   

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM