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

Linux驱动入门(6.2)按键驱动和LED驱动 --- 将逻辑电平与物理电平分离

前言

(1)在学习完Linux驱动入门(6)LED驱动—设备树之后,我们发现一个问题,设备树明明的gpios信息明明有三个元素gpios = <&gpio5 3 GPIO_ACTIVE_LOW>; &gpio5 3 用来确定控制那个引脚,而GPIO_ACTIVE_LOW究竟有什么用呢?
(2)通过前面的实验,我们发现,GPIO_ACTIVE_LOW似乎是没有使用上的。那么写上这个有什么用呢?
(3)Linux设备树中既然设置了这个元素,那么肯定是有意义的。接下来,我将讲解Linux中逻辑电平和物理电平之间的关系。

逻辑电平的意义

为什么需要逻辑电平

(1)在前面的代码里面,我们发现如果这个LED驱动硬件发生了改动,比如GND和VCC位置调整一下,代码就要进行比较多的改动。因为Linux的代码很多,很容易漏掉某个地方,导致硬件上的小改动,明明写好的软件又要做很多工作,调试,检查。
(2)Linux要与硬件进行强隔离,所以提出了逻辑电平的概念。

在这里插入图片描述

逻辑电平和物理电平的关系

(1)物理电平就是真实的电压值,物理电平1是指真实的高电平。例如TTL标准中,如果引脚接受到的是3.3V,那么就是1。如果引脚接受到的是0V,那么就是0。
(2)逻辑电平就是抽象出来的逻辑状态,逻辑电平的1是指有效电平。例如,上面左边的图,按键被按下,引脚为低电平,因此低电平是有效电平,对于这个按键的逻辑电平来说,1就是低电平。而右边这张图相反。

引入逻辑电平的好处

(1)引入逻辑电平之后,如果硬件只是有效电平发生了改变,驱动程序上就不再需要改动了。我们只需要将设备树中的gpios = <&gpio5 3 GPIO_ACTIVE_LOW>;改成gpios = <&gpio5 3 GPIO_ACTIVE_HIGH>;即可。
(2)这样做,能够实现软件和硬件上的强隔离作用,让同一个驱动程序对于各种类似的硬件上有更高适配能力。

编程中与上文的区别

(1)在讲解Linux驱动入门(4)LED驱动的时候,我提及了几个GPIO子系统函数进行了讲解介绍。而本文需要讲解的函数,其本质上也不过是这个几个函数的微调,底层调用的函数大差不差,感兴趣的可以自行阅读源码。既然只是微调,为什么不把老版本的删了?这当然是为了兼容老版本代码,所以没有删除他们。

of_get_gpio_flags()获取GPIO信息

函数介绍

(1)在上文中,我们获取gpio引脚号是调用的of_get_gpio()这个函数,而本文是使用的of_get_gpio_flags()函数。
(2)如果阅读源码会发现,of_get_gpio()就是调用的of_get_gpio_flags()函数,不过第三个参数是传入的一个空指针,并没有获取有效电平信息。

在这里插入图片描述

/* 作用 : 从设备树中获取GPIO引脚的标志信息的函数* 传入参数 :* np :设备节点* index : 节点中的索引* flags : 存储有效电平的信息* 返回值 : GPIO的引脚号
*/
int of_get_gpio_flags(struct device_node *np, int index, enum of_gpio_flags *flags)

原来获取GPIO信息

gpios[i].gpio = of_get_gpio(np, i);   //获得gpio信息

现在获取GPIO信息

gpios[i].gpio = of_get_gpio_flags(np, i, &flag);

devm_gpio_request_one()设置引脚初始化

函数介绍

(1)在上文中,我们是调用gpio_request()函数申请到GPIO,然后使用gpio_direction_output()函数将引脚设置成输出。
(2)现在这里只需要调用一个devm_gpio_request_one()函数即可。
(3)虽然这里调用的函数少了,但是需要进行的操作也变多了。

/* 作用 : 从设备树中获取GPIO引脚的标志信息的函数* 传入参数 :* dev :要申请GPIO的设备* gpio : 引脚号* flags : 有效电平,引脚的输入输出方向,默认输出电平(物理电平)信息* label : 注册GPIO时候的名字* 返回值 : 如果返回值小于0,表示申请失败
*/
int devm_gpio_request_one(struct device *dev, unsigned gpio,unsigned long flags, const char *label)

