前言
本文记录的是按键按键输入实验
一、硬件原理图
按键实验实际上是操作GPIO,读取IO电平。
默认情况下 GPIO3_C5 是低电平,所以我们通过使用杜邦线将图 13.2.1 中 GPIO3_C5 这个引脚接到 VCC 上的方式来模拟按键按下。也就是模拟按键按下,GPIO3_C5 为高电平,松开按键为低电平。
二、修改设备树文件
rk3568-pinctrl.dtsi
key-gpios{
key_gpio: key-pin {
rockchip,pins =
<3 RK_PC5 RK_FUNC_GPIO &pcfg_pull_none>;
};
};
rk3568-atk-evb1-ddr4-v10.dtsi
key {
compatible = "alientek,key";
pinctrl-names = "alientek,key";
pinctrl-0 = <&key_gpio>;
key-gpio = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>;
// interrupt-parent = <&gpio3>;
// interrupts = <21 IRQ_TYPE_EDGE_BOTH>;
status = "okay";
};
key-gpio 属性指定了 KEY 所使用的 GPIO,高电平有效
实验
设置GPIO为输入模式
按键检测思路:
①按下检测:若为高电平,延时200ms消抖,确认是否真的为高电平
②松手检测:等待低电平到来,若为低电平,延时200ms消抖,确认是低电平后,再上报数据给应用。
代码:key.c
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define KEY_CNT 1 /* 设备号个数 */
#define KEY_NAME "key" /* 名字 */
#define KEY_PRESS 0x0F /* 按键被按下 */
#define INVAKEY 0x00 /* 按键松开 */
#define GPIO_HIGH_LEVEL (1)
#define GPIO_LOW_LEVEL (0)
struct key_dev
{
dev_t devid; /* 设备号 */
struct cdev cdev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
struct device_node *nd; /* 设备节点 */
int key_gpio; /* key所使用的GPIO编号 */
atomic_t key_val; /* 原子变量 */
};
struct key_dev key;
static int key_drv_open(struct inode *inode, struct file *filp)
{
filp->private_data = &key; /* 设置私有数据 */
return 0;
}
static ssize_t key_drv_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
int ret = 0;
struct key_dev* p_key = filp->private_data;
int key_gpio_val = gpio_get_value(p_key->key_gpio);
if(key_gpio_val < 0) {
return -1;
} else if(key_gpio_val == GPIO_LOW_LEVEL) {
atomic_set(&p_key->key_val, INVAKEY);
} else if(key_gpio_val == GPIO_HIGH_LEVEL) {
mdelay(200);
if(gpio_get_value(p_key->key_gpio) == GPIO_HIGH_LEVEL) {
while(gpio_get_value(p_key->key_gpio) == GPIO_HIGH_LEVEL);
mdelay(200);
if(gpio_get_value(p_key->key_gpio) == GPIO_LOW_LEVEL) {
atomic_set(&p_key->key_val, KEY_PRESS);
}
}
}
ret = copy_to_user(buf, &p_key->key_val, sizeof(p_key->key_val));
return 0;
}
static ssize_t key_drv_write(struct file *filp, const char __user *buf,
size_t cnt, loff_t *offt)
{
return 0;
}
static int key_drv_realse(struct inode *inode, struct file *filp)
{
// struct key_dev *dev = filp->private_data;
return 0;
}
static const struct file_operations key_drv_ops = {
.owner = THIS_MODULE,
.open = key_drv_open,
.read = key_drv_read,
.write = key_drv_write,
.release = key_drv_realse,
};
int key_parse_dts(void)
{
int ret = 0;
const char *str = NULL;
/* 设置KEY所使用的GPIO */
/* 1、获取设备节点:key */
key.nd = of_find_node_by_path("/key");
if(key.nd == NULL) {
printk("key node not find!\r\n");
return -EINVAL;
}
/* 2.读取status属性 */
ret = of_property_read_string(key.nd, "status", &str);
if(ret < 0)
return -EINVAL;
if (strcmp(str, "okay"))
return -EINVAL;
/* 3、获取compatible属性值并进行匹配 */
ret = of_property_read_string(key.nd, "compatible", &str);
if(ret < 0) {
printk("key: Faikey to get compatible property\n");
return -EINVAL;
}
if (strcmp(str, "alientek,key")) {
printk("key: Compatible match faikey\n");
return -EINVAL;
}
/* 4、 获取设备树中的gpio属性,得到KEY所使用的KEY编号 */
key.key_gpio = of_get_named_gpio(key.nd, "key-gpio", 0);
if(key.key_gpio < 0) {
printk("can't get key-gpio");
return -EINVAL;
}
printk("key-gpio num = %d\r\n", key.key_gpio);
/* 5.向gpio子系统申请使用GPIO */
ret = gpio_request(key.key_gpio, "KEY-GPIO");
if (ret) {
printk(KERN_ERR "key: Faikey to request key-gpio\n");
goto free_gpio;
}
/* 6、设置GPIO为输入 */
ret = gpio_direction_input(key.key_gpio);
if(ret < 0) {
goto free_gpio;
}
return 0;
free_gpio:
gpio_free(key.key_gpio);
return -EIO;
}
static int __init key_drv_init(void)
{
int ret = 0;
printk("key init\n");
/* 原子变量初始值为1 */
atomic_set(&key.key_val, INVAKEY);
/* 注册字符设备驱动 */
/* 1、创建设备号 */
if (key.major) { /* 定义了设备号 */
key.devid = MKDEV(key.major, 0);
ret = register_chrdev_region(key.devid, KEY_CNT, KEY_NAME);
if(ret < 0) {
pr_err("cannot register %s char driver [ret=%d]\n", KEY_NAME, KEY_CNT);
goto dev_id_err;
}
} else { /* 没有定义设备号 */
ret = alloc_chrdev_region(&key.devid, 0, KEY_CNT, KEY_NAME); /* 申请设备号 */
if(ret < 0) {
pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", KEY_NAME, ret);
goto dev_id_err;
}
key.major = MAJOR(key.devid); /* 获取分配号的主设备号 */
key.minor = MINOR(key.devid); /* 获取分配号的次设备号 */
}
printk("key major=%d, minor=%d\r\n",key.major, key.minor);
/* 2、初始化cdev */
key.cdev.owner = THIS_MODULE;
cdev_init(&key.cdev, &key_drv_ops);
/* 3、添加一个cdev */
cdev_add(&key.cdev, key.devid, KEY_CNT);
if(ret < 0)
goto del_unregister;
/* 4、创建类 */
key.class = class_create(THIS_MODULE, KEY_NAME);
if (IS_ERR(key.class)) {
goto del_cdev;
}
/* 5、创建设备 */
key.device = device_create(key.class, NULL, key.devid, NULL, KEY_NAME);
if (IS_ERR(key.device)) {
goto destroy_class;
}
if(key_parse_dts() != 0) {
goto destroy_class;
}
return 0;
destroy_class:
class_destroy(key.class);
del_cdev:
cdev_del(&key.cdev);
del_unregister:
unregister_chrdev_region(key.devid, KEY_CNT);
dev_id_err:
return -EIO;
}
static void __exit key_drv_exit(void)
{
printk("key exit\n");
/* 释放GPIO */
gpio_free(key.key_gpio);
/* 注销字符设备驱动 */
device_destroy(key.class, key.devid);/* 注销设备 */
class_destroy(key.class);/* 注销类 */
cdev_del(&key.cdev);/* 删除cdev */
unregister_chrdev_region(key.devid, KEY_CNT);
return ;
}
module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lizh");
应用程序:keyApp.c
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#define KEY_PRESS 0x0F /* 按键被按下 */
#define INVAKEY 0x00 /* 按键松开 */
#define DRV_PATH_NAME "/dev/key"
int main(int argc, char *argv[])
{
int i = 0, count = 0;
int fd = 0;
int ret = 0;
int key_val = 0;
/* 判断参数是否正确 */
if(argc != 2) {
printf("user param error!\n");
printf("please input ./keyApp %s\r\n" , DRV_PATH_NAME);
goto input_error;
}
/* 打开文件 */
fd = open(DRV_PATH_NAME, O_RDWR);
if(fd < 0) {
goto open_failed;
}
while(1) {
ret = read(fd, &key_val, sizeof(key_val));
if(ret < 0) {
goto read_error;
}
if(key_val == KEY_PRESS) {
printf("key press down\n");
}
}
close(fd);
return 0;
read_error:
close(fd);
open_failed:
input_error:
return -1;
}
结果图: