JZ2440按键驱动-中断方式实现

模块名称:buttons_drv.c
开发环境:Ubuntu 16.04
编译环境:Linux 2.6.22.6
最后修改:2019.03.07

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

static unsigned char key_val ;
static struct class *buttons_drv_class ; 
static struct class_device *buttons_drv_dev ;		

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

/* 等待队列:
 * 当没有按键按下时,如果有应用程序调用buttons_drv_read()
 * 它将休眠
 */
static DECLARE_WAIT_QUEUE_HEAD(button_wait); 

/* 中断标记事件
 * 中断服务程序将它置1
 * buttons_drv_read()将它清除
 */
static volatile int ev_press = 0 ;

struct pin_desc {
	unsigned char pin ;
	unsigned char key_val ;
} ;

/* 按键描述结构体 引脚,按键值 */
static struct pin_desc pins_desc[4] = {
	{S3C2410_GPF0,  0x01},
	{S3C2410_GPF2,  0x02}, 
	{S3C2410_GPG3,  0x03}, 
	{S3C2410_GPG11, 0x04}, 
} ;


/* 按键中断处理函数 */
static irqreturn_t buttons_irq_handle(int irq, void *dev_id)
{
	struct pin_desc *my_pin_desc = (struct pin_desc *)dev_id ;
	unsigned int pin_val = 0 ;

	/* 获取引脚电平值 */
	pin_val = s3c2410_gpio_getpin(my_pin_desc->pin) ;

	if(!pin_val)
	{
		key_val = my_pin_desc->key_val ;
	}
	else 
	{
		key_val = my_pin_desc->key_val | 0x80 ;
	}

	ev_press = 1 ;
	wake_up_interruptible(&button_wait) ;
	
	return IRQ_HANDLED ;
}

/* 当应用程序open时,函数将向内核申请中断 */
static int buttons_drv_open(struct inode * inode, struct file * file)
{
	/* 向内核请求外部中断 */
	request_irq(IRQ_EINT0,  buttons_irq_handle, IRQ_TYPE_EDGE_BOTH, "S2", &pins_desc[0]);
	request_irq(IRQ_EINT2,  buttons_irq_handle, IRQ_TYPE_EDGE_BOTH, "S3", &pins_desc[1]);
	request_irq(IRQ_EINT11, buttons_irq_handle, IRQ_TYPE_EDGE_BOTH, "S4", &pins_desc[2]);	
	request_irq(IRQ_EINT19, buttons_irq_handle, IRQ_TYPE_EDGE_BOTH, "S5", &pins_desc[3]);

	return 0 ;	
}

/* 当应用程序read时,函数将按键值传到用户空间 */
static ssize_t buttons_drv_read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off)
{
	if(bytes != 1)
		return -EINVAL ;
	
	wait_event_interruptible(button_wait, ev_press) ;
	copy_to_user(userbuf, &key_val, 1) ;
	ev_press  = 0 ;
	return 1 ;
}

static int buttons_drv_close(struct inode * inode, struct file * file)
{
	/* 释放中断 */
	free_irq(IRQ_EINT0,  &pins_desc[0]) ;
	free_irq(IRQ_EINT2,  &pins_desc[1]) ;
	free_irq(IRQ_EINT11, &pins_desc[2]) ;
	free_irq(IRQ_EINT19, &pins_desc[3]) ;
	
	return 0 ;
}

/* 驱动架构核心结构体 - file_operations */
static struct file_operations buttons_fops = {
	.owner   = THIS_MODULE, 
	.open    = buttons_drv_open, 
	.read    = buttons_drv_read, 
	.release = buttons_drv_close, 
} ;

static int major = 0 ;		//主设备号

/* 驱动入口函数 */
static int buttons_drv_init(void)
{
	/* 注册字符设备 */
	major = register_chrdev(0, "buttons_drv", &buttons_fops) ;

	/* 自动创建设备节点 udev机制 */
	buttons_drv_class = class_create(THIS_MODULE, "buttons_drv") ;

	/* 创建设备节点 */
	buttons_drv_dev = class_device_create(buttons_drv_class, NULL, MKDEV(major, 0), NULL, "buttons") ;

	
#if 0	//使用中断机制不需要映射虚拟地址
	/* 虚拟地址映射 */
	gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16) ;
	gpfdat = gpfcon + 1 ;
	gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16) ;
	gpgdat = gpgcon + 1 ;
#endif
	
	return 0 ;
}

/* 驱动出口函数 */
static void buttons_drv_exit(void)
{
	/* 卸载字符设备 */
	unregister_chrdev(major, "buttons_drv") ;

	/* 删除设备节点 */
	class_device_unregister(buttons_drv_dev) ;

	/* 销毁节点类 */
	class_destroy(buttons_drv_class) ;

#if 0
	/* 解除虚拟地址映射 */
	iounmap(gpfcon) ;
	iounmap(gpgcon) ;
#endif

	return ;
}

/* 驱动入口:加载 */
module_init(buttons_drv_init) ;

/* 驱动出口:卸载 */
module_exit(buttons_drv_exit) ;

/* 许可 */
MODULE_LICENSE("GPL") ;
模块名称:buttons_test.c
开发环境:Ubuntu 16.04
编译环境:Linux 2.6.22.6
最后修改:2019.03.07

#include 
#include 
#include 
#include 
#include 

int main(int argv, char **argc)
{
	int fd ;
	unsigned char key_val ;

	/* 以读写方式打开设备 */
	fd = open("/dev/buttons", O_RDWR) ;
	if(fd < 0)
	{
		printf("can't open!\n") ;
		//return -1 ; 
	}

	while(1)
	{
		/* 从驱动读取按键值到key_val */
		read(fd, &key_val, 1) ;

		/* 串口打印键值 */
		printf("key_val = 0x%x\n", key_val) ;
	}
}

你可能感兴趣的:(嵌入式驱动开发,S3C2440)