本文轉載自:https://blog.csdn.net/lxllinux/article/details/80885331
一、關於PWM:
PWM(Pulse Width Modulation)——脈寬調制,它是利用微控制器的數字輸出來對模擬電路進行控制的一種非常有效的技術,廣泛應用於測量、通信、功率控制與變換等許多領域。
通過S3C2440底板原理圖可知蜂鳴器接2440的GPB0/TOUT0.即我們的蜂鳴器是通過GPB0 IO口使用PWM信號驅動工作的,而且GPB0口是一個復用的IO口,要使用它得把他設置成TOUT0 PWM輸出模式。
蜂鳴器可以發聲,靠的不僅僅是蜂鳴器硬件的驅動,還有Linux下的PWM(脈沖寬度調制)這種變頻技術,靠改變脈沖寬度來控制輸出電壓,通過改變周期來控制其輸出頻率。通過改變頻率可以使蜂鳴器發出不同的聲音。
二、修改設備樹
-
-
compatible =
"pwm-beeper";
-
pwms = <&pwm
0
1000000
0>;
-
pinctrl-names =
"default";
-
pinctrl
-0 = <&pwm0_pin>;
-
四、修改配置文件:
[yangni@yangni linux-3.0.54]$make menuconfig
[ * ] PWM device support
DeviceDrivers --->
Input device support --->
[* ] Miscellaneous devices --->
< * > PWM beeper support
配置完成以后就可以make了,然后將重新編譯好的內核下載到開發板上,開發板啟動的時候會聽到“滴”的一聲響。並且,在開發板會打印出如下相關的信息:
五、測試程序:
首先得查找出事件編號。因為后來移植過按鍵驅動,所以按鍵驅動被分配為input0了,導致后來再運行打開不了,所以測試程序寫的時候,open的時候要open對。
cat proc/bus/input/devices
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
int main(int argc, char *argv[])
-
-
-
-
struct input_event event;
-
if ((fd = open(
"/dev/event1", O_RDWR)) <
0) {
-
-
-
-
-
-
if(!
strcmp (argv[
1],
"1"))
-
-
-
-
-
ret = write(fd, &event,
sizeof(struct input_event));
-
-
-
ret = write(fd, &event,
sizeof(struct input_event));
-
-
-
else
if(!
strcmp (argv[
1],
"0"))
-
-
-
ret = write(fd, &event,
sizeof(struct input_event));
-
-
-
-
從應用程序中得到的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
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
#include <linux/module.h>
-
#include <linux/kernel.h>
-
#include <linux/platform_device.h>
-
-
-
-
-
-
-
-
#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
-
-
static int pwm_beeper_event(struct input_dev *input,
-
unsigned
int type,
unsigned
int code,
int value)
-
-
-
struct pwm_beeper *beeper = input_get_drvdata(input);
-
-
if (type != EV_SND || value <
0)
-
-
-
-
value = value ?
1000 :
0;
-
-
-
-
-
-
-
-
pwm_config(beeper->pwm,
0,
0);
-
pwm_disable(beeper->pwm);
-
-
period = HZ_TO_NANOSECONDS(value);
-
ret = pwm_config(beeper->pwm, period /
2, period);
-
-
-
ret = pwm_enable(beeper->pwm);
-
-
-
-
-
-
-
static
int __
devinit pwm_beeper_probe(struct platform_device *pdev)
-
-
unsigned
long pwm_id = (
unsigned
long)pdev->dev.platform_data;
-
struct pwm_beeper *beeper;
-
-
-
beeper = kzalloc(
sizeof(*beeper), GFP_KERNEL);
-
-
-
beeper->pwm = pwm_request(pwm_id,
"pwm beeper");
-
if (IS_ERR(beeper->pwm)) {
-
error = PTR_ERR(beeper->pwm);
-
dev_err(&pdev->dev,
"Failed to request pwm device: %d\n", error);
-
-
-
beeper->input = input_allocate_device();
-
-
dev_err(&pdev->dev,
"Failed to allocate input device\n");
-
-
-
-
beeper->input->dev.parent = &pdev->dev;
-
beeper->input->name =
"pwm-beeper";
-
beeper->input->phys =
"pwm/input0";
-
beeper->input->id.bustype = BUS_HOST;
-
beeper->input->id.vendor =
0x001f;
-
beeper->input->id.product =
0x0001;
-
beeper->input->id.version =
0x0100;
-
beeper->input->evbit[
0] = BIT(EV_SND);
-
-
beeper->input->sndbit[
0] = BIT(SND_TONE) | BIT(SND_BELL);
-
beeper->input->event = pwm_beeper_event;
-
input_set_drvdata(beeper->input, beeper);
-
error = input_register_device(beeper->input);
-
-
-
dev_err(&pdev->dev,
"Failed to register input device: %d\n", error);
-
-
-
platform_set_drvdata(pdev, beeper);
-
-
err_input_free: input_free_device(beeper->input);
-
err_pwm_free: pwm_free(beeper->pwm);
-
err_free: kfree(beeper);
return error;
-
-
-
static
int __
devexit pwm_beeper_remove(struct platform_device *pdev)
-
-
struct pwm_beeper *beeper = platform_get_drvdata(pdev);
-
platform_set_drvdata(pdev,
NULL);
-
input_unregister_device(beeper->input);
-
pwm_disable(beeper->pwm);
-
-
-
-
-
-
static int pwm_beeper_suspend(struct device *dev)
-
-
struct pwm_beeper *beeper = dev_get_drvdata(dev);
-
-
pwm_disable(beeper->pwm);
-
-
-
static int pwm_beeper_resume(struct device *dev)
-
-
struct pwm_beeper *beeper = dev_get_drvdata(dev);
-
-
{ pwm_config(beeper->pwm, beeper->period /
2, beeper->period);
-
-
-
-
-
static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops, pwm_beeper_suspend, pwm_beeper_resume);
-
#define PWM_BEEPER_PM_OPS (&pwm_beeper_pm_ops)
-
-
#define PWM_BEEPER_PM_OPS NULL
-
-
-
static
struct platform_driver pwm_beeper_driver = {
-
.probe = pwm_beeper_probe,
-
.remove = __devexit_p(pwm_beeper_remove),
-
.driver = { .name =
"pwm-beeper",
-
-
-
-
-
-
static
int __
init pwm_beeper_init(void)
-
-
return platform_driver_register(&pwm_beeper_driver);
-
-
module_init(pwm_beeper_init);
-
static
void __
exit pwm_beeper_exit(void)
-
-
platform_driver_unregister(&pwm_beeper_driver);
-
-
module_exit(pwm_beeper_exit);
-
MODULE_AUTHOR(
"Lars-Peter Clausen <lars@metafoo.de>");
-
MODULE_DESCRIPTION(
"PWM beeper driver");
-
-
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);
-
beeper->input->sndbit[
0] = BIT(SND_TONE) | BIT(SND_BELL);
-
PWM蜂鳴器的
事件類型
為
EV_SND
,
聲音的類型
為
SND_TONE或SND_BELL
-
這兩項內容就是我們在應用程序中要用到的
input_event中的type和code
4、事件處理函數:
-
static int pwm_beeper_event(struct input_dev *input,
-
unsigned
int type,
unsigned
int code,
int value)
-
-
-
struct pwm_beeper *beeper = input_get_drvdata(input);
-
-
if (type != EV_SND || value <
0)
-
-
-
-
value = value ?
1000 :
0;
-
-
-
-
-
-
-
-
pwm_config(beeper->pwm,
0,
0);
-
pwm_disable(beeper->pwm);
-
-
period = HZ_TO_NANOSECONDS(value);
-
ret = pwm_config(beeper->pwm, period /
2, period);
-
-
-
ret = pwm_enable(beeper->pwm);
-
-
-
-
-
-
這里是應用程序調用的關鍵。首先通過判斷
事件類型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_handle和input_handler。input_dev表示一個輸入設備,包含輸入設備的一些相關信息;input_handler表示對輸入事件的具體處理,它為輸入設備的功能實現了一個接口;input_handle是用來連接輸入設備和輸入事件。輸入子系統主要的任務就是把這三個結構體連接在一起。
總結:
該驅動新的知識點主要涉及申請pwm設備以及對事件處理函數的理解,還有就是輸入子系統的使用,輸入子系統詳細使用可參考博客:
一、關於PWM:
PWM(Pulse Width Modulation)——脈寬調制,它是利用微控制器的數字輸出來對模擬電路進行控制的一種非常有效的技術,廣泛應用於測量、通信、功率控制與變換等許多領域。
通過S3C2440底板原理圖可知蜂鳴器接2440的GPB0/TOUT0.即我們的蜂鳴器是通過GPB0 IO口使用PWM信號驅動工作的,而且GPB0口是一個復用的IO口,要使用它得把他設置成TOUT0 PWM輸出模式。
蜂鳴器可以發聲,靠的不僅僅是蜂鳴器硬件的驅動,還有Linux下的PWM(脈沖寬度調制)這種變頻技術,靠改變脈沖寬度來控制輸出電壓,通過改變周期來控制其輸出頻率。通過改變頻率可以使蜂鳴器發出不同的聲音。
二、修改設備樹
-
-
compatible =
"pwm-beeper";
-
pwms = <&pwm
0
1000000
0>;
-
pinctrl-names =
"default";
-
pinctrl
-0 = <&pwm0_pin>;
-
四、修改配置文件:
[yangni@yangni linux-3.0.54]$make menuconfig
[ * ] PWM device support
DeviceDrivers --->
Input device support --->
[* ] Miscellaneous devices --->
< * > PWM beeper support
配置完成以后就可以make了,然后將重新編譯好的內核下載到開發板上,開發板啟動的時候會聽到“滴”的一聲響。並且,在開發板會打印出如下相關的信息:
五、測試程序:
首先得查找出事件編號。因為后來移植過按鍵驅動,所以按鍵驅動被分配為input0了,導致后來再運行打開不了,所以測試程序寫的時候,open的時候要open對。
cat proc/bus/input/devices
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
int main(int argc, char *argv[])
-
-
-
-
struct input_event event;
-
if ((fd = open(
"/dev/event1", O_RDWR)) <
0) {
-
-
-
-
-
-
if(!
strcmp (argv[
1],
"1"))
-
-
-
-
-
ret = write(fd, &event,
sizeof(struct input_event));
-
-
-
ret = write(fd, &event,
sizeof(struct input_event));
-
-
-
else
if(!
strcmp (argv[
1],
"0"))
-
-
-
ret = write(fd, &event,
sizeof(struct input_event));
-
-
-
-
從應用程序中得到的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
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
#include <linux/module.h>
-
#include <linux/kernel.h>
-
#include <linux/platform_device.h>
-
-
-
-
-
-
-
-
#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
-
-
static int pwm_beeper_event(struct input_dev *input,
-
unsigned
int type,
unsigned
int code,
int value)
-
-
-
struct pwm_beeper *beeper = input_get_drvdata(input);
-
-
if (type != EV_SND || value <
0)
-
-
-
-
value = value ?
1000 :
0;
-
-
-
-
-
-
-
-
pwm_config(beeper->pwm,
0,
0);
-
pwm_disable(beeper->pwm);
-
-
period = HZ_TO_NANOSECONDS(value);
-
ret = pwm_config(beeper->pwm, period /
2, period);
-
-
-
ret = pwm_enable(beeper->pwm);
-
-
-
-
-
-
-
static
int __
devinit pwm_beeper_probe(struct platform_device *pdev)
-
-
unsigned
long pwm_id = (
unsigned
long)pdev->dev.platform_data;
-
struct pwm_beeper *beeper;
-
-
-
beeper = kzalloc(
sizeof(*beeper), GFP_KERNEL);
-
-
-
beeper->pwm = pwm_request(pwm_id,
"pwm beeper");
-
if (IS_ERR(beeper->pwm)) {
-
error = PTR_ERR(beeper->pwm);
-
dev_err(&pdev->dev,
"Failed to request pwm device: %d\n", error);
-
-
-
beeper->input = input_allocate_device();
-
-
dev_err(&pdev->dev,
"Failed to allocate input device\n");
-
-
-
-
beeper->input->dev.parent = &pdev->dev;
-
beeper->input->name =
"pwm-beeper";
-
beeper->input->phys =
"pwm/input0";
-
beeper->input->id.bustype = BUS_HOST;
-
beeper->input->id.vendor =
0x001f;
-
beeper->input->id.product =
0x0001;
-
beeper->input->id.version =
0x0100;
-
beeper->input->evbit[
0] = BIT(EV_SND);
-
-
beeper->input->sndbit[
0] = BIT(SND_TONE) | BIT(SND_BELL);
-
beeper->input->event = pwm_beeper_event;
-
input_set_drvdata(beeper->input, beeper);
-
error = input_register_device(beeper->input);
-
-
-
dev_err(&pdev->dev,
"Failed to register input device: %d\n", error);
-
-
-
platform_set_drvdata(pdev, beeper);
-
-
err_input_free: input_free_device(beeper->input);
-
err_pwm_free: pwm_free(beeper->pwm);
-
err_free: kfree(beeper);
return error;
-
-
-
static
int __
devexit pwm_beeper_remove(struct platform_device *pdev)
-
-
struct pwm_beeper *beeper = platform_get_drvdata(pdev);
-
platform_set_drvdata(pdev,
NULL);
-
input_unregister_device(beeper->input);
-
pwm_disable(beeper->pwm);
-
-
-
-
-
-
static int pwm_beeper_suspend(struct device *dev)
-
-
struct pwm_beeper *beeper = dev_get_drvdata(dev);
-
-
pwm_disable(beeper->pwm);
-
-
-
static int pwm_beeper_resume(struct device *dev)
-
-
struct pwm_beeper *beeper = dev_get_drvdata(dev);
-
-
{ pwm_config(beeper->pwm, beeper->period /
2, beeper->period);
-
-
-
-
-
static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops, pwm_beeper_suspend, pwm_beeper_resume);
-
#define PWM_BEEPER_PM_OPS (&pwm_beeper_pm_ops)
-
-
#define PWM_BEEPER_PM_OPS NULL
-
-
-
static
struct platform_driver pwm_beeper_driver = {
-
.probe = pwm_beeper_probe,
-
.remove = __devexit_p(pwm_beeper_remove),
-
.driver = { .name =
"pwm-beeper",
-
-
-
-
-
-
static
int __
init pwm_beeper_init(void)
-
-
return platform_driver_register(&pwm_beeper_driver);
-
-
module_init(pwm_beeper_init);
-
static
void __
exit pwm_beeper_exit(void)
-
-
platform_driver_unregister(&pwm_beeper_driver);
-
-
module_exit(pwm_beeper_exit);
-
MODULE_AUTHOR(
"Lars-Peter Clausen <lars@metafoo.de>");
-
MODULE_DESCRIPTION(
"PWM beeper driver");
-
-
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);
-
beeper->input->sndbit[
0] = BIT(SND_TONE) | BIT(SND_BELL);
-
PWM蜂鳴器的
事件類型
為
EV_SND
,
聲音的類型
為
SND_TONE或SND_BELL
-
這兩項內容就是我們在應用程序中要用到的
input_event中的type和code
4、事件處理函數:
-
static int pwm_beeper_event(struct input_dev *input,
-
unsigned
int type,
unsigned
int code,
int value)
-
-
-
struct pwm_beeper *beeper = input_get_drvdata(input);
-
-
if (type != EV_SND || value <
0)
-
-
-
-
value = value ?
1000 :
0;
-
-
-
-
-
-
-
-
pwm_config(beeper->pwm,
0,
0);
-
pwm_disable(beeper->pwm);
-
-
period = HZ_TO_NANOSECONDS(value);
-
ret = pwm_config(beeper->pwm, period /
2, period);
-
-
-
ret = pwm_enable(beeper->pwm);
-
-
-
-
-
-
這里是應用程序調用的關鍵。首先通過判斷
事件類型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_handle和input_handler。input_dev表示一個輸入設備,包含輸入設備的一些相關信息;input_handler表示對輸入事件的具體處理,它為輸入設備的功能實現了一個接口;input_handle是用來連接輸入設備和輸入事件。輸入子系統主要的任務就是把這三個結構體連接在一起。
總結:
該驅動新的知識點主要涉及申請pwm設備以及對事件處理函數的理解,還有就是輸入子系統的使用,輸入子系統詳細使用可參考博客: