Python 微控制器编程教程(二)

原文:Programming Microcontrollers with Python

协议:CC BY-NC-SA 4.0

六、数据变换

微控制器的主要优势之一是能够数字化我们熟悉的模拟信息,如温度、湿度和光强。我们可以获取这些模拟传感器读数,并将其转换为数字形式,作为我们可以分析和处理的信号。将模拟信号转换为数字信号是微控制器设备的标志,因为许多利用微控制器的智能设备应用将监控传感器,然后根据该数据执行一些操作。模数转换是任何对微控制器感兴趣的人的基本话题。

在这一章中,我们先来看看模数转换,然后看看我们可以通过这种方法获取数据的一些传感器。我们将使用的传感器包括电位计、光敏电阻和温度传感器。

模数转换

任何像样的微控制器都会配备模数转换模块。模数转换 (ADC)模块将提供给它们的信息转换成微控制器可以处理的二进制表示。

我们的物理世界本质上是模拟的。拿你手机的录音系统来说:你对着手机的麦克风说话,接收到的信号是模拟的。然后,麦克风必须将您声音中的模拟信息转换为微控制器可以解释的数字信号。其中一个应用是语音识别系统,设备中的微控制器需要监听“唤醒词”。“唤醒词”是可以用来引起计算设备注意的单词或短语。微控制器需要将你的声音转换成数字形式,这样它就可以搜索单词,从而将设备从睡眠中唤醒。大多数智能手机还具有过滤背景噪音或放大语音信号的功能。使用微控制器的 ADC 模块,您可以轻松滤除进入麦克风的音频中的噪声。这种类型的滤波器虽然不像我们在第二章中学习的硬件滤波器,但实际上是一种在软件中运行的数字滤波器。然而,它们的功能与您之前学习的过滤器相似。

我们在第二章中了解到,模拟电路被称为连续电路;因此,它们产生的信号本质上是连续的。当我们说一个信号是连续的时,我们的意思是它的特征是随着时间的变化在信号中的点之间平滑过渡。正如我们所知,由数字电路产生的数字信号具有离散性。出于我们的目的,这些可以被认为是连续信号的数字表示。

让我们先仔细看看什么是信号。信号可以被认为是我们用来提取信息的能量。在微控制器电子设备中,信号采用传感器产生的电压或电流形式。传感器产生的信号通常不是我们可以直接获取信息的形式,因为我们缺乏处理传感器信息的能力。微控制器需要为我们读取传感器,然后通过某种显示器以我们可以理解的方式传递信息。微控制器也可以获取这些信息并执行一些操作。例如,数控烤箱可能有一个热电偶传感器来读取烤箱内的温度。当烤箱内的温度达到预设温度时,微控制器需要关闭加热器。这只是可能使用 ADC 模块的一种应用。

ADC 硬件

有几种方法可以执行模数转换,其中最常用的是使用逐次逼近型 ADC 电路。逐次逼近型 ADC 使用各种电路元件。逐次逼近电路使用的这些组件包括将数字信号转换成模拟信号的数模转换器(DAC)。它还使用一个比较器、一个采样保持(S & H)电路和一个控制电路,我们一会儿会讲到。逐次逼近型 ADC 电路看起来有点像图 6-1 。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-1

逐次逼近电路

该电路首先从微控制器引脚获取输入。由于典型微控制器上只有一个 ADC 模块,使用多路复用器电路,我们可以将电压从几个引脚路由到微控制器内的 ADC 模块。这是因为一个模块一次只能读取一个引脚,很多时候,我们可能需要我们的微控制器来读取几个传感器。

为确保 ADC 正常工作,需要拍摄微控制器引脚电压的快照。由于传感器会持续输出数据,ADC 电路需要时间来执行转换,因此需要拍摄该快照。该快照是在微控制器调用 ADC 模块执行转换时拍摄的。简单来说,使用电容是因为,正如我们之前所学,电容可以存储电压。电容器将被充电到与我们想要读取其电压的引脚相同的电压值。例如,如果一个传感器输出 3.7 伏,然后快速输出 3.9 伏,然后 4.0 伏,我们需要一些序列来有效地数字化所有这些读数。这是因为单独的 ADC 模块无法执行并行转换。ADC 模块首先需要获取 3.7 伏电压,将其转换为数字形式,然后是 3.9 伏电压,依此类推。

电容充电的电压将是我们在微控制器上执行逐次逼近程序的电压。我们将 ADC 模块上带电容的前端称为采样保持(S&H)电路。

一旦 S&H 电路上有电压,与 ADC 电路一起工作的比较器就开始工作。这两个电路的结合将持续将输入端的电压减半。当我们说输入端的电压时,我们实际上指的是快照电压。该过程将继续,直到我们达到这些电路的组合所允许的最接近输入值。

在讨论 ADC 的时候,经常出现的一个词就是分辨率。让我们仔细看看什么是决心。ADC 模块有一系列值可用来表示转换后的信号。该值表示为模块输出的二进制位数。本质上,引起比特变化的最小电压就是我们所说的分辨率。根据 ADC 的分辨率,ADC 模块将具有一定数量的电压阶跃,可用于表示测量的电压。电压步进数越多,ADC 模块的分辨率越高。

为了输出信息,ADC 电路将利用来自比较器和 DAC 的转换值,将模块输出的二进制数的最高有效位(MSB)处理到最低有效位(LSB)。这是为了尽可能匹配输入端读取的电压值,并以二进制形式表示。该输出值当然受到 ADC 模块内 DAC 分辨率的限制。这是因为电压可以被削减的次数取决于 DAC 的分辨率。该值随后从 ADC 模块输出,以便 CPU 读取和处理。

为应用选择微控制器时,有时 ADC 的分辨率可能是选择器件的决定性因素。例如,读取温度或光传感器所需的分辨率可能不那么重要。然而,如果我们需要建立一个用于语音识别的电路,您将需要高分辨率来拥有一个功能电路。微控制器上的 ADC 电路通常具有约 8 至 16 位的分辨率。对于大多数传感器读数应用,这就足够了,如果需要更高的分辨率,例如在专业医疗应用中,可以使用外部 ADC IC。

深入了解 ADC

现在,我们来看看讨论 ADC 时可能会遇到的一些额外术语。在讨论 ADC 时,我们首先需要讨论的是采样。如前所述,为了让 ADC 模块有时间处理读取值,必须拍摄电压快照。采样是指我们在一段时间内可以拍摄的快照数量。当我们谈到模数转换中的采样时,我们指的是 ADC 模块复制正在分析的信号并创建尽可能接近原始信号的数字等效物的能力。数模转换器能够捕获的样本越多,原始信号的数字表示就越精确。

你可能会遇到的一个难题是,试图确定我们需要采集的样本的最佳数量,以便恰当地表示我们正在采样的信号。如果我们接受很多信号,那么我们的模块将会很慢,可能没有我们的应用所需要的响应时间。另一方面,如果我们取的样本太少,我们会有一个快速的转换;然而,我们可能没有原始信号的精确表示。称为奈奎斯特采样速率的特殊采样速率是用于确定最小采样时间的基准。

奈奎斯特采样速率是指最小采样速率至少是我们试图采样的信号最高频率的两倍。例如,如果信号的最高频率预计为 10 kHz,则最低采样速率必须为 20 kHz。换句话说,采样保持电路必须每秒拍摄 20,000 张快照才能充分测量信号。

ADC 模块还有一个数据采样速率,通常用 ADC 模块能够测量的每秒样本数(SPS)来衡量。通常,我们会看到制造商以每秒千个样本(kSPS)或每秒百万个样本(MSPS)来指定采样速率。

ADC 转换的另一个方面是量化。量化是指将 ADC 的输入电压值映射到 ADC 能够产生的输出值的过程。例如,一个 10 位 ADC 模块将具有从 0 到 1023 的步长。我们称这个从 0 到 1023 的值为 ADC 的量化电平。8 位 ADC 的量化级别为 0 至 255。这种量化是对等于我们试图在引脚上测量的电压的值进行舍入的名称。这意味着,在最严格的意义上,我们可以将分辨率重新定义为两个相邻量化级别之间的度量距离。

电位计

我们要研究的第一个配合 ADC 模块使用的器件是电位计。当我们观察现实世界中的嵌入式器件时,你会发现许多器件中都有电位计。例如,立体声系统和扬声器系统上的音量旋钮使用电位计,以允许用户调整这些设备上的音量。这些设备都使用电位计电路来正常工作。在图 6-2 中,我们看到了电位计的样子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-2

电位计

电位计可能有不同的形状因素;但是,如果我们观察它的机械结构,我们会发现它们都是三针装置,在顶部有一个转动机构,以便进行调整。有时,您可能会看到一个看起来像电位计的器件,上面有五个或更多的引脚。这不是电位计,而是旋转编码器,功能不同。

电位计的三个引脚中,一个连接到供电轨,另一个连接到地电源,第三个连接到微控制器的输入引脚。当您调节电位计上的旋钮时,您所做的是将模拟输入上的电压从 0 伏特改变到 VDD。这通常可以在 0 伏到 3.3 或 5 伏的范围内。

发生这种情况的原因是电位计可以被认为是一种分压器电路。分压器是一种电路,它利用两个电阻串联时存在的特性,将较大的电压转换成较小的电压。在图 6-3 中,我们看到了这个分压器的布局。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-3

该分压器

如图 6-3 所示的分压器将输出 VCC 上电压的大约一半。因此,如果 VCC 是 5 伏,那么分压器的输出将是 2.5 伏。分压器的输出被确定为在两个电阻的中点测得的电压。这就是分压器的作用。它们输出输入端输入电压的一小部分。分压器的输出不是任意的,而是由组成分压器电路的电阻值决定的。一旦我们知道了电阻值,我们就可以使用分压器公式来计算输出。当我们需要使用电阻获得特定的电压输出时,这个公式非常有用。

为了计算分压器的输出,我们使用以下公式

想要=葡萄酒(R2/(R1 + R2)

我们知道图 6-3 中的分压电路将提供一半的输入电压;然而,如果我们想计算同样的数学,我们可以使用我们的分压公式。如果我们想计算 Vout,可以使用公式并输入 Vin、R1 和 R2 的值。进行计算时,我们将 R1 作为顶部电阻,R2 作为底部电阻,我们得到

vot = 5v(1/(1+1)= 5v x 0.5 = 2.5

电位计起电阻器的作用,并表现出电阻。然而,它可以被认为是一个完全不同的设备,因此它有自己的示意性符号。我们在图 6-4 中看到电位计的示意符号。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-4

电位计示意图符号

电位计的示意符号由一个指向电阻器的箭头组成。这意味着电阻是可调的。有时,您会看到如图 6-5 所示的较小版本的电位计。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-5

微调电位器

这种电位计被称为微调电位计或微调电位计。功能与普通电位器相同;但是,它被设计为隐藏在最终产品中,并由制造商或服务人员进行调整,而不是由产品的最终用户进行调整。这些很容易识别,因为常规电位计的调节旋钮从器件中伸出,必须用螺丝刀调节才能改变它们的值。

CircuitPython 中的模数转换

CircuitPython 提供了使用 ADC 的库。我们将为模数转换工作的库是

  • board–board 模块包含我们正在使用的电路板的引脚常量。

  • analog io–这是一个包含所有类的模块,提供对我们正在使用的模拟输入和输出功能的访问。该模块允许我们从模拟引脚读取模拟数据。

  • 时间–时间库包含允许微控制器使用时间相关功能的函数。睡眠方法是我们用来帮助微控制器计时的方法。

Note

有些主板可能不支持某些功能,如模拟输入功能,尤其是当它们的固件处于测试阶段时;因此,最好查看您所用主板的发行说明。

带 MCU 的 ADC 原理图

电位计可以连接到我们的 MCU,如图 6-6 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-6

带电位计的 MCU

ADC 电路连接技巧

以下是连接电路的推荐步骤:

  1. 将一根跳线从微控制器上的 A0 引脚连接到试验板原型制作区域的一个插座。

  2. 将电阻器的中心引线连接到步骤 1 中使用的试验板同一行中的另一个插座。

  3. 用一根跳线将它从电位计的一根引线连接到 VCC。

  4. 用跨接线将电位计的最后一根引线接地。

当你完成电路连接后,它看起来应该类似于图 6-7 。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-7

带电位计试验板的 MCU

一旦你有了你的电路设置,我们运行如清单 6-1 所示的程序。记得检查你的连接,确保它们符合你的原理图,否则你有损坏电路板电路元件的危险。出于这个原因,我总是建议您在关闭电源的情况下连接电路,然后在编写测试程序之前打开电源。

带电位计程序的 CircuitPython

现在我们已经连接了物理电路,我们可以看看如何使用 CircuitPython 读取电位计。在 Mu 编辑器中编辑您的 code.py 文件,以反映清单 6-1 。

# import time module
import time

# import the board module
import board

# import our analog read function
1 from analogio import AnalogIn

# read the A0 pin on our board
2 analog_in = AnalogIn(board.A0)

# get the voltage level from our potentiometer
3  def get_voltage(pin):
    return (pin.value * 3.3) / 65536

# print the voltage we read

4  while True:
    print((get_voltage(analog_in),))
    time.sleep(0.1)

Listing 6-1MCU with Potentiometer Program

我们做的第一件事是导入控制电路板所需的 CircuitPython 库。这些是我们通常进口的时间和董事会图书馆。在(1)中,我们从analogio模块中导入AnalogIn对象。这是允许我们执行模拟功能的库。我们程序(2)的下一步是创建一个 p in 对象,它将允许我们读取正在使用的特定模拟引脚。在这种情况下,我们使用电路板上的引脚 A0,并将其命名为analog_in

沿着(3)的程序,我们有一个函数,允许我们从电位计读取电压电平。该函数get_voltage将引脚作为输入,然后执行计算以返回正确的电位计读数。在我们的超级循环中的(4)处,我们使用get_voltage函数读取我们的模拟输入引脚,并将该读取电压连续打印到我们的串行控制台。一旦一切运行正常,在你的串行控制台中,你应该看到如图 6-8 所示的输出。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-8

带电位计输出的 MCU

当我们调整电位计的输入时,我们看到输出从 0 伏变为 3.3 伏左右,该值被打印到串行端子上。

光敏电阻

由于这种读取模拟传感器的能力,我们现在能够使用的一种传感器是光敏电阻。光敏电阻是一种电阻器,它根据照射到其上的光量来改变电阻。当没有光照射在光敏电阻上时,它具有极高的电阻,大约为几十万欧姆。然而,当光照射到光敏电阻上时,电阻下降到几百欧姆。为了创建一个读取光敏电阻的程序,我们需要创建一个分压电路。这个分压电路将由光敏电阻和常规电阻组成。然后,微控制器将读取电路的输出,以确定光敏电阻的亮度变化。

带 mcu 原理图的光探测器

我们如图 6-9 所示连接电路。光敏电阻连接到引脚 A1,LED 连接到引脚 A2。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-9

带摄像头的单片机

光电探测器电路连接提示

以下是连接电路的推荐步骤:

  1. 将一根跳线从微控制器上的 A2 引脚连接到试验板原型制作区域的插座,再连接到 LED 的一根引线。

  2. 用跨接导线将 LED 的另一根导线接地。

  3. 用一根跳线将光敏电阻的一根引线连接到 VCC。

  4. 将光敏电阻的另一根引线连接到 10k 电阻器的一根引线上。

  5. 用一根跨接导线将 10k 电阻的另一根导线接地。

  6. 在电阻器和光敏电阻的交叉点与微控制器上的引脚 A1 之间连接一根跳线。

当你完成连接电路时,它应该看起来如图 6-10 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-10

带照片显示试验板的 mcu

现在我们有了电路设置,我们可以写一些代码了!

带 CircuitPython 程序的光敏电阻

在 Mu 编辑器中编辑您的 code.py,使其类似于清单 6-2 。

# import the board module
import board

# import time library
import time

# import our analog read function
from analogio import AnalogIn

# import pin control
import digitalio

# set an adjust value
1 adjustValue = 2000

# create object for pin we are using
2  led = digitalio.DigitalInOut(board.A2)

# set the pin to output

3 led.direction = digitalio.Direction.OUTPUT

# read the A1 pin on our board
4 photoresistor = AnalogIn(board.A1)

# set an ambient light value
5 ambientLightValue = photoresistor.value

# release the pin for other use
6 photoresistor.deinit()

# print the voltage we read
7 while True:
    # read the photoresistor
    photoresistor = AnalogIn(board.A1)
    # if bright turn the LED off
    if (photoresistor.value > ambientLightValue - 2000):
        led.value = False
    # turn the LED on
    else:
        led.value = True

    # release the pin for other use
    photoresistor.deinit()

Listing 6-2MCU with Photoresistor Program

这个程序做了很多事情。让我们看看会发生什么。在程序的顶部,我们导入我们的电路板、时间、模拟和数字库。在(1)中,我们创建了一个adjustValue变量,用于帮助读取光敏电阻。我们的下一步是在(2)处创建一个表示真实世界 LED 的对象,并将其设置为(3)处的输出。然后,我们创建一个对象来表示(4)处的光敏电阻。在(5)中,我们从光电管中读取一个读数并存储该值。这是当前存在的环境光的值。

注意,在读取数据后,我们必须使用(6)中的deinit方法来允许我们的 pin 在程序的其他部分使用。如果我们不这样做,那么我们就不能在超级循环中使用 pin。

在(7)的超级循环中,我们不断读取光敏电阻值。有一个条件,检查是否有黑暗的 LED。如果有黑暗,LED 就打开,有光亮的时候,我们就把 LED 关掉。

保存程序并运行它。你会观察到,用手盖住光敏电阻,LED 就亮了;然而,当 LED 处于环境亮度或更亮时,LED 将不被点亮。

温度传感器

现在我们可以使用模拟输入的一种传感器是温度传感器。我们将使用的温度传感器是 TMP36 温度传感器,它使用电压作为输出,以摄氏度为单位提供温度读数。TMP36 如图 6-11 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-11

TMP36 温度传感器认证:adafruit.com ada fruit

TMP36 温度传感器器件有三个引脚。一个是连接到正电源的 VCC 引脚。还有一个连接到地的 GND 引脚和一个由微控制器读取的模拟电压输出引脚。根据 VCC 的电压值,微控制器可以进行一些计算来确定我们正在读取的温度。

该传感器的工作电压范围为 2.7 至 5.5v,是一款非常通用的传感器,可以与所有配有 ADC 模块的普通微控制器配合使用。它能够与基于 CircuitPython 的 3.3 伏 MCU 配合使用,该电压在器件的电压范围内。温度传感器可以测量从-50 摄氏度到 125 摄氏度的温度,这对于您将要承担的大多数项目来说都是一个很好的范围。

带 MCU 原理图的温度传感器

我们如图 6-12 所示连接电路。我们的 TMP36 连接到 A0 模拟输入引脚。使用 CircuitPython MCU 时,我们需要电容 C1 和电阻 R1 来获得传感器的精确读数。这是由于高速读取传感器的设备的基本配置。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-12

带 MCU 原理图的温度传感器

温度传感器电路连接提示

以下是连接电路的推荐步骤:

  1. 将一根跳线从 TMP36 温度传感器的 VCC 引脚连接到试验板的正供电轨。

  2. 用一根跳线将传感器的接地引脚连接到试验板的接地轨。

  3. 将 47k 电阻器的一根引线连接到 TMP36 传感器的输出引脚,并将电阻器的另一根引线连接到传感器的接地引脚。

  4. 将电容的一根引线连接到接地引脚,另一根引线连接到 TMP36 传感器的输出引脚。

  5. 在温度传感器的输出引脚和运行 CircuitPython 的 MCU 上的引脚 A0 之间连接一根跳线。

当您完成电路连接时,它看起来应该类似于图 6-13 。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-13

试验板上集成 MCU 的温度传感器

带 CircuitPython 程序的温度传感器

在 Mu 编辑器中编辑您的 code.py,使其类似于清单 6-3 。这个例子由 Adafruit Industries 提供的用于读取传感器的例子修改而来。

# import the board module

import board

# import the time module
import time

# import module for reading analog input
import analogio

# sensor connected to pin A0
1 TMP36_PIN = board.A0

# function for reading the temperature sensor
2 def tmp36_temperature_C(analogin):
    # convert the voltage to a temperature
    millivolts = analogin.value * (analogin.reference_voltage * 1000 / 65535)
    return (millivolts - 500) / 10

# create instance of analog object for sensor
3 tmp36 = analogio.AnalogIn(TMP36_PIN)

# super loop

4 while True:
    # read temperature in Celsius
    temp_C = tmp36_temperature_C(tmp36)
    # use Celsius to get Fahrenheit value
    temp_F = (temp_C * 9/5) + 32

    # print our temperature
    print("Temperature: {}C {}F".format(temp_C, temp_F))

    # every second
    time.sleep(1.0)

Listing 6-3MCU with Temperature Sensor Program

在这个程序中,我们做了我们通常的导入工作来启动和运行开发板。在(1)中,我们设置了连接到模拟输入引脚 A0 的温度传感器。在(2)中,我们有一个读取温度传感器并进行电压到温度转换的函数。在(3)中,我们为温度传感器创建一个模拟对象的实例。在主程序中,我们在(4)处运行一个超级循环,读取传感器数据并每秒钟将其打印到输出控制台。如果您查看您的串行控制台,您应该会得到类似于我在图 6-14 中得到的输出。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6-14

温度传感器输出

结论

在本章中,我们讨论了模数转换的基础知识。我们研究了微控制器 ADC 电路在硬件中的工作原理,在学习这些电路工作原理的过程中,我们发现了电位计、光敏电阻和分压器的工作原理。我们还学习了如何将 CircuitPython 用于模拟输入。利用这些信息,我们能够读取温度传感器并将信息输出到串行终端。有了这里获得的知识,数百个传感器现在由你支配。

七、通信协议

在上一章中,我们讨论了如何利用微控制器读取传感器,即利用微控制器上的模数转换器。然而,当我们开始使用串行通信时,可以与微控制器接口的器件种类会急剧增加。在本章中,我们将了解 UART、SPI 和 I2C 的串行通信协议。理解这些协议将扩展我们微控制器的接口能力。

微控制器通信

在我们开始研究微控制器上的具体通信协议之前,我们先讨论一下微控制器的一般通信。在微控制器系统中,有两种类型的通信。有串行通信和并行通信。并行通信系统利用多条信号线来传输数据。并行通信速度很快,是老式计算系统中传输数据的首选方法。这是因为这些旧设备中的处理器速度不够快,无法处理与串行通信相关的开销。当我们谈到开销时,我们指的是软件开销,即 CPU 花费在做额外工作上的额外时间。

在串行通信中,数据以比特流的形式通过比并行系统少得多的通信线路发送。虽然串行通信比并行通信慢,但如果数据传输速率相等,现代硬件的速度足以消除对并行系统的需求。这并不是说并行通信已经过时。例如,像显示器这样的设备受益于使用并行通信获得的更高的传输速率。

如今,并行通信被保留用于与彼此距离较近的设备进行通信。这是因为通过并行总线传输数据需要大量额外的硬件。例如,组成传输线的电线需要使用额外的资源。因此,每当我们远距离传输数据时,我们倾向于使用串行通信。

如今存在无数的串行通信方法。有些很简单,而有些则采用复杂的协议,这些协议足够强大,可用于航空航天和汽车应用。

USART 通信公司

在本节深入探讨之前,我们应该花点时间讨论一下串行通信。串行通信有两种方式:异步或同步。

异步通信以比特流的形式发送数据。这个比特流通常包括三个部分。在数据流的开头是一个起始位,表示数据开始传输的时间。最后还有一个停止位,指示数据停止传输的时间。在起始位和停止位之间,有一个数据包,其中包含我们要传输的数据。数据包是我们给一个字节的格式化数据起的名字。发送开始和停止位的需要增加了一点软件开销,这限制了传输的速度。然而,这些起始和停止位对于实现通信设备之间的同步是必要的。

同步串行通信消除了与发送起始和停止位相关的开销,因此需要较少的软件开销。这是通过使用时钟来同步数据传输来实现的。然而,这种方法确实需要额外的线路来传送时钟信号。因此,虽然它确实提高了数据传输效率,但它需要额外的硬件才能正常运行。

到目前为止,在我们的整个项目中,我们一直在利用 USART 通信形式的串行通信,而没有意识到这一点。USART 是一种串行通信协议,代表通用同步异步收发器。每次我们在机器上使用 PuTTY 与 CircuitPython 微控制器对话时,我们都在利用 USART 的强大功能。

虽然它是一种能够同步使用的协议,但这些功能很少被利用,而且在基于微控制器的系统中,绝大多数 UART 通信都是异步使用的。因此,USART 通常被称为 UART(通用异步收发器),缩写中省略了同步位。因此,我们将把重点放在异步通信方法上。

异步 UART 有两条通信线路,分别连接到发送(TX)引脚和接收(RX)引脚。这些引脚在发送数据的发送器和接收数据的接收器之间共享。为了成功实现数据通信,器件之间还必须有公共接地连接。

由于没有时钟线来帮助通过异步 UART 传输数据,接收器和发送器必须就数据传输速率达成一致。这种数据传输速率称为波特率。波特率衡量每秒传输的位数。大多数低级通信接口使用 9600 波特的波特率。CircuitPython UART 接口的波特率约为 115,200。

UART 模块可以在三种通信模式下工作,即单工、半双工和全双工通信方式。为了理解这些是如何工作的,假设我们有一个 UART 通信系统,如图 7-1 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-1

UART 通信系统

如果数据从器件 1 单向传输到器件 2,这称为单工通信。如果数据从器件 1 传输到器件 2,然后从器件 2 传输到器件 1,但不是同时传输,则这称为半双工通信。如果数据从器件 1 传输到器件 2,然后同时从器件 2 传输到器件 1,这称为全双工通信。

更深入地了解 UART

我们之前在 UART 环境中讨论的比特流称为数据帧。数据帧是我们给单个数据传输单元起的名字。如果我们观察 UART 数据帧,会发现它由 1 个起始位、5 至 9 个数据位、0 至 1 个奇偶校验位和 1 至 2 个停止位组成。当我们将所有这些放在一起时,UART 数据帧类似于图 7-2 所示的排列。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-2

UART 数据帧

我们知道,起始位的目的是指示数据传输的开始,而停止位指示数据传输何时停止。但是,您可能想知道奇偶校验位的用途是什么。使用奇偶校验位原因是为了帮助错误检测。

异步 UART 有可能发生干扰。当我们谈到干扰时,我们的意思是某些因素可能导致数据损坏和数据传输中的错误。奇偶校验位用于帮助这种情况。奇偶校验位被添加到数据包的停止位之前。添加该奇偶校验位使得数据帧中 1 的数量为偶数或奇数。当数据包到达目的地时,如果数据帧与预期的偶数值或奇数值不同,我们就知道传输中有错误。利用这些信息,可以重新发送或丢弃数据包。

CircuitPython 中的 UART

在 CircuitPython 中,可以使用板载硬件外设进行串行通信,也可以通过称为位碰撞的过程使用软件例程来执行串行通信。由于使用硬件外设更有效,这是我们将用来控制串行设备的方法。

CircuitPython 提供“busio”库,支持 UART、SPI 和 I2C 通信的硬件外设。

这些是我们将用来与 CircuitPython MCU 进行 UART 通信的模块:

  • board–board 模块包含我们正在使用的电路板的引脚常量。

  • 时间–时间库包含允许微控制器使用时间相关功能的函数。睡眠方法是我们用来帮助微控制器计时的方法。

  • busio——这个库包含允许我们使用 CircuitPython 工具来控制硬件的函数。

带 MCU 原理图的 USB-UART

我们连接微控制器,如图 7-3 所示。我们可以使用 USB 转 UART 转换器,将微控制器连接到计算机。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-3

带 UART 的 MCU

带 USB-UART 电路连接提示的 MCU

以下是连接电路的推荐步骤:

  1. 首先,在进行串行通信连接时,最好保持电路断电,因为错误的连接会损坏计算机的 USB 端口。

  2. 用跳线将 MCU 连接到 USB 转 UART 模块,如下所示。用第一根跳线将 USB-UART 模块上的 RX 引脚连接到微控制器的 TX 引脚,并将 USB-UART 模块的 TX 引脚连接到微控制器的 RX 引脚。

  3. 如果你的 USB-UART 模块是 5 伏的,你必须通过一个逻辑电平转换器将 MCU 连接到模块。为此,将 USB-UART 模块的 RX 和 TX 引脚连接到逻辑电平转换器的 HL 引脚,并将微控制器的 RX 和 TX 引脚连接到逻辑电平转换器的 LV 引脚。

  4. 用跳线将模块上的接地引脚连接到试验板接地轨的接地引脚。

  5. 连接好所有东西后,仔细检查你的连接,然后给你的电路通电。

当你完成连接电路时,它应该看起来如图 7-4 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-4

MCU 连接到 USB-UART 试验板

UART 与 CircuitPython 程序

一旦您的电路被连接,在 Mu 编辑器中编辑您的 code.py 文件以反映清单 7-1 。

# import the board module
import board

# import time library
import time

# import library for working with UART
   1   import busio

#setup uart 9600 baudrate, 8 bits, no parity, 1 stop
   2   uart = busio.UART(board.TX, board.RX, baudrate=9600,
                  bits=8, parity=None,
                  stop=1, receiver_buffer_size=64)

   3   while True:
    # read up to 64 bytes of data
    dataRead = uart.read(64)

    # once we got data print it
    if dataRead is not None:
       print("I got data")

Listing 7-1Our Program

我们的程序是一个简单的程序,用于验证 UART 模块是否工作。首先,我们导入开发板和时间库来启动和运行开发板。在(1)中,我们导入了 busio 库来使用 UART。在(2)处,UART 模块随后设置为板的默认 RX 和 TX 引脚。我们还将 UART 配置为 9600 波特值和 8 位数据,并且不执行任何奇偶校验。此外,我们设置了 1 个停止位,并为接收器设置了 64 字节的缓冲区大小。这些设置是标准 UART 值。在(3)中,我们有一个无限循环,它从 UART 模块读取数据,然后让我们知道 UART 模块何时接收到数据。

当您保存文件和程序运行时,您可以开始与您的电路进行交互。如果您通过 USB-UART 模块的串行连接输入数据,您的串行终端将显示消息“我有数据”。

SPI 通信

我们接下来要讨论的串行通信协议是 SPI。SPI 代表串行外设接口。SPI 是微控制器使用的另一种串行通信方法。与可以同步或异步操作的 USART 不同,SPI 是一种仅同步协议。同步使得 SPI 成为一种快速协议,因为对模块速度的限制通常取决于模块的硬件限制。SPI 使用时钟来保持器件同步,该模块可以以通信时钟允许的最快速度运行。

SPI 对相互交互的器件采用主从关系。产生时钟的器件称为主器件,与之通信的另一个器件称为从器件。微控制器上的典型 SPI 模块可以在主机模式和从机模式下工作。

SPI 有四条线路。第一行叫做主出从入(MOSI)。该线路从主机向从机发送数据。另一条线路从从机向主机发送数据;我们称之为主入从出(MISO)。还有一条时钟线(SCK ),负责传送时钟信号,使器件保持同步。最后,还有一个从机选择(SS)线路,也称为片选(CS)线路。这条线有特殊的用途。您可以看到,SPI 总线可以支持从机选择线路允许的从机数量。当主机选择器件的从机选择线时,从机将知道必须选择该线。

你可以在图 7-5 中看到典型的 SPI 总线连接。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-5

SPI 总线

深入 SPI

查看图 7-6 中的图像,这样您就可以了解 SPI 通信的内部情况。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-6

SPI 基本框图

SPI 利用几个移位寄存器将并行数据转换成串行数据,然后通过 SPI 总线传输。回想一下,我们说过主机生成时钟。该时钟用作模块内所有移位寄存器的输入,进而让主机控制传输速度。

为了传输数据,SPI 总线的工作原理如下。从机选择线从高电平变为低电平,开始传输。然后根据时钟的上升沿或下降沿传输数据。我们称之为时钟阶段。根据用户选择的时钟极性,SPI 模块将知道何时工作。时钟极性是时钟线默认状态的名称,在我们称之为空闲状态期间,它可以是高电平或低电平。在该阶段结束时,从机选择线从低电平变为高电平。

SPI 电路连接

  1. 用一根跳线将 MCU 上的 MOSI 引脚连接到 MISO 引脚。

SPI 与 CircuitPython 程序

在将一根跳线从 MISO 引脚连接到 MOSI 引脚后,打开 Mu 编辑器并编辑您的程序,使其类似于清单 7-2 。

# import the board module
import board

# import time library
import time

# import library for working with SPI
1   import busio

# setup SPI
2   spi = busio.SPI(board.SCK, board.MOSI, board.MISO)

# lock spi bus
3   while not spi.try_lock():
    pass

# super loop
4   while True:
    # print numbers 0 to 8 via SPI bus
    for x in range(48, 57, 1):
        # buffer for send
        tx = chr(x)

        # buffer for receive
        rx = bytearray(1)

        # SPI RX_TX

        spi.write_readinto(tx, rx)

        # print sent and received data
        print("tx: " + str(tx))
        print("rx: " + str(rx))

        # sleep for 500 ms
        time.sleep(0.5)

Listing 7-2Our SPI Loopback Program

我们使用的 SPI 程序就是所谓的回送程序。回送程序是发送者发送和接收它已经发送的消息的程序。该回送程序可以用来验证 SPI 总线的工作情况。

在(1)中,我们使用 busio 库来处理 SPI 硬件外设。在(2)中,我们通过创建实例来设置 SPI 模块,同时设置 SCK、MOSI 和 MISO 引脚。在(3)中,我们需要锁定 SPI 总线才能使用。这是 CircuitPython 的要求之一,在您希望使用总线的任何时候都必须这样做。在(4)的超级循环中,我们使用 SPI 模块打印数字 0 到 8 来传输字符。发送完字符后,我们使用 SPI 模块读取已发送的字符。一旦字符被传输,我们每 500 毫秒将发送和传输的字符打印到串行终端。

I2C 通信公司

我们要看的最后一个通信协议是 I2C(集成电路间)协议。I2C 是一种串行协议,最近变得非常流行。你看,I2C 只需要两条通信线路。它们是串行时钟线(SCL)和串行数据线(SDA)。像 SPI 一样,I2C 也有一个主机,负责控制与之通信的总线和从机。在这方面,它与 SPI 的不同之处在于,I2C 总线上的任何器件都可以在任何时间点成为主机。看看图 7-7 你就知道 I2C 公共汽车是什么样子了。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-7

I2C 公共汽车

正如您所观察到的,主设备不需要使用从设备选择线来与总线上的 I2C 设备通信。这是因为 I2C 总线上的每个设备都被分配了一个我们可以用来读取它们的地址。该地址长度为 7 位,有效地址范围从 0 到 127,最多允许 128 个设备连接到总线。

由于我们缺少从机选择线,因此只有总线上的主机才能发起通信,这一点尤为重要。如果两个设备试图同时发起通信,就可能发生总线上的冲突。I2C 总线上的两条线被称为开漏引脚。开漏引脚需要一个上拉电阻才能输出高电平。因此,在 I2C 总线上,需要一个 4.7k 的上拉电阻来保证总线正常工作。

深入 I2C

当时钟线为低电平时,I2C 通过让数据线改变状态(高电平或低电平)来工作。当时钟线为高电平时,使用启动和停止信号读取数据,以实现无错误的数据传输。起始和停止条件都是通过保持时钟线为高电平,同时改变数据线上的电平来实现的。

像在所有串行通信协议中一样,数据在 I2C 中是通过使用包来传输的。为确保从机与主机通信,数据包中会发送一个特殊的应答位,称为应答位。当与接收器通信时,发送设备将释放 SDA 线。如果线路被拉低,那么我们将得到一个确认“ACK ”,因为我们知道器件已准备好通信。如果没有,我们将得到一个不承认“NACK”,我们知道设备不能正常通信。

图 7-8 向我们展示了 I2C 传输序列的样子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-8

I2C 传输

最近进入嵌入式设备领域的一种新总线是系统管理总线(SMBus)。SMBus 主要与 I2C 兼容,只是在功耗、超时要求和最大总线速度等方面有所改进。

CircuitPython 中的 I2C 支持

CircuitPython 提供了一个库,用于处理微控制器上的 I2C 模块。我们使用与 UART 和 SPI 模块相同的模块,即 busio 模块。

MPU6050

我们将用来测试 CircuitPython 的器件是 MPU6050。该器件在一个封装中集成了加速度计和陀螺仪,这使得它可以方便地用于传感器融合和机器人应用等领域。

设备内的陀螺仪测量角速度。这意味着陀螺仪测量旋转物体的角度变化率。另一种说法是,陀螺仪本质上是测量某个物体相对于三个轴的旋转,这三个轴是 X、Y 和 Z 轴。

设备中的加速度计测量物体速度变化的速率(实质上是作用在物体上的力)。这通常用重力或米/秒(或米/秒的平方)来衡量。

MPU6050 还包括一个板载温度传感器,我们可以用它来读取温度数据。

由于器件采用 SMD 封装,因此在使用器件时,通常在分线板上使用。分线板是一种特殊电路板的名称,通过提供外部焊盘,我们可以轻松连接到 MPU6050 等 SMD 器件。我们看到一个这样的 MPU6050 分线板,如图 7-9 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-9

MPU6050 早餐信贷:Adafruit,adafruit.com

来自加速度计和陀螺仪的数据可以在我们称之为传感器融合的过程中结合起来。当我们融合来自加速度计和陀螺仪的传感器数据时,我们得到的就是所谓的惯性测量单元(IMU)。IMU 通常使用软件过滤器将这些数据融合在一起。这种类型的过滤器本质上是对时间点上的一些数据进行采样的数学运算。有许多方法可以实现这种滤波,包括互补滤波器、卡尔曼滤波器和 Madgwick 滤波器。每种过滤器都有其优点和缺点,您可以根据自己的应用来决定。本书不涉及传感器融合;但是,如果您愿意,您可以使用各种过滤器对我们从 MPU6050 读取的数据进行研究。

带 MCU 原理图的 I2C

我们将 MPU6050 连接到 MCU,如图 7-10 所示。我们必须确保包括上拉电阻。请注意,1k 和 10k 之间的值将适用于 4.7k,这是一个很好的最佳值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-10

MCU 连接到 MPU6050 原理图

I2C 电路连接提示

以下是连接电路的推荐步骤:

  1. 首先,我们将每个电阻的一条引线连接到 VCC。

  2. 如下连接每个电阻的另一根引线。将第一个电阻的自由端连接到 SCL 线,将另一个电阻的自由端连接到 SDA 线。

  3. 接下来,在 MPU6050 的 SCL 和 SDA 引脚与微控制器的 SCL 和 SDA 引脚之间连接一条跳线。

  4. 将 MPU6050 上的 VCC 引脚连接到试验板上的 VCC 轨,将 GND 引脚连接到试验板的 GND 轨。

当你完成连接电路时,它应该看起来如图 7-11 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7-11

MCU 连接到 MPU6050 试验板

I2C 与 CircuitPython 程序

打开 Mu 编辑器并编辑您的程序,使其类似于清单 7-3 。

# import the board module
import board

# import time library
import time

# import library for working with I2C
import busio

# setup the I2C module
1   i2c = busio.I2C(board.SCL, board.SDA)

# lock the I2C bus
2   while not i2c.try_lock():
    pass

# super loop

3   while True:
    # scan for addresses on the bus
    print("I2C addresses found:", [hex(device_address)
                                   for device_address in i2c.scan()])

    # every two seconds
    time.sleep(2)

Listing 7-3Our I2C Test Program

我们编写的程序将确保传感器设置正确,并且通过 I2C 总线进行通信。我们执行通常的导入来让微控制器运行。在(1)中,我们为 I2C 通信进行了设置,创建了我们可以使用的 I2C 总线的实例,具有设备的物理 I2C 引脚的参数。在(2)中,我们锁定了总线的实例,因为这是 CircuitPython 所要求的。在(3)的主循环中,我们一直扫描 I2C 总线,并将连接的设备打印到我们的串行控制台。

一旦传感器正确连接,您应该看到连接到总线的 MPU6050(地址为 0x68)将被打印到我们的串行控制台。

添加库

检测到传感器在那里是好的;然而,我们也可能想要读取传感器数据。为此,我们必须添加已创建的库,以便轻松连接传感器。为设备添加库很简单。首先,请确保从 Adafruit 网站下载适用于您正在使用的设备的库包,网址是:

https://circuitpython.org/libraries

一旦你下载了你的库,解压它。打开连接到计算机的“CIRCUITPY”驱动器,并打开 lib 文件夹。将库包中的以下文件放入“CIRCUITPY”驱动器的 lib 文件夹中,以使用本节内容:

  • adafruit_register 文件夹

  • adafruit_bus_device 文件夹

  • adafruit_mpu6050.mpy 文件

一旦添加了这些文件,您就可以继续下一部分了。

MPU6050 与 CircuitPython 程序

现在我们已经导入了库,我们将能够读取 MPU6050。我们将读取温度、陀螺仪和加速度计数据,并将其打印到串行控制台。打开管理部门编辑器并编辑文件,使其类似于清单 7-4 。

# import the board module
import board

# import time library
import time

# import library for working with SPI
import busio

#import library for working with the MPU6050
1   import adafruit_mpu6050

# setup I2C

2   i2c = busio.I2C(board.SCL, board.SDA)

# create instance of MPU6050
3   mpu = adafruit_mpu6050.MPU6050(i2c)

#super loop
4   while True:
    #print accelerometer data
    print("Acceleration: X:%.2f, Y: %.2f, Z: %.2f m/s²" % (mpu.acceleration))

    # print gyroscope data
    print("Gyro X:%.2f, Y: %.2f, Z: %.2f degrees/s" % (mpu.gyro))

    # print temperature data
    print("Temperature: %.2f C" % mpu.temperature)

    # print space
    print("")

    # every second
    time.sleep(1)

Listing 7-4Reading Information from the MPU6050

在程序中,我们导入我们的库,以便与电路板和 I2C 模块一起工作。在(1)中,我们导入了用于使用 MPU6050 传感器的库。在(2)中,我们为要使用的微控制器上的管脚设置了 I2C 模块。在(3)中,我们创建了一个我们想要使用的 MPU6050 模块的实例。在我们的超级循环 at (4)中,我们使用我们库中可用的方法,每秒钟将加速度计、陀螺仪和温度数据等信息打印到控制台。

结论

在本章中,我们学习了如何在 CircuitPython 中使用串行通信。我们讨论了 UART、SPI 和 I2C 的使用。我们还学习了如何使用 MPU6050 传感器并从器件中读取陀螺仪、加速度计和温度数据。今天有如此多的传感器使用本章中涵盖的所有协议,你将能够感知和操纵来自我们物理世界的几乎任何数据。

八、显示界面

到目前为止,我们所有的电路都是在没有使用任何显示器的情况下构建的。我们可以使用 LED 或蜂鸣器等设备获得传感器数据的反馈,但要真正可视化传感器数据,我们需要一个显示器。虽然到目前为止我们一直使用的串行控制台是一种可视化信息的好方法,但您需要将您的 MCU 连接到您的计算机才能从中获取任何信息。然而,大多数嵌入式系统(计算器、手表等)都有一个显示器,用来给用户提供信息。在本节中,我们将通过学习显示器为制作这类设备打下基础。

该液晶显示器

我们将了解的第一种显示器是液晶显示器或 LCD。液晶显示器是由一种叫做液晶的特殊晶体制成的,这种晶体对电起反应。LCD 的工作原理是液晶被夹在玻璃层之间形成网格。这些陷印点中的每一个都称为一个像素。当电流通过它们时,它们具有能够阻挡光通过的效果。这种对光的阻挡使显示器变暗,并允许我们制作我们识别为信息的图案。这是单色显示器工作的前提。我们可以在图 8-1 中看到这种单色 LCD 的样子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8-1

单色液晶显示器鸣谢:adafruit.com 阿达弗洛

虽然单色 LCD 在过去很受欢迎,但如今我们的产品中设计全彩色 LCD 更为常见。我们说它们是单色的,因为它们只有一种不同的色调,通常是黑色。也有彩色液晶显示器,其工作原理与单色不同。

在我们讨论彩色液晶显示器之前,让我们花点时间来讨论颜色。在光的领域里,有两种颜色。我们称之为加色和减色。

加色有红、绿、蓝三原色,我们称之为 RGB。如果我们有一个黑色的背景(没有光线),我们在这个黑色的背景上加上加色的组合,我们可以得到各种各样的颜色。所有的加色混合会给你白色。

然而,如果我们有白色背景(白光存在),我们可以通过添加颜色来去除部分白光。某些被称为减色法的颜色可以去除光线中的其他颜色。我们可以添加青色(去除红色)、洋红色(去除绿色)和黄色(去除蓝色)来获得无数种颜色。这些颜色被表示为 CMY 或(-R,-G,-B)。当我们把所有的减色法结合起来,我们就得到黑色。

有没有想过为什么我们在打印机上使用 CMY 的颜色?这是因为如果我们有一张白纸(白色背景),我们从其中去除某些光线成分,我们就可以创建出我们在印刷媒体中使用的光谱。然而,混合所有的 CMY 颜色不会产生完全的黑色。这就是为什么我们的打印机必须使用黑色墨盒的原因。在印刷过程中,当我们使用黑色的 CMY 时,它被称为 CMYK,其中 K 代表黑色成分。

现在我们了解了颜色,我们可以继续讨论显示器了。

彩色显示器的像素由红色、绿色和蓝色(RGB)成分组成。像素可以阻挡它们必须表现的光的某些成分,这些被称为透射型液晶显示器。然而,也有显示器发出这些颜色,我们称之为发射型液晶显示器。

为了控制这些像素,我们使用了一个显示控制器,它可以处理有效操作 LCD 所需的所有细粒度控制。显示控制器本身是一个基于微处理器的设备,可以与我们的 MCU 通信,以控制显示器上的像素。如果我们没有显示控制器,那么我们将不得不编写代码来控制 LCD 上的每个像素,这将增加很多复杂性和软件开销。

使用 GLCD

我们将从如何连接单色 GLCD 开始我们的显示界面。虽然单色显示器通常使用并行接口,但如今,控制这些 LCD 通常使用串行通信接口。我们将使用的第一种显示器基于 PCD8544 显示驱动器,使用 SPI 通信协议。

基于 PCD8544 的液晶显示器很受欢迎,因为它们曾经是诺基亚 5110 手机的一部分,因此,它们有时被称为诺基亚 5110 显示器。这些显示器不仅能显示字母数字字符,还能显示图形和位图图像。当 LCD 可以显示图形和字母数字字符时,这种显示器有时被称为图形液晶显示器(GLCD)。

显示器具有 84x48 单色像素。图 8-2 向我们展示了 PCD8544 显示器的样子。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8-2

基于 PCD8544 的 LCD 认证:adafruit.com Adafruit

尽管图 8-2 中的显示器安装在绿色 PCB 上,但有时也经常发现显示器印刷在功能相同的红色 PCB 上。显示器有八个引脚,其功能如下:

  • VCC-连接 VCC。

  • GND-连接到地面。

  • SCE–我们的串行芯片使能引脚,用于在低电平有效时选择显示器。

  • RST–此引脚在拉低时复位 LCD。

  • d/C–这是数据和命令引脚,用于告诉 LCD 我们是否正在向 LCD 发送数据或命令。

  • MOSI–我们的主机输出从机输入引脚用于 SPI 通信。

  • SCLK–这是用于 SPI 通信的串行时钟线。

  • LED–LED 或 LIGHT 引脚为显示器上的背光供电。

单色 GLCD 示意图

我们现在可以将基于 PCD8544 的 LCD 连接到我们的显示器,如图 8-3 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8-3

带 MCU 的 PCD8544

请务必检查与 SPI MOSI 和 SPI SCLK 线相关的主板引脚排列。一旦你接通了电路,我们就可以进入下一步了。

  1. 用一根跳线将显示器的 GND 引脚连接到试验板的 GND 轨。

  2. 将 LED 引脚连接到微控制器上的 D10 引脚。

  3. 用一根跳线将显示器上的 VCC 引脚连接到试验板上的 VCC 导轨。

  4. 将 LCD 时钟引脚连接到微控制器的引脚 D13。

  5. 将 DIN 引脚连接到微控制器的 D11 引脚。

  6. 用一根跳线将 LCD 上的 DC 引脚连接到微控制器的 D6 引脚。

  7. 将 LCD 上的 CE 引脚连接到微控制器的 D5 引脚。

  8. 最后,将 LCD RST 引脚连接到微控制器的 D9 引脚。

带 CircuitPython 的 PCD8544

PCD8544 器件有一个为 CircuitPython 编写的库。要在 CircuitPython 中使用 LCD,我们必须将 Adafruit 库包中的以下文件添加到微控制器上的 lib 文件夹中:

  • -=伊甸园美剧 http://sfile . ydy . com =-荣誉出品本字幕仅供学习交流,严禁用于商业途径

  • -= ytet-伊甸园字幕组=-翻译

  • adafruit_bus_device 文件夹

一旦你将它们添加到你的 lib 文件夹中,我们就可以编写清单 8-1 中给出的程序了。

# import the board module
import board

# import time library
import time

# import library for working with SPI
import busio

# import library for digital I/O
import digitalio

# import the LCD library
(1) import adafruit_pcd8544

(2) # Initialize SPI bus
spi = busio.SPI(board.SCK, MOSI=board.MOSI)

       #initialize the control pins
dc = digitalio.DigitalInOut(board.D6)
cs = digitalio.DigitalInOut(board.D5)
reset = digitalio.DigitalInOut(board.D9)

# create instance of display
(3) display = adafruit_pcd8544.PCD8544(spi, dc, cs, reset)

(4) # set bias and contrast
display.bias = 4
display.contrast = 60

(5) # Turn on the Backlight LED
backlight = digitalio.DigitalInOut(board.D10)
backlight.switch_to_output()
backlight.value = True

# we'll draw from corner to corner, lets define all the pair coordinates here
(6) corners = (
    (0, 0),
    (0, display.height - 1),
    (display.width - 1, 0),
    (display.width - 1, display.height - 1),
)

(7) #draw some graphics
       display.fill(0)
for corner_from in corners

:
    for corner_to in corners:
        display.line(corner_from[0], corner_from[1], corner_to[0], corner_to[1], 1)
display.show()
time.sleep(2)

(8) # draw some graphics
display.fill(0)
w_delta = display.width / 10
h_delta = display.height / 10
for i in range(11):
    display.rect(0, 0, int(w_delta * i), int(h_delta * i), 1)
display.show()
time.sleep(2)

(9) # draw text
display.fill(0)
display.text("hello world", 0, 0, 1)
display.show()

(10) #super loop
while True:
    # invert display
    display.invert = True

    time.sleep(0.5)

    # remove invert
    display.invert = False
    time.sleep(0.5)

Listing 8-1PCD8544 with CircuitPython

我们的程序工作如下。首先,我们执行常规导入来设置电路板并使其运行。在(1)中,我们导入了用于处理 LCD 的库。在(2)中,我们初始化 SPI 总线,然后初始化控制引脚。At (3)是我们创建将要使用的实际模块的实例的地方。然后,我们继续设置选项,以控制(4)处显示的偏差和对比度。下一步是打开背光 LED,我们在(5)中这样做。

我们的下一步是展示 LCD 的图形功能。在(6)处,我们在显示器上画角,在(7)和(8)处画一些漂亮的图形效果,在(9)处,我们在 LCD 上写一些文字。在(10)的超级循环中,我们演示了库反转功能。

解决纷争

在撰写本文时,如果您试图按原样运行该程序,可能会出现错误。你会看到一个抱怨字体的输出,如图 8-4 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8-4

找不到字体错误

解决方法是将字体文件“font5x8.bin”放在 CIRCUITPY 驱动器的根文件夹中,如图 8-5 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8-5

放置字体文件

放置字体文件后,再次运行程序,您将看到文本输出到您的显示器上。程序输出的图形之一将如图 8-6 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8-6

试验板上的电路

现在,我们的单色 LCD 可以与 CircuitPython 配合使用。在下一节中,我们将看看如何扩展我们已经知道的内容。

帧缓冲区

为了更有效地更新显示输出,我们可以导入一个名为“adafruit_framebuf.mpy”的库,它提供了 framebuffer 功能。帧缓冲区是用来存放我们将要输出到内存中的数据帧的名称。framebuffer 不仅可以用于 LCD,还可以用于任何输出设备,包括串行终端。查看清单 8-2 ,我们使用帧缓冲区将数据输出到串行终端。

# import the frame buffer library
(1) import adafruit_framebuf

print("framebuf test will draw to the REPL")

(2) WIDTH = 32
HEIGHT = 8

(3) buffer = bytearray(round(WIDTH * HEIGHT / 8))
fb = adafruit_framebuf.FrameBuffer(
    buffer, WIDTH, HEIGHT, buf_format=adafruit_framebuf.MVLSB

)

(4) # Ascii printer for very small framebufs!
def print_buffer(the_fb):
    print("." * (the_fb.width + 2))
    for y in range(the_fb.height):
        print(".", end="")
        for x in range(the_fb.width):
            if fb.pixel(x, y):
                print("*", end="")
            else:
                print(" ", end="")
        print(".")
    print("." * (the_fb.width + 2))

(5) # Small function to clear the buffer
def clear_buffer():
    for i, _ in enumerate(buffer):
        buffer[i] = 0

(6) print("Shapes test: ")
fb.pixel(3, 5, True)
fb.rect(0, 0, fb.width, fb.height, True)
fb.line(1, 1, fb.width - 2, fb.height - 2, True)
fb.fill_rect(25, 2, 2, 2, True)
print_buffer(fb)

(7) print("Text test: ")
# empty
fb.fill_rect(0, 0, WIDTH, HEIGHT, False)

# write some text
fb.text("hello", 0, 0, True)
print_buffer(fb)
clear_buffer()

# write some larger text

fb.text("hello", 8, 0, True, size=2)
print_buffer(fb)

Listing 8-2Framebuffer with CircuitPython

清单 8-2 中的 framebuffer 示例是 CircuitPython 库包中提供的示例,它演示了我们如何使用 CircuitPython framebuffer 输出到显示器。首先,我们在(1)导入帧缓冲库。在(2)中,我们设置缓冲区的尺寸,然后在(3)中创建缓冲区。我们的下一步是创建一个打印缓冲区的函数,我们在(4)中完成。在(5)中,我们有一个清除缓冲区的函数。在(6)中,我们将形状打印到串行终端,在(7)中,我们打印一些文本,然后是一些更大的文本。

当您运行该程序时,您会发现在写入您的串行终端的 ASCII 代码中有文本和形状。

有机发光二极管(Organic Light Emitting Diode 的缩写)

液晶显示器是一项伟大的技术,已经为技术世界服务了很长时间。然而,另一种显示技术已经在技术世界中流行起来。这是基于有机发光二极管(有机发光二极管)的显示器。

有机发光二极管显示器产生丰富清晰的色彩,并且比传统的 LCD 显示器具有更宽的视角。它们还具有更快的响应时间。与液晶显示器相比,有机发光二极管显示器很容易辨别。由于显示器提供了更高的对比度,颜色更加清晰。与 LCD 显示器相比,有机发光二极管显示器在其结构中具有额外的层。这些层由允许它们发光的有机物质构成。将有机发光二极管想象成一个三明治,如图 8-7 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8-7

基本有机发光二极管结构

图 8-7 中的简化三明治很好地代表了有机发光二极管的样子。它由位于阳极和阴极材料之间的有机层组成。然后将它们放在由玻璃或塑料制成的底层之间,我们称之为基底。有机层本身由两层组成,即发射层和导电层。有机发光二极管通过使电流从阴极层穿过有机层到达阴极层来发光。这种电流导致了光的发射。由于有机发光二极管能够发出自己的光,所以不需要像大多数 LCD 显示器那样的背光。

与 LCD 一样,有机发光二极管显示器也需要一个驱动程序来轻松控制有机发光二极管,在下一节中,我们将探讨如何将显示器与我们自己的微控制器电路接口。

使用有机发光二极管

目前市场上有许多显示器;然而,有时与驱动程序接口可能是一个问题。出于这个原因,我们将考虑使用一个带有驱动程序的有机发光二极管,该驱动程序有一个我们可以轻松控制的可用库。

我们将使用的有机发光二极管显示器是基于 SSD1306 的有机发光二极管,具有 128 x 64 像素,屏幕尺寸为 1.44 英寸。该显示器的许多版本可以同时使用 SPI 和 I2C,例如图 8-8 中 Adafruit 提供的版本。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8-8

基于 SSD1306 的 LCD 认证:adafruit.com Adafruit

如图 8-9 所示,我们将在 I2C 模式下使用显示器,因为有许多低成本的 I2C 专用版本。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8-9

仅基于 SSD1306 的液晶 I2C

显示器的 I2C 模式使用四条线,分别是 VCC、GND、SCL 和 SDA。我们的 MCU 只使用 SCL 和 SDA 线,只需要两条 I/O 线来驱动有机发光二极管。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 8-10

内置 SSD1306 的 MCU 有机发光二极管原理图

带有机发光二极管原理图的 MCU

原理图包括将我们的有机发光二极管连接到 I2C 总线,如图 8-10 所示。一些版本的显示器包括 I2C 总线的上拉电阻;我们仍将包括它们,以防您的显示器版本不包括上拉电阻。

我们将显示器连接到微控制器,如下所示:

  1. 使用跳线将有机发光二极管上的 GND 引脚连接到试验板上的 GND 引脚。

  2. 用一根跳线将有机发光二极管的 VCC 引脚连接到试验板上的 VCC 轨。

  3. 将有机发光二极管上的 SDA 引脚连接到微控制器的 SDA 引脚。

  4. 将有机发光二极管的 SCL 引脚连接到微控制器的 SCL 引脚。

  5. 拿起电阻,按如下方式连接。将每个电阻的一根引线连接到试验板的 VCC 轨。取出自由引脚,将一端分别连接到有机发光二极管的 SDA 和 SCL 引脚。

CircuitPython 与有机发光二极管程序

在我们的程序中,我们使用 Adafruit 提供的示例在显示器上制作弹跳球的动画。清单 8-3 中给出了程序。

# usual imports
import board
import busio

(1) # import library for working with SSD1306
import adafruit_ssd1306

# Create the I2C interface.
i2c = busio.I2C(board.SCL, board.SDA)

(2) # Create the SSD1306 OLED class.
# The first two parameters are the pixel width and pixel height.  Change these
# to the right size for your display!
oled = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c)

(3) # Helper function to draw a circle from a given position with a given radius
# This is an implementation of the midpoint circle algorithm,
# see https://en.wikipedia.org/wiki/Midpoint_circle_algorithm#C_example for details
def draw_circle(xpos0, ypos0, rad, col=1):
    x = rad - 1
    y = 0
    dx = 1
    dy = 1
    err = dx - (rad << 1)
    while x >= y

:
        oled.pixel(xpos0 + x, ypos0 + y, col)
        oled.pixel(xpos0 + y, ypos0 + x, col)
        oled.pixel(xpos0 - y, ypos0 + x, col)
        oled.pixel(xpos0 - x, ypos0 + y, col)
        oled.pixel(xpos0 - x, ypos0 - y, col)
        oled.pixel(xpos0 - y, ypos0 - x, col)
        oled.pixel(xpos0 + y, ypos0 - x, col)
        oled.pixel(xpos0 + x, ypos0 - y, col)
        if err <= 0:
            y += 1
            err += dy
            dy += 2
        if err > 0:
            x -= 1
            dx += 2
            err += dx - (rad << 1)

(4) # initial center of the circle
center_x = 63
center_y = 15
# how fast does it move in each direction
x_inc = 1
y_inc = 1
# what is the starting radius of the circle
radius = 8

# start with a blank screen
oled.fill(0)
# we just blanked the framebuffer. to push the framebuffer onto the display, we call show()
oled.show()
(5) while True

:
    # undraw the previous circle
    draw_circle(center_x, center_y, radius, col=0)

    # if bouncing off right
    if center_x + radius >= oled.width:
        # start moving to the left
        x_inc = -1
    # if bouncing off left
    elif center_x - radius < 0:
        # start moving to the right
        x_inc = 1

    # if bouncing off top
    if center_y + radius >= oled.height:
        # start moving down
        y_inc = -1
    # if bouncing off bottom
    elif center_y - radius < 0:
        # start moving up

        y_inc = 1

    # go more in the current direction
    center_x += x_inc
    center_y += y_inc

    # draw the new circle
    draw_circle(center_x, center_y, radius)
    # show all the changes we just made
    oled.show()

Listing 8-3Bouncing Ball Program

在我们的程序中,我们创建了一个弹跳球。在(1)中执行我们通常的导入后,我们导入库以使用 SSD1306 显示器。在(2)中,我们创建了一个可以操作的 SSD1306 类的实例。由于球本质上是一个实心圆,在(3)中,我们有一个画圆的函数。在(4)中,我们设置球的参数,包括它的尺寸和速度。在(5)处,无限循环将弹跳球吸引到显示器。如果您查看 Adafruit 库包,您会看到其他可以用来处理显示的示例程序。

结论

在这一章中,我们看了界面显示。我们研究了如何同时使用液晶显示器(LCD)和有机发光二极管(有机发光二极管)。在这个过程中,我们了解了 LCD 和 GLCDs 的工作原理,并了解了如何使用 Adafruit 缓冲库向串行控制台输出信息。有了使用显示器的能力,您现在可以显示与计算机无关的信息。使用传感器的能力与显示信息的能力相结合,涵盖了许多嵌入式系统的功能。

九、控制 DC 执行器

微控制器可以用来控制致动器设备。致动器是一种负责运动的装置。致动器分为两组,即机械致动器和机电致动器。在这一章中,我们将研究控制一些重要的机电 DC 执行器。学会控制执行器后,整个世界向你敞开。自动锁,机器人,数控机床更容易理解,以后你就有能力造这种设备了。

DC 汽车公司

您将使用的第一种类型的致动器是 DC 马达。DC 电机被用于很多设备,从玩具,如遥控汽车,到电器和工具,如无绳电钻。

当电势差(电压)施加在它们的端子上时,这些电机通过旋转动作运行。

有两种类型的 DC 电机,有刷 DC 电机和无刷 DC 电机。无刷 DC 电机比有刷 DC 电机具有更好的热特性和更高的效率。然而,有刷 DC 电机可以以更简单的方式驱动,从而降低系统成本。

有刷 DC 电机是你可能会在自己的项目中使用的类型,尽管在本章的后面我们还会看到一种称为步进电机的无刷 DC 电机。

图 9-1 显示了玩具和其他简单设备中常见的典型有刷 DC 电机。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-1

有刷 DC 电机

由于其机械结构,有刷 DC 电机的寿命比无刷电机短。这是因为有刷 DC 电机有一个被称为电刷的部件,它会随着时间的推移而磨损。

驾驶 DC 汽车

有刷 DC 电机应该很好开;我的意思是,如果你把一个 DC 发动机连接到电池上,如图 9-2 所示,它应该会运行。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-2

带电池的 DC 发动机

然而,这种简单的方法缺乏智能控制,并且电机将仅在一个方向上旋转。如果我们想让马达在智能控制下反方向旋转,那就不仅仅需要把 DC 马达和电池连接起来。使用 MCU 驱动电机的方式如图 9-3 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-3

用单片机驱动 DC 电机

电路运行的前提很简单。来自微控制器的信号将使晶体管打开或关闭。根据晶体管的状态,电流将流过电机,使其旋转。我们必须记住,电机是一个感性负载。因此,D1 是一个缓冲二极管,保护其他电路元件免受电机产生的感应尖峰的影响。如果您确实需要对振动电机等小型电机进行简单的开/关控制,那么您可以使用图 9-4 中的电路。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-4

一种实用的开关控制电路

这里,在图 9-4 中,我们有一个如图 9-3 所示版本的实际电路。为了驱动电机,我们使用 2N2222A 晶体管,它可以处理高达 800 mA 的电流,足以驱动电机。虽然 2N3904 经常被宣传为 2N2222A 的替代品,但在本电路中并不合适,因为 2N3904 的电流处理能力仅为 200 mA。如果你想驱动需要简单开/关控制的小电机,这个电路是很好的。如图 9-5 所示,用于触觉反馈的振动电机或用于玩具等的标准 130 DC 电机是理想的选择。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-5

标准 130 adafruit.com DC 汽车信贷公司

虽然简单的开/关控制确实有其应用,但如果我们真的想要一个智能电机控制,我们需要像方向和速度控制这样的东西。这些属于脉宽调制的范畴,我们将在下一节讨论。

脉宽灯

如果你真的想用微控制器来控制电机的速度,那么你必须考虑一种叫做脉宽调制(PWM)的东西。在讲 PWM 之前,我们先来看一下图 9-6 中的方波。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-6

方波

在你的方波上,你有一个我们称之为高电平时间的时间和一个我们称之为低电平时间的时间。如果这个方波是由微控制器产生的,我们可以将高电平时间设为 3.3 伏,低电平时间设为 0 伏。这种脉冲重复的频率称为波的频率,我们用赫兹(Hz)来测量。波的周期是频率的倒数,指的是周期重复一次所需的时间。

由于波的周期是频率的倒数,随着波形频率的增加,波的周期将同时减小。

PWM 的一个重要方面是占空比。众所周知,数字信号可以是高电平或低电平。波形的高电平时间称为占空比,通常用百分比表示。例如,如果一个波一半时间为高电平,一半时间为低电平,则可以说它的占空比为 50%。

图 9-7 显示了我们可以在波形上识别占空比的图表。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-7

占空比

通过调整波形的占空比,我们可以有效地调整输出的电压水平。这是一种强大的技术。使用 PWM,我们可以控制发光二极管的亮度和控制电机的速度。

电路中的 PWM python

如今,几乎每个微控制器都提供了支持 PWM 的模块,CircuitPython 提供了用于控制这些 PWM 模块的库。大多数支持 CircuitPython 的 MCU 都有 PWM 引脚,在输出引脚旁边印有一个小小的波浪号“~”。为了在 CircuitPython 中使用 PWM,我们使用以下库:

  • 电路板–我们需要电路板库来指示微控制器的特定引脚。

  • 时间–时间库提供了处理基于时间的活动的功能。

  • pulse io–pulse io 库是 CircuitPython 中 PWM 用法的核心。该库为支持 PWM 的引脚提供了函数。

使用 CircuitPython 程序进行 PWM

我们可以使用 PWM 模块来淡化 LED。我们可以使用 PWM 来淡化电路板上的 LED。清单 9-1 中有一个程序,我们可以用它来淡化连接到 PWM 引脚的 LED,在本例中是引脚 D13。大多数支持 CircuitPython 的电路板都有一个 LED 连接到此引脚;如果此引脚上没有 LED,或者您使用的是定制板,则可以用一个 1k 电阻将 LED 连接到引脚 D13。

# import time functions
import time

# import our board specific pins
import board

# library for using PWM
(1) import pulseio

# setup the pwm using Pin13, with a frequency of 5000 Hz
(2) pwmLed = pulseio.PWMOut(board.D13, frequency=5000)

(3) while True:
    for i in range(100):
        # PWM LED up and down
        if i < 50:
            # below 50 pwm up
            pwmLed.duty_cycle = int(i * 2 * 65535 / 100)
        else

:
            # more than 50 pwm down
            pwmLed.duty_cycle = 65535 - int((i - 50) * 2 * 65535 / 100)
        # slow it down so we can see
        time.sleep(0.05)

Listing 9-1The PWM Program

当你运行程序时,你会看到 LED 变得非常亮,然后逐渐消失,变得暗淡。该程序的工作原理如下。在(1)中,我们导入了用于使用 PWM 的“pulseio”库。在(2)中,我们在引脚上创建一个 PWM 模块实例,并将频率设置为 5000 赫兹。在(3)中,我们有一个主程序循环,在该循环中,我们递增然后递减 PWM 占空比一个指定的周期。这导致 LED 具有褪色效果。

控制电机速度

如果你想控制 DC 电机,标准的方法是用 PWM 控制。如果我们提供电机运行所需的最大电压,那么电机将全速运行。然而,如果我们通过使用 PWM 调整电机的占空比来快速打开和关闭电机,我们将能够控制电机的有效或平均速度。

我们可以创建如图 9-8 所示的硬件连接来控制电机速度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-8

电机控制的连接

这与我们在本章前面讨论的电路相同。我们只需将“MCU”替换为您选择的引脚和一个晶体管,该晶体管可以处理您想要控制的电机的电流。为了控制电机速度,我们可以使用 MCU 的 PWM 来改变电机的速度,使用晶体管来做重物提升。然而,有一种更好的方法,我们将在下一节中探讨。

H 桥

如果你需要简单的开/关控制或者只是调整电机的转速,用晶体管驱动电机是很好的选择。然而,在有些情况下,你不仅希望调整电机的速度,还需要控制旋转的方向。移动机器人就是一个很好的例子。在移动机器人中,机器人不仅需要向前行驶,还需要反向行驶。要做到这一点,您可以使用一种称为 H 桥的电路配置,如图 9-9 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-9

H 桥

该电路将如下工作。当 S3 和 S2 关闭时,电机将正向旋转,当 S1 和 S4 关闭时,电机将反向旋转。使用 H 桥时,避免在驱动电机时产生短路非常重要。如果左边的两个开关(S1 和 S2)或右边的两个开关(S3 和 S4)都闭合,那么您将创建从源到地的短路。如果使用 H 桥 IC,通常会包括热关断功能,以防止这些器件被损坏。但是,您不应该依赖这种保护机制来保护您的 IC。

通常,当您需要高电流处理能力时(在这种情况下大于 5A),通常用 MOSFETs 构建 H 桥。然而,对于大多数低于 5A 的应用,您可以使用 H 桥 IC。有可能获得处理 5A 以上电流的 IC 驱动器。然而,由于功耗水平以及电机可能需要的峰值电流,构建分立 H 桥通常更经济。

可能有些人不同意我的观点,但随着半导体技术的进步,为大多数应用构建自己的分立驱动器将不再必要。

对于低电流应用(< 5A),通常使用 H 桥驱动器 IC。两种常见的 H 桥 IC 是 L293D 和 SN754410NE 四路半 H 驱动器。这些驱动程序非常相似,在大多数应用中可以互换使用。

与 L293D 相比,SN754410NE 具有更高的连续电流输出和更高的峰值电流。图 9-10 突出了这两种驱动器的特征差异。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-10

L293D 与 SN754410NE 特性的关系

在图 9-11 中,我们看到了两个设备的物理布局。L293D 在左边,SN754410NE 在右边。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-11

L293D 与 SN754410NE 物理封装

图 9-11 中的封装是 PDIP-16 封装,它们的引脚相互兼容。芯片的内部逻辑和电机电源都有 VCC 电源。VCC1 为芯片的内部逻辑供电,连接到 5v,VCC2 连接到电机电源,通常为 9–12v。

左马达连接到针脚 1Y 和 2Y,右马达连接到针脚 3Y 和 4Y。引脚 1A 和 2A 是左逻辑引脚,3A 和 4A 引脚是右逻辑引脚。1,2 EN 使能左驱动器,3,4 EN 使能右驱动器。

驾驶员基本上有三种被驱动状态,即前进、后退和制动。在正向状态下,我们将一个开关设为高电平,另一个设为低电平。对于反向旋转,我们反转引脚上的逻辑电平。为了创建制动场景,我们将两个逻辑电平引脚都设为低电平状态。

带 MCU 原理图的 h 桥

我们可以使用逻辑电平转换器将微控制器连接到 L293D,因为 L293D 需要 5v 逻辑,我们有运行 CircuitPython 的 3.3v 器件。如图 9-12 所示连接原理图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-12

带 H 桥的 CircuitPython MCU

  1. 将微控制器的引脚 D12 和 D13 连接到逻辑电平转换器的 LV1 和 LV2。

  2. 将逻辑电平转换器上的 GND 引脚连接到试验板上的接地轨。

  3. 将逻辑电平转换器的 HV1 引脚连接到 H 桥 ic 的 INPUT1 引脚,将 HV2 引脚连接到 INPUT2 引脚。

  4. 将电机的一个引脚连接到输出 1,另一个引脚连接到输出 2。

  5. H 桥的使能引脚连接到 MCU 的 A1。

  6. 将 H 桥 IC 的正极引脚连接到电源轨,将 GND 引脚连接到接地轨。

连接的电路应如图 9-13 所示。注意你使用的电机的大小很重要,因为越大的电机使用的电流越大;因此,你必须确保额外的电容器放置在电源轨上,如图 9-13 所示。如果你不放置这些额外的电容,那么微控制器将复位,你将有一个意外的操作。H 桥运行时,无需连接驱动器右侧的接地连接,但如果您愿意,也可以将其连接起来作为预防措施。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-13

试验板上集成 MCU 的 h 桥

h 桥与 CircuitPython 程序

我们将编写一个程序,使用 PWM 来限制由 L293D 电机驱动器驱动的电机的速度,这将允许我们控制电机的方向。我们将打开 Mu 编辑器并创建清单 9-2 中的程序。

# import time functions
import time

# import our board specific pins
import board

# library for using PWM
(1) import pulseio

# library for working with digital output
import digitalio

# create instance of enable pin
(2) en = digitalio.DigitalInOut(board.A1)

# set the enable pin to output
en.direction = digitalio.Direction.OUTPUT

# start with enable false
en.value = False

# setup the pwm using Pin13, with a frequency of 5000 Hz
# steup the pwm using Pin12, with a frequecny of 5000 Hz
(3) in1 = pulseio.PWMOut(board.D13, frequency=5000, duty_cycle=0)
in2 = pulseio.PWMOut(board.D12, frequency=5000, duty_cycle=0)

# turn in forward direction
(4) def forward():
    print("forward")
    en.value = True
    in1.duty_cycle = 20000
    in2.duty_cycle = 0
    time.sleep(3)

# reverse direction
(5) def reverse():
    print("reverse")
    en.value = True
    in1.duty_cycle = 0
    in2.duty_cycle = 20000
    time.sleep(3)

# stop motors
(6) def stop():
    print("stop")
    en.value = False
    in1.duty_cycle = 0
    in2.duty_cycle = 0
    time.sleep(2)

# super loop

(7) while True:
    # forward
    forward()

    # stop before transition
    stop()

    # reverse
    reverse()

    # stop before transition
    stop()

Listing 9-2Using the H-Bridge

在程序中,我们做我们通常的导入,在(1)中,pulseio 库被导入用于处理 PWM 引脚。在(2)处,设置使能引脚,并设置其方向和状态。在(3)处,设置 PWM 实例,用于连接 H 桥。在(4)、(5)和(6)中,创建了允许电机正转、反转和停止的功能。正向功能允许电机正向旋转,反向功能允许电机反向旋转,停止功能阻止电机移动。

在(7)的超级循环中,我们正向旋转电机 3 秒钟;请注意,在我们转换到另一个方向之前,我们停止电机 2 秒钟。在那之后,我们反转马达的方向,这样无限地继续下去。

伺服电机

在上一节中,我们介绍了有刷 DC 电机以及如何驱动它们。另一个常见的 DC 致动器你可能会遇到的是 DC 伺服电机。这些伺服电机是 R/C(无线电控制)伺服电机,因为它们旨在用于与遥控器(通常是模型飞机)相关的爱好应用。

这些电机不需要外部电机驱动器。这是因为它们是独立的,具有带控制电路的 DC 电机和集成到设备中的齿轮系。

这些电机有三根电线,如图 9-14 所示。一根导线向伺服系统供电,另一根导线接地,最后一根导线用于向电机发送控制信号。电源线通常为红色,接地线为棕色或黑色,控制信号为白色或橙色。

图 9-14 中的伺服系统是 MG90D,这是一个很好的标准微伺服系统,可以在您自己的项目中使用。

有两种类型的伺服,这是连续旋转和标准伺服电机。连续旋转伺服电机可以旋转完整的 360 度,而标准伺服范围从 0 到 180 度。

为了控制伺服电机,我们需要在信号线上发送一个脉冲。对于连续旋转的伺服系统,脉冲的长度将决定伺服系统旋转的速度。在标准伺服电机中,脉冲长度将决定伺服旋转的位置。

脉冲长度将由制造商指定。然而,对于典型的伺服系统,1 ms 的脉冲宽度将使电机转到 0 度位置。当提供 1.5 毫秒的脉冲宽度时,它将使电机转向 90 度位置。最后,当我们提供一个 2 毫秒的脉冲,电机将转向 180 度的位置。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-14

遥控伺服电机

伺服电机也需要发送一个信号来保持其位置一段时间。如果不发送该信号,电机的运行可能会变得非常不规则,出现跳动。

CircuitPython 中的伺服电机

控制一个伺服系统很简单,但对初学者来说可能会令人望而生畏。为了使用伺服功能,我们必须使用 PWM 来控制脉冲。幸运的是,CircuitPython 提供了我们可以用来控制伺服系统的库。要使用伺服电机,我们需要

  • pulseio 库——这将允许我们使用 PWM 来控制伺服电机。

  • 这个库包含了我们控制伺服电机所需要的函数。该库还提供了一些与刷 DC 和步进电机工作的函数。要使用这个库,我们需要将它复制到微控制器上的 lib 文件夹中。

带 MCU 原理图的伺服电机

我们需要使用逻辑电平转换器将微控制器连接到伺服系统,因为伺服系统运行在 5 伏电压下,但我们的微控制器是 3.3 伏器件。该示意图如图 9-15 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-15

带伺服原理图的 MCU

我们按如下方式连接电路:

  1. 将伺服电机的 V+引脚连接到电源轨。

  2. 将伺服电机的 GND 引脚连接到试验板的 GND 轨道。

  3. 信号引脚连接到逻辑电平转换器的 HV3 引脚。

  4. 将逻辑电平转换器的 LV3 连接到微控制器的 A2 引脚。

  5. 将逻辑电平转换器的 GND 引脚连接到试验板上的接地轨。

在图 9-16 中,我们看到了电路的试验版。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-16

带伺服试验板的 MCU

带 CircuitPython 程序的伺服电机

为了控制伺服电机,我们将使用 adafruit_motor 库函数,该示例将电机扫过 180 度圆弧。清单 9-3 中给出了程序。

# import the time library
import time

# import the board pins
import board

# import library for working with PWM
import pulseio

# import library for working with servo
(1) from adafruit_motor import servo

# create a PWMOut object on Pin A2.
(2) pwm = pulseio.PWMOut(board.A2, duty_cycle=2 ** 15, frequency=50)

# Create a servo object, my_servo and set the min and max pulse
(3) my_servo = servo.Servo(pwm, min_pulse = 500, max_pulse = 2800)

(4) while True:
    for angle in range(0, 180, 5):  # 0 - 180 degrees, 5 degrees at a time.
        my_servo.angle = angle
        time.sleep(0.05)
    for angle in range(180, 0, -5): # 180 - 0 degrees, 5 degrees at a time.
        my_servo.angle = angle
        time.sleep(0.05)

Listing 9-3Using the Servo Motor Program

在我们的程序中,我们执行通常的导入以及 pulseio 库来处理 PWM。在(1)中,我们导入了 adafruit_motor 库,以允许我们控制伺服电机。在(2)中,我们在引脚 A2 上创建了一个 PWM 实例。在(3)中,我们可以操作的伺服对象被创建,并且我们设置使伺服操作所需的脉冲宽度。在(4)的主程序循环中,我们首先从 0 度到 180 度扫描伺服范围,然后从 180 度到 0 度。

在 my_servo 对象中随意调整最小和最大脉冲;该程序已配置为与 MG90S 电机或同等产品一起工作。

步进电机

我们要看的最后一种电机是步进电机。步进电机是一种无刷 DC 电机,这当然使它在某些应用中优于有刷 DC 电机。步进电机非常适合位置控制,这使得它们在 CNC 机器、3D 打印机和绘图机中无处不在。步进电机还具有很高的保持转矩,这使得它们特别适合这种应用。然而,步进电机的缺点是,由于它们的结构,它们通常不如有刷 DC 电机运行得快。

步进电机有两种,双极步进电机和单极步进电机。不管是哪一种,步进电机都是通过给围绕中心转子的线圈通电来工作的,中心转子上有我们称之为磁极的永磁齿。线圈被称为定子。这些定子是电磁体,按顺序极化和去极化,使电机能够步进旋转。这些阶梯旋转一定的角度,我们称之为阶梯角。

步距角取决于步进电机内定子极和转子齿的数量。定子分阶段通电。定子的这些相是连续接通和断开的。这样做会产生一个磁场,使转子转动,从而产生步进动作。

双极步进电机由两个绕组和一个电机电枢组成,如图 9-17 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-17

双极步进电机

双极步进电机有四根电线。您通常会参考数据手册来确定哪两根电线属于哪个绕组对。电线通常用颜色标记,以便于识别。如果没有数据表,可以将万用表设在导通档。使用此功能,您可以检查连续性,以确定哪对电线属于同一对。

图 9-18 中所示的 42BYGHM809 是一种常见的双极步进电机。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-18

42building m809 步进电机

还有单极步进电机。这些电机可采用五引脚、六引脚或八引脚配置。由于六引脚配置最为常见,这就是我们要研究的品种。六针单极步进电机的原理图如图 9-19 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-19

六引线单极步进电机

单极步进电机每相有一个带中心抽头的绕组。这意味着电流只沿一个方向流过线圈。这与电流双向流过线圈的双极步进电机形成对比。

我们将使用的六线步进电机是 Sinotech 25BY4801,它代表一种普通的小型步进电机,如图 9-20 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-20

Sinotech 25BY4801 六线步进电机

如果需要,您可以通过简单地忽略中心抽头,将六引线单极步进电机作为双极电机来驱动。

有三种方式可以用来驱动步进电机,它们是波形驱动模式、全驱动模式和半驱动模式。

在波形驱动模式下,我们一次给电机上的每个定子线圈通电一次。这样做的结果是,它给了我们更少的输出扭矩,但也减少了电机消耗的功率。

在全驱动模式下,我们一次激励两个定子,为我们提供更大的输出扭矩,这也伴随着更大的电流消耗。

还有一种半驱动步进模式,交替激励一个相位,然后两个相位。它用于使电机的角度分辨率加倍(增加步数)。不过不要担心;我们将只关注波浪驱动和全驱动模式,这两种模式对于初学者来说更容易理解。

CircuitPython 中的步进电机

adafruit_motor 库包含用于单极和双极步进电机的函数。然而,如果我们手动驱动电机,那么理解发生了什么会更有用。因此,我们将不使用 adafruit_motor 库函数。

带 MCU 原理图的步进电机

为了控制电机,我们将使用 ULN2003 IC,它可以处理步进电机的高电流要求。ULN2003 在一个封装中包含七个达林顿晶体管,在最高 40v 的电压下,每个驱动器可以处理高达 500 mA 的电流。这对于驱动我们的马达来说绰绰有余。该驱动器还包括抑制二极管,这使得它很适合驱动电感负载,如我们的步进电机。这些抑制二极管提供额外的电路保护。示意图如图 9-21 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 9-21

步进电机控制

我们可以按如下方式连接电路:

  1. 将逻辑电平转换器的 GND 引脚接地。

  2. 将逻辑电平转换器的 LV4 引脚连接到 D13 引脚。

  3. 将逻辑电平转换器的 LV3 引脚连接到 D12 引脚。

  4. 将逻辑电平转换器的 LV2 引脚连接到引脚 D11,将 LV1 引脚连接到引脚 D10。

  5. 在逻辑电平转换器上,将引脚 HV4、HV3、HV2 和 HV1 分别连接到 ULN2003 的引脚 1B、2B、3B 和 4B。

  6. 将 ULN2003 的 COM 引脚连接到 VCC 引脚。

  7. 将步进电机的中心抽头 2 和 5 连接到 VCC。

  8. 将步进电机的电线 1 和 3 分别连接到 ULN2003 的引脚 4C 和 3C。

  9. 将步进电机的导线 4 和 6 分别连接到 ULN2003 的引脚 1C 和 2C。

由于 ULN2003 在 5v 逻辑下比在 3.3v 逻辑下输出更多的电流,我们使用逻辑电平转换器将来自 MCU 的 3.3v 信号转换为供 ULN2003 使用的 5v 信号。

步进电机与 CircuitPython 程序

我们现在可以编写一个程序,使用波形驱动和全驱动模式来控制步进电机。清单 9-4 中给出了程序。该程序在紧凑的 Python 代码方面效率不高,但我觉得它对初学者来说更容易理解。

# import pin constants for board we are using
import board

# import pin control
import digitalio

# import time
import time

(1) # create objects for pins we are using
WHT = digitalio.DigitalInOut(board.D10)
BLK = digitalio.DigitalInOut(board.D11)
YEL = digitalio.DigitalInOut(board.D12)
RED = digitalio.DigitalInOut(board.D13)

(2) # set the pins to output
WHT.direction = digitalio.Direction.OUTPUT
BLK.direction = digitalio.Direction.OUTPUT
YEL.direction = digitalio.Direction.OUTPUT
RED.direction = digitalio.Direction.OUTPUT

(3) # super loop
while True:
    for i in range(24):
        if i < 12:

            # phase 1
            # 1000
            RED.value = True    # A
            BLK.value = False   # B
            YEL.value = False   # C
            WHT.value = False   # D
            time.sleep(0.1)

            # phase 2
            # 0100

            RED.value = False    # A
            BLK.value = True     # B
            YEL.value = False    # C
            WHT.value = False    # D
            time.sleep(0.1)

            # phase 3
            # 0010
            RED.value = False    # A
            BLK.value = False    # B
            YEL.value = True     # C
            WHT.value = False    # D
            time.sleep(0.1)

            # phase 4
            0001
            RED.value = False   # A
            BLK.value = False   # B
            YEL.value = False   # C
            WHT.value = True    # D
            time.sleep(0.1)

            time.sleep(0.5)

        else:
            # phase 4
            # 1001
            RED.value = True    # A
            BLK.value = False   # B
            YEL.value = False   # C
            WHT.value = True    # D
            time.sleep(0.1)

            # phase 3
            # 0011

            RED.value = False   # A
            BLK.value = False   # B
            YEL.value = True    # C
            WHT.value = True    # D
            time.sleep(0.1)

            # phase 2
            # 0110
            RED.value = False   # A
            BLK.value = True    # B
            YEL.value = True    # C
            WHT.value = False   # D
            time.sleep(0.1)

            # phase 1
            # 1100
            RED.value = True    # A
            BLK.value = True    # B
            YEL.value = False   # C
            WHT.value = False   # D
            time.sleep(0.1)

            time.sleep(0.5)

Listing 9-4Using the Stepper Motor Program

在我们的程序(1)中,我们为正在使用的管脚创建对象,然后在(2)中,我们将管脚设置为输出管脚。这些接点是根据它们的导线颜色命名的,以使连接更容易理解。在我们的主循环中的(3)处,我们使用波形驱动模式向前旋转电机。然后,当我们达到 360 度旋转时,我们使用全驱动模式使电机回到其起始位置。

在我们的超级循环中,当“I”变量达到 12 时,步进电机将完成一次完整的旋转。由于电机的步进角度为 7.5 度,电机中有四个相位,每次变量增加,我们就步进四次,直到 7.5 度,也就是 30 度。12 次计数后,我们将旋转 360 度。

如果您愿意,可以测量电流消耗,您会发现全驱动模式比波形驱动模式消耗的电流更少。在我的测试中,全驱动模式的电流消耗约为 520 毫安,而波形驱动模式的电流消耗约为 325 毫安。

结论

在这一章中,我们讨论了各种 DC 致动器。我们研究了有刷和无刷 DC 电机,包括有刷 DC 电机、步进电机和伺服电机。我们讨论了这些电机的特性和用途,以及如何将它们与基于 Python 的 MCU 进行接口。通过学习这些主题,我们了解了 H 桥以及驱动 DC 电机的各种方法,并学习了控制步进电机的程序。如果您需要更多信息,我建议您查看特定电机驱动器的制造商数据表,包括 L293D 和 SN754410NE。机械臂、移动机器人,甚至无人机的设计和控制现在都可以用你在本章学到的知识来尝试。

十、Python MCU 接口

恭喜你!如果到目前为止,您已经掌握了使用微控制器和 Python 所需的基础知识。我们已经走了很长的路,但我们还没有完成。在本章中,我们将了解如何使用运行 CircuitPython 的微控制器与一些常见传感器接口。传感器接口的主题可以涵盖整个体积。然而,当您继续构建自己的项目时,可能会用到一些传感器;在这一章中,我们将介绍您可能希望在项目中使用的传感器。

RGB LED 指示灯

如果你认为发光二极管很棒,那么我有一个传感器会让你大吃一惊。有时,你不能决定在你的项目中使用的 LED 灯的颜色。在这种情况下,我们需要使用一个封装中包含三个 LED 的 LED。这是红色、绿色和蓝色或 RGB LED。RGB LED 如图 10-1 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 10-1

RGB LED 认证:Adafruit.com ada fruit

正如我们在显示器一章中所学的,使用红色、绿色和蓝色,我们将能够产生任何颜色的光。RGB LEDs 在同一个封装中有一个红色、一个绿色和一个蓝色 LED。使用 RGB LEDs,我们将能够生产出我们能想到的几乎任何颜色的 LED。这使得它们非常适用于指示器之类的东西。我们可以用一个 LED 来改变它的颜色,而不是用多个 LED 来传递信息。

RGB LED 具有四个引脚,并且 LED 可以是公共阳极或公共阴极。如果我们观察 RGB LED,我们会发现一个引脚比其他引脚长。这个长针可以连接到我们电源的阳极或阴极。

RGB LED 和 MCU 原理图

我们如图 10-2 所示连接电路。我们的 RGB LED 连接到引脚 D10、D11 和 D12。该原理图假设使用公共阴极 RGB LED。如果你使用一个普通的阳极 LED,最长的管脚将连接到 VCC 而不是地。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 10-2

RGB LED 和 MCU 原理图

请注意我们是如何使用三个电阻的,因为封装中的每个 LED 仍必须被视为一个单独的器件。

RGB LED 电路连接提示

以下是连接电路的推荐步骤:

  1. 根据您的版本,将 RGB LED 的长公共引脚连接到 VCC 或地。

  2. 使用 1k 电阻将其余三个短引脚分别连接到引脚 D10、D11 和 D12。

当你完成连接你的电路时,它看起来应该如图 10-3 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 10-3

试验板上带 MCU 的 RGB LED

我们需要的图书馆

这些是我们需要添加到 lib 文件夹中的库:

  • adafruit_rgbled 函数

  • 简单的

RGB LED 与 CircuitPython 程序

然后我们可以在 Mu 编辑器中打开我们的 code.py 文件,这样它就类似于清单 10-1 。

# import board library
import board

# import time library
import time

# import library for RGB led
(1) import adafruit_rgbled

# setup pin constants
(2) RED_LED = board.D10
GREEN_LED = board.D11
BLUE_LED = board.D12

# create a RGB LED object

# invert pwm = false if common cathode
#              true common anode
(3) rgbLed = adafruit_rgbled.RGBLED(RED_LED, GREEN_LED, BLUE_LED, invert_pwm=False)

(4) while True:
    # turn on red
    rgbLed.color = (128, 0, 0)
    time.sleep(1)

    # turn on green
    rgbLed.color = (0, 128, 0)
    time.sleep(1)

    # turn on blue
    rgbLed.color = (0, 0, 128)
    time.sleep(1)

    # mix 1
    rgbLed.color = (100, 0, 204)
    time.sleep(1)

    # mix 2
    rgbLed.color = (90, 20, 0)
    time.sleep(1)

Listing 10-1MCU with RGB LED Program

我们导入模块来设置我们的开发板以供使用。在(1)中,我们导入了 adafruit_rgbled 库,这将允许我们控制 led。完成这些后,在(2)中,我们为代表每个 LED 的引脚设置常数。完成后,我们在(3)处创建一个 RGB LED 对象。在我们的主循环(4)中,这个对象用于我们一次打开一个单独的 LED,红色,然后绿色,然后蓝色各一秒钟。然后我们练习混合颜色值。

HC-SR04 战斗机

有时候,无论什么原因,你都需要测量距离。例如,如果你正在建造一个基于微控制器的移动机器人,你将需要能够控制机器人行驶的方向。使用一个传感器,测量机器人与我们使用的物体之间的距离,我们可以创造一个半智能机器人。我们有两种方法可以测量距离,那就是利用光和声音。使用光是困难的,因为它会受到环境中环境光的干扰。有弹性光传感器,如基于激光雷达的传感器;然而,将它们集成到项目中是很昂贵的。一个不错的低成本解决方案是使用声音。最常用的声音传感器是 HC-SR04 传感器,它使用超声波来测量距离。该传感器如图 10-4 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 10-4

HC-SR04 超声波传感器:adafruit.com ada fruit

该传感器有四个引脚,分别是 VCC、GND、触发引脚和回波引脚。该装置有两个超声波传感器。其中一个传感器发射超声波脉冲,另一个设备监听发射的脉冲。该传感器可以测量从 2 厘米到 400 厘米的距离。

传感器的工作原理是,触发针(Trig)用于从其中一个传感器传输脉冲。当接收到反射信号时,echo 引脚变为高电平。根据设备在接收(回波)引脚上检测到信号所需的时间长度,我们可以确定被测物体的距离。

HC-SR04,带 MCU 原理图

电路连接如图 10-5 所示。由于 HC-SR04 是一个 5 伏器件,我们需要使用一个逻辑电平转换器将其与我们的 CircuitPython MCU 接口。trigger 引脚通过逻辑电平转换器连接到 MCU 的 D5 引脚,echo 引脚通过逻辑电平转换器连接到 MCU 的 D6 引脚。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 10-5

带 MCU 原理图的温度传感器

HC-SR04 电路连接提示

以下是连接电路的推荐步骤:

  1. 将 HC-SR04 传感器的 VCC 引脚连接到正极供电轨。

  2. 将传感器的接地引脚接地。

  3. 将 HC-SR04 的 Trig 引脚连接到 HV4,将 echo 引脚连接到 HV3。

  4. 将逻辑电平转换器的 LV4 引脚连接到微控制器的 D5 引脚,LV3 引脚连接到微控制器的 D6 引脚。

  5. 将逻辑电平转换器的 GND 引脚连接到试验板的负供电轨。

当你完成电路连接时,它应该看起来如图 10-6 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 10-6

试验板上带 MCU 的 HC-SR04

我们需要的图书馆

这些是我们需要添加到 lib 文件夹中的库:

  • 开源软件国际化之简体中文组

HC-SR04 带 CircuitPython 程序

在管理单元编辑器中编辑您的 code.py,使其类似于清单 10-2 。这个例子由 Adafruit Industries 提供的用于读取传感器的例子修改而来。

# import time library
import time

# import board library
import board

# import HCSR04 sensor
(1) import adafruit_hcsr04

# create instance of our HCSR04 object
(2) sonar = adafruit_hcsr04.HCSR04(trigger_pin=board.D5, echo_pin=board.D6)

# super loop
(3) while True:
    # try to get the distance
    try:
        print((sonar.distance,))

    # else tell us it failed
    except RuntimeError:
        print("Fail!")

    # wait 0.1s
    time.sleep(0.1)

Listing 10-2MCU with Temperature Sensor Program

在程序中,我们执行通常的导入,在(1)中,我们导入了允许我们使用 HC-SR04 传感器的库。在(2)中,我们创建了一个可以操作的 HCSR04 对象的实例,位于 D5 和 D6 引脚上。在主超级循环中的(3)处,我们有一个 try catch 语句,用于尝试读取传感器,如果失败,我们会告诉用户发生了错误。我们在图 10-7 中看到串行控制台的输出。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 10-7

HC-SR04 传感器输出

当我们把手靠近传感器时,距离读数变小,当我们把手拿开时,我们观察到距离读数变大。

压电扬声器

如果你曾经使用过微波炉或自动取款机,你一定会听到这些设备发出的电子哔哔声。有时,当我们需要提醒用户一些事情时,我们不仅可以使用 led 发出的光,还可以使用声音。产生声音的经典方法是使用压电扬声器,也称为压电蜂鸣器或压电扬声器。一个这样的扬声器如图 10-8 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 10-8

压电扬声器鸣谢:adafruit.com Adafruit

压电扬声器由一个微小的金属板组成,我们称之为压电元件。当我们向压电元件施加方波时,它就会振动,并产生可听见的声音。我们可以利用这种效应创建一个程序,允许我们向设备发送不同频率的波,以创建不同的声音。

带 MCU 的压电原理图

我们如图 10-9 所示连接电路。压电扬声器有两个引脚。我们将压电片上的正极引脚连接到 D5 引脚,另一引脚接地。压电的正极引脚通常在蜂鸣器上写有一个小的“+”号。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 10-9

带 MCU 原理图的温度传感器

压电电路连接提示

以下是连接电路的推荐步骤:

  1. 将压电扬声器的接地引脚接地。

  2. 将压电的正极引脚连接到引脚 D5。

当你完成电路连接时,它应该看起来如图 10-10 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 10-10

试验板上集成 MCU 的压电传感器

我们需要的图书馆

这些是我们需要添加到 lib 文件夹中的库:

  • 简单的

Piezo 和 CircuitPython 程序

在管理单元编辑器中编辑您的 code.py,使其类似于清单 10-3 。这个例子是由 Adafruit Industries 提供的用于创建声音的例子修改而来的。

# import board
import board

# import simple io library
import simpleio

# Define pin connected to piezo buzzer.
(1) PIEZO_PIN = board.D5

# Define a list of tones/music notes to play.
(2) TONE_FREQ = [ 262,  # C4
              294,  # D4
              330,  # E4
              349,  # F4
              392,  # G4
              440,  # A4
              494 ] # B4

# super loop

(3) while True:
    # Play tones going from start to end of list.
    for i in range(len(TONE_FREQ)):
        simpleio.tone(PIEZO_PIN, TONE_FREQ[i], duration=0.5)

    # Then play tones going from end to start of list.
    for i in range(len(TONE_FREQ)-1, -1, -1):
        simpleio.tone(PIEZO_PIN, TONE_FREQ[i], duration=0.5)

Listing 10-3MCU with Piezo Program

在程序中,我们做我们通常的导入;然后在(1)我们设置压电钉 D5。在(2)中,我们定义了要演奏的音符列表。使用程序中的注释列表,在(3)处的超级循环中,我们对它们进行迭代。一旦程序正常工作,你会听到从你的扬声器传来的音符。

DHT11

在模拟接口部分,我们讨论了温度传感器的使用。但是,有一种流行的二合一传感器可以测量温度和湿度,这就是 DHT11 传感器。DHT11 如图 10-11 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 10-11

DHT11 温度和湿度传感器:adafruit.com 阿达弗洛

该器件有四个引脚。一个引脚是 VCC,另一个引脚是接地引脚。还有一个输出引脚,用于从传感器读取数据。

带 MCU 原理图的 DHT11

我们如图 10-12 所示连接电路。我们将 DHT11 的输出连接到输入引脚 D10。为了正常工作,我们需要一个连接到 DHT11 输出引脚的上拉电阻。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 10-12

带 MCU 原理图的 DHT11 传感器

DHT11 传感器电路连接提示

以下是连接电路的推荐步骤:

  1. 将 DHT11 的 VCC 引脚连接到正电源轨。

  2. 将传感器的接地引脚接地。

  3. 将 1k 电阻从输出引脚连接到 VCC 引脚。

  4. 在 DHT11 传感器的输出引脚和运行 CircuitPython 的 MCU 上的 D10 引脚之间连接一根跳线。

当你完成电路连接时,它应该看起来如图 10-13 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 10-13

试验板上集成 MCU 的温度传感器

我们需要的图书馆

这些是我们需要添加到 lib 文件夹中的库:

  • adafruit_dht 函数

带 CircuitPython 程序的 DHT11 传感器

在管理单元编辑器中编辑您的 code.py,使其类似于清单 10-4 。这个例子由 Adafruit Industries 提供的用于读取传感器的例子修改而来。

# import board
import board

# import time
import time

# import busio
import busio

# import library for working with sensor
(1) import adafruit_dht

# connect the DHT11 to pin10
(2) dht = adafruit_dht.DHT11(board.D10)

(3) while True:
    try:
        # read the temperature and humidity
        temperature = dht.temperature
        humidity = dht.humidity

        # print the read temepratue and humidity
        print("Temp: {:.1f} *C \t Humidity: {}%".format(temperature, humidity))

    except RuntimeError as e:
        # if dosent work print error
        print("Reading from DHT failure: ", e.args)

    # print every second

    time.sleep(1)

Listing 10-4MCU with DHT11 Sensor Program

在程序中,我们做我们通常的导入;然后,在(1)中,我们导入库以使用 DHT11 传感器。在(2)中,我们在引脚 10 上创建了 DHT11 传感器的实例。在(3)的主循环中,我们读取传感器的温度和湿度,并将其打印到控制台。输出如图 10-14 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 10-14

DHT11 传感器输出

传感器将以稳定的速率输出数据,并且由于我们的 try catch,如果程序失败,它将继续运行并将数据输出到控制台。

请注意,尽管本例使用了 DHT11 传感器,但也可以使用 DHT22 而不会有任何问题,因为“adafruit_dht”库支持这两种器件。要在创建传感器实例时使用该传感器,只需将 DHT11 改为 DHT22。

结论

在本章中,我们讨论了使用基于 MicroPython 的微控制器与一些常见传感器进行接口。我们考虑使用 RGB LEDs、超声波传感器、声音、温度和湿度传感器。本章学到的知识将允许你构建一些非常有趣的嵌入式系统。

恭喜你!你已经读完了整本书。如果您已经做到了这一步,那么您就已经为在微控制器上使用 CircuitPython 打下了坚实的基础。不要停在那里!你仍然可以做很多事情来增加你的知识。检查 Adafruit 库包,并从那里运行代码示例。继续修修补补!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值