Linux 按键输入实验

news2024/7/6 19:04:56

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.coloradmin.cn/o/1833298.html

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

相关文章

OSPF 动态路由协议(思科、华为)

#交换设备 OSPF 动态路由协议 一、基本概念 1.中文翻译&#xff1a;开放式最短路径优先路由协议&#xff08;open shortest path first&#xff09;&#xff0c;是一个内部网关路由协议&#xff08;一个自治系统内&#xff09;2.也称为&#xff1a;链路状态路由协议&#xf…

CAD二次开发(9)- CAD中对象的实时选择

1. 点的拾取 有时候我们需要在CAD画布上实时选取起始点和结束点&#xff0c;然后绘制出来一条直线。实现如下&#xff1a; public void getPoint(){var doc Application.DocumentManager.MdiActiveDocument;var editor doc.Editor;var docDatabase doc.Database;PromptPoi…

Spring Cloud Alibaba Nacos持久化配置

所谓的持久化就是将Nacos配置持久化存储到数据库里面&#xff0c;在0.7版本之前&#xff0c;在单机模式时nacos使用嵌入式数据库实现数据的存储&#xff0c;不方便观察数据存储的基本情况。0.7版本增加了支持mysql数据源能力。 ① 找到并执行sql脚本 这里路径为&#xff1a;n…

摄影构图:人像摄影和风景摄影的一些建议

写在前面 博文内容涉及摄影中人像摄影和风景摄影的简单介绍《高品质摄影全流程解析》 读书笔记整理理解不足小伙伴帮忙指正 &#x1f603; 生活加油 不必太纠结于当下&#xff0c;也不必太忧虑未来&#xff0c;当你经历过一些事情的时候&#xff0c;眼前的风景已经和从前不一样…

SpringBootWeb 篇-入门了解 Apache POI 使用方法

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 Apache POI 概述 2.0 使用 Apache POI 读写 Excel 文件 2.1 写入 Excel 文件 2.2 写入 Excel 文件代码演示 2.3 读取 Excel 文件 2.4 读取 Excel 文件代码演示 1.…

胡说八道(24.6.9)——离散时间系统及simulink仿真

上回说道拉普拉斯变换的定义、性质以及在电路分析中的应用。今天先来谈谈simulink仿真&#xff0c;可为是让我非常的震惊&#xff0c;今天做了三种模型的应用。第一个是simulink中有限状态机的应用&#xff0c;用来解决一些复杂的逻辑问题&#xff0c;实现状态之间的转换。第一…

市场情绪周期2024-6-17(补涨回头潮视角验证)

竞价隔夜单 看长江通信&#xff0c;38亿涨到40亿又回落&#xff0c;那么周末最大的利好消息加持下&#xff0c;隔夜单不及预期&#xff0c;金溢科技 更是如此&#xff1b;空间板华闻集团8天7板&#xff0c;连扳5板&#xff0c;一字跌停&#xff0c;它也是有车联网的&#xff0c…

2V升6V升压恒流WT7012

2V升6V升压恒流WT7012 今天给大家介绍一款能驱动多达7串白光LED的升压转换器WT7012。 WT7012升压恒流应用&#xff1a;2V升6V升9V升12V恒流驱动。其通过外部电流检测电阻&#xff0c;采用电流控制模式以及固定的工作频率来控制LED电流&#xff0c;较低的反馈电压使得电流检测电…

Windows系统下制作Windows Server系统U盘启动及安装指导

Windows系统下制作Windows Server系统U盘启动及安装指导 一、准备工作 U盘不得小于8G(推荐使用usb3.0接口)&#xff1b;下载好对应的系统镜像&#xff1b;下载RUFUS或者软通碟U盘制作启动软件&#xff1b; 二、Windows操作系统下制作U盘启动&#xff08;这里以使用RUFUS软件…

Spring和SpringBoot(概述与功能)

目录 一、SpringBoot 概念 &#xff08;1&#xff09;基本介绍 &#xff08;2&#xff09;部分注解 &#xff08;3&#xff09;其它介绍 ​编辑 二、 Spring 缺点 &#xff08;1&#xff09;配置繁琐 &#xff08;2&#xff09;依赖繁琐 三、SpringBoot 功能&#xff0…

