第一、二期衔接——4.1 字符驱动设备之按键驱动—查询方式

字符驱动设备之按键驱动


前言

  在本片博文中,编写按键驱动,通过查询的方式得到按键信息。

一、框架搭建

1、编写驱动程序

static int button_drv_open(struct inode *inode, struct file *file)
{
	return 0;
}

static ssize_t button_drv_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
	return 0;
}

2、告诉内核有这个驱动程序

2.1 构建file_operations()结构体
static struct file_operations button_drv_fops = {
	.owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   button_drv_open,     
	.read   =	button_drv_read,	
};
2.2 构建入口函数button_drv_init()

由内核自动分配主设备节点、自动创建设备节点

static struct class *buttondrv_class;
static struct class_device	*buttondrv_class_dev;
int major;

static int button_drv_init(void)
{
	major = register_chrdev(0, "button_drv", &button_drv_fops); // 注册, 告诉内核

	buttondrv_class = class_create(THIS_MODULE, "buttondrv");
	buttondrv_class_dev = class_device_create(buttondrv_class, NULL, MKDEV(major, 0), NULL, "button"); /* /dev/button */

	return 0;
}
2.3 构建出口函数button_drv_exit()
static void button_drv_exit(void)
{
	unregister_chrdev(major, "button_drv"); // 卸载

	class_device_unregister(buttondrv_class_dev);
	class_destroy(buttondrv_class);
}

3、修饰入口、出口函数并添加模块的许可证声明

module_init(button_drv_init);
module_exit(button_drv_exit);

MODULE_LICENSE("GPL");

4、完整的框架代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>

static struct class *buttondrv_class;
static struct class_device	*buttondrv_class_dev;

static int button_drv_open(struct inode *inode, struct file *file)
{
	return 0;
}

static ssize_t button_drv_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
	return 0;
}

static struct file_operations button_drv_fops = {
	.owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   button_drv_open,     
	.read   =	button_drv_read,	
};

int major;
static int button_drv_init(void)
{
	major = register_chrdev(0, "button_drv", &button_drv_fops); // 注册, 告诉内核

	buttondrv_class = class_create(THIS_MODULE, "buttondrv");
	buttondrv_class_dev = class_device_create(buttondrv_class, NULL, MKDEV(major, 0), NULL, "button"); /* /dev/button */

	return 0;
}

static void button_drv_exit(void)
{
	unregister_chrdev(major, "button_drv"); // 卸载

	class_device_unregister(buttondrv_class_dev);
	class_destroy(buttondrv_class);
}

module_init(button_drv_init);
module_exit(button_drv_exit);

MODULE_LICENSE("GPL");

5、修改Makefile文件

KERN_DIR = /work/system/linux-2.6.22.6

all:
	make -C $(KERN_DIR) M=`pwd` modules 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order

obj-m	+= button_drv.o

5、编译验证

  1. 通过服务器上传到虚拟机,执行make
  2. 通过NFS网络系统,把button_drv.ko文件上传到开发版的根文件系统
  3. 加载驱动,观察功能是否可以正常实现。

在这里插入图片描述

二、硬件操作

1、查看原理图

在这里插入图片描述

由原理图可知
按键S2-EINT0-GPF0                  按键S3-EINT2-GPF2
按键S4-EINT11-GPG3                 按键S5-EINT19-GPG11

2、查看s3c2440芯片手册

在这里插入图片描述
在这里插入图片描述

3、代码编写

3.1 代码分布
  1. button_drv_init()中完成地址映射
  2. button_drv_open()中完成IO口设置为输入
  3. button_drv_read()中完成按键信息的读取
3.2 驱动代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>

int major;
static struct class *buttondrv_class;
static struct class_device	*buttondrv_class_dev;

volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;
volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;

static int button_drv_open(struct inode *inode, struct file *file)
{
	/* 配置GPF0,2为输入 */
	*gpfcon &= ~((0x3<<(0*2)) | (0x3<<(2*2)));

	/* 配置GPF3、11为输出 */
	*gpfcon &= ~((0x3<<(3*2)) | (0x3<<(11*2)));
	
	return 0;
}

static ssize_t button_drv_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
	int key_values[4];	//0-gdf0, 1-gdf2, 2-gdg3, 3-gdg11
	int value;
	unsigned long ret = 0;

	/* 如果字节数不对,则返回 */
	if(nbytes != sizeof(key_values))
		return -;EINVAL
	
	/* 读取GPF0,2 */
	value = *gpfdat;;
	key_values[0] = (value & (1<<0));
	key_values[1] = (value & (1<<2));

	/* 读取GPG3,11 */
	value = *gpgdat;
	key_values[2] = (value & (1<<3));
	key_values[3] = (value & (1<<11));

	/* 数据给到用户?*/
	ret = copy_to_user(buf, key_values, sizeof(key_values));
	if(ret < 0){
		printk("func button_drv_read() err: copy_to_user");
		return -EFAULT;
	}
	
 	return sizeof(key_values);
}

static struct file_operations button_drv_fops = {
	.owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   button_drv_open,     
	.read   =	button_drv_read,	
};

static int button_drv_init(void)
{
	major = register_chrdev(0, "button_drv", &button_drv_fops);	//注册一个字符设备,名字为button_drv

	buttondrv_class = class_create(THIS_MODULE, "button_drv");	//创建一个类,名字为buttond_rv(/class/button_drv)
	buttondrv_class_dev = class_device_create(buttondrv_class, NULL, MKDEV(major, 0), NULL, "button"); //创建一个设备节点,名为button(/dev/button)

	/* 映射物理地址 */
	gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
	gpfdat = gpfcon + 1;
	gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
	gpgdat = gpfcon + 1;
	
	return 0;
}

static void button_drv_exit(void)
{
	unregister_chrdev(major, "button_drv"); // 卸载字符设备

	class_device_unregister(buttondrv_class_dev);	//删除设备节点
	class_destroy(buttondrv_class);	//销毁类
	
	/* 取消映射 */
	iounmap(gpfcon);
	iounmap(gpgcon);
}

module_init(button_drv_init);
module_exit(button_drv_exit);

MODULE_LICENSE("GPL");
3.3 测试代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

/* buttondrvtest on
 * buttondrvtest off
 */
int main(int argc, char **argv)
{
	int fd;
	int ret = 0;
	int key_value[4] = {0};
	int count = 0;
	
	fd = open("/dev/button", O_RDWR);
	if (fd < 0){
		printf("can't open!\n");
		return -1;
	}

	while(1){
		ret = read(fd, key_value, sizeof(key_value));
		if(ret < 0){
			printf(" func read() err\n");
		}else{
			if(!key_value[0] || !key_value[1] || !key_value[2] || !key_value[3]){
				printf("%04d key pressed: %d %d %d %d\n",
					count++, key_value[0], key_value[1], key_value[2]
					       , key_value[3]);
			}
		}
	}
	
	return ret;
}

三、烧写验证

  1. 通过服务器上传到虚拟机,执行make
  2. 通过NFS网络系统,把button_drv.ko文件上传到开发版的根文件系统。
  3. 加载驱动,观察功能是否可以正常实现。
  4. 交叉编译buttondrvtest.c文件,得到buttondrvtest.o文件上传到开发版的根文件系统。
  5. 执行./buttondrvtest

可以看到正常运行。 在这里插入图片描述

四、查询方式缺点

1、不确定性高

2、CPU占用率大

在这里插入图片描述
可以看到,运行测试程序的时候CPU的占用率到达了恐怖的99%,这是因为测试程序一直在读取键值。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值