您的位置 首页 汽车

一个适当具体的MINI2440按键驱动详解

/*mini2440_buttons_my.c*//*后面加了_my*//*按键驱动程序*//*mini2440所用到的按键资源*//*********************************

/*mini2440_buttons_my.c*/

/*后边加了_my*/

/*按键驱动程序*/

/*mini2440所用到的按键资源*/
/**************************************************/
/* 按键 对应的IO寄存器 对应的中止引脚*/
/* K1 GPG0 EINT8 */
/* K2 GPG3 EINT11 */
/* K3 GPG5 EINT13 */
/* K4 GPG6 EINT14 */
/* K5 GPG7 EINT15 */
/* K6 GPG11 EINT19 */
/**************************************************/

/*要搞清楚谁是输入*/
/*在这儿,按键操控对应的中止引脚,然后操控对应的IO寄存器*/
/*相当于信息从外面输入*/
/*咱们要做的是依据对应的输入信息,来采纳相应的呼应动作*/
/*这就达到了中止呼应的意图*/
/*其中心便是要检测*/
/*那么,该怎么去检测呢?*/
/*通过什么来检测呢?*/

/*怎么得知一个设备终究用到哪些资源呢?*/
/*这是个非常重要的问题*/
/*我想应该看详细的电路原理图*/
/*只要看图,才干了解详细的电路衔接状况*/
/*然后得知设备所需的硬件资源*/
/*厂商的原理图一般给的都比较详细*/

/*引证的头文件*/

#include /*模块有关的*/

#include /*内核有关的*/

#include /*文件体系有关的*/

#include /*init*/

#include /*delay*/

#include /*poll*/

#include /*中止*/

#include interrupt.h> /*linux中止*/

#include /*uaccess*/

#include /*寄存器设置*/

#include /*hardware*/

/*界说宏*/

#define BUTTON_MAJOR 221 /*主设备号,本来是232,我改为221*/

#define DEVICE_NAME “buttons_my” /*设备名,本来是buttons,我加上了_my*/

/*界说按钮中止的描绘结构体*/
/*由它把按钮中止的信息归纳起来*/
/*各个成员表明什么意思?*/

struct button_irq_desc
{
int irq; /*中止号*/
/*中止号仅有表明一个中止*/

int pin; /*中止操控的寄存器*/
/*该寄存器的值由中止引脚设置*/
/*咱们期望从该寄存器读出操控信息*/

int pin_setting; /*中止的引脚*/
/*该引脚的电平由按键来操控*/
/*然后终究咱们由按键操控了寄存器的值*/

int number; /*编号*/
char *name; /*称号*/
};

/*指定6个按键的信息*/

static struct button_irq_desc button_irqs [] =
{
{IRQ_EINT8,S3C2410_GPG0,S3C2410_GPG0_EINT8,0,”KEY1″}, /*K1*/
{IRQ_EINT11,S3C2410_GPG3,S3C2410_GPG3_EINT11,1,”KEY2″}, /*K2*/
{IRQ_EINT13,S3C2410_GPG5,S3C2410_GPG5_EINT13,2,”KEY3″}, /*K3*/
{IRQ_EINT14,S3C2410_GPG6,S3C2410_GPG6_EINT14,3,”KEY4″}, /*K4*/
{IRQ_EINT15,S3C2410_GPG7,S3C2410_GPG7_EINT15,4,”KEY5″}, /*K5*/
{IRQ_EINT19,S3C2410_GPG11,S3C2410_GPG11_EINT19,5,”KEY6″}, /*K6*/
}

/*这样,资源就安排起来了*/
/*事实上,在这儿咱们不只安排起了硬件资源*/
/*咱们也把必定的软件资源也糅合进去了*/
/*像中止号*/

/*key_values数组*/
/*寄存各个按键在产生中止状况下的值*/
/*volatile是什么意思呢?*/
/*这个数组是咱们寄存按键操作成果的,因而非常重要*/

static volatile int key_values [] = {0,0,0,0,0,0};

/*宏DECLARE_WAIT_QUEUE_HEAD(),是干什么的呢?*/
/*该宏应该是创立了一个等候行列*/
/*等候行列,是进程调度的一种重要办法*/
/*等候行列也很有意思,button_waitq,表明按键等候的行列*/
/*便是说,按键一按下,就会激活其等候行列里的进程,来做相应的处理*/
/*因而,按键的等候行列,或者说中止所设置的等候行列,*/
/*是中止处理中非常重要的资源,它大大扩展了中止处理的才能*/

static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /*button_waitq是什么呢?*/
/*应该是等候行列的称号*/

/*key_values数组中是否有数据的标志,0表明无数据可读,1表明有数据可读*/

static volatile int ev_press = 0; /*初始为0*/

/*中止服务程序buttons_interrupt()的声明*/
/*即当检测到有中止时,就会履行该中止服务程序*/
/*那么怎么检测到有中止产生呢?*/
/*而且中止产生了,知道产生了什么样的中止呢?*/
/*中止有很多种,该中止服务程序终究该服务于哪一个中止呢?*/
/*明显,要把中止号与中止服务程序联合起来,构成一个全体*/
/*这个作业可以在open函数里做*/

/*参数irq—中止号*/
/*中止服务程序应该是与中止号一一对应的*/
/*对应于某个中止号的中止一产生,就会调用该中止号对应的服务程序*/
/*那么,检测中止的产生,就成了先决条件*/
/*参数dev_id —详细是哪一个按钮*/

static irqreturn_t buttons_interrupt(int irq,void *dev_id);

/*mini2440_buttons_open()函数声明*/
/*驱动函数open调用的详细函数*/
/*由open函数详细完成硬件的初始化作业*/
/*以及软件的初始化作业*/
/*为咱们的键盘设备的运转发明好环境*/

static int mini2440_buttons_open(struct inode *inode,struct file *file);

/*mini2440_buttons_close()函数的声明*/
/*release调用的详细函数*/
/*设备软件环境的拆开*/
/*详细便是中止的开释作业*/
/*由于中止资源,也是体系名贵的资源,所以不必的时分,要开释*/

static int mini2440_buttons_close(struct inode *inode,struct file *file);

/*mini2440_buttons_read()函数的声明*/
/*read调用的详细函数*/
/*由它读取键盘输入的成果*/
/*实质上便是读取key_values数组的值*/
/*它完成了键盘作为输入设备的中心功用*/
/*数组是否可读,要依据标志位ev_press来判别*/
/*假如数组可读,则读取数据到用户buffer中*/
/*假如数组不可读,则进程进入等候行列,等候到数组可读停止*/
/*等候行列机制,是中止办理中常用到的机制*/
/*由于有些进程常常需求等候某一工作的产生*/

static int mini2440_buttons_read(struct file *filp,char __user *buff,size_t count,loff_t *offp);
/*留意__user,指的是用户空间*/
/*即要把键盘的输入成果读取到用户空间去*/

/*mini2440_buttons_poll()函数的声明*/
/*poll调用的详细函数*/
/*poll实质上是select的调用函数*/
/*假如有按键数据,则select会马上回来*/
/*假如没有按键数据,则等候*/
/*实质上这是键盘等候输入的机制*/

static unsigned int mini2440_buttons_poll(struct file *file,struct poll_table_struct *wait);

/*file_operations结构体*/
/*驱动函数的设置*/
/*别离将前面的驱动函数设置进来*/

static struct file_operations mini2440_buttons_fops =
{
.owner = THIS_MODULE,

.open = mini2440_buttons_open, /*open()*/

.release = mini2440_buttons_close, /*release()*/

.read = mini2440_buttons_read, /*read()*/

.poll = mini2440_buttons_poll /*poll()*/
};

/*mini2440_buttons_init()函数的声明*/
/*module_init调用的详细函数*/
/*模块创立时的初始化函数*/
/*首要做的作业是注册设备和创立设备*/
/*而详细的硬件初始化作业,它可以不做*/
/*而把它留给fops里的函数来做*/

static int __init mini2440_buttons_init(void);

/*mini2440_buttons_exit()函数的声明*/
/*模块卸载时的扫尾作业*/
/*首要是设备的卸载作业*/

static void __exit mini2440_buttons_exit(void);

/*模块创立时的进口点*/

module_init(mini2440_buttons_init);

/*模块卸载时的进口点*/

module_exit(mini2440_buttons_exit);

/*驱动程序的一些信息*/

MODULE_AUTHOR(“http://www.arm9.net”); /*驱动程序的作者*/

MODULE_DESCRIPTION(“S3C2410/S3C2440 BUTTON Driver”); /*描绘信息*/

MODULE_LICENSE(“GPL”); /*遵从的协议*/

/********************************************************************/
/*********************下面是前面声明函数的完成***********************/
/********************************************************************/

/**********************mini2440_buttons_init()***********************/

static int __init mini2440_buttons_init(void)
{
int ret; /*设备注册的回来值*/

/*注册设备驱动程序*/
/*设备号,设备名,和驱动函数*/

ret = register_chrdev(BUTTON_MAJOR,DEVICE_NAME,&mini2440_buttons_fops);

/*对注册失利的处理*/

if(ret < 0)
{
printk(DEVICE_NAME ” cant register major number\n”);
return ret;
}

/*创立设备*/
/*devfs_mk_cdev()函数是内核态的设备创立函数*/
/*而mknod是用户态的设备创立函数*/

devfs_mk_cdev(MKDEV(BUTTON_MAJOR,0),S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,DEVICE_NAME);

printk(DEVICE_NAME ” initialized\n”);

return 0;
}

/******************mini2440_buttons_exit()****************************/

static void __exit mini2440_buttons_exit(void)
{
/*移除设备*/

devfs_remove(DEVICE_NAME);

/*注消设备驱动*/

unregister_chrdev(BUTTON_MAJOR,DEVICE_NAME);
}

/*****************mini2440_buttons_open()******************************/

static int mini2440_buttons_open(struct inode *inode,struct file *file)
{
int i; /*循环变量,由于有6个按钮*/

int err; /*中止注册函数的回来值*/

/*对每个按钮别离处理,用for循环来做*/
/*详细地是要联合寄存器和相应的引脚*/
/*联合中止号和相应的中止服务程序*/
/*这一步类似于前面所说的驱动的注册*/
/*咱们可以成功称作中止的注册*/

for(i = 0;i < sizeof(button_irqs)/sizeof(button_irqs[0]);i++)
{
/*寄存器与中止引脚的联合*/

s3c2410_gpio_cfgpin(button_irqs[i].pin,button_irqs[i].pin_setting);

/*中止的注册*/
/*request_irq()函数*/
/*要留意其输入参数*/
/*&button_irqs[i]是该中止享有的资源*/
/*会被传入buttons_interrupt,进行处理*/

err = request_irq(button_irqs[i].irq,buttons_interrupt,NULL,button_irqs[i].name,(void *)&button_irqs[i]);

/*中止类型的设置*/
/*set_irq_type()函数*/
/*IRQT_BOTHEDGE的中止类型代表什么样的中止呢?*/

/*有几个非常重要的问题*/
/*中止注册后,并设置好其中止类型之后,当有中止产生时,*/
/*即按下某个按钮时,体系可以主动检测到有中止产生吗?*/
/*检测到有中止产生,它可以主动辨别是几号中止吗?*/
/*知道了是几号中止,那么它能主动调用其中止服务程序吗?*/
/*对这几个问题的回答,够成了linux体系中止处理机制的中心*/

set_irq_type(button_irqs[i].irq,IRQT_BOTHEDGE);

/*注册失利的处理*/

if(err)
break; /*跳出循环*/
}

/*若有一个按钮中止注册失利*/
/*则还需把前面注册成功的中止给拆了*/

if(err)
{
i–; /*回到前面一个按钮的处理*/

for(;i >=0; i–) /*依此撤除*/
{
/*使中止不起作用*/

disable_irq(button_irqs[i].irq);

/*开释中止资源*/

free_irq(button_irqs[i].irq,(void *)&button_irqs[i]);
}

return -EBUSY; /*中止注册没成功的终究的回来值*/
}

return 0; /*正常回来*/
}

/**************************buttons_interrupt()*****************************/
/*此中止服务程序,在每中止一次,就要对key_values数组设一下值*/
/*并对数组可读标志位ev_press设一下值*/
/*并唤醒在等候行列里的进程*/
/*这是中止处理常常要做的工作*/
/*在这儿,等候行列button_waitq里常常等候的进程是数组的读取进程*/
/*便是说,读取进程在没有读到数据的时分就一直在等候,等候按键的输入*/
/*读取进程在等候,并不代表一切进程在等候,其它进程该干啥干啥去*/

static irqreturn_t buttons_interrupt(int irq,void *dev_id)
{
/*button_irq_desc结构体变量*/
/*对传入的资源进行处理*/

struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;

/*获取寄存器的值*/
/*这一步至关重要*/
/*s3c2410_gpio_getpin()函数直接获取寄存器的值*/

/*要留意,按一下按钮,会产生两次中止*/

/*即按下是一次中止,铺开又是一次中止*/

int up = s3c2410_gpio_getpin(button_irqs->pin);

/*通过电路原理图,可以知道没按下的时分,中止引脚应该是高电平*/
/*然后寄存器的值应该是1*/
/*变量取up也是有含义的,表明默许状况是弹起的状况*/
/*当按下按钮的状况下,寄存器的值就应该是0*/

/*下面临up的值进行处理*/
/*便是要把数据通过必定的改换存入key_values数组中*/

if(up) /*假如是弹起的状况*/
/*那么就要在key_values数组的相应位存入很大的一个值*/
/*一起又要能从值里辨别出是哪个按键*/

key_values[button_irqs->number] = (button_irqs->number + 1) + 0x80;
/*比方K1键敞开的状况下,key_values[0]被置为(0+1)+0x80,即为129*/

else /*假如按键是闭合的状况*/
/*那么就要在key_values数组的相应位存入一个很小的数*/
/*一起又要能从值中辨别出是哪个键*/

key_values[button_irqs->number] = (button_irqs->number + 1);
/*比方K1键闭合,则key_values[0]被置为(0+1),即为1*/

/*对数组可读标志位进行设置*/

ev_press = 1; /*表明数组现已可读了*/

/*唤醒休眠的进程?*/
/*button_waitq行列里寄存有相应的处理进程*/
/*如读取数组的值的进程*/
/*要留意wake_up_interruptible()这些函数的用法*/

wake_up_interruptible(&button_waitq);

/*回来*/

return IRQ_RETVAL(IRQ_HANDLED); /*?*/
}

/**********************mini2440_buttons_close()*****************************/

static int mini2440_buttons_close(struct inode *inode,struct file *file)
{
int i; /*循环变量,要操作好几个按键*/

/*for循环,对各个按键依此开释中止*/

for(i = 0;i < sizeof(button_irqs)/sizeof(button_irqs[0]);i++)
{
/*使中止失效*/

disable_irq(button_irqs[i].irq);

/*开释资源*/

free_irq(button_irqs[i].irq,(void *)&button_irqs[i]);
}

/*回来*/

return 0;
}

/**********************mini2440_buttons_read()***************************/

/*要留意,该read函数,只读取一次中止的值,而不是接连地读入*/
/*要做到接连地读入,则需求做一个循环,不断地调用该read函数,但那不是驱动程序里该做的工作*/

static int mini2440_buttons_read(struct file *filp,char __user *buff,size_t count,loff_t *offp)
{
unsigned long err; /*copy_to_user()函数的回来值*/

/*假如key_values 数组里没有值,则会此进程会休眠*/
/*一直到中止降临之后,中止服务程序会唤醒此休眠进程然后持续读取值*/
/*key_values数组里有没有值,是靠ev_press标志位来判别的*/
/*有值,便是1,无值,便是0*/

/*进程等候行列的机制,是进程调度的一种办法*/

if(!ev_press) /*标志位为0,即无数据时*/
{
if(filp->f_flags & O_NONBLOCK) /*??*/
return -EAGAIN;
else /*进程休眠,放进button_waitq等候行列*/
/*这儿,把ev_press标志位设成了休眠进程的标志位了?*/
/*这是为了便于运用poll_wait函数*/
/*也便是利于select函数*/
wait_event_interruptible(button_waitq,ev_press);
/*在中止处理函数中,此进程会被唤醒*/
/*唤醒前,ev_press 已被置1了*/
/*唤醒后的履行点从这儿开端*/
}

/*下面便是标志位为1,即有数据可读的的处理状况*/

/*那就开端往用户空间读数据呗*/

err = copy_to_user(buff,(const void *)key_values,min(sizeof(key_values),count));
/*copy_to_user()函数的运用*/

/*对key_values数组清零*/

memset((void *)key_values,0,sizeof(key_values));

/*对标志方位0*/
/*表明读取过了*/

ev_press = 0;

/*对err的处理*/

if(err) /*读取过错*/
return -EFAULT;
else /*读取正确*/
/*则回来读取到的字节数*/
return min(sizeof(key_values),count);
}

/************************mini2440_buttons_poll()***********************/

static unsigned int mini2440_buttons_poll(struct file *file,struct poll_table_struct *wait)
{
unsigned int mask = 0; /* */

/*poll_wait()函数*/
/*会监测进程行列button_waitq里的进程*/
/*例如,假如mini2440_button_read地点的进程的标志位ev_press置为1了*/
/*那么就不会再等候了*/
/*这实质上便是select函数的运转机制*/

poll_wait(file,&button_waitq,wait);

if(ev_press)
mask |= POLLIN | POLLRDNORM; /*??*/

return mask;
}

==================================================================================
下面是测验代码:

/*按键测验程序*/

#include /*规范输入输出头文件*/

#include /*规范库*/

#include /*一些宏的界说在这儿*/

#include /*设备的操控*/

#include /*界说了一些类型*/

#include /*状况*/

#include /*文件操控*/

#include /*挑选?*/

#include /*时刻方面的函数*/

#include /*有关过错方面的宏*/

/*主函数进口*/

int main(void)
{
int i; /*键盘输出时用到的循环变量*/

int buttons_fd; /*buttons设备号*/

int key_value[4]; /*四个按键的取值*/

/*翻开键盘设备文件*/

buttons_fd = open(“/dev/buttons”,0); /*以0方法翻开*/

/*翻开犯错处理*/

if(buttons_fd < 0) /*翻开犯错就会回来一个负值*/
{
perror(“open device buttons”); /*perror函数?*/

exit(1); /*回来1*/
}

/*for无限循环,等候用户输入*/
/*这是很典型的程序履行方法*/

for(;;)
{
fd_set rds; /*fd_set是types.h中界说的类型,实质上是int型*/
/*rds用来存储设备号*/

int ret; /*for循环内界说的局部变量ret*/

FD_ZERO(&rds); /*rds初始化*/
/*FD_ZERO是哪里界说的呢?*/

FD_SET(buttons_fd,&rds); /*将buttons设备号赋给rds*/
/*FD_SET是哪里界说的呢?*/

/*运用体系调用select查看是否可以从/dev/buttons设备读取数据*/
/*select函数是干什么的呢?*/

ret = select(buttons_fd + 1,&rds,NULL,NULL,NULL);
/*回来值ret*/
/*回来值的详细含义是什么呢?*/

/*对ret的处理*/

if(ret < 0) /*当ret小于0*/
{
perror(“select”);
exit(1);
}

if(ret == 0) /*当ret等于0*/
{
printf(“Timeout.\n”);
}
else /*可以读到数据*/
if(FD_ISSET(buttons_fd,&rds)) /*??*/
{
/*读取键盘驱动宣布的数据*/
/*key_value和键盘驱动中界说共同*/

int ret = read(buttons_fd,key_value,sizeof(key_value)); /*留意此处的ret和前面的ret有何不同*/
/*留意键盘设备读取的特色*/

/*对ret的处理*/
if(ret != sizeof(key_value)) /*没有接纳够*/
{
if(errno != EAGAIN) /*???*/
perror(“read buttons\n”);
continue;
}
else /*正确接纳,则打印到规范终端*/
{
for(i = 0;i < 4;i++) /*最开端界说的循环变量i*/
printf(“K%d %s, key value = 0x%02x\n”,i,(key_value[i] & 0x80) ? “released” : key_value[i] ? “pressed down” : “”,key_value[i]);
/*这一连串的输出,要留意格局*/
}
}
}

/*封闭设备*/

close(buttons_fd);

return 0; /*主函数回来*/

END!!

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/qiche/274507.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部