首页 > 系统相关 >Linux 按键输入实验

Linux 按键输入实验

时间:2024-06-16 10:10:16浏览次数:25  
标签:return Linux keydev key 按键 gpio include 输入

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;
}

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

标签:return,Linux,keydev,key,按键,gpio,include,输入
From: https://www.cnblogs.com/bathwind/p/18250224

相关文章

  • VirtIO drivers for Linux, Windows and etc.
    WhydoyouneedVirIOdriver?It'susefulwhenyouuseLinux/WindowsVMsinPVE.UsuallywhenyouattachanetworktotheVM,youwouldclickthe"Hardware"->"NetworkDevice"->"Bridge"->"vmbr0"o......
  • linux下C语言如何操作文件(二)
    上篇文章中,我们简单介绍了file_util.h中定义的各函数,今天我们来讲解如何实现头文件中定义的各函数。首先,在file_util.c中,我们需要引入相应的头文件:#include<stdio.h>#include<stdlib.h>#include<stdbool.h>#include<unistd.h>#include<sys/stat.h>#include<sys/typ......
  • Linux下基于USB的WiFi(RTL8723DU):驱动、wpa_supplicant配置、hostapd配置等
    1WiFi基本概念WLAN(WirelessLAN)是无线局域网的意思。IEEE802.11是现今无线局域网通用的标准。关于802.11协议介绍参考《80211协议介绍》。信道也就是频段,是以无线信号作为传输载体的数据信号传送通道。802.11工作组划分了4个独立的频段:2.4GHz、3.6GHz、4.9GHz和5.8......
  • 分析Linux操作指令及使用场景与频率分析 持续更新
    本篇主要针对在日常工作与学习中使用较多的linux指令的使用方法以及使用频次进行分析与讲解,旨在能够更好的掌握这些必备的技能。linux指令非常的多,如果要记住所有的指令使用方法是非常困难的且要花费很长的时间,很多人习惯离开使用去通篇死记,这样过一段时间就容易忘记,在这个方......
  • Linux常用命令汇总
    文件与目录操作                cd命令    切换目录cd/home#切换到"/home"目录cd..#返回上一级目录cd../..#返回上两级目录cd-#返回上一次所在目录test=/homecd"$(test)"......
  • ffmpeg封装和解封装介绍-(9)根据输入时间参数和文件名调整截断时间
    头文件:xformat.h#pragmaonce///<summary>///封装和解封装基类///</summary>#include<mutex>structAVFormatContext;structAVCodecParameters;structAVPacket;structXRational{intnum;///<Numeratorintden;///<Denominator......
  • Linux实验八:流式套接字编程
    目录一、实验目的二、实验内容三、实验环境四、参考代码五、实验步骤步骤1.编辑源代码blockserver.c和blockclient.c步骤2.编译源代码blockserver.c和blockclient.c步骤3.运行可执行程序blockserver和blockclient六、实验结果七、实验总结一、实验目的1、深入......
  • Linux环境搭建和项目基础
    GCCGCC原名为GNUC语言编译器(GNUCCompiler)GCC(GNUCompilerCollection,GNU编译器套件)是由GNU开发的编程语言译器。GNU编译器套件包括C、C++、Objective-C、Java、Ada和Go语言前端,也包括了这些语言的库(如libstdc++,libgcj等)GCC不仅支持C的许多“方言”,......
  • Linux的目录结构
    问题产生:在学习Linux的过程中对Linux系统的文件结构感到不解,例如/bin和/usr/bin和/usr/local/bin之间的区别以及各个目录到底是存放什么内容的问题,需要进行进一步的学习!列举一下Linux中比较重要的部分目录:主目录:/root,/home/username用户可执行文件:/bin,/usr/b......
  • 【Linux】进程_4
    文章目录五、进程4.进程状态5.进程优先级6.进程的调度和转换未完待续五、进程4.进程状态当进程属于挂起状态时,进程的可执行程序代码和数据均会被从内存中换入到磁盘中,此时进程的PCB并没有消失,只要操作系统还需要管理这个进程,进程的PCB就需要存在,所以我们可以......