本文通过TQ2440开发板上可外接的GPIO口GPG14连接蜂鸣器,通过控制GPG14引脚的高低电平的输出和高低电平输出之间的时间间隔来使蜂鸣器发出不同的声音。
1.打开S3C2440的底板原理图找到GPIO,如下图所示:
使用万用表先找到VDD5V那个引脚,然后对照图找到GPG14,将蜂鸣器的正极连上GPG14,负极连上GND接地。
2.蜂鸣器驱动程序源代码My_Beep.c:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/irq.h> #include <mach/regs-gpio.h> //定义s3c2410的GPIO #include <mach/hardware.h> //定义操作s3c2410的GPIO的函数 #include <linux/device.h> //自动创建设备文件应该包含的头文件 #define DEVICE_NAME "My_Beep" //加载模块后执行cat/proc/devices中看到的设备名称 #define Beep_MAJOR 104 //主设备号 #define Beep_High 1 #define Beep_Low 0 static int My_Beep_open(struct inode *inode,struct file *file) { s3c2410_gpio_cfgpin(S3C2410_GPG14,S3C2410_GPG14_OUTP);//配置GPG14为输出功能 printk("My_Beep open\n"); return 0; } static int My_Beep_ioctl(struct inode * inode, struct file * file,unsigned int cmd) { switch(cmd) { case Beep_High: s3c2410_gpio_setpin(S3C2410_GPG14, 1);//设置GPG14引脚为输出电平为1 return 0; case Beep_Low: s3c2410_gpio_setpin(S3C2410_GPG14, 0);//设置GPG14引脚为输出电平为0 return 0; default: return -1; } } //定义文件操作 file_operations static struct file_operations My_Beep_fops = { .owner = THIS_MODULE, .open = My_Beep_open, .ioctl = My_Beep_ioctl, }; static struct class *Beep_class; static int __init My_Beep_init(void) { int ret; printk("My_Beep start\n"); //注册字符设备驱动程序 //参数为主设备号、设备名字、file_operations结构 //这样主设备号就与file_operations联系起来 ret = register_chrdev(Beep_MAJOR, DEVICE_NAME, &My_Beep_fops); if(ret < 0) { printk("can‘t register major number\n"); return ret; } //注册一个类,使mdev可以在"/dev/目录下建立设备节点" Beep_class = class_create(THIS_MODULE, DEVICE_NAME); if(IS_ERR(Beep_class)) { printk("failed in My_Beep class.\n"); return -1; } device_create(Beep_class, NULL, MKDEV(Beep_MAJOR,0), NULL, DEVICE_NAME); printk(DEVICE_NAME "initialized\n"); return 0; } static void __exit My_Beep_exit(void) { unregister_chrdev(Beep_MAJOR, DEVICE_NAME); device_destroy(Beep_class, MKDEV(Beep_MAJOR,0));//注销设备节点 class_destroy(Beep_class);//注销类 } module_init(My_Beep_init); module_exit(My_Beep_exit); MODULE_LICENSE("GPL");
源码分析:
本驱动程序通过linux系统内核中对S3C2410 GPIO操作的函数来实现对GPG14引脚输出功能和引脚高低电平输出功能的配置。
s3c2410_gpio_cfgpin(S3C2410_GPG14,S3C2410_GPG14_OUTP) 这行代码实现了配置GPG14为输出模式的功能。s3c2410_gpio_cfgpin这个函数定义在内核源码中的hardware.h头文件中,使用此函数需要包含头文件 #include<mach/hardware.h> 。
S3C2410_GPG14是GPIO的编号,S3C2410_GPG14_OUTP是端口的功能,他们都定义在内核源码的regs-gpio.h头文件中。
regs-gpio.h头文件的源码路径为 :arch/arm/mach-s3c2410/include/mach/regs-gpio.h ,打开源码来看下他们的定义:
#define S3C2410_GPG14 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 14)
#define S3C2410_GPG14_INP (0x00 << 28)
#define S3C2410_GPG14_OUTP (0x01 << 28)
在源码中我们可以看到其实S3C2410_GPG14_OUTP就是一个二进制数,再打开S3C2440的芯片手册找到端口GPG14,如下图所示:
可以知道,只要将GPG14的二进制位的29位和28位分别配置成0和1,这个端口便被设置成了输出模式,S3C2410_GPG14_OUTP就实现了二进制29位和28位的正确配置,所以他实现了对GPG14配置成输出模式的功能。
s3c2410_gpio_setpin函数同样定义在hardware.h头文件中,此函数实现了对GPG14输出高低电平的功能。说到这干脆来系统整理下常用的S3C2410 GPIO的操作函数,在hardware.h文件(源码路径:arch/arm/mach-s3c2410/include/mach/hardware.h)中 有:
void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function) //配置端口的GPIO的功能
其中pin为端口的编号,function为端口的功能。
unsigned int s3c2410_gpio_getcfg(unsigned int pin) //读取功能配置
void s3c2410_gpio_pullup(unsigned int pin, unsigned int to) //配置上拉电阻
unsigned int s3c2410_modify_misccr(unsigned int clr, unsigned int chg) //杂项配置
int s3c2410_gpio_getirq(unsigned int pin) //给定端口,转换出IRQ号
int s3c2410_gpio_irqfilter(unsigned int pin, unsigned int on, unsigned int config); //配置IRQ过滤使能与否
void s3c2410_gpio_setpin(unsigned int pin, unsigned int to) //写数据到端口
其中pin为端口的编号,to为你想要写进端口的数据。
unsigned int s3c2410_gpio_getpin(unsigned int pin) //从端口读数据
3.Makefile文件:
obj-m:=My_Beep.o CC=arm-linux-gcc KERNELDIR=/usr/local/opt/EmbedSky/linux-2.6.30.4 PWD:=$(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
4.驱动程序对应的上层应用程序源码My_Beep_Test.c
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() { int fd,i,cmd=0; long t; fd=open("/dev/My_Beep",0); if (fd<0) { printf("open Beep_driver error"); exit(1); } printf("please input t\n"); scanf("%ld",&t); while(1) { switch(cmd) { case 0: printf("Beep Low \n"); ioctl(fd,0); for(i=0;i<t;++i); case 1: printf("Beep High \n"); ioctl(fd,1); for(i=0;i<t;++i); } } return 0; }
运行此程序时可以通过scanf("%ld",&t)来设置不同的t,从而实现高低电平之间的时间间隔的变化,最终实现蜂鸣器发出不同声音的功能,本人经过尝试发现只要t的值取得恰当,你甚至可以使蜂鸣器发出美妙的音乐出来。