硬件原理
从图中可以看到按键断开时,由于接了上拉电阻,所以CPU检测到默认是高电平的,当按键被按下时,电路导通,所以KEY0引脚变成低电平,即低电平有效。
那么按键是接到CPU哪个引脚呢?代码里需要操作该引脚。
通过在电路原理图中搜索KEY0,可以发现他是接到了UART1_CTS上,再搜索UART1_CTS,发现它接到了CPU的K15,做按键驱动我们需要将其复用为普通IO即可,即GPIO1_IO18,硬件电路分析完毕。
软件编写
这里采用kernel的dts,gpio和pinctrl子系统去完成对按键引脚的初始化和电平读取等。
修改imx6ull-14x14-evk.dts文件
代码语言:javascript复制#include <dt-bindings/input/input.h>
#include "imx6ull.dtsi"
/ {
`````省略`````
key {
#address-cells = <1>;
#size-cells = <1>;
compatible = "key";
pinctrl-name = "default";
pinctrl-0 = <&pinctrl_key>;
key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; // 低电平有效
status = "okay";
}
`````省略`````
};
`````省略`````
&iomuxc_snvs {
pinctrl-names = "default_snvs";
pinctrl-0 = <&pinctrl_hog_2>;
imx6ul-evk {
`````省略`````
pinctrl_key: keygrp {
fsl,pin = <
MX6UL_PAD_UART1_CTS_B__GPIO1_IO18 0xF080
>;
};
};
`````省略`````
};
`````省略`````
在根节点/下添加key节点,key节点调用pinctrl节点,设置GPIO1_IO18的寄存器地址。
编译dts
输入make dtbs,生成了一堆dtb文件,我用的是imx6ull-14x14-evk.dtb,将其拷贝到tftpboot目录下,我的板子使用tftpboot加载zImage和dtb文件。
如何判断刚刚添加的key设备节点是否有效?
进入/proc/device-tree, 可以查看到有key节点,说明添加成功。
编写按键驱动程序
key.c
代码语言:javascript复制#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <linux/gpio.h>
#include <linux/miscdevice.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include "key.h"
struct key_dev dev;
static int key_open(struct inode *nd, struct file *flip)
{
flip->private_data = &dev;
return 0;
}
static ssize_t key_read(struct file *flip, char __user *buf, size_t size, loff_t *offset)
{
int isPress;
int ret;
isPress = gpio_get_value(dev.gpio);
pr_info("[kernel] isPress=%dn", isPress);
ret = copy_to_user(buf, &isPress, sizeof(isPress));
return ret;
}
static struct file_operations key_fops= {
.owner = THIS_MODULE,
.open = key_open,
.read = key_read,
};
struct miscdevice key_miscdev = {
.minor = 143,
.name = "key",
.fops = &key_fops,
};
static int key_probe(struct platform_device * pdev)
{
int err;
dev.np = of_find_node_by_path("/key");
if (!dev.np) {
pr_err("can't find key in dtsn");
return -EINVAL;
}
dev.gpio = of_get_named_gpio(dev.np, "key-gpio", 0);
if (!gpio_is_valid(dev.gpio)) {
return -ENODEV;
}
err = misc_register(&key_miscdev);
if (err < 0) {
pr_err("KEY error: cannot register devicen");
return err;
}
return 0;
}
static int key_remove(struct platform_device *pdev)
{
// 注销misc设备驱动
(void)misc_deregister(&key_miscdev);
return 0;
}
static const struct of_device_id key_of_match_table[] = {
{ .compatible = "key" },
{},
};
static struct platform_driver key_drv = {
.probe = key_probe,
.remove = key_remove,
.driver = {
.owner = THIS_MODULE,
.name = "key",
.of_match_table = key_of_match_table,
}
};
static int __init key_init(void)
{
return platform_driver_register(&key_drv);
}
static void __exit key_exit(void)
{
platform_driver_unregister(&key_drv);
}
module_init(key_init);
module_exit(key_exit);
MODULE_LICENSE("GPL");
key.h
代码语言:javascript复制#ifndef __KEY_H
#define __KEY_H
struct key_dev {
dev_t dev_id;
struct cdev cdev;
struct class *class;
struct device *device;
struct device_node *np;
int gpio;
};
#endif
对misc进行简要说明,misc设备驱动是杂项设备驱动,它其实也是一种字符设备,可以理解成封装好了更多的步骤。正常我们注册一般的字符设备驱动需要以下步骤:
- alloc_chrdev_region // 注册字符设备驱动
- cdev_init
- cdev_add
- class_create //创建类
- device_create // 创建设备
而杂项设备只需一个函数搞定,杂项设备的minor也是可以用动态注册,自动分配。
应用层程序
key.c
代码语言:javascript复制#include "stdio.h"
#include "unistd.h"
#include "stdlib.h"
#include "sys/types.h"
#include "fcntl.h"
#include "sys/stat.h"
#include "key.h"
int main(int argc, char *argv[])
{
char *filename = NULL;
int fd = -1;
char isPress;
int ret;
filename = argv[1];
fd = open(filename, O_RDWR);
if (fd < 0) {
printf("open %s failn", filename);
return -1;
}
ret = read(fd, &isPress, sizeof(isPress));
if (ret < 0) {
printf("read key value failn");
close(fd);
return -1;
}
if (isPress == 0)
printf("key is press, isPress=%dn", isPress);
else
printf("key is loosen, isPress=%dn", isPress);
close(fd);
return 0;
}
key.h
代码语言:javascript复制#ifndef __KEY_H
#define __KEY_H
#endif
测试
测试OK!