gpio button driver

今天心情不错,听着歌,喝着铁观音,就把这两天做的事情写一下下。

 

平台:davinci dm6441linux2.6.18

 

我们在板子上焊了一个按键,是和gpio41连接的,当然需要驱动咯,于是我就做了一个,但是没有去抖动,因为我这个应用不需要去抖动,公司催得紧,所以尽快完成,功能是

按下按键4s后重置系统的ip

 

/* drivers/char/davinci_dm644x_button.c*/

 

#include <linux/device.h>

#include <linux/fs.h>

#include <linux/module.h>

#include <linux/errno.h>

#include <linux/kernel.h>

#include <linux/init.h>

#include <linux/platform_device.h>

#include <asm/arch/gpio.h>

#include <linux/types.h>

#include <linux/cdev.h>

 

#include <linux/interrupt.h>

#include <linux/irq.h>                     //define for irq number

#include <asm-arm/arch-davinci/irqs.h>

#include <asm/arch-davinci/gpio.h>

//#include <linux/interrupt.h>

 

#include <asm/uaccess.h>

#include <asm/io.h>

#include <asm/arch/hardware.h>

#include <asm/arch/gpio.h>

//#include <asm/arch/regs-gpio.h>

 

 

#define DEVICE_NAME "dm6441_button"   /*定义设备驱动的名字,或设备节点名称*/

#define BUTTON_MAJOR 198 /*使用 cat /proc/devices查看不要和存在的char节点重复*/

 

/*my app gpio define*/

#define ZX_GPIO41         41    /*GPIO41*/

 

 

/* 等待队列:

 * 当没有按键被按下时,如果有进程调用buttons_read函数,

 * 它将休眠

 */

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

 

/* 中断事件标志, 中断服务程序将它置1buttons_read将它清0 */

static volatile int ev_press = 0;

 

static irqreturn_t buttons_interrupt(int irq, void *dev_id)

{

       printk("key is pressed!/n");

       ev_press = 1;

       wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */

       return IRQ_RETVAL(IRQ_HANDLED);

}

 

static int buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)

{

       unsigned long err;

       int gpio41_value;

       ev_press = 0;        //put it before function wait_event_interruptible(button_waitq, ev_press)

       /* 如果ev_press等于0,休眠 */

       wait_event_interruptible(button_waitq, ev_press);

      

       /* 将按键状态复制给用户 */

      err = copy_to_user(buff, (void *)&ev_press, sizeof(ev_press));

       printk("this is at kernel, ev_press = %d/n", ev_press);

 

       return err ? -EFAULT : 0;

}

 

static int buttons_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)

{

       int gpio41_value1 = gpio_get_value(ZX_GPIO41);

       printk("this is kernel, gpio41_value1 = %d/n", gpio41_value1);

      

       return gpio41_value1;     

}

static int buttons_open(struct inode *inode, struct file *file)

{

       printk("open gpio,here is driver/n"); 

 

       int result;

       int irq_number;     

       int gpio41_value;   

 

       gpio_request(ZX_GPIO41, "buttons_interrupt");

       irq_number = gpio_to_irq(ZX_GPIO41);

       printk("irq_number = %d/n", irq_number);

 

       gpio_direction_input(ZX_GPIO41);

       set_irq_type((gpio_to_irq(ZX_GPIO41)), IRQ_TYPE_EDGE_FALLING);     //RISING   or  Edge Falling type

       enable_irq((gpio_to_irq(ZX_GPIO41)));

 

       result = request_irq((gpio_to_irq(ZX_GPIO41)), buttons_interrupt, 0, "button", "key");

       if (result < 0)

       {

              printk("Cannot initialize IRQ /n");

              return result;

       }

       printk("initialize IRQ successful, result = %d/n", result);

       gpio41_value = gpio_get_value(ZX_GPIO41);

 

       printk("gpio41_value = %d/n", gpio41_value);

       return 0;/*该函数可以什么都不做,也可以加入类似初始化的设置*/

}

 

static int buttons_close(struct inode *inode,struct file *filp)

{

       free_irq(gpio_to_irq(ZX_GPIO41), "key");   //it will emit error if do not add "key"

       return 0;

}

 

 

