基于S3C2440的Linux-3.6.6移植 PWM蜂鸣器驱动

转载地址: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函数这三个函数的内容,则是最底层的驱动代码,即直接与处理器打交道,这里就不再介绍了

你可能感兴趣的:(kernel-driver)