矩阵键盘驱动开发实验报告
实验报告书
专业班级:
学号:
姓名:
联系电话:
指导老师:
实验时间:
计算机学院计算机科学与技术
一、实验目的
1、掌握4×4 键盘驱动的写法;
2、深入了解linux 驱动架构。
二、实验设备
1、装有Linux 系统或装有Linux 虚拟机的PC 机一台;
2、凌阳ARM9 嵌入式实验箱SP-32AM11A 一台;
3、S3C2410 CPU 核心板一块;
4、本实验用到实验箱的模块有:S3C2410 CPU 板模块、4×4 键盘模块。
三、实验要求
1、实现功能:编写4×4 键盘驱动,并将键值通过控制台打印出来;
2、实验现象:每个键值通过控制台打印出来。
四、实验原理
1、硬件原理
本实验箱采用GPF0~7 连接4×4 键盘,其中GPF0~3 与K1~K4 连接,GPF4~7 与KA~KD连接,分别用于控制4×4 键盘的纵列和横列。硬件连接如图6.2所示。
4×4 键盘一般采用行列扫描方法获取键值,为了进一步提高驱动程序的效率,这里结合外部中断获取键值。GPF 组IO 端口都有外部中断功能,设置GPF0~3 为上升沿触发外部中断,设置GPF4~7输出高电平,这样当有任何一个按键按下的时候,按键所在列对应的GPIO 端口就会触发外部中断,由外部中断服务程序判断具体是哪个按键被按下。
2、外部中断
S3C2410 处理器集成了外部中断功能,所谓外部中断是指处理器中具有触发中断功能GPIO,当GPIO 出现电平变动时会触发中断。触发中断的方式有多种,比如高电平触发低电平触发、上升沿触发,下降沿触发等。S3C2410 处理器中具有外部中断功能的GPIO 每一位都可以单独设定中断触发方式,以满足不同的需要。在4×4
键盘驱动中,使用上升沿沿触发中断的方式,当按键按下时外部中
断被触发获得一次键值。
在linux 系统中对外部中断提供了比较好的支持,可以通过以下的函数设置外部中断。
【函数原型】int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
unsigned long irq_flags,
const char * devname,
void *dev_id)
【功能】申请中断
【参数】irq:要申请的硬件中断号
handler:向系统登记的中断处理函数,当中断发生时,系统调用这个函数
irqflags:中断处理的属性,若设置了SA_INTERRUPT,则表示中断处理函数是快 速处理函数;若设置为SA_SHIRQ 则表示多个设备共享中断
devname:设备标识字符串
dev_id在中断共享时用到,用于标识不同的中断响应。
【返回值】成功返回0,失败返回错误码
【函数原型】int set_external_irq(int irq, int edge, int pullup)
【功能】设置外部中断的中断触发方式
【参数】irq:外部中断号
edge:设置外部中断触发类型
pullup:设置GPIO 上拉或下拉
【返回值】成功返回0,失败返回错误码
【函数原型】int disable_irq(int irq)
【功能】禁止外部中断
【参数】irq:外部中断号
【返回值】成功返回0,失败返回错误码
【函数原型】int enable_irq(int irq)
【功能】使能外部中断
【参数】irq:外部中断号
【返回值】成功返回0,失败返回错误码
【函数原型】void free_irq(unsigned int irq, void *dev_id)
【功能】释放外部中断
【参数】irq:外部中断号
dev_id: 在中断共享时会用到,用于标识不同的中断,这个参数要和request_irq 函数中的参数dev_id 相同
【返回值】成功返回0,失败返回错误码
3、4×4 键盘驱动架构
4×4 键盘驱动通过全局数据缓冲队列实现在中断服务程序和读取函数之间数据传递,键值数据转移流程如图6.3所示。
五、实验步骤
1、实验指导书附带的源码中已经提供了键盘的驱动,驱动程序源码的路径为:\drivers\gpf4×4Keyboard,里面包含了键盘驱动程序和测试程序,可以使用下面的命令编译该驱动程序。其中,-I 参数后面的斜体部分需要换成实际的Linux 源码路径。
arm-linux-gcc –c –I/root/kernel/include –D__KERNEL__
s3c2410-gpf-keyboard.c
–o s3c2410-gpf-keyboard.o // 编译驱动
arm-linux-gcc test.c -o test // 编译应用程序
2、执行上面的命令后,将生成的s3c2410-gpf-keyboard.o、test 复制到目标板上,增加执行权限,然后使用下面的命令将驱动程序插入到目标Linux 系统的内核中,由于在驱动程序中已经使用devfs 为自己创建了设备文件节点,所以这里无需再使用mknod 命令。最后运行测试程序test,按下不同的键观察串口输出。
insmod s3c2410-gpf-keyboard.o
chmod +x test
./test
注意:按键使用的GPF0 与网络模块复用,应将网络模块跳线J8 断开
六、实验内容
1.代码:
驱动程序:
/*========================================================================= 工程名称: ex25_4MUL4keyboard_gpf
组成文件: key44_driver.c
功能描述: 实现带中断及定时器的2*3键盘,通过管道缓存键值,知识点的综合应用 硬件连接: GPF0~0与键盘行相连,GPF2~4与键盘列相连
维护记录: 2010-08-24 v1.1 add by dxh
=========================================================================*/ #include /*module_init()*/
#include /* printk() */
#include
/* __init __exit */
#include /* file_operation */
#include /* copy_to_user, copy_from_user */
#include /*class ,class_create ,device_create 等*/
#include /* Error number */
#include /* mdelay ,ndelay*/
#include /* udelay */
#include /*S3C2410_GPGCON*/
#include /*S3C24XX_VA_GPIO*/
#include //set_irq_type ,IRQ_TYPE_EDGE_FALLING
#include //IRQ_EINT2
#include //request_irq , free_irq
#include
//#define DEBUG //open debug message
#ifdef DEBUG
#define PRINTK(fmt, arg...) printk(KERN_WARNING fmt, ##arg)
#else
#define PRINTK(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
#endif
#define DRIVER_NAME "key44_eint"
//#define KEY1_IRQ IRQ_EINT0
//#define KEY2_IRQ IRQ_EINT1
//#define KEY3_IRQ IRQ_EINT2
//#define KEY4_IRQ IRQ_EINT3
#define KEY1_IRQ IRQ_EINT16
#define KEY2_IRQ IRQ_EINT17
#define KEY3_IRQ IRQ_EINT18
#define KEY4_IRQ IRQ_EINT19
#define GPFCON (*(volatile unsigned long *)S3C2410_GPGCON) //ADC control #define GPFDAT (*(volatile unsigned long *)S3C2410_GPGDAT) //ADC touch screen control
#define GPFUP (*(volatile unsigned long *)S3C2410_GPGUP) //ADC start or Interval Delay
static int MAJOR_NR = 0; /* Driver Major Number */
static int MINOR_NR = 0; //次设备起始号
struct class *my_class;
static struct semaphore readable; //定义信号量
#define BUFFER_SIZE 16 //定义fifo的大小为16
static struct kfifo kbuffer; //定义一个fifo变量
#define INVALID_KEY 0xFF
typedef unsigned char KEYVALUE;
static irqreturn_t key44_irqsrv(int irq, void *dev_id);
/*request irqs*/
static void register_irqs(void)
{
int err = 0;
err = request_irq(KEY1_IRQ,&key44_irqsrv,IRQF_DISABLED,"KEY1", (void *)0); PRINTK("\nkeyDriver_open int0 %d\n",err);
err = request_irq(KEY2_IRQ,&key44_irqsrv,IRQF_DISABLED,"KEY2", (void *)1); PRINTK("keyDriver_open int1 %d\n",err);
err = request_irq(KEY3_IRQ,&key44_irqsrv,IRQF_DISABLED,"KEY3", (void *)2); PRINTK("keyDriver_open int2 %d\n",err);
err = request_irq(KEY4_IRQ,&key44_irqsrv,IRQF_DISABLED,"KEY4", (void *)3); PRINTK("keyDriver_open int3 %d\n",err);
}
static int release_irqs(void)
{
free_irq(KEY1_IRQ, (void *)0);
free_irq(KEY2_IRQ, (void *)1);
free_irq(KEY3_IRQ, (void *)2);
free_irq(KEY4_IRQ, (void *)3);
return 0;
}
/*open the irq_eint*/
static void open_irqs(void)
{
enable_irq(KEY1_IRQ);
enable_irq(KEY2_IRQ);
enable_irq(KEY3_IRQ);
enable_irq(KEY4_IRQ);
}
/*close the irq_eint*/
static void close_irqs(void)
{
disable_irq_nosync(KEY1_IRQ);
disable_irq_nosync(KEY2_IRQ);
disable_irq_nosync(KEY3_IRQ);
disable_irq_nosync(KEY4_IRQ);
}
static void init_keyIO(void)
{
PRINTK("in init_keyIO!!\n");
unsigned short data = 0;
unsigned int config = 0;
unsigned short up = 0;
GPFCON &=~((0x03
GPFCON &=~((0x03
GPFCON |= (0x02
GPFCON |= (0x01
set_irq_type(KEY2_IRQ,IRQ_TYPE_EDGE_FALLING);
set_irq_type(KEY3_IRQ,IRQ_TYPE_EDGE_FALLING);
set_irq_type(KEY4_IRQ,IRQ_TYPE_EDGE_FALLING);
//GPFUP &= ~((0x01) | (0x01
GPFUP &= ~((0x01
GPFDAT &= ~((0x01
data = GPFDAT;
config = GPFCON;
up = GPFUP;
PRINTK("[init_keyIO] data is 0x%x; config is %x; up is %x", data, config, up); }
static void revs_keyIO(int row)
{
PRINTK("in revs_keyIO!!\n");
PRINTK("[revs_keyIO] the row is %d\n", row);
GPFCON &= ~((0x03
GPFUP |= (0x01
GPFDAT |= (0x01
GPFDAT = ~(0x01
//udelay(100);
}
static const KEYVALUE keyMap[][0x10] = {
{INVALID_KEY, 0x01, 0x05, INVALID_KEY, 0x09, INVALID_KEY, INVALID_KEY, INVALID_KEY, 0x0D, INVALID_KEY, INVALID_KEY, INVALID_KEY, INVALID_KEY, INVALID_KEY, INVALID_KEY, INVALID_KEY},
{INVALID_KEY, 0x02, 0x06, INVALID_KEY, 0x0A, INVALID_KEY, INVALID_KEY, INVALID_KEY, 0x0E, INVALID_KEY, INVALID_KEY, INVALID_KEY, INVALID_KEY, INVALID_KEY, INVALID_KEY, INVALID_KEY},
{INVALID_KEY, 0x03, 0x07, INVALID_KEY, 0x0B, INVALID_KEY, INVALID_KEY, INVALID_KEY, 0x0F, INVALID_KEY, INVALID_KEY, INVALID_KEY, INVALID_KEY, INVALID_KEY, INVALID_KEY, INVALID_KEY},
{INVALID_KEY, 0x04, 0x08, INVALID_KEY, 0x0C, INVALID_KEY, INVALID_KEY, INVALID_KEY, 0x10, INVALID_KEY, INVALID_KEY, INVALID_KEY, INVALID_KEY, INVALID_KEY, INVALID_KEY, INVALID_KEY},
};
static KEYVALUE key_scan(int row)
{
int line=0;
unsigned short data = 0;
revs_keyIO(row);
data = GPFDAT;
line = data >> 12;
PRINTK("line=%d!!\n",line);
return keyMap[row][line];
}
/*the interrupt deal function*/
static irqreturn_t key44_irqsrv(int irq, void *dev_id)
{
KEYVALUE key;
PRINTK("in irqsrv!!\n");
close_irqs(); //关外部中断
PRINTK("row=%d!!\n",(int)dev_id);
key = key_scan((int)dev_id);
PRINTK("[key44_irqsrv] key value is %d\n", key);
if(key!=INVALID_KEY)
{
kfifo_in(&kbuffer,&key,sizeof(key));
up(&readable);
}
udelay(1000);
init_keyIO();
open_irqs();
return 0;
}
static int keyDriver_open(struct inode *inode, struct file *file)
{
int err;
sema_init(&readable,0);
err=kfifo_alloc(&kbuffer,BUFFER_SIZE,GFP_KERNEL);
if(err!=0)
{
PRINTK("kfifo alloc failed!\n");
return -ENOMEM;
}
init_keyIO(); //初始化io口 //申请外部中断
register_irqs();
return 0;
}
static int keyDriver_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
int ret = 0;
KEYVALUE key;
if(down_interruptible(&readable) != 0)
return 0;
if(kfifo_len(&kbuffer) >= sizeof(key))
{
ret = count > sizeof(key) ? sizeof(key) : count;
if(kfifo_out(&kbuffer, &key, sizeof(key))!=0)
ret=copy_to_user(buff, &key, ret);
}
return ret;
}
static int keyDriver_release(struct inode *inode, struct file *file)
{
PRINTK("myDriver release called!\n");
close_irqs();
if(release_irqs()==0)
PRINTK("irq free succeed!!\n");
kfifo_free(&kbuffer);
return 0;
}
/* Driver Operation structure */
static struct file_operations keyDriver_fops = {
.owner = THIS_MODULE,
.read = keyDriver_read,
.open = keyDriver_open,
.release = keyDriver_release,
};
static int __init myModule_init(void)
{
PRINTK("keyDriver_init\n");
/* Driver register */
MAJOR_NR = register_chrdev(MAJOR_NR, DRIVER_NAME, &keyDriver_fops); if(MAJOR_NR
{
PRINTK("register char device fail!\n");
return MAJOR_NR;
}
my_class=class_create(THIS_MODULE,"udev_key23_eint");
device_create(my_class,NULL, MKDEV(MAJOR_NR, MINOR_NR), NULL,DRIVER_NAME);
PRINTK("register myDriver OK! Major = %d\n", MAJOR_NR); return 0;
}
static void __exit myModule_exit(void)
{
/* Module exit code */
PRINTK("exit in\n");
/* Driver unregister */
if(MAJOR_NR > 0)
{
unregister_chrdev(MAJOR_NR, DRIVER_NAME);
device_destroy(my_class,MKDEV(MAJOR_NR, MINOR_NR)); class_destroy(my_class);
PRINTK("myModule_exit ok\n");
}
return;
}
module_init(myModule_init);
module_exit(myModule_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("sunplus");
用户程序:
#include
#include
#include
#include
int main(int argc, char *argv[])
{
char *devname = "/dev/key44_eint";
int fd;
if(argc > 1)
devname = argv[1];
printf("NO: [1**********] Name:liyang\n");
printf("NO: [1**********] Name:liyang\n");
while(1)
{
fd = open(devname, O_RDONLY);
if(fd
{
perror("key44_enit open:");
exit(1);
} } } int len; unsigned char key; len = read(fd, &key, sizeof(key)); printf("~~~~~KEY~~~~~ len = %d, key = %d\n", len, key); close(fd);
2.运行结果:
七、实验总结
通过本次矩阵键盘驱动开发实验我掌握了4×4 键盘驱动的写法; 并且深入了解linux 驱动架构。还掌握了Linux 下S3C2410 IO 端口驱动程序的编写方法;知道了端口驱动程序的使用方法。并对驱动进行了测试。