linux设备驱动之mini6410按键驱动程序(普通版与输入子系统版)

mini6410硬件资源

按键驱动用到的是中断处理,中断处理的函数学习在上一篇博客中断处理中有,可以参考
中断处理函数描述

总共有 8 个用户测试用按键,它们均从 CPU 中断引脚直接引出,属于低电平触
发,这些引脚也可以复用为 GPIO 和特殊功能口。硬件显示图如下:
在这里插入图片描述
在这里插入图片描述

配置寄存器

GPNCON为GPN的配置寄存器,GPNDAT为GPN的数据寄存器
GPLCON为GPL的配置寄存器,GPLDAT为GPL的数据寄存器

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

配置GPIO口

用到的GPIO口有GPN0,GPN1,GPN2,GPN3,GPN4,GPN5,GPL11,GPL12
在这里插入图片描述
在这里插入图片描述

可以配置寄存器的和GPIO的功能进行按键的读,从而可以编写按键驱动程序。

按键驱动程序(普通版)

key.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio-bank-n.h>
#include <mach/gpio-bank-l.h>

#define DEVICE_NAME "mybuttons" //设备名

//按键中断结构体
struct button_irq_desc {
	int irq;
	int number;
	char *name; 
};

static struct button_irq_desc button_irqs [] = {
	{IRQ_EINT( 0), 0, "KEY0"},
	{IRQ_EINT( 1), 1, "KEY1"},
	{IRQ_EINT( 2), 2, "KEY2"},
	{IRQ_EINT( 3), 3, "KEY3"},
	{IRQ_EINT( 4), 4, "KEY4"},
	{IRQ_EINT( 5), 5, "KEY5"},
	{IRQ_EINT(19), 6, "KEY6"},
	{IRQ_EINT(20), 7, "KEY7"},
};

static volatile char key_values [] = {'0', '0', '0', '0', '0', '0', '0', '0'};

static DECLARE_WAIT_QUEUE_HEAD(button_waitq); //定义并初始化等待队列

static volatile int ev_press = 0;

//中断处理函数
static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
	struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
	int down;
	int number;
	unsigned tmp;
	udelay(0);
	number = button_irqs->number;
	
	switch(number) {
	case 0: case 1: case 2: case 3: case 4: case 5:
		tmp = readl(S3C64XX_GPNDAT);
		down = !(tmp & (1<<number));
	break;
	case 6: case 7:
		tmp = readl(S3C64XX_GPLDAT);
		down = !(tmp & (1 << (number + 5)));
	break;
	default:
		down = 0;
}

	if (down != (key_values[number] & 1)) {
		key_values[number] = '0' + down;
		ev_press = 1;
		wake_up_interruptible(&button_waitq);
	}
	return IRQ_RETVAL(IRQ_HANDLED);
}

static int s3c64xx_buttons_open(struct inode *inode, struct file *file)
{
	int i;
	int err = 0;
	
	for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
		if (button_irqs[i].irq < 0) {
			continue;
		}
		err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,button_irqs[i].name, (void *)&button_irqs[i]);
		
		if (err)
			break;
		}
		
		if (err) {
			i--;
			for (; i >= 0; i--) {
			if (button_irqs[i].irq < 0) {
				continue;
			}
		disable_irq(button_irqs[i].irq);
		free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
	}
		return -EBUSY;
	}
	ev_press = 1;
	return 0;
}

static int s3c64xx_buttons_close(struct inode *inode, struct file *file)
{
	int i;
	for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
		if (button_irqs[i].irq < 0) {
			continue;
		}
		free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
	}
	return 0;
}	

static int s3c64xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
	unsigned long err;
	if (!ev_press) {
		if (filp->f_flags & O_NONBLOCK)
			return -EAGAIN;
		else
			wait_event_interruptible(button_waitq, ev_press);
	}
	
	ev_press = 0;
		err = copy_to_user((void *)buff, (const void *)(&key_values), min(sizeof(key_values), count));
		return err ? -EFAULT : min(sizeof(key_values), count);
}

static unsigned int s3c64xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
{
	unsigned int mask = 0;
	poll_wait(file, &button_waitq, wait);
	if (ev_press)
		mask |= POLLIN | POLLRDNORM;
	return mask;
}

static struct file_operations dev_fops = {
	.owner = THIS_MODULE,
	.open = s3c64xx_buttons_open,
	.release = s3c64xx_buttons_close,
	.read = s3c64xx_buttons_read,
	.poll = s3c64xx_buttons_poll,
};

static struct miscdevice misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &dev_fops,
};

static int __init dev_init(void)
{
	int ret;
	ret = misc_register(&misc);
	printk (DEVICE_NAME"\tinitialized\n");
	return ret;
}

