imx6ull pro 在下降沿触发时,按键按一次,会触发两次中断;同样是按键,按键1正常,按键2会多次触发

设备树

在根节点下的节点:
 key_add_input_dev{
        compatible = "100ask,my_key";
        pinctrl-names = "default";
        pinctrl-0 = <&my_key_add_input_dev1 &my_key_add_input_dev2>;

        key_add_input_dev1{
            label = "key_add_input_dev1";
            key-gpios = <&gpio5 1 GPIO_ACTIVE_LOW>;
            interrupt-parent = <&gpio5>;
            interrupts = <1 IRQ_TYPE_EDGE_FALLING>;
            linux,code = <KEY_1>;
        };

           key_add_input_dev2{
                      label = "key_add_input_dev2";
                      key-gpios = <&gpio4 14 GPIO_ACTIVE_LOW>;
                      interrupt-parent = <&gpio4>;
                      interrupts = <14 IRQ_TYPE_EDGE_FALLING>;
                      linux,code = <KEY_2>;
                  };
              };

pinctrl:
&iomuxc {
....
     my_key_add_input_dev2: my_key_add_input_dev2{
                fsl,pins = <
                    MX6UL_PAD_NAND_CE1_B__GPIO4_IO14 0xF080
                >;
            };
....
}

&iomuxc_snvs {
...
       my_key_add_input_dev1: my_key_add_input_dev1{
                  fsl,pins = <
                      MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01     0x000110A0  
                  >;
              };
....
}



       
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include<linux/delay.h>


struct mykey{
    unsigned int key;
    int irq;
    unsigned int code;
    unsigned int flag;//触发类型
    const char* name;
    int gpio;
    struct timer_list timer;
    irqreturn_t (*handler)(int , void *);
    struct gpio_desc* gpio_desc;
};

static struct mykey keys[2];
static struct input_dev * g_input_dev;

//timer function
void timer_function(unsigned long data)
{
    int value;
    struct mykey* key = (struct mykey*)data;

    value = gpio_get_value(key->gpio);  
    printk("%s code = %d value = %d\n",key->name, key->code, value);
    printk("\n");

    input_event(g_input_dev, EV_KEY, key->code, value);
	input_sync(g_input_dev);   
}

//irq handler
static irqreturn_t key_handler(int irq, void *dev_id)
{
    struct mykey* key = (struct mykey*)dev_id;

    key->timer.data = (unsigned long)dev_id;
    mod_timer(&key->timer,  jiffies + msecs_to_jiffies(20));//超时20ms

	return IRQ_HANDLED;
}

static int my_key_probe(struct platform_device * pdev)
{
    struct device_node *node = pdev->dev.of_node; 
    struct device_node * pp;
    int key_nums;
    int i = 0;
    int ret;
     
    printk("%s %s %d\n", __FILE__,__FUNCTION__, __LINE__);

   
   //获取子节点的个数
    key_nums = of_get_available_child_count(node);
    if (key_nums == 0)
    {
        printk("not keys!/n");
        return -1;
    }
 
    //找出节点中的所有子节点,并获取子节点信息
	for_each_available_child_of_node(node, pp) {
        //获取gpio
        keys[i].gpio = of_get_named_gpio(pp, "key-gpios",0);
        gpio_request(keys[i].gpio, keys[i].name);
        gpio_direction_input(keys[i].gpio);
        
        //获取按键的中断号
        //keys[i].irq = irq_of_parse_and_map(pp, 0);
        keys[i].gpio_desc = gpio_to_desc(keys[i].gpio);
        keys[i].irq = gpiod_to_irq(keys[i].gpio_desc);

        //获取按键的键码
        of_property_read_u32(pp, "linux,code", &keys[i].code);
       
        //获取触发类型
        keys[i].flag = irq_get_trigger_type(keys[i].irq);
    
        //获取label属性
        keys[i].name = devm_kzalloc(&pdev->dev, sizeof(char)*20, GFP_KERNEL);
        of_property_read_string(pp, "label", &keys[i].name);
  
        printk("gpio = %d\n", keys[i].gpio);
        printk("irq = %d\n", keys[i].irq);
        printk("code = %d\n", keys[i].code);
        printk("flag = %d\n", keys[i].flag);
        printk("name = %s\n", keys[i].name);
        printk("\n");

        keys[i].handler = key_handler;
        //ret = request_irq( keys[i].irq, keys[i].handler, keys[i].flag, keys[i].name, &keys[i]);

        *//设置为上升沿时,正常,第一个按键和第二个按键,按下都只有一次中断*
*        //ret = request_irq( keys[i].irq, keys[i].handler, IRQF_TRIGGER_RISING, keys[i].name, &keys[i]);*
*        //设置为下降沿时,第一个按键按下有一次中断;第二个按键按下,会有两次中断*
*        ret = request_irq( keys[i].irq, keys[i].handler, IRQF_TRIGGER_FALLING, keys[i].name, &keys[i]);*
        if(ret < 0)
        {
            printk("request irq failed!\n");
            return ret;
        }

        //初始化timer
        init_timer(&keys[i].timer);
        //设置timer
        keys[i].timer.function = timer_function;
        keys[i].timer.expires = jiffies + msecs_to_jiffies(20);

        i++;
    }

    //分配input_dev
    g_input_dev = devm_input_allocate_device(&pdev->dev);
	if (!g_input_dev) {
		dev_err(&pdev->dev, "failed to allocate input device\n");
		return -ENOMEM;
	}

    //设置input_dev
    g_input_dev->name = "input_dev_demo";
	g_input_dev->phys = "input_dev_demo";
	g_input_dev->dev.parent = &pdev->dev;
	g_input_dev->id.bustype = BUS_HOST;
    __set_bit(EV_KEY, g_input_dev->evbit);
	//__set_bit(EV_REP, g_input_dev->evbit);
    for(i = 0; i < key_nums; i++)
    {
        __set_bit(keys[i].code, g_input_dev->keybit);
    }
	
    //注册input_dev
    ret = input_register_device(g_input_dev);
 
    return 0;
}



