嵌入式linux中GPIO的逻辑电平与物理电平

2021-02-28

关键字:active_low、电平转换


 

这篇文章讲述了在Linux平台开发GPIO驱动程序时如何用“正确的姿势”读取其电平状态。

 

不知所云?简单说就是:你通过命令 cat 出来的某个引脚的电平值可能和你预想的不一样,例如,从原理图上看按下某个按扭它怎么都应该识别到低电平的,可实际查询时却是高电平。导致这种现象的根本原因在于 active_low 的配置值。

 

下面直接通过一个示例来解释这种现象的根本原因。

 

假设我们的开发板上有一个按钮需要我们编写程序去监听它的按下状态,其原理图如下所示:

 

图中 "[4,7]DEFAULT" 接的就是芯片某个GPIO引脚。从原理图可以很清楚地知道,当按钮抬起时,VDD直接接入芯片引脚中,使CPU识别为高电平。而当按钮被按下时,VDD被导入地,CPU识别为低电平。

 

我们的目的是监听到按钮的按下状态,这里原理很清晰,处理逻辑也很简单。按理说这个程序开发起来不要太简单。但,Linux内核想的可比我们多也比我们周全。

 

假设我们已经根据这份原理图开发好了我们需要的程序,并且它工作的非常好。突然有一天,原理图因为某些不可抗拒的原因变更了,VDD和GND的位置互换了,导致按钮在抬起时CPU识别为低电平,按下时反而被识别为高电平。这个时候的你该怎么办?也许你会说,这个简单啊,原有的程序花个几分钟稍微改一下参数就可以了。确实没错,GPIO驱动开发想应付原理图变更工作量确实不大。但,Linux内核想的却是:我能不能做到不管原理图如何变更,我的程序都不用改动的程度?

 

答案是:几乎可以。

 

Linux内核的解决方案是:将GPIO的电平状态划分为“物理态”和“逻辑态”两种,即物理电平与逻辑电平。

 

原理图的变更所改变的只能是实实在在的物理电平二态值,出于惯性思维,我们会认为物理电平与软件电平就应该是一致的,但当我们跳脱出来,将它们分开对待,当在某种条件下,你物理电平是高时我软件电平也识别为高,当条件改变了,你物理电平得为低时我软件电平才识别为高,这不就能达到我们的目的了吗?这套机制中我们唯一需要更改的就是这个“条件”,Linux内核将这个条件命名为 "active_low"。使用这套机制,当我们在将驱动以及上层应用开发好后原理图再发生变更,仅需更改驱动程序的 active_low 的配置值即可,上层应用完全不需要动。因此前面才说答案是几乎可以。

 

众所周知,GPIO驱动在开发好后会在文件系统( /sys/class/gpio )上有个节点映射,其形式如笔者的开发板下的内容所示:

--w-------    1 root     root        16384 Feb 28 09:58 export
lrwxrwxrwx    1 root     root            0 Feb 28 09:58 gpio19 -> ../../devices/virtual/gpio/gpio19
lrwxrwxrwx    1 root     root            0 Feb 22 15:15 gpio33 -> ../../devices/virtual/gpio/gpio33
lrwxrwxrwx    1 root     root            0 Feb 28 09:58 gpiochip0 -> ../../devices/virtual/gpio/gpiochip0
--w-------    1 root     root        16384 Feb 28 09:58 unexport

 

GPIO驱动的开发不属于本篇文章的讨论范畴。这里只看 active_low、物理电平与逻辑电平之间的关系。

 

本文示例的按钮的驱动程序在笔者的开发板中被映射为 gpio33 节点,我们进入到该节点目录。可以看到有如下所示的文件结构:

-rw-r--r--    1 root     root        16384 Feb 22 15:15 active_low
-rw-r--r--    1 root     root        16384 Feb 28 09:58 edge
lrwxrwxrwx    1 root     root            0 Feb 28 09:58 subsystem -> ../../../../class/gpio
-rw-r--r--    1 root     root        16384 Feb 28 09:58 uevent
-rw-r--r--    1 root     root        16384 Feb 22 15:15 value

 

active_low的值为1:

# cat active_low 
1
#

这表示当物理电平为低时,将逻辑电平视为激活态,即高电平。此时不按下按钮,我们看看 value 记载的电平值:

# cat value 
0
# 

为0,未激活态,即低电平。此时的实际情况是:物理电平为高,但逻辑电平却显示为低。

 

现在我们按下按钮,再查看一下电平状态:

# cat value 
1
#

与预想的一致。

 

我们尝试着改变 active_low 的值再看看结果,直接用命令将 active_low 的值改为0:

# echo  0 > active_low 
# cat active_low 
0
# 

当按钮抬起时,value值查得为1,按钮按下时value值查得为0:

# cat value 
1
#
# cat value 
0
#

与预想的完全一致。

 

物理电平与逻辑电平分离是个非常明智且有必要的策略,active_low属性控制着它们之间的转换关系。不过,不同平台关于这一属性的配置方式可能不同,有的是直接在专门的配置文件中配置,有的是在驱动代码中配置,更有的是在dts文件中配置。虽殊途同归,但仍然需要各位同学自行了解最适合自己配置方式以简化开发。

 


 

posted @ 2021-02-28 11:06  大窟窿  阅读(1275)  评论(0编辑  收藏  举报