buttons.c
/********************************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static int major;
static struct timer_list button_timer; // 定义定时器链表,作用是延时消抖
static struct pins_desc * pin_pd = NULL;
static DECLARE_WAIT_QUEUE_HEAD(button_waitq); // 定义并初始化等待队列
static volatile int ev_press = 0;
static struct class *bottons_class;
static unsigned int keynum;
struct pins_desc { // 定义一个引脚描述的结构
int irq; // 中断号
int pin; // 引脚
int keyval; // 定义键值
char *name; // 名字
};
static struct pins_desc pins_desc[6] = {
{IRQ_EINT8, S3C2410_GPG(0), 1, "S1"},
{IRQ_EINT11, S3C2410_GPG(3), 2, "S2"},
{IRQ_EINT13, S3C2410_GPG(5), 3, "S3"},
{IRQ_EINT14, S3C2410_GPG(6), 4, "S4"},
{IRQ_EINT15, S3C2410_GPG(7), 5, "S5"},
{IRQ_EINT19, S3C2410_GPG(11), 6, "S6"},
};
// 当按键按下时,产生中断,进入中断函数,在中断函数中设置定时器的超时时间。
static irqreturn_t bottons_handler(int irq, void *dev_id)
{
pin_pd = (struct pins_desc *)dev_id;
// 设置定时器的超时时间为10ms,用于按键的消抖
mod_timer(&button_timer, jiffies + HZ/100);
return IRQ_RETVAL(IRQ_HANDLED);
}
// 当定时器超时后,调用该函数
static void buttons_fun(unsigned long data)
{
struct pins_desc * desc =pin_pd;
int key;
// 当添加后定时器第一次超时后,调用该超时函数,但是此时,按键并未按下,并未产生中断,排除该情况
if(!pin_pd)
return;
// 获取引脚的值,判断是上升沿,还是下降沿
key = s3c2410_gpio_getpin(desc->pin);
if(key)
keynum = 0x80 | desc->keyval;
else
keynum = desc->keyval;
// 当有按键按下时,唤醒睡眠状态为TASK_INTERRUPTIBLE的进程,实现按下一次,想用户空间发一次数据。
wake_up_interruptible(&button_waitq);
ev_press = 1;
}
static int bottons_open(struct inode *inode, struct file *filp)
{
int i;
/* 注册中断 */
for(i = 0; i < 6; i++)
request_irq(pins_desc[i].irq, bottons_handler, IRQ_TYPE_EDGE_BOTH, pins_desc[i].name, &pins_desc[i]);
// 终端号 中断处理函数 中断触发方式 中断的名字 传入中断处理函数的参数(使用该参数可以区分共享中断的)
return 0;
}
// 用户空间使用read系统调用时,调用驱动中的该函数
static ssize_t bottons_read(struct file *filep, char __user *buf, size_t size, loff_t *opps)
{
// 当ev_press为真时,立刻返回,否则进程进入TASK_INTERRUPTIBLE的模式睡眠,并挂载指定的等待队列上
wait_event_interruptible(button_waitq, ev_press);
// 设置ev_press,使进程进入下一次阻塞
ev_press = 0;
// 将数据返回到用户空间
copy_to_user(buf, &keynum, 1);
return 1;
}
// 当用户空间使用poll或者select系统调用时,调用驱动的该函数
static unsigned int bottons_poll(struct file *file, struct poll_table_struct *wait)
{
int mask = 0;
// 将进程添加到等待队列上,
poll_wait( file, &button_waitq, wait);
// 如果有数据可读,返回掩码
if(ev_press)
mask |= POLLIN | POLLRDNORM;
return mask;
}
// 函数操作集结果体
static struct file_operations bottons_fops= {
.owner = THIS_MODULE,
.open = bottons_open,
.read = bottons_read,
.poll = bottons_poll,
};
static __init int bottons_init(void)
{
/*
* 注册字符设备驱动,register_chrdev函数的第一个参数为零时,表示由系统自动分配主设备号,
* 分配的主设备好通过函数的返回值返回给major变量,
* 当第一个参数不为零的是,表示指定主设备号
*/
major = register_chrdev(0, "bottons", &bottons_fops);
// 创建一个类
bottons_class = class_create(THIS_MODULE, "KEY");
// 在类下面创建一个设备,通过busybox的mdev机制在/dev目录下创建/dev/bottons的设备节点
device_create(bottons_class, NULL, MKDEV(major, 0), NULL, "bottons");
// 初始化定时器
init_timer(&button_timer);
// 设置定时器的超时函数
button_timer.function = buttons_fun;
// 向内核注册定时器
add_timer(&button_timer);
return 0;
}
static __exit void bottons_exit(void)
{
int i;
// 释放中断
for(i = 0; i < 6; i++)
free_irq(pins_desc[i].irq, &pins_desc[i]);
// 注销字符设备驱动
unregister_chrdev(MKDEV(major, 0),"bottons");
// 删除设备文件
device_destroy(bottons_class, MKDEV(major, 0));
// 删除类
class_destroy(bottons_class);
}
module_init(bottons_init);
module_exit(bottons_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("");
/********************************************************************************************/
buttonstest_poll.c
/********************************************************************************************/
#include
#include
#include
#include
#include
int main(int argc,char **argv)
{
int fd;
int res = 0;
unsigned int keynum = 0;
struct pollfd fds[1];
fd = open("/dev/bottons",O_RDWR);
if(fd<0)
{
printf("cannot open !\n");
return -1;
}
fds[0].fd = fd;
fds[0].events = POLLIN; // 有数据可读
while(1)
{
// int poll(struct pollfd *fds, nfds_t nfds, int timeout);
res = poll(fds, 1, 5000);
if( 0 == res)
printf("time out!\n");
else
{
read(fd, &keynum, 1);
printf("keyval = 0x%x\n", keynum);
}
}
close(fd);
return 0;
}
/********************************************************************************************/
buttons_select.c
/********************************************************************************************/
#include
#include
#include
#include
#include
int main(int argc,char **argv)
{
int fd;
int res = 0;
unsigned int keynum = 0;
fd_set set;
fd = open("/dev/bottons",O_RDWR);
if(fd<0)
{
printf("cannot open !\n");
return -1;
}
static struct timeval time = {
.tv_sec = 5,
.tv_usec = 0,
};
while(1)
{
/*
* int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
参数:
nfds: 文件描述符的范围,比检测的最大文件描述符大1
readfds: 被读监控的文件描述符集
writefds:被写监控的文件描述符集
exceptfds:被异常监控的文件描述符集
timeout:取不同的值,该调用有不同的表示
timeout值为0,不管是否有文件满足要求,都立刻返回,无文件满足要求返回0,有文件满足要求返回一个正值
timeout值为NULL,select将阻塞进程,直到文件满足要求
timeout值为正整数,就是等待的最长时间,既select在timeout时间内阻塞进程
返回值:
Select调用返回时,返回值有如下情况:
1.正常情况下返回满足要求的文件描述符个数;
2.经过了timeout等待后仍无文件满足要求,返回值为0;
3.如果select被某个信号中断,它将返回-1并设置errno为EINTR。
4.如果出错,返回-1并设置相应的errno
使用方法:
1.将要监控的文件添加到文件描述符集
2.调用Select开始监控
3.判断文件是否发生变化
系统提供了4个宏对描述符集进行操作:
#include
void FD_SET(int fd, fd_set *fdset)
void FD_CLR(int fd, fd_set *fdset)
void FD_ZERO(fd_set *fdset)
void FD_ISSET(int fd, fd_set *fdset)
宏FD_SET将文件描述符fd添加到文件描述符集fdset中;
宏FD_CLR从文件描述符集fdset中清除文件描述符fd;
宏FD_ZERO清空文件描述符集fdset;
在调用select后使用FD_ISSET来检测文件描述符集fdset中的文件
fd发生了变化。
例子:
FD_ZERO(&fds); //清空集合
FD_SET(fd1,&fds); //设置描述符
FD_SET(fd2,&fds); //设置描述符
maxfdp=fd1+1;//描述符最大值加1,假设fd1>fd2
switch(select(maxfdp,&fds,NULL,NULL,&timeout))
case -1: exit(-1);break; //select错误,退出程序
case 0:break;
default:
if(FD_ISSET(fd1,&fds)) //测试fd1是否可读
*/
FD_SET(fd, &set);
res = select(fd+1, &set, NULL, NULL, &time);
if( 0 == res)
printf("time out!\n");
else
{
read(fd, &keynum, 1);
printf("keyval = 0x%x\n", keynum);
}
time.tv_sec = 5; //必须从新设置超时时间
}
close(fd);
return 0;
}
/********************************************************************************************/
阅读(2000) | 评论(0) | 转发(0) |