1 实验目的
(1)了解按键原理及其与S3C2410的接口电路设计
(2)了解S3C2410芯片的外部中断处理机制
(3)掌握按键中断驱动的编写及测试过程
2 实验原理
(1)按键的硬件原理
在嵌入式系统中,按键的硬件原理比较简单,通过一个上拉电阻将处理器的外部中断(或GPIO)引脚拉高,电阻的另一端连接按键并接地即可实现。如图2-1所示:
图
2-1按键接口电路
仔细看图2-1,不难知道,当按键被按下时,EINT0上将产生低电平,这个低电平将中断CPU,CPU可以依据中断判断按键被按下。
(2)按键“消抖”
所有按键、触摸屏等机械设备都存在一个固有的问题,那就是"抖动",按键从最初接通到稳定接通要经过数毫秒乃至数十毫秒,其间可能发生多次"接通―断开"的过程。因此仅仅依据中断被产生就认定有一次按键行为是很不准确的。如果不消除"抖动"的影响,一次按键可能被理解为多次按键。
消除按键抖动影响的方法是:在判断有键按下后,进行软件延时(如20ms,在延时过程中要屏蔽对应中断),再判断键盘状态,如果仍处于按键按下状态,则可以判定该按键被按下。图2-2是典型的包含消抖功能的按键中断处理流程。
图2-2 按键中断处理流程
3 实验任务
(1)编写按键中断设备驱动程序,驱动程序中手动定义设备名称及主设备号为213,实现与BUTTON设备相应的端口配置,中断的申请,以及读写设备的接口函数等。
(2)将驱动编译成模块,并实现模块的加载及卸载。
(3)编写驱动的测试程序,在程序中实现打开BUTTON设备和LED设备,主循环中不断读取按键的状态,当按键按下时,控制LED亮一段时间(1S左右)后灭掉。
4. 实验步骤
以下操作都在nfs文件系统目录(/home/kernel/rootfs/rootfs)下进行,因此先执行如下命令。获取
cd /home/kernel/rootfs/rootfs
(1)编写led.c文件
建立led目录:
mkdir usr/button
进入button目录,在该目录下建立两个子目录driver 和test ,前者用来存放驱动程序,后者用来存放驱动测试程序:
cd usr/ button
mkdir driver test
进入驱动程序目录,建立设备驱动文件button.c:
cd driver
vi button.c
按键驱动程序如下button.c所示:
/*************************** 头文件 ***************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//*********************** 定义设备结构体及相关宏 ***************************
#define DEVICE_NAME "button" //定义设备名
#define DEVICE_MAJOR 213 //手动定义BUTTON设备的主设备号为213
static int button_major = DEVICE_MAJOR ;
#define BUTTON_IRQ IRQ_EINT0 //定义BUTTON对应S3C2410的外部中断0
#define BUTTON S3C2410_GPF0 //定义BUTTON对应S3C2410的GPF0端口
#define BUTTON_EINT0 S3C2410_GPF0_EINT0
#define BUTTON_INP S3C2410_GPF0_INP
#define BUTTON_UP 0 //按键抬起状态
#define BUTTON_DOWN 1 //按键按下状态
#define BUTTON_X 2 //不确定状态,本实例中可理解为抖动状态
//定义BUTTON设备结构体
struct button_dev
{
struct cdev cdev; //BUTTON设备对应一个字符设备结构体
int status; //按键状态标识:抬起、按下、抖动
};
static struct button_dev dev;
//***************************** 函数声明 *********************************
void s3c2410_button_s3c2410_button_InitIO(void); //初始化IO端口的函数
/*
******************************s3c2410_button_InitIO**********************
*描述 :初始化IO端口
*参数 :无
*返回值:无
*************************************************************************
*/
void s3c2410_button_InitIO(void)
{
s3c2410_gpio_cfgpin(BUTTON,BUTTON_EINT0); //配置按键中断的端口为中断功能
}
/*
******************************isr_button()*******************************
*描述 :EINT0的中断处理函数,设置按键状态为BUTTON_X
*参数 :irq :中断号; dev_id;regs;
*返回值:成功返回0
*在linux/interrupt.h中定义了typedef irqreturn_t (*irq_handler_t)(int, void *);
*************************************************************************
*/
static irqreturn_t isr_button(int irq,void *dev_id,struct pt_regs *regs)
{
disable_irq(0); //禁止中断
dev.status = BUTTON_X;//将按键置为抖动状态,说明有按键中断,但不一定有键按下
enable_irq(0); //使能中断
return 0;
}
/*
**************************s3c2410_button_open()**************************
*描述 :打开设备函数,向系统申请中断
*参数 :struct inode *inode,struct file *filp
*返回值:失败返回错误代码ret,成功返回0
*************************************************************************
*/
static int s3c2410_button_open(struct inode *inode,struct file *filp)
{
int ret;
ret=request_irq(BUTTON_IRQ,isr_button,IRQF_SAMPLE_RANDOM,DEVICE_NAME,NULL); //申请中断
if(ret) {//申请失败
printk("BUTTON_IRQ: could not register interrupt\n");
return ret;
}
return 0;
}
/*
************************s3c2410_button_release()*************************
*描述 :注销设备函数,实现中断释放
*参数 :struct inode *inode,struct file *filp
*返回值:0
*************************************************************************
*/
static int s3c2410_button_release(struct inode *inode,struct file *filp)
{
free_irq(BUTTON_IRQ,NULL); //释放中断
return 0;
}
/*
**************************3c2410_button_ioctl()**************************
*描述 :IO控制函数,本实例中不做任何事
*参数 :cmd: 用户定义的IO控制命令; arg: 传递用户参数
*返回值:0
*************************************************************************
*/
static int s3c2410_button_ioctl(struct inode *inode,struct file *filp,
unsigned int cmd,unsigned long arg)
{
return 0;
}
/*
**************************s3c2410_button_read()**************************
*描述 :读函数,读取按键的状态
*参数 :buffer: 用来存储按键状态;
count: 用来记录用户读取了多少个字符
*返回值:count:用户读取的字符数
*************************************************************************
*/
static ssize_t s3c2410_button_read(struct file *filp,char *buffer,size_t count,loff_t *ppos)
{
int ret = count ;
if(dev.status = BUTTON_X){//如果按键状态是BUTTON_X,说明有按键中断产生。
msleep(20); //延时20毫秒去除按键抖动
disable_irq(0); //禁止中断
s3c2410_gpio_cfgpin(BUTTON,BUTTON_INP); //配置按键中断的端口为输入功能
if(!s3c2410_gpio_getpin(BUTTON)){//读取端口的值,如果是0说明按键按下
dev.status = BUTTON_DOWN;
}
else dev.status = BUTTON_UP; //如果为1,说明只是按键抖动
//重新配置按键中断的端口为中断功能
s3c2410_gpio_cfgpin(BUTTON,S3C2410_GPF0_EINT0);
enable_irq(0); //使能中断
}
put_user(dev.status,(int *)buffer); //将按键状态提交给用户
return ret;
}
/*
**************************3c2410_button_write()**************************
*描述 :写操作函数,本实例中不做任何事
*参数 :
*返回值:count
*************************************************************************
*/
static ssize_t s3c2410_button_write(struct file *filp,char *buffer,size_t count,loff_t *ppos)
{
int ret = count;
return ret;
}
/*
**************************s3c2410_button_fops****************************
*描述 :文件操作结构体,实现 s3c2410_button_open()等函数与open()等系统调用的连接
*参数 :
*返回值:
*************************************************************************
*/
static struct file_operations s3c2410_button_fops = {
.owner = THIS_MODULE,
.open = s3c2410_button_open,
.release = s3c2410_button_release,
.ioctl = s3c2410_button_ioctl,
.read = s3c2410_button_read,
.write = s3c2410_button_write,
};
/*
**************************button_setup_cdev()****************************
*描述 :安装模块的函数,在设备加载模块里面调用
*参数 :无
*返回值:无
*************************************************************************
*/
static void button_setup_cdev(void)
{
int err,devno = MKDEV(button_major,0);
cdev_init(&dev.cdev,&s3c2410_button_fops);
dev.cdev.owner = THIS_MODULE;
dev.cdev.ops = &s3c2410_button_fops;
err = cdev_add(&dev.cdev,devno,1);
if(err)
printk("Error %d adding BUTTON%d",err);
}
/*
****************************s3c2410_button_init()************************
*描述 :模块加载,IO及相关变量初始化
*参数 :无
*返回值:无
*************************************************************************
*/
static int s3c2410_button_init(void)
{
int result;
set_irq_type(BUTTON_IRQ,IRQT_FALLING); //设置外部中断0为下降沿中断
dev_t devno = MKDEV(button_major,0);
if(button_major)
result = register_chrdev_region(devno,1,DEVICE_NAME);
else{
result = alloc_chrdev_region(&devno ,0 ,1,DEVICE_NAME);
button_major = MAJOR(devno);
}
if(result < 0)return result;
button_setup_cdev();
s3c2410_button_InitIO(); //初始化IO端口
dev.status = BUTTON_UP; //初始化按键状态为抬起状态
printk(DEVICE_NAME " initialized\n");
return 0;
}
/*
****************************s3c2410_button_exit()************************
*描述 :模块卸载
*参数 :无
*返回值:无
*************************************************************************
*/
static void s3c2410_button_exit(void)
{
cdev_del(&dev.cdev); //注销设备
unregister_chrdev_region(MKDEV(button_major,0),1); //释放设备号
}
module_init(s3c2410_button_init);
module_exit(s3c2410_button_exit);
MODULE_LICENSE("GPL");
(2)编写Makefile文件(主机的/home/kernel/rootfs/rootfs/usr/button/driver目录下)
vi Makefile
在该文件中加入以下内容:
#如果已定义KERNELRELEASE,则说明是从内核构造系统调用的,
#因此可利用其内建语句。
ifneq ($(KERNELRELEASE),)
obj-m := button.o
#要构建的模块名称为button.ko,
#并由两个源文件生成(比如file1.c和file2.c)
#module-objs := file1.o file2.o
#否则,是直接从命令行调用的,
#这时要调用内核构造系统。
else
KERNELDIR := /home/kernel/linux-2.6.24.4
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
clean:
rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions *.order *symvers
(3)编译
make
执行make命令进行编译,编译完成后,该目录下有一些文件,其中button.ko就是编译成功的模块文件。
(4)加载模块
首先设置内核以NFS方式加载根文件系统,在宿主机上运行minicom,加载完U-Boot,内核和nfs文件系统之后按回车键进入目标机shell控制台,在目标机控制台中输入模块加载命令:
insmod usr/led/driver/led.ko
insmod usr/button/driver/button.ko
如果输出“button initialized”,表示button设备驱动加载成功
(5)编写测试文件button_led.c(该测试程序还依赖LED的驱动)
进入宿主机的/home/kernel/rootfs/rootfs/usr/button/test目录。
cd /home/kernel/rootfs/rootfs/usr/button/test
vi button_led.c
驱动测试文件button_led.c如下所示:
#include
#include
#include
#include
#include
#include
#define UP 0
#define DOWN 1
#define X 2
#define LED_ON 0
#define LED_OFF 1
int main()
{
int fd0,fd1,i,j;
int ret= 0;
int button_status;
fd0 = open("/dev/button",O_RDWR);//打开button设备fd0
if (fd0 == -1) {//打开fd0设备失败
printf("open device button errr!\n");
return 0;
}
fd1 = open("/dev/led",O_RDWR);//打开led设备fd1
if (fd1 == -1) {//打开fd1设备失败
printf("open device led errr!\n");
return 0;
}
ioctl(fd1,LED_OFF); //先熄灭D1
printf("button test show. press ctrl+c to exit \n");
while(1) {//主循环
read(fd0,&button_status,1);//读取按键的状态,看是否被按下
if(button_status == DOWN){//如果按键按下了
ioctl(fd1,LED_ON); //点亮D1
for(i=0;i<300;i++)
for(j=0;j<5000;j++);//延时一段时间
ioctl(fd1,LED_OFF); //熄灭D1
}
for(i=0;i<300;i++)
for(j=0;j<5000;j++);
}
close(fd0);//关闭fd0设备
close(fd1);//关闭fd1设备
return 0;
}
(6)编译测试程序
arm-linux-gcc -o led_button led_button.c
该命令的意思是使用的交叉编译器arm-linux-gcc对测试程序led_button.c进行编译,编译成功后在test目录下会生成目标机的可执行文件led_button。
(7)运行测试程序
创建设备
在目标机中,通过查看/proc/devices中注册进内核的设备条目及相关的设备号。进入/dev目录,创建设备,设备名为led,属于字符型设备,主设备号是212,次设备号是0;创建设备,设备名为button,属于字符型设备,主设备号是213,次设备号是0。(要与led.c和button.c文件中的定义相符):
cat /proc/devices
cd /dev
mknod led c 212 0
mknod button c 213 0
./usr/button/test/led_button或/usr/led/test/led_button
(10)卸载模块
rmmod /dev/button
rmmod /dev/led
From:嵌入式Linux初级实验
上一篇:[ARM笔记]GPIO硬件介绍
下一篇:[ARM笔记]驱动对设备的识别过程及实例——NAND Flash
推荐阅读最新更新时间:2024-03-16 15:21