学习目标

  • 了解mos管的使用
  • 了解GPIO输出模式
  • 了解GPIO输入模式
  • 理解ARM系列GPIO相关理论
  • 掌握GD32几种GPIO工作模式编码实现

    学习内容

    基础概念

    GPIO,全称为“通用输入/输出”(General Purpose Input/Output),是计算机系统中用于与外部世界进行数字通信的一种接口标准。它允许硬件和软件通过电信号来交换数据,控制外部设备或接收外部事件。GPIO通常用于连接各种外设,如按钮、LED灯、传感器、马达、继电器等,以便与计算机系统进行交互。
    GPIO具备一些特性:
  1. 通用性:GPIO是通用的,意味着它们可以在不同类型的硬件平台上使用,而不必修改底层电路。这种通用性使得开发人员能够设计和编写与硬件无关的代码,实现跨平台的应用。
  2. 输入和输出:每个GPIO引脚都可以被配置为输入或输出。当配置为输入时,引脚用于接收外部信号,例如按钮状态或传感器读数。当配置为输出时,引脚用于控制外部设备,例如点亮LED或控制马达运动。
  3. 数字信号:GPIO是数字信号接口,这意味着它们只能传输二进制值(0和1)。通过在引脚之间切换电压,可以传递这些数字值,从而实现数据传输和通信。
  4. 控制:操作系统或应用程序可以通过读取或写入GPIO引脚来控制外部设备。通过设置输出引脚的电平(高电平或低电平),可以打开或关闭连接的设备。
  5. 事件触发:GPIO引脚可以用于检测外部事件。例如,一个传感器可能连接到GPIO引脚上,当传感器检测到特定条件时,它会改变引脚的状态,从而触发系统中的相应操作。
  6. 引脚编号:每个GPIO引脚都有一个唯一的编号,通常用数字或字母和数字的组合来表示。在不同的硬件平台上,引脚编号可能会有所不同。
  7. 驱动程序和库:为了在操作系统中使用GPIO,通常需要适当的驱动程序或库。这些驱动程序或库可以帮助应用程序与硬件进行交互,读取和写入GPIO引脚的状态。

    背景知识

    MOS管

    MOS管有NMOS和PMOS两种类型。MOS管包含了三个极:
  • gate:栅极,缩写G
  • drain:漏极,缩写D
  • source:源极,缩写S

MOS管的作用就是开关,通过栅极控制漏极和源极的导通。

mos管主要关注两个点:

  1. 控制。负责mos管导通和不导通,高电平导通还是低电平导通。
  2. 流向。是从漏极流向源极,还是从源极流向漏极。

NMOS

080.png
特点:

  1. 给栅极输入高电平时,mos管导通;给栅极输入低电平时,mos管断开;
  2. 导通时,方向是由漏极到源极;

通常NMOS的使用示例:
081.png
注意:

  1. nmos需要下拉电阻
  2. nmos的开关线路上的消耗物必须放到漏极侧

PMOS

084.png
特点:

  1. 给栅极输入高电平时,mos管断开;给栅极输入低电平时,mos管导通;
  2. 导通时,方向是由源极到漏极;

085.png
注意:

  1. pmos需要上拉电阻
  2. pmos的开关线路上的消耗物必须放到漏极侧

GPIO输出模式

推挽输出

086.png
基本的示意图如上,作为编程人员,我们更多关注的是下图:
087.png
我们希望的是,通过寄存器控制外部引脚的高低电平。

  1. 寄存器的配置高电平,外部引脚输出高电平
  2. 寄存器的配置低电平,外部引脚输出低电平

上图为简略图,这里提供一个详细的图:
091.png
上图过于复杂,在分析结果道理后,为了方便观察,我们看下图:
092.png
加上外部电路,我们理解以下推挽中的推是什么:
093.png
推挽中的挽是什么:
094.png
总结,寄存器控制输出低电平时,外部引脚为低;寄存器控制输出高电平时,外部引脚为高

开漏输出

096.png
基本的示意图如上,作为编程人员,我们更多关注的是下图:
097.png
我们希望的是,通过寄存器控制外部引脚的高低电平。

  1. 寄存器的配置高电平,外部引脚断开。
  2. 寄存器的配置低电平,外部引脚输出低电平。

上图为简略图,这里提供一个详细的图:
098.png
上图过于复杂,在分析结果道理后,为了方便观察,我们简化看下图:
099.png
加上外部电路,我们理解一下开漏输出中的开是什么:
100.png
这里存在一个问题,开漏输出时,如果给高电平,外接的灯是不会亮的,因此开漏状态下我们需要进行上拉,如下所示:
101.png
我们理解一下开漏输出中的漏是什么:
102.png

高阻态

高阻态表示,不输出任何高低电平。
104.png
无论寄存器输出高或者低,外部引脚始终断开。
内部详细电路为:
105.png
高阻态是断路状态,无论外部上拉下拉,都不会导通。

GPIO输出线与

将两个外部引脚接到一起,我们称之为线与。

推挽线与

