⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 zxzkeyboard.c

📁 ARM S3C2410的4X4键盘中断驱动程序(包括驱动, Makefile, 测试源码), 在Linux 2.6.8.1环境下测试过.
💻 C
字号:
/********************************************************************'创建日期: 2007-06-21'文件名称: zxzkeyboard.c'文件作者: zhangxianzhu''文件功能: 按键驱动'文件描述: 开发板上一共有16个按键	4X4 矩阵键盘	四个输入引脚:	EINT0 -----( GPF0  )----INPUT	(键10, 11, 12, 16)			EINT2 -----( GPF2  )----INPUT			(键 7,  8,  9, 15)			EINT11-----( GPG3  )----INPUT			(键 4,  5,  6, 14)			EINT19-----( GPG11 )----INPUT			(键 1,  2,  3, 13)														|   |   |   |	四个输出引脚:	KEYSCAN0---( GPE11 )----OUTPUT-------   |   |   |			KEYSCAN1---( GPG6  )----OUTPUT-------------------   |   |			KEYSCAN2---( GPE13 )----OUTPUT-----------------------   |			KEYSCAN3---( GPG2  )----OUTPUT---------------------------		当按下按键时分别产生0、2、11、19号外部中断。*********************************************************************/#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/spinlock.h>
#include <asm/hardware.h>
#include <asm/delay.h>
#include <asm/uaccess.h>
#include <asm-arm/arch-s3c2410/regs-gpio.h>

#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm-arm/arch-s3c2410/irqs.h>
#include <asm-arm/irq.h>

#define DEVICE_NAME "button"
#define	MAX_KEY_COUNT  32									//必须是2的n次方

typedef struct
{
	unsigned long jiffy[MAX_KEY_COUNT];		//按键时间, 如果读键时, 5秒钟以前的铵键作废
	unsigned char buf[MAX_KEY_COUNT];		//按键缓冲区
	unsigned int head,tail;					//按键缓冲区头和尾
}KEY_BUFFER;

static KEY_BUFFER g_keyBuffer;		//键盘缓冲区
static spinlock_t buffer_lock;		//缓冲区锁static int major = 231;				//Define device major
static void *gpecon;static void *gpedat;static void *gpfcon;static void *gpfdat;static void *gpgcon;static void *gpgdat;///////////////////////////////////////////////////////////////
//功能: 获取当前的毫秒数(从系统启动开始, 相当于window 的API GetTickCount()
//入口:	
///////////////////////////////////////////////////////////////
static unsigned long GetTickCount(void){	struct timeval currTick;
	unsigned long ulRet;

	do_gettimeofday(&currTick);
	ulRet = currTick.tv_sec;
	ulRet *= 1000;
	ulRet += (currTick.tv_usec + 500) / 1000;
	return ulRet;
}

