字符设备驱动查询方式读取按键值

上一篇博文讲到通过字符型设备文件愉快的实现了控制led的亮灭,涉及到一个知识点是用户程序(用户空间)向内核程序(内核空间)发送数据实现对led的控制。今天我们是字符设备驱动之按键,要把内核程序读到的按键值发送到用户空间实现显示。
由于原理和之前类似,这里不再累述,直接看代码即可。

按键驱动程序:

#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 <linux/module.h>  
#include <linux/device.h>     //class_create

static struct class *keys_drv_class;
static struct device *keys_drv_device;

volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;

int major;
static int keys_drv_open(struct inode * inode, struct file * filp)
{
	/*  K1 ---- EINT1,K2 ---- EINT4,K3 ---- EINT2,K4 ---- EINT0
  	 *  配置GPF1、GPF4、GPF2、GPF0为输入引脚
	 */
	 *gpfcon &= ~((0x3 << (1*2)) | (0x3 << (4*2)) | (0x3 << (2*2)) | (0x3 << (0*2)));
	return 0;
}

static ssize_t keys_drv_read(struct file *file, char __user *user, size_t size,loff_t *ppos)
{
	unsigned char key_vals[4];
	unsigned long val;  	//用于接收按键值

	if (size != sizeof(key_vals))
			return -EINVAL;

	/*  K1 ---- EINT1,K2 ---- EINT4,K3 ---- EINT2,K4 ---- EINT0
  	 *  读GPF1、GPF4、GPF2、GPF0引脚值
	 */
	val = *gpfdat; //从硬件获取按键值
	
	key_vals[0] = (val & (1<<1)) ? 1 : 0;
	key_vals[1] = (val & (1<<4)) ? 1 : 0;
	key_vals[2] = (val & (1<<2)) ? 1 : 0;
	key_vals[3] = (val & (1<<0)) ? 1 : 0;

	/* 读出值后,将数据传给应用程序 */
	copy_to_user(user, key_vals, sizeof(key_vals)); //sizeof函数里边传入数组名,计算的结果就是这整个数组的大小

	return sizeof(key_vals);
	
}
/* File operations struct for character device */
static const struct file_operations keys_drv_fops = {
	.owner		= THIS_MODULE,
	.open		= keys_drv_open,
	.read		= keys_drv_read,
};


/* 驱动入口函数 */
static int keys_drv_init(void)
{
	/* 主设备号设置为0表示由系统自动分配主设备号 */
	major = register_chrdev(0, "second_drv", &keys_drv_fops);

	/* 创建keys_drv类 */
	keys_drv_class = class_create(THIS_MODULE, "keys_drv");

	/* 在keys_drv类下创建buttons设备,供应用程序打开设备*/
	keys_drv_device = device_create(keys_drv_class, NULL, MKDEV(major, 0), NULL, "buttons");

	/* 将物理地址映射为虚拟地址 */
	gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);//这里就把从0x56000050到0x5600005C这16字节地址映射完了,其中gpfcon为映射首地址
	gpfdat = gpfcon + 1; //根据gpfcon算出gpfdat的虚拟地址,注意这里“+1”是4字节地址对齐,因为gpfcon是unsigned long型指针
	
	return 0;
}

/* 驱动出口函数 */
static void keys_drv_exit(void)
{
	unregister_chrdev(major, "keys_drv");
	device_unregister(keys_drv_device);  //卸载类下的设备
	class_destroy(keys_drv_class);		 //卸载类
	iounmap(gpfcon);					 //解除映射
}

module_init(keys_drv_init);  //用于修饰入口函数
module_exit(keys_drv_exit);  //用于修饰出口函数	

MODULE_AUTHOR("CLBIAO");
MODULE_DESCRIPTION("Just for Demon");
MODULE_LICENSE("GPL");  //遵循GPL协议

测试应用程序:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

/* second_test
 */ 
int main(int argc ,char *argv[])

{
	int fd;
	unsigned char key_vals[4];
	int cnt = 0; //用于测试按下一次按键程序能够读多少次按键值
	
	fd = open("/dev/buttons",O_RDWR);
	if (fd < 0)
	{
		printf("open error\n");
	}

	/* 查询式不停地读按键值 */
	while(1)
	{
		read(fd,key_vals,sizeof(key_vals));
		if(!key_vals[0] || !key_vals[1] || (!key_vals[2]) || (!key_vals[3]))
		{
			printf("%04d key pressed: %d %d %d %d\n",cnt++,key_vals[0],key_vals[1],key_vals[2],key_vals[3]);
		}
	}
	return 0;
}

运行结果:按下一次按键,会出现连续打印一连串按键值相同的情况,这是因为按下一次按键过程中,我们的反应相对于CPU来说实在是太慢了,按一次应用程序已经执行好多好多次while(1)里面的内容了。
如何实现使按下一次按键打印一条信息?
在驱动程序里面添加一个等待按键释放之后再赋值按键值。
具体实现:在second_drv_read()函数的“val = *gpfdat;” 这一句下面添加下面语句即可。

unsigned long val1;
val1 = val;
while( (!(val&(1<<1))) || (!(val&(1<<4))) || (!(val&(1<<2))) || (!(val&(1<<0))))//等待按键释放
{
	val1 = *gpbdat;
}


搞定微笑

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值