static void __exit dev_exit(void)
{
	misc_deregister(&misc);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");

应用程序

keyapp.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>

int main(void)
{
	int buttons_fd;
	char buttons[6] = {'0', '0', '0', '0', '0', '0'};
	
	buttons_fd = open("/dev/buttons", 0);
	if (buttons_fd < 0) {
		perror("open device buttons");
		exit(1);
	}
	
	for (;;) {
		char current_buttons[6];
		int count_of_changed_key;
		int i;
		
		if  (read(buttons_fd,  current_buttons,  sizeof(current_buttons)  !=  sizeof(current_buttons) {
			perror("read buttons:");
			exit(1);
		}
		
		for (i = 0, count_of_changed_key = 0; i < sizeof 			buttons / sizeof buttons[0]; i++) {
		
			if (buttons[i] != current_buttons[i]) {
				buttons[i] = current_buttons[i];
				printf("%skey %d is %s", count_of_changed_key? ", ": "", i+1, buttons[i] == '0' ? "up" : "down");
				count_of_changed_key++;
			}
		}
			if (count_of_changed_key) {
				printf("\n");
			}
		}
		
	close(buttons_fd);
	return 0;
}

按键驱动程序(linux输入子系统版)

buttons.c

扫描二维码关注公众号,回复: 6142533 查看本文章
#include <linux/module.h>
 
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
 
#include <asm/irq.h>
#include <asm/io.h>
#include <mach/gpio.h>

MODULE_AUTHOR("silence");
MODULE_LICENSE("GPL");

static struct input_dev * button_dev;
static struct pin_desc *irq_pd = NULL;
static struct timer_list buttons_timer;
 
struct pin_desc{
	int irq;
	char *name;
	unsigned int pin;
	unsigned int key_val;
};
 
struct pin_desc pins_desc[5] = {
	{IRQ_EINT(0), "s1", S3C64XX_GPN(0), KEY_L},
	{IRQ_EINT(1), "s2", S3C64XX_GPN(1), KEY_S},
	{IRQ_EINT(2), "s3", S3C64XX_GPN(2), KEY_ENTER},
	{IRQ_EINT(3), "s4", S3C64XX_GPN(3), KEY_LEFTSHIFT},
	{IRQ_EINT(4), "s5", S3C64XX_GPN(4), KEY_ESC},
};

static irqreturn_t buttons_irq(int irq, void *dev_id)
{
	irq_pd = (struct pin_desc *)dev_id;
	mod_timer(&buttons_timer, jiffies + HZ/100);
	return IRQ_RETVAL (IRQ_HANDLED);
 
} 

static void buttons_timer_function(unsigned long data)
{
	struct pin_desc * pindesc = irq_pd;
	unsigned int pinval;
	if (!pindesc)
		return;
		
	pinval = gpio_get_value(pindesc->pin);
	if (pinval)
	{
	/* 松开的 *//* 最后一个参数0表示松开1表示按下 */
		input_event(button_dev, EV_KEY, pindesc->key_val, 0);
		input_sync(button_dev);
	}
	else 
	{
	/* 按下的 */
		input_event(button_dev, EV_KEY, pindesc->key_val, 1);
		input_sync(button_dev);
	}
}
 
static int button_init(void)
{
	int i = 0;
	/*1. 分配一个input_dev结构体 */
	button_dev = input_allocate_device();
	/* 2.设置 能产生按键类事件*/
		/* 哪类事件 */
	set_bit(EV_KEY, button_dev->evbit);
	/* 重复类事件 */
	set_bit(EV_REP, button_dev->evbit);
		/* 能产生哪些事件L,S,ENTER,SHIFT */
 
	for (i = 0; i < 5; i++)
	{
		set_bit(pins_desc[i].key_val, button_dev->keybit);
	}
 
	/* 3.注册 */
	input_register_device(button_dev);
	
	/* 4.硬件相关的操作 */
	init_timer(&buttons_timer);
	buttons_timer.function = buttons_timer_function;
	buttons_timer.expires  = jiffies + HZ/100;
	add_timer(&buttons_timer);
	
	for (i = 0; i < 5; i++)
	{
		request_irq(pins_desc[i].irq, buttons_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, pins_desc[i].name, &pins_desc[i]);		
 
	}
	return 0;
 
}
 
static void button_exit(void)
{
	int i; 
	for (i = 0; i < 5; i++)
	{
		free_irq(pins_desc[i].irq, &pins_desc[i]);
	}
	del_timer(&buttons_timer);
	 
	input_unregister_device(button_dev);
	input_free_device(button_dev);
}
 
 
module_init(button_init);
module_exit(button_exit);
 

应用程序(linux输入子系统版)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <linux/input.h>

int main(void)
{
	int buttons_fd;
	int key_value, i = 0, count;
	 
	struct input_event ev_key;
	
	buttons_fd = open("/dev/input/event1",O_RDWR);
	if(buttons_fd < 0)
	{
		perror("open device buttons");
		exit(1);
	} 
	
	for(;;)
	{
		count = read(buttons_fd,&ev_key,sizeof(struct input_event));

	for(i = 0; i < (int)count/sizeof(struct input_event); i++)
	{
		if(EV_KEY == ev_key.type)
			printf("type:%d,code:%d,value:%d\n",ev_key.type,ev_key.code,ev_key.value);
	}
	
	if(EV_SYN == ev_key.type)
		printf("syn event\n\n");
	}
close(buttons_fd);
return 0;
}

效果测试

在测试之前,按键模块已经安装(安装命令 insmod )(想必都很清楚了)
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_41782149/article/details/89519442