///////////////////////////////////////////////////////////////
//功能: 初始化键盘缓冲区
//入口:	
///////////////////////////////////////////////////////////////
static void init_keybuffer(void){	int i;	spin_lock_irq(&buffer_lock);	g_keyBuffer.head = 0;	g_keyBuffer.tail = 0;	for(i = 0; i < MAX_KEY_COUNT; i++)	{		g_keyBuffer.buf[i] = 0;		g_keyBuffer.jiffy[i] = 0;	}	spin_unlock_irq(&buffer_lock);}///////////////////////////////////////////////////////////////
//功能: 删除过时(5秒前的按键值)
//入口:	
///////////////////////////////////////////////////////////////
static void remove_timeoutkey(void){	unsigned long ulTick;	spin_lock_irq(&buffer_lock);	while(g_keyBuffer.head != g_keyBuffer.tail)	{		ulTick = GetTickCount() - g_keyBuffer.jiffy[g_keyBuffer.head];		if (ulTick  < 5000)	//5秒			break;		g_keyBuffer.buf[g_keyBuffer.head] = 0;		g_keyBuffer.jiffy[g_keyBuffer.head] = 0;		g_keyBuffer.head ++;		g_keyBuffer.head &= (MAX_KEY_COUNT -1);	}	spin_unlock_irq(&buffer_lock);}
///////////////////////////////////////////////////////////////
//功能: 初始化GPIO, 设置中断0, 2, 11, 19为下降沿中断
//入口:	
///////////////////////////////////////////////////////////////
static void init_gpio(void){	//将GPE13 11 设置低位	writel((readl(gpecon) | ((3<<26)|(3<<22))) & (~((1<<27)|(1<<23))), gpecon);	//GPE13,11 set output	writel(readl(gpedat) & 0xffffd7ff, gpedat);									//GPE13,11 output 0
	//将GPG6, 2 设置低位	writel((readl(gpgcon) | 0x3030) & 0xffffdfdf, gpgcon);	//GPG6,2 set output	writel(readl(gpgdat) & 0xffffffbb, gpgdat);				//GPG6,2 output 0
	writel((readl(gpfcon) | 0x33) & 0xffffffee, gpfcon);						//GPF2, 0 set EINT	writel((readl(gpgcon) | (3<<22) | (3<<6)) & (~((1<<22) | (1<<6))), gpgcon);	//GPG11,3 set EINT
	set_irq_type(IRQ_EINT0, IRQT_FALLING);
	set_irq_type(IRQ_EINT2, IRQT_FALLING);
	set_irq_type(IRQ_EINT11, IRQT_FALLING);
	set_irq_type(IRQ_EINT19, IRQT_FALLING);
}///////////////////////////////////////////////////////////////
//功能: 激活中断
//入口:	
///////////////////////////////////////////////////////////////
static __inline void enable_irqs(void){	enable_irq(IRQ_EINT0);	enable_irq(IRQ_EINT2);	enable_irq(IRQ_EINT11);	enable_irq(IRQ_EINT19);}///////////////////////////////////////////////////////////////
//功能: 屏蔽中断
//入口:	
///////////////////////////////////////////////////////////////
static __inline void disable_irqs(void){	disable_irq(IRQ_EINT0);	disable_irq(IRQ_EINT2);	disable_irq(IRQ_EINT11);	disable_irq(IRQ_EINT19);}
///////////////////////////////////////////////////////////////
//功能: 进入中断后, 扫描铵键码
//入口:	
//返回: 按键码(1-16), 0xff表示错误
///////////////////////////////////////////////////////////////
static __inline unsigned char button_scan(int irq)
{
	long lGPF, lGPG;
	//不利用irq号, 直接扫描键盘
	//设置G2低位, G6, E11, E13高位
	writel((readl(gpgdat) | (1<<6)) & (~(1<<2)), gpgdat);	writel(readl(gpedat) | (1<<11) | (1<<13), gpedat);	//取GPF0, GPF2, GPG3, GPG11的值	lGPF = readl(gpfdat);	lGPG = readl(gpgdat);	//判断按键	if ((lGPF & (1<<0)) == 0)		return 16;
	else if((lGPF & (1<<2)) == 0)	return 15;
	else if((lGPG & (1<<3)) == 0)	return 14;
	else if((lGPG & (1<<11)) == 0)	return 13;

	//设置G6低位, G2, E11, E13高位
	writel((readl(gpgdat) | (1<<2)) & (~(1<<6)), gpgdat);	lGPF = readl(gpfdat);	lGPG = readl(gpgdat);	if ((lGPF & (1<<0)) == 0)		return 11;
	else if((lGPF & (1<<2)) == 0)	return 8;
	else if((lGPG & (1<<3)) == 0)	return 5;
	else if((lGPG & (1<<11)) == 0)	return 2;

	//设置E11低位, G2, G6, E13高位
	writel(readl(gpgdat) | (1<<6) | (1<<2), gpgdat);	writel((readl(gpedat) | (1<<13)) & (~(1<<11)), gpedat);	lGPF = readl(gpfdat);	lGPG = readl(gpgdat);	if ((lGPF & (1<<0)) == 0)		return 10;
	else if((lGPF & (1<<2)) == 0)	return 7;
	else if((lGPG & (1<<3)) == 0)	return 4;
	else if((lGPG & (1<<11)) == 0)	return 1;

	//设置E13低位, G2, G6, E11高位
	//writel(readl(gpgdat) | (1<<6) | (1<<2), gpgdat);	writel((readl(gpedat) | (1<<11)) & (~(1<<13)), gpedat);	lGPF = readl(gpfdat);	lGPG = readl(gpgdat);	if ((lGPF & (1<<0)) == 0)		return 12;
	else if((lGPF & (1<<2)) == 0)	return 9;
	else if((lGPG & (1<<3)) == 0)	return 6;
	else if((lGPG & (1<<11)) == 0)	return 3;

	/*long lGPG, lGPE;
	//设置G2, G6, E11, E13高位
	writel(readl(gpgdat) | (1<<6) | (1<<2), gpgdat);	writel(readl(gpedat) | (1<<13) | (1<<11), gpedat);
	//读G2, G6, E11, E13的值
	lGPG = readl(gpgdat);
	lGPE = readl(gpedat);

	switch(irq)
	{
	case IRQ_EINT0:
		{
			if ((lGPE & (1 << 11)) == 0) return 10;
			if ((lGPG & (1 << 6)) == 0) return 11;
			if ((lGPE & (1 << 13)) == 0) return 12;
			if ((lGPG & (1 << 2)) == 0) return 16;
		}
		break;
	case IRQ_EINT2:
		{
			if ((lGPE & (1 << 11)) == 0) return 7;
			if ((lGPG & (1 << 6)) == 0) return 8;
			if ((lGPE & (1 << 13)) == 0) return 9;
			if ((lGPG & (1 << 2)) == 0) return 15;
		}
		break;
	case IRQ_EINT11:
		{
			if ((lGPE & (1 << 11)) == 0) return 4;
			if ((lGPG & (1 << 6)) == 0) return 5;
			if ((lGPE & (1 << 13)) == 0) return 6;
			if ((lGPG & (1 << 2)) == 0) return 14;
		}
		break;
	case IRQ_EINT19:
		{
			if ((lGPE & (1 << 11)) == 0) return 1;
			if ((lGPG & (1 << 6)) == 0) return 2;
			if ((lGPE & (1 << 13)) == 0) return 3;
			if ((lGPG & (1 << 2)) == 0) return 13;
		}
		break;
	default:
		break;
	}*/
	return 0xff ;
}
///////////////////////////////////////////////////////////////
//功能: 中断函数, 
//入口:	irq 中断号
//
///////////////////////////////////////////////////////////////
static irqreturn_t button_irq(int irq, void *dev_id, struct pt_regs *regs)
{
	unsigned char ucKey;
	disable_irqs();
	//延迟50毫秒, 屏蔽按键毛刺
	udelay(50000);
	ucKey = button_scan(irq);
	if ((ucKey >= 1) && (ucKey <= 16))
	{
		//如果缓冲区已满, 则不添加
		if (((g_keyBuffer.head + 1) & (MAX_KEY_COUNT - 1)) != g_keyBuffer.tail)
		{
			spin_lock_irq(&buffer_lock);			g_keyBuffer.buf[g_keyBuffer.tail] = ucKey;
			g_keyBuffer.jiffy[g_keyBuffer.tail] = GetTickCount();
			g_keyBuffer.tail ++;
			g_keyBuffer.tail &= (MAX_KEY_COUNT -1);
			spin_unlock_irq(&buffer_lock);		}
	}
	init_gpio();
	enable_irqs();
	return 0;
}
///////////////////////////////////////////////////////////////
//功能: 申请中断
//入口:	
//
///////////////////////////////////////////////////////////////
static __inline int request_irqs(){	int ret;	ret = request_irq(IRQ_EINT0, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
	if (ret < 0)
		return ret;
	ret = request_irq(IRQ_EINT2, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
    if (ret >= 0)
	{
	    ret = request_irq(IRQ_EINT11, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
		if (ret >= 0)
		{
		    ret = request_irq(IRQ_EINT19, button_irq, SA_INTERRUPT, DEVICE_NAME, NULL);
			if (ret >= 0)
				return ret;
			free_irq(IRQ_EINT11, button_irq);		}		free_irq(IRQ_EINT2, button_irq);	}
	free_irq(IRQ_EINT0, button_irq);	return ret;}///////////////////////////////////////////////////////////////
//功能: 释放中断
//入口:	
//
///////////////////////////////////////////////////////////////
static __inline void free_irqs(){	free_irq(IRQ_EINT0, button_irq);	free_irq(IRQ_EINT2, button_irq);	free_irq(IRQ_EINT11, button_irq);	free_irq(IRQ_EINT19, button_irq);}///////////////////////////////////////////////////////////////
//功能: 打开文件, 开始中断
//入口:	
//
///////////////////////////////////////////////////////////////
static int button_open(struct inode *inode,struct file *filp) 
{
	int ret = nonseekable_open(inode, filp);	if (ret >= 0)	{		init_keybuffer();
		enable_irqs();
	}
	return ret;
}

///////////////////////////////////////////////////////////////
//功能: 关闭文件, 屏蔽中断
//入口:	
//
///////////////////////////////////////////////////////////////
static int button_release(struct inode *inode,struct file *filp)
{
    disable_irqs();
    return 0;
}

///////////////////////////////////////////////////////////////
//功能: 读键盘
//入口:	
//
///////////////////////////////////////////////////////////////
static ssize_t button_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
	ssize_t ret = 0;

	remove_timeoutkey();
	spin_lock_irq(&buffer_lock);	while((g_keyBuffer.head != g_keyBuffer.tail) && (((size_t)ret) < count) )	{		buffer[ret] = (char)(g_keyBuffer.buf[g_keyBuffer.head]);		g_keyBuffer.buf[g_keyBuffer.head] = 0;		g_keyBuffer.jiffy[g_keyBuffer.head] = 0;		g_keyBuffer.head ++;		g_keyBuffer.head &= (MAX_KEY_COUNT -1);		ret ++;	}	spin_unlock_irq(&buffer_lock);	return ret;
}

///////////////////////////////////////////////////////////////
//功能: 清空键盘缓冲区
//入口:	
//
///////////////////////////////////////////////////////////////
static int button_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){	init_keybuffer();	return 1;}
static struct file_operations button_fops =
{
	.owner = THIS_MODULE,
	.ioctl = button_ioctl,	.open = button_open,
	.read = button_read,
	.release = button_release,
};

///////////////////////////////////////////////////////////////
//功能: 驱动初始化
//入口:	
//
///////////////////////////////////////////////////////////////
static int __init button_init(void)
{
	int ret;

	gpecon = ioremap(0x56000040, 0x04);	gpedat = ioremap(0x56000044, 0x04);	gpfcon = ioremap(0x56000050, 0x04);	gpfdat = ioremap(0x56000054, 0x04);	gpgcon = ioremap(0x56000060, 0x04);	gpgdat = ioremap(0x56000064, 0x04);	init_gpio();	ret = request_irqs();
	if (ret < 0) return ret;

	ret = register_chrdev(major, DEVICE_NAME, &button_fops);
	if (ret < 0)
	{
		free_irqs();
		return ret;
	}
	devfs_mk_cdev(MKDEV(major, 0), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH, DEVICE_NAME);

	disable_irqs();
	printk("button initialized.\n");
	return 0;
}

///////////////////////////////////////////////////////////////
//功能: 驱动释放
//入口:	
//
///////////////////////////////////////////////////////////////
static void __exit button_exit(void)
{
	disable_irqs();
	free_irqs();
	devfs_remove(DEVICE_NAME);
	unregister_chrdev(major, DEVICE_NAME);
}

module_init(button_init);
module_exit(button_exit);
MODULE_AUTHOR("ZhangXianZhu <zxzsrh@163.com>");
MODULE_DESCRIPTION("button Driver");
MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -