一、做好准备
您已经决定使用 Python 学习微控制器。尽管直接投入到开发很酷的东西是很好的,但是有几个步骤你必须首先做好准备。如果你有软件开发的经验,你可能习惯于打开一个编辑器,然后马上输入代码。然而,微控制器开发比这要复杂一些。毕竟,你将为一台单独的计算机编写程序,所以有更多的东西需要设置。微控制器开发环境不仅包括软件,还包括一个完整的硬件生态系统,必须理解这个生态系统才能释放这些器件的所有魅力。
在这一章中,我们将着眼于建立一个开发环境。本章结束时,您将准备好选择微控制器板,并拥有开始使用 CircuitPython 进行开发所需的软件工具和硬件设备。我们开始吧!
微控制器简介
在我们的现代世界,计算机无处不在。它们变得更小,更容易使用,并且更加融入我们的日常生活。几年前,要连接互联网和查看电子邮件,你必须坐在一个大型桌面设备前。今天,你可以用一台手掌大小的电脑做同样的事情,把世界上所有的信息都放在你的指尖。
科技进步给我们带来了智能手机和平板电脑,也让数十亿人拥有了电脑。然而,这些手持设备并不是唯一存在的计算机。如果我告诉你,与静静地围绕在我们周围的另一种计算设备相比,数十亿部正在使用的智能手机相形见绌,会怎么样?事实上,现在在你的房间里可能有半打或更多这样的设备。这些微型计算机被称为微控制器,或MCU,它们在我们身边随处可见。
微控制器是小型、智能、可编程的计算机,我们用它来执行重复性的任务,或者需要某种程度的智能控制而不需要人工干预的任务。微控制器设备的计算能力只是智能手机或平板电脑等设备的一小部分,但它们有很多很多用途。你周围的任何设备,只要一按下按钮就会做出反应,显示信息,或者在出错时发出声音,都很可能是由微控制器控制的。从计算器到电视到微波炉和洗碗机,你能想到的几乎每一个家用设备都包含一个微控制器。
如果你熟悉计算机,你肯定听说过中央处理器(CPU)或微处理器:计算机背后的大脑。微处理器让计算机执行许多功能。某一天你可能用电脑来打文件,下一天你可能用电脑来播放你最喜欢的电影,等等。微处理器有许多支持电路,包括存储器和输入输出芯片,使它能够执行所有这些任务。这些通常是容纳微处理器的主板的一部分。
微控制器将微处理器工作所需的所有支持芯片放在一个芯片上。这就是为什么微控制器被称为单片计算机。微控制器仍然像微处理器一样工作,但它被设计成只执行一项任务,并且尽可能高效地执行。因此,微控制器芯片包含完成任务所需的最小处理能力。
固件、内存和时钟速度
像通用计算机一样,微控制器通过运行程序来工作。因为微控制器执行有限的功能,所以为它们编写的程序不会经常改变。因此,为微控制器编写的程序被称为固件。该固件通常存储为二进制文件(具有. bin 文件扩展名)或十六进制文件,后者是文本表示的二进制文件(具有. hex 文件扩展名);该文件包含写入 flash 的存储器内容,因此可以在微控制器上运行。一种叫做编程器的特殊设备将这个二进制文件加载到微控制器的存储器中。
存储器由两部分组成:主存储器(有时称为程序存储器)和随机存取存储器(ram,有时称为数据存储器)。主存储器是非易失性的,而 RAM 是易失性的。这意味着当微控制器断电时,RAM 中包含的信息将会消失,而主存储器中的信息将会保留。因此,主存储器也被称为只读存储器(ROM)。它的内容被设计成主要被读取,而 RAM 被设计成在运行时由用户改变其内容。
在通用计算机系统中,通常一个程序在执行前被装入 ram。微控制器系统有点不同。在微控制器中,固件直接从 rom 中执行,RAM 用于存储帮助运行固件的临时信息(这些通常是运行时变量,可以包含堆栈或堆,以及特殊的内存结构)。
有些微控制器器件可以在出厂时就将 ROM 内容编程到器件中,一旦装入器件,最终用户就无法更改。这些被称为一次性可编程(OTP)设备,它们更便宜,更容易制造。不过,通常 ROM 是由闪存制成的,这意味着它可以在制造后进行编程和更改。
闪存可让您对设备进行数千次编程,这对于学习和产品原型制作非常有用。如果固件程序中出现错误,flash 还允许您通过更新来修复错误,这一过程称为修补。更新通常采取空中下载(OTA)更新的形式,通过无线连接改变 ROM 的内容。OTA 更新对于物联网(IoT)设备来说很常见。或者,您可以使用电缆将设备连接到电脑来更新固件。
微控制器根据运行设备的时钟速度执行其固件程序中的指令。速度是以赫兹或每秒钟的周期数来衡量的。时钟速度越快,设备执行指令就越快。微控制器的时钟速度通常在 1 MHz 左右(尽管对于极低成本、低功耗的应用,时钟速度可以低至 32.768 kHz,对于高速系统,时钟速度最高可达 1 GHz。)
8 位与 32 位
我们将在本书中使用的设备非常强大。它们由 32 位微处理器内核组成,这意味着微控制器可以处理的数据位数由 32 个寄存器组成(寄存器是微控制器内数据的小存储单元;我们将在下一章详细讨论这一点)。然而,在很长一段时间里,8 位设备统治着微控制器市场。
历史上,32 位设备价格昂贵且难以编程。由于技术的进步,32 位设备的成本已经下降到可以与 8 位设备竞争的程度,但对成本极其敏感的应用除外。与此同时,由于现在有无数的工具可以使用,32 位设备已经变得非常容易编程和控制。虽然 8 位微控制器仍然存在并具有相关性,但它们的许多市场份额正在被 32 位设备取代。
对于初学者来说,32 位设备是一个特别有用的学习工具,因为它们包含更多的内存和更大的内存寻址能力。这允许更高层次的抽象,意味着在没有完全理解其内部工作原理的情况下,通常更容易对 32 位设备进行编程。相比之下,由于 8 位设备的处理能力和内存较小,因此您需要更深入地了解设备的内部,以便更好地管理内存资源和编写有效的程序。
微控制器编程语言
在本节中,我们将了解一些可用于微控制器编程的语言。虽然有几个选项可供选择,但微控制器对构成现代软件开发环境的拥挤不堪的编程语言大多持抵制态度。从历史上看,微控制器领域一直由 C 编程语言主导。这是因为微控制器传统上只有几个字节的内存,并且以几十兆赫的时钟速度运行,而 C 特别适合在内存受限的系统上工作。然而现在,你可以找到拥有运行频率高达千兆赫的多核微控制器设备,并拥有数兆字节的内存,为其他语言的使用开辟了空间。
汇编语言
曾经有一段时间,微控制器完全是用汇编语言编程的。今天,汇编语言是为内存有限的设备和程序员需要充分发挥微控制器性能的情况而保留的。汇编语言在这些情况下很有用,因为许多汇编指令在微控制器上直接翻译成机器指令。这意味着指令执行的开销更少,使得用汇编语言编写的代码段更快。虽然汇编语言速度很快,但在设备上执行一个简单的任务需要大量指令。汇编语言的另一个限制因素是,对于你正在编程的每一个设备,你必须学习那个设备的汇编语言。鉴于这些限制,除了小众场合,这种语言的受欢迎程度已经下降。
C
长期以来,c 语言一直是嵌入式领域的首选语言。它可以在内存受限的设备上运行,比如微控制器。C 给了我们有效控制底层硬件的能力——一条 C 指令翻译成几条汇编语言指令——而且它可以和大多数应用程序的汇编语言速度相匹配。由于 C 语言已经使用了很长时间,所以许多代码都经过了实战测试,并被证明适用于它们的应用程序。c 用户可以访问包含有用信息和代码片段的大型代码库。然而,这种语言需要对硬件有很好的理解,初学者很难掌握。
C++
随着时间的推移,嵌入式设备变得越来越强大,一些微控制器制造商和软件供应商开始为他们的设备添加 C++支持。C++正在专业嵌入式领域慢慢获得牵引力。然而,在业余爱好者领域,C++在 Arduino 平台上得到了广泛的应用。然而,C++是一门庞大而难学的语言。许多使 C++在通用计算应用中比 C 更有效的特性,有时不能在资源受限的微控制器设备上实现。这是因为虽然 C++在大多数应用程序中的性能可以与 C 相媲美,但 C++倾向于使用更多的内存,这是一种宝贵的资源,通常在微控制器设备上并不丰富。因此,C++是为高端设备保留的。
基本的
在 21 世纪初,如果一个初学者开始接触微控制器,并且不喜欢汇编,BASIC 是可以使用的编程语言。BASIC 代表初学者的通用符号指令代码,是一种易于使用的编程语言。微控制器芯片上通常有一个基本的解释程序来运行指令。
BASIC 最终不受欢迎,因为运行它的主板相对于它们的能力来说花费了很多钱。此外,运行 BASIC 解释程序会降低芯片速度,并在已经受限的 8 位设备上占用太多资源。此外,最流行的基本设备的工具和软件都是闭源的,所以人们不能制造他们自己的基本设备。当像 Arduino 这样的开源替代品出现时,像 BASIC Stamp 这样的设备就不再受欢迎了。
锈
Rust 编程语言相对于 C(几乎有半个世纪的历史)来说是新的,它被设计用来颠覆 C 和 C++对系统编程的控制,包括嵌入式编程。随着微控制器变得更加强大,并发性(一次执行多个进程的能力)等因素开始变得重要,Rust 相对于 C 的优势开始显现。Rust 更适合并发性,因为它可以处理数据竞争,即两个设备试图同时访问内存中的同一位置。
虽然 Rust 可能会取代 C,但业界没有理由在短期内采用它。嵌入式软件被称为固件是有原因的:它不会经常改变,而且许多已经写好的代码没有理由改变成一种新的语言。c 工作并且有许多已建立的工具链和设备,并且有许多熟练的开发人员熟悉这种语言。然而,已经有工具允许 Rust 用于微控制器,随着时间的推移,Rust 可能会在嵌入式市场获得一些份额。
计算机编程语言
Python 相对来说是嵌入式领域的新手,它可能会成为该领域的主要参与者。Python 比 C 语言简单得多,是当今最流行的编程语言之一。虽然对于初学者来说,BASIC 比 C 容易,但是 Python 比 BASIC 有几个优点,这使它更适合作为嵌入式语言使用。值得注意的是,虽然流行的基本微控制器是闭源的,但 Python 是开源的,如果您愿意,可以在您的定制设备上运行 Python。Python 文件也可以被编译成更小的文件,这样你就可以创建紧凑的、节省内存的程序。
很多人说 Python 这样的解释语言不适合微控制器的局限性。这可能曾经是真的,但随着今天更强大的设备,微控制器完全有可能运行解释语言,而不会像旧的基本设备那样遇到速度限制。对于非常省时的计算,也称为实时计算,解释语言仍然不适合。然而,Python 应该可以满足大多数微控制器项目的速度要求。
虽然 Python 在微控制器上运行时没有 C 语言快或高效,但它的易用性使它值得一试,尤其是如果你现在开始接触微控制器的话。此外,您可以用 C 扩展 Python 代码,这意味着您可以利用现有的 C 代码库,这些代码库经过了几十年的考验和改进。
存在于通用计算机上的 Python 解释器不能直接在具有相同可用特征的微控制器上实现。这是因为标准 Python 解释器是一个大型程序,它依赖于操作系统提供的功能,尤其是内存和硬件接口功能,而这在微控制器设备上是不存在的。语言解释器的两种修改形式,MicroPython 和 CircuitPython,在标准 Python 解释器和嵌入式空间之间架起了一座桥梁。其中,MicroPython 更面向专业开发人员,领先于 CircuitPython。由 Adafruit 开发的 CircuitPython 使用起来更简单,使其成为初学者的优秀学习工具以及专业人士的平台。CircuitPython 用户友好的主要特点是,在微控制器上运行程序之前,您不需要编译程序。一旦你保存了一个程序,它就会运行并执行。
CircuitPython 有望在更多资源受限的设备上使用,并有望在可预见的未来保持良好的支持。由于这些原因,我们将在整本书中使用它。
选择开发板
为了完成本书中的项目,您需要一个带有微控制器的开发板,可以运行 CircuitPython。开发板包含一个微控制器以及为开发板供电并使其启动和运行所需的连接。开发板允许您对微控制器进行使用、编程和原型制作,而无需担心硬件设置。
在撰写本文时,CircuitPython 支持超过 140 种电路板,并且这个列表还在不断增长。你可以在 CircuitPython 网站这里查看名单: https://circuitpython.org/downloads
。这些兼容的板中的任何一个都可以和这本书一起工作。您也可以选择创建自己的支持 CircuitPython 的定制设备,这个过程我将在本章末尾讨论。然而,对于初学者来说,使用预制的 CircuitPython 兼容板总是更好的选择。它将确保硬件正常工作,并允许您更快地开始为您的设备编写软件。
在本节中,我们将了解一些可以运行 CircuitPython 的预配置设备。尽管许多公司提供能够运行 CircuitPython 的微控制器板,但 Adafruit 设备拥有最好的支持,因为它们首创了 CircuitPython 语言,并拥有一个围绕 circuit python 构建的完整生态系统及其开发环境。我们将看看他们提供的一些电路板,以及其他制造商提供的可以与 CircuitPython 一起使用的流行电路板。这个列表并不详尽,但是这里介绍的板将与本书中讨论的例子兼容。
阿达水果 M0 地铁快线
我们要看的第一块广告牌是 Adafruit Metro M0 快车,如图 1-1 所示。该板由 SAMD21G18A 微控制器供电,是配合本书示例使用的理想选择。该板还具有 Arduino 外形;这意味着它可以与现有的 Arduino 盾牌一起使用。因此,强大的 Arduino 生态系统可以很容易地用 Python 中的 Arduino shields 进行原型设计。SAMD21G18A 代表运行 CircuitPython 所需的“理想最小值”。它的特性允许它运行解释器没有任何停顿。SAMD21G18A 具有 48 MHz 时钟、256KB 闪存和 32KB RAM。(相比之下,由 ATmega328 微控制器供电的电路板,如 Arduino Uno,其闪存和 RAM 分别减少了 8 倍和 16 倍。)M0 地铁快车还有 2MB 的闪存,可以用来存储程序和其他文件。与 SAMD21G18A 相比,您可以使用更少的内存或更少的处理能力来运行 CircuitPython 解释器,但体验可能并不完美。
SAMD21G18A 微控制器是首批支持 CircuitPython 的器件之一,围绕该器件构建的电路板通常是首批获得最新版本解释器的器件。特别是,M0 地铁快线是第一个使用 CircuitPython 设计的 Adafruit 地铁板。它被认为是运行 CircuitPython 的标准板,它将能够体面地运行本书中的程序。
图 1-1
阿达果地铁 M0 快车[信用:阿达果,adafruit.com]
阿达果羽毛 M0 快递
如果你想要一个更简约的开发方法,你可以从 Adafruit 获得羽毛 M0 快车,如图 1-2 所示。因为它也是围绕 SAMD21G18A 处理器构建的,所以该板具有 Metro M0 Express 的所有功能:相同的 48 MHz 时钟、256KB 闪存和 32KB RAM。它还拥有相同的板载 2MB 闪存。然而,它比 Metro 更紧凑,并且少了 5 个 I/O 引脚——20 个而不是 25 个。
这种板的一个很酷的特点是前面的小原型区域,这是 M0 地铁快车所没有的。当你已经熟悉了 M0 地铁快线,并且你想要一个板嵌入到你自己的项目中,那么你可以使用这个更小、更便宜的板。还有 Adafruit 的 QT Py 板,如果您的项目需要更小的板,它甚至比 M0 Express 板更紧凑。
图 1-2
阿达果羽毛 M0 快递[信用:阿达果,adafruit.com]
阿达水果 M4 地铁快线
如果你需要比 M0 地铁快线更多的魅力,那么你可以搭乘 M4 地铁快线,如图 1-3 所示。该板由 SAMD51J19 微控制器供电,其性能优于之前讨论的基于 SAMD21G18A 的器件。在 120 MHz 时,它的运行速度是基于 SAMD21G18A 的主板的两倍多,它具有 512KB 的闪存、192KB 的 RAM 和额外的 2MB 板载闪存。
这些增强功能为数字信号处理(DSP)和浮点处理应用提供了更好的支持。如果您希望将您的主板用于音频处理等应用,或者如果您正在寻求更好的安全功能或总体性能改进,那么这是一款不错的主板。然而,性能的提高是有代价的。M4 地铁快线的处理器将比之前讨论的主板消耗更多的能量。不过,根据您的应用,这可能不是一个很大的因素,因为电路板仍然是高能效的。
图 1-3
阿达果 M4 地铁快车[信用:阿达果,adafruit.com]
Adafruit Grand Central M4 高速公路
我们可以用来和这本书一起工作的下一块板是阿达果大中央 M4 快车,如图 1-4 所示。与 Metro M0 相比,该板非常强大,因为它具有 ATSAMD51P20 微控制器。它的运行速度快了 2.5 倍,并且该板的 RAM 大小是 Metro M0 的 8 倍。事实上,大中央车站的内存量相当于整个 M0 地铁的闪存量(让它沉下去一点)。Grand Central 还具有 1MB 的闪存,通过 QSPI 芯片的 8MB 板载闪存,以及内置的 SD 卡插座。该板的 I/O 几乎是 M0 地铁上的三倍。当您有一个大型应用程序,并且在一个更基本的主板上耗尽了空间(内存或 I/O)时,这个主板是一个很好的迁移途径。
Grand Central 绝对可以用来运行本书中的所有示例,当您准备好开始自己的更复杂的 CircuitPython 项目时,像这样使用 ATSAMD51 微控制器的电路板是理想的选择。由于其充足的资源,机器学习和视频游戏编程等应用可以通过该板完成。
图 1-4
阿达果大中央 M4 快递[信用:阿达果,adafruit.com]
Arduino Zero
Arduino Uno 无疑是有史以来最受欢迎的微控制器板之一,至今仍被广泛使用。它向我们展示了 8 位微控制器的强大功能。然而,世界在变,32 位微控制器已经接管了很多曾经由 8 位设备控制的细分市场。Arduino Uno 平台有许多限制,因此 Arduino Zero 的创建是为了将 32 位开发带入 Arduino 世界。图 1-5 中的 Arduino Zero 向 Arduino 平台的用户提供 32 位嵌入式计算迈出了一步。
Arduino Zero 由 SAMD21G18A 微控制器供电,该微控制器与 Metro M0 和 Feather M0 电路板中使用的微控制器相同。虽然它缺少 Adafruit Metro 主板上的板载闪存,但 Arduino 仍然可以运行相当大的程序,并将与本书中提供的所有示例一起工作。与前面讨论的其他开发板不同,虽然开源(是的,所有硬件原理图和源代码都是可用的)特定于一个供应商,但 Arduino Zero 有许多开发板的克隆版本,您可以在其上运行 CircuitPython。这些复制品通常比制造商提供的官方主板便宜。
图 1-5
Arduino Zero[鸣谢:adafruit.com 阿达果]
STM32F746ZG 核子
基于 STM32 的电路板也可以运行本书中讨论的例子。例如,STM32F746ZG Nucleo 已经过本书中所有示例的测试,运行良好。这个板子,如图 1-6 所示,绝对是个庞然大物。它采用 STM32F746ZG 微控制器,其处理器的性能优于前面讨论的任何一种板。该板运行在惊人的 216 MHz,有 320KB 的内存和 1MB 的闪存。
STM32F746ZG 主要面向之前有微控制器经验的人。虽然它在 CircuitPython 上运行良好,但它并不像该系列中的其他主板那样开箱即用。尽管如此,该板的速度和性能使其值得用于通过 CircuitPython 执行计算密集型任务,如解码 MP3 文件或使用以太网,或者当您需要大量 I/O 时。
如果你想要更多的果汁,你可以考虑购买 STM32H743 Nucleo,它有一个运行频率高达 400 MHz 的微控制器。凭借 1MB 的内存和 2MB 的闪存,它可以轻松地与 CircuitPython 配合使用。这个板也不像其他一些板那样对初学者友好,而且它带有一些陷阱。例如,如果没有自己的支持,微控制器上的一些外来外设可能无法在 CircuitPython 中使用。其中包括 HDMI、DFSDM 和运算放大器等外设。此外,由于该板的工作频率为 400 MHz,功耗很大,因此不适合电池供电的应用。新的微控制器用户最好先从一块 Metro 板开始,然后再使用这块板。
图 1-6
STM32F746ZG Nucleo
设备比较
表 1-1 比较了可用于跟随本书的电路板的特性。选择合适的微控制器可能是一项艰巨的任务。在我看来,最好的微控制器是 Adafruit Grand Central M4,因为它功能强大,并得到 CircuitPython 的良好支持。然而,前面讨论的任何板都可以满足我们的目的。
表 1-1
设备比较
|设备
|
时钟速度
|
随机存取存储
|
闪光
|
板载 SPI
闪存
|
| — | — | — | — | — |
| 米-0 | 48 兆赫 | 32KB | 256 千字节 | 2MB |
| 羽毛 M0 | 48 兆赫 | 32KB | 256KB | 2MB |
| M4 公尺 | 120 兆赫 | 192KB | 512KB | 2MB |
| 大 M4 中心 | 120 兆赫 | 265KB | 1MB | 8MB |
| Arduino 零号 | 48 兆赫 | 32KB | 256KB | 没有人 |
| STM32F746ZG 核子 | 216 兆赫 | 320KB | 1MB | 没有人 |
组件列表
微控制器是我们电路的核心。然而,本书中的项目也需要支持组件和设备。表 1-2 列出了本书中需要用到的所有设备和组件。不要担心:每一部分都会在出现时详细讨论。为了方便起见,表中为每个商品提供了两个产品编号,一个来自 Amazon,一个来自另一个供应商。但是,欢迎您货比三家,寻找符合商品描述的其他选项。
表 1-2
组件列表
|项目
|
量
|
小贩
|
产品编号
|
| — | — | — | — |
| 各种电阻器套件 | one | 贾梅克亚马逊 ASIN | Two million two hundred and seventeen thousand five hundred and elevenB07L851T3V |
| 10K 试验板微调电位计 | Two | 阿达果亚马逊 ASIN | Three hundred and fifty-sixB07S69443J |
| 220 μF 电容电解的 | one | 阿达果亚马逊 ASIN | Two thousand one hundred and ninety-twob07 sbw 1 和 17 战斗机 |
| 0.1 μF 陶瓷电容 | one | 阿达果亚马逊 ASIN | Seven hundred and fifty-threeB07RB4W4MR |
| 5 毫米红色指示灯 | one | 阿达果亚马逊 ASIN | Two hundred and ninety-nineB077X95F7C |
| 触摸开关按钮 | one | 阿达果亚马逊 ASIN | One thousand one hundred and nineteenB01GN79QF8 战斗机 |
| 10 lux 50–100 kohm 光敏电阻 | 至少 2 个 | 阿达果亚马逊 ASIN | One hundred and sixty-oneB01N7V536K |
| TMP36 温度传感器 | one | 阿达果亚马逊 ASIN | One hundred and sixty-fiveb11 GH 32 突击步枪 |
| MPU6050 加速度计和陀螺仪 | one | 阿达果亚马逊 ASIN | Three thousand eight hundred and eighty-sixB00LP25V1A |
| 4 通道逻辑电平转换器 | Two | 阿达果亚马逊 ASIN | Seven hundred and fifty-sevenB07LG646VS |
| CP2102 或 CP2104 USB-UART 转换器 | one | 阿达果亚马逊 ASIN | Three thousand three hundred and nineB07D6LLX19 战斗机 |
| 诺基亚 5110 GLCD | one | 阿达果亚马逊 ASIN | Three hundred and thirty-eight-= ytet-伊甸园字幕组=-翻译 |
| 固态盘 1306 | one | 阿达果亚马逊 ASIN | Three hundred and twenty-sixb076pdvfqd |
| 6V 爱好 DC 电机尺寸 130 | one | 阿达果亚马逊 ASIN | Seven hundred and elevenB07BHHP2BT |
| 微型伺服电机 MG90S 或 SG92R | one | 阿达果亚马逊 ASIN | One hundred and sixty-nineB07NV476P7 |
| 5V 500mA 步进电机 | one | 贾梅克电子公司亚马逊 ASIN | Two hundred and thirty-seven thousand eight hundred and twenty-fiveB00B88DHQ2 突击步枪 |
| ULN2003 达林顿阵列 | one | 数码电子亚马逊 ASIN | 497-2344-5-NDB07YSS1MQL |
| L293D 四路半 H 驱动器 | one | 阿达果亚马逊 ASIN | Eight hundred and sevenb200 nay 2 欧元 |
| DHT11 温度和湿度传感器 | one | 阿达果亚马逊 ASIN | Three hundred and eighty-sixb11 有 8 个 ZMWK |
| 公共阳极 RGB LED(最好是漫射的) | one | 阿达果亚马逊 ASIN | One hundred and fifty-nineb 0194 和 6 兆瓦 2 |
| HC-SR04 超声波传感器 | one | 阿达果亚马逊 ASIN | Three thousand nine hundred and forty-twoB004U8TOE6 |
| 压电扬声器 | one | 阿达果亚马逊 ASIN | One hundred and sixtyb21 gj 5 bs |
| 1N4001 通用硅二极管 | one | 阿达果亚马逊 ASIN | Seven hundred and fifty-fiveB0087YKE02 战斗机 |
| 400 连接点无焊试验板 | Two | 阿达果亚马逊 ASIN | Sixty-fourb7pcie 9 代 |
| 跳线男性对男性 | 至少 20 个 | 阿达果亚马逊 ASIN | Four thousand four hundred and eighty-twob07 会计年度第 7 季第 1 集 |
| 实验室工作台电源 | one | 亚马逊 ASIN 各种制造商 | B081SFKW2R |
关于电源,一个高质量的 1 安培墙上适配器可以作为通过这本书的最低要求。(避免使用便宜的 1A 适配器,因为很多时候它们很吵,会干扰正常的电路操作。)然而,如果你打算长期使用微控制器,一个更坚固的实验室电源是一个不错的投资。这在使用电机时尤其重要,因为电机在启动时会消耗大量功率。标准是使用 30V 5A 的实验台电源,如表 1-2 中建议的电源。这些电源几乎可以处理你扔给它们的任何东西。
管理部门编辑器
现在我们已经讨论了硬件,我们可以设置本书中练习所需的软件。使用 CircuitPython 对设备编程的最佳方式是使用 Mu 编辑器,它与 Windows、macOS 和 Linux 机器兼容。你可以从 https://codewith.mu/
下载编辑器。
下载并安装了 Mu 编辑器后,用 USB 线把你的 CircuitPython 设备连接到你的电脑上(没错,就是这么简单!)并开辟 Mu。你会看到一个类似图 1-7 的窗口。
图 1-7
管理部门编辑器
如果您的 CircuitPython 设备已连接,您将能够读取设备上的文件(该文件是核心 CircuitPython 的一部分)。一旦 CircuitPython 出现在连接的设备上,一旦插入设备,code.py 文件就会出现在设备上。如果您选择的电路板不包含 CircuitPython,您可以在 https://learn.adafruit.com/installing-circuitpython-on-samd21-boards/overview
遵循本指南。点击编辑器上的加载按钮,如图 1-8 所示。
图 1-8
管理部门中的加载按钮
将出现一个对话框。Mu 将默认为文件的目录,但如果没有,则导航到您的 CircuitPython 设备的驱动器(CircuitPython 就像 USB 闪存驱动器一样显示)。你会看到一个 code.py 文件,如图 1-9 所示。。py 扩展名表示这是一个 Python 代码文件。
图 1-9
在管理部门中加载 code.py 文件
打开文件,您会看到它包含一行代码:print('Hello world!')
。当一台普通的计算机运行print
命令时,括号中包含的消息显示在终端中,在这个窗口中,您可以输入命令并查看代码的输出。但是,您的微控制器没有显示功能或内置终端窗口。幸运的是,CircuitPython 设备默认允许通过串行通信协议与主机通信。Mu 有一个称为串行监视器的特性,允许您查看微控制器的程序输出。它还会提醒您程序中可能存在的任何错误。
加载完代码文件后,点击 Mu 中的串口按钮,打开串口连接,如图 1-10 所示。
图 1-10
管理部门中的串行按钮
单击该按钮后,编辑器下方会弹出一个小的终端窗口。这将显示微控制器的串行输出。它看起来应该类似于图 1-11 。
图 1-11
串行输出窗口
点击编辑器中的保存按钮,您将会看到print
语句已经执行并且正在串行输出窗口中运行。您的输出应该类似于图 1-12 。
图 1-12
在串行输出窗口中运行 code.py
串行终端有一些有用的特性。按 ctrl-C(或 Mac 的 command-C)将打开一个读取-评估-打印循环(REPL),由屏幕上的>>>
符号指示,如图 1-13 底部所示。
图 1-13
在串行终端中打开电路 Python REPL
REPL 允许你逐行输入你的 Python 代码,并让你的微控制器执行它。当你在学习如何用一种新语言编程时,有时你只想快速尝试。REPL 是有用的,因为它为您提供了一种方便测试代码片断的方法,而无需编写整个程序。当您打开 REPL 时,微控制器停止执行它正在运行的程序。要离开 REPL 并让程序重新运行,请按 ctrl-D
其他串行通信工具
除了 Mu 的串行终端之外,还有其他工具允许您通过串行连接与开发板通信。如果您使用的是定制板,或者 Mu 在检测板时遇到问题,这些工具将会非常有用。在这些情况下,您有时会看到如图 1-14 所示的错误信息。当 Mu 找不到它认为可以与串行端口一起工作的连接板时,就会出现错误。
图 1-14
Mu 找不到连接的板时的错误消息
如果你用的是 macOS 或者 Unix 电脑,可以看看 CoolTerm 之类的程序,可以在 http://freeware.the-meiers.org/
下载。在 www.mac-usb-serial.com/docs/tutorials/access-serial-port-on-mac-with-coolterm.html#
可以找到不错的 macOS 用户 CoolTerm 教程。请注意,在较新版本的 macOS 上,运行该程序可能会出现问题,因为开发者未经验证。为了解决这个问题,打开终端并输入sudo spctl –master-disable
,这将允许你运行从任何地方下载的应用程序。
如果 Windows 用户在 Mu 内部使用串行终端时遇到问题,他们也可以使用名为 PuTTY 的程序与开发板建立串行连接。可在 www.chiark.greenend.org.uk/~sgtatham/putty/
下载。PuTTY 允许计算机通过 Telnet 和 SSH 等协议相互通信。PuTTY 还可以与连接到计算机 COM 端口的虚拟设备通信。虽然我们使用 USB 电缆将 CircuitPython 设备连接到计算机,但该设备显示为计算机的串行端口。当 USB 设备被模拟为串行 COM 端口时,我们称之为虚拟 COM 端口(VCP),PuTTY 等程序允许我们从这个 VCP 读取信息,就像它是一个实际的串行端口一样。
下载并安装 PuTTY 后,在 Windows 计算机上,转到控制面板并打开设备管理器。在“端口”下查找您的设备,如图 1-15 所示。记下与您的主板相关的端口号。
图 1-15
COM 端口设备
现在,您可以使用 PuTTY 来打开与您的板的串行通信。为此,当您打开 PuTTY 时,使用单选按钮将连接类型设置为 Serial 。在串行线框中输入端口(在本例中为 COM9 ,将速度设置为 115200 ,然后按打开。如图 1-16 所示。
图 1-16
用 PuTTY 打开串行通信
PuTTY 允许您与微控制器交互,就像您从 Mu 中的串行终端与它交互一样。一旦在 PuTTY 和微控制器之间建立了串行连接,您将看到如图 1-17 所示的输出。PuTTY 还允许您访问 REPL。
图 1-17
PuTTY 中的成功连接
本节中概述的选项都是在 Mu 无法与您的主板建立串行连接时的替代选项。但是,如果您可以从 Mu 内部连接您的板,我建议您这样做,因为这是一种更方便的方法。
建立自己的董事会
Note
这一部分是为已经有使用微控制器经验的读者准备的,比如 Arduino 平台,但是如果你刚刚得到一个 Arduino Zero 并且想继续学习,那么你可以这样做!
开源的一个很好的特点是,你可以构建自己的设备副本,而不用担心任何后果。例如,CircuitPython 项目源于 MicroPython 项目,并拥有非常宽松的 MIT 许可证。这个许可意味着你可以用 CircuitPython 做任何你想做的事情,包括编译源代码和自己运行。也可以自己搭建硬件开发板,用 CircuitPython 编程。
虽然不建议刚接触微控制器的人创建定制板,但这一选项为经验丰富的用户提供了许多好处。建立自己的董事会给你很大的灵活性,尤其是如果你想开发自己的商业产品。制作自己的设备也比购买预制电路板便宜得多。例如,如果你正在设计一个成品,你不会将整个 Arduino Zero 或 Metro M0 板放入设备中。相反,你会有一个围绕标准微控制器设计的印刷电路板(PCB)。
如果您希望设置自己的 CircuitPython 板,最好选择 Microchip Technology 的 SAMD21G18A 微控制器。这个微控制器得到了 CircuitPython 的良好支持,也是最容易上手的。有两种方法可以将 CircuitPython 加载到您自己的板上。第一种是使用 Arduino IDE,第二种是使用 Atmel Studio。我将把重点放在 Arduino IDE 方法上,因为它更简单,更容易设置。此外,Atmel Studio 只能在 Windows 设备上运行,因此 Arduino IDE 方法可能是在 macOS 或 Linux 机器上工作的唯一方法。
你需要做的第一件事是从 Arduino IDE 中加载 Arduino Zero bootloader。最好使用 Atmel-ICE 编程器来完成这项工作。一旦你将你的 ISP 连接到你的目标,打开 Arduino IDE,进入工具 ➤ 编程器,选择 Atmel-ICE ,如图 1-18 所示。确保您有一个 LED 连接到引脚 PA17,并且设置了复位电路。
图 1-18
在 Arduino IDE 中选择程序员
选择好编程器后,将板设置到 Arduino Zero(编程端口),如图 1-19 。
图 1-19
在 Arduino IDE 中选择电路板
然后选择刻录引导程序刻录引导程序,如图 1-20 所示。
图 1-20
在 Arduino IDE 中刻录引导程序
烧完引导程序后,通过选择文件 ➤ 示例 ➤ 基础知识 ➤ 闪现来运行一个示例草图。确保连接到 PA17 的 LED 在闪烁。
下一步是加载 USB 闪存格式(UF2)引导程序。这个引导程序允许微控制器显示为大容量存储设备,因此您可以在板上加载和运行 CircuitPython。
接下来,前往 CircuitPython 网站,在 https://circuitpython.org/board/arduino_zero/
抓取 Arduino Zero 的 UF2 文件。该页面应类似于图 1-21 中的页面。点击 Download .UF2 Now 按钮,记下你保存文件的位置。
图 1-21
下载 CircuitPython UF2 文件
一旦你下载了这个文件,去 https://github.com/adafruit/uf2-samdx1/releases
下载最新版本的草图来更新 Zero 的引导程序。你会看到类似图 1-22 的东西。该文件的名称应该类似于update-boot loader-zero-v3-10-0 . ino,尽管版本号可能不同。
图 1-22
下载 Arduino 草图
打开草图并运行它。完成后,将 USB 电缆连接到您的微控制器。引脚 34 为 D+,引脚 33 为 D-。确保同时连接设备上的接地连接。微控制器现在应该在您的计算机上显示为可移动存储设备,内容如图 1-23 所示。
图 1-23
UF2 引导成功后的 CircuitPython 设备
将我们之前从 CircuitPython 网站下载的 UF2 文件复制到设备上。复制完成后,设备将显示为 CircuitPython 设备,如图 1-24 所示。
图 1-24
CircuitPython 在设备上运行时应该存在的文件
您的设备现在可以运行 CircuitPython 了。在撰写本文时,Mu 编辑器无法识别以这种方式安装了 CircuitPython 的设备,因此您需要使用 PuTTY 在 Windows 或终端上查看设备的输出,如果您使用的是 macOS 设备,则需要使用 CoolTerm。
作为参考,图 1-25 显示了基于 Arduino Zero 的器件的引脚排列,相当于我们刚刚设置的定制 SAMD21G18A 器件。如果你使用的是定制设备,你需要参考这张图片。它显示了所有的数字引脚、模拟引脚、通信和编程接口。
图 1-25
基于 Arduino Zero 的定制设备的引脚排列
结论
在这一章中,我们讲述了微控制器的基础知识,并了解了我们需要什么来设置我们的开发环境。一旦你完成了本章中的步骤,你的环境应该已经准备好用 Python 编程你的微控制器了。然而,对电子学有一个基本的了解将帮助你更有效地使用微控制器来构建你自己的设备。我们将在下一章讨论这个话题。
二、电子学入门
在上一章中,我们介绍了微控制器的基础知识以及如何建立开发环境。然而,要真正使用微控制器,我们需要将它们与外界连接。这需要对电子学有一个基本的了解。虽然不可能在一章中完全涵盖这个庞大而复杂的领域,更不用说一整本书了,但本章介绍了电子学原理,并概述了您将在微控制器中使用的一些元件。
电子学可以分为两个主要领域:模拟电子学和数字电子学。模拟电子学涉及电路中的元件可以有几种状态的电子学;因此,我们说模拟电子学处理的是连续电路。另一方面,数字电路有两种状态,因此数字电子学处理的是分立电路。在本章中,我将讨论这两个问题。
电就是电子的流动。为了理解电子,让我们谈一点原子。据说原子是物质存在的最小部分,这一切都很好,除了它还不止于此。即使在原子内部,也存在无数的亚原子粒子,它们被描述为粒子动物园。我们有质子、中子、电子、夸克、介子,这个清单可以一直列下去。
亚原子物理学可能需要一生的时间来研究。我们将跳过这一复杂性,把原子描述为由三种主要粒子组成。这些是质子、中子和电子。据说质子带正电荷,中子不带电荷,电子带负电荷。
现在我们的头脑中有了这些粒子,人们会认为它们的大小都一样。然而,事实并非如此。质子和中子比电子大得多。关于这些粒子,还需要记住的是,它们在原子中不是随机分布的。中子和质子在原子中心聚集在一起,电子围绕着它。
这种电子流称为电流。这些电子流动的原因很简单:导体本身由原子构成。当我们施加一个电压时,这是导体两点之间的电位差,它使这些电子流动,这就是所谓的电流。现在,记住导体的原子已经有电子了;因此,当你让更多的电子流过导体时,它会导致一个单独的原子向其邻居“扔出”电子,这个过程以光速继续进行许多许多次。
所以,让我们为那些仍然困惑的人回顾一下。原子是由质子、中子和电子组成的。这些电子可以流过允许它们流动的叫做导体的材料,而不能流过不允许它们流动的绝缘体。当这些电子流动时,它们产生电流,它们流动是因为导体两端存在电位差,也称为电压。允许电流通过的物质被称为导体,而不允许电流通过的物质被称为绝缘体。为了流动,电流需要一个闭合的路径或回路,称为电路。
一种叫做电源的设备两端有一个电势差,可以让电流流动。在上面的一段中,我们谈到了电压,它是导致电流流过物体的电位差。然而,这种电位差并不是来自它本身;它来自一种能产生电力的装置,我们需要这种电力来驱动电子设备。电池和蓄电池就是两种这样的电源。
在这个时代出生的任何人都肯定遇到过这种或那种形式的电池。你想过电池是什么吗?我的意思是,你知道在你的手机和你身边的小玩意里有一个。然而,你想过什么是电池吗?
电池是一种我们用来发电的装置,由一组电池组成。虽然我们把所有能提供电能的东西都叫做电池,但事实并非如此。电池是产生能量的单一单位。当我们收集了一些细胞时,就形成了一个电池。
原电池是这样一种电池,其中发生的产生电的化学反应不容易逆转。你更换过多少次家里钟表和遥控器的电池?一旦原电池耗尽,就意味着要被丢弃。
你用来给玩具和小玩意供电的碱性电池通常是原电池。当 AA 或 AAA 电池用完时,你可以扔掉它,根据预期用途放入一个新的。
如果我们观察细胞,我们会看到两个标记。电源的正极叫做阳极,你会看到它标有一个小小的十字符号(+)。你的电源的负极端被称为阴极,你会看到它标有一个小破折号(-)。这种阴极有时被称为接地。
虽然原电池有其用途,但我们向“绿色”的转变以及电池技术的进步使我们倾向于使用能量耗尽后可以充电的电池。我们称这些细胞为次级细胞。
二次电池是一种产生电能的化学反应可以逆转的电池。想一想;如果化学反应产生了电,增加电不应该能够逆转产生电的化学反应吗?
这是二次电池工作的前提。由于它的电池化学性质,它可以充电。这些类型的电池在我们的手机和平板电脑等现代电子设备中很常见。
我们将在整本书中使用的电子元件具有不同的物理属性,允许电子以特定的方式在其中流动。当我们使用这些组件时,我们所做的就是利用这些属性来产生某些想要的效果。在这一章中,我将依次讨论每个组件,以展示它如何影响电流。
电线
电线是电流在电路中流动的媒介。电线由包围导电金属芯的绝缘体组成。这种芯可以是实心的或绞合的。实芯焊丝由芯部的一片金属组成。图 2-1 显示了绝缘下面的实芯电线。
图 2-1
实芯线
绞合芯线由许多捆在一起的实心线组成。图 2-2 向我们展示了绞合芯线的样子。
图 2-2
绞合芯线
绞合线和实芯线都有它们的应用。在抗腐蚀方面,实芯焊丝非常坚固,对于可能发生腐蚀的应用,它们是首选的焊丝类型。另一方面,多股绞合线更柔软,因此用于电线可能会有很多弯曲的应用中。实芯线在大量弯曲下更容易断裂,不适合这种应用。
随着您获得更多经验,您将能够确定哪种电线适合哪种应用。有时,成本或电流处理能力等其他因素可能会决定您将在应用中使用哪种类型的电线。
关于电线,需要了解的另一件重要事情是它们的尺寸。我们用来指代导线尺寸的名称是导线的规格。美国线规(AWG)是大多数人在处理电线尺寸时使用的标准。
一些总是让初学者困惑的事情与电线的尺寸和分配给他们规格的数字有关。在 AWG 系统中,数字越大,导线越细。这意味着 12 号线(家庭布线的常用尺寸)比电子工程中常见的 22 号线粗。
更粗的线通常意味着更大的处理能力。当比较导线时,在 DC 电压下,单芯导线通常比具有相同横截面积的相应绞合导线提供更高的额定电流。原因很简单:归结起来就是热。
导线处理电流的能力取决于当电流流过导线时它能多好地消除产生的热量。单芯导线必须将热量从导线散发到外部,这比绞合导线要好。想想绞线中间的那一股。热量必须从该中心线股散发,并依靠其他线股将热量传导到电线外部,这导致散热较差。
试验板
当我们开始使用电子元件时,我们需要一个表面来构建电路。我们称这个表面为试验板。试验板是一块由称为插座的小孔组成的板,我们可以在那里连接我们的组件。图 2-3 显示了一个试验板。
图 2-3
一块试验板
试验板的插座按组排列。电路板的两侧是垂直的插座带,称为电源端子、电源轨或电源总线。这些都标有一个加号“+”用于指示电源的正极连接位置,一个负号“-”用于指示电路上的接地连接位置。在试验板内部,单个+或–列中的所有插座都用金属线连接,允许电流在插座之间流动。电源轨如图 2-4 所示。
图 2-4
试验板的电源轨用红线突出显示
试验板的中心由成行排列的插座条组成。试验板的这一部分称为原型制作区域。每排有两组五个插座,中间有一条沟隔开。每一排插座都有一个编号;在我们的示例试验板上,它们从 1 到 30 编号。一行中的每个插座也有指定的字母。在试验板内部,每组五个插座用一根金属线连接,如图 2-5 中突出显示的。中央沟槽用于支撑集成电路元件,这将在本章后面讨论。
图 2-5
原型区域,前几行插座用红色突出显示
公对公跳线,如图 2-6 所示,其连接器可以轻松插入试验板的插座中。
图 2-6
跳线
这些跳线用于将不同的插座连接在一起,为电流流经试验板上的元件提供路径。
电子原理图
描述电子学时,习惯上用一个原理图来表示你正在处理的电路。想象我们有一个连接到电池的电机,如图 2-7 所示。
图 2-7
一个简单的电路,特点是电机连接到电池
可以用电路元件的特殊电子表示来替换电路元件的图形表示。这种表示叫做示意图。在我们的第一个电路中,我们将电池连接到马达上。图 2-8 向我们展示了如何将这个电路重新绘制成图形。这个电路的图示叫做电路图。在电路图中,我们用图形来表示我们的电路。
图 2-8
我们电路的图示
图 2-8 中的电路可以用特殊符号重新绘制,以表示组成电路的元件,如图 2-9 所示。这是因为虽然电路图很棒,但随着我们的电路变得越来越复杂,充分使用电路图来表示我们的电路变得越来越困难。图 2-9 中的图表使用示意符号来表示我们的电路。原理图使用抽象的图形符号来表示电路中的元件,而不是图形表示。
图 2-9
使用原理图符号显示的相同电路
然而,这些术语并不是一成不变的,因为有些人在某些情况下将电路图称为示意图。然而,你应该知道,我们如何表示我们的电路存在差异。在本书中,我使用原理图,因为它们比电路图更清晰、更简洁。另外,一旦你理解了原理图,你应该可以毫无问题地理解电路图。
我们在电子产品中使用的每个元件都有自己的标准化示意图符号来表示。以下是我们迄今为止使用的组件的符号。
导线用直线表示,如图 2-10 所示。试验板上成行成列的连接孔可视为导线,因为它们允许电路内的元件连接。
图 2-10
导线原理图符号
如图 2-11 所示,一个单元由一条短线和一条长线表示,它们相互平行,导线从两侧伸出。
图 2-11
单元示意图符号
电池的示意符号是几个电池连接在一起,如图 2-12 所示。
图 2-12
电池示意图符号
电机也有自己的示意符号,它是一个内部带有大写字母 M 的圆圈,如图 2-13 所示。
图 2-13
电机示意图符号
随着我们的进展,将会逐渐引入更多的示意性符号。
无源元件
我们现在准备讨论所谓的无源元件。它们被称为无源,因为它们不需要任何外部电源来运行。这些装置能够消耗、储存和释放电能。无源元件包括电阻、电容和电感。
电阻
电阻器是一种阻止电子流通过的装置。这需要限制流入某些电路元件的电流。如果我们将太多的电流注入某些元件,它们就会被损坏,因此,我们使用电阻来防止这种情况发生。电阻也是一个特别重要的分压电路的一部分。分压器将较大的电压转换成较小的电压,这在构建某些电路时很有用,我们将在本书后面看到。
我们用欧姆这个单位来测量物质的电阻。仔细想想,所有电阻都是电路中电压和电流之间的关系。本质上,它是一种物质上的电压与通过它的电流之比。这种关系形成了所谓的欧姆定律。
我们谈论电路不能不讨论欧姆定律。一个叫格奥尔格·欧姆的家伙发展了一个定律,在这个定律中,他发现了电压、电阻和电流之间的关系。
- 电压=电流*电阻
这种关系简单却强大!为了说明这一点,让我们假设有一个 5 伏的电压流过你的电路,一个电阻的值为 1k。
我们如何计算电流?我们简单地重新排列欧姆定律,得到如下结果:
- 电流=电压/电阻
因此,我们可以计算出电路中的电流为 5v / 1000 欧姆= 0.5 毫安。
在图 2-14 中,我们可以看到一些电阻的样子。
图 2-14
一些抵抗组织:阿达果,adafruit.com
电阻器上印有彩色条带,用以指示电阻值。第一段表示电阻的第一位数字,第二段表示第二位数字,第三段表示零的数量。第四个频段称为电阻的容差水平,它告诉我们电阻可能超出或低于原始值的百分比。我们从左到右阅读这些电阻颜色代码;然后根据带的颜色,我们根据电阻的带数来赋值。我们通常可以通过寻找电阻上的金或银容差带来判断哪个带是第一带;公差带将是最右边的带,因此我们从最左边的带开始确定值。通常,公差带和值带之间会有一个小的间隙,但情况并非总是如此。表 2-1 列出了我们可以用来确定电阻值的颜色。
表 2-1
电阻器颜色代码
|颜色
|
数字
|
| — | — |
| 黑色 | Zero |
| 褐色的 | one |
| 红色 | Two |
| 柑橘 | three |
| 黄色 | four |
| 格林(姓氏);绿色的 | five |
| 蓝色 | six |
| 紫罗兰 | seven |
| 灰色的 | eight |
| 白色的 | nine |
公差带可以是棕色,表示 1%的公差,金色,表示 5%的公差,银色,表示 10%的公差,或者根本没有条纹,表示 20%的公差。还有其他指示其他容差级别的波段,但这些是您最常遇到的波段。
电阻是一种性能良好的元件。电阻是罪魁祸首的电路很少会有问题。如果一个电路中的电阻出现故障,要么是因为设计选择不当,要么是因为另一个元件的故障导致该电阻出现故障。
电阻的示意符号是一条锯齿线,如图 2-15 所示。
图 2-15
电阻器原理图符号
电容器
电容器负责在电路中储存电能。它们最常见的用途之一是与电阻一起用于滤波电路。电子设备中的滤波器用于允许特定频率的电信号通过,同时衰减或减少其他频率的电信号。一些类型的滤波器甚至能够放大或修改它们允许通过的频率信号。
滤波器电路有许多重要的应用;一个这样的应用是在音频信号中,当您想要创建一个双向分频电路,并将所有高频信号导向高音扬声器,同时衰减低频信号。
电容器的特性取决于制造它们的材料;我们称它们是由电介质制成的材料。电介质将决定电容器是极化的还是非极化的。极化电容器在电路中必须以某种方式连接;否则,它将被损坏并存在火灾危险。无极性电容器可以在电路中以任何一种方式连接都没有问题。
电容器所能容纳的电荷量称为电容,用法拉来度量。一法拉是很大的存储量,因此,我们通常使用少量电容,通常为微法拉(μF)和皮法拉(pF),分别为百万分之一法拉和万亿分之一法拉。
使用电容器时,需要注意的一些事项是它们的温度范围和工作电压。这些值通常印在设备的主体上。温度范围代表电容器正常工作的最低和最高温度,而工作电压是可以馈入电容器的最大电压。重要的是不要超过工作电压,因为这将导致设备损坏。当电容器发生故障时,它们会燃烧,并有能力带走其他电路元件。
如果电容器上没有电压或温度额定值,最好查阅器件的数据手册。这是一份列出您正在使用的设备的所有特性的文件,包括安全工作电压和推荐的工作温度。
极化电容器
你可能遇到的最常见的极化电容器是铝电解电容器,如图 2-16 所示。与大多数非极化电容器相比,这些电容器具有较大的电容。
图 2-16
一种铝电解电容器
由于其结构,铝电解电容器存在一个称为漏电流的问题。漏电流的产生是因为电容器中的绝缘体不完美,因此确实有一些电流流过。为了解决这个问题,人们发明了另一种电容,称为钽电解电容,它的漏电流比铝制电容低得多。
非极化电容器
陶瓷电容是迄今为止最常见的非极化电容。图 2-17 显示了陶瓷电容器的样子。
图 2-17
陶瓷电容器
陶瓷电容器比铝电解电容器提供更高的工作电压,尽管它们的电容要低得多。通常,您会根据自己的电容要求选择其中之一。如果你需要一个大电容,那么你可以使用电解电容;然而,如果你想要一个小电容,你会倾向于使用陶瓷电容。
如果你观察陶瓷电容器,你会发现上面印着三个数字。前两位数字代表电容值,最后一位数字给出乘数。我们所做的就是取前两位数,加上乘数中指定的零的个数。这给出了以皮法为单位的电容。如果您没有看到任何第三位数字,那么这就是皮法值。因此,如果你看到一个电容只写着“10”,那么这个电容的值就是 10 皮法。
例如,图 2-17 中列出的电容器的标记为 104。因此,该值将为 10 + 0000,即 100 000 pF 或 0.1uF。
电容器示意图符号
图 2-18 向我们展示了如何用示意符号表示极化电容器,图 2-19 向我们展示了如何表示非极化电容器。
图 2-19
非极化电容器示意图符号
图 2-18
极化电容器示意图符号
极化电容器总是有一根导线,旁边有一个加号“+”。非极化电容器没有任何极化标记。
感应器
电感是一个特别重要的元件,我认为很多人把它看得过于复杂。你可以利用微积分和其他高等数学来了解电感,或者你可以从电感是什么的角度来看待电感,它只是另一个可以使用的元件。不要担心;在本书中,我们将以最实用的方式介绍电感及其用途。
在深入研究电感之前,我们必须了解电磁学。在生活中的某个时刻,我们可能会遇到磁铁。
磁铁是展示磁性的材料。也就是说,磁体是具有磁场特性的材料。当电流通过导体时,就会产生磁场。如果我们有一根有电流流过的导线,磁场会很弱。如果将导线缠绕成线圈,就会产生更强的磁场,这就形成了电磁铁的基础。
一圈线圈会产生磁场。然而,这仍然是一个非常薄弱的领域。我们可以通过增加线圈的匝数来增加磁场的强度。
为了大幅增加磁场强度,我们将一种导磁材料放入线圈核心。这种材料通常是铁。当我们这样做时,我们现在得到了一个可以电子控制的强磁体。带有导磁磁芯的线圈称为螺线管。
我们刚刚学习了电容器,它们可以储存电荷,我们还学习了电磁学,即当电场穿过导体时,导体会产生磁场。
电感器是储存电荷的装置。我们刚才谈到的盘绕在一起的导线是一个电感器,因此,你有时可能会听到人们把电感器称为线圈。
电感和电容的区别在于,电感以磁场的形式储存电荷。就电感器的工作而言,你可以把电感器当作一个在磁场中储存电荷的黑匣子来设计许多电路。
电感缩写为(L ),测量单位为亨利(H)。
电感有各种不同用途的封装。这些电感可以是屏蔽的,也可以是未屏蔽的。记得我们说过电磁学吗?感应器的设计会产生磁场。该磁场会对周围的电子电路产生不良影响。我们可以通过屏蔽的方式来设计电感,从而将干扰对环境的影响降至最低。
电感用于阻挡电力线上的高频噪声;为此,我们把这样使用的电感器叫做扼流圈。这在逆变器和电机控制电路等电源电路中很常见。这些扼流圈可以用来处理相当大的电流。它们也用于设计类似于电容器的电子滤波器。
长期以来,“空芯”电感器被广泛使用。这些电感器是通过简单地将一根实心线缠绕成线圈而形成的。尽管这些仍然有其应用,但这种电感器有其局限性。只需添加一个铁芯供电线缠绕,就可以在更小的封装中获得更大的电感。随着时间的推移,其他核心材料和设计变得普遍。因此,现代电感器看起来不仅仅是一个中间有铁条的线圈。图 2-20 显示了一种你可能在现代电子产品中遇到的电感,左边是它的原理图符号。
图 2-20
电感器及其原理图符号
技术进步如此之快,以至于出现了一种叫做平面电感器的小型电感器。考虑到它们的小尺寸,它们具有非常高的电流处理能力。
半导体
在上一节中,我们讨论的电阻、电容和电感被称为无源模拟电路元件。还有一类电路元件称为有源电路元件。我们将从半导体开始讨论有源模拟电路元件。我之前提到了半导体,并告诉您我们将在稍后讨论它们。本节是对这一承诺的履行。
我们了解到,虽然银和铜等一些物质是良导体,但橡胶和玻璃等其他物质是绝缘体。然而,有一类特殊的材料被称为半导体。
在电子学中,元件中最常用的两种半导体材料是锗(Ge)和硅(Si)。尽管近年来市场上出现的一些其他半导体元件是由氮化镓(GaN)和碳化硅(SiC)制成的。
半导体的导电性比绝缘体好,但比导体差。另一种思考半导体的方式是,它们的电阻比绝缘体小,但比导体大。然而,这些器件仅在特殊条件下表现出这些性质,例如温度或添加其他材料。
前面提到的半导体不是最好的导体。这些半导体的导电性可以通过添加某些杂质来提高,从而使它们成为 n 型半导体或 p 型半导体。添加这些杂质的过程称为掺杂。当这些 p 型和 n 型半导体结合在一起时,就形成了所谓的 pn 结。
当你在一个电路的两点之间施加 DC 电压进行某项操作时,这就是所谓的偏置。当你施加的电压可以流过 pn 结时,这就是所谓的正向偏置。你可以通过在 pn 结上连接一个电源来实现正向偏置,这样电源的正端连接到 pn 结的正端,负端连接到 pn 结的负端。
然而,如果施加电压时,正端连接到 pn 结的负端,负端连接到 pn 结的正端,就会产生反向偏置。当反向偏置 pn 结时,它的电阻很大,不允许电压流过。
然而,当反向偏置 pn 结时,你应该知道有极少量的电流流过。还有,当你给 pn 结施加电压时,它会取一定量以上的电压;对于硅来说,它是 0.7 伏,即电流开始在电路中流动时的电压降。这种导致电流快速增加的电压称为拐点电压。如果对 pn 结施加大的反向电压,它就会击穿。结击穿时的电压称为反向电压。峰值反向电压是一个术语,人们用来指 pn 结被损坏前可以施加的最大反向电压量。
二极管
我们要看的第一个半导体器件是二极管。二极管只允许电流单向流动。当二极管以那个方向连接并且电流可以流动时,就说它正向偏置。当它以另一个方向连接时,二极管不允许电流流动,这被称为反向偏置。二极管由设备阴极上的条带来识别。图 2-21 显示了二极管的样子。
图 2-21
二极管
二极管的示意符号是一个三角形,尖端有一条线,如图 2-22 所示。
图 2-22
二极管示意图符号
二极管正向连接时电阻可以忽略不计,反向连接时电阻非常高。有信号二极管,用于小电流电路,和功率二极管,可以处理更高的电压和电流。
发光二极管
发光二极管(LED) 是一种特殊类型的二极管,在正向偏置时会发光。LED 可以产生可见光、红外光或紫外光。图 2-23 显示了发光二极管的样子。
图 2-23
发光二极管
LED 示意符号与二极管符号相似,只是其周围有一个圆圈,如图 2-24 所示。
图 2-24
LED 示意符号
像所有二极管一样,led 也有一个电压降。这个电压降取决于制造 LED 的材料类型。
一个 LED 上有两条引线。这些导线中较长的称为阳极。这是你连接正电压的地方。较短的导线称为阴极。这是你接地的地方。如果我们观察塑料外壳内的 LED,您会发现其中一条引线在外壳内看起来更大更平。这也可以用来识别阴极。此外,在圆形 led 上,塑料外壳本身有一个平边;这是另一种识别 LED 的方法。所有这些标记的原因是 led 不喜欢以错误的方式连接。我们将在以后学习如何使用 LED 时讨论这一点。
晶体管
晶体管是有史以来最重要的设备之一。它是计算机革命得以发生的核心。
晶体管是通过将 p 型或 n 型材料放在一对相反类型的材料之间而形成的。因此,当你有两个 N 型半导体,中间有一个 P 型半导体时,你得到一个 N-P-N 晶体管。如果你有两个 P 型半导体,中间有一个 N 型半导体,你将得到一个 P-N-P 晶体管。这个 NPN 或 PNP 结可以看作是两个背靠背连接的二极管,一个正向偏置,另一个反向偏置。晶体管的名字来源于这样一个事实,即它将信号从低电阻二极管传输到高电阻二极管。事实上,晶体管是“转移电阻器”的简称
到目前为止,我们一直在讨论的器件有一个共同点:它们都是双端器件。然而,晶体管有三个端子:基极、集电极和发射极。它们分别由字母 B、C 和 E 表示。图 2-25 显示了这些晶体管引脚的样子。
图 2-25
晶体管图
当电流流入晶体管的基极时,从集电极流向发射极的电流比流入基极的电流大。基极流过的电流越多,从集电极流到发射极的电流也就越多。我们在图 2-26 中看到一个晶体管。
图 2-26
物理晶体管
NPN 晶体管示意符号如图 2-27 所示。该符号是一个圆形,三个引脚集电极、基极和发射极分别标记为 C、B 和 E。NPN 晶体管发射极上的箭头指向远离基极的方向。
图 2-27
NPN 晶体管图
PNP 晶体管原理图符号如图 2-28 所示。PNP 晶体管看起来像 NPN 晶体管,发射极指向基极。
图 2-28
PNP 晶体管图
p 型和 n 型导体相遇点称为结。发射极和基极相遇的结称为发射极二极管。请记住,晶体管可以被认为是两个背靠背的二极管。集电极和基极相遇的结称为集电极二极管。发射极二极管正向偏置,允许电流流过,集电极二极管反向偏置,不允许电流流过。在我们前进的时候,请记住这一点。由于这种构造晶体管的方法,这里讨论的晶体管类型被称为双极结型晶体管或 BJT。
金属氧化物半导体场效应晶体管
金属氧化物半导体场效应晶体管(MOSFET) 像压控电阻器(VCR)一样工作。这些录像机有一个输入端口和两个输出端口;输入端口电压控制输出端口之间的电阻。电阻随着输入到器件的电压非线性变化。这一特性允许输入端口有效地开启或关闭 MOSFET。当 MOSFET 导通时,它的电阻很低,只有几分之一欧姆。查看 MOSFET 的数据手册时,您会看到它的 RDS(On)值,即器件饱和时的漏源电阻。当最大量的栅极电压施加到器件上时,MOSFET 处于饱和状态,这使得 RDS(On)非常小,允许最大漏极电流流过 MOSFET。较低的 RDS(On)值意味着器件的运行温度更低,效率更高。
我们可以在图 2-29 中看到一个 MOSFET。这是一种 TO-220 封装,对于 MOSFETs 来说并不少见。
图 2-29
金属氧化物半导体场效应晶体管
正如 BJT 有两种类型一样,MOSFETs 也有两种类型。图 2-30 显示了两者的示意符号。左边是 N 沟道 MOSFET,类似于 NPN BJT,右边是 P 沟道 MOSFET,类似于 PNP BJT。
图 2-30
N 沟道和 P 沟道 MOSFETs 的原理图符号
需要考虑的一个重要因素是栅极阈值电压,它表示将电流导入漏极所需的最小电压。将注意力转移到图 2-31 上。
像晶体管一样,MOSFETs 也有三个引脚。它们被标记为漏极、栅极和源极。它们分别相当于晶体管的集电极、基极和发射极引脚。通常,在大多数应用中,你可以用 MOSFET 代替晶体管。
图 2-31
晶体管和 MOSFET 并排
使用 MOSFET 的一些注意事项是确保不要超过栅极到源极的电压,否则会损坏 MOSFET。此外,当切换感性负载(我们称之为电感器件)时,要确保使用反激二极管(连接在器件两端的二极管)来保护 MOSFET。这是我们将在稍后研究如何在我们的设备中使用电机时更详细讨论的内容;现在请记住,当您使用 MOSFETs 时,如果您使用的器件充当继电器或电机等电感,您将需要使用二极管来保护该器件。
集成电路
到目前为止,我们看到的器件被称为分立元件,因为每个器件只有一个功能。相比之下,集成电路(IC) 是一种将几个分立元件放入一个设备中的元件。IC 在紧凑的封装中提供了许多有用的功能。通过将我们之前看到的元件组合成不同的电路配置,我们可以得到该电路的理想功能。我们可以把分立器件小型化,做成集成电路,而不是用分立元件来实现这个功能。例如,正如我们在上一章中所了解的,微控制器是一种将许多不同电路功能集成到一个封装中的集成电路。我们将在书中进一步了解不同类型的 IC。
图 2-32 显示了集成电路的样子。顶部通常有一个凹口,表示哪个引脚是器件上的第一个引脚。如果你没有看到一个凹口,那么你会看到一个点,这将起到同样的作用。
图 2-32
集成电路块
图 2-32 所示的 IC 类型是双列直插式封装(DIP) IC。这些 ic 通常有 4 到 40 个引脚,通常引脚均匀分布在容纳所有器件的塑料外壳的两侧。引脚间距通常为 0.1 英寸,这使得 DIP ICs 非常适合插入试验板。
虽然 DIP 技术非常适合原型制作,但今天你会发现大多数 IC 都是作为表面贴装技术(SMT)器件制造的,这种器件是为机器组装而制造的,可以更容易地以更低的成本制造电子产品。对于初学者来说,SMT 设备很难使用,尽管有一些适配器可以让它们适应试验板。
数字逻辑
既然我们已经讲述了基本的模拟电子学,我们可以看看数字电子学了。模拟电路元件,如晶体管和二极管,可以不同的方式组合成数字逻辑电路。所有数字电子设备的基本构件是逻辑门;因此,我们对数字逻辑的讨论从对逻辑门的讨论开始。逻辑门是一种电路配置,它接收输入并输出高或低状态。这些高和低状态对应于电压电平。在传统的数字电子设备中,我们认为高电压通常为 1.8、3.3 或 5 伏,低电压通常为 0 伏。这些高电压和低电压可能会有所不同,但对于大多数应用,这些都是我们将考虑的电压水平。本质上,高电压就是我们所说的逻辑电平“1”,低电压就是逻辑电平“0”。这就是所谓的二进制系统。二进制系统只用 0 和 1 两个数字来表示它们所有的信息。数字逻辑电路在其输入端使用这些逻辑电平,并产生二进制输出。对于我们在本节中所研究的数字逻辑电路,这种输出通常是一个二进制数字,我们称之为位。
逻辑门通常有两个输入和一个输出。尽管可以用任意数量的输入来构造门,但为了简单起见,我们将坚持使用两个输入。逻辑门是可以连接到微控制器的分立元件(我们将在下一节讨论这种类型),当这样使用时,它们被称为胶合逻辑,因为它们允许不同的逻辑电路通过桥接接口连接在一起工作。它们也是电路中较大的部分,我们将在本书后面的章节中看到。在这一节,我们将看看一些常见的逻辑门。
我们要看的第一个逻辑门是与门。与门比较两个二进制值。如果两个值都是逻辑 1 或高,则结果是逻辑 1,输出将是高。如果输入端的任何值为逻辑 0 或低电平,则门的输出将为低电平。图 2-33 显示了与门的符号。
图 2-33
与门
一个或门比较两个二进制值。如果任一值为逻辑 1 或高,则结果为逻辑 1,输出将为高。如果两个值都是逻辑 1,那么输出也将是高电平。如果输入端的两个值都是逻辑 0 或低电平,那么门的输出将是低电平。图 2-34 显示了或门的符号。
图 2-34
或门
非门是数字电子世界中经常遇到的另一种门。它将输入的反义词作为输出。如果输入低,则输出高,如果输入高,则输出低。图 2-35 为非门的示意符号。
图 2-35
非门
另一个重要的门是异或门。XOR 代表异或。如果输入具有相反的值,异或门将输出高值,否则输出将为低。换句话说,只有当一个或另一个输入(而不是两个)为高时,它的输出才会为高。XOR 门可以使用这种独特的能力来执行许多不同的任务,例如奇偶校验(用于确保数据正确传输),XOR 门的一个特点是任何数字与其自身进行 XOR 运算都会产生一个 0,用于清除机器代码中的寄存器(我们将在后面了解更多),并可以在微处理器中使用,以帮助进行更有效的加法。此外,如果您对同一个数字进行两次 XOR 运算(我们执行一次 XOR 运算,取其输出值,然后再次进行 XOR 运算),您将得到原始值!异或门符号如图 2-36 所示。
图 2-36
异或门
我们要看的最后一个门是缓冲门。缓冲门接收输入信号,并且不反转输出;如果你输入一个逻辑高,你得到一个高输出,如果你输入一个低输入,你得到一个低输出。缓冲门用于放大数字信号。如果在输入端提供逻辑信号的器件无法向目标提供足够的吸电流(提供电流)或源电流(接收电流)(稍后我们将在讨论微控制器的输入和输出时看到这一点),则使用缓冲栅极来增强器件输入端的驱动能力。图 2-37 为缓冲器的示意符号。
图 2-37
缓冲器
我们将会看到,所有这些逻辑门都有其应用。
逻辑电平转换
使用数字逻辑时,有时需要将一个逻辑电平(如 1.8 伏、3.3 伏或 5 伏)转换为另一个。这是因为不同的设备在不同的电压下运行。在数字电路的早期,系统通常在 5 伏电压下运行。由于 5 伏是微处理器等智能设备出现时的标准电压,因此它们也使用 5 伏逻辑进行操作。因此,许多外设(我们稍后将了解微控制器上的特殊电路)和模块都是为 5 伏系统设计的。
然而,近年来,行业趋势已转向使用 3.3 伏逻辑电平或甚至 1.8 伏。由于 5 伏系统已经存在多年,有时有必要将较低的逻辑电平与使用 5 伏逻辑的模块或其它 ic 结合起来,因为它们可能更容易获得,或者设计团队可能对这种器件的鲁棒性有经验。在这种情况下,可能需要执行逻辑电平转换,以便从一种电压转换到另一种电压。请注意,如果您使用的是具有 3.3 伏逻辑电平的 5 伏外设,则无需进行逻辑电平转换就可以与器件接口。然而,当使用具有 5 伏逻辑电平的 3.3 伏逻辑器件时,转换器是必要的。不过,为了安全起见,当您跨不同的逻辑电平域工作时,可以使用逻辑电平转换器。为此,您通常会使用专用 IC。也可以使用如图 2-38 所示的特殊逻辑电平转换 PCB。这个逻辑电平转换器可以看作是几个缓冲逻辑门,它们不改变信息,只是修改输入或输出端的驱动电流。
图 2-38
逻辑电平转换器
触发器
既然我们已经检查了分立逻辑门,我们可以转向构成计算机存储器一部分的触发器。触发器是最小的存储单元。触发器可以以高或低逻辑状态的形式存储单个数据位。在数字电子技术中,有组合逻辑电路和时序逻辑电路。组合逻辑电路的输入一旦改变,其输出也随之改变;以与门为例,一旦门的输入改变;输出将响应输入而立即改变。触发器可以用作计算机存储器的原因是它属于时序逻辑电路的范畴。这种电路只有在你有意改变输出状态时才会改变,不管你在输入端做了什么。该输出通常不仅响应于当前输入,还响应于设备的先前输入而改变。触发器是时序逻辑电路的基本构件。
钟控 RS 触发器就是这样一种时序触发器电路。钟控 RS 触发器依赖施加于输入端的时钟脉冲来改变状态。JK 触发器清除了与 S 和 R 都为逻辑 1 所导致的无效条件相关的一些不一致。因此,JK 触发器是一种常用于数字设计的流行触发器。在图 2-39 中,我们看到一个 JK 触发器。
图 2-39
JK 人字拖
我们看到的触发器上 J 和 K 引脚(输入引脚)之间的三角形引脚是时钟引脚;时钟引脚接受时钟输入。时钟可以被认为是在高和低逻辑电平状态之间交替的逻辑电平信号。时钟由低变高的状态称为时钟的上升沿,时钟由高变低的状态称为时钟的下降沿。我们还有输出引脚 Q 和!Q(不是 Q)。如果 J 和 K 处于逻辑高电平状态,并且我们施加一个时钟信号,那么 Q 和!q 会改变状态。如果 J 和 K 都处于低逻辑电平状态,并且我们施加一个时钟脉冲,那么输出将没有变化。
JK 触发器被称为通用触发器,它们用于移位寄存器、脉宽调制(PWM)电路和计数器等设备,以及我们将在本书中了解的其他类型的电路。
寄存器和移位寄存器
触发器构成了计算存储器基本单元的基础。我们用来指代计算机内存基本单元的另一个术语是寄存器。寄存器用于存储和操作计算机中的数据。寄存器利用触发器来存储数据,因此我们可以轻松地对其进行读写。为了便于理解,可以把寄存器想象成一个存储一位数据的黑盒,这个黑盒内部使用一个触发器来完成这个任务。
你会遇到的一种寄存器是移位寄存器。移位寄存器可以存储 4 位或 8 位的序列。移位寄存器具有一系列触发器,这些触发器以这样的方式连接,即当一个时钟脉冲施加到器件时,位从一个触发器移动到下一个触发器。
为了实现这一点,移位寄存器通常有一串 D 触发器。D 触发器可以认为是只有一个输入的 JK 触发器,这使得触发器更容易使用;然而,它需要更多的逻辑电路来实现它的实现。在一个移位寄存器中,我们称每个 D 触发器为一个锁存器。从最严格的意义上来说,锁存器和触发器的区别在于锁存器是电平触发的,而触发器是边沿触发的。这意味着锁存器是异步的,输入一改变逻辑电平,锁存器就改变输入,而触发器依赖时钟信号的状态(利用时钟的上升沿或下降沿)来改变输入。
我们说,当某样东西对器件输入端的电压电平转换做出响应时,它就是电平触发的,电压电平可以是高逻辑电平,也可以是低逻辑电平。另一方面,边沿触发器件响应时钟信号的边沿,它可以由时钟信号的上升沿或下降沿触发。
在包含锁存器的移位寄存器中,一个锁存器的输出连接到另一个锁存器的输入,数据可以串行(一次一位)或并行(所有位同时)馈入移位寄存器。
由于这种安排,我们可以有各种各样的移位寄存器
-
串行输入串行输出(SISO)
-
串行输入并行输出(SIPO)
-
平行输入平行输出(PIPO)
-
并行输入串行输出(PISO)
-
双向移位寄存器
每种类型的移位寄存器都有自己的应用。如今,许多移位寄存器都集成在 IC 中,因此不经常作为分立元件使用。
多路复用器和多路分离器
一个多路复用器是一个数字电路,它将两个或多个输入线路组合成一个输出。这有时被称为复用。图 2-40 有多路复用电路。
图 2-40
多路复用器
多路复用器有两个标记为 0 和 1 或 A 和 B 的输入,另一个输入线称为 SEL0,还有一个输出线 y。根据输入线的值,多路复用器为输出分配一个值。如果选择器线为低电平,则线 1 将在输出端。如果选择器线为高电平,输出将反映线 0 上的值。
一个解复用器做与复用器相反的事情。它将单个输入转换成许多不同的输出。解复用器如图 2-41 所示。
图 2-41
多路分解器
解复用器具有输入 A 或 F 和两个输出 0 和 1 或 A 和 B,以及选择器线 SEL0。输入线上的二进制模式将被转换为输出线上的值。当选择线为低时,A 处的输入将在线 0 上,当选择线为高时,多路复用器将 A 处的输入分配给线 1。
结论
在本章中,我们讲述了基本的电子学,重点是一些常用的实际元件。我们研究了无源模拟器件,以及半导体、二极管、晶体管和 MOSFETs。我们还看了一些数字电子元件。掌握了数字电子学的基础知识,当你以后使用微控制器时,你会更好地理解它们。我希望在这一章中,你将会学到电子学的概念是建立在彼此的基础上的。尽管二极管和晶体管是模拟元件,但它们可以组合起来形成数字构建模块,构成微控制器;这里学到的知识现在看起来很抽象,但我可以保证,当我们了解微控制器上的各种外设时,我们就会明白这些信息有多有用。这里的信息将为您的微控制器之旅服务。
三、嵌入式系统概述
虽然我很希望你开始写代码,但我们必须谈一谈,这样你才能理解你的代码是如何构建的。在这一章中,我们将介绍一点嵌入式系统的理论,这将为你理解本书的其余部分提供背景知识。在上一章中,我们概述了一些基本的电子学,在本章中,我们将嵌入式系统作为一个整体来看,以便更好地理解微控制器在整个事物中的位置。的确,光是这一章的内容,本身就能占据一本书的篇幅!这一章一定会很长,并且会给你一些贯穿你整个职业生涯的原则。
嵌入式系统概述
我们将从一开始就开始我们的旅程,讨论什么是嵌入式系统。嵌入式系统是为一个目的或一个功能而设计的系统,它被期望以高度的可靠性和最少的用户干预来执行。嵌入式系统的定义特征是它们通常是资源受限的。这意味着它们通常没有千兆字节的 RAM、无尽的电力和常规计算系统的计算能力。
这些系统依次由两个主要的子系统组成,一个是由主处理设备和相关电子设备组成的硬件组件,另一个是在硬件配置上运行的软件系统。
嵌入式系统有时需要在其他计算系统会失效的恶劣环境中执行其功能,例如在深空或海底。一些嵌入式系统预计在单个电池上运行数年,因此节能设计对大多数嵌入式系统至关重要。
大多数嵌入式系统都是为实时操作而设计的,这意味着系统的延迟必须最小。在某些情况下,如果不在要求的规格范围内,这种延迟可能会导致受伤或死亡。
微控制器与应用处理器
在我们进一步讨论嵌入式系统之前,我认为有一个重要的区别是必须要做的。您将在嵌入式设计中遇到的处理设备类型之间的区别。你会遇到基于微控制器的系统和基于应用处理器的其他系统。
微控制器(MCU)是一种独立的设备,将处理器、内存和支持设备集成到一个封装中。微控制器通常用于实时应用或要求高容量、低成本、低功耗或三者兼备的应用。
智能手机行业的诞生催生了应用处理器。应用处理器可以被认为是“类固醇处理器”,因为这些设备通常与许多 CPU 内核、图形处理单元(GPU)、高速缓存、多媒体编解码器、通信控制器和许多其他好东西一起封装在一个封装中。
两者之间的主要区别在于它们的预期应用。虽然微控制器意味着深度嵌入式应用,其中最复杂的应用需要实时操作系统(RTOS),但应用处理器通常至少具有 RTOS,更常见的是运行通用操作系统,如 Linux。应用处理器也可用于平板电脑或智能手机等通用计算系统。
嵌入式系统结构
尽管嵌入式系统的结构在不断变化,但大多数系统仍然在设计中使用经典的结构。嵌入式系统的经典结构如图 3-1 所示,由四个主要组件组成。嵌入式系统的主要硬件组件之一是与通常称为固件的软件程序协作的微控制器。固件是一种软件程序,最终用户不得更改。还有一些类型的输入和输出设备与这种微控制器-固件双核通信。虽然这种类型的配置不会很快出现,但重要的是要承认它不是构建系统的唯一方式。
图 3-1
嵌入式系统的经典结构
现在的趋势如图 3-2 所示,运行基于 Linux 操作系统的通用应用处理器系统与微控制器设备相结合,以执行实时处理功能。这可以被组合在一个系统级封装(SiP)中,该系统级封装将所有芯片包含在一个封装中或者在板上,其中应用处理器可以是位于“实时”处理器附近的系统级模块,该“实时”处理器通常运行实时操作系统(RTOS)或者裸机循环执行系统。
图 3-2
嵌入式系统的趋势结构
请记住,这些不是嵌入式系统的唯一结构,因为有时可编程逻辑结构可能存在于系统的某个地方。不管系统的结构如何,它都会从系统的输入/输出(I/O)端口接收一些输入,执行一些处理,然后通过一些输出设备向用户提供一些有用的输出。
具有经典结构的嵌入式系统的典型例子是计算器。计算器将通过键盘接收输入,然后嵌入式处理器将执行计算,输出将显示在屏幕上。类似地,大多数“哑”消费电器、办公自动化设备,甚至车辆的子系统都遵循这种“经典”结构。这是本书关注的焦点,因为它是构建其他结构的“基础”。
曾经为非常高端的系统保留的“更新”结构开始更频繁地出现。随着计算能力的提高,芯片成本不断下降,加上市场对“智能”设备的需求,导致了应用和实时处理器结构的这种耦合。机器人是利用这种结构的嵌入式系统的最好例子。一个微控制器执行实时处理,控制电机和读取传感器,然后与运行 Linux 的应用处理器通信,后者使用该数据来执行定位和映射,并通过某种串行协议与微控制器通信来控制平台的移动速度。
智能设备也具有这种结构,使用运行 Linux 的应用处理器来获得丰富的用户体验,它与底层微控制器或操作实时子系统的几个微控制器通信。
硬件系统
正如我们之前所讨论的,嵌入式系统的组件由硬件和软件组成,它们协同工作来执行某些任务。不管制造商是谁,硬件肯定会包括处理器、内存和 I/O 端口。
处理器通常是微控制器,在一个封装中包含中央处理器(CPU)内核、内存和 I/O 引脚。目前常用的微控制器内核有 ARM、PIC 和 AVR 架构。RISC-V 是另一种 CPU 核心架构,由于其设计的开源性质,有一天可能会流行起来。许多制造商,如许多 CircuitPython 板中使用的 SAM 微控制器的制造商 Microchip Technology,也将在封装中包括外设,以简化开发时间。
包装上的存储器分为两类,即程序存储器和数据存储器。程序存储器通常是基于闪存或 FRAM 的非易失性存储系统,通常大小从几千字节到几兆字节。程序存储器用于存储将由 CPU 内核执行的程序。另一方面,数据存储器是用于存储微控制器在运行时使用的数据的存储器,通常是几十或几百千字节的 SRAM。
I/O 引脚是拼图的第三块。它们允许 CPU 内核与系统外部的设备通信,例如传感器和其他集成电路。系统所需的 I/O 数量因应用程序而异。
最后,硬件设备上还包括外设。这些外围设备可以执行各种功能,例如系统支持、通信、数据转换、安全功能或者调试和诊断能力。一些较新的微控制器还具有核心独立外设(CIP ),无需 CPU 干预即可执行功能。诸如直接存储器存取(DMA)之类的一些外设可能需要 CPU 的初始干预,但是随后将执行它们的其余功能,而无需 CPU 的进一步干预。
在主处理器的外部,硬件系统可以包括其他设备,例如传感器、用户接口设备或某种类型的可编程逻辑设备。这些共同构成了嵌入式系统的硬件组件。
软件系统
软件负责使硬件变得有用。运行在嵌入式系统上的软件被称为固件,因为它不是为系统用户而设计的。然而,这并不是一成不变的,因为更新和升级可能会在部署后进行。现代物联网设备尤其如此,在这个消费者期待“最新最棒”的世界里也是如此。一般的理解是固件不应该被改变。
嵌入式系统中的软件通常分为两类。您可以设计一个裸机循环执行系统,也可以让一个实时操作系统运行该系统。两者的区别在于处理任务的方式。任务可以被认为是固件程序的最小单位。
在一个循环执行系统中,只有一个任务采取所谓无限循环的形式。在这样的系统中,程序会有一个主入口点,然后循环执行系统必须执行的一系列动作。大多数嵌入式设计都利用这种类型的系统。它实现起来很简单,并且在您不需要太多系统的时候使用。这是我们将在本书的开头部分使用的系统类型。
在基于 RTOS 的系统中,有许多任务需要执行。由于硬件系统的资源有限,因此需要一个调度程序来管理任务访问资源的方式。内核根据任务的优先级管理每个任务如何利用系统的硬件资源。RTOSs 通常被认为是一个高级主题,因此将在本书的高级部分讨论。
固件开发的一个主要方面是它必须被设计成模块化的。拥有模块化软件非常重要,因为它允许您在一个处理器家族中的多个成员之间,甚至在多个架构之间利用现有的代码库和知识。跨平台使用代码的能力被称为可移植性。从本质上讲,模块化和可移植性是协同工作的,我们可以在图 3-3 中看到这一点。
图 3-3
模块化和可移植性在好的软件设计中一起工作
这一点很重要,因为在对正确的软件开发过程进行初始投资后,新系统开发所需的技能和时间可以显著减少。有时,由于成本或设计要求,您可能需要升级或降级您设计的处理器,并且您将感谢您将您的软件设计为可移植的。
工具链
开发嵌入式系统时要考虑的一个重要部分是工具链。工具链是指用于创建嵌入式系统的工具,包括编译器和程序员/调试器。
先说编译器。我们编写软件的嵌入式设备称为主机,通常是 PC、MAC 或 Linux 机器,我们为其编写设备的设备称为目标。嵌入式工具链中的编译器在业内被称为交叉编译器。交叉编译器是一种特殊的编译器,它在主机上运行,但在链接器的帮助下创建最终的可执行程序,该程序是为目标程序设计的。
嵌入式工具链的第二部分是将交叉编译器创建的程序加载到设备上的机制。加载是一个过程,通过这个过程,称为程序员的设备将可执行映像放入目标的非易失性存储器中。这通常通过编程器经由主机和目标之间的 USB 或网络链路连接来实现。许多程序员也有调试能力,允许你验证一个程序是否正常工作。出于这个原因,许多程序员被称为在线调试器(ICD ),它允许您在不从电路中移除设备的情况下调试设备。
联合测试行动小组(JTAG)是一个流行的调试和测试嵌入式硬件的行业标准。
像 CircuitPython 这样的开发环境的好处是,通过将解释器放在芯片上,我们可以消除与使用传统开发环境相关的大多数常规细微差别。由于集成了引导加载程序,我们只需将程序放到芯片上,设备就会为我们解释它。
软件测试
嵌入式软件通常被忽视的一部分是测试。测试确保嵌入式软件按预期运行;这些测试被称为功能测试。还有性能测试,它主要确定软件是否满足“SSS”要求(速度、稳定性和可伸缩性)。性能测试本质上是质量测试。故障播种是我们可以执行的另一个常见测试,在该测试中,我们将故障引入系统,并确定它如何响应。我们这样做是为了确保我们的异常处理代码正常工作。
为了帮助测试,许多供应商提供了模拟器,这是一种在您将程序部署到实际硬件之前确保程序正常工作的方法。由于嵌入式硬件的限制,有时 LED 可能是帮助测试的最佳工具,因为它提供了程序按预期工作的视觉反馈。
嵌入式软件架构
为了保持固件的可移植性,开发人员的趋势是为他们的固件生态系统使用所谓的分层架构。分层架构的作用是将如何控制实际硬件的具体驱动程序细节与应用程序本身分开。甚至制造商也将这种类型的设计强加给开发者,所有微控制器供应商都提供某种硬件抽象层(HAL ),该硬件抽象层在软件设计中通过分层架构使用可重用固件的原理。
您将使用的分层应用程序的类型将取决于您的设计的复杂性。您将会遇到的典型分层软件配置通常有 2 到 7 层,我们现在来看一下。
这些层甚至可能没有这里所描述的布局,因为一旦它们被组合到框架中(稍后会有更多介绍),它可能看起来不完全像这种严格的分层方法。一些制造商设计的框架在框架的同一层上有几层。然而,我在这里提供的信息将阐明嵌入式软件是如何构造的。让我们一次检查一层。
驱动程序层
驱动程序层具有特定于 CPU 内核的代码,并且是软件架构中唯一通常不可重用的部分。这是因为,即使在器件系列中,也可能有针对该器件的特殊外设说明。有时,芯片缺陷是存在的,为了确保开发人员仍然可以使用为其编写驱动程序的外设,有必要采取变通办法。
好的司机有四个主要功能。它们执行外设的初始化,配置外设,执行运行时控制,并正确关闭驱动程序。
有时,对于简单的系统,应用层是建立在驱动程序层之上的。因此,在这一点之后,可能不需要其他层,一旦驱动程序层就位,应用层就可以位于任何其他层之上。
这意味着所有后续层都可以放在驱动程序层之上,但在应用层之下。因此,在我看来,驱动程序层是软件架构中最重要的一层。很多开发后期出现的问题都可以追溯到这一层。这一层必须尽可能坚固耐用。
如果您想知道什么是应用层,应用层是包含您希望系统执行的任务的程序。这是根据系统需求实现程序的地方。我们在图 3-4 中看到了这种结构。
图 3-4
驱动程序层构建在硬件层之上
硬件抽象层
下一层是硬件抽象层(HAL)。HAL 位于驱动程序层和应用层之间。
HAL 为驱动程序层增加了模块性,允许开发者在不了解硬件细节的情况下使用特定的硬件特性。这提供了一致性,可以跨设备系列使用,甚至独立于 CPU 核心架构。许多微控制器供应商现在都提供了 HAL,可用于提高开发人员的工作效率。设备制造商通常投入大量资源来提供一个好的 HAL,因为它允许开发者尽可能容易地使用他们的设备。
图 3-5 允许我们消除该结构。
图 3-5
硬件抽象层(HAL)的布局
主板支持包(BSP)
一些架构在 HAL 之上有一个板支持包(BSP ),提供了更高层次的抽象。制造商通常为他们的产品提供开发板。制造商将提供一个板支持包,以便开发人员可以测试并确保一切正常工作。
由于电路板的布局,可能需要特殊的软件配置来确保电路板正常工作。有时,当您与设计公司签订合同来帮助产品开发时,他们可能还会提供 BSP,特别是当希望董事会运行操作系统来确保一切顺利运行时。当你计划迭代未来电路板的设计时,BSP 是很好的选择。如图 3-6 所示。
图 3-6
放置板支持包(BSP)
如果你正在开发一个基于 Linux 的系统,那么主板支持包是必不可少的。BSP 将配置通信总线、时钟、内存以及操作系统正常工作所需的任何其他相关外围设备。BSP 通常还包括一个 bootloader,它是一个在应用程序代码运行之前运行的小程序。
中间件
有一层可以让您的生活变得更加轻松,这一层通常是应用程序实现之前的最顶层,它就是中间件层。中间件通常位于 HAL(或者 BSP,如果该层存在的话)和应用程序之间。中间件将板支持包的软件组件与应用层连接起来。请记住,BSP 是可选的,所以很多时候你会发现中间件直接将 HAL 与应用层连接起来。
中间件很重要,因为它通常包含用于高级外设和复杂协议的软件。好的供应商提供中间件来确保他们的客户使用他们的产品。编写一个设备驱动程序可能需要几个小时到几天,而中间件的开发需要几个星期到几个月的时间。我们在图 3-7 中看到了新的结构。
图 3-7
中间件布局
软件框架
虽然分层体系结构有利于对软件的结构有一个总体的了解,但是嵌入式软件体系结构可以以不同的方式配置。这些层的互操作性的具体配置和布局被称为软件框架。框架提供的是简化开发的驱动程序和中间件库的特定配置。我们在图 3-8 中看到了这一切的全貌。
图 3-8
示例软件框架模型
虽然图 3-8 中的框架模型并不代表所有或任何特定的实现,但是它可以让你理解一个软件框架是如何构建的。软件框架是模块化的,注重可扩展性。它没有一种结构化的方法,也不一定具有前面提到的严格的分层方法。本质上,框架提供了构建应用程序的“脚手架”。通常不希望你修改框架。然而,在实践中,您可能会遇到框架中的错误,这可能需要修改。请记住,大多数框架都有许可证,可能会限制更改或要求您发布您所做的任何更改。在做任何修改之前,最好先看看您打算使用的框架的许可协议。
编码发生器
行业趋势表明,软件框架的下一个层次是代码生成器。代码生成器目前是嵌入式软件开发的最后前沿。代码生成器所做的通常是使用一个图形化的配置环境,它详细描述了您的应用程序中需要的软件框架的组件。生成您需要的组件,不需要的组件不会添加到项目中。代码生成器生成代码结构、目录、引导加载程序和最终应用程序所需的任何其他组件。
平台
一起工作的硬件和软件的组合被称为平台。该平台包括硬件、开发工具(包括工具链和 ide)、软件框架,有时还包括代码生成器。平台可以大大减少开发时间。我们可以用来学习嵌入式系统的一个平台是 Arduino 平台。Arduino 平台是由板、屏蔽、引导装载程序、库和开发环境组成的整个生态系统。类似地,CircuitPython 遵循这个概念,结合了硬件、软件和开发工具。图 3-9 显示了这种关系。
图 3-9
平台包含的三元组
嵌入式系统限制
嵌入式系统在设计上有各种各样的限制。最常见的限制是成本、性能和能源预算。我们将在下面的小节中讨论每一个问题。
费用
首先,嵌入式系统最关键的一个方面是成本。成本是嵌入式设计的主要驱动因素之一。由于大多数嵌入式系统被设计成消费类产品,这些产品将被大量生产,并且基本上是“一次性的”,因此设计中的组件成本至关重要。有时候,从长远来看,一个设计的几分钟会产生巨大的影响。
嵌入式设计中使用的组件列表称为物料清单(BOM)。在讨论成本时,你会碰到一个术语叫非经常性工程(NRE)成本。NRE 成本是指将产品投入生产的初始成本。像研究、原型、产品设计和文档都属于这一类。
设计嵌入式系统时需要考虑的另一个因素是您将使用的器件类型。有从零售商处或直接从制造商处购买的商业现货(COTS)零件。还有一些专用集成电路(ASICs)是为你在任何地方都找不到的特定目的而设计的。
虽然有些人可能不同意我的观点,但在 COTS 解决方案和 ASIC 之间存在一个中间点,ASIC 是现场可编程器件的一种混合解决方案。现场可编程门阵列(FPGAs)、可编程逻辑器件(PLD)和现场可编程模拟阵列(FPAAs)等器件可以“现货”购买,但需要设计人员确定器件的具体功能,因为这些器件在购买时并未设计具体功能。
然而,有时嵌入式设计的成本是不可避免的。如果你是为需要特殊组件的航空航天或深空应用而设计,你可能无法降低成本。例如,在深空探测中,抗辐射组件(rad-hard)可能需要几十万美元。在这里,可靠性和质量比成本更重要。一般来说,你通常需要在价格和质量之间做出平衡。
表演
性能是嵌入式系统设计的一个重要方面。当我们谈论性能时,我们谈论的是系统在合理的时间内执行其功能的能力。合理的定义将在设计的背景下。对于电动牙刷,合理的时间可能是几秒,而气囊展开系统将有十分之几秒来执行其功能。硬件和软件组件都可能影响系统的性能。
在硬件方面,处理器架构和时钟频率会对性能产生影响。运行在 400 MHz 的 32 位微控制器比运行在 4 MHz 的 8 位微控制器具有更好的性能。这两者是相辅相成的,选择合适的架构和合适的频率是一项需要时间学习的技能。
硅缺陷也会对性能产生影响。在许多情况下,CPU 内核或外设会出现问题,这是由制造商的设计错误引起的。这种错误被制造到产品中的结果导致硅缺陷。这些问题会对性能产生严重影响,尤其是在设计过程中未被发现的情况下。有时有必要查看您正在使用的芯片器件的勘误表,通常可以在数据手册中找到。
在软件方面,抽象和算法复杂性以及任务细节都会影响性能。尽管软件开发的当前趋势是在应用程序中有大量的抽象和复杂算法的实现,但重要的是要记住,抽象层越多,需要的 CPU 时间周期就越多。为此,有时从应用层,有时可能需要直接调用较低层次的驱动程序层,如果你需要一定水平的性能。保持算法简单也可以大大减少开发时间。总的来说,在设计你的算法和软件时,记住“KISS”(保持简单,愚蠢)。
任务细节指的是调度程序如何为系统中的任务分配优先级,以及任务之间如何通信会极大地影响系统的性能。根据您的调度程序如何向您的系统分配任务,如果资源分配有错误,它会影响您的最终应用程序。任务的互通也会影响绩效;如果有信息需要在任务之间传递,那么必须确保任务之间的通信没有错误。
能量预算
我把嵌入式系统约束的这个方面留到最后,因为所有嵌入式系统都有能源预算。即使是用市电供电的电器也有能源预算,因为它们在设计时必须考虑能效。嵌入式系统通常依靠电池运行,因此,它们必须尽可能节能。您将使用的电源类型也会影响设备的重量和尺寸。我说尺寸是因为电池和一些电源可能需要特殊冷却,这会增加系统的总重量和尺寸。
制定电池预算是确保您的系统不会消耗太多电力的好方法,尽管大多数设计师都是事后才考虑电力。我甚至可以说,在您开发应用需求之后,甚至在您选择处理器之前,就应该确定您的电池预算。问一个问题,系统将使用多少功率?确定预算后,您可以专注于选择处理器。
您还应该查看可用的处理器节能模式。这个重要的特性意味着你的产品运行几天和几个月的区别。阅读处理器的数据表,确定处理器的睡眠模式。通常,HAL 和驱动程序层将提供选择睡眠模式类型的功能。
拥有专用电源监控电路、通过 I/O 引脚为外部设备供电,以及使用更节能的现代设备,都是降低设备功耗的好方法。
嵌入式系统分类
既然我们对嵌入式系统的硬件和软件组件以及限制有了很好的了解,我们就可以专注于理解嵌入式系统是如何分类的。嵌入式系统可以分为小型、中型和高性能系统。
小规模系统
小型嵌入式系统采用单个微控制器设计,通常是 8 位、16 位或低功能 32 位微控制器。在硬件方面,单个微控制器控制整个系统。这种系统构成了当今世界嵌入式系统的主体。这些类型的嵌入式系统通常限制最多,通常要求极低的成本、低性能和超低功耗。它们有几千字节的程序存储器和几千字节的数据存储器。
这个市场由 8 位架构的设备主导,也可能包括 4 位架构的设备(是的,4 位)。制造商一直试图为这个市场设计 32 位设备,32 位正在逐渐蚕食 8 位微控制器在这个领域的份额。这类设备通常需要在单个电池上运行数年。此类别中的“高端”设备也可能是低功耗 16 位设备。尤其是较新的基于 ARM 的设备越来越能够满足这些标准。
在软件方面,小规模系统通常运行具有循环执行系统的裸机,并且在某些情况下可能实现协作调度器。这些设备通常具有两层软件架构,汇编代码甚至可能用于开发这些设备的应用程序。这些是我们将在本书中重点介绍的系统类型,因为您的大多数设计任务都属于这一类别。我们在图 3-10 中看到了这些系统的结构。
图 3-10
小规模系统
中型系统
中等规模的嵌入式系统可能只有一个处理设备,也可能有多个设备。它们可以将 8 位微控制器和 16 位或 32 位处理设备与几百千字节的数据存储器和高达大约 2 兆字节的程序存储器结合起来。一些供应商还为这一类别的设备提供 16 位和 32 位多核设备。
软件可能由运行 RTOS 的主处理器组成,尽管其他处理器可能是裸机小型系统。汇编语言可以在这一级使用,但可能只在小规模的子系统上使用。它们可能包括网络功能,用于“中档”嵌入式设计。这种设计如图 3-11 所示。
图 3-11
中等规模系统
高性能系统
高性能嵌入式系统可能有一个或多个处理设备。它们可能包含多个高端处理器和 FPGAs,有些可能需要数字信号处理器(DSP)作为其硬件设计的一部分。它们可以被设计成具有运行 RTOSss 的多个处理器子系统,RTOS 本身由几个子处理器系统组成。一些组件甚至可能由运行完整操作系统的应用处理器组成。这些是嵌入式系统所能得到的最昂贵和高性能的。
这种系统的软件设计通常需要几个具有不同专业领域的开发人员来设计。开发工具和调试器也可能需要昂贵的许可费用(尤其是在使用高端 FPGAs 的情况下)。可能还需要购买制造和工具供应商支持的额外费用。
分布式嵌入式系统
中型和高性能系统本身可以归类为分布式嵌入式系统。分布式嵌入式系统由不同的子系统组成,每个子系统都是一个更大系统的节点。这些系统可以具有不同的通信模型,可以是 TCP/IP、can、LIN 等,并且是存在于有线网络(例如汽车和飞机中的那些)以及无线网络(例如物联网)中的系统类型。
这种系统需要有经验的架构师来设计,因为需要协调系统事件,异步系统的设计至关重要。全分布式嵌入式系统没有“主”处理器;所有节点都独立运行,是一个独立的系统。
这些分布式系统允许模块化设计,这允许安全关键部件与非安全关键部件分离。安全关键系统是这样一种系统,如果该系统发生故障,将会导致严重的人身伤害或死亡,或者导致重大的经济损失。因此,例如,在汽车中,拥有分布式系统将允许控制车辆防抱死系统的设备与信息娱乐系统分离。
通常,分布式嵌入式系统是为大型系统保留的,如航空航天、汽车和军事工业中的系统。然而,最近发生的事情是,随着物联网(一种分布式嵌入式系统)的引入,开发人员需要了解分布式嵌入式系统。
开发嵌入式产品的七个步骤
开发嵌入式产品有七个步骤,即创意、需求规格、功能设计、快速原型制作、测试、保护和推向市场。
第一步:创意
第一步可能是最重要的,那就是提出你的想法。这可能是这个过程中最难的一步。最重要的是你的想法必须有一个预定的目标市场,最重要的是你的产品必须解决一些问题。很多时候,客户会提出这个想法。
步骤 2:需求规格
流程的第二步是设计需求规格。这一步很重要,因为你必须对你将要设计的东西有一个清晰的方向和焦点。如果你有一个客户,你的客户想要一个特性,然后又不想要,这种情况并不罕见,这导致了客户和开发人员之间的分歧。拥有一份清晰的需求规格文档将有助于作为客户需求的证明文件,否则你可能会发现自己不断地添加新功能,直到超出预算并落后于上市时间。确保在此步骤中包含一个框图,显示系统将如何组成。
第三步:功能设计
有了需求之后,下一步是进行功能设计。需求规格说明描述了我们将要设计的东西,而功能设计是我们将如何着手构建它。功能设计将包括制作系统模型和进行验证。这是使用统一建模语言(UML)等工具对系统建模的阶段。您还应该有功能块,这些功能块进一步细分需求部分中的块,以包括更多的细节。
第四步:快速原型制作
传统上,嵌入式系统开发遵循几个阶段或一系列步骤,并使用几个开发过程。这些包括常见的软件架构,如瀑布、V-Cycle 和基于螺旋的开发方法。大多数书和课程都会教这个。然而,在行业内,发展趋势是朝着快速成型。快速原型所做的是利用现有的高级硬件和软件工具来实现你的产品。快速原型制作利用平台、代码生成器和硬件模块来开发产品。您通常会对设计执行迭代,编写软件和测试,直到您完成原型设计,而不是遵循一系列增量步骤。
这样做的目的是以更快的速度给你一个原型,并提供更快的上市时间(TTM),这是指从提出想法到销售你的产品的时间。快速原型制作节省了大量时间,就好像你必须重新设计一个原型一样;它包括简单地对模块进行“即插即用”和重写软件。
传统上,你会在试验板上设计一个电路,然后为原型旋转 PCB 板,得到某种类型的外壳,修改它,然后你就有了你的原型。这一过程既昂贵又耗时。
借助 Arduino 等平台和 3D 打印等技术,有可能让您的产品在外观和功能上非常接近最终设计,因为您可以在几小时或几天内迭代设计,而不是几周或几个月。
第五步:测试
即使您将在原型阶段执行测试,在产品创建之后执行测试也是很重要的。测试是一个复杂的过程,可以跨越几本书的信息。出于这个原因,我已经包括了一些基本的测试,您可以执行这些测试来确保您有一个功能合理的系统。当涉及到测试时,总是有扩展的空间。
您将执行硬件测试,包括查找系统中的缺陷和错误。硬件缺陷是实际原型和设计之间的差异。当有缺陷的系统运行时会发生错误,这可能导致硬件系统的故障。
在软件方面,执行软件性能测试很重要,重点是速度和稳定性,有时是可伸缩性。原型完成并且重构软件之后,您必须执行单元测试。单元测试是分离软件的类和功能组件,并将其与系统的其余部分隔离开来进行测试的过程。您将执行数据测试和场景测试。
在数据测试中,您可以在广泛的输入值范围内测试隔离函数。在基于场景的测试中,您既要检查软件处于典型用例中的普通用例场景,也要检查与普通用例不一致的场景。对系统的这种测试会使系统中的任何错误变得可见。在这种类型的测试中,创造力是必不可少的,因为你永远不知道你的用户会做什么。尝试你能想到的任何事情。在极端情况下测试系统。执行基本的篡改,看看会发生什么。你会很高兴你做了。
您还必须执行交互测试。交互测试是一种测试硬件和软件系统互操作性的方法。您可以执行这种类型的测试的方法是执行故障播种,并查看系统的每个组件如何对该故障做出反应。尽管这些测试方法简单且不完整,但是简单地执行这些测试就可以让您领先于那些不执行这些基本测试就发布产品的开发人员。我见过无数失败的产品,它们本可以在产品上市前通过执行这些简单的步骤而受益。
步骤 6:保护您的系统
不用考虑系统安全性的日子已经一去不复返了。随着嵌入式系统上的软件变得越来越复杂,有必要引入安全措施来保护您的嵌入式系统。
没有连接到网络的小型系统通常需要实施较少的安全措施。对于此类系统,设置一些锁定位或熔丝位并启用代码保护可能就足够了。您还可以在软件中引入某种类型的 ID 认证方法,防止固件未经验证就运行。用环氧树脂涂覆您的重要组件也是保护您的系统免受恶意攻击的好方法。
虽然上述方法有利于保护未连接到网络的嵌入式系统,但是嵌入式系统还有一个额外的安全隐患,那就是联网。随着物联网(IoT)的日益普及,需要将更多设备连接到互联网。事实上,物联网寻求将一切连接到互联网。
针对连接的嵌入式系统的一些常见攻击是广播风暴、重放攻击和端口探测。广播风暴攻击是指通过网络链路向最初广播数据的节点重新广播数据,从而导致网络故障的过程。重放攻击包括破坏加密或使用不安全的网络来拦截在网络上传输的数据,然后延迟其传输或稍后重新发送。端口探测是在网络上寻找开放端口以利用漏洞的过程。
黑客利用嵌入式系统安全性的另一种常见方式是使用上述攻击之一来访问您的嵌入式系统,对您的设备重新编程,然后将其与其他嵌入式设备一起使用来发起分布式拒绝服务攻击(DDoS)。如果您引入了我们安全讨论第一部分中提到的 ID 认证,它将防止黑客对您的设备重新编程,并防止 DDoS 攻击。
不可能为您的系统提供所有级别的安全性。最强大的设备是人类的大脑。因为人们是如此有创造力,如果一个黑客想找到进入你的系统的方法,他们会找到这样做的方法。你所能做的就是通过采取上述措施来降低与你的产品相关的风险。通常用于基于 CitcuitPython 的电路板的 SAMD 微控制器制造商 Microchip Technology 也提供有助于网络安全的解决方案,并为物联网设备提供使用案例。微芯片 ATECC608A 等设备提供了可用于保护您设备的算法和安全协议。他们为两家最大的云提供商提供了使用案例,这是为您的网络嵌入式系统增加安全性的良好开端。
第七步:推向市场
创建嵌入式设备的最后一步是将其推向市场。这是最长和最详细的步骤。在下面的步骤中,您创建了一个工程原型。将设备推向市场的步骤太多了,本书无法一一介绍。因此,如果你没有这方面的经验,我建议你使用专注于产品开发的工程公司。
如果你没有经验,我建议你使用工程公司的原因是,这个行业是一个鲨鱼吃鲨鱼的世界。组件供应商和外壳制造商,尤其是那些不在美国的供应商和制造商,会窃取你的设计,给你假的组件,甚至可能拿走你的钱,却不提供你所购买的产品。这些工程公司将与经过验证的供应商和设计师建立联系,并利用他们的专业知识帮助你将产品推向市场。
结论
在这一章中,我们研究了软件开发的所有方面,包括嵌入式系统的概述、嵌入式软件架构、嵌入式系统的分类以及开发嵌入式产品的步骤。本章包含的信息将在您的嵌入式职业生涯中为您服务。在使用 CircuitPython 开发自己的微控制器产品时,以及在使用复杂工具和更高性能系统时,这里学到的技术和术语将对您有所帮助。
四、Python 编程
Python 是一门庞大而复杂的语言,我不希望在一章中涵盖它。有太多的函数、结构和编程构造需要详细说明,以使您成为一名优秀的 Python 用户。然而,我将尝试应用 80/20 法则,也称为帕累托法则。因为我们学习 Python 的目的是用 CircuitPython 编写微控制器,所以我可以省略很多东西,但仍然可以让你理解这本书。
出于这个原因,我们将学习你需要知道的 20%的语言,以处理你可能用 CircuitPython 做的 80%的任务。因此,这一章将呈现核心语言的一个子集,简要地涵盖该语言最重要的方面。如果你有使用 Python 编程的经验,你可能会认为我应该添加其中的一个。不过,我在这里介绍的子集已经足够了,例如,任何来自 Arduino 或 C 背景的人都可以轻松掌握。
编写 Python 程序
Python 程序通常是在带有“.”的文本文件中编写的。py”扩展名。Python 有两个版本,一个是 Python 2,它是 Python 的传统版本,另一个是 Python 3,它是当前版本,得到了很好的支持。出于大多数目的,我们将关注 Python 3,因为这是 CircuitPython 中使用的版本。如果您觉得有必要运行本章中介绍的任何程序,您可以使用 Mu 编辑器。
为此,只需点击如图 4-1 所示的模式按钮。
图 4-1
模式按钮
点击这个按钮后,应该会出现一个窗口;从对话框中,选择图 4-2 中的“Python 3”选项,启动一个新的 Python 程序。
图 4-2
Python 3 选项
一旦它被选中,一个窗口打开;我们在文本区输入我们的程序,并按下图 4-3 中描述的运行按钮。
图 4-3
运行按钮
我们的程序将运行,并在位于 IDE 底部的控制台窗口中查找输出。我们的程序完成后,我们可以点击停止按钮,如图 4-4 所示。
图 4-4
停止按钮
现在你知道了如何运行 Python 程序,我们可以看看如何使用 Python 语言了。
首先看一下 Python 程序的典型结构,以此开始我们对 Python 编程的讨论是很重要的。清单 4-1 展示了我们将在本书中使用的一个基本 Python 程序的例子。我们使用 import 语句在程序中引入模块,这样我们就可以使用其中包含的方法。我们还将有一个主循环,称为无限期运行的超级循环。哈希符号“#”后的单词称为注释。这些被 Python 解释器忽略了,但是允许我们向自己和其他程序员解释我们的代码在做什么。
# import math modules
import math
# main super loop
while(True):
# call method from our imported module
print (math.sqrt(4))
Listing 4-1Basic Python Program
在 Python 中,我们访问的方法是代码块,只有在被点符号调用时才会运行。我们的数学模块有一个我们想要使用的名为平方根的方法,所以我们称之为“math.sqrt”方法。还有一个打印声明。print 语句用于将东西输出到我们的显示器,我们可以用它从程序中获取信息。所有 Python 程序都将保持这种基本结构,你将在后面的章节中看到。
空白
需要注意的是 Python 程序中的空白。当我们谈论空白时,我们真正谈论的是不可见的字符,比如制表符、空格、换行符等等。
一些编程语言会忽略空白,但是在 Python 中,空白扮演了一个特殊的角色。空白用来构建你的 Python 程序。虽然许多编程语言使用花括号来表示代码块的开始和结束位置,但在 Python 中,空格用于表示程序中的代码块。注意空格,因为它在 Python 代码中特别重要。这本书里的程序相当短,没有太多的缩进层次;然而,你仍然应该注意程序中的空白。
评论
注释是 Python 程序中的文本,是为了程序员的利益而存在的,因为它们被解释器忽略了。良好的编程实践要求您包含足够多的注释,以便其他程序员在阅读您的代码时能够意识到程序在做什么。注释还会提醒你你的代码做了什么,因为将来你可能需要更新或维护你的代码。
虽然评论是必要的,但重要的是不要滥用评论和放置太多。就像生活中的任何事情一样,保持平衡很重要,所以使用评论,但不要过度使用。
用 Python 写注释有两种方法。有一个单行注释占据了一行,它是通过使用散列符号来完成的。解释器会忽略放在这个符号后面的任何内容。这是在清单 4-2 中完成的。
# a single line comment
Listing 4-2Single-Line Comment
还有跨越多行的注释,用于注释块。这些使用包含在两组三重引号中的注释。清单 4-3 向我们展示了这些多行注释的样子。
"""
a multiline block comment
That spans multiple lines
"""
Listing 4-3Multiline Comment
每种注释类型都有它们的用途,并且它依赖于您所维护的代码库或者您所工作的团队,这将决定您将使用哪种注释风格。
变量和常数
当编写程序时,我们需要某种方法来存储信息并在以后检索它。我们用变量来做这件事。变量允许我们给一个内存位置指定一个名字,在那个位置存储数据,然后在以后检索它。我们在 Python 中赋予变量的名称必须符合某些参数。有效的 Python 变量可以是数字和字符的组合,也统称为字母数字字符。这意味着在创建 Python 程序时,只能使用字母、数字和下划线。清单 4-4 展示了有效 Python 变量名的例子。
Foo
dove
_topshot
RickyTicky99
Listing 4-4Example Valid Python Variable Names
虽然在变量名中使用数字是有效的,但是不能以数字作为变量名的开头。变量名中也不能有空格,并且不能在变量名中使用关键字(作为语言的一部分保留的词)。清单 4-5 向我们展示了一些无效的变量名。
20Foo
dove bird
$upermaniac
for
Listing 4-5Example Invalid Python Variable Names
为了确保您不使用任何保留的关键字作为变量名,表 4-1 显示了我们不能使用的变量名,按字母顺序排序。
表 4-1
Python 中的保留关键字
| 和 | 艾列弗 | 如果 | 或者 | 产量 | | 如同 | 其他 | 进口 | 及格 | | | 维护 | 除...之外 | 在 | 上升 | | | 破裂 | 最后 | 存在 | 返回 | | | 班级 | 错误的 | 希腊字母的第 11 个 | 真实的 | | | 继续 | 为 | 非局部的 | 尝试 | | | 极好的 | 从 | 没有人 | 随着 | | | 是吗 | 全球的 | 不 | 正在… | |当变量在程序执行过程中不能改变时,我们称这些变量为常量。在 Python 中,我们通常用大写字母来声明变量,并将它们放在 constants.py 文件中。
在程序中使用变量之前,我们必须声明它。清单 4-6 向我们展示了如何声明一个变量。
# declaring a variable
myVar = None
Listing 4-6Declaring a Variable
在我们声明一个变量后,我们可以给它赋值。给变量赋值的过程称为变量的初始化。清单 4-7 展示了我们如何初始化一个变量。
# initializing a variable
myVar = 10
Listing 4-7Initializing a Variable
如清单 4-8 所示,我们可以同时声明和初始化一个变量。
# declaring and initializing a variable
anotherVar = 10
Listing 4-8Declaring and Initializing a Variable
数据类型
变量可以属于 Python 语言中几种数据类型中的一种。变量的类型决定了变量中可以存储什么类型的数据以及它占用多少内存。我们不需要告诉 Python 一个变量是整数(一个数字)还是字符串(比如单词),因为 Python 会自动处理这些赋值。表 4-2 中给出了我们程序中最常用的四种数据类型及其用法。
表 4-2
常见数据类型
|数据类型
|
描述
|
| — | — |
| int(整数) | 这是一个整数或数字,如 10、20、100、4000 |
| 浮动(浮动) | 这是一个浮点数或带小数点的数字,例如 4.5、60.9、300.03、1000.908 |
| 字符串 | 字符串是引号(双引号或单引号)之间的一组字符,例如,“Andy”或“Maggie” |
| 布尔值(布尔值) | 可以表示真或假的数据类型 |
要查看变量的类型,只需使用“type(variableName)”就可以看到关键字的类型。
经营者
Python 提供了执行许多逻辑和数学运算的工具。为了实现这一点,Python 有很多可以使用的操作符。这些运算符可以是算术、关系、逻辑、按位和赋值运算符。表 4-3 列出了一些常见的运算符,并提供了它们的用法示例。这个表并不详尽,但它确实让您了解了可用的内容。
表 4-3
常见 Python 运算符
|操作员
|
类型
|
描述
|
使用
|
| — | — | — | — |
| + | 算术 | 将两个操作数相加 | X + Y |
| - | 算术 | 从第一个操作数中减去第二个操作数 | X - Y |
| * | 算术 | 将两个操作数相乘 | X * Y |
| / | 算术 | 将分子除以分母 | X / Y |
| % | 算术 | 模运算符给出除法运算的余数 | X % Y |
| ** | 算术 | 这是指数运算符,执行幂运算 | X**Y |
| == | 比较 | 如果两个操作数相等,那么 Python 会将条件评估为真 | A==B |
| != | 比较 | 如果两个操作数不相等,Python 会将条件评估为真 | 答!= B |
| > | 比较 | 如果左边的操作数大于右边的操作数,则条件为真 | A > B |
| < | 比较 | 如果左边的操作数小于右边的操作数,则条件为真 | A < B |
列表
任何编程语言的一个重要组成部分是它支持的数据结构的类型。列表是 Python 编程语言的一种基础数据结构。列表存储已排序的项目集合,并且列表可以更改。清单 4-9 向我们展示了 Python 中的一个列表。
# python list
myList = ["boy", 15, 1.2]
Listing 4-9Example List in Python
在 Python 中,列表中的项目被分配一个从 0 开始的索引。我们可以通过使用清单 4-10 中的代码来获得一个列表的索引。
# get index of list item
myList = ["red", "orange", "green"]
print(thislist[1])
Listing 4-10Get Index of List Item
请记住,当我们向前移动时,列表中的元素从零开始。
元组
我们在上一节中了解到,列表可以存储数据类型的集合。但是,有时您需要另一种方法来组织数据。对于这样的场景,我们可以使用元组。
在 Python 中,一个元组结构可以被认为是一个列表;然而,我们不能改变元组中包含的元素。清单 4-11 向我们展示了如何使用元组。
# tuple example
myTuple=("red", "orange", "green")
print(myTuple)
Listing 4-11Python Tuple
我们可以像访问列表一样访问元组中条目的索引。清单 4-12 展示了我们如何做到这一点。
# access tuple item index
myTuple = ("red", "orange", "green")
print(myTuple[0])
Listing 4-12Access Items of Tuple
如果语句
if 语句用于在你的程序中做决定(参见清单 4-13 )。为此,该语句检查布尔表达式是否为真。如果表达式为真,将执行该语句。
if (x > y):
doSomething()
Listing 4-13if Statement
else 语句
else 语句用于补充 if 语句并创建一个决策块。当 if 语句中的布尔语句评估为 false 时,else 语句执行。清单 4-14 向我们展示了 else 语句是如何运行的。
if (x > y):
doSomething()
else:
doSomethingElse()
Listing 4-14else Statement
elif 语句
很多时候,在我们的程序中,我们可能需要在我们的决策块中测试不止两个条件。为了测试几个条件,我们必须使用 elif 语句来测试这些多个条件。清单 4-15 给了我们一个 else if 语句的例子。
if (x > y):
doSomething()
elif (x == y):
doSomethingElse()
else:
doTheOtherThing()
Listing 4-15else if Statement
有几分地
有时,如果 if 语句足够短,并且您只有一条语句要执行,您可以将它放在一行中,如清单 4-16 所示。
# single line if statement
if x == y: print("its equal")
Listing 4-16One-Line if
for 循环
有时,我们需要执行一段代码指定的次数。为了帮助实现这一点,我们使用了一个与范围函数相结合的 for 循环。清单 4-17 给出了一个使用范围函数的 for 循环的例子。
# print 0 to 9
for x in range(10):
print(x)
Listing 4-17for Loop
请记住,Python 从 0 开始计数,因此当前面的语句运行时,它将在控制台中打印从 0 到 9 的数字,而不是 10。
while 循环
虽然带有 range 函数的 for 循环允许我们运行一个代码块一定的次数,但有时我们不确定我们需要执行一个代码块多少次。在这种情况下,我们使用一个 while 循环,该循环在为循环执行指定的条件保持为真的情况下执行。这是本书中许多程序的基础。清单 4-18 向我们展示了 while 循环的样子。
while (True):
runForever()
Listing 4-18while Loop
while 循环在许多嵌入式应用程序中用于保持程序无限期运行,当它这样做时,我们称之为超级循环。
功能
有时,在您的 Python 程序中,您可能会发现自己不得不一遍又一遍地运行相同的代码块。Python 中有一个名为 function 的特性,可以让您调用自己编写的代码块并返回一个值,而不是多次键入同一个代码块。这些函数可以接受输入的参数,然后对它们做一些事情。如果你需要从一个函数中返回值,你可以使用 return 语句。
例如,让我们编写一个函数,它将接受两个变量并对其中一个进行乘方,然后调用它三次来演示这是如何工作的,如清单 4-19 所示。
def addVar(a, b):
print (a**b)
addVar(3, 6)
addVar(2, 8)
addVar(1, 9)
Listing 4-19Functions
λ函数
有时,在我们的程序中,我们可以创建一个 lambda 函数。Lambda 函数允许我们编写不需要命名的小函数。清单 4-20 向我们展示了如何用一个小的 lambda 函数替换前面的函数。
x = lambda a, b : a**b
print (x(2, 5))
Listing 4-20Lambda Functions
异常处理
有时,我们的部分代码不能按预期工作,尽管它可能遵循程序的所有语法规则,但 Python 解释器执行的语句可能仍然无效。例如,如果我们试图打印一个不存在的变量,尽管用 print 函数打印一个变量在语法上是正确的,但由于变量尚不存在,我们会得到一个错误。当我们“尝试”执行语句失败时,程序不会崩溃,我们可以通过将代码块放在 except 语句下来做其他事情。清单 4-21 向我们展示了异常处理是如何工作的。
try:
print(undefVar)
except:
print("Causes Exception")
Listing 4-21Exception Handling
面向对象编程
Python 是众所周知的面向对象编程语言。这意味着我们可以创建一些特殊的代码,作为我们创建其他代码的蓝图。我们称这个特殊的蓝图代码为类。这个蓝图的正式名称是对象构造函数,我们用它来创建类的实例。我们也可以拥有存在于一个类中的函数,当我们这样做时,它们被称为方法。
在 Python 中,类中有一个特殊的方法,每次我们使用它来创建一个名为 init()方法的对象时,都会调用这个方法。在 Python 中,我们可以在用“self”调用之前修改类的实例;使用 self,我们可以访问 Python 类的属性和方法,这些属性和方法将在对象初始化后对其执行。
一个对象是一个类的实例,它从这个类中获取它的属性,并用来模拟现实世界中的事物。想想一辆车;两辆汽车可能有相同的品牌和型号,但颜色不同。除了颜色属性不同之外,它们在功能上是相同的。在 Python 中,也是这样——有时,我们需要相同的对象,但需要对其中一个进行稍微不同的修改;在这种情况下,我们将创建一个类,然后我们可以分别修改该类的两个实例。清单 4-22 向我们展示了 Python 中的类是什么样的。
# create the car class
class AwesomeCar:
# our special method that lets us
# initialize attributes of the class
def __init__(self, color):
# these remain the same
self.make = "Tayaba"
self.model = "Nimbus"
# the colors change
self.color = color
# create instance of our car class
Car1 = AwesomeCar("Red")
# create another instance of our car class
Car2 = AwesomeCar("Blue")
# print car attributes
# prints {'make': 'Tayaba', 'model': 'Nimbus', 'color': 'Red'}
print(Car1.__dict__)
# print car attributes
# prints {'make': 'Tayaba', 'model': 'Nimbus', 'color': 'Blue'}
print(Car2.__dict__)
Listing 4-22Python Class
我们可以用(dict)打印该类的所有属性,该属性本身就是对象拥有的属性,包含为对象本身定义的所有属性。
在 Python 中,我们将在整本书中使用许多类及其方法;《objects》中的这一小段碰撞部分足以让你看完这本书的其余部分。
随机和时间
有时,我们需要生成随机数,因此,Python 提供了一个名为 random 模块的模块来实现这一点。我们还有一个模块叫做睡眠模块,它允许我们的代码等待一段时间。清单 4-23 展示了我们如何一起使用这两个模块。
# our random module
import random
# the time module
import time
# super loop
while (True):
# create a random number between 1 and 10
x = random.randint(1, 10)
# print the number
print(x)
# once per second
time.sleep(1)
Listing 4-23Using Random and Time
Python 诉 circuitpython 案
使用最广泛的 Python 发行版是我们所知的 CPython。CPython 是 Python 编程语言的“黄金标准”实现,因为它被称为该语言的参考实现。CircuitPython 旨在兼容 CPython 然而,由于微控制器内存不足的明显原因以及为了便于使用,一些在 CPython 中可用的库可能在 CircuitPython 中不可用。然而,来自核心语言的大多数东西都可以工作,用 CircuitPython 编写的代码在 CPython 中是有效的,但反过来就不一定了。然而,核心语言就在那里,你可以用 CircuitPython 编写许多令人惊叹的程序。
我的 Python 程序如何运行?
如果您不熟悉微控制器和编程,您可能会想知道 Python 程序是如何从您的编辑器(如 Mu)进入微控制器执行的。因此,在这一节中,我给出了运行 Python 程序时会发生什么的高级概述。
Python 是一种被称为解释语言的语言,这意味着当程序运行时,它获取我们源代码中的每条语句,并将其转换为我们的微控制器可以理解的机器语言。这与编译器不同,编译器在程序运行之前将编写的代码转换成机器代码。
Python 程序以三种状态运行,它们是
-
初始化
-
汇编
-
执行
在我们开始研究 Python 程序是如何运行的之前,我们必须记住 Python 解释器是用 C 编写的,因此它仅仅是一个用 C 代码运行的程序。在初始阶段,当我们编写程序并将其加载到 C 程序中时,程序会寻找 Py_main 程序,这是主程序中的主要高级函数。然后调用处理程序初始化等的其他支持函数。
在这个阶段之后,我们进入编译阶段,在这个阶段,称为解析树生成和抽象语法树(AST)生成的事情发生,以帮助生成字节码。这些字节码指令不是机器码;相反,它们独立于我们正在使用的平台。然后字节码被优化,我们得到了需要运行的目标代码。在 CircuitPython 中,我们可以选择在程序运行之前将代码转换成字节码,这样的文件会被赋予一个“.”。mpy”扩展名。这些文件可以像普通文件一样导入,并且更紧凑,从而更有效地使用内存。
最后,我们的程序在 Python 虚拟机(PVM)中执行。虚拟机是运行这些字节码指令的解释程序中的一个功能。它只是一个大的超级循环,一个接一个地读取我们的字节码指令并执行它们。
结论
在这一章中,我们简要地看了一下 Python 和这种语言的一些特性。一旦你阅读了这一章,你应该对 Python 编程语言有一个基本的了解。编程是一件需要你长时间学习的事情,因此我们写代码是很重要的。只有通过编写代码,你才能学会你正在使用的语言,因此,在下一章,我们将看看如何使用 Python 来控制我们的微控制器硬件。
五、数字控制
至此,我们已经了解了足够多的电子学、嵌入式系统架构和编程知识,可以开始使用物理微控制器器件了。现在,我们将学习毫无疑问的最重要的操作的基础:输入和输出。在每一个微控制器系统的核心,你至少会有一个输出被执行,许多系统也接受输入。
在本章中,您将开始了解如何控制和操作物理传感器和执行器。操纵物理 I/O 设备有一种满足感,这是在屏幕上推动像素无法比拟的。我希望这一章将向你介绍使用硬件的乐趣,并激起你进一步尝试微控制器开发的欲望。
I/O 引脚
微控制器设备附有物理引脚。这些引脚有一个有价值的用途:它们允许微控制器设备与外界接口。正如我们之前所讨论的,微控制器系统通常接受某种输入,执行某种处理,然后提供一个输出来操作某种外部外设或设备。这是输入输出控制的前提。
一般来说,你可以从一个微控制器控制的输入和输出引脚的数量上了解它。微控制器上更多的引脚意味着设备通常会包含更多的外设和强大的 CPU 内核。微控制器通常至少有六个 I/O 引脚。有时,他们可以有数百个。
过去,微控制器通常采用 DIP 封装。在使用它之前,你需要将它连接到一个编程器上,并设置好正确的连接和电源配置。虽然这在专业环境中可能是好的,但对于制造商和刚刚开始使用微控制器的人来说,这太复杂了。开发板的出现简化了爱好者的过程。该板带有一个处于已知工作状态的微控制器,允许用户立即专注于构建电路和连接外部设备,并专注于编写软件。
微控制器硬件上的输出和输入
在底层硬件上,微控制器通常使用所谓的端口来执行输出。在其最简单的形式中,端口可以被认为是允许微控制器与外界接口的一组引脚。
端口通常排列成引脚组;典型地,大约 8 或 16 个引脚构成一个端口,并且这可以高达 32 个引脚。您可能想知道为什么我们首先需要在端口中分组微控制器引脚。你必须知道,在微控制器架构中,端口只不过是寄存器。因此,端口中的引脚数量与微控制器的架构之间存在一定的相关性。因此,8 位微控制器的端口通常有 8 个引脚,16 位微控制器的端口有 16 个引脚。然而,这并不是一成不变的;例如,您可能有一个 32 位微控制器,它的端口为 16 位宽(有 16 个引脚),甚至一个端口只有 4 个引脚。作为微控制器设备的用户,您可以简单地将此视为制造商的设计决策。您还应该知道,由于端口是一个寄存器,该端口上的每个引脚都是寄存器中的一个位。端口有一个名称,如 PORTA 或 PORTB,每个端口内的引脚都有编号。假设我们有一个假设的微控制器,它有两个端口,PORTA 和 PORTB,如图 5-1 所示。
图 5-1
一个假想的有两个端口的微控制器
PORTA 上的所有引脚都被命名为“AX”,其中“X”是与该端口相关的一个位。例如,A1 引脚指 PORTA 的第一位。像“A1”这样的缩写名称便于识别和标记,当您为微控制器编写代码时,如果需要访问某个特定的引脚,它们也会有所帮助。
图 5-1 中 PORTB 上的引脚遵循与 PORTA 不同的命名惯例:“PBX”而非“BX”不同的制造商遵循不同的端口引脚命名惯例。有些人会用“A1”和“D1”这样的名字,而有些人会用“PA1”和“PD1”这样的名字只有命名约定发生了变化。针本身的工作方式是一样的。实际上,微控制器的管脚号从 0 开始是很常见的。因此,PORTA 中的第一个引脚通常被称为 A0 或 PA0。
为简单起见,微控制器上的每个引脚最好都有一个专用功能。例如,我们可以想象我们假设的设备上的 PORTA 是一个输出端口,这意味着端口上的引脚只能将数据带出微控制器。类似地,我们可以想象 PORTB 是一个仅输入端口,只能接受来自外部世界的数据和进入微控制器的数据。
然而,如果我们有一个微控制器必须与许多输出设备接口的应用,比如控制 15 盏灯,会发生什么呢?或者我们可以让微控制器控制一个有 12 个输入的键盘?在这两种情况下,如果引脚仅用于一个目的,我们可用于输入或输出的引脚数量将会受到限制。因此,通常根据电路设计者的判断将微控制器上的引脚配置为输入或输出。
为此,微控制器内部必须有某种机制,允许端口上的引脚用作输入或输出。因此,在许多微控制器中,通常有一个寄存器可以控制数据进出引脚的方向。该寄存器通过软件编程,并由微控制器编程器控制。
当以这种方式使用时,端口可以简单地配置为输入或输出,端口引脚处于数字状态。当处于数字状态时,引脚可以多种方式配置;通常,我们说它有三种状态。作为输出,它可以配置为输出高电平,此时引脚为输出,向负载提供电流。该引脚也可以是输出低电平,此时该引脚仍配置为输出引脚,但不向负载提供电流。该引脚也可以配置为输入。
不仅可以控制引脚方向的内部电路很常见,而且很多时候微控制器还有其他内部外设和模块也依赖这些引脚来执行输入和输出功能。这些包括像串行接口,使用内部定时器或模拟功能。这些被称为引脚的替代功能,因制造商而异。
这是必要的,因为虽然大多数微控制器包括许多功能和板载外设,但大多数应用在任何时候都只需要使用其中的一两个外设。因此,为了确保微控制器上的每个外设都不需要引脚,这种内部电路将确保板载功能可以在有限的 I/O 引脚之间共享(还记得我们之前谈到的多路复用器吗?这是微控制器内设备的应用)。因此,通常会看到微控制器更像图 5-2 中的图。
图 5-2
更现实的微控制器
图 5-2 中的微控制器更准确地描述了微控制器的样子,考虑到大多数引脚都包含替代功能。如您所见,有多个端口:PORTA、PORTB、PORTC 和 PORTD。这些端口中的每一个都有各种功能的引脚。所有微控制器上的 VDD 引脚和 VSS 引脚都不与任何 I/O 端口相关联,因为它们为器件供电。一个有经验的设计师将能够看到一个类似的图表,并意识到哪些引脚用于何种目的。例如,我们知道引脚 PA0 最有可能用于数字输入和输出。然而,标签 AN0 也贴在销上。这意味着该引脚也可以用于模拟输入功能。第三个标签 INT0 表示我们也可以将该引脚用于外部中断功能。
随着您获得更多设计经验并更加熟悉不同的微控制器系列,您将能够查看电路图上的端口,并知道哪些引脚具有哪些替代功能。即使有这样的经验,你也不能总是说出一枚别针的用途。例如,看看针 PC3。得益于替代的 AN3 标签,你可能认为它可以用于数字和模拟功能,但 CMP 标签是什么意思呢?这些问题最好通过您所用器件的数据手册来回答。例如,数据手册可能会显示此引脚仅用于输入,不能用于任何数字输出功能。请务必准备一份器件数据手册,以回答此类问题。
深入了解微控制器 I/O
您可能想知道微控制器如何使用引脚执行输入和输出。虽然其工作原理背后的数字电子器件可能会变得非常复杂,但我们可以通过一些基本的电路结构来了解最底层发生了什么。
对于输出引脚,参见图 5-3 中的电路图。
图 5-3
MCU 输出电路
在这种情况下,我们有一个触发器或其他受 CPU 控制的锁存电路。你还记得我们谈过人字拖吗?这是触发器在微控制器中的一个应用。记得我们说过触发器一次只能存储一位数据。嗯,当我们的引脚设置为输出时,我们可以输出高值(逻辑 1)或输出低值(逻辑 0)。触发器用于存储我们想要的位(高或低)。锁存电路的输出通过缓冲器。该缓冲电路允许更大的驱动强度,并为微控制器内部器件提供更好的保护,防止器件外部的电气干扰。这里也没有显示保护二极管,它们与缓冲器一起保护微控制器。然后,缓冲电路的输出连接到微控制器上的物理引脚。
同样,我们可以通过查看图 5-4 中的电路图来了解微控制器上的输入是如何工作的。
图 5-4
MCU 输入电路
这种情况下,该引脚连接到具有使能功能的缓冲器。当 CPU 操纵缓冲器的使能引脚时(当然,这是基于程序代码中的指令),可以读取引脚的电压电平,然后 CPU 可以将该信息用于任何目的。
当然,在真实的 MCU 中,电路可能会比这更复杂,因为微控制器上的引脚必须同时充当输入和输出。这称为双向控制,它允许微控制器使用单个引脚进行输入和输出。
当使用微控制器 I/O 引脚时,我们对输入和输出电路的观察使我们想到了另一个考虑因素:微控制器端口的吸电流或源电流能力。源电流是指微控制器向负载提供电流的能力。也就是说,电流将从微控制器引脚流过负载,然后流向地。图 5-5 描绘了源电流的样子。
图 5-5
MCU 源电流
微控制器 I/O 引脚也可以吸收电流。这种情况下,电流从电源流出,经过负载,流入 I/O 引脚。图 5-6 显示了吸电流的样子。
图 5-6
MCU 吸电流
使用输出:点亮 LED
现在我们对微控制器端口有了一些了解,我们可以看看如何使用输出来执行功能:在本例中,点亮 LED。很像打印“你好,世界!”是学习新编码语言的传统第一步,操作 LED 是使用新微控制器的传统第一步。它允许您验证您有一个工作程序,并且您的所有连接都是准确的。在本节中,我们将探讨如何构建一个物理电路来连接微控制器和 LED。然后我们将介绍如何使用 CircuitPython 来控制微控制器的输出并使 LED 亮起。
LED 控制
回想一下第二章,led 是一种特殊的二极管,当电流通过时会发光。为了驱动 LED,你需要电压流入 LED。虽然微控制器可以向 LED 提供稳定的电压,但也必须调节电流以避免损坏它,因为 LED 具有最大额定电流,您不得超过该电流。要调节流经 LED 的电流,可以使用一个电阻。
使用 LED 还会产生电压降。例如,典型的红色 LED 将下降大约 2 伏。然而,不同的 LED 颜色会降低不同的电压,并且电压降也会因制造商而异。最好查阅所用器件的数据手册,了解 LED 的具体规格。
知道了电压降,就可以计算出流经 LED 的输出电流。如果微控制器输出 5 伏电压,那么 LED 产生 2 伏电压降,串联电阻上的电压将为 3 伏。如果您希望流经 LED 的最大电流为 10 mA(您可以在数据手册中找到实际数字),那么您可以使用欧姆定律来计算所需的电阻。
用欧姆定律,电压除以电流。这将是 3 V / 0.01 A,产生 300 欧姆,这是保护 LED 所需的最小电阻。在这种情况下,470 欧姆的电阻是一个不错的选择。请记住,每个电阻都有一个容差水平,可以使实际电阻值小于预期值,因此使用您计算的精确电阻值是有风险的。如果你想接近最大亮度,那么 330 欧姆的电阻就足够了。
构建电路
完成计算后,您就可以设计一个电路,让微控制器控制一个 LED。该电路将类似于图 5-7 所示的原理图。
图 5-7
MCU 电路 LED
我们还使用一个 1k 电阻来限制从器件流出的电流量;请记住,使用较高阻值的电阻没有问题,如果您想要更高的电源效率,这是可取的,因为较高的电阻会消耗较少的电流。
您可以按照以下步骤来设置这个简单的电路。确保在给设备加电之前连接好电路。
-
将微控制器连接到电源,确保微控制器正常供电;如果您使用的是开发板,那么您的 USB 连接将足以为设备供电。如果您希望连接外部电源,请将电源的 Vout 连接到主板的 Vin 输入端(电源输出电压的值通常为 7.5 至 12;这取决于您使用的电路板,因此请务必查阅电路板的用户手册以了解输入电压范围),并将地连接到微控制器电路板的接地引脚。
-
将一根跳线从微控制器上的 A1 引脚连接到试验板原型制作区域的一个插座上。
-
将电阻的一根引线连接到步骤 2 中使用的试验板同一行中的另一个插座。
-
将电阻的另一端连接到试验板另一排的插座上。
-
将 LED 的长引线连接到步骤 4 中使用的试验板同一行中的另一个插座。
-
将 LED 的短引线连接到试验板另一排的插座上。
-
将跳线连接到步骤 6 中使用的试验板同一行中的不同插座。
-
将跨接导线的另一端接地。
当你完成连接电路时,它看起来应该类似于图 5-8 。
图 5-8
试验板上的 MCU LED 电路
用 CircuitPython 点亮 LED
既然我们已经介绍了构建物理 LED 电路,我们可以看看如何使用 CircuitPython 来控制 LED。我们将要编写的程序将打开 LED。打开 Mu 编辑器,将程序输入到您的“code.py”文件中,如清单 5-1 所示。
# import pin constants for board we are using
1 import board
# import pin control
2 import digitalio
# create object for pin we are using
3 led = digitalio.DigitalInOut(board.A1)
# set the pin to output
4 led.direction = digitalio.Direction.OUTPUT
# turn the LED on
5 while True:
led.value = True
Listing 5-1Controlling the LED
在这段代码中,我们做的第一件事是导入控制电路板所需的 CircuitPython 库。CircuitPython 提供了许多库来执行数字 I/O。在 1 ,我们导入了board
模块,该模块包含所用电路板的引脚常数。导入该模块后,我们可以通过名称来引用管脚,例如,A1
。在 2 ,我们导入digitalio
模块。它包含了所有的类,这些类提供了对我们将要使用的输入和输出函数的访问。此外,该模块允许我们设置引脚方向,以确定我们是将数据读入引脚还是将数据写入引脚。
导入必要的库后,在 3 我们创建一个正在使用的 pin 对象的实例。这个实例在内存中有自己的位置,这样做允许我们访问我们导入的“digitalio”模块中存在的所有方法。这是普通的 Python 语法,向我们展示了我们常规的 Python 能力可以在 CircuitPython 程序中使用。然后,我们使用该实例来设置 LED 在 4 处的方向。在这种情况下,我们将方向设置为输出。
至此,一切都设置好了,所以我们现在可以打开 LED 了。在 5 ,我们创建了一个在嵌入式系统世界中通常被称为超级循环的东西。任何属于while True
语句的指令都将被反复执行,直到程序被告知停止。在这种情况下,我们将led
对象的value
设置为True
,这意味着 LED 将会亮起。
我们使用超级循环是因为没有操作系统为我们做任何控制。while True
线确保我们一遍又一遍地执行我们的 LED on 行动。如果你省略了这一行,那么 LED 将在短时间内点亮一次(短到我们看不见)。几乎所有你为你的微控制器编写的程序都会包含这样一个超级循环。
点击管理部门编辑器中的保存按钮;这应该会将更改保存到“code.py”文件中,您将会看到连接到引脚 A1 的 LED 已经亮起。既然您已经成功控制了数字输出,请随意尝试一下。你能让 LED 受 A0 脚控制吗?还是 pin A2?要做到这一点,您只需将您的电路重新连接到不同的引脚,并在我们创建引脚对象时更改引脚号。
如果你的电路不工作,确保并测试你的连接;我的意思是确保你的电线被正确地插入指定的孔中。检查,检查,再检查。
LED 闪烁
打开 LED 灯很有趣,但有点无聊。如果我们使用电阻将 LED 直接连接到电源,我们会有相同的效果。智能控制的妙处在于我们可以智能地做事。
例如,您可以对您的微控制器进行编程,使其不仅开启 LED,还能以您选择的时间间隔开启 LED。这个程序可以让你验证你的时钟设置是否正确,因为你可以看到 LED 闪烁的频率。如果 LED 以不正确的频率闪烁,您需要确保选择了正确的主板,或者可能需要检查您的 USB 和电路连接。您已经建立了物理电路。您所需要做的就是用几行代码更新您的代码。您可以使用清单 5-2 中给出的程序。
# import pin constants for board we are using
import board
# import pin control
import digitalio
1 # import time library
import time
# create object for pin we are using
led = digitalio.DigitalInOut(board.A1)
# set the pin to output
led.direction = digitalio.Direction.OUTPUT
# super loop
while True:
# turn the LED on
2 led.value = True
# sleep for a second
3 time.sleep(1)
# turn the LED off
4 led.value = False
# sleep for a second
5 time.sleep(1)
Listing 5-2Blinking the LED
在这个程序中,除了导入board
和digitalio
模块,正如我们在清单 5-1 中所做的,我们还在 1 导入了time
库。该库包含允许微控制器遵循时间相关指令的函数。特别是,我们将使用time
库的sleep
方法。它告诉微控制器等待一定的时间,以秒为单位。
在启动了清单 5-1 中的led
pin 对象后,我们进入一个带有while True
语句的超级循环,就像之前一样。这一次,循环包含了更多的指令。在 2 ,我们打开 LED,就像清单 5-1 中一样。在 3 ,我们通过在sleep
方法中传递 1 秒的值来告诉微控制器等待。在 4 ,我们通过将led
对象的value
设置为False
来关闭 LED。然后我们在 5 再次使用sleep
再延迟一秒钟,然后重新开始循环。
点击 Mu 编辑器中的“Save”按钮,您将观察到连接到引脚 A1 的 LED 每秒闪烁一次。如果您想让 LED 闪烁得更快,您可以向sleep
方法传递一个更小的值,比如 0.5 秒。如果您希望 LED 运行得更慢,您可以使用更长的值,如 2 秒。
使用输入:添加按钮
下次我们与我们的评估板合作时,我们将关注数字输入。为此,我们将在电路中添加一个按钮。在微控制器上,输入是输出的补充,它们共同构成了两个最基本的微控制器操作。如果使用 LED 就像嵌入式开发中的“Hello World”允许你在屏幕上打印一些东西,那么阅读按钮就相当于学习从键盘上阅读输入。我们的输入项目将读取一个引脚上按钮的状态,然后根据读取的状态,我们将切换连接到另一个 I/O 引脚的 LED。
上拉电阻与下拉电阻
在我们开始构建电路之前,需要注意的是,按钮与微控制器引脚有不同的连接方式。可以使用上拉或下拉电路配置来实现。上拉配置的原理图如图 5-9 所示。
图 5-9
上拉配置
在上拉配置中,我们使用一个高阻值电阻(通常在 4.7 到 10k 之间)将按钮的一端拉至 VDD(也就是说,通过电阻将 I/O 引脚连接至 VDD)。该电阻器确保当按钮被按下时,电路中几乎没有电流流出。
图 5-10 显示了下拉配置中按钮的示意图。
图 5-10
下拉配置
上拉电阻将引脚上拉至高值,而下拉电阻将引脚下拉至逻辑低值,也就是说,我们通过该电阻将 I/O 引脚接地。要在下拉配置中连接开关,开关必须置于电源电压和微控制器引脚之间。
使用上拉配置时,电路将为低电平有效,而使用下拉配置时,电路将为高电平有效。一般来说,您将使用带有上拉电阻的按钮,大多数微控制器在其引脚上提供上拉电阻。然而,这两种配置都可以工作。根据您的电路应用,这种或那种方法可能更好。
在这一部分的后面,我们将讨论如何使用具有上拉和下拉电路配置的微控制器。不过,对于本书中的其它电路,我将只使用上拉配置。如果您愿意,可以按照如下所示的下拉模型修改本书中的任何电路,以使用下拉配置,而不会对电路工作产生任何不利影响。
开关去抖
使用一种开关时,处理微控制器输入时需要考虑的另一个方面是开关去抖。没有一个设备是完美的,一个设备的活动部件越多,与之相关的问题就越多。当机械按钮开关必须连接到微控制器电路时,也是如此。
由于其结构,当您按下按钮开关时,微控制器可能会记录开关的多次按下。这种效果类似于你丢球时发生的情况。球不会立即停下来,而是弹起一段时间。同样,当你按下一个开关时,它没有一个固定的闭合。当开关闭合时,会发生一系列的打开和闭合。
由于这种反弹效应,微控制器有可能会在开关应该闭合时将其记录为断开。为了解决这个问题,您可以实施一种称为开关去抖的技术。这可以使用硬件或软件来完成。
对于硬件去抖,我们可以使用触发器(latch),如图 5-11 所示。触发器的作用相当于一个去抖开关,因为一旦 Q 被输入设置为高电平状态,Q 的输出端将不再发生变化,只要状态发生变化,触发器就会锁存输入值。还有其他硬件去抖方法,例如使用施密特触发器;然而,触发器方法简单而有效。
图 5-11
触发器去反跳
除了添加硬件,您还可以在代码中构建一个去抖动效果。有几种软件去抖方法,包括使用计数器和软件移位寄存器。在本节的后面,我们将使用一个简单的软件延迟来使按钮开关去抖。
带 MCU 原理图的输入(上拉)
我们可以通过构建如图 5-12 所示的程序来构建一个简单的按钮电路。在本电路中,我们使用上拉配置的开关。
图 5-12
带上拉开关的 MCU
按钮上拉电路连接提示
这个电路很简单,很难搞砸。如果您的电路不工作,请确保并测试您的连接。检查,检查,再检查。确保在给设备加电之前连接好电路。
以下是连接电路的推荐步骤:
-
按照示意图,按照我们在上一节中所做的那样连接 LED。为方便起见,您可能需要将一根跳线从开发板上的 3.3v 电压输出插座连接到正极供电轨,另一根跳线从接地连接连接到接地供电轨。为了简化布局并更好地利用电路板,可以将两个正供电轨和两个负供电轨连接在一起。
-
将开关放置在电路板中央的凹槽上,确保开关两侧的两个引脚都插入到试验板原型制作区域的插孔中。
-
将开关的一端连接到同一插座中电阻的一端,将电阻的另一端连接到正极供电轨。不需要使用跳线,因为电阻器可以直接连接到 LED。
-
使用跨接导线将开关的另一端接地。
-
从开关和电阻器之间的交叉点连接一根跳线,连接到 MCU 上的 A5 引脚。为此,确保开关引线、电阻器引线和跨接导线都在同一插孔内。
当你完成连接电路时,它应该看起来如图 5-13 所示。
图 5-13
试验板上的 MCU 上拉电路
用 CircuitPython 程序上拉按钮
连接好电路后,我们可以编写一个程序,让 MCU 在上拉电路配置中使用按钮。编辑 code.py 文件,使其类似于清单 5-3 。
# import pin constants for board we are using
import board
# import pin control
import digitalio
# import time library
import time
# create object for LED we are using
1 led = digitalio.DigitalInOut(board.A1)
# create object for the switch we are using
switch = digitalio.DigitalInOut(board.A5)
# set the LED to output
led.direction = digitalio.Direction.OUTPUT
# set the switch to input
switch.direction = digitalio.Direction.INPUT
# super loop
2 while True:
# if our pin goes low
if switch.value == False:
# wait 100 ms to see if switch is still on
3 time.sleep(0.1)
# if the switch is still on
4 if switch.value == False:
# turn the LED on 5
led.value = True
else:
# turn the LED off 6
led.value = False
Listing 5-3Reading a Pushbutton with MCU Pull-Up
保存文件,观察会发生什么。当您按下按钮时,LED 将亮起,当您松开按钮时,LED 将停止亮起。
你知道这里发生了什么吗?让我们看一下这个程序。我们做我们通常的导入,并为我们在 1 使用的 LED 和开关设置对象。一旦我们有了这些设置,在 1 之后,我们将 LED 的方向设置为输出,将开关的方向设置为输入。我们的超级循环就是一切发生的地方。
在 2 的超级循环中,我们使用条件 if/else 语句来测试我们的程序,看看按钮是否被按下。如果按钮被按下,那么我们在 3 等待 100 毫秒。如果 100 毫秒过去后,按钮在 4 仍被按下,那么我们在 5 打开 LED。为了完成这个任务,我们使用一个嵌套的 if 语句。检查按钮,等待,然后再次检查按钮,这就是我们所说的开关去抖。在 6 ,我们关闭 LED,这是程序的默认状态。
当我们看条件,我们看到,如果按钮是低,然后我们打开 LED。如果您对此感到困惑,请记住,上拉电阻会将引脚设置为默认高电平状态,因此我们的 MCU 会不断读取引脚上的高电平状态。本质上,由于我们有一个连接到引脚的上拉电阻,因此引脚将保持高电平,直到 3.3 伏的 VCC。当我们按下按钮时,针脚会被拉低至地面。我们也可以在下拉状态下使用按钮,我们将在下一节学习。
带 MCU 原理图的输入(下拉)
我们可以通过构建如图 5-14 所示的程序来构建一个简单的按钮电路。在本电路中,我们使用下拉配置的开关。
图 5-14
带下拉开关的 MCU
按钮下拉电路连接提示
如果您的电路不工作,请确保并测试您的连接。检查,检查,再检查。确保在给设备加电之前连接好电路。
以下是连接电路的推荐步骤:
-
按照我们在上一节中所做的那样连接 LED。为方便起见,您可能需要将一根跳线从开发板上的 3.3v 电压输出插座连接到正极供电轨,另一根跳线从接地连接连接到接地供电轨。为了简化布局并更好地利用电路板,可以将两个正供电轨和两个负供电轨连接在一起。
-
将开关放置在电路板中央的凹槽上,确保开关两侧的两个引脚都插入到试验板原型制作区域的插孔中。
-
将开关的一端连接到电阻器的一端,确保它们在试验板上的同一个插座中,并将电阻器的另一端连接到接地轨;这里不需要使用跳线,因为电阻足够长。
-
使用跨接线将开关的另一端连接到正极供电轨。
-
从开关和电阻器之间的交叉点连接一根跳线,连接到 MCU 上的 A5 引脚。
当你完成连接电路时,它应该看起来如图 5-15 所示。
图 5-15
试验板上的 MCU 下拉电路
用 CircuitPython 程序进行按钮下拉
连接好电路后,我们可以编写一个程序,让 MCU 在上拉电路配置中使用按钮。一旦你完成了你的电路,你就可以连接你的电路板并编辑 code.py 文件,使其类似于清单 5-4 。
# import pin constants for board we are using
import board
# import pin control
import digitalio
# import time library
import time
# create object for LED we are using
led = digitalio.DigitalInOut(board.A1)
# create object for the switch we are using
switch = digitalio.DigitalInOut(board.A5)
# set the LED to output
led.direction = digitalio.Direction.OUTPUT
# set the switch to input
switch.direction = digitalio.Direction.INPUT
# super loop
while True:
# if our pin goes high
1 if switch.value == True:
# wait 100 ms to see if switch is still on
2 time.sleep(0.1)
# if the switch is still on
3if switch.value == True:
# turn the LED on
4led.value = True
else:
# turn the LED off
led.value = False
Listing 5-4Reading a Pushbutton with MCU Pull-Down
保存文件,观察会发生什么。您将获得与使用连接有按钮的 MCU 作为上拉电阻时相同的效果。当您按下按钮时,LED 将亮起,当您松开按钮时,LED 将停止亮起。
您将看到的唯一变化是检查按钮的条件语句。当我们在 1 处查看条件时,我们看到如果按钮为高,我们在 2 处等待 100 毫秒,然后在 3 处,一旦开关仍被按下,然后在 4 处,我们打开 LED。我们必须记住,由于我们有一个下拉电阻连接到引脚,因此引脚将保持低电平至地。当我们按下按钮时,插脚将从电源获得电流。
您可以尝试使用不同的引脚来连接按钮。不要害怕实验,不断尝试新事物。记住这是你学习的唯一途径;尝试交换引脚,尝试将您的“真”条件更改为假,并观察它将产生的效果(LED 将一直亮着,直到您按下按钮,然后它将熄灭)。这就是你学习如何使用你的微控制器所需要做的。
结论
在本章中,我们讨论了许多与微控制器的输入和输出相关的信息。我们学习了微控制器端口以及如何将它们用作输入和输出。我们讨论了如何使用输出来控制 led,以及如何使用输入来控制上拉和下拉状态下的按钮。下一章,我们将扩展到数字信号之外,看看如何执行模拟输入和输出,并利用它与模拟器件接口。