转载地址:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=23795897&id=4537163
Linux带有基于PWM蜂鸣器的通用驱动程序——Pwm-beeper.c(位于drivers/input/misc目录下),它是属于输入子系统的。因此要移植该驱动,只需要在自己的开发板程序文件中添加PWM蜂鸣器的平台设备及相关属性即可。
在arch/arm/mach-s3c24xx目录下的mach-zhaocj2440.c文件中的适当位置添加如下内容:
/* beeper */
static struct platform_device zhaocj2440_beeper_device= {
.name= "pwm-beeper",
.dev= {
.parent = &s3c_device_timer[0].dev,
.platform_data = 0,
},
.id= 0,
};
其中.parent =&s3c_device_timer[0].dev,表示的是该PWM蜂鸣器应用的是定时器0。定时器的平台设备是在arch/arm/plat-samsung目录下的Devs.c文件中定义的。
然后在zhaocj2440_devices[]数组的尾部添加定时器设备和刚刚定义的PWM蜂鸣器设备:
staticstruct platform_device *zhaocj2440_devices[] __initdata = {
……
&s3c_device_timer,
&zhaocj2440_beeper_device,
};
最后在zhaocj2440_init(void)函数中的适当位置添加对定时器0引脚的配置:
staticvoid __init zhaocj2440_init(void)
{
……
/*PWM beeper */
gpio_request(S3C2410_GPB(0),"beeper");
s3c_gpio_setpull(S3C2410_GPB(0), S3C_GPIO_PULL_NONE);
s3c_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPB0_TOUT0);
……
}
至此,在mach-zhaocj2440.c文件中的修改就完成了。但为了把PWM蜂鸣器的通用驱动程序——Pwm-beeper.c添加到内核中,还需要修改/drivers/input/misc目录下的Kconfig文件,把该文件中的第447行注释掉,即:
configINPUT_PWM_BEEPER
tristate "PWM beeper support"
# depends on HAVE_PWM
help
Say Y here to get support for PWM based beeper devices.
If unsure, say N.
To compile this driver as a module, choose M here: the module willbe
called pwm-beeper.
另外在配置内核的时候,不要忘了把关于PWM驱动的部分都要选上:
SystemType --->
[ * ] PWM device support
DeviceDrivers --->
Input device support --->
[* ] Miscellaneous devices --->
< * > PWM beeper support
- * - Pules-Width Modulation (PWM) Supprot --->
--- Pulse-Width Modulation (PWM) Support
- *- Samsung pwmsupport
这样就完成了PWM蜂鸣器的移植。编译后下载到开发板上,在启动的时候会听到“嘀”一声,这说明蜂鸣器已经配置正确可以使用了。另外在启动的过程中,系统会输出下列语句:
……
input:gpio-keys as /devices/platform/gpio-keys/input/input1
……
input: pwm-beeper as /devices/platform/s3c24xx-pwm.0/pwm-beeper.0/input/input0
这说明蜂鸣器已做为输入子系统的事件0,而在上一篇文章中移植的按键改为事件1了。我们也可以通过查看输入子系统的设备来进一步确认:
[root@zhaocj/]#cat proc/bus/input/devices
I:Bus=0019 Vendor=001fProduct=0001 Version=0100
N:Name="pwm-beeper"
P:Phys=pwm/input0
S:Sysfs=/devices/platform/s3c24xx-pwm.0/pwm-beeper.0/input/input0
U:Uniq=
H:Handlers=kbd event0
B:PROP=0
B:EV=40001
B:SND=6
I:Bus=0019 Vendor=0001 Product=0001 Version=0100
N:Name="gpio-keys"
P:Phys=gpio-keys/input0
S:Sysfs=/devices/platform/gpio-keys/input/input1
U:Uniq=
H:Handlers=kbd event1
B:PROP=0
B:EV=3
B:KEY=100000 0 38000000 0
下面我们编写应用程序来对蜂鸣器进行操作,程序内容如下:
#include
#include
#include
#include
#include
#include
#include
intmain(int argc, char *argv[])
{
int fd, version, ret;
struct input_event event;
if ((fd = open("/dev/event0", O_RDWR)) < 0) {
perror("beep test");
return 1;
}
event.type = EV_SND;
event.code = SND_ TONE;
event.value = 2000;
ret = write(fd, &event, sizeof(struct input_event));
close(fd);
return 0;
}
通过改变event.value的值,来使蜂鸣器发出各种不同的声音,如果event.value的值为0,则关闭蜂鸣器。
下面就具体介绍一下Linux下的PWM蜂鸣器的工作原理。
在通用PWM蜂鸣器驱动程序Pwm-beeper.c中,有一个平台驱动结构:
static struct platform_driverpwm_beeper_driver = {
.probe = pwm_beeper_probe,
.remove= __devexit_p(pwm_beeper_remove),
.driver= {
.name = "pwm-beeper",
.owner = THIS_MODULE,
.pm = PWM_BEEPER_PM_OPS,
},
};
其中的name与我们在Mach-zhaocj2440.c中定义的zhaocj2440_beeper_device平台设备中的name一致,这样平台设备与平台驱动就匹配上了。再来看pwm_beeper_probe函数:
static int __devinit pwm_beeper_probe(structplatform_device *pdev)
{
unsignedlong pwm_id = (unsigned long)pdev->dev.platform_data;
structpwm_beeper *beeper;
interror;
//为蜂鸣器设备开辟一段内存空间,并清零
beeper= kzalloc(sizeof(*beeper), GFP_KERNEL);
if(!beeper)
return-ENOMEM;
//申请一个PWM设备,该函数在drivers/pwm目录下的Core.c文件中
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);
gotoerr_free;
}
//为输入设备开辟一段内存空间,因为linux把PWM蜂鸣器也看成是一个输入设备
beeper->input= input_allocate_device();
if(!beeper->input) {
dev_err(&pdev->dev,"Failed to allocate input device\n");
error= -ENOMEM;
gotoerr_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;
//PWM蜂鸣器的事件类型为EV_SND,声音的类型为SND_TONE或SND_BELL
//这两项内容就是我们在应用程序中要用到的input_event中的type和code
beeper->input->evbit[0]= BIT(EV_SND);
beeper->input->sndbit[0]= BIT(SND_TONE) | BIT(SND_BELL);
//指定PWM蜂鸣器的事件处理函数
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);
gotoerr_input_free;
}
platform_set_drvdata(pdev,beeper);
return0;
err_input_free:
input_free_device(beeper->input);
err_pwm_free:
pwm_free(beeper->pwm);
err_free:
kfree(beeper);
returnerror;
}
在该函数内,最重要的是指定事件处理函数pwm_beeper_event,下面就来介绍该函数:
static int pwm_beeper_event(struct input_dev*input,
unsigned int type, unsigned int code, intvalue)
{
intret = 0;
structpwm_beeper *beeper = input_get_drvdata(input);
unsignedlong period;
//如果从应用程序中得到的input_event.type不是EV_SND或input_event.value小于0,则退出
if(type != EV_SND || value < 0)
return-EINVAL;
//从应用程序中得到的input_event.code只能为SND_BELL或SND_TONE,否则退出
//如果为SND_BELL,不管input_event.value为多少,最终的value只能为1000或0,即不能改变蜂鸣器的频率
//如果为SND_TONE,则可以通过改变input_event.value的值来调整蜂鸣器的频率,从而发出各种不同的音调
switch(code) {
caseSND_BELL:
value= value ? 1000 : 0;
break;
caseSND_TONE:
break;
default:
return-EINVAL;
}
if(value == 0) {
//input_event.value为0,关闭蜂鸣器
pwm_config(beeper->pwm,0, 0);
pwm_disable(beeper->pwm);
}else {
//配置蜂鸣器,使蜂鸣器发声
period= HZ_TO_NANOSECONDS(value);
ret= pwm_config(beeper->pwm, period / 2, period);
if(ret)
returnret;
ret= pwm_enable(beeper->pwm);
if(ret)
returnret;
beeper->period= period;
}
return0;
}
在上述函数中,配置PWM函数(pwm_config),无效PWM函数(pwm_disable)和使能PWM函数(pwm_enable)是核心所在,基于不同的处理器,系统会调用不同的函数。对于s3c2440来说,系统分别调用的是drivers/pwm目录下Pwm-samsung.c文件中的s3c_pwm_config函数,s3c_pwm_disable函数和s3c_pwm_enable函数。因为在Pwm-samsung.c文件中,我们会看到s3c_pwm_probe函数内有下面一��代码:
s3c->chip.ops = &s3c_pwm_ops;
而s3c_pwm_ops的内容为:
static struct pwm_ops s3c_pwm_ops = {
.enable= s3c_pwm_enable,
.disable= s3c_pwm_disable,
.config= s3c_pwm_config,
.owner= THIS_MODULE,
};
具体s3c_pwm_config函数,s3c_pwm_disable函数和s3c_pwm_enable函数这三个函数的内容,则是最底层的驱动代码,即直接与处理器打交道,这里就不再介绍了