MT7620 按键驱动(二)

一:为了消抖,笔者学习了Linux驱动之定时器API(参考 LDD定时器API (p198))
 
定时器相关结构:
struct timer_list{
/*…省略一些字段…*/
struct list_head list; //双向链表头,用来将多个定时器连成一个双向链表
unsigned long expires;//定时有效时间
void (*function)(unsigned long);//定时器处理函数
unsigned long data;//此成员传给处理函数的参数
};
 
相关函数:
void  init_timer(struct timer_list *timer);//初始化定时器
void add_timer(struct timer_list  *timer);//设置定时器
void  del_timer(struct timer_list  *timer);//删除定时器
 
二代码:

/*
***************************************************************************
* Ralink Tech Inc.
* 4F, No. 2 Technology 5th Rd.
* Science-based Industrial Park
* Hsin-chu, Taiwan, R.O.C.
*
* (c) Copyright, Ralink Technology, Inc.
*
*  This program is free software; you can redistribute  it and/or modify it
*  under  the terms of  the GNU General  Public License as published by the
*  Free Software Foundation;  either version 2 of the  License, or (at your
*  option) any later version.
*
*  THIS  SOFTWARE  IS PROVIDED   ``AS  IS‘‘ AND   ANY  EXPRESS OR IMPLIED
*  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
*  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
*  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
*  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
*  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
*  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
*  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
*  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*  You should have received a copy of the  GNU General Public License along
*  with this program; if not, write  to the Free Software Foundation, Inc.,
*  675 Mass Ave, Cambridge, MA 02139, USA.
*
*
***************************************************************************
*/

#ifndef __RALINK_GPIO_H__
#define __RALINK_GPIO_H__

#include <linux/init.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/poll.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <asm/mach-ralink/surfboardint.h>
//The Kenel header file, include soc virtual address
#include <asm/mach-ralink/rt_mmap.h>

#define RALINK_SYSCTL_ADDR        RALINK_SYSCTL_BASE    // system control
#define RALINK_REG_GPIOMODE        (RALINK_SYSCTL_ADDR + 0x60) //GPIO MODE

#define RALINK_PRGIO_ADDR        RALINK_PIO_BASE // Programmable I/O

#define RALINK_REG_PIO7140INT    (RALINK_PRGIO_ADDR + 0x60)//中断地址
#define RALINK_REG_PIO7140EDGE   (RALINK_PRGIO_ADDR + 0x64)//边沿触发方式地址
#define RALINK_REG_PIO7140RENA  (RALINK_PRGIO_ADDR + 0x68)//上升沿触发掩码
#define RALINK_REG_PIO7140FENA    (RALINK_PRGIO_ADDR + 0x6C)

#define  RALINK_REG_PIO7140DATA  (RALINK_PRGIO_ADDR + 0x70)    //数据地址

#define  RALINK_REG_PIO7140DIR     (RALINK_PRGIO_ADDR + 0x74)    //方向地址

#define  RALINK_IRQ_ADDR         RALINK_INTCL_BASE  
#define  RALINK_REG_INTENA        (RALINK_IRQ_ADDR   + 0x34)//enable 中断地址
#define  RALINK_REG_INTDIS        (RALINK_IRQ_ADDR   + 0x38)//disable 中断地址

#endif

//实际引脚是:GE2_R XD1,  对应GPIO67

#include "key_driver.h"
#define KEY_DRIVER_NAME "key_driver"

//保存当前中断方式
static u32 ralink_gpio7140_intp = 0;
//保存当前边沿触发方式
static u32 ralink_gpio7140_edge = 0;

//gpio状态
typedef enum
{
    e_gpio_rising = 0,
    e_gpio_falling,
    e_gpio_edge_unknow
}e_gpio_edge_t;

//gpio对应信息
struct gpio_status
{
    int gpio_num;
    e_gpio_edge_t edge;
    u32 key_value;//键值传给用户空间
    struct timer_list timer;//定时器(用于去抖)
};

static struct gpio_status g_gpio67 = {67, e_gpio_edge_unknow, 0};

//生成一个等待队列头wait_queue_head_t,名字为key_waitq
/* 等待队列:
* 当没有按键被按下时,如果有进程调用key_driver_read函数,
* 它将休眠
*/
static DECLARE_WAIT_QUEUE_HEAD(key_waitq);

/* 中断事件标志, 中断服务程序将它置1,key_driver_read将它清0*/
static volatile int ev_press = 0;

