蜂鸣器是通过I/O 口GPF15来间接控制的,为了增加驱动能力,增加了三级管驱动电路。当三极管的基极(B)为高电平即GPF15 为高电平时,蜂鸣器会鸣叫,反之则不响。通过设置两者之间的时间(即改变频率)可以使蜂鸣器发出不同的声音,甚至播放乐曲。
OK6410蜂鸣器原理图:
通过I/O 口GPF15来控制:
查看s3c6410芯片手册,端口M对应的三个寄存器地址:
实验相应寄存器
端口配置寄存器
端口数据寄存器
代码:
been_wait.c
#include <linux/module.h>//内核模块相关宏定义,必须有
#include <linux/init.h>//__init 和 __exit宏定义所在头文件
#include <linux/miscdevice.h>// 混杂设备所需的头文件
#include <linux/fs.h>// file_operations结构的定义所在的头文件
#include <linux/ioport.h>// ioremap映射所需的头文件
#include <asm/io.h>//io内存所需的头文件
#include <linux/wait.h>//阻塞型IO所需的头文件
#include <linux/sched.h>//进程调度所需的头文件
#define GPFCON 0x7F0080A0 //蜂鸣器的控制端口的物理地址
#define GPFDAT 0x7F0080A4 //蜂鸣器的数据端口的物理地址
static volatile unsigned long* gpfcon_addr;//经过ioremap映射后的虚拟地址
static volatile unsigned long* gpfdat_addr;
void been_port_init(void)
{
//设置GPF15寄存器为输出模式
*gpfcon_addr &= (~(0x1<<(31)));
*gpfcon_addr |= (0x1<<(2*15));
//设置GPF[15]为低电平,不响
*gpfdat_addr &= 0x7fff;
}
//开启蜂鸣器
void been_start(void)
{
*gpfdat_addr |= 0x8000;
}
//关闭蜂鸣器
void been_stop(void)
{
*gpfdat_addr &= 0x7fff;
}
DECLARE_WAIT_QUEUE_HEAD(wq);//初始化等待队列头
static volatile int flag=0; //设置条件标志
ssize_t my_read(struct file* filp,char __user* buf, size_t size,loff_t* ppos)
{
if(filp->f_flags & O_NONBLOCK)//非阻塞的读取
{
if(flag !=0)
been_stop();
return -EAGAIN;
}
else
{
been_start();
wait_event_interruptible(wq,flag!=0);
flag=0;
been_stop();
}
return 0;
}
ssize_t my_write(struct file* filp,const char __user* buf,size_t size,loff_t* ppos)
{
flag=1;
wake_up_interruptible(&wq);
return 0;
}
struct file_operations my_fops={
.owner = THIS_MODULE,
.read = my_read,
.write = my_write,
};
struct miscdevice mymisc={
.minor = MISC_DYNAMIC_MINOR,//MISC_DYNAMIC_MINOR=10,动态的获得次设备号
.name = "my_been",
.fops = &my_fops,
};
static int __init been_wait_init(void)
{
int ret;
//申请IO内存,不是必须的。
if(!request_mem_region(GPFCON,1,"been"))
{
ret = -EBUSY;
goto request_mem_failed;
}
gpfcon_addr = ioremap(GPFCON,1);//将物理地址映射为虚拟地址
if(NULL==gpfcon_addr)
{
ret = -EIO;
printk("gpfcon remap failed\n");
goto con_map_failed;
}
gpfdat_addr = ioremap(GPFDAT,1);
if(NULL==gpfdat_addr)
{
ret = -EIO;
printk("gpfdat remap failed\n");
goto dat_map_failed;
}
printk("gpfcon_addr remap on %p\n",gpfcon_addr);
printk("gpfdat_addr remap on %p\n",gpfdat_addr);
been_port_init();//初始化been
ret=misc_register(&mymisc);//注册一个混杂设备驱动,主设备号为10
if(ret)
{
printk("misc_register failed\n");
goto failed;
}
printk("been init\n");
return 0;
failed:
iounmap(gpfdat_addr);
dat_map_failed:
iounmap(gpfcon_addr);
con_map_failed:
release_mem_region(GPFCON,1);
request_mem_failed:
return ret;
}
static void __exit been_wait_exit(void)
{
iounmap(gpfdat_addr); //取消映射
iounmap(gpfcon_addr);
release_mem_region(GPFCON, 1); //释放I/O内存
misc_deregister(&mymisc);
printk("been exit\n");
}
module_init(been_wait_init);
module_exit(been_wait_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liye");
方案2:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <plat/gpio-core.h>
#include <plat/gpio-cfg.h>
#include <plat/gpio-cfg-helpers.h>
void been_port_init(void)
{
s3c_gpio_cfgpin(S3C64XX_GPF(15),S3C_GPIO_OUTPUT); //设置端口为输出
gpio_set_value(S3C64XX_GPF(15),0); //设置端口值为低电平
}
void been_start(void)
{
gpio_set_value(S3C64XX_GPF(15),1);
}
void been_stop(void)
{
gpio_set_value(S3C64XX_GPF(15),0);
}
DECLARE_WAIT_QUEUE_HEAD(wq);
static volatile int flag=0;
ssize_t my_read(struct file* filp,char __user* buf, size_t size,loff_t* ppos)
{
if(filp->f_flags & O_NONBLOCK)
{
if(flag !=0)
been_stop();
return -EAGAIN;
}
else
{
been_start();
wait_event_interruptible(wq,flag!=0);
flag=0;
been_stop();
}
return 0;
}
ssize_t my_write(struct file* filp,const char __user* buf,size_t size,loff_t* ppos)
{
flag=1;
wake_up_interruptible(&wq);
return 0;
}
struct file_operations my_fops={
.owner = THIS_MODULE,
.read = my_read,
.write = my_write,
};
struct miscdevice mymisc={
.minor = MISC_DYNAMIC_MINOR,//MISC_DYNAMIC_MINOR=10
.name = "my_been",
.fops = &my_fops,
};
static int __init been_wait_init(void)
{
int ret;
been_port_init();
ret=misc_register(&mymisc);
if(ret)
{
printk("misc_register failed\n");
return -1;
}
printk("been init\n");
return 0;
}
static void __exit been_wait_exit(void)
{
misc_deregister(&mymisc);
}
module_init(been_wait_init);
module_exit(been_wait_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liye");
测试代码:
read.c
#include <stdio.h>
#include <fcntl.h>
int main()
{
int fd;
char buf;
fd=open("/dev/my_been",O_RDWR);
read(fd,buf,1);
return 0;
}
write.c
#include <stdio.h>
#include <fcntl.h>
int main()
{
int fd;
char buf='a';
fd=open("/dev/my_been",O_RDWR);
write(fd,buf,1);
return 0;
}
Makefile
ifneq ($(KERNELRELEASE),)
obj-m := been_wait.o
else
KDIR := /home/liye/forlinux/linux-2.6.36
all:
make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
install:
cp been_wait.ko write read /home/liye/forlinux/rootfs/course
clean:
rm -f *.o *.ko *.mod.c *.mod.o *.order *.symvers
endif