矩阵键盘驱动开发实验报告

实验报告书

专业班级:

学号:

姓名:

联系电话:

指导老师:

实验时间:

计算机学院计算机科学与技术

一、实验目的

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 端口驱动程序的编写方法;知道了端口驱动程序的使用方法。并对驱动进行了测试。


相关文章

  • 电机控制设计报告最终版本
  • "盟升杯"大学生电子设计大赛 <电机控制>组设计报告 设计题目:电机控制 目 录 1. 引言...................................................................... 3 2. 设计指标........ ...

  • 按键控制数码管和流水灯设计报告实验报告
  • 摘要 单片机自20世纪70年代以来,以其极高的性价比,以及方便小巧受到人们极大的重视和关注.本设计选用msp430f249芯片作为控制芯片,来实现矩阵键盘对LED 数码管显示的控制.通过单片机的内部控制实现对硬件电路的设计, 从而实现对4*4矩阵键盘的检测识别.用单片机的P3口连接4×4矩阵键盘,并 ...

  • 单片机实验总结报告
  • 1 福建农林大学计算机与信息学院 信息工程类 实验报告 课程名称: 姓 名: 系: 专 业: 年 级: 学 号: 指导教师: 职 称: 单片机技术 电子信息工程系 电子信息工程 易金聪 副教授 2013 年 10 月 28 日 实验项目列表 福建农林大学计算机与信息学院信息工程类实验报告 系: 电子 ...

  • 数控稳压电压源实验报告
  • 电子信息工程专业中期实习报告> 题 目数控直流稳压电源的设计与实现 学 号 姓 名 班 级 指导教师 同组 人 电子工程系 < 目 录 一.概述 . ..................................................................... ...

  • 基于STC89C52单片机音乐播放器的设计
  • 目 录 第1章 单片机系统硬件电路 ........................................... 2 1.1 实习目的与要求............................................... 2 1.2 单片机型号及特性............. ...

  • 智能窗控制系统的设计
  • 课程设计报告 课程名称 微机控制技术 设计题目 智能窗自动控制系统设计 专业班级 姓 名 学 号 指导教师 起止时间 2013.12.23~2013.12.31 电气与信息学院 课程设计考核和成绩评定办法 1. 课程设计的考核由指导教师根据设计表现.设计报告.设计成果.答辩等几个方面,给出各项权重, ...

  • 可编程脉冲信号发生器的设计
  • 可编程脉冲信号发生器的设计 摘 要 基于单片机的可编程脉冲信号发生器,通过4x4的非编码矩阵键盘键入脉冲信号的指标参数频率.占空比和脉冲个数,在单片机的控制处理下发出满足信号指标的脉冲信号,并在液晶显示屏的制定位置显示出相关参数.复位电路采用上电复位和手动复位的复合复位方式,保证单片机在上电和程序运 ...

  • 基于单片机的病房呼叫系统设计
  • 学号:[1**********] 本科毕业论文 题 目: 基于单片机的病房呼叫系统的设计 院 系: 专 业: 班 级: 学生姓名: 导师姓名: 生命科学技术学院 生物医学工程 2009级1班 宋攀婷 李中伟 二○一三年六月 新乡医学院生命科学技术学院毕业设计(论文) 毕业设计(论文)诚信声明书 本人 ...

  • 北工大电工实验2报告
  • 题目:闭环温度控制系统设计与实现电子工程设计报告 专业:电子信息工程 小组: 姓名学号: 指导教师:张印春 完成日期:2016年1月1日 摘要 本电子工程设计实验的任务是完成一套小型的温度控制系统.这个系统需要完成非电量到电量信号转换.信号处理.数据采集.数据处理.人机交互.数据通信.控制等设计工作 ...

© 2024 范文中心 | 联系我们 webmaster# onjobs.com.cn