常见的Redis使用问题及解决方案

目录 1. 缓存穿透 1.1 解决方案 2. 缓存击穿 2.1 解决方案 3. 缓存雪崩 3.1 概念图及问题描述 ​编辑3.2 解决方案 4. 分布式锁 4.1 概念 4.2 基于redis来实现分布式锁 4.3 用idea来操作一遍redis分布式锁 4.4 分布式上锁的情况下&#xff0c;锁释放了服务器b中的锁…

教育培训系统(FastAdmin+ThinkPHP+Unipp)

引领学习新风尚 &#x1f4da; 引言&#xff1a;教育新篇章 随着科技的不断发展&#xff0c;教育形式也在不断创新与变革。教育培训系统作为这一变革的重要载体&#xff0c;正逐渐改变着我们的学习方式。今天&#xff0c;就让我们一起探索教育培训系统的魅力&#xff0c;看看它…

[ARM-2D 专题]3. ##运算符

C语言的宏系统相当强大&#xff0c;它允许使用##符号来处理预处理期的文本替换。这种用法被称为标记连接&#xff08;token pasting&#xff09;操作&#xff0c;其结果是将两个标记紧紧地连接在一起&#xff0c;而省略掉它们之间的所有空格。在复杂的宏定义中&#xff0c;运用…

部分CVE复现Web(1)

Apache HTTP Server 路径穿越漏洞CVE-2021-41773 ​ 首先&#xff0c;先来看一下这个漏洞的官方描述&#xff1a; ​ CVE-2021-41773 是在 Apache HTTP Server 2.4.49 中对路径规范化所做的更改中发现了一个缺陷。攻击者可以使用路径遍历攻击将 URL 映射到预期文档根目录之外的…

三星(中国)投资公司线上入职测评笔试邀请数字推理语言逻辑真题题库

三星&#xff08;中国&#xff09;有限公司北京分公司 邀请您参加 SHL线上笔试 具体安排如下&#xff1a; 笔试时间&#xff1a;周三 9:00 笔试时长&#xff1a;1.5h ~ 2h 笔试内容及要求&#xff1a;数字推理限时30min&#xff1b;语言逻辑限时30min&#xff1b;性格测试不…

国内外典型的知识图谱项目

文章目录 早期的知识库项目互联网时代的知识图谱中文开放知识图谱垂直领域知识图谱 从人工智能的概念被提出开始&#xff0c;构建大规模的知识库一直都是人工智能、自然语言理解等领域的核心任务之一。下面分别介绍早期的知识库项目、互联网时代的知识图谱、中文开放知识图谱和…

什么是Vue开发技术

概述 Vue.js 是一个用于构建用户界面的渐进式框架&#xff0c;它设计得非常灵活&#xff0c;可以轻松地被集成到任何项目中。 vue是视图的发音&#xff0c;其目的是帮助开发者易于上手&#xff0c;提供强大的功能构建复杂的应用程序 示例 以下是vue基本的语法概述 声明式渲…

激活和禁用Hierarchy面板上的物体

1、准备工作&#xff1a; (1) 在HIerarchy上添加待隐藏/显示的物体&#xff0c;名字自取。如&#xff1a;endImage (2) 在Inspector面板&#xff0c;该物体的名称前取消勾选&#xff08;隐藏&#xff09; (3) 在HIerarchy上添加按钮&#xff0c;名字自取。如&#xff1a;tip…

“深入探讨Redis主从复制:原理、配置与优化“

目录 # 概念 1. 配置主从同步步骤 1.1 创建文件夹 1.2 复制配置文件 1.3 配置文件关闭 1.4 查看端口号&#xff0c;发现端口号存在 1.5 连接三个端口号 1.6 查看主机运行情况 1.7 让服务器变成&#xff08;主机&#xff09;或&#xff08;从机&#xff09; 1.8 实现效…

HarmonyOS之自选股App

支持在 鸿蒙、安卓、苹果设备上运行。 1.界面效果展示 2.数据存储 数据存储采用的是官方的 ohos.data.relationalStore.relationalStore stock_code表用来存储A股市场5000多家公司的股票代码和名称等信息 const TAB_STOCK_CODE "stock_code" const CREATE_TABL…