目录

1.1 了解树莓派引脚

1.2 用Python控制GPIO

1.3 连接LED灯

1.4 点亮LED灯

1.5 用LED灯表示摩斯电码


在本节,我们将学习如何用树莓派点亮LED灯(发光二极管)并控制其明暗程度,最后我们还会结合摩斯电码用LED灯来表示"Hello World"。该项目涉及到的材料有:

  1. 树莓派 * 1
  2. LED灯 * 1
  3. 限流电阻 * 1
  4. 杜邦线(母对母) * 3

树莓派python控制led灯 树莓派led灯阵_树莓派

1.1 了解树莓派引脚

以下是树莓派的引脚图:

树莓派python控制led灯 树莓派led灯阵_树莓派python控制led灯_02

大家可以看到,一共有40个引脚,每个引脚都带有编号,左列是奇数,右列是偶数。我们用到的引脚主要分为以下几类:

  • GPIO (General-Purpose Input/Output)引脚:用来读取或输出高低电平,我们可以用该引脚达到与其他硬件交互的目的。
  • Ground引脚:缩写为GND,用于接地。
  • 3V3和5V引脚:给其他硬件供电。

我们可以在终端中输入pinout命令来快速查看当前版本树莓派的引脚图:

树莓派python控制led灯 树莓派led灯阵_树莓派python控制led灯_03

1.2 用Python控制GPIO

我们可以使用RPi.GPIO这个库来控制GPIO,通过以下命令即可安装:

pip3 install RPi.GPIO

接着导入RPi.GPIO库(此时运行代码,如果没有任何报错,说明安装成功):

import RPi.GPIO as GPIO

在导入模块后要做的第一件事就是确定所采用的GPIO引脚编码方式:

GPIO.setmode(GPIO.BOARD)

注:树莓派的GPIO引脚有多种编码方式,如wiringPi、BCM、BOARD等。RPi.GPIO这个库提供BCM和BOARD编码方式,但在本系列教程中笔者将统一使用BOARD编码(即引脚上的编号)。使用该编码方式的好处是:树莓派各版本的BOARD编码方式是相同的,所以就算更换了树莓派版本,我们也无需修改代码。关于其他编码方式,笔者这里不作详解,大家可以自行去了解下。

确定完编码方式之后,我们就要设置所用到的GPIO引脚(先设置,再使用):

# 将引脚设置为输入模式
GPIO.setup(num, GPIO.IN)

# 将引脚设置为输出模式
GPIO.setup(num, GPIO.OUT)

我们可以通过GPIO.setup方法将相应引脚设置为输入模式或者输出模式,num即为引脚编号(请见1.1小节中的图片)。

设置完之后,我们就可以通过GPIO.input和GPIO.output来接收或者发送高低电平:

# 通过GPIO.input方法来判断接收的是高电平还是低电平
if GPIO.input(num) == GPIO.HIGH:
    ...
else:
    ...

# 通过GPIO.output方法来发送高低电平
GPIO.output(num, GPIO.HIGH)
GPIO.output(num, GPIO.LOW)

我们可以用1或者True来代替GPIO.HIGH,用0或者False来代替GPIO.LOW。

注:树莓派GPIO引脚输出的电压为3.3V。

使用完GPIO引脚后,我们要调用GPIO.cleanup()来将引脚释放掉:

# 释放GPIO引脚资源
GPIO.cleanup()

如果不释放,那么再次运行代码后,会提示当前GPIO引脚正在使用当中:

树莓派python控制led灯 树莓派led灯阵_Python_04

如果不想要出现这提示,可以使用GPIO.setwatnings(False)来屏蔽掉。不过大家应该养成好习惯,记得加入这行代码,否则可能会对树莓派造成损坏。

1.3 连接LED灯

首先我们来学习下如何区分LED灯的正负极,笔者这里提供两种方法:

  1. 长腿的是正极,短腿的是负极。
  2. 铁片小的是正极,铁片大的是负极。

