Tiny6410 + Linux2.6.38 按键驱动改写和说明

60 篇文章 0 订阅
44 篇文章 1 订阅

四个按键分别接在GPN11, GPL8, GPL9, GPL10,本驱动对应的测试程序test.c可以分别检测这四个按钮的按下和释放状态。

本驱动改写自友善官方驱动,仅供学习参考之用。

驱动程序源码:

/**********************************************
基于Tiny6410 + linux-2.6.38 的按键中断驱动程序。
**********************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio-bank-n.h>
#include <mach/gpio-bank-l.h>


#define DEVICE_NAME     "buttons"    

struct button_irq_desc {
    int irq;
    int number;
    char *name;	
};

static struct button_irq_desc button_irqs [] = {
    {IRQ_EINT(11), 0, "KEY0"},   //IRQ_EINT(11) 对应的管脚是 GPN11
    {IRQ_EINT(16), 1, "KEY1"},   //IRQ_EINT(16) 对应的管脚是 GPL8
    {IRQ_EINT(17), 2, "KEY2"},   //IRQ_EINT(17) 对应的管脚是 GPL9
    {IRQ_EINT(18), 3, "KEY3"}	  //IRQ_EINT(18) 对应的管脚是 GPL10
};

static volatile char key_values [] = {'r', 'r', 'r', 'r'};   //初始状态都是released状态'r',当有按键按下时变为pressed状态'p'。


/*****************************************************
DECLARE_WAIT_QUEUE_HEAD(name)是一个与等待队列有关的宏
展开为:
wait_queue_head_t name={
	.lock=__SPIN_LOCK_UNILOCKED(name.lock),
	.task_list={&(name), task_list, &(name).task_list}
}
其中wait_queue_head_t的定义为:
typedef struct __wait_queue_head wait_queue_head_t;
struct __wait_queue_head{
	spinlock_t lock;
	struct list_head task_list;
};
仅供参考!
******************************************************/
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);  

static volatile int action_flag = 0;     //事件监测,用于在read操作中判断是否有按键按下

static irqreturn_t buttons_interrupt(int irq, void *dev_id)   //中断处理函数,结构是固定的。
{
	struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
	int down;
	int number;
	unsigned tmp;
	unsigned excp=0;

	number = button_irqs->number;
	switch(number) {
	case 0:
		tmp = readl(S3C64XX_GPNDAT);
		down = (tmp & (1 << 11));    //GPN11
		break;
	case 1:
		tmp = readl(S3C64XX_GPLDAT);
		down = (tmp & (1 << 8));    //GPL8
		break;
	case 2:
		tmp = readl(S3C64XX_GPLDAT);
		down = (tmp & (1 << 9));    //GPL9
		break;
	case 3:
		tmp = readl(S3C64XX_GPLDAT);
		down = (tmp & (1 << 10));    //GPL10
		break;
	default:
		excp=1;
		down = 0;
	}
	
	if(excp==0){   //此处主要是为了防止其他不可预测的误操作。
		if(down==0){
			key_values[number]='p';
		}else{
			key_values[number]='r';
		}
        	action_flag = 1;
        	wake_up_interruptible(&button_waitq);
	}
    return IRQ_RETVAL(IRQ_HANDLED);
}


static int s3c64xx_buttons_open(struct inode *inode, struct file *file)
{
    int i;
    int err = 0;
    
    for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
	if (button_irqs[i].irq < 0) {
		continue;
	}

/***********************************************************************************************************************************
Linux中中断的申请
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev);
其中:
irq表示要申请的中断号;
handler表示中断服务程序入口地址;
flags表示中断处理的属性,控制上升沿触发、下降沿触发什么的;
name是设备描述,表示那一个设备在使用这个中断;
dev一般是指向设备数据结构的指针,在调用中断处理程序的时候它就会传递给中断处理程序的void *dev,如果中断没有被共享,dev可以设置为NULL。
*************************************************************************************************************************************/
        err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH, 
                          button_irqs[i].name, (void *)&button_irqs[i]);
        if (err)
            break;
    }

    if (err) {
        i--;
        for (; i >= 0; i--) {
	    if (button_irqs[i].irq < 0) {
		continue;
	    }
	    disable_irq(button_irqs[i].irq);
            free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
        }
        return -EBUSY;
    }

    for(i=0; i<sizeof(key_values)/sizeof(key_values[0]); i++)
    {
	key_values[i]='r';
    }

    action_flag = 0;
    
    return 0;
}


static int s3c64xx_buttons_close(struct inode *inode, struct file *file)
{
    int i;
    
    for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
	if (button_irqs[i].irq < 0) {
	    continue;
	}

/*****************************************
释放中断:
void free_irq(unsigned int irq, void *dev_id);
******************************************/
	free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
    }

    return 0;
}


static int s3c64xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
    unsigned long err;

    if (!action_flag) {
	if (filp->f_flags & O_NONBLOCK)   //判断设备文件是否以非阻塞模式打开,若是,则马上返回
	    return -EAGAIN;
	else
	    wait_event_interruptible(button_waitq, action_flag);   //等待中断发生。
    }
    
    action_flag = 0;

    err = copy_to_user((void *)buff, (const void *)(&key_values), min(sizeof(key_values), count));

    return err ? -EFAULT : min(sizeof(key_values), count);
}

static unsigned int s3c64xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
{
    unsigned int mask = 0;
    poll_wait(file, &button_waitq, wait);
    if (action_flag)
        mask |= POLLIN | POLLRDNORM;
    return mask;
}


static struct file_operations dev_fops = {
    .owner   =   THIS_MODULE,
    .open    =   s3c64xx_buttons_open,
    .release =   s3c64xx_buttons_close, 
    .read    =   s3c64xx_buttons_read,
    .poll    =   s3c64xx_buttons_poll,
};

static struct miscdevice misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &dev_fops,
};

static int __init dev_init(void)
{
	int ret;

/******************************************************************************
(misc device)杂项设备也是在嵌入式系统中用得比较多的一种设备驱动。在Linux内核的
include/linux目录下有Miscdevice.h文件,要把自己定义的misc device从设备定义在这里。
其实是因为这些字符设备不符合预先确定的字符设备的范畴,所有这些设备采用主编号10,
一起归于misc device, 其实misc_register就是主编号10调用register_chrdev()的。
也就是说,misc设备其实也就是特殊的字符设备,可自动生成设备节点。
*******************************************************************************/
	ret = misc_register(&misc);      //系统会自动在/dev/下生成buttons设备文件

	printk (DEVICE_NAME"\tinitialized\n");

	return ret;
}

static void __exit dev_exit(void)
{
	misc_deregister(&misc);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FlyCloud Inc.");


驱动程序Makefile文件:

obj-m :=buttons.o

KERNELDIR :=/Tiny6410/kernel/linux-2.6.38/
PWD :=$(shell pwd)

all:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

clean:
	rm -rf *.o *.ko *.mod.* *.symvers
.PHONY:	all clean


测试程序源码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

#define DEVICE_NAME "/dev/buttons"

int main(int argc, char* argv[])
{
	int fd;
	fd = open(DEVICE_NAME, O_RDWR);
	if(fd < 0)
	{
		printf("Open %s failed!\n", DEVICE_NAME);
		exit(-1);
	}

	unsigned char key_values[4];
	unsigned char history_values[4];
	int i=0;
	for(i=0;i<4;i++){
		history_values[i]='r';
	}
	
	for( ; ; )
	{
		int i=read(fd, key_values, sizeof(key_values));
		if(i!=-1){
			for(i=0;i<4;i++){
				if(history_values[i]=='r' && key_values[i]=='p'){
					printf("按键 %d 被按下!\n", i);
				}
				if(history_values[i]=='p' && key_values[i]=='r'){
					printf("按键 %d 被释放!\n", i);
				}
				history_values[i]=key_values[i];
			}
		}
	}
	
	close(fd);
	return 0;
}


测试效果截图:

(------------仅供参考,仅供参考-----------)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值