Linux 按键输入实验

news/2024/10/4 19:26:50

Linux 按键输入实验

1、添加 pinctrl 节点

首先修改在设备树里面添加关于按键的节点。I.MX6U-ALPHA 开发板上的 KEY 使用了 UART1_CTS_B 这个 PIN,打开 imx6ull-alientekemmc.dts,在 iomuxc 节点的 imx6ul-evk 子节点下创建一个名为“pinctrl_key”的子节点,节点内容如下所示:

pinctrl_key: keygrp {fsl,pins = <MX6UL_PAD_UART1_CTS_B__GPIO1_IO18		0xF080	/* KEY0 */>;
};

2、 添加key设备节点:

在根节点“/”下创建 KEY 节点,节点名为“key”,节点内容如下:

	key {#address-cells = <1>;#size-cells = <1>;compatible = "atkalpha-key";pinctrl-names = "default";pinctrl-0 = <&pinctrl_key>;key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEY0 */interrupt-parent = <&gpio1>;interrupts = <18 IRQ_TYPE_EDGE_BOTH>; /* FALLING RISING */status = "okay";};

pinctrl-0 属性设置 KEY 所使用的 PIN 对应的 pinctrl 节点。key-gpio 属性指定了 KEY 所使用的 GPIO。

3、检查 PIN 是否被其他外设使用

在本章实验中蜂鸣器使用的 PIN 为 UART1_CTS_B,因此先检查 PIN 为 UART1_CTS_B 这个 PIN 有没有被其他的 pinctrl 节点使用,如果有使用的话就要屏蔽掉,然后再检查 GPIO1_IO18这个 GPIO 有没有被其他外设使用,如果有的话也要屏蔽掉。
按键驱动和 LED 驱动原理上来讲基本都是一样的,都是操作 GPIO,只不过一个是读取GPIO 的高低电平,一个是从 GPIO 输出高低电平。本章我们实现按键输入,在驱动程序中使用一个整形变量来表示按键值,应用程序通过 read 函数来读取按键值,判断按键有没有按下。
按键驱动程序为:

#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 <linux/semaphore.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>#define KEY_CNT 1      /* 设备号个数 */
#define KEY_NAME "key" /* 名字 */
/* 定义按键值 */
#define KEY0VALUE 0XF0 /* 按键值 */
#define INVAKEY 0X00   /* 无效的按键值 */
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;           /* led 所使用的 GPIO 编号 */atomic_t keyvalue;      /* 原子变量 */
};
struct key_dev keydev; /* led 设备 */
static int keyio_init(void)
{keydev.nd = of_find_node_by_path("/key");if (keydev.nd == NULL){return -EINVAL;}keydev.key_gpio = of_get_named_gpio(keydev.nd, "key-gpio", 0);if (keydev.key_gpio < 0){printk("can't get key0\r\n");return -EINVAL;}printk("key_gpio=%d\r\n", keydev.key_gpio);/* 初始化 key 所使用的 IO */gpio_request(keydev.key_gpio, "key0"); /* 请求 IO */gpio_direction_input(keydev.key_gpio); /* 设置为输入 */return 0;
}
static int key_open(struct inode *inode, struct file *filp)
{int ret = 0;filp->private_data = &keydev; /* 设置私有数据 */ret = keyio_init();            /* 初始化按键 IO */if (ret < 0){return ret;}return 0;
}
/** @description : 从设备读取数据* @param – filp : 要打开的设备文件(文件描述符)* @param - buf : 返回给用户空间的数据缓冲区* @param - cnt : 要读取的数据长度* @param – offt : 相对于文件首地址的偏移* @return : 读取的字节数,如果为负值,表示读取失败*/
static ssize_t key_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{int ret = 0;unsigned char value;struct key_dev *dev = filp->private_data;if(gpio_get_value(dev->key_gpio) == 0){while(!gpio_get_value(dev->key_gpio));atomic_set(&dev->keyvalue,KEY0VALUE);}else{atomic_set(&dev->keyvalue,INVAKEY);}value = atomic_read(&dev->keyvalue);ret = copy_to_user(buf,&value,sizeof(value));return ret;
}
/* 设备操作函数 */
static struct file_operations key_fops = {.owner = THIS_MODULE,.open = key_open,.read = key_read,
};
static int __init mykey_init(void)
{int ret = 0;atomic_set(&keydev.keyvalue, INVAKEY);/*1、创建设备号*/if (keydev.major){keydev.devid = MKDEV(keydev.major, 0);register_chrdev_region(keydev.devid, KEY_CNT, KEY_NAME);}else{alloc_chrdev_region(&keydev.devid, 0, KEY_CNT, KEY_NAME);keydev.major = MAJOR(keydev.devid);keydev.minor = MINOR(keydev.devid);}printk("newcheled major: %d minor: %d", keydev.major, keydev.minor);keydev.cdev.owner = THIS_MODULE;cdev_init(&keydev.cdev, &key_fops);cdev_add(&keydev.cdev, keydev.devid, KEY_CNT);keydev.class = class_create(THIS_MODULE, KEY_NAME);if (IS_ERR(keydev.class)){return PTR_ERR(keydev.class);}keydev.device = device_create(keydev.class, NULL, keydev.devid, NULL, KEY_NAME);if (IS_ERR(keydev.device)){return PTR_ERR(keydev.device);}return 0;
}
static void __exit mykey_exit(void)
{/* 注销字符设备驱动 */gpio_free(keydev.key_gpio);cdev_del(&keydev.cdev); /* 删除 cdev */unregister_chrdev_region(keydev.devid,KEY_CNT);device_destroy(keydev.class, keydev.devid);class_destroy(keydev.class);
}
module_init(mykey_init);
module_exit(mykey_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wyw");

其实按键与LED相似的就是一个是读一个是写。按键则是读取高低电平。也就是检测这个GPIO编号时候,利用原子变量写入key->value这个值。
key_open 函数通过调用 keyio_init 函数来始化按键所使用的 IO,应用程序每次打开按键驱动文件的时候都会初始化一次按键 IO。key_read 函数,应用程序通过 read 函数读取按键值的时候此函数就会执行。读取按键 IO 的电平,如果为 0 的话就表示按键按下了,如果按键按下的话就等待按键释放。按键释放以后标记按键值为 KEY0VALUE。
测试App程序为:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"#define KEY0VALUE 0XF0
#define INVAKEY 0X00
int main(int argc, char *argv[])
{int fd, retvalue;char *filename;unsigned char keyvalue;if (argc != 2){printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR);if (fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;}while (1){read(fd, &keyvalue, sizeof(keyvalue));if(keyvalue == KEY0VALUE){printf("KEY0 Press, value = %#X\r\n", keyvalue);/* 按下 */}}printf("App running finished!");retvalue = close(fd); /* 关闭文件 */if (retvalue < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0;
}

现在就是按键按下终端输出如下图所示的现象:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/44236.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

网上购物框架

javaweb项目实战 1.功能概述 网上购物商城分为管理员和普通用户页面,用户可以自行注册登录,用户可以加入商品到购物车中,可以查看商品的详情,管理员可以对商品进行增删改查,比如发布商品,下架商品,修改商品,搜索商品。 本项目采用mysql数据库进行储存数据,所以先搭建项…

Pixel Transformer:用像素代替补丁可以提升图像分类精度

在快速发展的人工智能领域,ViTs已成为各种计算机视觉任务的基础模型。ViTs通过将图像划分为小块并将这些小块作为标记来处理图像。6月刚发布一篇论文,引入了一种新颖的方法,即像素级Transformers,它通过将单个像素视为令牌来挑战这种范式。本文将讨论Pixel Transformer的复…

Lakehouse 还是 Warehouse?(2/2).md

这篇博文包括 Onehouse 首席执行官 Vinoth Chandar 于 2022 年 3 月在奥斯汀数据委员会发表的重要演讲的后半部分。本文是第 2 部分,比较了架构的功能和性价比特征。最后,它描述了一个面向未来的、湖仓一体的架构。 数据仓库和Lakehouse:功能对比对于核心读写:湖仓一体和仓…

k8s资源管理方式

目录一、什么是k8s资源管理1、简介2、系统资源查看3、常见的资源类型二、资源管理的方式1、命令式对象管理2、命令式对象配置3、声明式对象配置4、区别三、命令式对象管理1、kubectl命令1.1、语法格式1.2、常见操作1.3、示例1.3.1、查看所有的pod1.3.2、查看某个pod1.3.3、查看…

dotnet 简单控制台使用 KernelMemory 向量化文本嵌入生成和查询

本文将和大家简单介绍一下如何在控制台里面使用 Microsoft.KernelMemory 调用 TextEmbedding 对一些文本知识库内容生成向量化信息,以及进行向量化查询本文属于 SemanticKernel 入门系列博客,更多博客内容请参阅我的 博客导航 或 博客园的合集 根据 new bing 对 Microsoft.Ke…

WPF 通过 SetWindowDisplayAffinity 配置禁止对窗口进行截图或录屏

有些应用程序比较机密或隐私,不期望被其他截图软件截图到应用的窗口,或者被录屏软件录制到。简单的方法是通过 SetWindowDisplayAffinity 方法进行配置窗口阻止截图软件对其截图开始之前必须说明的是对抗截图录屏是一个矛和盾的事情,截图和录屏技术方向在千方百计尝试对所有…

[转帖]性能分析之TCP全连接队列占满问题分析及优化过程

https://cloud.tencent.com/developer/article/1558493 前言 在对一个挡板系统进行测试时,遇到一个由于TCP全连接队列被占满而影响系统性能的问题,这里记录下如何进行分析及解决的。 理解下TCP建立连接过程与队列 从图中明显可以看出建立 TCP 连接的时候,有两个队列:syns…