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

按键输入驱动

目录

一、硬件原理

二、添加设备树

1、创建pinctrl

2、创建节点

3、检查

 编译复制

 三、修改工程模板​编辑

四、驱动编写

1、添加keyio函数 

 2、添加调用

 3、驱动出口函数添加释放

 4、添加原子操作

5、添加两个宏定义

 6、初始化原始变量

7、打开操作

 8、读操作

 总体代码如下

 五、应用编写

总体代码如下

验证


一、硬件原理

打开原理图找到KEY

 可以看到KEY0是接到UART1 CTS上

打开参考手册,找到IOMUXC_SW_MUX_CTL_PAD_UART1_CTS_B

看到是可以复用为GPIO1_IO18的

根据IOMUXC_SW_PAD_CTL_PAD_UART1_CTS_B配置的电气属性为0xF080,具体属性如下

配置UART1_CTS_B的IO属性    
     *bit 16:0 HYS关闭
     *bit [15:14]: 11 默认22K上拉
     *bit [13]: 1 pull功能
     *bit [12]: 1 pull/keeper使能
     *bit [11]: 0 关闭开路输出
     *bit [7:6]: 10 速度100Mhz
     *bit [5:3]: 000 关闭输出
     *bit [0]: 0 低转换率

二、添加设备树

1、创建pinctrl

先查找UART1_CTS对应的宏这个在arch/arm/boot/dts/imx6ul-pinfunc.h

 在&iomuxc里添加keygrp

2、创建节点

创建key节点

3、检查

 添加完之后在文件里分别搜索MX6UL_PAD_UART1_CTS_B__GPIO1_IO018和&gpio1 18检查一下是否有存在使用,避免占用

打开开发板检查创建是否存在

 编译复制

 三、修改工程模板

 修改makefile

复制基本的字符驱动模板到key.c

四、驱动编写

自行编写好基本的字符设备

1、添加keyio函数 

在驱动入口前添加 

96行,of_find_node_by_path 函数通过路径来查找指定的节点“key”,返回找到的节点

102行,of_get_named_gpio函数获取设备树中“key-gpios”属性信息,0为其第一个信息,获取到的 GPIO 编号

108行,gpio_request 函数,在使用 GPIO 之前申请GPIO 管脚,设名字“key0”,返回值0申请成功

114行,gpio_direction_input函数设置 GPIO 为输入,返回值0设置成功

 2、添加调用

在驱动入口函数里面后部添加

在驱动入口里面调用 keyio函数 

 3、驱动出口函数添加释放

 4、添加原子操作

5、添加两个宏定义

方便原子操作,值不是固定的 

 6、初始化原始变量

初始化keyvalue,把宏INVAKEY赋值给keyvalue,在驱动入口函数定义

7、打开操作

设置私有数据

 8、读操作

 61行,获取私有数据

63-67行, gpio_get_value函数返回值为 GPIO 的输入电平状态,1 为高电平,0 为低电平。如果按下不放就是1进入while等待释放,释放为0取反进入while中执行 atomic_set函数把KEY0VALUE赋值给keyvalue。否则直接执行 atomic_set函数,把宏INVAKEY赋值给keyvalue

69行,atomic_read函数读取keyvalue值赋给value

71行,copy_to_user函数把value的值拷贝给用户空间,成功执行拷贝操作,则返回0

 总体代码如下

#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_irq.h>
#include <linux/slab.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/atomic.h>#define KEY_CNT 1      /* 设备号个数 	*/
#define KEY_NAME "key" /* 名字 		*/#define INVAKEY 0x00
#define KEY0VALUE 0XF0/* key设备结构体 */
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;           /* key所使用的GPIO编号		*/atomic_t keyvalue;      /* 按键值 		*/
} key; /* key设备 *//** @description		: 打开设备* @param - inode 	: 传递给驱动的inode* @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量* 					  一般在open的时候将private_data指向设备结构体。* @return 			: 0 成功;其他 失败*/
static int key_open(struct inode *inode, struct file *filp)
{filp->private_data = &key;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 value;struct key_dev *dev = filp->private_data;int ret = 0;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;
}/** @description		: 向设备写数据* @param - filp 	: 设备文件,表示打开的文件描述符* @param - buf 	: 要写给设备写入的数据* @param - cnt 	: 要写入的数据长度* @param - offt 	: 相对于文件首地址的偏移* @return 			: 写入的字节数,如果为负值,表示写入失败*/
static ssize_t key_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{return 0;
}/** @description		: 关闭/释放设备* @param - filp 	: 要关闭的设备文件(文件描述符)* @return 			: 0 成功;其他 失败*/
static int key_release(struct inode *inode, struct file *filp)
{return 0;
}/* 设备操作函数 */
static struct file_operations key_fops = {.owner = THIS_MODULE,.open = key_open,.read = key_read,.write = key_write,.release = key_release,
};/*key io初始化*/
static int keyio_init(struct key_dev *dev)
{  int ret = 0;  /*初始化atomic*/atomic_set(&key.keyvalue,INVAKEY);dev->nd = of_find_node_by_path("/key");if (dev->nd == NULL){ret = -EINVAL;goto fail_nd;}dev->key_gpio = of_get_named_gpio(dev->nd, "key-gpios", 0);if (dev->key_gpio < 0){ret = -EINVAL;goto fail_gpio;}ret = gpio_request(dev->key_gpio, "key0");if (ret){ret = -EBUSY;printk(" IO %d can't request\r\n", dev->key_gpio);}ret = gpio_direction_input(dev->key_gpio);if (ret){ret = -EINVAL;goto fail_input;}return 0;
fail_input:gpio_free(dev->key_gpio);
fail_gpio:
fail_nd:device_destroy(key.class, key.devid);return ret;
}/** @description	: 驱动入口函数* @param 		: 无* @return 		: 无*/
static int __init key_init(void)
{int ret =0;/* 初始化原子变量 */atomic_set(&key.keyvalue,INVAKEY);/* 注册字符设备驱动 */key.major = 0;/* 1、创建设备号 */if (key.major){ /*  定义了设备号 */key.devid = MKDEV(key.major, 0);register_chrdev_region(key.devid, KEY_CNT, KEY_NAME);}else{                                                          /* 没有定义设备号 */alloc_chrdev_region(&key.devid, 0, KEY_CNT, KEY_NAME); /* 申请设备号 */key.major = MAJOR(key.devid);                          /* 获取分配号的主设备号 */key.minor = MINOR(key.devid);                          /* 获取分配号的次设备号 */}if(ret < 0){goto fail_devid;}printk("major = %d , minor = %d \r\n", key.major, key.minor);/* 2、初始化cdev */key.cdev.owner = THIS_MODULE;cdev_init(&key.cdev, &key_fops);/*初始化的 cdev 结构体变量*//* 3、添加一个cdev */cdev_add(&key.cdev, key.devid, KEY_CNT);/*添加字符设备*/if(ret){goto fail_cdevadd;}/* 4、创建类 */key.class = class_create(THIS_MODULE, KEY_NAME);if (IS_ERR(key.class)){ret = PTR_ERR(key.class);goto fail_class;}/* 5、创建设备 */key.device = device_create(key.class, NULL, key.devid, NULL, KEY_NAME);if (IS_ERR(key.device)){ret =  PTR_ERR(key.device);goto fail_device;}ret = keyio_init(&key);if (ret < 0){goto fail_device;}printk("key_init\r\n");return 0;
fail_device:class_destroy(key.class);
fail_class:cdev_del(&key.cdev);
fail_cdevadd:unregister_chrdev_region(key.devid,KEY_CNT);
fail_devid:return ret;
}/** @description	: 驱动出口函数* @param 		: 无* @return 		: 无*/
static void __exit key_exit(void)
{/* 注销字符设备驱动 */cdev_del(&key.cdev);/*  删除cdev */unregister_chrdev_region(key.devid, KEY_CNT); /* 释放设备号 */device_destroy(key.class, key.devid);/*摧毁设备*/class_destroy(key.class);/*删除类*/gpio_free(key.key_gpio);/*释放io*/
}module_init(key_init);
module_exit(key_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");

 五、应用编写

总体代码如下

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>#define INVAKEY 0x00
#define KEY0VALUE 0XF0/*argc:应用程序参数个数(argv数组元素个数)argv:具体参数,也可以写作char **argv./keyAPP <filename>    ./keyAPP  /dev/key 
*/
int main(int argc, char *argv[])
{int fd,retvalue;char *filename;unsigned char databuf[1];int value=0;/*判断命令行输入参数是否正确*/if(argc != 2){printf("error usage!\r\n");return -1;}/*用指针指向文件*/filename = argv[1];/*打开文件*/fd = open(filename , O_RDWR);if(fd < 0){printf("file open failed\r\n",filename);return -1;}/*循环读取*/while(1){read(fd,&value,sizeof(value));if(value == KEY0VALUE){printf("KEY0 press , value = %d\r\n",value);}}/*关闭文件*/close(fd);return 0;
}

循环读取处,用read读取内核拷贝的数据

验证

 

 可以看到按下按键就会打印value的值

这里埋下一个伏笔,运行测试APP会占用99%的CPU资源

 后续章节进行介绍

相关文章:

按键输入驱动

目录 一、硬件原理 二、添加设备树 1、创建pinctrl 2、创建节点 3、检查 编译复制 三、修改工程模板​编辑 四、驱动编写 1、添加keyio函数 2、添加调用 3、驱动出口函数添加释放 4、添加原子操作 5、添加两个宏定义 6、初始化原始变量 7、打开操作 8、读操作 总体代…...

2023年第七周总周结 | 开学倒数第三周

为什么要做周总结&#xff1f; 1.避免跳相似的坑 2.客观了解上周学习进度并反思&#xff0c;制定可完成的下周规划 一、上周问题解决情况 晚上熬夜导致第二天学习状态不好 这周熬夜一天&#xff0c;晚上帮亲戚修手机到22:30&#xff0c;可能是晚上自己的事什么都没做&#xff…...

Springboot扫描注解类

Springboot扫描注解类的入口在AbstractApplicationContext的refresh中&#xff0c;对启动步骤不太了解的&#xff0c;可参考https://blog.csdn.net/leadseczgw01/article/details/128930925BeanDefinitionRegistryPostProcessor接口有多个实现类&#xff0c;扫描Controller、Se…...

Apache日志分析器

您的Apache HTTP服务器生成的日志数据是信息的宝库。使用这些信息&#xff0c;您可以判断您服务器的使用情况、找出漏洞所在&#xff0c;并设法改进服务器结构和整体性能。审核您的Apache日志可在以下情况派上用场&#xff0c;其中包括&#xff1a;识别和纠正频繁出现的错误以增…...

啪,还敢抛出异常

&#x1f649; 作者简介&#xff1a; 全栈领域新星创作者 &#xff1b;天天被业务折腾得死去活来的同时依然保有对各项技术热忱的追求&#xff0c;把分享变成一种习惯&#xff0c;再小的帆也能远航。 &#x1f3e1; 个人主页&#xff1a;xiezhr的个人主页 前言 去年又重新刷了…...

Apache JMeter 5.5 下载安装以及设置中文教程

Apache JMeter 5.5 下载安装以及设置中文教程JMeter下载Apache JMeter 5.5配置环境变量查看配置JDK配置JMeter环境变量运行JMeter配置中文版一次性永久设置正文JMeter 下载Apache JMeter 5.5 官方网站&#xff1a;Apache JMeter 官网 版本介绍&#xff1a; 版本中一个是Bina…...

string类模拟实现

了解过string常用接口后&#xff0c;接下来的任务就是模拟实现string类。 目录 VS下的string结构 默认成员函数和简单接口 string结构 c_str()、size()、capacity()、clear()、swap() 构造函数 拷贝构造函数 赋值重载 析构函数 访问及遍历 容量操作 reserve resize …...

cadence SPB17.4 S032 - allegro - 保存/载入光绘层定义

文章目录cadence SPB17.4 S032 - allegro - 保存/载入光绘层定义概述保存光绘层在新板子中载入已经保存的相同类型老板子定义好的光绘层定义文件碎碎念ENDcadence SPB17.4 S032 - allegro - 保存/载入光绘层定义 概述 以前布线完成, 准备出板厂文件时, 总是要手工重新建立光绘…...

微服务实战--高级篇:分布式缓存 Redis

分布式缓存 – 基于Redis集群解决单机Redis存在的问题 单机的Redis存在四大问题&#xff1a; 1.Redis持久化 Redis有两种持久化方案&#xff1a; RDB持久化AOF持久化 1.1.RDB持久化 RDB全称Redis Database Backup file&#xff08;Redis数据备份文件&#xff09;&#xf…...

【C语言】可变参数列表

本篇博客让我们来认识一下C语言学习过程中往往被忽略的可变参数列表 所谓可变参数&#xff0c;就是一个不限定参数数量的函数&#xff0c;我们可以往里面传入任意个数的参数&#xff0c;以达成某些目的。 关联&#xff1a;C11可变模板参数&#xff1b;本文首发于 慕雪的寒舍 …...

目标检测的旋框框文献学习

这是最近打算看完的文献&#xff0c;一天一篇 接下来将记录一下文献阅读笔记&#xff0c;避免过两天就忘了 RRPN 论文题目&#xff1a;Arbitrary-Oriented Scene Text Detection via Rotation Proposals 论文题目&#xff1a;通过旋转方案进行任意方向的场景文本检测&#x…...

Hive 在工作中的调优总结

总结了一下在以往工作中&#xff0c;对于Hive SQL调优的一些实际应用&#xff0c;是日常积累的一些优化技巧&#xff0c;如有出入&#xff0c;欢迎在评论区留言探讨~ EXPLAIN 查看执行计划 建表优化 分区 分区表基本操作&#xff0c;partitioned二级分区动态分区 分桶 分…...

每天一道大厂SQL题【Day09】充值日志SQL实战

每天一道大厂SQL题【Day09】充值日志SQL实战 大家好&#xff0c;我是Maynor。相信大家和我一样&#xff0c;都有一个大厂梦&#xff0c;作为一名资深大数据选手&#xff0c;深知SQL重要性&#xff0c;接下来我准备用100天时间&#xff0c;基于大数据岗面试中的经典SQL题&#…...

MATLAB 遗传算法

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…...

探讨 Java 中 valueOf 和 parseInt 的区别

前言 在编程中&#xff0c;遇到类型转换&#xff0c;好像会经常用到 parseInt 和 valueOf&#xff0c;当然这里只拿 Integer 类型进行陈述&#xff0c;其他类型也是雷同的&#xff1b; 想必有读者也跟我一样&#xff0c;经常交叉使用这两个方法&#xff0c;但却不知道这两者到…...

JSON学习笔记

♥课程链接&#xff1a;【狂神说Java】一小时掌握JSON_哔哩哔哩_bilibili配套的当然还要学习ajax不管是前端后端&#xff0c;感觉这部分内容是必须的&#xff0c;不然真的做项目的时候云里雾里。总体json的内容不多&#xff0c;具体就&#xff1a;1. 列表、对象等语法格式2. js…...

家政服务小程序实战教程07-轮播图组件

小程序中首页一般显示轮播图的功能&#xff0c;点击轮播图会跳转到具体的一篇文章或者是产品&#xff0c;本篇我们就介绍一下轮播图功能的开发 01 设计数据源 我们轮播图组件需要两个字段&#xff0c;一个是展示的图片&#xff0c;一个是跳转页面传入的参数。打开数据源&…...

MySQL之索引创建、删除、唯一索引、普通索引、及命名规则、注意事项

一、MySQL 索引 定义 索引是一个数据结构&#xff0c;用于加速数据库表中数据的查询。索引存储了一些数据表中的列值&#xff0c;以及这些列值在数据表中的位置&#xff0c;这样就可以通过索引来快速查找到数据表中的某一行数据。 MySQL 支持多种索引类型&#xff0c;包括普通…...

【C++设计模式】学习笔记(3):策略模式 Strategy

目录 简介动机(Motivation)模式定义结构(Structure)要点总结笔记结语简介 Hello! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出~ ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简介:因C语言结识编程,随后转入计算机专业,获得过国家奖学金…...

Java——聊聊JUC中的ThreadLocal

文章目录&#xff1a; 1.什么是ThreadLocal&#xff1f; 1.1 api介绍 1.2 最简单的案例认识ThreadLocal 1.3 线程池结合ThreadLocal案例 2.Thread &ThreadLocal & ThreadLocalMap 3.ThreadLocal内存泄漏问题 3.1 四大引用之强引用 3.2 四大引用之软引用 3.3 四…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

【C++进阶篇】智能指针

C内存管理终极指南&#xff1a;智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...