Skip to content

Latest commit

 

History

History
820 lines (536 loc) · 56.4 KB

File metadata and controls

820 lines (536 loc) · 56.4 KB

十一、光线、指示器和显示信息

在上一章中,我们探索并学习了如何使用光耦、晶体管和继电器电路,以及这三个组件如何协同工作以创建通用的继电器控制模块。我们还介绍了如何使用万用表测量负载的当前使用情况,以便您能够明智地决定应使用何种方法或组件来切换或控制外部负载。

在本章中,我们将介绍使用 RGB LED 进行着色的两种可选方法,并创建一个简单的应用程序来监控 Raspberry Pi 的 CPU 温度,并在 OLED 显示屏上显示结果。最后,我们将看到如何将 PWM 和蜂鸣器结合起来产生声音。

完成本章后,您将拥有知识、经验和代码示例,可以根据需要将信息显示给用户、制造噪音或简单地用灯光使他们眼花缭乱的情况来调整自己的项目!此外,如果您希望进一步探讨这些主题,您所学到的内容将适用于其他类型的兼容显示器和照明设备。

本章将介绍以下主题:

  • 使用 RGB LED 进行着色
  • 用 SPI 控制多色 APA102 LED 带
  • 使用 OLED 显示器
  • 使用蜂鸣器和 PWM 发出声音

技术要求

要执行本章中的练习,您需要以下内容:

  • 树莓皮 4 B 型
  • Raspbian OS Buster(带桌面和推荐软件)
  • Python 3.5 版的最低版本

这些需求是本书中代码示例的基础。只要您的 Python 版本是 3.5 或更高版本,就可以合理地期望代码示例在 Raspberry Pi 3 Model B 或不同版本的 Raspbian OS 上无需修改即可工作。

您可以在 GitHub 存储库中的chapter08文件夹中找到本章的源代码:https://github.com/PacktPublishing/Practical-Python-Programming-for-IoT

您需要在终端中执行以下命令,以设置虚拟环境并安装本章代码所需的 Python 库:

$ cd chapter08               # Change into this chapter's folder
$ python3 -m venv venv       # Create Python Virtual Environment
$ source venv/bin/activate   # Activate Python Virtual Environment
(venv) $ pip install pip --upgrade        # Upgrade pip
(venv) $ pip install -r requirements.txt  # Install dependent packages

以下依赖项是从requirements.txt安装的:

本章练习所需的电子元件包括:

让我们从了解如何使用 PWM 设置 RGB LED 的颜色开始。

使用 RGB LED 和 PWM 进行着色

在本节中,我们将学习如何使用脉宽调制脉宽调制)与 RGB LED 一起创建不同的颜色。作为提醒,PWM 是一种产生可变电压的技术,当应用于 LED 和电阻器对时,可用于改变 LED 的亮度。在第 2 章*Python 和 IoT 入门中,我们首先讨论了 PWM,并使用它来改变 LED 的亮度。*然后我们在第 5 章中更深入地介绍了 PWM,将树莓 Pi 连接到物理世界。

RGB LED 是一个封装中的三个单色 LED(红色、绿色和蓝色),如图 8.1所示:

Figure 8.1 – RGB LED varieties

您会注意到显示了两种类型:

  • 公共阴极:红色、绿色和蓝色 LED 共用一个公共阴极支腿,表示公共支腿连接到负极或接地电压源阴极=负极。
  • 公共阳极:红色、绿色和蓝色 LED 共用一个公共阳极支腿,这意味着公共支腿是连接到正极电压源的部件—阳极=正极。

The common leg will be the longest of the four legs. If the longest leg is closest to the flat side of the LED's casing, it's a common cathode type. On the other hand, if the longest leg is nearer the lip (and hence furthest from the flat side), it's a common anode type.

我们之前在第 5 章中学习过,将树莓 Pi 连接到物理世界,如何使用 PWM 设置单个 LED 的亮度,但是如果我们改变 RGB LED 中三种颜色的亮度,会发生什么情况?我们混合不同的颜色来创造新的颜色!让我们创建一个回路并开始混合。

创建 RGB LED 电路

在本节中,我们将创建一个简单的电路来控制 RGB LED,我们将使用一个公共阴极RGB LED(即,三个单独的 LED 共享一个公共 GND 连接)。

**我们首先在我们的实验板上构建电路,如图 8.2所示:

Figure 8.2 – Common cathode RGB LED schematic

以下是我们将要构建的该示意图的随附试验板布局:

Figure 8.3 – Common cathode RGB LED circuit

以下是与图 8.3中编号的黑色圆圈相匹配的步骤:

  1. 首先,将 RGB LED 放入您的实验板,注意根据其阴极支脚的位置确定 LED 的方向。

  2. 定位 200Ω电阻器(R1)。该电阻器的一端连接到 LED 的红色支脚。

  3. 定位第一个 15Ω电阻器(R2)。该电阻器的一端连接到 LED 的蓝色支脚。

  4. 定位第二个 15Ω电阻器(R3)。该电阻器的一端连接到 LED 的绿色支脚。

  5. 将 Raspberry Pi 上的接地引脚连接到负极电源轨。

  6. 将覆盆子 Pi 上的 GPIO 16 连接到您在步骤 2中放置的 200Ω电阻器(R1)的另一端。

  7. 将 RGB LED 的阴极支脚连接至负极电源轨。

  8. 将覆盆子 Pi 上的 GPIO 20 连接到您在步骤 3中放置的 15Ω电阻器(R2)的另一端。

  9. 将覆盆子 Pi 上的 GPIO 21 连接到您在步骤 4中放置的 15Ω电阻器(R3)的另一端。

在我们测试 RGB LED 电路之前,让我们简要回顾一下我们是如何获得该电路中的 200Ω和 15Ω电阻器的。200Ω电阻器(R1)的推导过程与我们在第 6 章电子 101 中为软件工程师介绍的过程相同。R2 和 R3 的 15Ω电阻器使用相同的过程推导得出,不同之处在于蓝色和绿色 LED 计算中使用的典型正向电压为 3.2 伏。如果您研究样本数据表,您会注意到蓝色和绿色 LED 的正向电压列出了 4.0 伏的最大正向电压。即使在 3.2 伏的典型值下,我们也非常接近树莓皮 GPIO 引脚的 3.3 伏。如果你不幸得到一个 RGB LED 需要超过 3.3 伏的蓝色或绿色 LED,它将无法工作,虽然我从来没有遇到过一个…还。

现在我们准备测试我们的 RGB LED。

运行和探索 RGB LED 代码

现在您已经准备好了电路,让我们运行示例代码。我们的示例将点亮 LED 并使其交替不同的颜色。以下是要遵循的步骤:

  1. 运行chapter08/rgbled_common_cathode.py文件中的代码,您应该观察 RGB LED 循环颜色。注意前三种颜色,分别是红色、绿色和蓝色。

To use a common anode RGB LED, it needs to be wired differently than shown in Figure 8.2 — the common anode leg must go to the +3.3V pin on your Raspberry Pi, while the GPIO connections remain the same. The other change is in code where we need to invert the PWM signals—you will find a file called rgbled_common_anode.py in the chapter08 folder with the differences commented.

  1. 如果您的前三种颜色不是红色、绿色,然后是蓝色,则 RGB LED 的支脚顺序可能与图 8.1中的 RGB LED 和图 8.2中的电路顺序不同。您需要做的是更改代码中的 GPIO 引脚编号(请参阅以下代码段),然后重新运行代码,直到颜色顺序正确为止。
  2. 在红色、绿色和蓝色循环之后,RGB LED 将在程序完成之前设置彩虹颜色的动画。

让我们讨论一下代码中有趣的部分,看看它是如何工作的:

在第(1)行中,我们从PIL.ImageColor模块导入getrgbgetrgb为我们提供了一种方便的方法,将常见颜色名称(如红色)或十六进制值(如#FF0000转换为 RGB 分量值,如(255,0,0):

from time import sleep
import pigpio
from PIL.ImageColor import getrgb    # (1)

GPIO_RED = 16
GPIO_GREEN = 20
GPIO_BLUE = 21

pi.set_PWM_range(GPIO_RED, 255)      # (2)
pi.set_PWM_frequency(GPIO_RED, 8000)
# ... truncated ... 

从第(2)行开始,我们为每个 GPIO 引脚明确配置 PWM(占空比范围 255 和频率 8000 是 PiGPIO 的默认值)。PWM 占空比范围 0 到 255 完美地映射到 RGB 组件颜色值范围 0…255,我们将很快看到如何设置每个彩色 LED 的单独亮度。

在下面的代码第(3)行中,我们有set_color()定义,它负责设置 RGB LED 的颜色。color参数可以是yellow等常用颜色名称,也可以是# FFFF00 等十六进制值,也可以是getrgb()可以解析的多种格式之一(常见格式列表请参见rgbled_common_cathode.py源文件):

    def     set_color(color):                                     # (3)        
                rgb = getrgb(color)                                   
                    print    (    "LED is {} ({})"    .format(color, rgb))
    pi.set_PWM_dutycycle(GPIO_RED,   rgb[    0    ])              # (4)
                pi.set_PWM_dutycycle(GPIO_GREEN, rgb[    1    ])
    pi.set_PWM_dutycycle(GPIO_BLUE,  rgb[    2    ])

在第(4)行中,我们将看到如何使用带有单个 GPIO 引脚的 PWM 来设置 RBG LED 的颜色。以 yellow 为例,我们看到以下内容:

  • GPIO_RED设置为 0 的占空比。
  • GPIO_GREEN设置为 255 的占空比。
  • GPIO_BLUE设置为 255 的占空比。

绿色和蓝色的占空比值为 255 意味着这些 LED 完全亮起,正如我们所知,混合绿色和蓝色会变成黄色。

在浏览源文件时,您将在第(6)和(7)行遇到另外两个函数:

def     color_cycle    (colors=("red", "green", "blue"), delay_secs=1):   # (6)
    # ...truncated...

def rainbow_example(loops=1, delay_secs=0.01):                    # (7)
    # ...truncated...

这两种方法都委托给set_color()color_cycle()循环通过color参数提供的颜色列表,而rainbow_example()生成并循环通过一系列颜色来生成彩虹序列。这些函数是我们在步骤 1中运行代码时生成的光序列。

我们的 RGB LED 电路具有局限性和缺点:

  • 首先,每个 RGB LED 需要三个 GPIO 引脚。
  • 其次,我们用电阻器将电流限制在 8mA,因此我们无法实现单个 LED 的最大潜在亮度(我们需要约 20mA 的全亮度)。

虽然我们可以引入晶体管(或适当的多通道 LED 驱动 IC)来增加电流,但我们的电路很快就会变得笨重!幸运的是,我们还有另一种方法可以用 LED 创造颜色,那就是可寻址 LED,我们将在下一步介绍。

用 SPI 控制多色 APA102 LED 带

APA102 是一种可寻址多色(RGB)LED,使用串行外围接口SPI进行控制。简单地说,我们向 LED 发送指令,询问其显示什么颜色,而不是像上一个示例中那样使用 PWM 单独控制 LED 的三个红、绿、蓝支脚中的每一个。

如果您需要快速复习 SPI,我们将在第 5 章将您的树莓 Pi 连接到物理世界中介绍。我们还将在探索 APA102 特定代码之后,在 APA102、Raspberry Pi 和 Python 的上下文中进一步讨论 SPI。

APA102 LED 还可以连接或链接在一起,以创建 LED 条带或 LED 矩阵,从而创建动态和多 LED 照明和显示解决方案。无论 LED 是如何排列的,我们都使用一种通用技术来控制它们,即向 APA102 LED 链发送多组指令。每个单独的 LED 消耗一条指令,并将其余指令传递给上游 LED。我们将看到这个想法在行动,因为我们很快与 APA102 LED 带工作。

APA102 LEDs also go by the name Super LEDs, DotStar LEDs, and sometimes Next Generation NeoPixels. There is also another addressable LED, the WS2812, also known as a NeoPixel. While similar in principle and operation, W S2812 RGB LEDs are not compatible with the APA102.

让我们创建一个电路并运行代码来控制我们的 APA102 LED 条。

创建 APA102 电路

在本节中,我们将创建 APA102 电路,如下图所示。我们将在试验板上分两部分进行此操作:

Figure 8.4 – APA102 LED strip circuit schematic

让我们从第一部分开始,这将是放置元件并连接逻辑电平转换器的低压侧:

Figure 8.5 – APA102 LED circuit (part 1 of 2)

以下是要遵循的步骤。步骤编号与图 8.5中编号的黑色圆圈相匹配:

  1. 将逻辑电平转换器(逻辑电平移位器)放入试验板,将低压侧朝向覆盆子 Pi。不同的逻辑电平转换器可能有不同的标签,但是,应该清楚哪个是低压侧。在我们的示例中,一侧有一个LV低压)端子,而另一侧有一个HV高压)端子,用于区分两侧。
  2. 连接左侧和右侧电源导轨上的负极导轨。
  3. 将 Raspberry Pi 上的 3.3 伏针脚连接到左侧电源导轨的正极导轨上。
  4. 将逻辑电平转换器上的低压端子连接到左侧电源轨的正极轨上。
  5. 将 Raspberry Pi 上的MOSI主输出从输入引脚)连接至逻辑电平转换器上的 A2 端子。
  6. 将 Raspberry Pi 上的SLCK串行时钟引脚)连接至逻辑电平转换器上的 A1 端子。
  7. 将逻辑电平转换器上的 GND 端子连接至左侧电源导轨上的负极导轨。
  8. 将左侧电源导轨上的负极导轨连接到 Raspberry Pi 上的 GND 引脚。

现在我们已经将逻辑电平转换器的低压侧连接到 Raspberry Pi,接下来我们将高压侧连接到 APA102 LED 条。提醒一下,Raspberry Pi GPIO 引脚的工作电压为 3.3 伏(因此为电压),而 APA102 的工作电压为 5 伏(因此为电压):

Figure 8.6 – APA102 LED circuit (part 2 of 2)

以下是构建的第二部分要遵循的步骤。步骤编号与图 8.6中编号的黑色圆圈相匹配:

  1. 将逻辑电平转换器的高压端子连接至右侧电源轨的正极轨。
  2. 将一条跨接导线从端子 B2 连接到试验板上一个未使用的行(在图中,如 G16 孔所示)。
  3. 将另一根跨接导线从端子 B1 连接到试验板上未使用的一行(在图中,该跨接导线显示在孔 H14 处)。
  4. 将逻辑电平转换器高压侧的 GND 端子连接至右侧电源导轨的负极导轨。
  5. 将电源的正极输出连接到右侧电源导轨的正极导轨。
  6. 将电源的负极输出连接到右侧电源导轨的负极导轨。
  7. 将 APA102 LED 带的 VCC 端子或导线连接到右侧电源导轨的正极导轨上。

Your APA102 must be connected the correct way around. You will notice the arrows on the APA102 LED strip shown in Figure 8.4. These arrows indicate the direction of the data flow. Make sure your APA102 LED strip arrows match the illustration (that is, the arrows are pointing away from the breadboard). If your APA102 does not have the arrows, look at the naming of the terminals. One side of an LED strip may have CI/DI (I = Input), while the other side has DO/CO (O = Output). It's the Input side we need to connect to the logic level converter.

  1. 将 APA102 LED 条的CI时钟输入端子或导线连接到您在步骤 3处放置的导线,该导线连接回逻辑电平转换器的 B1 端子。
  2. 将 APA102 LED 条的DI数据输入端子或导线连接到您在步骤 2处放置的导线,该导线连接回逻辑电平转换器的 B2 端子。
  3. 最后,将 APA102 LED 带的 GND 端子或导线连接到右侧电源导轨的负极导轨上。

做得好!您现在已经完成了 APA102 LED 条带电路。当您完成这个电路构建时,您会注意到我们使用的是逻辑电平转换器。这是因为 APA102 需要 5 伏逻辑才能正确操作*。APA102 数据表明确提到最小逻辑电压为 0.7 VDD,即 0.7 x 5 伏=3.5 伏,高于 Raspberry Pi 的 3.3 伏逻辑电平。*

*If you need a refresher on logic-levels and logic-level conversion, refer back to Chapter 6, Electronics 101 for the Software Engineer.

让我们考虑一下情况(如果你想知道)3.3 伏的电压仅仅略小于 3.5 伏特,那就足够了吗?你可以尝试用 3.3 伏电压控制 APA102,它可能会给你带来一定程度的成功。然而,您可能也会遇到一些随机效应和混乱-例如,随机 LED 没有按预期打开或关闭,闪烁的 LED,或 LED 显示颜色错误。不幸的是,APA102 是不兼容 3.3 伏的 5 伏逻辑设备之一,因此我们必须采取额外措施,使用逻辑电平转换器以满足其 3.5 伏最低逻辑电平要求。

现在您已经构建了 APA102 电路,接下来我们将讨论为该电路供电需要考虑的事项。

为 APA102 电路供电

第 7 章中,我们讨论了了解您正在使用的“负载”的当前需求的重要性。让我们将这一学习应用到我们的 APA102 LED 带上,以便正确地为其供电。我们的示例假设一个 LED 条包含 60 个 LED,但是,您需要根据条上 LED 的数量调整计算。

*举例来说,我们有以下几点:

  • 带有 60 个 LED 的 APA102 LED 条。
  • 每个 LED(平均)使用的最大电流为 25mA(根据数据表并通过测量确认)。
  • 闲置时 LED 条消耗约 15mA(没有 LED 点亮)。

A single RGB LED uses its maximum current when it is set to the color white, which is when each individual LED (red, green, and blue) are at their full brightness.

使用上述数值,我们可以计算出 60 个 LED 的预期最大电流需求,刚好超过 1.5 安培:

如果我们假设我们使用的是一个实验板电源,那么如果我们保守地假设我们的实验板电源供应器最多只能供应 700mA 左右,我们就无法实际地将 60 个 LED 条上的所有 LED 都打开到全白色。如果我们这样做,那么(取决于电源),如果内部过载保护启动,它可能会关闭,它可能会冒出一股烟,或者它可能会限制其输出电流,我们可能会观察到 LED 显示为红色而不是白色。

让我们反向计算 700mA 电源可以供电的 LED 的安全数量:

如果我们减去 2 个 LED(50mA)作为一个小的安全缓冲,我们得到 25 个 LED。记住这个数字(或您计算的数字),因为我们下一步运行示例代码时需要它。

在计算了可以与电源一起使用的安全指示灯的数量之后,我们现在准备配置并运行 Python 示例。

配置和运行 APA102 LED 条带代码

现在,您已经准备好了电路,并且我们的 LED 带达到了预期的当前使用量,让我们配置并点亮我们的 LED 带:

  1. 编辑chapter08/apa102_led_strip.py文件,并在文件顶部附近查找以下行。将该数量调整为您之前计算的安全 LED 数量,或者如果您的条带具有适当的电源,则调整条带上的 LED 数量:
NUM_LEDS = 60     # (2)
  1. 保存编辑并运行代码。如果所有设备都连接正确,则应通过红色、绿色和蓝色循环观察条带上的 LED,然后执行几个不同的灯光顺序。

If your LED strip is not working, check out the APA102 LED strip troubleshooting tips later in the section.

如果您的条带没有按该顺序显示红色、绿色和蓝色,那么您需要调整代码以设置正确的顺序-我将在代码中的什么位置向您展示,当我们稍后讨论代码部分时,您可以调整 LED 顺序。

现在,我们在代码中配置了安全数量的 LED,让我们来浏览一下代码,看看它是如何工作的。

APA102 LED 条带代码演练

从下面代码的第(1)行开始,我们有了导入。我们将使用一个 Pythondeque集合实例(为了简单起见,我将仅将 is 称为数组)在内存中对 APA102 LED 条进行建模-我们将建立并操作我们希望每个 LED 在该数组中显示的颜色顺序,然后再将其应用于 LED 条。然后,我们从 PIL 库导入getrgb函数,用于处理颜色格式(正如我们在前面的 RGB LED 示例中所做的):

