4412 按键和中断

一、轮询方式获取按键

1.原理分析:

  • 按键会影响电平的输入,通过GPIO的输入电平来检测按键的变化
  • 按下0,前面实验“GPIO读”用到过
  • 应用中是read

 

2.硬件准备

  • 使用两个按键:Home和Back
  • UART_RING和SIM_DET→
  • GPX1_1和GPX1_2→
  • EXYNOS4_GPX1(1),EXYNOS4_GPX1(2)

3.软件准备

  • Device Drivers --->
  • Input device support --->
  • Keyboards ---> GPIO Buttons
  • 平台文件中注册设备pollkey
  • 在leds结构体后面添加:
    struct platform_device s3c_device_pollkey_ctl = {
        .name = "pollkey",
        .id      = -1,
    };

    &s3c_device_pollkey_ctl,
  • 重新编译烧写

4.驱动和应用

  • 驱动在led基础上修改
    • 设置keyIO→对应为输入模式
    • 添加驱动中的read函数
  • 应用在gpio读的基础上修改
    • 按键按下的时候,应该是0。检测按键就是判断buffer有没有0

 使用查询的方式,占用CPU达到50%,效率非常低
    后面介绍中断的方式来获取按键值

read_button.c:

#include <linux/init.h>
#include <linux/module.h>

/*驱动注册的头文件,包含驱动的结构体和注册和卸载的函数*/
#include <linux/platform_device.h>
/*注册杂项设备头文件*/
#include <linux/miscdevice.h>
/*注册设备节点的文件结构体*/
#include <linux/fs.h>

/*Linux中申请GPIO的头文件*/
#include <linux/gpio.h>
/*三星平台的GPIO配置函数头文件*/
/*三星平台EXYNOS系列平台,GPIO配置参数宏定义头文件*/
#include <plat/gpio-cfg.h>
#include <mach/gpio.h>
/*三星平台4412平台,GPIO宏定义头文件*/
#include <mach/gpio-exynos4.h>

#include <linux/string.h>
/* copy to user */
#include <asm/uaccess.h>

#define DRIVER_NAME "pollkey"
#define DEVICE_NAME "pollkey"


static int key_gpios[] = {
        EXYNOS4_GPX1(1),
        EXYNOS4_GPX1(2),
};