树莓派python控制led灯 树莓派led灯阵_GPIO_05

本项目中所使用的黄色LED灯工作电压约为1.8V (可以使用万用表测量),而这种LED灯的额定电流为20mA,我们应该加上一个限流电阻来防止LED灯被烧坏。

已知树莓派GPIO高电平输出电压为3.3V,黄色LED等的工作电压为1.8V,额定电流为20mA。根据欧姆定律

树莓派python控制led灯 树莓派led灯阵_Python_06

,限流电阻最小应为:(3.3-1.8) / 0.02 = 75 Ω

注:不同颜色的LED灯工作电压不同,请大家根据实际情况挑选合适的限流电阻。如果电阻太小,LED灯会过亮容易烧坏;如果电阻过大,LED灯会太暗,也会导致现象不明显。一般LED灯给个2mA-10mA电流就会发亮,只是亮度不同,给20mA的话就最亮了。

笔者这里选择了一个100Ω的限流电阻。

现在我们将LED灯的两个腿插到杜邦线中(短腿黄色,长腿橙色):

树莓派python控制led灯 树莓派led灯阵_LED灯_07

再将限流电阻插到任意一个杜邦线中,笔者这里插到了连接短腿的杜邦线里:

树莓派python控制led灯 树莓派led灯阵_LED灯_08

接着将限流电阻的另一头插到剩下的一根杜邦线中:

树莓派python控制led灯 树莓派led灯阵_树莓派python控制led灯_09

接着将连接负极的线插到14号GND接地引脚中,将连接正极的线插到16号GPIO引脚中:

树莓派python控制led灯 树莓派led灯阵_LED灯_10

树莓派python控制led灯 树莓派led灯阵_Python_11

                           

到此LED灯的连接工作就结束了。

注:在以后的章节中我们会使用面包板。

1.4 点亮LED灯

要点亮LED灯,我们只需给它一个高电平即可:

import RPi.GPIO as GPIO
import time

# 设置编码方式
GPIO.setmode(GPIO.BOARD)

# 设置GPIO引脚
GPIO.setup(16, GPIO.OUT)

# 用16号引脚输出一个高电平,灯亮
GPIO.output(16, GPIO.HIGH)

# 等3秒
time.sleep(3)

# 用16号引脚输出一个低电平,灯灭
GPIO.output(16, GPIO.LOW)

# 等3秒
time.sleep(3)

# 再亮灯
GPIO.output(16, GPIO.HIGH)

# 等3秒
time.sleep(3)

# 使用结束,释放引脚
GPIO.cleanup()

这里我们还引入了time模块来控制亮灯灭灯时长。最后结束时,调用GPIO.cleanup()释放了引脚资源。

以下是代码运行后的视频:


《树莓派项目实战》第一节 点亮LED灯


1.5 用LED灯表示摩斯电码

下面是摩斯电码对照表:

树莓派python控制led灯 树莓派led灯阵_Python_12

我们将它用python字典来表示:

MORSE_DICT = {
    'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.', 'G': '--.',
    'H': '....', 'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..', 'M': '--', 'N': '-.',
    'O': '---', 'P': '.--.', 'Q': '--.-', 'R': '.-.', 'S': '...', 'T': '-', 'U': '..-',
    'V': '...-', 'W': '.--', 'X': '-..-',  'Y': '-.--', 'Z': '--..',

    '1': '.----', '2': '..---', '3': '...--', '4': '....-', '5': '.....',
    '6': '-....', '7': '--...', '8': '---..', '9': '----.', '0': '-----',
}

那怎么用灯来表示摩斯电码呢?我们来看下图片上的这段话:

树莓派python控制led灯 树莓派led灯阵_树莓派_13

翻译出来是这样的:

  1. 一个点算一个单位。
  2. 一个横线算三个单位。
  3. 单个字母的摩斯电码,其点线(点点或线线)间的间隔算一个单位。
  4. 字母与字母间的间隔算三个单位。
  5. 单词与单词间的间隔算七个单位。

