转自:https://blog.csdn.net/techexchangeischeap/article/details/72569999

概述

能将处理器的GPIO(General Purpose Input and Output)内部结构和各种模式彻底弄清楚的人并不多,最近在百度上搜索了大量关于这部分的资料,对于其中很多问题的说法并不统一。本文尽可能的将IO涉及到的所有问题罗列出来,对于有明确答案的问题解释清楚,对于还存在疑问的地方也将问题提出,供大家讨论。

概括地说,IO的功能模式大致可以分为输入、输出以及输入输出双向三大类。其中作为基本输入IO,相对比较简单,主要涉及的知识点就是高阻态;作为输出IO,相比于输入复杂一些,工作模式主要有开漏(Open Drain)模式和推挽(Push-Pull)模式,这一部分涉及的知识点比较多;对于输入输出IO,容易产生疑惑的地方是准双向和双向端口的区别。

下面就按照这样的顺序依次介绍各个模式的详细情况。

输入IO

这里所说的输入IO,指的是只作为输入,不具有输出功能。此时对于input引脚的要求就是高阻(高阻与三态是同一个概念)。基本输入电路的类型大致可以分为3类:基本输入IO电路、施密特触发输入电路以及弱上拉输入电路。

先从最基本的基本输入IO电路说起,其电路如图 1所示。

其中的缓冲器U1是具有控制输入端,且具有高阻抗特性的三态缓冲器。通俗地说就是这个缓冲器对外来说是高阻的,相当于在控制输入端不使能的情况下,物理引脚与内部总线之间是完全隔离的,完全不会影响内部电路。而控制输入端的作用就是可以发出读Pin状态的操作指令。其过程如图 2所示。

这种基本电路的一个缺点是在读取外部信号的跳变沿时会出现抖动,如下图所示。

于是施密特触发输入电路就是解决了上述这种抖动的问题,其经过施密特触发器后的信号如图 4所示。

对于输入电路还存在另外一个问题,就是当输入引脚悬空的时候,输入端检测到的电平是高还是低?当输入信号没有被驱动,即悬空(Floating)时,输入引脚上任何的噪声都会改变输入端检测到的电平,如图 5所示。

为了解决这个问题,可以在输入引脚处加一个弱上拉电阻,如图 6所示。

这样,当输入引脚悬空时,会被RP上拉到高电平,在内部总线上就有确定的状态了。

但是这种结构是有一定问题的。首先很明显的一点是,当输入引脚悬空时读到的是1,当输入引脚被高电平驱动时读到的也是1,只有当输入引脚被低电平驱动时读到的才是0。也就是对于读1采取的方式是"读取非零"的方式。

另一个问题是该电路对外呈现的不是高阻,某种意义上说也在向外输出,当外部驱动电路不同时可能出现错误的检测结果。例如外部驱动电路是如图 7所示的结构,该电路结构中通过K打到不同端可以输出高电平或者低电平。

如果将如图 7所示的电路输出低电平,连接到带有弱上拉电阻的输入引脚,其结构如下所示。

由欧姆定律知,测试点处的电平是,于是CPU测得的输入信号为高,而外部驱动电路希望输出的电平为低。这种错误的原因就在于这种结构的输入电路并不是真正的高阻,或者说这个输入IO其实也在输出,而且影响了外部输入电路。

这种情况的发生也说明了:信号前后两级传递,为什么需要输出阻抗小,输入阻抗大的原因。在这个例子中,外围驱动电路的输出阻抗很大,达到了100Kohm;而输入端的阻抗又不够大,只有10Kohm,于是就出现了问题。如果输入端的输入阻抗真正做到高阻(无穷大),如下所示,就不会出现问题。

上面提到的这个带弱上拉的输入电路,也就是在后续章节会提到的准双向端口的情况。

输出IO

IO输出电路最主要的两种模式分别是推挽输出(Push-Pull Output)和开漏输出(Open Drain Output)。

推挽输出(Push-Pull Output)

推挽输出的结构是由两个三极管或者MOS管受到互补信号的控制,两个管子始终保持一个处于截止,另一个处于导通的状态。如图 10所示。

推挽输出的最大特点是可以真正能真正的输出高电平和低电平,在两种电平下都具有驱动能力。

补充说明:所谓的驱动能力,就是指输出电流的能力。对于驱动大负载(即负载内阻越小,负载越大)时,例如IO输出为5V,驱动的负载内阻为10ohm,于是根据欧姆定律可以正常情况下负载上的电流为0.5A(推算出功率为2.5W)。显然一般的IO不可能有这么大的驱动能力,也就是没有办法输出这么大的电流。于是造成的结果就是输出电压会被拉下来,达不到标称的5V。