原来设置GPIO

/*------------GPIO设置成默认低电平输出------------*/
//申请指定GPIO引脚,申请的时候需要用到名字
err = gpio_request(gpios[i].gpio, gpios[i].name);
//如果返回值小于0,表示申请失败
if (err < 0) 
{//如果GPIO申请失败,打印出是哪个LED申请出现问题printk("can not request gpio %s \n", gpios[i].name);return -ENODEV;
}
//如果GPIO申请成功,设置输出低电平
gpio_direction_output(gpios[i].gpio, 0);
/*------------GPIO设置成默认高电平输出------------*/
//申请指定GPIO引脚,申请的时候需要用到名字
err = gpio_request(gpios[i].gpio, gpios[i].name);
//如果返回值小于0,表示申请失败
if (err < 0) 
{//如果GPIO申请失败,打印出是哪个LED申请出现问题printk("can not request gpio %s \n", gpios[i].name);return -ENODEV;
}
//如果GPIO申请成功,设置输出高电平
gpio_direction_output(gpios[i].gpio, 1);
/*------------GPIO设置成输入------------*/
//申请指定GPIO引脚,申请的时候需要用到名字
err = gpio_request(gpios[i].gpio, gpios[i].name);
//如果返回值小于0,表示申请失败
if (err < 0) 
{//如果GPIO申请失败,打印出是哪个LED申请出现问题printk("can not request gpio %s \n", gpios[i].name);return -ENODEV;
}
//如果GPIO申请成功,设置为输入引脚
gpio_direction_input(gpios[i].gpio);

现在设置GPIO

/*------------GPIO设置成默认低电平输出(注意,这里是物理电平)------------*/
gpios[i].flag = GPIOF_OUT_INIT_LOW;  //将GPIO设置成默认低电平输出(注意,这里是物理电平)
if (flag & OF_GPIO_ACTIVE_LOW) //判断有效电平是否为低电平
{gpios[i].flag |= GPIOF_ACTIVE_LOW;
}
printk("gpios[%d].flag is %d \r\n",i,gpios[i].flag); 
err = devm_gpio_request_one(&pdev->dev, gpios[i].gpio, gpios[i].flag, gpios[i].name);
//如果返回值小于0,表示申请失败
if (err < 0) 
{//如果GPIO申请失败,打印出是哪个LED申请出现问题printk("can not request gpio %s \n", gpios[i].name);return -ENODEV;
}
/*------------GPIO设置成默认高电平输出(注意,这里是物理电平)------------*/
gpios[i].flag = GPIOF_OUT_INIT_HIGH;  //将GPIO设置成默认高电平输出(注意,这里是物理电平)
if (flag & OF_GPIO_ACTIVE_LOW) //判断有效电平是否为低电平
{gpios[i].flag |= GPIOF_ACTIVE_LOW;
}
printk("gpios[%d].flag is %d \r\n",i,gpios[i].flag); 
err = devm_gpio_request_one(&pdev->dev, gpios[i].gpio, gpios[i].flag, gpios[i].name);
//如果返回值小于0,表示申请失败
if (err < 0) 
{//如果GPIO申请失败,打印出是哪个LED申请出现问题printk("can not request gpio %s \n", gpios[i].name);return -ENODEV;
}
/*------------GPIO设置成输入------------*/
gpios[i].flag = GPIOF_IN;  //将引脚设置成输入方向
if (flag & OF_GPIO_ACTIVE_LOW)  //判断有效电平是否为低电平
{gpios[i].flag |= GPIOF_ACTIVE_LOW;
}
printk("gpios[%d].flag is %d \r\n",i,gpios[i].flag); 
err = devm_gpio_request_one(&pdev->dev, gpios[i].gpio, gpios[i].flag, gpios[i].name);
//如果返回值小于0,表示申请失败
if (err < 0) 
{//如果GPIO申请失败,打印出是哪个LED申请出现问题printk("can not request gpio %s \n", gpios[i].name);return -ENODEV;
}