095.png
推挽过程中,如果一方输出高,一方输出低,则会烧芯片。
因此,推挽是不可以线与的。不能线与,说明不可以和其他芯片同时采用推挽这种方式连接,否则会出现问题。

开漏线与

106.png
开漏过程中,无论双方输出高低电平,芯片都不会收到影响。I2C就是线与的一个实例。

GPIO输入模式

107.png
上图为GPIO输入的基本示意图,其中包含了上拉电路,下拉电路,模拟信号处理,数字逻辑信号接收。

TTL(Transistor-Transistor Logic)施密特触发器是一种数字电路元件,常用于去除输入信号的噪声和抖动,并提供稳定的输出信号。它的作用在于消除输入信号的不稳定性,确保在输入信号发生变化时,输出信号能够产生可靠的、干净的转换。 TTL施密特触发器的主要作用有以下几个方面:

  1. 消除噪声和抖动:在数字电路中,输入信号可能受到噪声的干扰或产生抖动(快速的信号变化)。这可能导致触发器在边沿变化时产生不稳定的输出。TTL施密特触发器使用了一个双阈值比较器,只有当输入信号超过一定的阈值范围时,才会引发输出状态变化。这样可以防止在噪声或抖动情况下触发误动作。
  2. 提供干净的信号转换:TTL施密特触发器能够确保输入信号的干净转换,即使输入信号存在一些不稳定性或较慢的上升/下降时间。它的阈值范围内,输入信号的微小变化都不会引起输出变化,从而产生更加可靠的输出信号。
  3. 信号整形:TTL施密特触发器可用于整形输入信号的波形。如果输入信号具有不规则的上升和下降时间,触发器可以将其整形为规整的方波信号,以便后续电路处理。
  4. 边沿检测器:由于TTL施密特触发器对输入信号的变化非常敏感,它常被用作边沿检测器。当输入信号的上升沿或下降沿发生变化时,触发器可以产生输出脉冲,用于触发其他数字电路中的特定操作。

总之,我们简单的理解就是,输入的电压值被转化为了逻辑的0和1.

浮空输入

108.png
浮空输入,就是将模拟信号,上拉,下拉全部断开。只接收外部电路的输入信号。
这里会存在一个问题,如果外部没有输入信号,那么这个逻辑信号是未知的,不确定的。

上拉输入

109.png
上拉输入,就是将模拟信号,下拉全部断开,同时将上拉开关打开。然后接收外部电路的输入信号。
当外部输入高电平时,如下图:
110.png
当外部接入地时,如下图:
111.png
上拉输入的一些总结:
当输入信号未连接时,上拉电阻将确保输入信号保持在高电平状态,从而避免浮空输入。当外部信号连接到输入引脚时,外部信号可以通过上拉电阻的电流被拉低到低电平。

下拉输入

112.png
下拉输入,就是将模拟信号,上拉全部断开,同时将下拉开关打开。然后接收外部电路的输入信号。
当外部输入高电平时,如下图:
113.png
当外部接入地时,如下图:
114.png
下拉输入的一些总结:
当输入信号未连接时,下拉电阻将确保输入信号保持在低电平状态,从而避免浮空输入。当外部信号连接到输入引脚时,外部信号可以通过下拉电阻的电流被拉高到高电平。

模拟输入

115.png
模拟输入的内部结构如上图。
在真实的物理世界中,我们外部输入的是电压值,这个属于模拟信号。其他的几种输入就是把模拟信号变为逻辑的0和1。通常输入的电压在1v以下我们认为是逻辑的0,1v以上是逻辑的1。(不同的芯片处理信号方式不同,这里简单的举例为1v上下)。
模拟输入的是物理世界中的真实电压,模拟信号的处理不同与逻辑电路的转换,逻辑转换只有两种值。模拟信号则是线性的值,电压多大,就转换一个数字值,也就是将物理世界中的模拟数据转换为数字值,这个值是线性,有范围的。

ARM系列GPIO

083.png
以上为完整的结构图。

寄存器

GPIO的寄存器大体上分为一下几种:

  1. 配置寄存器:配置输入输出,上拉下拉
  2. 数据寄存器:读取输入输出的状态
  3. 复位寄存器:输出高低电平
  4. 复用寄存器:配置复用功能

    保护二极管

    在线与,或者电压过大的情况下,将电压进行钳制,防止芯片烧毁。(当然这个是有限度的,得参考芯片手册内部的二极管特性)。

    GPIO工作模式

    | 模式 | 性质 | | —- | —- | | 浮空输入 | 数字输入 | | 上拉输入 | 数字输入 | | 下拉输入 | 数字输入 | | 模拟输入 | 模拟输入 | | 开漏输出 | 数字输出 | | 推挽输出 | 数字输出 | | 复用开漏输出 | 数字输出 | | 复用推挽输出 | 数字输出 |

GD32/STM32系列GPIO工作模式

浮空输入

116.png

  1. // 时钟初始化
  2. rcu_periph_clock_enable(RCU_GPIOx);
  3. // 配置GPIO模式
  4. gpio_mode_set(GPIOx, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_x);
  • RCU_GPIOx: 具体是哪个端口。(A,B,C,D,E,F,G,H,I)
  • GPIOx: 具体是哪个端口。(A,B,C,D,E,F,G,H,I)
  • GPIO_PIN_x:具体是哪个引脚。(0~15)

    上拉输入

    117.png

    1. // 时钟初始化
    2. rcu_periph_clock_enable(RCU_GPIOx);
    3. // 配置GPIO模式
    4. gpio_mode_set(GPIOx, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_x);
  • RCU_GPIOx: 具体是哪个端口。(A,B,C,D,E,F,G,H,I)

  • GPIOx: 具体是哪个端口。(A,B,C,D,E,F,G,H,I)
  • GPIO_PIN_x:具体是哪个引脚。(0~15)

    下拉输入

    118.png

    1. // 时钟初始化
    2. rcu_periph_clock_enable(RCU_GPIOx);
    3. // 配置GPIO模式
    4. gpio_mode_set(GPIOx, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_x);
  • RCU_GPIOx: 具体是哪个端口。(A,B,C,D,E,F,G,H,I)

  • GPIOx: 具体是哪个端口。(A,B,C,D,E,F,G,H,I)
  • GPIO_PIN_x:具体是哪个引脚。(0~15)

    模拟输入

    119.png

    1. // 时钟初始化
    2. rcu_periph_clock_enable(RCU_GPIOx);
    3. // 配置GPIO模式
    4. gpio_mode_set(GPIOx, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_x);
  • RCU_GPIOx: 具体是哪个端口。(A,B,C,D,E,F,G,H,I)

  • GPIOx: 具体是哪个端口。(A,B,C,D,E,F,G,H,I)
  • GPIO_PIN_x:具体是哪个引脚。(0~15)

    推挽输出

    120.png

    1. // 1. 时钟初始化
    2. rcu_periph_clock_enable(RCU_GPIOx);
    3. // 2. 配置GPIO 输入输出模式
    4. gpio_mode_set(GPIOx, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_x);
    5. // 3. 配置GPIO 模式的操作方式
    6. gpio_output_options_set(GPIOx, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_x);
  • RCU_GPIOx: 具体是哪个端口。(A,B,C,D,E,F,G,H,I)

  • GPIOx: 具体是哪个端口。(A,B,C,D,E,F,G,H,I)
  • GPIO_PIN_x:具体是哪个引脚。(0~15)

    开漏输出

    121.png

    1. // 1. 时钟初始化
    2. rcu_periph_clock_enable(RCU_GPIOx);
    3. // 2. 配置GPIO 输入输出模式
    4. gpio_mode_set(GPIOx, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_x);
    5. // 3. 配置GPIO 模式的操作方式
    6. gpio_output_options_set(GPIOx, GPIO_OTYPE_OD, GPIO_OSPEED_MAX, GPIO_PIN_x);
  • RCU_GPIOx: 具体是哪个端口。(A,B,C,D,E,F,G,H,I)

  • GPIOx: 具体是哪个端口。(A,B,C,D,E,F,G,H,I)
  • GPIO_PIN_x:具体是哪个引脚。(0~15)

通常开漏时,需要外部上拉,当然内部电路也可以控制上拉。

复用推挽输出

122.png

  1. // 1. 时钟初始化
  2. rcu_periph_clock_enable(RCU_GPIOx);
  3. // 2. 配置GPIO 输入输出模式
  4. gpio_mode_set(GPIOx, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_x);
  5. // 3. 配置GPIO 模式的操作方式
  6. gpio_output_options_set(GPIOx, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_x);
  7. // 4. 复用功能
  8. gpio_af_set(GPIOx, GPIO_MODE_AF, GPIO_AF_x, GPIO_PIN_x);
  • RCU_GPIOx: 具体是哪个端口。(A,B,C,D,E,F,G,H,I)
  • GPIOx: 具体是哪个端口。(A,B,C,D,E,F,G,H,I)
  • GPIO_PIN_x:具体是哪个引脚。(0~15)
  • GPIO_AF_x:具体复用什么功能。(0-15)

    复用开漏输出

    123.png

    1. // 1. 时钟初始化
    2. rcu_periph_clock_enable(RCU_GPIOx);
    3. // 2. 配置GPIO 输入输出模式
    4. gpio_mode_set(GPIOx, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_x);
    5. // 3. 配置GPIO 模式的操作方式
    6. gpio_output_options_set(GPIOx, GPIO_OTYPE_OD, GPIO_OSPEED_MAX, GPIO_PIN_x);
    7. // 4. 复用功能
    8. gpio_af_set(GPIOx, GPIO_MODE_AF, GPIO_AF_x, GPIO_PIN_x);
  • RCU_GPIOx: 具体是哪个端口。(A,B,C,D,E,F,G,H,I)

  • GPIOx: 具体是哪个端口。(A,B,C,D,E,F,G,H,I)
  • GPIO_PIN_x:具体是哪个引脚。(0~15)
  • GPIO_AF_x:具体复用什么功能。(0-15)

通常开漏时,需要外部上拉,当然内部电路也可以控制上拉。

练习题

  1. 实现多种输出模式下的点灯,体会输出模式。
  2. 实现按键输入,测试输入模式。