本次主要是通过讲解蜂鸣器的开发来讲述驱动开发的流程,平台是三星猎户座4412.
本次使用的是板载蜂鸣器为有源蜂鸣器,在这里我们先看电路图:
从电路图中我们可以看出我们的电平信号会控制NPN型三极管的方式驱动蜂鸣器,可以看出当PWM0为高电平时,蜂鸣器可以发出声音,低电平不发声音,接下来我们找PWM0是哪个引脚,如下图:
从图中可知,PWM0是GPD0_0,下面我们去找datasheet,以确定GPD0_0的地址,如下图:
从图中可以看出,GPD0_0的地址为0x1140000 + 0x00A0 = 0x114000A0,好的,我们现在已经掌握了我们所需要的数据:蜂鸣器高电平响,低电平不响,控制它的引脚为GPD0_0,它的地址为0x114000A0.接下来,我们写驱动程序,如下:
#include
#include
#include
#include
#include
#include
#include
#define BEEP_Switch _IOW('L', 0x1234, int)
struct exynos4412_beep{
unsigned int dev_major;
struct class *cls;
struct device *dev;
int value;
};
struct exynos4412_beep *beep_dev;
volatile unsigned long *gpd0_conf;
volatile unsigned long *gpd0_data;
int beep_open(struct inode *inode, struct file *filp){
printk("-------%s--------\n", __func__);
// 硬件初始化,设置成输出模式
*gpd0_conf &= ~0xf;
*gpd0_conf |= 0x01;
return 0;
}
ssize_t beep_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos){
printk("-------%s--------\n", __func__);
return count;
}
ssize_t beep_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos){
int ret = -1;
printk("-------%s--------\n", __func__);
ret = copy_from_user(&beep_dev->value, buf, count);
if(ret > 0){
printk("copy from user failed!\n");
return -EFAULT;
}
if(beep_dev->value){
*gpd0_data |= 0x01;
}
else{
*gpd0_data &= ~0x01;
}
return count;
}
long beep_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){
printk("-------%s--------\n", __func__);
switch(cmd)
{
case BEEP_Switch:
if(arg){
*gpd0_data |= 0x01;
}
else{
*gpd0_data &= ~0x01;
}
break;
default:
break;
}
return 0;
}
int beep_close(struct inode *inode, struct file *filp){
printk("-------%s--------\n", __func__);
// 设置成关闭蜂鸣器
*gpd0_data &= ~0x01;
return 0;
}
struct file_operations beep_fops = {
//.owner = THIS_MODULE,
.open = beep_open,
.read = beep_read,
.write = beep_write,
.unlocked_ioctl = beep_ioctl,
.release = beep_close,
};
static __exit void beep_exit(void)
{
printk("--------%s---------\n", __func__);
device_destroy(beep_dev->cls, MKDEV(beep_dev->dev_major, 0));
class_destroy(beep_dev->cls);
unregister_chrdev(beep_dev->dev_major, "beep_drv");
kfree(beep_dev);
}
static __init int beep_init(void)
{
int ret = -1;
printk("--------%s---------\n", __func__);
// 1,实例化一个设备对象
beep_dev = kzalloc(sizeof(struct exynos4412_beep), GFP_KERNEL);
if(NULL == beep_dev){
printk("kzalloc failed!\n");
return -ENOMEM;
}
// 2,申请设备号
beep_dev->dev_major = register_chrdev(0, "beep_drv", &beep_fops);
if(beep_dev->dev_major < 0){
printk("register failed\n");
return -EINVAL;
}
// 3,创建设备类
beep_dev->cls = class_create(THIS_MODULE, "led_cls");
if(IS_ERR(beep_dev->cls)){
printk("class_register failed!\n");
ret = PTR_ERR(beep_dev->cls);
goto err1;
}
// 4,创建设备节点
beep_dev->dev = device_create(beep_dev->cls, NULL, MKDEV(beep_dev->dev_major, 0), NULL, "beep1");
if(IS_ERR(beep_dev->dev)){
printk("device create failed!\n");
ret = PTR_ERR(beep_dev->dev);
goto err2;
}
// 5,硬件映射
gpd0_conf = ioremap(0x114000a0, 8);
gpd0_data = gpd0_conf + 1;
return 0;
err2:
class_destroy(beep_dev->cls);
err1:
unregister_chrdev(beep_dev->dev_major, "beep_drv");
return ret;
}
module_init(beep_init);
module_exit(beep_exit);
MODULE_LICENSE("GPL");
写好了驱动,接下来我们来写上层测试程序,如下:
#include
#include
#include
#include
#include
#include
#include
#define BEEP_Switch _IOW('L', 0x1234, int)
int main(void)
{
int fd = -1,
val = -1;
fd = open("/dev/beep1", O_RDWR);
if(fd < 0){
perror("open");
exit(1);
}
while(1)
{
ioctl(fd, BEEP_Switch, 1);
sleep(1);
ioctl(fd, BEEP_Switch, 0);
sleep(1);
val = 1;
write(fd, &val, 4);
sleep(1);
val = 0;
write(fd, &val, sizeof(int));
sleep(1);
}
return 0;
}
好的,以上是测试程序,接下来还有makefile,如下:
ifeq ($(KERNELRELEASE),)
KERNELDIR = /home/george/xshark/exynos/linux-3.5
PWD =$(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
arm-none-linux-gnueabi-gcc beep_test.c -o beep_test
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *_test *.so *.o *.ko .tmp_versions *.mod.c *.order *.symvers
else
obj-m :=beep.o
endif
其中驱动的文件名为beep.c,测试程序的文件名为beep_test.c.
编译之后,运行tiny4412之后,insmod加载beep.ko,然后运行./beep_test,你就可以听到蜂鸣器隔一秒响一秒了.哈哈.