static int my_key_remove(struct platform_device *pdev)
{
    struct device_node *node = pdev->dev.of_node; 
    int key_nums;
    int i = 0;

    key_nums = of_get_available_child_count(node);
    if (key_nums == 0)
    {
        printk("not keys!/n");
        return -1;
    }

    for(i = 0; i < key_nums; i++)
    {
        gpio_free( keys[i].gpio);
        free_irq( keys[i].irq, &keys[i]);
        del_timer(& keys[i].timer);
    }
    
    input_unregister_device(g_input_dev);
    input_free_device(g_input_dev);

    return 0;
}

static const struct of_device_id  my_key_of_match[] = {
    { .compatible = "100ask,my_key"},
    { },
};

static struct platform_driver my_gpio_key_driver = {
    .probe = my_key_probe,
    .remove = my_key_remove,
    .driver = {
        .name = "my_key",
        .of_match_table = of_match_ptr(my_key_of_match),
    }
};

static int __init gpio_keys_init(void)
{
    printk("%s %s %d\n", __FILE__,__FUNCTION__, __LINE__);
	return platform_driver_register(&my_gpio_key_driver);
}

static void __exit gpio_keys_exit(void)
{
    printk("%s %s %d\n", __FILE__,__FUNCTION__, __LINE__);
    
	platform_driver_unregister(&my_gpio_key_driver);
}

module_init(gpio_keys_init);
module_exit(gpio_keys_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("SSSS");
MODULE_DESCRIPTION("Keyboard driver for GPIOs");


修改 MX6UL_PAD_NAND_CE1_B__GPIO4_IO14 0xF080
为 MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x000110A0
即将按键的内部上拉改为下拉;


我个人推测是 C38 电容充放电导致的下降沿二次触发;

iomuxc 解释如下图:图片来源

另:如果使用 input 的话,要使能上升沿和下降沿一起触发;就是按键抬起事件也要上报;

将之前的

MX6UL_PAD_NAND_CE1_B__GPIO4_IO14 0xF080

改成

MX6UL_PAD_NAND_CE1_B__GPIO4_IO14  0x000110A0 

按键就触发一次中断了。

发现配置成这样也行

MX6UL_PAD_NAND_CE1_B__GPIO4_IO14 0x1F080

就是将HYS(bit16):设为1,使能迟滞比较器。

想请问下,关于引脚配置这块的知识,有没有比较详细的资料介绍,我这方面知识还弄的不是很清楚

1 Like

不知道下图是否能帮到你,我也不懂什么是迟滞比较器。
滞回器配置,仅输入时有效,施密特触发器,使能后可以过滤输入噪声?

好的,谢谢,大概是这个意思

我改成这样还是不对 :face_exhaling:

MX6UL_PAD_NAND_CE1_B__GPIO4_IO14 0x000110A0

设备树按键节点

mykeys {
            compatible = "my_keys";
            mykey1{
                pinctrl-names = "default";
                pinctrl-0 = <&key1_pinctrl>;
                status = "okay";
                key-gpios = <&gpio5 1 GPIO_ACTIVE_HIGH>;
                interrupt-parent = <&gpio5>;
                interrupts = <1 IRQ_TYPE_EDGE_FALLING>;
            };
 
            mykey2{
                pinctrl-names = "default";
                pinctrl-0 = <&key2_pinctrl>;
                status = "okay";
                key-gpios = <&gpio4 14 GPIO_ACTIVE_HIGH>;
                interrupt-parent = <&gpio4>;
                interrupts = <14 IRQ_TYPE_EDGE_FALLING>;
            };
 };

pinctrl部分

&iomuxc {
……
    key2_pinctrl:mykey2{
                fsl,pins = <
                    MX6UL_PAD_NAND_CE1_B__GPIO4_IO14           0x000110A0
                >;
            };
……
}
&iomuxc_snvs {
……
    key1_pinctrl:mykey1{
                fsl,pins = <
                MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01     0x000190A0
                >;
            };
……
}

probe函数

static int gpio_key_probe(struct platform_device *pdev)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	int i,err;
	struct device_node *node = pdev->dev.of_node;
	count = device_get_child_node_count(&pdev->dev);
	my_keys = kzalloc(sizeof(struct my_keys_gpiod) * count, GFP_KERNEL);
	for(i=0;i<count;i++)
	{
		child_fw = of_get_next_child(node, child_fw);
		if(child_fw)
		{
			my_keys[i].key_gpio = of_get_named_gpio(child_fw,"key-gpios",0);
			if(my_keys[i].key_gpio)
			{
				err = gpio_request(my_keys[i].key_gpio, "key");
				if (err < 0)
				{
					printk("gpio_request is error \n");
					return -1;
				}
			}
			else
			{
				printk("of_get_named_gpio is error \n");
				return -1;
			}
			gpio_direction_input(my_keys[i].key_gpio);
			my_keys[i].key_gpiod = gpio_to_desc(my_keys[i].key_gpio);
			err = request_irq(my_keys[i].irq, gpio_key_isr, IRQF_TRIGGER_FALLING, "my_gpio_key",&my_keys[i] );
			if(err != 0) {
				dev_err(&pdev->dev, "Faikey to request IRQ for key\n");
				return err;	
			}
		}
	}
	return 0;
}

按键1正常,按键2还是会两次触发。
大佬帮我看看啥问题,感谢感谢