gpiod_set_value()设置引脚输出电平(逻辑电平)

函数介绍

(1)上文我们调用gpio_set_value()函数是设置的物理电平,而本文将会使用gpiod_set_value()函数设置逻辑电平。
(2)感兴趣的朋友可以看看gpio_set_value()函数和gpiod_set_value()函数的底层实现,我们会发现他们都调用了gpiod_set_raw_value()函数。只不过gpio_set_value()是直接将自己的参数传递进去,而gpiod_set_value()函数会判断有效电平信息,然后根据有效电平信息翻转Value值。
(3)这里需要注意的一点是,gpiod_set_value()函数第一参数传入的是gpio_desc结构体类型指针,而gpio_set_value()传入的是引脚号。我们可以调用gpio_to_desc()函数利用引脚号获得gpio_desc结构体类型指针。

在这里插入图片描述

原来设置GPIO输出电平(物理电平)

/* gpios[(int)tmp_buf[0]].gpio是引脚号* tmp_buf[1]是要设置的物理电平信息
*/
gpio_set_value(gpios[(int)tmp_buf[0]].gpio, tmp_buf[1]);

现在设置GPIO输出电平(逻辑电平)

(1)因为gpiod_set_value()函数第一个参数需要传入的是一个gpio_desc结构体。所以我在probe函数中获取,并且存入gpios结构体中。
(2)这里需要注意了,我们现在设置的是逻辑电平了。如果你的LED需要低电平点亮,你在设备树中设置了有效电平是低电平。那么现在gpiod_set_value()函数传入的第二个值,如果是1,输出的其实是低电平!!!

/*---- 在probe函数中我们使用了gpio_to_desc函数获得gpio_desc结构体 */
gpios[i].gpiod = gpio_to_desc(gpios[i].gpio);
/*---- 下面这段是在驱动程序中write函数中修改 */
/* gpios[(int)tmp_buf[0]].gpio是引脚号* tmp_buf[1]是要设置的物理电平信息
*/
gpiod_set_value(gpios[(int)tmp_buf[0]].gpiod, tmp_buf[1]);

需要注意gpio_desc结构体无法被访问

(1)这里有一个注意的点,gpio_desc结构体是Linux内核结构体。他与我们所编写的C文件编译环境是隔离的。所以我们没有访问gpio_desc结构体权限。这个问题卡了我很久,望各位了解。
(2)如果有头铁的兄弟说,哎,我就是牛逼,我就要访问,怎么滴?可以的,我也给头铁的兄弟们提供思路。

// 在对于的内核文件中写入下面这个宏,然后重新编译Linux内核。
EXPORT_SYMBOL(gpio_to_desc);

gpiod_get_value()获得引脚电平(逻辑电平)

函数介绍

(1)同样的道理,我们阅读gpiod_get_value()和gpio_get_value()函数源码会发现,他们都会调用gpiod_get_raw_value()函数获取真实的物理电平。
(2)但是,gpiod_get_value()会根据设备树中设置的有效电平,翻转gpiod_get_raw_value()函数返回值。而gpio_get_value()函数则是直接将gpiod_get_raw_value()函数返回值输出。
(3)因此gpiod_get_value()返回的是逻辑电平,gpio_get_value()返回的是物理电平。

在这里插入图片描述

原来获取GPIO电平(物理电平)

tmp_buf[1] = gpio_get_value(gpios[(int)tmp_buf[0]].gpio);

现在获取GPIO电平(逻辑电平)

(1)同输出电平一样,如果设备树中,LED驱动设置的有效电平是低电平。那么我们调用gpiod_get_value()函数发现LED的物理电平是高电平的时候,他返回的却是0!

tmp_buf[1] = gpiod_get_value(gpios[(int)tmp_buf[0]].gpiod);

总结

(1)将逻辑电平和物理电平隔离之后,驱动文件不再需要修改。如果硬件产生了更换,也只需要修改设备树。
(2)编写应用程序的程序员,也不需要管LED点亮到底是高电平还是低电平,在他眼里,输入1就是点亮,输入0就是熄灭。

相关文章:

Linux驱动入门(6.2)按键驱动和LED驱动 --- 将逻辑电平与物理电平分离