# ...truncated...
    from     collections     import     deque                                       # (1)
        from     PIL.ImageColor     import     getrgb
    from     luma.core.render     import     canvas
    from     luma.led_matrix.device     import     apa102
    from     luma.core.interface.serial     import     spi, bitbang

最后,三个luma进口用于 APA102 LED 条带控制。Luma 是一个成熟的高级库,用于使用 Python 处理一系列常见的显示设备。它支持 LCD、LED 条带和矩阵,以及更多,包括 OLED 显示器,我们将在本章后面介绍。

在本章中,我们只能对使用 Luma 库所能做的事情略知一二,因此我鼓励您探索它的文档和示例范围——您可以在本章末尾的进一步阅读部分找到链接。

接下来,我们来到下面代码中的第(3)行,其中我们将color_buffer分配给deque的一个实例,该实例使用与条带中的 LED 相同数量的元素进行初始化。每个元素默认为黑色(即 LED 熄灭):

# ...truncated...
color_buffer = deque([    'black'    ]*NUM_LEDS,     maxlen    =NUM_LEDS)          # (3)

在下面代码的第(4)行中,我们开始创建 APA102 的软件接口。这里,我们正在创建一个spi()实例,表示 Raspberry Pi 上的默认硬件 SPI0 接口。要使用此接口,您的 APA102 必须连接到 Raspberry Pi 上的 SPI 引脚,如下所示:

  • DI 连接到 MOSI
  • 连接到 SCLK 的 CI

以下代码段port=0device=0与 SPI0 接口相关:

    # ...truncated...
    serial = spi(    port    =    0    ,     device    =    0    ,     bus_speed_hz    =    2000000    )           # (4)    

bus_speed_hz参数设置 SPI 接口的速度,在我们的示例中,我们将其从默认值 8000000 降低到 2000000,以确保逻辑电平转换器能够工作。并不是所有的逻辑电平转换器都是相同的,它们将具有转换逻辑电平的最大速度。如果 SPI 接口的工作速度超过逻辑电平转换器的转换速度,我们的电路将无法工作。

在下面代码的第(5)行中(注释掉了),我们有一个软件替代硬件 SPI,称为 big banging,它将以牺牲速度为代价在任何 GPIO 引脚上工作。这类似于我们在第 5 章中讨论的软件与硬件 PWM 权衡,将树莓 Pi 连接到物理世界:

    # ...truncated...
        # serial = bitbang(SCLK=13, SDA=6)                             # (5)

# ...truncated...
    device = apa102(    serial_interface    =serial,     cascaded    =NUM_LEDS)    # (6)

在前面代码的第(6)行中,我们创建了一个apa102类的实例,指定了我们刚刚创建的serial实例以及条带中的 LED 数量。从代码的这一点开始,为了与我们的 APA102 LED 条交互,我们使用了device实例。

为了初始化我们的 LED 条,在下面代码的第(7)行中,我们调用device.clear()并将默认的全局对比度设置为 128(因此,半亮度)。您可以调整此级别,以找到适合的亮度,请记住,对比度/亮度越高,当前使用的亮度就越多。请注意,在我们之前计算安全 LED 数量时,计算中使用的每个 LED 25mA 假定最大亮度(即 255):

device.clear()                                                       # (7)
    contrast_level =     128         # 0 (off) to 255 (maximum brightness)
    device.contrast(contrast_level)

在下面代码的第(8)行中,我们有set_color()函数。我们使用此函数将单个或所有元素设置为color_buffer数组中的指定颜色。这就是我们如何在内存中建立我们希望 APA102 LED 条显示的颜色排列:

    def     set_color(color=    'black'    , index=-    1    ):                              # (8)
                    if     index == -    1    :
            global     color_buffer
        color_buffer = deque([color]*NUM_LEDS,     maxlen    =NUM_LEDS)
        else    :
        color_buffer[index] = color

现在,我们将跳转到update()函数的下面代码块中的第(12)行。此函数通过color_buffer循环,并使用代表我们的 APA102 的 Lumadevice实例,为设备提供颜色,以便使用draw.point((led_pos, 0), fill=color)显示。这就是 Luma 库的神奇之处——它为我们提供了一个非常简单的软件界面,使我们免受低级 APA102 和 SPI 数据和硬件协议的影响。

If you want to learn more about lower level SPI use and protocols, then APA102 is a good place to start. Start by reading the APA102 datasheet for its data protocol, then find a simple APA102 module on pypi.org or GitHub and review its code. There is also an APA102 example that can be found on the PiGPIO website — a link is included in the Further reading section.

重要的是要记住,更改color_buffer后需要调用update()

    def     update():                                                       # (12)
                    with     canvas(device)     as     draw:
            for     led_pos     in     range(    0    , len(color_buffer)):
            color = color_buffer[led_pos]

            #    # If your LED strip's colors are are not in the expected
                    ## order, uncomment the following lines and adjust the indexes
                    ## in the line color = (rgb[0], rgb[1], rgb[2])
                    # rgb = getrgb(color)
                    # color = (rgb[0], rgb[1], rgb[2])
                    # if len(rgb) == 4:
                    #     color += (rgb[3],)  # Add in Alpha

                        draw.point((led_pos,     0    ),     fill    =color)

如果由于某种原因,您发现您的 LED 条颜色不是标准的红色、绿色和蓝色顺序,那么前面注释掉的代码部分可用于更改颜色顺序。我从来没有遇到过非标准的 APA102,但我读过关于可寻址 RGB LED 具有非标准顺序的文章,所以我想我应该把这段代码放进去,以防万一。

继续到第(9)、(10)和(11)行,我们有三个简单操作color_buffer的函数:

    def     push_color(color):                                           # (9)        
                color_buffer.appendleft(color)

    def     set_pattern(colors=(    'green'    ,     'blue'    ,     'red'    )):               # (10)             
        range(    0    , int(ceil(float(NUM_LEDS)/float(len(colors))))):
            for     color     in     colors:
            push_color(color)

def rotate_colors(count=1):                                 # (11)
    color_buffer.rotate(count)

第(9)行中的push_color(color)在索引 0 处将新颜色推入color_buffer,而第(10)行中的set_pattern()用重复的颜色图案序列填充color_buffer。第(11)行中的rotate_colors()旋转color_buffer中的颜色(并将其环绕—最后一个颜色变为第一个颜色)。您可以使用计数值<0 向后旋转。

最后,在源代码的末尾,我们提供了以下函数,这些函数提供了您在运行该文件时看到的示例。这些功能使用前面讨论的功能组合来控制 LED 条:

  • cycle_colors(colors=("red", "green", "blue"), delay_secs=1)
  • pattern_example()
  • rotate_example(colors=("red", "green", "blue"), rounds=2, delay_secs=0.02)
  • rainbow_example( rounds =1, delay_secs=0.01)

我们将通过一些关于 APA102 使用 SPI 接口的总结说明来完成对 APA102 的介绍。

APA102 与 SPI 接口的讨论

如果你回想一下第 5 章将你的树莓圆周率连接到物理世界,在那里我们讨论了串行外围接口SPI,你可能还记得我们提到过它使用四根电线进行数据传输。但是,如果您考虑我们的电路在 Ty9 T9 中,图 8.6 是 T10T,我们只使用两条线(DI 和 CI),而不是四根。发生什么事?

以下是 APA102 的 SPI 映射:

  • 您的 Raspberry Pi 上的(MOSI中的主输出从机连接到 APA102 上的DI中的数据。在这里,您的 Raspberry Pi 是向条带上的APA102 LED 发送数据。
  • 由于 APA102 不需要将数据发送回 Raspberry Pi,所以主进从出MISO未连接。
  • 将覆盆子 Pi 上的 SCLK 连接到 APA102 上(CI中的时钟。
  • 客户端启用/从机选择CE/SS未连接。

最后一行 CE/SS 非常重要,值得进一步讨论。主设备使用 CE/SS 通道通知特定的从设备它将要接收数据。正是这种机制允许单个 SPI 主机控制多个 SPI 从机。

但是,我们没有(也不能)将 CE/SS it 与 APA102 一起使用,因为我们没有地方连接 CE/SS 引脚。这意味着 APA102 总是列出来自主机的指令,有效地占用了 SPI 通道。

如果我们使用的是 APA102(或任何没有 CE/SS 的设备),那么我们不能将多个 SPI 设备连接到主机的硬件 SPI,除非我们采取额外步骤。部分方案如下:

  • 如果性能降低没有负面影响,请在通用 GPIO 引脚上使用大碰撞。
  • 在 Raspberry Pi 上启用硬件 SPI1。默认不启用,需要编辑/boot/config.txt。如果您在网上搜索树莓 Pi enable SPI1,您将找到相关说明和提示。
  • 查找包含启用引脚的逻辑电平转换器,并编写代码以作为代理 CE/SS 手动控制该引脚。

我们将在 APA102 的本节中总结一些故障排除技巧。

APA102 LED 带故障排除提示

如果您无法点亮 APA102,或者您发现随机 LED 没有打开或关闭,或者它们显示意外的颜色或随机闪烁,请尝试以下操作:

  • APA102 需要 5 伏逻辑:确保您使用的是逻辑电平转换器,且其连接方式正确-高压至 5 伏,低压至 3.3 伏。
  • 确保 APA102 的 DI/CI 侧连接至逻辑电平转换器。
  • 确保你的电源能提供足够的电流。例如,电流或电压不足会使白色看起来更像红色。
  • 确保电源的接地连接到 Raspberry Pi 上的接地引脚。
  • 如果您使用的是大爆炸,请转到硬件 SPI。
  • 如果使用硬件 SPI(即创建spi()类的实例),请尝试以下操作:
    • 如果您收到错误SPI 设备未找到,请确保已在 Raspbian 操作系统中启用 SPI。我们在第 1 章中介绍了如何设置您的开发环境。
    • 如果您以前使用 GPIO 8、9、10 或 11 进行常规 I/O,请按照上述要点禁用并重新启用 SPI 接口,或者重新启动 Raspberry Pi 以重置硬件 SPI 接口。
    • 如果您的逻辑电平转换器无法跟上,请尝试降低 SPI 总线速度,也就是说,它无法将 3.3 伏到 5 伏的信号转换为 SPI 接口产生信号的速度(提示:将serial = spi(port=0, device=0, bus_speed_hz=2000000)中的bus_speed_hz参数降低到 1000000 或 500000)。
    • 将 APA102 的 DI 和 CI 直接连接到 Raspberry Pi 上的 SDA 和 SCLK。这里的目标是绕过逻辑电平转换器,将其排除在问题之外。

做得好!这是一个关于 APA102 的冗长而详细的章节。除了 APA102 本身,我们还介绍了许多概念,包括如何计算 LED 条的功率需求,以及 Luma 库的介绍,Luma 库可用于控制除 APA102 之外的许多不同照明和显示设备。然后,我们总结了一些实用的故障排除技巧,以防您的 APA102 电路、设置或代码在第一次运行时无法正常工作。

所有这些知识和经验将适用于您承担的类似照明项目和基于 SPI 的项目。特别是,它将是一个有用的参考,以计算照明项目的电力需求和故障排除电路和代码时,他们不工作。它还提供了基本的基础,我们将在下一节中介绍如何将 OLED 显示器与 Raspberry Pi 接口。

使用 OLED 显示器

OLED有机 LED显示器是一种用于制作屏幕的技术。我们的示例将使用 SSD1306,这是一种单色 128x64 像素显示器,但是,该信息也将应用于其他 OLED 显示器。

我们的示例程序将读取 Raspberry Pi 的 CPU 温度,并将其与温度计图标一起显示在 OLED 显示屏上。我们将假设 OLED 将使用 I2C 接口连接,但是,如果您使用spi()实例(如 APA102 示例中的实例)作为serial对象,则 SPI 接口设备也应兼容。更改 Luma 库使用的交互方法的能力意味着您可以将现有代码与兼容的显示设备一起重用,而代码更改最少。

我们将首先将 OLED 显示器连接到 Raspberry Pi,并验证其是否已连接。

连接 OLED 显示器

让我们将 OLED 显示器连接到 Raspberry Pi,如图 8.7 所示:

Figure 8.7 – I2C OLED display circuit IMPORTANT NOTE ON POWERING YOUR OLED: Our circuit, shown in Figure 8.6, and the associated discussion uses a 5-volt power supply. If you consult the SSD1306 OLED datasheet mentioned at the beginning of this chapter, you will discover that it mentions a minimum supply voltage of 7 volts. Furthermore, you will find other sources and SSD1306 OLED modules that indicate different voltage requirements. Please consult the documentation or place of purchase to obtain the correct operating voltage for your OLED and adjust the supply voltage as required (steps 7 and 8 in the following list).

您可以通过以下步骤连接 OLED,这些步骤对应于图 8.7中编号的黑色圆圈:

  1. 连接左侧和右侧电源导轨上的负极导轨。

  2. 将覆盆子 Pi 的 SDA1(数据)引脚连接到试验板上的空行中。

  3. 将 OLED 显示器的 SDA(数据)端子或导线连接到步骤 2中使用的同一行。

  4. 将覆盆子圆周率的 SCL1(时钟)引脚连接到面包板上的一个空行中。

  5. 将 OLED 显示器的 SCL(时钟)端子或电线连接到步骤 4中使用的同一行。

  6. 将 Raspberry Pi 上的 GND 引脚连接到左侧电源导轨的负极导轨上。

  7. 将 5 伏电源的正极输出连接到右侧电源导轨的正极导轨上。

  8. 将 5 伏电源的负极输出连接到右侧电源导轨的负极导轨上。

  9. 将 OLED 显示器的 GND 端子或导线连接到右侧电源导轨的负极导轨上。

  10. 将 OLED 显示器的 VCC 端子或导线(也可能被命名为 VDD、Vin、V+,或表示电压输入的类似物)连接到右侧电源导轨的正极导轨上。

干得好这就完成了我们的 OLED 电路。如您所见,我们通过 5 伏电源为 OLED 供电,但是 SDA(数据)/SLC(时钟)通道直接连接到 Raspberry Pi。与前一节中使用的 APA102 LED 条不同,SSD1306 OLED 与 3.3 伏逻辑兼容,因此,我们不需要逻辑电平转换器来转换时钟和数据通道上的逻辑电平电压。

让我们简要地考虑当前对 SSD1306 OLED 的要求。我的测试产生了以下电流测量结果:

  • 黑屏:~3mA
  • 白色屏幕(每个像素上):~27mA

在最大电流使用量为~27mA 时,您可以尝试将+5V 连接到 Raspberry Pi 的 5 伏引脚,但请记住,这将带走 Raspberry Pi 的备用电流(如果 Raspberry Pi 的电源不足,则在运行代码时可能会重置)。

If you need a recap on current measurement using a digital multimeter, please refer to Chapter 7, Turning Things On and Off.

接下来,将 OLED 连接到 Raspberry Pi 的 SDA 和 SCL 引脚,我们将验证 Raspberry Pi 是否已使用i2cdetect实用程序检测到 OLED。

验证 OLED 显示器是否已连接

之前,在第 5 章中,将您的 Raspberry Pi 连接到物理世界中,我们使用i2cdetect命令行工具检查 I2C 设备是否已连接,并验证其 I2C 地址。通过在终端中运行以下命令,检查 Raspberry Pi 是否可以看到 OLED 显示屏:

$ i2cdetect -y 1

如果您的 OLED 已连接,您将看到以下输出,它告诉我们检测到 OLED 并具有十六进制地址0x3C

# ...truncated...
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- -- 
# ...truncated...

如果您的地址不同,那没关系,我们只需要在代码中调整地址,接下来我们将进行调整。

配置和运行 OLED 示例

我们将要探索的代码包含在chapter08/oled_cpu_temp.py文件中。继续之前,请查看此文件以全面了解其中包含的内容:

  1. 如果您在前面获得的 OLED I2C 地址与0x3C不同,请在源代码中找到以下行,并更新 address 参数以匹配您的 OLED I2C 地址:
serial = i2c(    port    =    1    ,     address    =    0x3C    )
  1. 运行该程序,您应该观察 CPU 温度和 OLED 显示屏上绘制的温度计图标。

一旦您在代码中配置了 OLED 显示地址并确认该示例在您的 OLED 上工作,我们就准备好查看代码并了解其工作原理。

OLED 代码演练

从导入开始,在第(1)行中,我们从PIL枕头)模块导入类,我们使用该模块创建要在 OLED 显示屏上渲染的图像。我们还从与 SSD1306 OLED 及其 I2C 接口相关的 Luma 模块导入了其他几个类(SPI 也导入以供参考)。

我们将看到如何在第(2)行中创建一个 I2C 实例,该实例表示 OLED 所连接的接口。注释掉是一种 SPI 替代方案。在第(3)行中,我们创建了一个表示 OLED 显示器的ssd1306实例,并将其分配给device变量。如果您使用的 OLED 显示器与 SSD1306 不同,则需要识别并调整ssd1306导入行,以及在第(3)行中创建的设备实例:

    from     PIL     import     Image, ImageDraw, ImageFont         # (1)
    from     luma.core.interface.serial     import     i2c, spi
    from     luma.core.render     import     canvas
    from     luma.oled.device     import     ssd1306    
    #...truncated...

    # OLED display is using I2C at address 0x3C
    serial = i2c(    port    =    1    ,     address    =    0x3C    )                  # (2)
#serial = spi(port=0, device=0)

    device = ssd1306(serial)                            # (3)
device.clear()
    print    (    "Screen Dimensions (WxH):"    , device.size)

在第(4)行中,我们遇到了get_cpu_temp()函数,该函数调用一个命令行实用程序来检索 Raspberry Pi 的 CPU 温度,然后解析并返回我们将很快用于构建显示图像的结果:

    def     get_cpu_temp():     # (4)    
                temp = os.popen(    "vcgencmd measure_temp"    ).readline()     # Eg 62.5'C
                data = temp.strip().upper().replace(    "TEMP="    ,     ""    ).split(    "'"    )
    data[    0    ] = float(data[    0    ])

        if     data[    1    ] ==     'F'    :      # To Celsius just in case it ever returns Fahrenheit
                    data[    0    ] = (data[    0    ] -     32    ) *     5    /    9
                    data[    1    ] =     'C'

                    return     (data[    0    ], data[    1    ])      # Eg (62.5, 'C')

在第(5)行的以下代码中,我们定义了影响 OLED 显示屏上显示图标的温度阈值。我们还将使用高阈值使 OLED 显示屏闪烁,以帮助创建视觉注意力捕捉器。

在第(6)行中,我们加载了三个温度计图像,并从第(7)行开始将其缩小到一个可以使用 SSD1306 OLED 的 128x64 像素尺寸的尺寸:

    # Temperature thresholds used to switch thermometer icons        
    temp_low_threshold =     60           # degrees Celsius                     # (5)
    temp_high_threshold =     85          # degrees Celsius

        # Thermometer icons
    image_high = Image.open(    "temp_high.png"    )                        # (6)
image_med  = Image.open(    "temp_med.png"    )
image_low  = Image.open(    "temp_low.png"    )

    # Scale thermometer icons (WxH)
    aspect_ratio = image_low.size[    0    ] / image_low.size[    1    ]            # (7)
height =     50
    width = int(height * aspect_ratio)
image_high = image_high.resize((width, height))
image_med  = image_med.resize((width, height))
image_low  = image_low.resize((width, height))

接下来,我们定义以下第(8)行开始的两个变量。refresh_secs是我们检查 CPU 温度并更新 OLED 显示的速率,同时high_alert用于标记违反最高温度阈值并启动屏幕闪烁:

refresh_secs = 0.5               # Display refresh rate                           #(8)
    high_alert =     False         # Used for screen blinking when high temperature

        try    :
        while         True    :
        current_temp = get_cpu_temp()
        temp_image =     None

                    canvas = Image.new(    "RGB"    , device.size,     "black"    )              # (9)
        draw = ImageDraw.Draw(canvas)                                # (10)
        draw.rectangle(((0,0), 
                   (device.size[0]-1, device.size[1]-1)), 
                   outline="white")

在第(9)行的while循环中,我们看到了 PIL 模块的使用。这里,我们使用与 OLED 设备相同的尺寸(即 SSD1306 为 128x64)创建一个空白图像,并将其存储在canvas变量中。在随后的代码中,我们在将该内存画布图像发送到 SSD1306 进行渲染之前对其进行操作。

第(10)行中创建的 draw 实例是一个用于在画布上绘制的 PIL 帮助器类。我们使用此实例在画布周围放置一个边框,稍后将使用它向画布添加文本。draw实例还可用于绘制许多其他形状,包括直线、圆弧和圆。可在进一步阅读部分找到 PIL API 文档的链接。

high_alertTrue时,以下第(11)行开始的代码块将使我们的 OLED 显示屏闪烁:

                    if     high_alert:                                     # (11)
            device.display(canvas.convert(device.mode))
            high_alert =     False        
                        sleep(refresh_secs)
                continue

从第(12)行开始,我们将从get_cpu_temp()获得的温度读数与前面定义的阈值进行比较。根据结果,我们更改将显示的温度计图像,对于高阈值破坏,我们设置high_alert = True。将high_alert设置为True将导致 OLED 显示屏在下一次循环迭代时闪烁:

            if     current_temp[    0    ] < temp_low_threshold:           # (12)
            temp_image = image_low
            high_alert =     False        

                        elif     current_temp[    0    ] > temp_high_threshold:
            temp_image = image_high
            high_alert =     True        

                        else    :
            temp_image = image_med
            high_alert =     False        

我们从下面的第(13)行开始构建显示器。我们将image_xy计算为温度计图像在显示器上居中的点,然后使用image_x_offsetimage_x_offset变量偏移该点,将图像移动到我们想要渲染的位置。

在第(14)行中,我们将温度计图像粘贴到画布上:

    # Temperature Icon 
    image_x_offset = -    40                    # (13)
    image_y_offset = +    7
    image_xy = (((device.width - temp_image.size[    0    ]) //     2    ) + 
        image_x_offset, ((device.height - temp_image.size[    1    ]) //     2    ) 
        + image_y_offset)
canvas.paste(temp_image, image_xy)      # (14)

在下面的代码块中转到第(15)行,我们创建要在 OLED 屏幕上显示的文本,并使用与图像相同的技术将文本定位在画布上的第(17)行。注意使用draw.textsize()获取文本的像素尺寸。

在第(16)行中,我们将font = None设置为使用默认系统字体作为示例,因为我无法完全确定 Raspberry Pi 上有哪些可用字体。注释掉的第(16)行之后的一行显示了使用自定义字体的示例。

Run the fc-list command in a Terminal to see a list of fonts installed on your Raspberry Pi.

最后,在第(18)行中,我们在画布上绘制文本:

    # Temperature Text (\u00b0 is a 'degree' symbol)                 # (15)
    text =     "{}\u00b0{}"    .format(current_temp[    0    ], current_temp[    1    ])     # Eg 43'C

    font = None # Use a default font.                                # (16)
# font = ImageFont.truetype(font="Lato-Semibold.ttf", size=20) 

text_size = draw.textsize(text,     font    =font)                       # (17)
text_x_offset = +    15                                      
    text_y_offset =     0
    text_xy = (((device.width - text_size[    0    ]) //     2    ) + text_x_offset, 
((device.height -  text_size[    1    ]) //     2    ) + text_y_offset)
draw.text(text_xy, text,     fill    =    "white"    ,     font    =font)                # (18)

现在我们已经到达 while 循环的末尾。在下面代码的第(19)行中,我们使用表示 SSD1306 OLED 显示器的device实例来显示canvascanvas.convert(device.mode)调用将我们创建的画布图像转换为 SSD1306 可用的格式:

    # Render display with canvas
    device.display(canvas.convert(device.mode))        # (19)
sleep(refresh_secs)

在我们完成 OLED 的探索之前,我想给大家介绍更多的例子。Luma 库包含大量示例,涵盖使用 OLED 显示器的许多方面。有关示例的链接,请参见进一步阅读

OLED 显示器成本低,体积小,功耗低,因此您经常会发现它们用于电池驱动的设备中。如果您想探索 Raspberry Pi 的其他显示选项,您可能希望调查可用的 Raspberry Pi TFT 显示器的范围(只需在 eBay.com 或 Banggood.com 等网站上搜索该术语)。这些都是全彩色迷你显示器为您的覆盆子 Pi,甚至有触摸屏选项可用。

现在,我们用 Raspberry Pi 和 Python 对照明和显示的介绍到此结束。到目前为止,您所学到的知识将使您能够使用并正确地为自己的简单 LED 照明项目供电,并利用一系列 OLED 显示器为您希望向用户显示文本和图形信息的项目供电。

为了结束本章的练习,接下来,我们将简要回顾脉宽调制脉宽调制),看看如何使用它来产生声音。

使用蜂鸣器和 PWM 发出声音

在本章的最后一节中,我们将通过一个示例介绍如何使用 PWM 制作简单的声音和音乐。我们的示例程序将在蜂鸣器上播放音阶,我们将使用一种称为铃声文本传输语言(RTTTL****的乐谱格式,这是诺基亚在智能手机之前的时代为创建铃声而开发的。正如我们所了解的,我们可以使用一个简单的 Python 库来解析 RTTTL 乐谱,并将其音符转换为 PWM 频率和持续时间,然后可以使用 PWM 频率和持续时间来关联蜂鸣器以创建可审计的曲调。

**为了通过 PWM 发出声音,我们需要一种扬声器,我们将使用所谓的无源蜂鸣器。蜂鸣器有两种基本形式:

  • 有源蜂鸣器:这些蜂鸣器包含一个内部振荡器,可产生一个设定音调。你所需要做的就是给一个活跃的蜂鸣器施加一个直流电压,它会发出噪音。
  • 被动式蜂鸣器:这些蜂鸣器不包含任何使其工作的内部智能,因此振荡必须由控制装置完成。这样做的好处是,我们可以根据自己的意愿设置和更改音调,我们可以使用 PWM 实现这一点。

现在,我们了解了一点如何使用蜂鸣器发声,让我们继续创建发声电路。

构建 RTTTL 电路

在本节中,我们将构建一个驱动无源蜂鸣器的电路。此电路如图 8.8 所示,非常类似于我们在第 7 章中所述的 MOSFET 电路开启和关闭,只是这次连接了一个蜂鸣器作为负载:

Figure 8.8 – Buzzer driver circuit Schematic

我们将通过将组件放置在试验板上开始电路构建:

Figure 8.9 – Buzzer driver circuit (part 1 of 2)

以下步骤编号与图 8.9中编号的黑色圆圈匹配:

  1. 将 MOSFET 放在试验板上,注意部件相对于支腿的方向。如果您需要帮助识别 MOSFET 的管脚,请参见第 7 章中的图 7.7**打开和关闭
  2. 将 100kΩ电阻器(R2)放入试验板。该电阻器的一端与 MOSFET 的栅极(G)支腿共用同一行。
  3. 将 1kΩ电阻器(R1)放入试验板。该电阻器的一端也与 MOSFET 的栅极(G)支腿共用同一行。
  4. 将二极管放入实验板中,阴极支腿(带束末端的支腿)指向实验板的末端。
  5. 将蜂鸣器的正极线连接到二极管阴极支脚共用的同一排。
  6. 将蜂鸣器的负极线连接到一排空的试验板上。

现在我们已经铺设了组件,让我们将它们连接起来:

Figure 8.10 – Buzzer driver circuit (part 2 of 2)

以下步骤编号与图 8.10 中编号的黑色圆圈相匹配:

  1. 将左侧电源导轨的负极导轨连接至 1kΩ电阻器(R2)。

  2. 将 MOSFET 的源极支脚连接到左侧电源轨的负极轨。

  3. 将左侧电源导轨的负极导轨连接到 Raspberry Pi 上的 GND 引脚。

  4. 将 100kΩ电阻器(R1)的末端连接到 Raspberry Pi 上的 GPIO 12/PWM0。作为提醒,GPIO 12 的替代功能是通道 PWM0,一个硬件 PWM 引脚。

  5. 将 MOSFET 的漏极管脚(D)连接到二极管的阳极管脚。

  6. 将二极管的阳极支脚连接到蜂鸣器的负极导线上。

  7. 将蜂鸣器的正极导线/二极管的阴极支脚连接到右侧电源导轨的正极导轨上。

  8. 连接左侧和右侧电源导轨的负极导轨。

  9. 将电源的正极输出连接到右侧电源导轨的正极导轨上。

  10. 将电源的负极输出连接到右侧电源导轨的负极导轨上。

现在您已经完成了这个电路构建,我们将继续运行我们的 Python 示例,它将制作一些音乐!

运行 RTTTL 音乐示例

运行chapter08/passive_buzzer_rtttl.py文件中的代码,蜂鸣器将播放简单的音阶。

执行此操作的代码非常简单。在下面代码的第(1)行中,我们使用rtttl模块将 RTTTL 乐谱解析为一系列由频率和持续时间定义的音符。我们的分数存储在rtttl_score变量中:

    from     rtttl     import     parse_rtttl
rtttl_score = parse_rtttl(    "Scale:d=4,o=4,b=125:8a,8b,        # (1)
    8c#,8d,8e,8f#,8g#,8f#,8e,8d,8c#,8b,8a"    )

接下来,在第(2)行中,我们循环通过rtttl_score中解析的注释,并提取频率和持续时间:

        for     note     in     rtttl_score[    'notes'    ]:                        # (2)
        frequency = int(note[    'frequency'    ])    
                    duration = note[    'duration'    ]     # Milliseconds    
        pi.hardware_PWM(BUZZER_GPIO, frequency, duty_cycle)  # (3)
        sleep(duration/    1000    )                                 # (4)

在第(3)行中,我们使用 PWM 在蜂鸣器的 GPIO 引脚上设置频率,并在第(4)行保持音符的持续时间,然后继续下一个音符。

In line (3), note that we are using PiGPIO's hardware_PWM() and that BUZZER_GPIO must be a hardware compatible PWM pin. PiGPIO's hardware-timed PWM (which is available on any GPIO pin) is not suitable for music creation because it is restricted to a discrete range of frequencies. If you need a refresher on PWM techniques, revisit Chapter 5, Connecting Your Raspberry Pi to the Physical World.

可以说,用 RTTTL 制作音乐是非常电子化的,并且是一种使用资源有限的微控制器的流行技术。但是,请记住,使用 Raspberry Pi,我们有足够的资源和内置硬件来播放 MP3 等富媒体。

Try a web search for RTTTL Songs and you'll find many scores for songs, retro computer games, and TV and movie themes.

如果您想探索通过 Python 播放和控制 MP3,您可以在 web 上找到许多资源、教程和示例。幸运的是,有很多方法可以完成这项任务(包括在不同版本的 Raspbian 操作系统之间进行更改),因此有时要可靠地设置和配置 Raspberry Pi 和 Raspbian 操作系统可能有点挑剔。如果你沿着这条路线走下去,我的建议是首先探索在命令行上播放 MP3 和控制音频(即改变音量)。一旦您有了一个稳定可靠的设置,然后继续探索一种基于 Python 的方法。

总结

在本章中,我们学习了如何使用 PWM 来设置 RGB LED 的颜色,并且一个独立的 RGB LED 需要三个专用 GPIO 引脚,分别为红色、绿色和蓝色工作。然后,我们探索了另一种类型的 RGB LED,APA102,这是一种 2 线 SPI 可控设备,可以链接在一起创建 LED 照明条。接下来,我们通过创建一个显示 Raspberry Pi 的 CPU 温度的示例应用程序来学习如何使用 OLED 显示器。我们最后给出了一个使用 PWM 和被动蜂鸣器通过解析 RTTTL 乐谱来发声的示例。

您在本章学到的内容将允许您向自己的项目添加可视和可审核的反馈。您还可以相对轻松地将学习扩展到其他类型的显示器,因为我们使用的 Luma 库能够与本章中使用的 APA102 LED 带和 SSD1306 OLED 设备之外的一系列其他显示器类型和型号配合使用。

在下一章中,我们将介绍测量环境条件(包括温度、湿度和光照)的组件和技术。

问题

最后,以下是一系列问题,供您测试有关本章内容的知识。您可以在本书的评估部分找到答案:

  1. 您的 APA102 LED 条设置为将所有 LED 显示为白色,但所有 LED 显示为红色。有什么问题吗?
  2. APA102 对 SPI 有什么限制?
  3. 当您使用逻辑电平转换器时,APA102 不工作,但当您将其直接连接到 Raspberry Pi 上的 MOSI 和 SCK 引脚时,APA102 似乎工作(因此绕过逻辑电平转换器)。问题的可能原因是什么?
  4. 使用 Luma OLED 库在 OLED 显示器上创建和显示图像的基本过程是什么?
  5. 什么是 RTTTL?

进一步阅读

APA102 是开始学习低级数据协议和通信的好选择。在审查 APA102 数据表的数据协议后(参见本章开头的技术要求下的链接),下一个逻辑步骤是审查一些较低级别的代码。PiGPIO 的 APA102 示例就是这样一个起点,但您可以在 PyPi.org 上找到其他示例:

除了本章介绍的 APA102 和 SSD1306 OLED 外,Luma 库套件还提供了许多高级模块,用于将普通显示器与 Raspberry Pi 集成。此外,Luma 还包含大量示例:

Luma 使用 PIL(Python 图像库)/API 来绘制和操作显示。我们在 OLED 示例中特别使用了ImageDraw。您可以在以下链接中找到 PIL API 文档:

如果您想进一步探索 RTTTL 格式,其 Wikipedia 站点是一个很好的起点: