1、项目描述
使用杂项设备来完成一个蜂鸣器的应用,上层应用发送1来打开蜂鸣器,发送0来关闭蜂鸣器。开发板使用imx6ull。要控制蜂鸣器的逻辑操作,就涉及到了对寄存器的操作,Linux开发不同于单片机或者裸机开发不能直接对硬件寄存器进行操作,需要将物理地址映射为虚拟地址进行操作。
2、涉及到的知识点
(1)杂项设备的概念
(2)杂项设备驱动的写法
(3)对imx6ull开发板蜂鸣器所在物理寄存器的了解,查看开发板手册得到蜂鸣器的数据寄存器地址是GPIO5_DR=0x020AC000
(4)使用MMU,将物理地址映射为虚拟地址
3、驱动代码
(1)蜂鸣器的驱动代码beep.c
#include<linux/init.h>
#include<linux/module.h>
/*注册杂项设备头文件*/
#include <linux/miscdevice.h>
/*注册设备节点的文件结构体*/
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#define GPIO5_DR 0x020AC000
unsigned int * vir_gpio5_dr;
int beep_open(struct inode* inode,struct file* file){
printk("beep_open\n");
return 0;
}
int beep_release(struct inode* inode,struct file* file){
printk("beep_release\n");
return 0;
}
ssize_t beep_read(struct file *file, char __user * ubuf, size_t size, loff_t * loff_t){
printk("beep_read\n ");
return 0;
}
ssize_t beep_write(struct file *file, const char __user * ubuf, size_t size, loff_t * loff_t){
//上层应用传入参数1为打开蜂鸣器,传入参数0为关闭蜂鸣器
char kbuf[64]={0};
if(copy_from_user(kbuf,ubuf,size)!=0)
{
printk("copy_from_user error\n ");
return -1;
}
if(kbuf[0]==1){
//打开蜂鸣器,第二位数据置为1(1左移1位为10,与虚拟地址或第二位置为1)
*vir_gpio5_dr|=(1<<1);
}else if(kbuf[0]==0){
//关闭蜂鸣器,第二位数据置为0
*vir_gpio5_dr&=~(1<<1);
}
printk("beep_write\n ");
return 0;
}
struct file_operations misc_fops={
.owner = THIS_MODULE, //宏,指向当前模块
.open = beep_open,
.release = beep_release,
.read = beep_read,
.write = beep_write,
};
struct miscdevice beep_dev = {
.minor = MISC_DYNAMIC_MINOR, //宏,自动分配空闲的次设备号
.name = "beep",
.fops = &misc_fops,
};
static int beep_init(void){
int ret;
ret = misc_register(&beep_dev);
//注册杂项设备
if(ret<0)
{
printk("misc registe is error\n");
}
printk("misc registe is succeed\n");
vir_gpio5_dr = ioremap(GPIO5_DR,4);
if(vir_gpio5_dr == NULL){
printk("GPIO5_DR Ioremap error\n");
return -EBUSY;
}
printk("GPIO5_DR Ioremap succeed\n");
return 0;
}
static void beep_exit(void){
misc_deregister(&beep_dev);
iounmap(vir_gpio5_dr);
printk("misc gooodbye! \n");
}
module_init(beep_init);
module_exit(beep_exit);
MODULE_LICENSE("GPL");
(2)上层应用测试代码app.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char const* argv[]){
int fd;//定义一个句柄
char buf[64] = {0};
fd = open("/dev/beep",O_RDWR);//打开设备节点
if(fd < 0)
{
perror("open error \n");
return fd;
}
buf[0]=atoi(argv[1]);
write(fd,buf,sizeof(buf));
close(fd);
return 0;
}
(3)Makefile文件
obj-m += beep.o # 编译驱动模块
KDIR := /home/zyd/topeet/driver/imx6ull/linux-imx-rel_imx_4.1.15_2.1.0_ga
PWD ?= $(shell pwd) # 获取当前路径
ARCH = arm
CROSS_COMPILE = /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
# 默认目标,编译内核模块
all: beep.ko app
# 编译内核模块
beep.ko:
make -C $(KDIR) M=$(PWD) modules ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE)
# 编译应用程序
app: app.c
$(CROSS_COMPILE)gcc -o app app.c
# 清理目标
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.symvers *.order app