/**
* 设置gpio60-gpio71模式为gpio.
*/
static void set_7160_gpio_mode(void)
{
    u32 gpiomode;
   
    gpiomode = le32_to_cpu(*(volatile u32 *)(RALINK_REG_GPIOMODE));
    gpiomode |= (0x1<<10);
    *(volatile u32 *)(RALINK_REG_GPIOMODE) = cpu_to_le32(gpiomode);
}

/**
* 设置gpio40-gpio71的数据方向.(按键设置gpio67为输入)
*/
static void set_7140_dir(int gpio,int dir)
{
    u32 gpiomode;

gpiomode = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO7140DIR));
    gpiomode &= ~(0x01<<(gpio-40));
    gpiomode |= (dir?0x01:0x0)<<(gpio-40);
   
    *(volatile u32 *)(RALINK_REG_PIO7140DIR) = cpu_to_le32(gpiomode);   
}

//读取gpio67引脚的数据
/*static int read_gpio_data(int gpio)
{
    unsigned long tmp;
   
    tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO7140DATA));

return (tmp&(0x1<<(gpio-40))) ? 1 : 0 ;
}*/

//设置pio enable interrupt
static void enable_intp(void)
{   
    //在rt_mmap.h头文件中定义RALINK_INTCTL_PIO ,即第六位控制pio中断
    //#define RALINK_INTCTL_PIO       (1<<6)
    *(volatile u32 *)(RALINK_REG_INTENA) = cpu_to_le32(RALINK_INTCTL_PIO);
}

//设置pio disable interrupt
static void disable_intp(void)
{
    //在rt_mmap.h头文件中定义RALINK_INTCTL_PIO ,即第六位控制pio中断
    //#define RALINK_INTCTL_PIO       (1<<6)
    *(volatile u32 *)(RALINK_REG_INTDIS) = cpu_to_le32(RALINK_INTCTL_PIO);
}

//set Edge Interrupt
static void gpio_reg_irq(int irq)
{
    unsigned long tmp;
   
    tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO7140RENA));
    tmp |= (0x1 << (irq-40));
    *(volatile u32 *)(RALINK_REG_PIO7140RENA) = cpu_to_le32(tmp);
    /*tmp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO7140FENA));
    tmp |= (0x1 << (irq-40));
    *(volatile u32 *)(RALINK_REG_PIO7140FENA) = cpu_to_le32(tmp);*/
}

//先保存当前中断及触发寄存器的值,再清空
static void ralink_gpio7140_save_clear_intp(void)
{
    //保存当前中断寄存器数据
    ralink_gpio7140_intp = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO7140INT));
    //保存当前边沿触发方式
    ralink_gpio7140_edge = le32_to_cpu(*(volatile u32 *)(RALINK_REG_PIO7140EDGE));
    *(volatile u32 *)(RALINK_REG_PIO7140INT) = cpu_to_le32(0xFFFFFFFF);
    *(volatile u32 *)(RALINK_REG_PIO7140EDGE) = cpu_to_le32(0xFFFFFFFF);
}

//扫描时候是gpio67被按下
static int scan_gpio_num(void)
{
    if(!(ralink_gpio7140_intp & (1<<(g_gpio67.gpio_num - 40))))
    {
        printk("Have no key pressed...\n");
        return -1;
    }
    if (ralink_gpio7140_edge & (1<<(g_gpio67.gpio_num - 40))) {
        printk("set gpio value..\n");
            g_gpio67.edge = e_gpio_rising;
            g_gpio67.key_value = 1;
                //上升沿才有效,具体需要根据硬件设计。
    }
    else {
        printk("Have no edag...\n");
        return -1;
    }
    return 0;
}

//打开设备
static int key_driver_open(struct inode *inode, struct file *file)
{
    set_7160_gpio_mode();            //set RGMII2_GPIO_MODE to gpio mode.pro.p38
    set_7140_dir(0x01<<(67-40),0);    //set gpio60-gpio63 to gpin.
    enable_intp();        //set pio enable interrupt
   
    gpio_reg_irq(67);     //set Edge Interrupt
    return 0;
}

//关闭设备
static int key_driver_close(struct inode *inode, struct file *file)
{
    disable_intp();
    //禁止中断
    return 0;
}

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

if(!ev_press) {
        if(filp->f_flags & O_NONBLOCK)
            return -EAGAIN;
        else//由wake_up_interruptible等待中断
            wait_event_interruptible(key_waitq, ev_press);
    }
    ev_press = 0;
    err = copy_to_user(buff, (const void *)&g_gpio67.key_value, sizeof(g_gpio67.key_value));

