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

news2024/9/20 8:43:51

前言

(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就是熄灭。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue,element。监听快捷键粘贴图片,添加到el-upload的列表。

在①中&#xff0c;粘贴图片&#xff0c;图片能够自动添加到底下el-upload组件的文件列表②。 // 对应① <el-card><el-tooltip content"粘贴图片至此" placement"top"><input readonly class"pasteImg" paste.prevent"hand…

GitHub Actions自动化部署+定时百度链接推送

前言 最近用VuePress搭建了一个静态网站&#xff0c;由于是纯静态的东西&#xff0c;每次修改完文章都要重新打包上传很是麻烦。虽然vuepress-theme-vdoing主题作者提供了GitHub Actions自动化部署的教程文章&#xff0c;但是过于简陋且是19年发布的。。 1. 创建一个GitHub仓…

从 1 杯咖啡到 1 首歌的时间,炎凰数据如何实现 Pipeline 执行提速 6 倍?

目录 痛点&#xff1a;工具链复杂、功能少、网不好&#xff0c;太 EMO&#xff01; 解法&#xff1a;All in one&#xff0c;一个平台串联需求→开发→发布 代码、CI/CD 一站式服务 需求、Story、Bug 一站式管理 关联代码 案例&#xff1a;3 个具体使用场景见真章 1. 灵…

MYSQL完全卸载、安装与账号创建、权限控制

一、卸载mysql CentOS 卸载 MySQL 1. 查看安装情况 使用以下命令查看当前安装mysql情况&#xff0c;查找以前是否装有mysql rpm -qa|grep -i mysql这里显示我安装的 MySQL 服务有有&#xff1a; 2. 停止 mysql 服务、删除之前安装的 mysql 删除命令&#xff1a;rpm -e –n…

聚焦纺织行业断线检测,AI机器视觉助力小场景的大未来

随着人工智能技术的发展&#xff0c;机器视觉以其快速、准确、高效的信息采集和处理优势&#xff0c;逐渐成为制造企业现代化升级不可缺少的技术之一&#xff0c;持续赋能相关传统劳动密集型产业的自动化与智能化转型。 传统劳动密集型产业对于人工依赖程度高&#xff0c;供应…

【IDEA报错:Cause: java.sql.SQLSyntaxErrorException: ORA-00942: 表或视图不存在】

报错内容如下&#xff1a; 2023-08-17 11:17:16.274 ERROR [egrant-biz,e44d96001eb5f212,e44d96001eb5f212,true] 29700 --- [ XNIO-1 task-2] c.i.c.l.c.RestExceptionController : 服务器异常org.springframework.jdbc.BadSqlGrammarException: ### Error queryin…

【计组】校验码(奇偶校验码、海明校验码、CRC)

前言 1、小提示&#xff1a;本篇博文是参考王道&#xff0c;在复习 ASCII 码的基础上&#xff0c;总结校验码的相关学习知识点&#xff0c;并在最后搭配习题&#xff08;含408真题&#xff09;进行巩固练习。&#xff08;对重点内容进行划线、标色&#xff09; 2、适用人群&…

前端笔试+面试分享

以下是个人线下面试遇到的真实的题&#xff0c;仅供参考和学习 1. css 选择符有哪些&#xff1f;哪些属性可以继承&#xff1f;优先级算法加何计算&#xff1f; CSS选择符有很多种&#xff0c;例如类型选择器、类选择器、ID选择器、属性选择器、伪类选择器、伪元素选择器等。 …

linux tomcat server.xml 项目访问路径变更不生效

如果想改成默认的127.0.0.1:8080 访问项目 先确定更改的作用文件 server.xml 的 host:appBase 标签 默认找到appBase webapps 下的war包&#xff0c;并解压&#xff0c;解压后的appname为访问路径 也就变成了 127.0.0.1:8080/appname host:Context:path 标签 appBase的 优先…

深度学习技巧应用25-深度学习在的常见问题与使用技巧,让大家更容易理解深度学习,通过面试

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下深度学习技巧应用25-深度学习在的常见问题与使用技巧&#xff0c;让大家更容易理解深度学习。在面对AI领域公司面试的过程中&#xff0c;往往面试官会针对AI领域或者算法实践进行提问&#xff0c;这些问题可能大家知…

laravel-admin之 解决上传图片不显示 $form->image(‘image‘); 及 $grid->column(‘image‘);

参考 https://blog.csdn.net/u013164285/article/details/106017464 $grid->column(‘image’)->image(‘http://wuyan.cn’, 100, 100); // //设置服务器和宽高 图片上传的域名 上传的图片不显示 在 这里设置了图片的上传路径 在这里设置 域名 就可以回显图片

如何用思维导图+Markdown提升工作效率?

在日常的工作中&#xff0c;我们常常需要记录一些信息、重要的事情或者一些重要的想法&#xff0c;Markdown就是一种非常好用的记录工具。搭配思维导图可以提高我们的记录效率&#xff0c;让我们的记录更加结构化。 为什么使用思维导图&#xff1f; 思维导图可以帮助我们整理…

你敢信?代码小白30min就能搭建一套酷炫级的驾驶舱!

大量研究结果表明&#xff0c;人类通过图像获取信息的速度比通过阅读文字获取信息的速度要快很多。 近几年&#xff0c;数据可视化在企业中越发“流行”&#xff0c;将数字以可视化的形式展示&#xff0c;不仅清晰明了地展现企业真正的实力&#xff0c;也能让管理者快速了解细节…

问道管理:股票打板风险大吗?怎么降低打板风险?

在股票市场上&#xff0c;一些出资者喜爱低吸&#xff0c;一些喜爱打板&#xff0c;那么&#xff0c;股票打板危险大吗&#xff1f;怎么下降打板危险&#xff1f;下面问道管理为大家准备了相关内容&#xff0c;以供参阅。 股票打板通常是指在个股涨停时买入&#xff0c;这种买入…

ssm社区管理与服务系统源码和论文

ssm社区管理与服务的设计与实现031 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 研究背景 当今时代是飞速发展的信息时代。在各行各业中离不开信息处理&#xff0c;这正是计算机被广泛应用于信息管理系统的…

【C++】一文带你初识C++继承

食用指南&#xff1a;本文在有C基础的情况下食用更佳 &#x1f340;本文前置知识&#xff1a; C类 ♈️今日夜电波&#xff1a;napori—Vaundy 1:21 ━━━━━━️&#x1f49f;──────── 3:23 …

【学习笔记之java】使用RestTemplate调用第三方接口

1.首先需要导入依赖 <!-- RestTemplate使用导入的依赖--><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.13</version></dependency>2.跟启动类同级创建…

Java【Spring】Bean 的作用域和生命周期

文章目录 前言前言一、关于 Bean 的作用域问题引入二、Bean 的作用域1, 什么是 Bean 的作用域2, Bean 的六种作用域3, 设置 Bean 的作用域(解决开篇的问题) 三、Bean 的生命周期总结 前言 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你: &#x1f4…

前端大屏常用的适配方案

假设我们正在开发一个可视化拖拽的搭建平台&#xff0c;可以拖拽生成工作台或可视化大屏&#xff0c;或者直接就是开发一个大屏&#xff0c;首先必须要考虑的一个问题就是页面如何适应屏幕&#xff0c;因为我们在搭建或开发时一般都会基于一个固定的宽高&#xff0c;但是实际的…

【K210】K210学习笔记一——sensor

【K210】K210学习笔记一——sensor 前言sensor的配置模块导入模块配置模块各配置解释 完整源码 前言 本人大四学生&#xff0c;电赛生涯已经走到尽头&#xff0c;一路上踩过不少坑&#xff0c;但运气也不错拿了两年省一&#xff0c;思来想去&#xff0c;决定开始写博客&#x…