当然如果只是数字信号的传递,下一级的输入阻抗理论上最好是高阻,也就是只需要传电压,基本没有电流,也就没有功率,于是就不需要很大的驱动能力。

对于推挽输出,输出高、低电平时电流的流向如图 11所示。所以相比于后面介绍的开漏输出,输出高电平时的驱动能力强很多。

开漏输出(Open Drain Output)

常说的与推挽输出相对的就是开漏输出,对于开漏输出和推挽输出的区别最普遍的说法就是开漏输出无法真正输出高电平,即高电平时没有驱动能力,需要借助外部上拉电阻完成对外驱动。下面就从内部结构和原理上说明为什么开漏输出输出高电平时没有驱动能力,以及进一步比较与推挽输出的区别。

首先需要介绍一些开漏输出和开集输出。这两种输出的原理和特性基本是类似的,区别在于一个是使用MOS管,其中的"漏"指的就是MOS管的漏极;另一个使用三极管,其中的"集"指的就是MOS三极管的集电极。这两者其实都是和推挽输出相对应的输出模式,由于使用MOS管的情况较多,很多时候就用"开漏输出"这个词代替了开漏输出和开集输出。

介绍就先从开集输出开始,其原理电路结如图 12所示。

图 12左边的电路是开集(OC)输出最基本的电路,当输入为高电平时,NPN三极管导通,Output被拉到GND,输出为低电平;当输入为低电平时,NPN三极管闭合,Output相当于开路(输出高阻)。高电平时输出高阻(高阻、三态以及floating说的都是一个意思),此时对外没有任何的驱动能力。这就是开漏和开集输出最大的特点,如何利用该特点完成各种功能稍后介绍。这个电路虽然完成了开集输出的功能,但是会出现input为高,输出为低;input为低,输出为高的情况。

图 12右边的电路中多使用了一个三极管完成了"反相"。当输入为高电平时,第一个三极管导通,此时第二个三极管的输入端会被拉到GND,于是第二个三极管闭合,输出高阻;当输入为低电平时,第一个三极管闭合,此时第二个三极管的输入端会被上拉电阻拉到高电平,于是第二个三极管导通,输出被拉到GND。这样,这个电路的输入与输出是同相的了。

接下来介绍开漏输出的电路,如图 13所示。原理与开集输出基本相同,只是将三极管换成了MOS而已。

当MOS管闭合时,开漏输出电路输出高电平,且连接着负载时,电流流向是从外部电源,流经上来电阻RPU,流进负载,最后进入GND。

  1. 开漏输出的这一特性一个明显的优势就是可以很方便的调节输出的电平,因为输出电平完全由上拉电阻连接的电源电平决定。所以在需要进行电平转换的地方,非常适合使用开漏输出。
  2. 开漏输出的这一特性另一个好处在于可以实现"线与"功能,所谓的"线与"指的是多个信号线直接连接在一起,只有当所有信号全部为高电平时,合在一起的总线为高电平;只要有任意一个或者多个信号为低电平,则总线为低电平。而推挽输出就不行,如果高电平和低电平连在一起,会出现电流倒灌,损坏器件。

推挽与开漏输出的区别

双向IO

很多处理器的引脚可以设置为双向端口,双向端口的要求就是既可以输出信号,又可以读回外部信号输入。要同时做到这两点从原理上来说有点困难,首先从处理器的开漏输出IO口的内部结构说起,如图 16所示。

双向开漏IO

但是对图 16的结构稍作修改,如图 17所示时,该结构称为双向开漏IO的结构。所做的改动是将输入驱动缓冲器连接到了PIN上。

该结构输出为"1"时,T1断开,此时pin对外呈现高阻,作为输入引脚没有任何问题。但是如果该结构输出"0"时,T1导通,此时pin对外短路到地,即无论外部输入什么信号,U2读回的全部是低。所以对于这样的结构,如果需要作为输入引脚使用时,必须给U1输出"1"后才能读取外部引脚数据。

准双向开漏IO

很多文献中还提到了准双向端口,其实准双向端口就是图 17的结构中加了一个上拉电阻,如图 18所示。

这个结构与图 17相比有以下相同与不同之处:

  1. 作为输入引脚使用时,也必须先向U1中写"1",以达到断开T1的目的。所以是否需要提前写"1"并不是双向IO与准双向IO的区别。两者做输入端口时都需要提前写"1"。
  2. 双向端口作为输入时是真正的高阻态,而准双向IO作为输入端口时,输入阻抗不为高阻,于是有可能出现如本文图 8所示的问题。
  3. 准双向端口读取输入状态,默认为高。也就是判断外部输入信号的方法是"非低则为高"。即该结构只能准确的识别外部的低电平,无法区分悬空和真正的高。于是只要读到的不是0,都认为外部为1。

推挽输出作为双向IO

如果双向端口中的输出部分采用的是推挽输出结构,那么作为输入时必须将上下两个管子全部端口才能成为高阻,作为输入。

51单片机的P0端口

在双向端口的讨论中,比较复杂的就是51单片机的P0端口了。这里就详细讨论一下51单片机的P0端口结构和工作原理。

P0端口的内部结构如图 19所示。

内部结构比较复杂,包括以下这些器件:

  1. U1:与门。一个输入连着控制线,另一个输入连接这地址/数据信号。由于与门的特性,当控制线为1时,与门输出与地址/数据信号的电平保持一致;如果控制线为0,则输出恒为。于是控制信号线相当于与门的使能信号。
  2. U2:反相器,输出信号为地址/数据信号的反相信号。
  3. U3和U6都是具有控制输入端且具有高阻抗特性的三态缓冲器,作用是对于外部呈现高阻态。当控制端使能时可以将外部信号的电平读进数据总线。
  4. U4:为锁存器,目的就是控制引脚输出信号的时间。
  5. U5:模拟开关,可以控制V2的输入信号是来自锁存器U4的Q非输出还是来自于反相器U2的输出。
  6. V1和V2分别是两个MOS管。

了解了各个独立器件之后就开始介绍工作在各个模式下的工作原理:

P0用于地址/数据线时:

在P0作为地址/数据线时,是地址、数据复用总线,P0需要输出地址,同时需要读回数据信号。

当P0需要输出地址信息时,U1的控制信号为0,模拟开关U5接到U2反相器的输出。于是当地址信号线传来的信号为1,与控制线"1"相与之后输出到V1的输入信号为"1",V1截止。地址信号"1"经反相之后,通过模拟开关输出到V2的输入端为"0",V2导通,于是情况如图 20所示,pin输出"0"。

当地址信号线传来的信号为1,与控制线"1"相与之后输出到V1的输入信号为"0",V1导通。地址信号"0"经反相之后,通过模拟开关输出到V2的输入端为"1",V2截止,于是情况如图 21所示,pin输出"1"。

于是在作为地址线输出时,V1、V2两个MOS管均使用了,是推挽输出。

当P0在输出低8位地址信息后,将变为数据总线,此时CPU的操作是控制端输出0,模拟开关打到锁存器的Q非端,且向锁存器中打入"1"。于是Q非输出为0,V2截止。同时控制线为0使得与门输出为0,V1截止。由于V1和V2都截止,所以此时pin对外完全呈现高阻,作为输入端口,外部数据通过U6进入内部总线,情况如图 22所示。(相当于将推挽输出的两个MOS管全部断开了)此时由于对外呈现高阻,所以是真正的输入引脚。这就解释了为什么说P0是真正的双线端口。

P0用于普通IO时:

在P0作为普通IO并作为输出时,控制信号为0,使V1始终处于截止状态。模拟开关连接到Q非输出,当作为输出时,锁存器的输入端直接输入0或者1,Q非将反相信号输入到V2的输入端。即当输出"0"时,V2输入端为"1",V2导通,pin输出"0";当输出"1"时,V2输入端为"0",V2截止,pin输出高阻的0。即当P0工作在普通IO模式下,输出为开漏输出,且内部没有上拉电阻。

在P0作为普通IO并作为输入时,控制信号为0,使V1始终处于截止状态。模拟开关连接到Q非输出,且CPU自动向锁存器输入端写1,则V2输入端为0,V2截止。与之前在作为地址/数据线,作为输入时一样,也是两个MOS管全部断开,pin直接连接到U6,对外呈现高阻。于是也是真正的输入引脚。

综上P0无论工作在哪种模式下都是真正的双端口IO。

51单片机的P1~P3端口

51单片机的其他三个端口的内部结构如图 23所示,与P0相比简单了很多,没有了顶部的MOS管,也没有了地址/数据信号的选项。作为输出时是带有上拉电阻的的开漏输出,作为输入时是有上拉电阻存在的,于是输入端口对外不是高阻。这就解释了为什么P1~P3只能是准双向端口。

GPIO输入输出各种模式(推挽、开漏、准双向端口)详解的更多相关文章

  1. php开发面试题---php面向对象详解(对象的主要三个特性)

    php开发面试题---php面向对象详解(对象的主要三个特性) 一.总结 一句话总结: 对象的行为:可以对 对象施加那些操作,开灯,关灯就是行为. 对象的形态:当施加那些方法是对象如何响应,颜色,尺寸 ...

  2. TortoiseGit学习系列之TortoiseGit基本操作将提交到本地的项目推送到在线仓库(图文详解)

    前面博客 TortoiseGit学习系列之TortoiseGit基本操作克隆项目(图文详解) TortoiseGit学习系列之TortoiseGit基本操作修改提交项目(图文详解) TortoiseG ...

  3. Android开发之 android:windowSoftInputMode属性详解

    android:windowSoftInputMode activity主窗口与软键盘的交互模式,可以用来避免输入法面板遮挡问题,Android1.5后的一个新特性. 这个属性能影响两件事情: [一] ...

  4. NDK开发之Application.mk文件详解

    做过NDK开发的同学应该都知道有个Application.mk文件,这是android NDK构建系统使用的一个可选构建文件.它的目的是描述应用程序需要哪些模块,也定义了所有模块的一些通用变量.主要有 ...

  5. JAVAWEB开发之HttpServletResponse和HttpServletRequest详解(上)(各种乱码、验证码、重定向和转发)

    HttpServletResponse简介 Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象.和代表响应的response对象 request和re ...

  6. KMP(梅开三度之数据结构详解版

    前言 KMP算法是一种字符串匹配算法,其重中之重是next数组的构建,其代码的简洁与神奇使其广受关注. 但不难发现,acm中学到的KMP和数据结构里面学到的KMP并不一样o(︶︿︶)o 之前我写过ac ...

  7. iOS开发之UITextField的使用详解

    UITextField的使用详解 UITextField控件是开发中,使用频率比较高的控件了,那么有必要总结一下. 一.UITextField手动编写控件 UITextField  *txtAccou ...

  8. Android开发之Path类使用详解,自绘各种各样的图形!

    玩过自定义View的小伙伴都知道,在View的绘制过程中,有一个类叫做Path,Path可以帮助我们实现很多自定义形状的View,特别是配合xfermode属性来使用的时候.OK,那我们今天就来看看P ...

  9. JAVAWEB开发之HttpServletResponse和HttpServletRequest详解(下)(各种乱码、验证码、重定向和转发)

    HttpServletRequest获取请求头信息  (1)获取客户机请求头 String getHeader(String name) Enumeration<String> getHe ...

随机推荐

  1. MATLAB实现二值化函数

    function  bc = binary_conversion(a)  %这是灰度值二值化转换函数,阈值为平均值j=imread(a);             %读取灰度图像   j=double ...

  2. Python 3.6print 出现 SyntaxError: invalid syntax

    开始使用sublime学习python,编写代码如图 Ctrl+B运行以后,报错   SyntaxError: invalid syntax 百度查询以后,大部分的回答都是说,python在3.0以后 ...

  3. CSS3中设置字体的抗锯齿或光滑度的属性

    刚刚接触前端开发,对于-webkit-font-smoothing: antialiased; 这个属性不了解.上网查找了一些资料. 总结一下: -webkit-font-smoothing  :  ...

  4. CNN试验记录

    CIFAR-10 图像处理:(预处理还是很重要的) 数据随机裁剪,填充0 依概率p水平翻转 1.VGG16 SGD lr=0.01 momentum 0.9 weight_decay=0.0001 e ...

  5. jmeter简单的接口性能测试

    原文转自:https://blog.csdn.net/lovesoo/article/details/78579547 Apache JMeter是一款纯java编写负载功能测试和性能测试开源工具软件 ...

  6. 芯灵思Sinlinx A64 开发板移植SQLite3

    开发平台 芯灵思Sinlinx A64 内存: 1GB 存储: 4GB 开发板详细参数 https://m.tb.cn/h.3wMaSKm 开发板交流群 641395230 首先到 http://ww ...

  7. C++ 属性类

    又一次向目标迈进了... 这次是实现一个物体所拥有的属性类.没什么好说的,非常简单. 因为我是C++新手,不知道对这次的实现有没有什么更好的实现方式.因为这个类对以后的进展很重要,而且,要充分考虑易用 ...

  8. CentOS 7编译安装php7.0.7以及可能遇到的问题的解决方案

    https://blog.csdn.net/chenxiabinffff/article/details/51612149

  9. Cobbler全自动批量安装部署Linux系统

    说明: Cobbler服务器系统:CentOS 5.10 64位 IP地址:192.168.21.128 需要安装部署的Linux系统: eth0(第一块网卡,用于外网)IP地址段:192.168.2 ...

  10. JavaScript的定时器如何先触发一次再延时

    var data3=0; (function count3(){ console.log("count3:",data3++); setTimeout(count3,1000); ...