g_gpio67.edge = e_gpio_edge_unknow;
    g_gpio67.key_value = 0x0;
   
    return err ? -EFAULT : sizeof(g_gpio67.key_value);
}

long key_driver_ioctl(struct file *filp, unsigned int req, unsigned long arg)
{
    return 0;
}

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

/*把调用poll 或者select 的进程挂入队列,以便被驱动程序唤醒*/
    /*需要注意的是这个函数是不会引起阻塞的*/
    poll_wait(file, &key_waitq, wait);
    if(ev_press)
        mask |= POLLIN | POLLRDNORM;
    return mask;
}

static struct file_operations key_fops =
{
    .owner        = THIS_MODULE,
    .open        = key_driver_open,
    .release    = key_driver_close,
    .read        = key_driver_read,
    .unlocked_ioctl = key_driver_ioctl,
    .poll        = key_driver_poll,
};

static struct miscdevice key_misc =
{
    .minor = MISC_DYNAMIC_MINOR,
    .name = KEY_DRIVER_NAME,
    .fops = &key_fops,
};

static void key_timer_handler(unsigned long data)
{
    ev_press = 1; /*设置中断标志为1*/
     wake_up_interruptible(&key_waitq); /*唤醒等待队列*/
    del_timer(&(g_gpio67.timer));
   
    printk("interrupt wake up...\n");
       
}

//中断处理函数
/*关于中断处理函数的返回值:中断程序的返回值是一个特殊类型—irqreturn_t。
中断程序的返回值却只有两个: IRQ_NONE和IRQ_HANDLED。*/
static irqreturn_t ralink_key_interrupt(int irq, void *irqaction)
{
    int ret;

printk("interrupt handler...\n");
   
    //先保存当前中断及触发寄存器的值,再清空
    ralink_gpio7140_save_clear_intp();

//查看是否是gpio67被按下
    ret = scan_gpio_num();

//printk("Int func,gpio num %d.jiffies %ld,HZ %d\n",num,jiffies,HZ);
    if(ret < 0)
        return IRQ_RETVAL(IRQ_NONE);

//设置定时器10ms
    init_timer(&(g_gpio67.timer));
    g_gpio67.timer.expires = jiffies + HZ/100;//消抖时间为10ms
    g_gpio67.timer.function = &key_timer_handler;
    add_timer(&(g_gpio67.timer));
   
   
    return IRQ_RETVAL(IRQ_HANDLED);
}

//初始化
static int __init key_driver_init(void)
{
    int ret;
    /*注册中断请求
     中断号:SURFBOARDINT_GPIO
     中断处理函数:ralink_key_interrupt
     中断属性(方式):上升沿触发
     使用此中断的设备:gpio_key
    */
    ret = request_irq(SURFBOARDINT_GPIO, ralink_key_interrupt,
                     /*IRQF_DISABLED |*/ IRQ_TYPE_EDGE_RISING, "gpio_key", NULL);
    if (ret)
        return ret;

ret = misc_register(&key_misc);//初始化设备
    printk("key_driver_init OK!\n");
    return ret;
}

//退出
static void __exit key_driver_exit(void)
{
    int ret;

free_irq(SURFBOARDINT_GPIO,NULL);//注销中断
   
    ret = misc_deregister(&key_misc);//注销设置
    if(ret < 0)
        printk("key_driver_exit error.\n");
    printk("key_driver_exit.\n");
}