static int poll_key_release(struct inode *inode, struct file *file)
{
        printk(KERN_EMERG "poll_key release
");
        return 0;
}

static int poll_key_open(struct inode *inode, struct file *file)
{
        printk(KERN_EMERG "poll_key open
");
        return 0;
}

//read
static ssize_t poll_key_read(struct file *filp, char __user *buff, size_t size, loff_t *ppos)
{
        unsigned char key_value[2];

        if(size != sizeof(key_value)) {
                return -1;
        }

        key_value[0] =  gpio_get_value(key_gpios[0]);
        key_value[1] =  gpio_get_value(key_gpios[1]);

        copy_to_user(buff, key_value, sizeof(key_value));

        return 0;
}

static struct file_operations poll_key_ops = {
        .owner = THIS_MODULE,
        .open = poll_key_open,
        .release = poll_key_release,
        .read = poll_key_read,
};

static  struct miscdevice poll_key_dev = {
        .minor = MISC_DYNAMIC_MINOR,
        .name = DEVICE_NAME,
        .fops = &poll_key_ops,
};

static int poll_key_probe(struct platform_device *pdv)
{
        int ret, i;

        printk(KERN_EMERG "	initialized
");

        for(i=0;i<2;i++) {
                char *gpio_name = "Button";
                char string[5];
                snprintf(string, 5, "%d", i);
                strcat(gpio_name, string);
                ret = gpio_request(key_gpios[i], gpio_name);
                if(ret < 0) {
                        printk(KERN_EMERG "gpio_request EXYNOS4_GPX1(%d) failed!
",i+1);
                        return ret;
                } else {
                        s3c_gpio_cfgpin(key_gpios[i], S3C_GPIO_INPUT);
                        s3c_gpio_setpull(key_gpios[i], S3C_GPIO_PULL_NONE);
                }
        }

        misc_register(&poll_key_dev);

        return 0;
}

static int poll_key_remove(struct platform_device *pdv)
{
        printk(KERN_EMERG "	remove
");
        gpio_free(key_gpios[0]);
        gpio_free(key_gpios[1]);
        misc_deregister(&poll_key_dev);
        return 0;
}

static void poll_key_shutdown(struct platform_device *pdv)
{
        ;
}

static int poll_key_suspend(struct platform_device *pdv,pm_message_t pmt)
{
        return 0;
}

static int poll_key_resume(struct platform_device *pdv)
{
        return 0;
}

struct platform_driver poll_key_driver = {
        .probe = poll_key_probe,
        .remove = poll_key_remove,
        .shutdown = poll_key_shutdown,
        .suspend = poll_key_suspend,
        .resume = poll_key_resume,
        .driver = {
                .name = DRIVER_NAME,
                .owner = THIS_MODULE,
        }
};


static int __init poll_key_init(void)
{
        int DriverState;

        printk(KERN_EMERG "poll_key enter!
");
        DriverState = platform_driver_register(&poll_key_driver);

        printk(KERN_EMERG "	DriverState is %d
",DriverState);
        return 0;
}


static void __exit poll_key_exit(void)
{
        printk(KERN_EMERG "poll_key exit!
");

        platform_driver_unregister(&poll_key_driver);
}

module_init(poll_key_init);
module_exit(poll_key_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET");
read_button

app:

#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

#include <string.h>

int main(void)
{
        int fd;
        unsigned char buffer[2];
        char *read_key = "/dev/pollkey";

        if((fd = open(read_key, O_RDWR|O_NDELAY)) < 0) {
                printf("APP open %s failed
", read_key);
                return -1;
        }
        printf("APP open %s success!
", read_key);

        while(1) {
                if(read(fd, buffer, sizeof(buffer)) < 0) {
                        printf("read %s failed", read_key);
                }
                if(!buffer[0] || !buffer[1]) {
                        printf("key home is %d, key back is %d
", buffer[0], buffer[1]);
                }
        }

        close(fd);
}
app_pollkey.c

二、中断方式获取按键

  • 按键按下(产生了中断)→跳转到异常向量入口,执行中断函数
  • 中断函数要做什么
  1. 保护现场
  2. 执行中断处理函数
  3. 恢复现场
  • 我们需要做什么?
    • 学会使用中断注册函数,了解注册中断相关的函数和结构体

注册中断函数:request_irq的5个参数

request_irq(unsigned irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
参数1:irq,中断号。(和平台架构相关,结合datasheet以及平台文件)
参数2:中断处理函数
参数3:中断标记。上升/下降沿,高/低电平。。。
参数4:中断名字    cat /proc/interrupts
参数5:使用和设备的设备结构体或者NULL。 free_irq

free_irq(irq, *dev_id)

  requset_irq的调用

4412上外部中断号如何对应:(IRQ_EINT(x)和datasheet对应)

  • HOME和BACK
  • EXYNOS4_GPX1[1]和EXYNOS4_GPX1[2]
    • →KP_COL[1]和KP_COL[2]
    • →XEINT_9和XEINT_10
  • IRQ_EINT(9)和IRQ_EINT(10)

准备工作:

    在平台文件中注册设备:keyirq
    添加:
    struct platform_device s3c_device_keyirq_ctl = {
        .name   = "keyirq",
        .id     = -1,
    };
    &s3c_device_keyirq_ctl,
    
    重新编译烧写内核

 驱动代码:

#include <linux/init.h>
#include <linux/module.h>

/*驱动注册的头文件,包含驱动的结构体和注册和卸载的函数*/
#include <linux/platform_device.h>
/*注册杂项设备头文件*/
#include <linux/miscdevice.h>
/*注册设备节点的文件结构体*/
#include <linux/fs.h>

/*Linux中申请GPIO的头文件*/
#include <linux/gpio.h>
/*三星平台的GPIO配置函数头文件*/
/*三星平台EXYNOS系列平台,GPIO配置参数宏定义头文件*/
#include <plat/gpio-cfg.h>
#include <mach/gpio.h>
/*三星平台4412平台,GPIO宏定义头文件*/
#include <mach/gpio-exynos4.h>

#include <linux/string.h>
/* copy to user */
#include <asm/uaccess.h>
/* irq  */
#include <linux/irq.h>
#include <linux/interrupt.h>

#define DRIVER_NAME "keyirq"
#define DEVICE_NAME "keyirq"

static int key_gpios[] = {
        EXYNOS4_GPX1(1),
        EXYNOS4_GPX1(2),
};

static irqreturn_t eint9_interrupt(int irq, void *dev_id)
{
        printk("receive a interrupt 9!");
        return IRQ_HANDLED;
}

static irqreturn_t eint10_interrupt(int irq, void *dev_id)
{
        printk("receive a interrupt 10!");
        return IRQ_HANDLED;
}

static int key_irq_probe(struct platform_device *pdv)
{
        printk(KERN_EMERG "	initialized
");
        //注册中断
        request_irq(IRQ_EINT(9), eint9_interrupt, IRQ_TYPE_EDGE_FALLING, "my_eint9", pdv);
        request_irq(IRQ_EINT(10), eint10_interrupt, IRQ_TYPE_EDGE_FALLING, "my_eint10", pdv);

        return 0;
}

static int key_irq_remove(struct platform_device *pdv)
{
        printk(KERN_EMERG "	remove
");

        free_irq(IRQ_EINT(9), pdv);
        free_irq(IRQ_EINT(10), pdv);
        return 0;
}

static void key_irq_shutdown(struct platform_device *pdv)
{
        ;
}

static int key_irq_suspend(struct platform_device *pdv,pm_message_t pmt)
{
        return 0;
}

static int key_irq_resume(struct platform_device *pdv)
{
        return 0;
}

struct platform_driver key_irq_driver = {
        .probe     = key_irq_probe,
        .remove    = key_irq_remove,
        .shutdown  = key_irq_shutdown,
        .suspend   = key_irq_suspend,
        .resume    = key_irq_resume,
        .driver    = {
                .name  = DRIVER_NAME,
                .owner = THIS_MODULE,
        }
};


static int __init key_irq_init(void)
{
        printk(KERN_EMERG "key_irq enter!
");
        platform_driver_register(&key_irq_driver);
        return 0;
}


static void __exit key_irq_exit(void)
{
        printk(KERN_EMERG "key_irq exit!
");
        platform_driver_unregister(&key_irq_driver);
}

module_init(key_irq_init);
module_exit(key_irq_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("TOPEET");
key_irq.c

测试结果:

[root@iTOP-4412]# insmod key_irq.ko                                                    
[   38.013956] key_irq enter!
[   38.015271]  initialized
[root@iTOP-4412]# [   48.024770] receive a interrupt 10!
[root@iTOP-4412]# receive a interrupt 10!receive a interrupt 9!receive a interrupt 9!receive a interrupt 9!receive a interrupt 10!receive a interrupt 9!receive a interrupt 10!receive a interrupt 9!receive a interrupt 9!receive a interrupt 9!receive a interrupt 9!receive a interrupt 9!receive a interrupt 9!receive a interrupt 9!receive a interrupt 10!receive a interrupt 10!receive a interrupt 10!receive a interrupt 10!receive a interrupt 10!receive a interrupt 10!receive a interrupt 10!receive a interrupt 10!
[   64.641192] failed to copy MFC F/W during init
[   65.540973] receive a interrupt 10!receive a interrupt 10!receive a interrupt 10!receive a interrupt 10!receive a interrupt 10!receive a interrupt 10!receive a interrupt 10!receive a interrupt 10!receive a interrupt 10!receive a interrupt 10!receive a interrupt 10!receive a interrupt 10!receive a interrupt 10!
[root@iTOP-4412]# 
[root@iTOP-4412]# lsmod
key_irq 1521 0 - Live 0xbf000000
[root@iTOP-4412]# rmmod key_irq
测试结果

cat查看proc下的interrupts中断注册

361:         11          0          0  exynos-eint  my_eint9
362:         25          0          0  exynos-eint  my_eint10

 

原文地址:https://www.cnblogs.com/ch122633/p/9512041.html