数字输入与输出

数字输入与输出是最常用的一种外设通信方式,虽然每一个端口只有0和1两种状态,但却可以组合出各种各样的变化。

数字输入与输出原理

数字输入与输出,也称为通用输入输出,全称是General-purpose input/output,大部分情况下,我们都会用简称GPIO



GPIO的功能,主要是指这个管脚可以作为数字信号的输入或者输出使用,到底是输入还是输出呢,这需要我们使用系统寄存器来进行配置。



当GPIO被设置为输入模式时,就可以读取这个管脚上的电平高低,从而实现读取外部信号的功能,比如外部按键的信号,就是这样读取的。



当GPIO被设置为输出模式时,我们就可以主动改变管脚的电平高低了,这样就能通过电平的变化,控制外部的设备,比如点亮或者熄灭一个LED灯。



除此之外,一些IO口还可以配置成其他功能,比如PWM输出,串口通信等等。
关于旭日X3派的管脚定义,大家可以参考这张图,他会显示每个管脚具体可以实现什么功能。



这里需要说明一点,旭日X3派的管脚序号定义有三种编码方式:

第一种就是Board编码,这种就是按照主板上管脚排针编号,分别对应1~40号排针。

第二种就是BCM编码,这种方式是参考 Broadcom SOC 的通道编号,侧重CPU寄存器,在使用BCM库或者使用python编程时,常采用的一种编码方式。

第三种就是X3编码,可以理解为是旭日X3派自己的编码方式,在使用旭日派自己的驱动库编程时,会使用到这种编码。

大家在后续编程开发中,需要结合函数接口的说明,使用对应的引脚编号,不然可能会出现意料之外的问题。

引脚复用配置工具 虽然从硬件上看,扩展出来的引脚只有40个,不过大部分引脚还可以配置成多种功能,这样延伸出来变化可就多了,那如何配置引脚的不同功能呢?

我们可以使用旭日X3派中提供的srpi-config工具进行配置。 直接在终端中输入这个指令,就可以看到这样的窗口,okay配置对应管脚为专用功能,disabled配置对应管脚为GPIO模式,按照自己的需要进行配置即可,配置完成后重启才会生效。 了解了GPIO的概念,接下来我们就要开始编程啦。 数字输入编程
首先我们来看下最基本的数字输入测试,读取一个按键的状态。

硬件接线

我们将按键的一边连接到旭日X3派的38号引脚上,这是一个GPIO的接口,另一边连接到39号引脚,也就是GND。

原理很简单,按键没按下时,导线是断开的,GPIO是默认的状态,按键按下后,导线就会导通,GPIO的状态变成了GND。

这样,我们就可以通过电平的变化,知道按键的状态啦。  

运行例程

我们不妨来运行一下,看下实际效果是不是这样。
bash
$ sudo python3 simple_input.py

我们将一个按键开关接到对应的管脚上, 然后连接到旭日X3PI当中,运行刚才的程序。之后我们按下或是松开按键时,他都会打印出对应的电平变化。
按下显示的就是LOW,也就是低电平,松开就是HIGH,也就是高电平。

代码解析

数字输入的功能实现啦,我们来看下代码是如何实现的。
simple_input.py:

python
#!/usr/bin/env python3

import Hobot.GPIO as GPIO
import time

# 定义使用的GPIO通道为38
input_pin = 38 # BOARD 编码 38

def main():
    prev_value = None

    # 设置管脚编码模式为硬件编号 BOARD
    GPIO.setmode(GPIO.BOARD)
    # 设置为输入模式
    GPIO.setup(input_pin, GPIO.IN)

    print("Starting demo now! Press CTRL+C to exit")
    try:
        while True:
            # 读取管脚电平
            value = GPIO.input(input_pin)
            if value != prev_value:
                if value == GPIO.HIGH:
                    value_str = "HIGH"
                else:
                    value_str = "LOW"
                print("Value read from pin {} : {}".format(input_pin, value_str))
                prev_value = value
            time.sleep(1)
    finally:
        GPIO.cleanup()

if __name__=='__main__':
    main()

我们在Python中需要引入X3Pi的GPIO库,从而使用对应的GPIO功能。 然后在主函数里首先需要定义了一个变量来存储这个管脚的电平信号。 然后进行初始化,设置管脚的编码模式为BOARD模式,之后定义输入用的管脚,这里使用的是BOARD编码,因为他是第38个管脚,所以为38号。 之后的话就会进入到循环当中,这样的一个函数就可以读出当前的电平值。 然后进行判断,没有按下按键的时候,38管脚和GND是断开的,他就是高电平,按下按键时,就和GND导通了,这个管脚就会编程低电平。 如果电平值和上一次存储的不一致,后面的判断就会判断出是升高还是降低,最后会把结果打印出来。 松开按键时同理。

数字输出编程

GPIO不止有输入功能,还有输出功能,大家应该经常看到电子设别上的LED灯吧,这就是典型的IO输出控制的设备,我们也来试一试。

硬件接线

我们将一个LED灯的正极连接到了2号引脚,这是5V电源,用来给LED供电的,不过LED电阻小,为了不至于烧坏,我们最好还是在电路中串联一个电阻来限制电流大小。
接下来,当LED的负极接到GND,也就是电源的0V上时,LED两边会因为有电势差而被点亮;当LED的负极也连接到高电平,也就是5V的时候,会因为两边没有电势差,也就不会被点亮。

这个负极的电平信号我们就用GPIO来输出,所以我们把LED的负极连接到任意的GPIO引脚上,比如38号,将这个引脚设置为输出模式,当输出高电平时,灯就会熄灭,输出低电平,灯就会被点亮。
把这两个动作放到一个循环中,就可以实现闪烁的功能啦。

运行示例程序

快来运行例程试一试吧,按照刚才的设置,将实物连接完成,然后连接到X3Pi,运行程序,就可以看到这个LED就会开始闪烁了。
bash
$ sudo python3 simple_input.py

代码解析

simple_out.py:

python
#!/usr/bin/env python3

import Hobot.GPIO as GPIO
import time

# 定义使用的GPIO通道为38
output_pin = 38 # BOARD 编码 38

def main():
    # 设置管脚编码模式为硬件编号 BOARD
    GPIO.setmode(GPIO.BOARD)
    # 设置为输出模式,并且初始化为高电平
    GPIO.setup(output_pin, GPIO.OUT, initial=GPIO.HIGH)
    # 记录当前管脚状态
    curr_value = GPIO.HIGH
    print("Starting demo now! Press CTRL+C to exit")
    try:
        # 间隔1秒时间,循环控制LED灯亮灭
        while True:
            time.sleep(1)
            GPIO.output(output_pin, curr_value)
            curr_value ^= GPIO.HIGH
    finally:
        GPIO.cleanup()

if __name__=='__main__':
    main()

数字输入与输出集成

学习了GPIO的输入与输出功能后,我们就可以做一些自动化的功能了。

硬件接线

比如我们同时连接一个按键和LED灯,尝试实现按键被按下,灯被点亮,松开,灯熄灭,这样的功能。

运行示例程序

我们通过这个例程来试一试效果如何?
bash
$ sudo python3 button_led.py
我们来看一下具体的操作,同样的,按照刚才的设置,将实物连接完成,然后连接到X3Pi,运行刚才的程序。按下按键后,就可以看到LED被点亮了,松开之后,LED就熄灭了。

代码解析

button_led.py

python
#!/usr/bin/env python3

import Hobot.GPIO as GPIO
import time

# 定义使用的GPIO通道:
# 36号作为输出,可以点亮一个LED
# 38号作为输入,可以接一个按钮
led_pin = 36 # BOARD 编码 36
but_pin = 38 # BOARD 编码 38

# 禁用警告信息
GPIO.setwarnings(False)

def main():
    prev_value = None

    # Pin Setup:
    GPIO.setmode(GPIO.BOARD)  # BOARD pin-numbering scheme
    GPIO.setup(led_pin, GPIO.OUT)  # LED pin set as output
    GPIO.setup(but_pin, GPIO.IN)  # Button pin set as input

    # Initial state for LEDs:
    GPIO.output(led_pin, GPIO.LOW)
    print("Starting demo now! Press CTRL+C to exit")
    try:
        while True:
            curr_value = GPIO.input(but_pin)
            if curr_value != prev_value:
                GPIO.output(led_pin, curr_value)
                prev_value = curr_value
                print("Outputting {} to Pin {}".format(curr_value, led_pin))
            time.sleep(1)
    finally:
        GPIO.cleanup()  # cleanup all GPIO

if __name__ == '__main__':
    main()
button_led.py

```python
#!/usr/bin/env python3

import Hobot.GPIO as GPIO
import time

# 定义使用的GPIO通道:
# 36号作为输出,可以点亮一个LED
# 38号作为输入,可以接一个按钮
led_pin = 36 # BOARD 编码 36
but_pin = 38 # BOARD 编码 38

# 禁用警告信息
GPIO.setwarnings(False)

def main():
    prev_value = None

    # Pin Setup:
    GPIO.setmode(GPIO.BOARD)  # BOARD pin-numbering scheme
    GPIO.setup(led_pin, GPIO.OUT)  # LED pin set as output
    GPIO.setup(but_pin, GPIO.IN)  # Button pin set as input

    # Initial state for LEDs:
    GPIO.output(led_pin, GPIO.LOW)
    print("Starting demo now! Press CTRL+C to exit")
    try:
        while True:
            curr_value = GPIO.input(but_pin)
            if curr_value != prev_value:
                GPIO.output(led_pin, curr_value)
                prev_value = curr_value
                print("Outputting {} to Pin {}".format(curr_value, led_pin))
            time.sleep(1)
    finally:
        GPIO.cleanup()  # cleanup all GPIO

if __name__ == '__main__':
    main()
```

基本的配置还是一样的,引入基本的库,然后设置为BOARD模式,然后初始化两个管脚,一个用来读取按键的通断,一个用来控制LED的亮灭,之后只需要判断按键的状态来做出对应的控制LED的动作就可以了。