前言 &#xff08;1&#xff09;在学习完Linux驱动入门&#xff08;6&#xff09;LED驱动—设备树之后&#xff0c;我们发现一个问题&#xff0c;设备树明明的gpios信息明明有三个元素gpios <&gpio5 3 GPIO_ACTIVE_LOW>; &gpio5 3 用来确定控制那个引脚&#xf…...

CentOS系统环境搭建(十四)——CentOS7.9安装elasticsearch-head

centos系统环境搭建专栏&#x1f517;点击跳转 关于node的安装请看上一篇CentOS系统环境搭建&#xff08;十三&#xff09;——CentOS7安装nvm&#xff0c;&#x1f517;点击跳转。 CentOS7.9安装elasticsearch-head 文章目录 CentOS7.9安装elasticsearch-head1.下载2.解压3.修…...

设计HTML5图像和多媒体

在网页中的文本信息直观、明了&#xff0c;而多媒体信息更富内涵和视觉冲击力。恰当使用不同类型的多媒体可以展示个性&#xff0c;突出重点&#xff0c;吸引用户。在HTML5之前&#xff0c;需要借助插件为网页添加多媒体&#xff0c;如Adobe Flash Player、苹果的QuickTime等。…...

基于YOLOv8模型和Caltech数据集的行人检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要 基于YOLOv8模型和Caltech数据集的行人检测系统可用于日常生活中检测与定位行人&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的行人目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检测算法训练数据集…...

Flutter 宽高自适应

在Flutter开发中也需要宽高自适应&#xff0c;手动写一个工具类&#xff0c;集成之后在像素后面直接使用 px或者 rpx即可。 工具类代码如下&#xff1a; import dart:ui;class HYSizeFit {static double screenWidth 0.0;static double screenHeight 0.0;static double phys…...

LeetCode 0833. 字符串中的查找与替换

【LetMeFly】833.字符串中的查找与替换 力扣题目链接&#xff1a;https://leetcode.cn/problems/find-and-replace-in-string/ 你会得到一个字符串 s (索引从 0 开始)&#xff0c;你必须对它执行 k 个替换操作。替换操作以三个长度均为 k 的并行数组给出&#xff1a;indices,…...

Redis对象和五种常用数据类型

Redisobject 对象 对象分为键对象和值对象 键对象一般是string类型 值对象可以是string&#xff0c;list&#xff0c;set,zset,hash q&#xff1a;redisobj的结构 typedef struct redisObject { //类型 unsigned type:4; //编码 unsigned encoding:4; //指向底层实现…...

常用的Elasticsearch查询DSL

