本次主要是通过讲解蜂鸣器的开发来讲述驱动开发的流程,平台是三星猎户座4412.
本次使用的是板载蜂鸣器为有源蜂鸣器,在这里我们先看电路图:
从电路图中我们可以看出我们的电平信号会控制NPN型三极管的方式驱动蜂鸣器,可以看出当PWM0为高电平时,蜂鸣器可以发出声音,低电平不发声音,接下来我们找PWM0是哪个引脚,如下图:
从图中可知,PWM0是GPD0_0,下面我们去找datasheet,以确定GPD0_0的地址,如下图:
从图中可以看出,GPD0_0的地址为0x1140000 + 0x00A0 = 0x114000A0,好的,我们现在已经掌握了我们所需要的数据:蜂鸣器高电平响,低电平不响,控制它的引脚为GPD0_0,它的地址为0x114000A0.接下来,我们写驱动程序,如下:
#include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/slab.h> #include <asm/io.h> #include <asm/uaccess.h> #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); 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 <stdio.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <sys/ioctl.h> #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/1702/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,你就可以听到蜂鸣器隔一秒响一秒了.哈哈.