设备树配置

	key{compatible = "alientek,key";pinctrl-0 = <&key_gpio>;pinctrl-names = "alientek,key";key-gpio = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>;status = "okay";};

配置信息方便后面直接引用:

	// Narnat 2025-6-14key-gpios{/omit-if-no-ref/key_gpio: key-pin {rockchip,pins=<3 RK_PC5 RK_FUNC_GPIO &pcfg_pull_none>;};};

在这里插入图片描述
注册节点,将信息写入设备,方便后续驱动读取设备

驱动代码

#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/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;             // cdevstruct class* class;         // 类struct device* device;       // 设备int major;                  // 主设备号int minor;                 // 次设备号struct device_node* nd;   // 设备节点int key_gpio;            // key所使用的gpio编号atomic_t  keyvalue;     // 按键值
};static struct key_dev keydev;  // 按键值static int keyio_init(void){int ret;const char* str;/*设置LED所使用GPIO*/// 1.获取节点keydevkeydev.nd = of_find_node_by_path("/key");if(keydev.nd == NULL){printk("keydev node not find! \r\n");return -1;}// 2.读取status属性ret = of_property_read_string(keydev.nd, "status", &str);if(ret < 0) return -1;if(strcmp(str, "okay")) return -1;// 3.获取compatible属性并对比ret = of_property_read_string(keydev.nd, "compatible", &str);if(ret < 0){printk("failed to get compatible\n");return -1;}if(strcmp(str, "alientek,key")){printk("compatible match failed\n");return -1;}// 4.获取gpio属性,得到kEY编号keydev.key_gpio = of_get_named_gpio(keydev.nd, "key-gpio", 0);if(keydev.key_gpio < 0){printk("cant get key-gpio \n");return -1;}printk("key-gpio num= %d \n", keydev.key_gpio);// 5.向gpio子系统申请使用gpioret = gpio_request(keydev.key_gpio, "KEY0");if(ret){printk("failed to request key-gpio \n");return ret;}// 6.设置gpio输入模式ret = gpio_direction_input(keydev.key_gpio);if(ret < 0){printk("cant set gpio \n");return ret;}return 0;
}static int key_open(struct inode* inode, struct file* filp){int ret = 0;filp->private_data = &keydev;ret = keyio_init();if(ret < 0) return ret;return 0;
}static ssize_t key_read(struct file* filp, char __user* buf, size_t cnt, loff_t* offt){int ret = 0;int value;struct key_dev* dev = filp->private_data;if(gpio_get_value(dev->key_gpio) == 1){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 ssize_t key_write(struct file* filp, const char __user* buf, size_t cnt, loff_t* offt){return 0;
}static int key_release(struct inode* inode, struct file* filp){struct key_dev* dev = filp->private_data;gpio_free(dev->key_gpio);return 0;
}
// 设备操作函数
static struct file_operations key_fops = {.owner = THIS_MODULE,.open = key_open,.read = key_read,.write = key_write,.release = key_release,
};/* 驱动入口 */
static int __init mykey_init(void){int ret;// 1.初始化原子变量keydev.keyvalue = (atomic_t)ATOMIC_INIT(0);// 2.初始值设置为INVAKEYatomic_set(&keydev.keyvalue, INVAKEY);/*注册字符驱动*/// 1.创建设备号if(keydev.major){ // 定义了设备号keydev.devid = MKDEV(keydev.major, 0);ret = register_chrdev_region(keydev.devid, KEY_CNT, KEY_NAME);if(ret < 0){printk("cannt register %s char driver \n", KEY_NAME);return -1;}}else{ // 未定义设备号ret = alloc_chrdev_region(&keydev.devid, 0, KEY_CNT, KEY_NAME); // 申请设备号if(ret < 0){printk("%s couldnot alloc_chrdev_region \n", KEY_NAME);return -1;}keydev.major = MAJOR(keydev.devid); // 分配主设备号keydev.minor = MINOR(keydev.devid); // 分配次设备}printk("keydev major=%d, minor=%d \r\n", keydev.major, keydev.minor);// 2.初始化cdevkeydev.cdev.owner = THIS_MODULE;cdev_init(&keydev.cdev, &key_fops);// 3.添加一个cdevret = cdev_add(&keydev.cdev, keydev.devid, KEY_CNT);if(ret < 0) goto del_unregister;// 4.创建类keydev.class = class_create(THIS_MODULE, KEY_NAME);if(IS_ERR(keydev.class)) goto del_cdev;// 5.创建设备keydev.device = device_create(keydev.class, NULL, keydev.devid, NULL, KEY_NAME);if(IS_ERR(keydev.device)) goto destroy_class; return 0;
destroy_class:device_destroy(keydev.class, keydev.devid);
del_cdev:cdev_del(&keydev.cdev);  
del_unregister:unregister_chrdev_region(keydev.devid, KEY_CNT);return -1;
}/* 驱动出口 */
static void __exit mykey_exit(void){// 注销字符设备驱动cdev_del(&keydev.cdev); // 删除cdevunregister_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("Narnat");

补全read函数,应用层调用read命令后读取到驱动发送的数据

应用层:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名		: keyApp.c
作者	  	: 正点原子Linux团队
版本	   	: V1.0
描述	   	: 按键输入测试应用程序
其他	   	: 无
使用方法	 :./keyApp /dev/key  
论坛 	   	: www.openedv.com
日志	   	: 初版V1.0 2021/01/5 正点原子Linux团队创建
***************************************************************//* 定义按键值 */
#define KEY0VALUE	0XF0
#define INVAKEY		0X00/** @description		: main主程序* @param - argc 	: argv数组元素个数* @param - argv 	: 具体参数* @return 			: 0 成功;其他 失败*/
int main(int argc, char *argv[])
{int fd, ret;char *filename;int keyvalue;if(argc != 2){printf("Error Usage!\r\n");return -1;}filename = argv[1];/* 打开key驱动 */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) {	/* KEY0 */printf("KEY0 Press, value = %#X\r\n", keyvalue);	/* 按下 */}}ret= close(fd); /* 关闭文件 */if(ret < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0;
}

效果:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

参展回顾 | AI应用创新场景:数据分析助手ChatBI、璞公英教学平台亮相2025四川国际职教大会暨产教融合博览会

2025年6月11日-13日&#xff0c;以“数字赋能产教融合&#xff0c;创新驱动技能未来”为主题的2025四川国际职业教育大会暨产教融合博览会在成都盛大开幕。璞华联合百度共同参展&#xff0c;并携旗下创新产品ChatBI数据分析助手、璞公英教学平台重磅亮相&#xff0c;凭借前沿的…

动态规划之01背包问题

动态规划算法 动态规划算法介绍 动态规划(Dynamic Programming)算法的核心思想是&#xff1a;将大问题划分为小问题进行解决&#xff0c;从而一步步获取最优解的处理算法动态规划算法与分治法类似&#xff0c;其基本思想也是将待解决问题分解成若干个子问题&#xff0c;先求解…

人大金仓新建用户,并且赋值查询权限

-- 1. 创建用户 visitor&#xff0c;并且设置密码 CREATE USER visitor WITH PASSWORD 1234qwer; -- 2. 授予该用户连接到数据库 "yonbip_db" 的权限 GRANT CONNECT ON DATABASE yonbip_db TO visitor; -- 3. 假设你要让 visitor 查询的模式是 public&#xff08;或…

学习笔记丨信号处理新趋势:量子计算将如何颠覆传统DSP?

在算力需求爆炸式增长的今天&#xff0c;传统数字信号处理&#xff08;DSP&#xff09;芯片正面临物理极限的严峻挑战。当经典计算机架构在摩尔定律的黄昏中挣扎时&#xff0c;量子计算正以颠覆性姿态崛起&#xff0c;准备重新定义信号处理的未来图景。 目录 传统DSP的瓶颈&am…

react day.js使用及经典场景

简介 Day.js 是一个轻量级的 JavaScript 日期库&#xff0c;它提供了简单易用的 API 来处理日期和时间。以及更加轻量级&#xff0c;并且具有更快的性能。 安装 npm install dayjs 使用 import dayjs from "dayjs";dayjs().format("YYYY-MM-DD HH:mm:ss&qu…

【机器学习深度学习】线性回归

目录 一、定义 二、举例说明 三、 数学形式 四、 训练过程&#xff08;机器怎么学会这条线&#xff1f;&#xff09; 五、在 PyTorch 中怎么实现线性回归&#xff1f; 六、如果你学懂了线性回归&#xff0c;你也能理解这些 七、综合应用&#xff1a;线性回归示例 7.1 执…

如何在 Manjaro Linux 上安装 .NET Core

.NET 是一个开源的开发框架平台,可在所有流行的操作系统(如 Windows、Linux 和 macOS)上免费使用和安装。它是跨平台的,是主要由微软员工在 .NET 基金会下开发的专有 .NET Framework 的继承者。.NET 是一个统一的平台,用于开发各种操作系统上的软件,如 Web、移动、桌面应…

Mysql解惑(一)

使用 or 可能不走索引 使用 union替代 使用in&#xff0c;可能不走索引 如果优化&#xff1a; 临时表强制索引exists代替

基于机器学习的侧信道分析(MLSCA)Python实现(带测试)

一、MLSCA原理介绍 基于机器学习的侧信道分析(MLSCA)是一种结合传统侧信道分析技术与现代机器学习算法的密码分析方法。该方法通过分析密码设备运行时的物理泄漏信息(如功耗、电磁辐射等)&#xff0c;利用机器学习模型建立泄漏数据与密钥信息之间的关联模型&#xff0c;从而实…

【LLM】位置编码

【LLM】位置编码 1 绝对位置嵌入为什么用 1000 0 2 t d 10000^{\frac{2t}{d}} 10000d2t​? 2 相对位置嵌入2.1 Shaw等人的方法&#xff08;2018&#xff09;2.2 Dai等人的方法&#xff08;2019&#xff09;2.3 Raffel 等人的方法&#xff08;2020&#xff09;2.4 He 等人的方法…

Java 根据分组key构建合并数据集

文章目录 前言背景总结 前言 请各大网友尊重本人原创知识分享&#xff0c;谨记本人博客&#xff1a;南国以南i、 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 背景 Java 需要返回一组数据供前端展示&#xff0c;获取到的数据格式如下&#xff1a; …

Linux平台Oracle开机自启动设置

网上和官方文档已经有不少介绍如何设置开机启动Oracle实例的文章(Linux平台)&#xff0c;不过以sysvinit和service这种方式居多。最近遇到了UAT环境的服务器打补丁后需要重启服务器的情况&#xff0c; 需要DBA去手工启动Oracle实例的情形&#xff0c;和同事讨论&#xff0c;决定…

商品中心—商品B端搜索系统的实现文档(二)

8.步骤四&#xff1a;基于索引实现搜索功能 (1)基于suggest索引的自动补全实现 实现自动补全的代码比较简单&#xff0c;其原理是&#xff1a;把搜索词汇和倒排索引里的所有前缀匹配的词条进行score比较&#xff0c;然后把分数最高的那些返回&#xff0c;其中会涉及到suggest索…

Codeforces Round 1027 (Div. 3)

A. Square Year 题目大意 给你一个四个字符的字符串&#xff0c;代表一个数字s 问是否存在a,b两个数字&#xff0c;使得 ( a b ) 2 s (ab)^2s (ab)2s 思路 如果s是奇数或不能被开根号一定不行 设sq为s开根号后的结果 将sq一分为2&#xff0c;考虑sq/2有没有余数的情况 //…

时序数据库IoTDB的架构、安装启动方法与数据模式总结

一、IoTDB的架构 IoTDB的架构主要分为三个部分&#xff1a; ‌时序文件&#xff08;Tsfile&#xff09;‌&#xff1a; 专为时序数据设计的文件存储格式。支持高效的压缩和查询性能。可独立使用&#xff0c;并可通过TsFileSync工具同步至HDFS进行大数据处理。 ‌数据库引擎‌…

ArrayList和LinkedList详解

在Java后端开发中&#xff0c;集合框架是我们日常编程不可或缺的工具&#xff0c;它为数据存储和操作提供了丰富的实现方式。作为Java集合框架中最常用的两种List实现&#xff0c;ArrayList和LinkedList各自具有独特的特性和适用场景。 1. 基本概念 1.1 ArrayList的定义与特性…

警惕微软Entra ID风险:访客账户存在隐蔽的权限提升策略

访客用户订阅权限漏洞解析 微软Entra ID的订阅管理存在访问控制缺陷&#xff0c;允许访客用户在受邀租户中创建和转移订阅&#xff0c;同时保留对这些订阅的完全所有权。访客用户只需具备在源租户创建订阅的权限&#xff0c;以及受邀成为外部租户访客的身份即可实施此操作。这…

EEG分类攻略2-Welch 周期图

在EEG信号处理的上下文中&#xff0c;使用Welch方法来估算信号的功率谱密度&#xff08;Power Spectral Density, PSD&#xff09;是一种常见的做法。你的代码片段是利用**scipy.signal.welch**函数来进行功率谱密度估算&#xff0c;并且涉及到一些关键的参数和步骤。让我们逐步…

开疆智能CCLinkIE转ModbusTCP网关连接脉冲计数器配置案例

本案例是三菱PLC通过CCLinkIE转ModbusTCP网关连接脉冲计数器的配置案例&#xff0c;具体配置如下。 配置过程&#xff1a; 首先设置从站通讯参数 主要设置IP地址&#xff0c;工作模式以及端口号&#xff08;Modbus默认502&#xff09; 找到通讯点表&#xff0c;找到需要读写的…

gRPC 使用(python 版本)

.proto 文件 .proto 文件 是 gRPC 和 Protocol Buffers 的接口定义文件&#xff0c;它描述了&#xff1a; 要传递什么数据&#xff08;也就是消息体 message&#xff09;。要暴露什么接口&#xff08;也就是服务 service 和它们的 方法&#xff09;。 也就是一份规范文件&am…