矩阵键盘工作原理参考: http://blog.csdn.net/jklinux/article/details/73649292
实现一个2x2的矩阵键盘驱动, 在设备树里的描述:
mykeypad {
compatible = "mykeypad";
/* 行线的io口, 以数组的形式列出 */
row-gpios = <&pio 0 12 GPIO_ACTIVE_HIGH>, <&pio 0 11 GPIO_ACTIVE_HIGH>;
/* 列线的io口, 以数组的形式列出 */
col-gpios = <&pio 0 19 GPIO_ACTIVE_HIGH>, <&pio 0 18 GPIO_ACTIVE_HIGH>;
/* 按键顺序对应的键码 */
keycodes = <KEY_L, KEY_S, KEY_ENTER, KEY_UP>;
};
驱动源码:
/* mydrv.c */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/input.h>
typedef struct {
struct gpio_descs *rows; //存放行线io口信息
struct gpio_descs *cols; //存放列线io口信息
struct input_dev *idev;
int *keycodes; //存放设备树提供的键码
int key_pressed; //记录第几个按钮已按下,用于在松手中断时汇报键松手
}mypdata;
irqreturn_t irq_func(int irqno, void *arg)
{
mypdata *pdata = (mypdata *)arg;
int r = -1, i, c = -1;
//找出哪一行发生中断
for (i = 0; i < pdata->rows->ndescs; i++)
{
if (irqno == gpiod_to_irq(pdata->rows->desc[i]))
{
r = i;
break;
}
}
if (r < 0)
return IRQ_HANDLED;
//先把行线的中断暂时关闭
disable_irq_nosync(irqno);
if (gpiod_get_value(pdata->rows->desc[r])) //松手
{
//如果有记录键已按下,则汇报键松手
if (pdata->key_pressed >= 0)
{
input_report_key(pdata->idev, pdata->keycodes[pdata->key_pressed], 0);
input_sync(pdata->idev);
pdata->key_pressed = -1;
}
goto out;
}
else
{
//轮流让每一个列线输出高电平
for (i = 0; i < pdata->cols->ndescs; i++)
{
gpiod_set_value(pdata->cols->desc[i], 1);
if (gpiod_get_value(pdata->rows->desc[r]))
{
gpiod_set_value(pdata->cols->desc[i], 0);
c = i;
break;
}
gpiod_set_value(pdata->cols->desc[i], 0);
}
if (c < 0)
goto out;
//汇报键按下
pdata->key_pressed = r*pdata->cols->ndescs+c;
input_report_key(pdata->idev, pdata->keycodes[pdata->key_pressed], 1);
input_sync(pdata->idev);
}
out:
msleep(100); //防止抖动
enable_irq(irqno); //恢复行线的中断功能
return IRQ_HANDLED;
}
int myprobe(struct platform_device *pdev)
{
mypdata *pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
int ret = -ENODEV;
int i;
//获取列线col-gpios的io口信息 */
pdata->cols = devm_gpiod_get_array(&pdev->dev, "col", GPIOD_OUT_LOW);
if (IS_ERR(pdata->cols))
{
printk("rows gpio failed\n");
goto err0;
}
//获取行线row-gpios的io口信息 */
pdata->rows = devm_gpiod_get_array(&pdev->dev, "row", GPIOD_IN);
if (IS_ERR(pdata->rows))
{
printk("cols gpio failed\n");
goto err1;
}
//准备存放键码的空间
pdata->keycodes = devm_kzalloc(&pdev->dev, sizeof(int)*pdata->rows->ndescs*pdata->cols->ndescs, GFP_KERNEL);
//获取设备树里的键码信息
device_property_read_u32_array(&pdev->dev, "keycodes", pdata->keycodes, pdata->rows->ndescs*pdata->cols->ndescs);
pdata->key_pressed = -1;
input dev
pdata->idev = input_allocate_device();
pdata->idev->name = pdev->name;
set_bit(EV_KEY, pdata->idev->evbit);
set_bit(EV_REP, pdata->idev->evbit);
for (i = 0; i < pdata->rows->ndescs * pdata->cols->ndescs; i++)
set_bit(pdata->keycodes[i], pdata->idev->keybit);
ret = input_register_device(pdata->idev);
if (ret < 0)
goto err2;
//请求中断
for (i = 0; i < pdata->rows->ndescs; i++)
{
ret = devm_request_threaded_irq(&pdev->dev, gpiod_to_irq(pdata->rows->desc[i]),
NULL, irq_func, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING|IRQF_ONESHOT, pdev->name, pdata);
if (ret < 0)
goto err3;
}
platform_set_drvdata(pdev, pdata);
printk("in myprobe\n");
return 0;
err3:
while (i)
devm_free_irq(&pdev->dev, gpiod_to_irq(pdata->rows->desc[--i]), pdata);
err2:
input_unregister_device(pdata->idev);
devm_kfree(&pdev->dev, pdata->keycodes);
devm_gpiod_put_array(&pdev->dev, pdata->rows);
err1:
devm_gpiod_put_array(&pdev->dev, pdata->cols);
err0:
return ret;
}
int myremove(struct platform_device *pdev)
{
mypdata *pdata = platform_get_drvdata(pdev);
int i;
input_unregister_device(pdata->idev);
devm_kfree(&pdev->dev, pdata->keycodes);
for (i = 0; i < pdata->rows->ndescs; i++)
devm_free_irq(&pdev->dev, gpiod_to_irq(pdata->rows->desc[i]), pdata);
devm_gpiod_put_array(&pdev->dev, pdata->rows);
devm_gpiod_put_array(&pdev->dev, pdata->cols);
printk("in myremove\n");
return 0;
}
struct of_device_id ids[] = {
{.compatible = "mykeypad"},
{},
};
struct platform_driver mydrv = {
.probe = myprobe,
.remove = myremove,
.driver = {
.owner = THIS_MODULE,
.name = "mydrv" ,
.of_match_table = ids,
},
};
module_platform_driver(mydrv);
MODULE_LICENSE("GPL");