好,那么我们规定一个单位算0.1秒,碰到点和线就亮相应时长,碰到间隔就灭相应时长。

现在我们就将LED灯摩斯电码结合起来表示"Hello World"这句话。以下是完整代码:

import RPi.GPIO as GPIO
import time

# 设置编码方式
GPIO.setmode(GPIO.BOARD)

# 设置GPIO引脚
GPIO.setup(16, GPIO.OUT)

# 摩斯电码对照字典
MORSE_DICT = {
    'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.', 'G': '--.',
    'H': '....', 'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..', 'M': '--', 'N': '-.',
    'O': '---', 'P': '.--.', 'Q': '--.-', 'R': '.-.', 'S': '...', 'T': '-', 'U': '..-',
    'V': '...-', 'W': '.--', 'X': '-..-',  'Y': '-.--', 'Z': '--..',

    '1': '.----', '2': '..---', '3': '...--', '4': '....-', '5': '.....',
    '6': '-....', '7': '--...', '8': '---..', '9': '----.', '0': '-----',
}

# 时长单位
UNIT = 0.1


def get_morse(sentence):
    """获取每个字母的摩斯电码"""
    morse_list = []

    for c in sentence.upper():
        morse = MORSE_DICT.get(c)

        if morse:
            morse_list.append(morse)
        else:
            morse_list.append(' '*7)

    print(morse_list)
    return morse_list


def show(morse_list):
    """遍历列表,用LED灯表示点、线和间隔"""
    for element in morse_list:

        # 单词间的间隔
        if ' ' in element:
            GPIO.output(16, GPIO.LOW)
            time.sleep(UNIT*7)

        # 点和线
        else:
            for i, c in enumerate(element):
                if c == '.':
                    GPIO.output(16, GPIO.HIGH)
                    time.sleep(UNIT)

                elif c == '-':
                    GPIO.output(16, GPIO.HIGH)
                    time.sleep(UNIT*3)

                # 点或线如果是最后一个的话,则不用算间隔
                if i != len(element)-1:
                    GPIO.output(16, GPIO.LOW)
                    time.sleep(UNIT)

            # 字母间的间隔
            GPIO.output(16, GPIO.LOW)
            time.sleep(UNIT*3)


def main():
    """主逻辑"""
    sentence = 'Hello World'            # 1

    morse_list = get_morse(sentence)    # 2
    show(morse_list)                    # 3

    GPIO.cleanup()                      # 4


if __name__ == '__main__':
    main()

1. 首先构造"Hello World"字符串,存储在sentence变量中。

2. 通过get_morse方法,循环遍历字符串中每个字符,转换成摩斯电码后添加到morse_list列表中。由于字典中的键是大写的,所以要调用upper()将sentence中的字符变为大写。碰到空字符时,就表示遇上了单词间的间隔,所以我们就往列表中添加7个空字符(1个字符为0.1秒)。"Hello World"打印出来的morse_list是这样的:

['....', '.', '.-..', '.-..', '---', '       ', '.--', '---', '.-.', '.-..', '-..']

3. 接着我们调用show()来表示morse_list中的点、线以及间隔。

  • 循环列表,如果元素中存在空字符' ',那么就表明现在碰到了单词间的间隔,输出低电平让灯灭掉,持续0.7秒;
  • 如果不存在空字符,那么就是字母的摩斯电码,于是我们循环该摩斯电码中的每个字符。如果是点'.',那么输出一个高电平,持续0.1秒;如果是线'-',那么点亮0.3秒。点或线表示完之后就要表示间隔,持续0.1秒。不过如果点或者线已经是该摩斯电码中最后一个了的话,则无需表示间隔。
  • 单个字母的摩斯电码循环完毕之后,就可以表示字母间的间隔了,持续0.3秒。