MT7620 按鍵驅動 (一)


一:看電路圖,查找datasheet以及ProgrammingGuid確定引腳及待時用的寄存器

//實際引腳是:GE2_R XD1,  對應GPIO67

 

 

二:編碼

#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

 

 

#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;//鍵值傳給用戶空間
};

 

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);   
}

//設置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,
};

//中斷處理函數
/*關於中斷處理函數的返回值:中斷程序的返回值是一個特殊類型—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);

    //設置鍵值
   
   
    ev_press = 1; /*設置中斷標志為1*/
     wake_up_interruptible(&key_waitq); /*喚醒等待隊列*/

    printk("interrupt wake up...\n");
       
    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, 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");

 

 

三:通過寫測試代碼發現,按鍵一次按下,會產生兩次中斷,但是上升沿只檢測一次,故分析可能是按鍵抖動產生。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM