《OK6410-蜂鸣器驱动程序设计》之阻塞型字符设备

        蜂鸣器是通过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


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值