本文转载自:https://blog.csdn.net/lxllinux/article/details/80885331
通过S3C2440底板原理图可知蜂鸣器接2440的GPB0/TOUT0.即我们的蜂鸣器是通过GPB0 IO口使用PWM信号驱动工作的,而且GPB0口是一个复用的IO口,要使用它得把他设置成TOUT0 PWM输出模式。
蜂鸣器可以发声,靠的不仅仅是蜂鸣器硬件的驱动,还有Linux下的PWM(脉冲宽度调制)这种变频技术,靠改变脉冲宽度来控制输出电压,通过改变周期来控制其输出频率。通过改变频率可以使蜂鸣器发出不同的声音。
二、修改设备树
-
beeper {
-
compatible = "pwm-beeper";
-
pwms = <&pwm 0 1000000 0>;
-
pinctrl-names = "default";
-
pinctrl -0 = <&pwm0_pin>;
-
};
四、修改配置文件:
-
/*********************************************************************************
-
* Copyright: (C) 2017 qicheng
-
* All rights reserved.
-
*
-
* Filename: beer_test.c
-
* Description: This file
-
*
-
* Version: 1.0.0(04/19/2017)
-
* Author: yangni <497049229@qq.com>
-
* ChangeLog: 1, Release initial version on "04/19/2017 11:44:58 AM"
-
*
-
********************************************************************************/
-
#include<stdint.h>
-
#include<string.h>
-
#include<fcntl.h>
-
#include<unistd.h>
-
#include<stdio.h>
-
#include<linux/input.h>
-
#include<unistd.h>
-
int main(int argc, char *argv[])
-
{
-
int fd, version, ret;
-
int i;
-
struct input_event event;
-
if ((fd = open( "/dev/event1", O_RDWR)) < 0) {
-
perror( "beep test");
-
return 1;
-
}
-
event.type = EV_SND;
-
event.code = SND_TONE;
-
if(! strcmp (argv[ 1], "1"))
-
{
-
for(i= 0;i<= 7;i++)
-
{
-
event.value = 2000; //打开蜂鸣器
-
ret = write(fd, &event, sizeof(struct input_event));
-
sleep( 0.5);
-
event.value = 0; //关闭蜂鸣器
-
ret = write(fd, &event, sizeof(struct input_event));
-
}
-
}
-
else if(! strcmp (argv[ 1], "0"))
-
{
-
event.value = 0;
-
ret = write(fd, &event, sizeof(struct input_event));
-
}
-
close(fd);
-
return 0;
-
}
-
/* Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
-
* PWM beeper driver
-
*
-
* This program is free software; you can redistribute it and/or modify it
-
* under the terms of the GNU General Public License as published by the
-
* Free Software Foundation; either version 2 of the License, or (at your
-
* option) any later version.
-
*
-
* You should have received a copy of the GNU General Public License along
-
* with this program; if not, write to the Free Software Foundation, Inc.,
-
* 675 Mass Ave, Cambridge, MA 02139, USA.
-
*
-
*/
-
#include <linux/input.h>
-
#include <linux/module.h>
-
#include <linux/kernel.h>
-
#include <linux/platform_device.h>
-
#include <linux/pwm.h>
-
#include <linux/slab.h>
-
struct pwm_beeper {
-
struct input_dev *input;
-
struct pwm_device *pwm;
-
unsigned long period;
-
};
-
#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
-
//事件处理函数
-
static int pwm_beeper_event(struct input_dev *input,
-
unsigned int type, unsigned int code, int value)
-
{
-
int ret = 0;
-
struct pwm_beeper *beeper = input_get_drvdata(input);
-
unsigned long period;
-
if (type != EV_SND || value < 0)
-
return -EINVAL; //若input_event.type不是EV_SND或input_event.value小于0,则退出
-
switch (code) {
-
case SND_BELL:
-
value = value ? 1000 : 0;
-
break;
-
case SND_TONE:
-
break;
-
default:
-
return -EINVAL;
-
}
-
if (value == 0) {
-
pwm_config(beeper->pwm, 0, 0); //配置PWM函数
-
pwm_disable(beeper->pwm); //关闭PWM函数
-
} else {
-
period = HZ_TO_NANOSECONDS(value);
-
ret = pwm_config(beeper->pwm, period / 2, period);
-
if (ret)
-
return ret;
-
ret = pwm_enable(beeper->pwm);
-
if (ret)
-
return ret;
-
beeper->period = period;
-
}
-
return 0;
-
}
-
static int __ devinit pwm_beeper_probe(struct platform_device *pdev)
-
{
-
unsigned long pwm_id = ( unsigned long)pdev->dev.platform_data;
-
struct pwm_beeper *beeper;
-
int error;
-
//为蜂鸣器设备开辟一段内存空间,并清零
-
beeper = kzalloc( sizeof(*beeper), GFP_KERNEL);
-
if (!beeper)
-
return -ENOMEM;
-
beeper->pwm = pwm_request(pwm_id, "pwm beeper"); //申请一个PWM设备
-
if (IS_ERR(beeper->pwm)) {
-
error = PTR_ERR(beeper->pwm);
-
dev_err(&pdev->dev, "Failed to request pwm device: %d\n", error);
-
goto err_free;
-
}
-
beeper->input = input_allocate_device(); //为输入设备结构体分配一个空间,并对其主要的成员进行了初始化.
-
if (!beeper->input) {
-
dev_err(&pdev->dev, "Failed to allocate input device\n");
-
error = -ENOMEM;
-
goto err_pwm_free;
-
}
-
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);
-
if (error)
-
{
-
dev_err(&pdev->dev, "Failed to register input device: %d\n", error);
-
goto err_input_free;
-
}
-
platform_set_drvdata(pdev, beeper);
-
return 0;
-
err_input_free: input_free_device(beeper->input);
-
err_pwm_free: pwm_free(beeper->pwm);
-
err_free: kfree(beeper); return error;
-
}
-
//移除pwm设备
-
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);
-
pwm_free(beeper->pwm);
-
kfree(beeper);
-
return 0;
-
}
-
#ifdef CONFIG_PM
-
static int pwm_beeper_suspend(struct device *dev)
-
{
-
struct pwm_beeper *beeper = dev_get_drvdata(dev);
-
if (beeper->period)
-
pwm_disable(beeper->pwm);
-
return 0;
-
}
-
static int pwm_beeper_resume(struct device *dev)
-
{
-
struct pwm_beeper *beeper = dev_get_drvdata(dev);
-
if (beeper->period)
-
{ pwm_config(beeper->pwm, beeper->period / 2, beeper->period);
-
pwm_enable(beeper->pwm);
-
}
-
return 0;
-
}
-
static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops, pwm_beeper_suspend, pwm_beeper_resume);
-
#define PWM_BEEPER_PM_OPS (&pwm_beeper_pm_ops)
-
#else
-
#define PWM_BEEPER_PM_OPS NULL
-
#endif
-
//创建平台驱动,此处name与上面我们在mach-smdk2440.c添加的名字相同,用于匹配
-
static struct platform_driver pwm_beeper_driver = {
-
.probe = pwm_beeper_probe,
-
.remove = __devexit_p(pwm_beeper_remove),
-
.driver = { .name = "pwm-beeper",
-
.owner = THIS_MODULE,
-
.pm = PWM_BEEPER_PM_OPS,
-
},
-
};
-
//驱动入口
-
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_LICENSE( "GPL");
-
MODULE_ALIAS( "platform:pwm-beeper");
-
beeper->input->sndbit[ 0] = BIT(SND_TONE) | BIT(SND_BELL);
-
PWM蜂鸣器的 事件类型 为 EV_SND , 声音的类型 为 SND_TONE或SND_BELL
-
这两项内容就是我们在应用程序中要用到的 input_event中的type和code
-
static int pwm_beeper_event(struct input_dev *input,
-
unsigned int type, unsigned int code, int value)
-
{
-
int ret = 0;
-
struct pwm_beeper *beeper = input_get_drvdata(input);
-
unsigned long period;
-
if (type != EV_SND || value < 0)
-
return -EINVAL;
-
switch (code) {
-
case SND_BELL:
-
value = value ? 1000 : 0;
-
break;
-
case SND_TONE:
-
break;
-
default:
-
return -EINVAL;
-
}
-
if (value == 0) {
-
pwm_config(beeper->pwm, 0, 0); //配置PWM函数
-
pwm_disable(beeper->pwm); //关闭PWM函数
-
} else {
-
period = HZ_TO_NANOSECONDS(value);
-
ret = pwm_config(beeper->pwm, period / 2, period);
-
if (ret)
-
return ret;
-
ret = pwm_enable(beeper->pwm);
-
if (ret)
-
return ret;
-
beeper->period = period;
-
}
-
return 0;
-
}
- 如果code为SND_BELL,不管input_event.value为多少,最终的value只能为1000或0,即不能改变蜂鸣器的频率。0表示关闭。
- 如果code为SND_TONE,则可以通过改变input_event.value的值来调整蜂鸣器的频率,从而发出各种不同的音调。
Linux输入子系统包括三个层次:事件处理层(Event Handler)、核心层(Input Core)和驱动层(Input Driver)。
1.事件处理层负责与用户程序打交道,将核心层传来的事件报告给用户程序。
2.核心层是链接其他两个层之间的纽带与桥梁,向下提供驱动层的接口,向上提供事件处理层的接口。
3.驱动层负责操作具体的硬件设备,这层的代码是针对具体的驱动程序的,键盘、鼠标、触摸屏等字符设备驱动功能的实现工作主要就在这层。
输入子系统有三个核心结构体:input_dev,input_handle和input_handler。input_dev表示一个输入设备,包含输入设备的一些相关信息;input_handler表示对输入事件的具体处理,它为输入设备的功能实现了一个接口;input_handle是用来连接输入设备和输入事件。输入子系统主要的任务就是把这三个结构体连接在一起。
总结:
通过S3C2440底板原理图可知蜂鸣器接2440的GPB0/TOUT0.即我们的蜂鸣器是通过GPB0 IO口使用PWM信号驱动工作的,而且GPB0口是一个复用的IO口,要使用它得把他设置成TOUT0 PWM输出模式。
蜂鸣器可以发声,靠的不仅仅是蜂鸣器硬件的驱动,还有Linux下的PWM(脉冲宽度调制)这种变频技术,靠改变脉冲宽度来控制输出电压,通过改变周期来控制其输出频率。通过改变频率可以使蜂鸣器发出不同的声音。
二、修改设备树
-
beeper {
-
compatible = "pwm-beeper";
-
pwms = <&pwm 0 1000000 0>;
-
pinctrl-names = "default";
-
pinctrl -0 = <&pwm0_pin>;
-
};
四、修改配置文件:
-
/*********************************************************************************
-
* Copyright: (C) 2017 qicheng
-
* All rights reserved.
-
*
-
* Filename: beer_test.c
-
* Description: This file
-
*
-
* Version: 1.0.0(04/19/2017)
-
* Author: yangni <497049229@qq.com>
-
* ChangeLog: 1, Release initial version on "04/19/2017 11:44:58 AM"
-
*
-
********************************************************************************/
-
#include<stdint.h>
-
#include<string.h>
-
#include<fcntl.h>
-
#include<unistd.h>
-
#include<stdio.h>
-
#include<linux/input.h>
-
#include<unistd.h>
-
int main(int argc, char *argv[])
-
{
-
int fd, version, ret;
-
int i;
-
struct input_event event;
-
if ((fd = open( "/dev/event1", O_RDWR)) < 0) {
-
perror( "beep test");
-
return 1;
-
}
-
event.type = EV_SND;
-
event.code = SND_TONE;
-
if(! strcmp (argv[ 1], "1"))
-
{
-
for(i= 0;i<= 7;i++)
-
{
-
event.value = 2000; //打开蜂鸣器
-
ret = write(fd, &event, sizeof(struct input_event));
-
sleep( 0.5);
-
event.value = 0; //关闭蜂鸣器
-
ret = write(fd, &event, sizeof(struct input_event));
-
}
-
}
-
else if(! strcmp (argv[ 1], "0"))
-
{
-
event.value = 0;
-
ret = write(fd, &event, sizeof(struct input_event));
-
}
-
close(fd);
-
return 0;
-
}
-
/* Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
-
* PWM beeper driver
-
*
-
* This program is free software; you can redistribute it and/or modify it
-
* under the terms of the GNU General Public License as published by the
-
* Free Software Foundation; either version 2 of the License, or (at your
-
* option) any later version.
-
*
-
* You should have received a copy of the GNU General Public License along
-
* with this program; if not, write to the Free Software Foundation, Inc.,
-
* 675 Mass Ave, Cambridge, MA 02139, USA.
-
*
-
*/
-
#include <linux/input.h>
-
#include <linux/module.h>
-
#include <linux/kernel.h>
-
#include <linux/platform_device.h>
-
#include <linux/pwm.h>
-
#include <linux/slab.h>
-
struct pwm_beeper {
-
struct input_dev *input;
-
struct pwm_device *pwm;
-
unsigned long period;
-
};
-
#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
-
//事件处理函数
-
static int pwm_beeper_event(struct input_dev *input,
-
unsigned int type, unsigned int code, int value)
-
{
-
int ret = 0;
-
struct pwm_beeper *beeper = input_get_drvdata(input);
-
unsigned long period;
-
if (type != EV_SND || value < 0)
-
return -EINVAL; //若input_event.type不是EV_SND或input_event.value小于0,则退出
-
switch (code) {
-
case SND_BELL:
-
value = value ? 1000 : 0;
-
break;
-
case SND_TONE:
-
break;
-
default:
-
return -EINVAL;
-
}
-
if (value == 0) {
-
pwm_config(beeper->pwm, 0, 0); //配置PWM函数
-
pwm_disable(beeper->pwm); //关闭PWM函数
-
} else {
-
period = HZ_TO_NANOSECONDS(value);
-
ret = pwm_config(beeper->pwm, period / 2, period);
-
if (ret)
-
return ret;
-
ret = pwm_enable(beeper->pwm);
-
if (ret)
-
return ret;
-
beeper->period = period;
-
}
-
return 0;
-
}
-
static int __ devinit pwm_beeper_probe(struct platform_device *pdev)
-
{
-
unsigned long pwm_id = ( unsigned long)pdev->dev.platform_data;
-
struct pwm_beeper *beeper;
-
int error;
-
//为蜂鸣器设备开辟一段内存空间,并清零
-
beeper = kzalloc( sizeof(*beeper), GFP_KERNEL);
-
if (!beeper)
-
return -ENOMEM;
-
beeper->pwm = pwm_request(pwm_id, "pwm beeper"); //申请一个PWM设备
-
if (IS_ERR(beeper->pwm)) {
-
error = PTR_ERR(beeper->pwm);
-
dev_err(&pdev->dev, "Failed to request pwm device: %d\n", error);
-
goto err_free;
-
}
-
beeper->input = input_allocate_device(); //为输入设备结构体分配一个空间,并对其主要的成员进行了初始化.
-
if (!beeper->input) {
-
dev_err(&pdev->dev, "Failed to allocate input device\n");
-
error = -ENOMEM;
-
goto err_pwm_free;
-
}
-
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);
-
if (error)
-
{
-
dev_err(&pdev->dev, "Failed to register input device: %d\n", error);
-
goto err_input_free;
-
}
-
platform_set_drvdata(pdev, beeper);
-
return 0;
-
err_input_free: input_free_device(beeper->input);
-
err_pwm_free: pwm_free(beeper->pwm);
-
err_free: kfree(beeper); return error;
-
}
-
//移除pwm设备
-
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);
-
pwm_free(beeper->pwm);
-
kfree(beeper);
-
return 0;
-
}
-
#ifdef CONFIG_PM
-
static int pwm_beeper_suspend(struct device *dev)
-
{
-
struct pwm_beeper *beeper = dev_get_drvdata(dev);
-
if (beeper->period)
-
pwm_disable(beeper->pwm);
-
return 0;
-
}
-
static int pwm_beeper_resume(struct device *dev)
-
{
-
struct pwm_beeper *beeper = dev_get_drvdata(dev);
-
if (beeper->period)
-
{ pwm_config(beeper->pwm, beeper->period / 2, beeper->period);
-
pwm_enable(beeper->pwm);
-
}
-
return 0;
-
}
-
static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops, pwm_beeper_suspend, pwm_beeper_resume);
-
#define PWM_BEEPER_PM_OPS (&pwm_beeper_pm_ops)
-
#else
-
#define PWM_BEEPER_PM_OPS NULL
-
#endif
-
//创建平台驱动,此处name与上面我们在mach-smdk2440.c添加的名字相同,用于匹配
-
static struct platform_driver pwm_beeper_driver = {
-
.probe = pwm_beeper_probe,
-
.remove = __devexit_p(pwm_beeper_remove),
-
.driver = { .name = "pwm-beeper",
-
.owner = THIS_MODULE,
-
.pm = PWM_BEEPER_PM_OPS,
-
},
-
};
-
//驱动入口
-
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_LICENSE( "GPL");
-
MODULE_ALIAS( "platform:pwm-beeper");
-
beeper->input->sndbit[ 0] = BIT(SND_TONE) | BIT(SND_BELL);
-
PWM蜂鸣器的 事件类型 为 EV_SND , 声音的类型 为 SND_TONE或SND_BELL
-
这两项内容就是我们在应用程序中要用到的 input_event中的type和code
-
static int pwm_beeper_event(struct input_dev *input,
-
unsigned int type, unsigned int code, int value)
-
{
-
int ret = 0;
-
struct pwm_beeper *beeper = input_get_drvdata(input);
-
unsigned long period;
-
if (type != EV_SND || value < 0)
-
return -EINVAL;
-
switch (code) {
-
case SND_BELL:
-
value = value ? 1000 : 0;
-
break;
-
case SND_TONE:
-
break;
-
default:
-
return -EINVAL;
-
}
-
if (value == 0) {
-
pwm_config(beeper->pwm, 0, 0); //配置PWM函数
-
pwm_disable(beeper->pwm); //关闭PWM函数
-
} else {
-
period = HZ_TO_NANOSECONDS(value);
-
ret = pwm_config(beeper->pwm, period / 2, period);
-
if (ret)
-
return ret;
-
ret = pwm_enable(beeper->pwm);
-
if (ret)
-
return ret;
-
beeper->period = period;
-
}
-
return 0;
-
}
- 如果code为SND_BELL,不管input_event.value为多少,最终的value只能为1000或0,即不能改变蜂鸣器的频率。0表示关闭。
- 如果code为SND_TONE,则可以通过改变input_event.value的值来调整蜂鸣器的频率,从而发出各种不同的音调。
Linux输入子系统包括三个层次:事件处理层(Event Handler)、核心层(Input Core)和驱动层(Input Driver)。
1.事件处理层负责与用户程序打交道,将核心层传来的事件报告给用户程序。
2.核心层是链接其他两个层之间的纽带与桥梁,向下提供驱动层的接口,向上提供事件处理层的接口。
3.驱动层负责操作具体的硬件设备,这层的代码是针对具体的驱动程序的,键盘、鼠标、触摸屏等字符设备驱动功能的实现工作主要就在这层。
输入子系统有三个核心结构体:input_dev,input_handle和input_handler。input_dev表示一个输入设备,包含输入设备的一些相关信息;input_handler表示对输入事件的具体处理,它为输入设备的功能实现了一个接口;input_handle是用来连接输入设备和输入事件。输入子系统主要的任务就是把这三个结构体连接在一起。
总结:
来源:oschina
链接:https://my.oschina.net/u/4326108/blog/3675315