1.基本查询 GET /index_name/_search {"query": {"match": {"dispatchClass": "1"}} }2.多条件查询 GET /index_name/_search {"query": {"bool": {"must": [{"match": {"createUser&…...

计算机网络笔记

TCP有连接可靠服务 TCP特点&#xff1a; 1.TCP是面向连接的传输层协议&#xff1b; 2.每条TCP连接只能有两个端点&#xff0c;每条TCP连接是一对一的&#xff1b; 3.TCP提供可靠交付&#xff0c;保证传送数据无差错&#xff0c;不丢失&#xff0c;不重复且有序&#xff1b; 4.…...

高效反编译luac文件

对于游戏开发人员,有时候希望从一些游戏apk中反编译出源代码,进行学习,但是如果你触碰到法律边缘,那么你要非常小心。 这篇文章,我针对一些用lua写客户端或者服务器的编译过的luac文件进行反编译,获取其源代码的过程。 这里我不赘述如何反编译解压apk包的过程了,只说重点…...

密码湘军,融合创新!麒麟信安参展2023商用密码大会,铸牢数据安全坚固堡垒

2023年8月9日至11日&#xff0c;商用密码大会在郑州国际会展中心正式开幕。本次大会由国家密码管理局指导&#xff0c;中国密码学会支持&#xff0c;郑州市人民政府、河南省密码管理局主办&#xff0c;以“密码赋能美好发展”为主题&#xff0c;旨在推进商用密码创新驱动、前沿…...

关于视频监控平台EasyCVR视频汇聚平台建设“明厨亮灶”具体实施方案以及应用

一、方案背景 近几年来&#xff0c;餐饮行业的食品安全、食品卫生等新闻频频发生&#xff0c;比如某火锅店、某网红奶茶&#xff0c;食材以次充好、后厨卫生被爆堪忧&#xff0c;种种问题引起大众关注和热议。这些负面新闻不仅让餐饮门店的品牌口碑暴跌&#xff0c;附带的连锁…...

区块链系统探索之路:私钥的压缩和WIF格式详解

在前面章节中&#xff0c;我们详细介绍了公钥的压缩&#xff0c;在比特币网络中&#xff0c;一个私钥可以对应两个地址&#xff0c;一个地址是由未压缩公钥所生成的地址&#xff0c;另一个就是由压缩公钥所创建的地址&#xff0c;从公钥到区块链地址的转换算法&#xff0c;我们…...

DiffusionDet: Diffusion Model for Object Detection

DiffusionDet: Diffusion Model for Object Detection 论文概述不同之处整体流程 论文题目&#xff1a;DiffusionDet: Diffusion Model for Object Detection 论文来源&#xff1a;arXiv preprint 2022 论文地址&#xff1a;https://arxiv.org/abs/2211.09788 论文代码&#xf…...

CH01_重构、第一个示例

概述 在这一章节&#xff0c;作者给出了一个戏剧演出团售票的示例&#xff1a;剧目有悲剧&#xff08;tragedy&#xff09;和喜剧&#xff08;comedy&#xff09;&#xff1b;为了卖出更多的票&#xff0c;剧团则更具观众的数量来为下次演出打折扣&#xff08;大致意思是这次的…...

学习篇之React Fiber概念及原理

什么是React Fibber&#xff1f; React Fiber 是 React 框架的一种底层架构&#xff0c;为了改进 React 的渲染引擎&#xff0c;使其更加高效、灵活和可扩展。 传统上&#xff0c;React 使用一种称为堆栈调和递归算法来处理虚拟 DOM 的更新&#xff0c;这种方法在大型应用或者…...

商城-学习整理-高级-全文检索-ES(九)

目录 一、ES简介1、网址2、基本概念1、Index&#xff08;索引&#xff09;2、Type&#xff08;类型&#xff09;3、Document&#xff08;文档&#xff09;4、倒排索引机制4.1 正向索引和倒排索引4.2 正向索引4.3 倒排索引 3、相关软件及下载地址3.1 Kibana简介3.2 logstash简介…...

无人机跟随一维高度避障场景--逻辑分析

无人机跟随一维高度避障场景--逻辑分析 1. 源由2. 视频3. 问题3.1 思维发散3.2 问题收敛 4. 图示4.1 水平模式4.2 下坡模式4.3 上坡模式4.4 碰撞分析 5. 总结5.1 一维高度避障场景5.2 业界跟随产品5.3 APM集成跟随 6. 参考资料7. 补充资料 - 大疆智能跟随7.1 炸机7.2 成功 1. 源…...

Android Studio Giraffe控制台乱码

这几天在使用Android Studio Giraffe进行一个App的开发&#xff0c;在项目构建的时候&#xff0c;控制台输出中文都是乱码&#xff0c;看着很不爽&#xff0c;进行了两项配置&#xff0c;中文就可以正常输出了&#xff0c;看起来就爽多了。 第一个配置&#xff1a;点击Help菜单…...

云原生 envoy xDS 动态配置 java控制平面开发 支持restful grpc实现 EDS 动态endpoint配置

envoy xDS 动态配置 java控制平面开发 支持restful grpc 动态endpoint配置 大纲 基础概念Envoy 动态配置API配置方式动静结合的配置方式纯动态配置方式实战 基础概念 Envoy 的强大功能之一是支持动态配置&#xff0c;当使用动态配置时&#xff0c;我们不需要重新启动 Envoy…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?

uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件&#xff0c;用于在原生应用中加载 HTML 页面&#xff1a; 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

智能AI电话机器人系统的识别能力现状与发展水平

一、引言 随着人工智能技术的飞速发展&#xff0c;AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术&#xff0c;在客户服务、营销推广、信息查询等领域发挥着越来越重要…...