module_init(key_driver_init);
module_exit(key_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("XYH");

MT7620 按键驱动(二)

时间: 2024-10-11 07:53:44

MT7620 按键驱动(二)的相关文章

入门级的按键驱动——按键驱动笔记之poll机制-异步通知-同步互斥阻塞-定时器防抖

文章对应视频的第12课,第5.6.7.8节. 在这之前还有查询方式的驱动编写,中断方式的驱动编写,这篇文章中暂时没有这些类容.但这篇文章是以这些为基础写的,前面的内容有空补上. 按键驱动——按下按键,打印键值: 目录 概要 poll机制 异步通知 同步互斥阻塞 定时器防抖 概要: 查询方式: 12-3 缺点:占用CPU99%的资源.中断方式:12-4 缺点:调用read函数后如果没有按键按下,该函数永远不会结束,一直在等待按键按下. 优点:使用到了休眠机制,占用cpu资源极少.poll机制: 1

Linux设备驱动之button按键驱动学习与小结

button按键驱动,相对于前面的LED驱动来说.增加了中断服务程序以及等待队列等新知识点. 先上学习的驱动代码. 内核:linux3.0 板子:fl2440 /********************************************************************************* * Copyright: (C) 2011 Guo Wenxue<[email protected]> * All rights reserved. * * Filenam

OSAL之按键驱动

本博文根据协议栈1.3.2,尊重原创,注明出处,欢迎转载 学习按键驱动的主要有两大块: 第一:按键引脚设置: 第二:按键事件的触发检测与轮询,以及按键消息的发送 先说明第一大块,按键引脚设置, 超出cc2540片子从机的按键两个按键,按键是共地.所以它的触发方式是下降沿触发按键中断,同时程序对按键按下这个动作检测是中断方式检测,一旦有按键按下,触发一个按键轮询函数执行,同时把对按键的检测方式换成轮询模式.所以对按键引脚设置的内容也就出来了.主要一下几个方面: 1.设置按键映射到的物理引脚GPIO

linux系统下标准GPIO按键驱动

前言: linux下platform平台驱动是为了方便隔离bsp与driver,利于移植.体现好代码的高内聚,低耦合.Linux设备驱动模型中,关心总线,设备和驱动三个实体.总线将设备和驱动绑定.在系统每注册一个设备的时候,都会寻找与之相匹配的驱动,相反的,每加载一个驱动的时候,也会寻找与之匹配的设备.匹配由总线完成.linux发明了一种虚拟的总线,称之为platform总线,相应的设备称之为platform_device,驱动为platform_driver. 基于这个模型,又根据面向对象的思

第三十四天:按键驱动与蜂鸣器驱动以及MMU

编写按键驱动的过程和编写LED灯的驱动类似.区别在于按键是在底板上的.要从底板的电路图开始看.   使用key为关键字查找电路图: 可以得知的信息:按键松开时候XEINT26为高电平.按下时候为底电平.所以要判断按键是否被按下,就要检测XEINT26的状态. 使用XEINT26为关键字,在核心板上查看其定义.找到下图: XEINT26在核心板上链接到GPX3_2上.然后就是去datasheet中查找GPX3_2的说明了. 按键为检测,GPX3CON置0,GPX3DAT置1,表示按键被按下.代码如

mt7620 wireless驱动特性意外发现

前言 今天又客户反映无线參数SSID编程了HT_AP0, 同事通过后台给他改动后反映给我,我想不正确啊,难道是无线驱动crash了?那应该不能玩才对啊... 追查线索 我们的路由器会定期汇报数据SSID,谁知道,server并没有保存到数据库中.这点我很很的生气,之前的规格,说得清清楚楚,为什么做事不到位!好了,既然发现了这个问题就改动下本地的server,这一改不要紧,把一台路由器的SSID改不见了. 刨根问底 路由器的SSID不见了,这个但是大问题,我们找到了那台路由器,发现其SSID编程了

ARM Linux 驱动Input子系统之按键驱动测试

上一篇已经谈过,在现内核的中引入设备树之后对于内核驱动的编写,主要集中在硬件接口的配置上了即xxxx.dts文件的编写. 在自己的开发板上移植按键驱动: 1.根据开发板的原理图 确定按键的硬件接口为:GPIO2_2.GPIO2_3.GPIO2_5.GPIO0_30. 修改dts文件使其与原理图的按键接口一致. gpio_buttons: [email protected]0 { compatible = "gpio-keys"; #address-cells = <1>;

检测键盘按键(二)——jquery写法

jquery写法 keyCode: IE.Chrome支持,在FF下,keyCode返回非字符按键的Unicode,如果是字符按键返回始终为0. which:   FF,Chrome支持:在IE下,which和charCode始终为undefined ,jquery方式下和keyCode值相同. charCode:   Chrome支持,在FF下,非字符键返回0,如果是字符按键返回Unicode <!DOCTYPE html> <html> <head> <tit

11.按键驱动之定时器防抖(详解)

本节目标: 通过定时器来防止按键抖动,测试程序是使用上节的:阻塞操作的测试程序 1.如下图所示,在没有定时器防抖情况下,按键没有稳定之前会多次进入中断,使得输出多个相同信息出来 2.按键波形图,如下所示: 3.如何消去按键抖动 通过定时器延时10ms,然后每当按键进入中断时就更新定时器延时10ms,若延时10ms到了说明已经过了抖动范围,然后再打印按键电平信息 4.定时器结构体和函数介绍 我们先来看看两个全局变量: jiffies: 是系统时钟,全局变量,默认每隔10ms加1 HZ:是每S的频率