当前位置: 首页 > news >正文

Linux按键输入实验-按键功能完善

一.  简介

前面一篇文章实现了 按键的字符设备驱动代码框架,文章地址如下:

Linux按键输入实验-按键的字符设备驱动代码框架-CSDN博客

本文在 字符设备驱动框架实现的基础上,加入按键GPIO的初始化功能。

二.  Linux按键输入实验-按键的GPIO初始化

1.  驱动代码完善

这里在前面按键的字符设备驱动代码框架的基础上,添加按键的GPIO初始化代码。

打开 11_key工程代码,key.c文件添加 GPIO初始化代码后如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>#define  KEY_CNT       1
#define  KEY_NAME      "key"
#define  INVALID_VALUE 0x00  //按键未按下值
#define  VALID_VALUE   0x01  //按键按下值 static int key_open(struct inode *inode, struct file *filp);
ssize_t key_read(struct file * filp, char __user * buf, size_t count, loff_t * ppos);
int key_close(struct inode * inode, struct file * filp);//按键的设备结构体
struct key_dev{dev_t  devid;  //主设备号+次设备号int major; //主设备号int minor; //次设备号struct cdev key_cdev;struct class* key_class;struct device* device;struct device_node * dev_node;//设备节点int gpio_number;//gpio编号atomic_t key_value; //按键值//这里使用原子变量作为按键值类型
};struct key_dev key;static const struct file_operations key_fops = {.open = key_open,.owner = THIS_MODULE,.read = key_read,.release = key_close, 
};/* 打开设备函数*/
static int key_open(struct inode *inode, struct file *filp)
{filp->private_data = &key;return 0;
}/* 从设备中读取数据函数*/
ssize_t key_read(struct file * filp, char __user * buf, size_t count, loff_t * ppos)
{int ret = 0, value = 0;struct key_dev* dev = filp->private_data;if(0 == gpio_get_value(dev->gpio_number)) //按键被按下{while(!gpio_get_value(dev->gpio_number));atomic_set(&dev->key_value, VALID_VALUE);} else  //按键未按下{atomic_set(&dev->key_value, INVALID_VALUE);}value = atomic_read(&dev->key_value);ret = copy_to_user(buf, &value, sizeof(value));if(ret < 0){printk("kernel send to app for data failed!\n");return -1;}return 0;
}/* 关闭设备函数 */
int key_close(struct inode * inode, struct file * filp)
{return 0;
}/*按键的GPIO初始化函数*/
static int key_gpio_init(struct key_dev* dev)
{int ret = 0;/*1. 获取按键的设备节点*/dev->dev_node = of_find_node_by_path("/key");if(NULL == dev->dev_node){printk("find device node failed!\n");ret = -EINVAL;goto find_devnode;}/*2. 获取GPIO编号 */dev->gpio_number = of_get_named_gpio(dev->dev_node, "key-gpio", 0);if((dev->gpio_number) < 0){printk("get device number failed!\n");ret = -EINVAL;goto get_dev_number;}/*3.请求IO */ret = gpio_request(dev->gpio_number, "key");if(ret < 0){printk("gpio_request failed!\n");goto gpio_request;}/*4. 设置GPIO为输入功能*/ret = gpio_direction_input(dev->gpio_number);if(ret < 0){printk("set input io\n");goto set_input_io;}return 0;set_input_io:gpio_free(dev->gpio_number);
gpio_request:
get_dev_number:
find_devnode:return ret;
}/*模块入口函数*/
static int __init key_init(void)
{ int ret = 0;key.major = 0;//按键值初始化atomic_set(&key.key_value, INVALID_VALUE);/*1. 申请设备号*/if(key.major) //如果给出主设备号,则注册设备号{key.devid = MKDEV(key.major, 0);ret = register_chrdev_region(key.devid, KEY_CNT, KEY_NAME);		    }else //否则申请设备号{ret = alloc_chrdev_region(&key.devid, 0, KEY_CNT, KEY_NAME);key.major = MAJOR(key.devid);key.minor = MINOR(key.devid);}printk("major: %d minor: %d\n", key.major,key.minor);if(ret < 0){printk("devid apply failed!\n");goto devid_failed;}/*2. 设备初始化,添加设备*/key.key_cdev.owner = THIS_MODULE;cdev_init(&key.key_cdev, &key_fops);ret = cdev_add(&key.key_cdev, key.devid, KEY_CNT);if(ret < 0){printk("cdev_add failed!\n");goto cdev_init_failed;}/*3. 自动创建设备节点*/key.key_class = class_create(THIS_MODULE, KEY_NAME);if (IS_ERR(key.key_class)) {printk(KERN_ERR "class_create failed!\n");ret = PTR_ERR(key.key_class);goto class_create_failed;}key.device = device_create(key.key_class, NULL, key.devid, NULL, KEY_NAME);if (IS_ERR(key.device)) {printk(KERN_ERR "class_create failed!\n");ret = PTR_ERR(key.device);goto device_create_failed;}/*4. 按键的GPIO初始化*/key_gpio_init(&key);return 0;device_create_failed:class_destroy(key.key_class);
class_create_failed:    cdev_del(&key.key_cdev);
cdev_init_failed:unregister_chrdev_region(key.devid, KEY_CNT);
devid_failed:return ret;
} /*模块出口函数*/
static void __exit key_exit(void)
{/*1. 释放设备编号 */gpio_free(key.gpio_number);/*2. 删除设备*/cdev_del(&key.key_cdev);/*3. 注销设备号 */unregister_chrdev_region(key.devid, KEY_CNT);/*4. 摧毁设备/摧毁类*/device_destroy(key.key_class, key.devid);class_destroy(key.key_class);
}/*模块入口与出口*/
module_init(key_init);
module_exit(key_exit);
MODULE_LICENSE("GPL"); //驱动License
MODULE_AUTHOR("WeiWuXian"); //作者