/*定义驱动设备文件API,在linux系统当中,任何设备都可以当做文件的方式操作,这一点和单片机和MCU有很大差别*/

static const struct file_operations davinci_dm644x_gpio_fileops = {

       .owner   = THIS_MODULE,

       .open    = buttons_open,

       .release = buttons_close,

       .read    = buttons_read,

       .ioctl   = buttons_ioctl, 

};

 

static int __init davinci_dm644x_button_open(void) /*内核初始化会调用该函数*/

{

       int ret;       

 

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

       if(ret < 0)

       {

              printk(DEVICE_NAME " register falid!/n");

              return ret;

       }

 

       printk (DEVICE_NAME" initialized/n");

 

       return ret;

}

 

static void __exit davinci_dm644x_button_exit(void)

{

       unregister_chrdev(BUTTON_MAJOR, DEVICE_NAME);

}

 

module_init(davinci_dm644x_button_open);

module_exit(davinci_dm644x_button_exit);

 

MODULE_AUTHOR("zhongxian <fenglailin>");

MODULE_DESCRIPTION("Davinci DM644x gpio driver");

MODULE_LICENSE("GPL");

 

一、             等待队列:

这个驱动中最值得一提的是wait_queue,读书时都是只闻其身见其面而已,重来都没好好地抚摸一下它,记得以前在大学写网络程序实验时说read()默认是阻塞的,我在做这个驱动时也想当然的这样认为咯(请不要见外,鄙人刚刚进入这个行业,还是可以原谅哈)。后来想起曾几何时听到过等待队列这个词语,于是查了一下资料,原来在linux驱动程序中,可以用等待队列来实现阻塞进程的唤醒。

所谓阻塞,就是当应用程序进行read(),write()等系统调用时,若设备的资源不能或得,而用户又希望以阻塞的方式访问设备,驱动程序应在设备的xxx_read(), xxx_write()等操作中将进程阻塞之到资源可以获取。

好了,现在回到等待队列的问题上来,我这个驱动程序中的实现是(红色部分)当button 没有被按下时,中断标志ev_press=0wait_event_interruptible(button_waitq, ev_press)起作用,

应用程序调用read()时会阻塞,但当button被按下后,中断标志ev_press=1

wait_event_interruptible(button_waitq, ev_press)就不起作用咯,因为这个函数就是这样规定的,当第二个参数必须满足时,才管用,我这里把它设置成0,所以当为1时就不管用啦,wake_up_interruptible(&button_waitq)函数唤醒了这个等待队列。此时应用程序调用read就不会休眠啦。关于等待队列的操作网上和一些驱动书上都讲得比较多,我这里只是说了一个简单的操作方式。比如http://zhuwenlong.blog.51cto.com/209020/40021这里。

一般步骤如下:

1、  定义等待队列头,这通常是全局的,wait_queue_head_t my_queue,

2、  初始化wait_queue_headinit_waitqueue_head(&my_queue),也可以把这两歩合成一步用DECLARE_WAIT_QUEUE_HEAD (name)“定义并初始化等待队列头”。像我这里就是这样做的。

3、  定义等待队列

DECLEAR_WAITQUEUE(name, tsk)

4、  添加/移除等待队列 这两歩通常在你的xxx_read(),XXX_write()中;由我这里看出这两歩不是必要的,我想可能是我只用一个等待队列吧。

5、  等待事件,这些函数就不写啦,因为想出去走走啦。网上都有介绍的。

只说我这里用到的一个wait_event_interruptible(button_waitq, ev_press)只有当第二个参数条件满足时才会管用即阻塞。

二、             copy_to_user(buff, (void *)&ev_press, sizeof(ev_press))

这个函数一看就知道是干什么的,我以前没有用过,在刚刚接触到它时参数还是没有搞对,呵呵,其实现在很多地方都是第一次实践,以前只是看书学习,不过我相信慢慢会积累的。我只说一点,就是第二个参数的类型,以后就这样用咯。

三、result = request_irq((gpio_to_irq(ZX_GPIO41)), buttons_interrupt, 0, "button", "key");

button 是注册中断的名字。用cat /proc/interrupt 可以看到它。

key”在后面的free_irq(gpio_to_irq(ZX_GPIO41), "key")中要用到。

buttons_interrupt当然就是中断处理程序咯

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值