2.  编译驱动

对上面驱动代码进行编译:

wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/11_key$ make
make -C /home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/wangtian/zhengdian_Linux/Linux_Drivers/11_key modules
make[1]: 进入目录“/home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga”CC [M]  /home/wangtian/zhengdian_Linux/Linux_Drivers/11_key/key.oBuilding modules, stage 2.MODPOST 1 modulesCC      /home/wangtian/zhengdian_Linux/Linux_Drivers/11_key/key.mod.oLD [M]  /home/wangtian/zhengdian_Linux/Linux_Drivers/11_key/key.ko
make[1]: 离开目录“/home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga”
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/11_key$

可以看出,驱动模块可以正常编译。

三.  编写测试应用程序

按键驱动程序的测试,需要用到应用程序,这里编写测试(应用)程序。可以参考前面实验中的测试程序 led_app.c的实现,这里创建 key_app.c文件。

key.app.c文件代码完成后如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>#define  INVALID_VALUE 0x00  //按键未按下值
#define  VALID_VALUE   0x01  //按键按下值 /*
*测试按键是否按下
* 运行命令: ./key_app /dev/key 
*/
int main(int argc, char* argv[])
{int fd = 0;char * device_name = NULL;int key_value = 0;if(argc != 2){printf("main's param number error!\n");return -1;}device_name = argv[1];fd = open(device_name, O_RDWR);if(fd < 0){printf("open led device failed!\n");return -1;}while(1){read(fd, &key_value, sizeof(key_value));if(VALID_VALUE == key_value) //按键被按下{printf("Key0 Press: key_value: %d\n", key_value);}}close(fd);return 0;
}

编译测试程序:

ubuntu进入 11_key工程目录下,对测试程序进行编译:

wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/11_key$ arm-linux-gnueabihf-gcc key_app.c -o key_app

测试程序也可以编译通过。接下来就是对驱动模块进行测试,测试按键功能是否可用。

相关文章:

Linux按键输入实验-按键功能完善

一. 简介 前面一篇文章实现了 按键的字符设备驱动代码框架&#xff0c;文章地址如下&#xff1a; Linux按键输入实验-按键的字符设备驱动代码框架-CSDN博客 本文在 字符设备驱动框架实现的基础上&#xff0c;加入按键GPIO的初始化功能。 二. Linux按键输入实验-按键的GPIO…...

二分查找讲解

关于我为什么要写单独开一篇文章写二分,实际上那么多困难的算法,比如线段树,并查集等等都没有难倒我,我最近却被二分难倒了,而且是两次,两次在赛场上做不出来二分的应用题,于是我决定写一篇二分查找的算法总结.刚接触算法的时候本来是要写一篇的,但后面因为各种原因搁置了,现在…...

跨区域复制建筑UI输入框脚本迷你世界

--复制区域文件 --设置坐标起点&#xff0c;终点 --创建区域 --获取坐标id,data --星空露珠工作室制作 local pos1{x-16,y7,z28} local pos2{x28,y44,z-9} local block{num0} local str{} local str0{} local num0 local count0 local ui6 --几个输入框 local romath.random(…...

取消退出流程控制方法

在自动化设备动作流程中&#xff0c;人为任意想取消当前动作&#xff0c;常见方法是使用全局变量&#xff0c;实时检测变量决定退出。这里介绍一个System.Threading空间下的 CancellationTokenSource类&#xff0c;他可以设置超时&#xff0c;设置信息等封装 基本使用超时和手…...

力扣-跳跃游戏

问题 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 解答 class Solu…...

李沐动手学习深度学习——3.2练习

以下是个人理解&#xff0c;希望进行讨论求解。 练习 1. 如果我们将权重初始化为零&#xff0c;会发生什么。算法仍然有效吗&#xff1f; 根据SGD算法公式如上&#xff0c;第一次迭代的值可知w只与b相关&#xff0c;而对于b的迭代更新&#xff0c;只是与b的初始值相关&#x…...

代码随想录Day20 | Leetcode77 组合

题目 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。你可以按 任何顺序 返回答案。示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1a; [[2,4],[3,4],[2,3],[1,2],[1,3],[1,4], ]示例 2&#xff1a; 输入&#xff1a;n 1, k 1 …...

Android Duplicate class 排除重复类

一、起因&#xff1a; 在迭代开发的时候&#xff0c;发现2个ijk很多类重复。但又2个库实现的功能是不一样&#xff0c;目前不能合并。但又想保留2个功能。需要排除其中一个库。 二、报错如何下图&#xff1a; 三、解决方法&#xff1a; 3.1 在terminal 也就是命令行处输入 …...

【Kubernetes】服务(Service)是什么?有什么用?有哪些类型?

系列文章目录 K8s中的Namespace是什么&#xff1f; Kubernetes 集群的组件介绍 Kubernetes 对象是什么&#xff1f; Pod——k8s中最重要的对象之一 Kubernetes 和 Docker 之间有什么区别&#xff1f; 部署安装 K8s 为什么要关闭 swap 分区&#xff1f; k8s中容器之间、pod之间…...

【前端素材】推荐优质后台管理系统DAdmin平台模板(附源码)

一、需求分析 1、系统定义 后台管理系统是一种用于管理网站、应用程序或系统的管理界面&#xff0c;通常由管理员和工作人员使用。它提供了访问和控制网站或应用程序后台功能的工具和界面&#xff0c;使其能够管理用户、内容、数据和其他各种功能。 2、功能需求 后台管理系…...

Redis高级特性详解:事务处理、发布订阅、持久化和集群

Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源的基于内存的数据结构存储系统&#xff0c;被广泛应用于缓存、队列、计数器等场景中。除了基本的键值存储功能外&#xff0c;Redis还提供了许多高级特性&#xff0c;包括事务处理、发布订阅、持久化和集群。在…...

nwjs做自动化测试

分别是2个常用的自动化测试化框架 GitHub - nwutils/nw-selenium-javascript-example: An example of end-to-end testing with Selenium for NW.js apps via JavaScript GitHub - nwutils/nw-puppeteer-example: An example of using NW.js via Puppeteer. 看习惯使用哪个&…...

【前端素材】推荐优质在线特殊品牌商城电商网页eStore平台模板(附源码)

一、需求分析 1、系统定义 在线特殊品牌商城是指一个通过互联网提供特定品牌或特殊类型商品购买服务的电子商务平台。这类商城专注于某个特定品牌、设计风格或商品类型&#xff0c;为顾客提供独特、专业的购物体验。 2、功能需求 在线特殊品牌商城是指一个通过互联网提供特…...

Redis之一: 简介及环境安装搭建

什么是NoSQL? NoSQL&#xff0c;指的是非关系型的数据库。NoSQL有时也称作Not Only SQL的缩写&#xff0c;是对不同于传统的关系型数据库的数据库管理系统的统称。 NoSQL用于超大规模数据的存储。&#xff08;例如谷歌或Facebook每天为他们的用户收集万亿比特的数据&#xf…...

关于电脑一天24小时多少度电电脑的一天用电量计算

随着这几年物价的上涨&#xff0c;一些地区的电价越来越高&#xff0c;而我们经常需要使用电脑&#xff0c;那么一台电脑一天24小时用多少度电呢&#xff1f; 如何计算电脑一天的用电量&#xff1f; 让我们跟随小编来了解更多吧。 1、功耗、主机箱功耗 现在的计算机中&#xf…...

Unity3D 物理引擎的基本配置详解

前言 在Unity3D中&#xff0c;物理引擎主要由两部分组成&#xff1a;碰撞检测和物理模拟。在本文中&#xff0c;我们将详细介绍Unity3D物理引擎的基本配置&#xff0c;并给出相应的技术详解和代码实现。 对惹&#xff0c;这里有一个游戏开发交流小组&#xff0c;希望大家可以…...

CSS:弹性盒子Flexible Box布局

CSS:Flexible Box弹性盒子布局 一、flex布局原理 ​ flex是flexible Box的缩写,意为 ”弹性布局“&#xff0c;用来为盒状模型提供最大的灵活性&#xff0c;任何一个容器都可以指定为flex布局。 当我们的父盒子设置为flex布局之后&#xff0c;子元素的 float 、clear 和 vert…...

java常用环境docker安装

配置目录 rocketmqredismysql不配置binlog配置binlog Nacoszookeeper 本文为精简安装&#xff0c;部分不带容器卷映射&#xff0c;仅供以学习使用。 rocketmq nameservice sudo docker run -d \ --privilegedtrue \ --name rmqnamesrv \ -p 9876:9876 \ -e "MAX_HEAP_SI…...

Code-Audit(代码审计)习题记录6-7

介绍&#xff1a; 自己懒得搭建靶场了&#xff0c;靶场地址是 GitHub - CHYbeta/Code-Audit-Challenges: Code-Audit-Challenges为了方便在公网练习&#xff0c;可以随地访问&#xff0c;本文所有的题目均来源于网站HSCSEC-Code Audit 6、习题6 题目内容如下&#xff1a; 源代…...

go 的使用总结

go的内存逃逸&#xff1f; go语言在编辑阶段通过逃逸分析把分配在栈上变量 分配到堆上去。 栈内存&#xff1a; 一段连续的内存&#xff0c;便于高效运行指令过程中的临时变量存储。 堆内存&#xff1a; 主要由垃圾回收器 回收没有被引用的指针。 逃逸分析&#xff1a;栈内…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南

&#x1f680; C extern 关键字深度解析&#xff1a;跨文件编程的终极指南 &#x1f4c5; 更新时间&#xff1a;2025年6月5日 &#x1f3f7;️ 标签&#xff1a;C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言&#x1f525;一、extern 是什么&#xff1f;&…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)

前言&#xff1a; 在Java编程中&#xff0c;类的生命周期是指类从被加载到内存中开始&#xff0c;到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期&#xff0c;让读者对此有深刻印象。 目录 ​…...

Java数值运算常见陷阱与规避方法

整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...

wpf在image控件上快速显示内存图像

wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像&#xff08;比如分辨率3000*3000的图像&#xff09;的办法&#xff0c;尤其是想把内存中的裸数据&#xff08;只有图像的数据&#xff0c;不包…...