用于科研的-Arduino-教程-全-
用于科研的 Arduino 教程(全)
原文:Arduino in Science
协议:CC BY-NC-SA 4.0
一、LED 照明的按钮控制
本章中的练习实际上是最简单的计算机控制形式之一,通过单击主系统显示屏上的按钮图标或运行几行计算机代码来打开和关闭 LED 设备。图形用户界面(GUI)是显示屏,包含按钮图标、滑动控件、仪表、数字显示、图形带状图记录器显示和其他符号,主动/被动和基于文本,可用于监测和控制当前过程。单击屏幕按钮可以打开和关闭 LED,系统的状态由 LED 是否亮起来直观地确定。作为 SCADA 软件与 HMI 和试验板电子设备接口的初始练习,演示了从显示屏或键盘控制简单电子电路供电的能力。
为了将在基于“视窗”的计算机操作系统上运行的面向视觉的数字软件连接到位于工作台上或嵌入在实验环境中的“插入式”快速原型“试验板”,需要数字电气连接。用于将外围设备连接到主计算机的 USB 电缆可以用作将主计算机连接到机器接口的电信号传输线。
机器接口连接可以是多种 USB 兼容的可编程硬件设备中的任何一种,能够接收数字代码的输入,解释或识别代码的意图,并产生所需的数字输出信号。
在图 1-1 中,显示了能够提供所需输入解释并产生适当输出信号的数据采集或 HMI 设备的选择。
项目 1 和 3 来自美国科罗拉多州莱克伍德的 LabJack 公司。该公司生产大约 12 种与 USB、以太网和 Wi-Fi 系统兼容的多功能数据采集(DAQ)设备。LabJacks 是坚固耐用的设备,旨在用于艰苦的工业和实验室应用,带有沉重的塑料保护壳和大型螺丝端子接线连接。所描述的两种设备是成本最低的 U3-HV(115 美元;参见 LabJack 文献,了解所有可用的附加功能和特性)和原始 LabJack 多功能 DAQ 设备(160 美元)。
第 2 项是 Digilent Inc .的 chipKIT Uno32(已退役)Arduino 兼容微控制器。图示设备已被 chipKIT uC32,3.3 伏 Arduino 兼容微控制器(42 CDN)所取代。第 4 项和第 5 项是 SparkFun Inc .的廉价量产 SMT Arduino 兼容微控制器(20-30 美元)。
所示的每个设备都能够接收单个数字开/关信号或编码指令,并产生所需的输出。
图 1-1
一些人机界面设备
微控制器目前由十几家公司制造,具有各种各样的功能和各种各样的成本。
实验的
驻留在外部独立供电的原型板上的 LED 的原始控制屏幕按钮照明是在 2007 年使用 LabJack U12 创建和写入的,如图 1-1 第 3 项所示。许多年后,随着低成本微控制器板的出现,图 1-1 中的项目 2、4 和 5 可用于实现相同的接口功能。虽然这个练习描述了 U12 的使用,但是任何微控制器板都可以用来代替图 1-1 中的 LabJacks。第十一章给出了激活 Arduino 以点亮 LED 的 DAQFactory 控制屏幕按钮的详细配置。在“实验下载-向微处理器发送数据”部分,配置了三个按钮来打开和关闭 LED 的电源,并按照编程的顺序进行。
五金器具
图 1-2 显示了本练习中合适电子元件的典型选择,放置在原型“试验板上”
图 1-2
原型试验板和各种组件
项目 1 是一个塑料电池盒,能够容纳八节 AA 型电池,以提供 12 伏的标称电压。(八个 1.5 伏的新鲜碱性电池将提供 12 伏,而八个 1.2 伏的镍金属氢化物(NiMH)可充电电池将提供初始的 9.6 伏 DC 电源。锂电池每个可以提供 3.6 伏的额定电压。)
标题 2 旁边的项目是发光二极管。左侧的红色 LED 是一个 3 毫米(1/8 英寸)的微型设备,接下来的四个彩色二极管是 6 毫米(1/4 英寸)的设备,而右侧的透明设备是一个 10 毫米(3/8 英寸)的白光 LED。
第 3 项是 CD4050 同相十六进制缓冲器(见下文)。项目 4 是一个典型的 1/8 瓦限流电阻,而项目 5 是一个 2 英寸(50 毫米)乘 6 1/4 英寸(163 毫米)的原型板。该板在顶面的顶部和底部有两条独立的电源轨,标有红色(+)和蓝色(–)线。每条轨道电力线可以容纳 50 个电源连接和 50 个接地连接。上下电源轨之间是两组独立的 63 列五个连接点。
电路原理图:DAQFactory 和 LabJack 组合
图 1-3 中的电路原理图用于前两个练习。第二章中使用了完整的四个 LED 电路,其中监控各种颜色 LED 的单独功耗。在第一个练习中,仅使用红色 LED 电路中的接线。
图 1-3
用于 LED 功率控制的电路
CD4050 芯片中的单个开关是单向的,因为施加到输入端的电压变化会影响输出,但输出端的电压波动不会影响输入引脚。
软件
所需的页面组件:单个按钮
正如“练习路线图”中所讨论的,在进行任何科学训练时,都应该做详细的笔记。在本练习中,应选择用于控制 HMI 的通道名称,或在本例中为 LabJack,并配置软件,将通道输出信号传送至 LabJack 端子板上的第一个输入/输出端子(I/O 0)。(参见 LabJack U12 或 U3-HV 用户指南。)应遵循 DAQFactory 和 LabJack 用户指南中的详细信息,在本练习中,作者使用了一个名为 RedLed 的通道。任何 DAQFactory 项目中使用的通道都应该在放置屏幕组件之前通过 Apply 按钮进行配置和激活。通过在创建屏幕组件之前定义频道,频道名称将出现在弹出菜单中,如图 1-11 所示。(关于使用微控制器代替 LabJacks,参见第十一章“实验下载”。))
与所有编程一样,文档是必需的。不记录软件是一种很差的实践。在放置和配置按钮之前,应该在屏幕上放置一条描述性的文本消息,以记录该按钮的功能。通过选择静态选项,然后选择文本选项,从鼠标右键弹出菜单(人民币-PUM)创建文本组件。显示允许创建屏幕消息的窗口。
图 1-4 、 1-5 和 1-6 描述了静态文本屏幕组件的选择、组件属性子窗口的显示以及活动文本输入面板。
图 1-5
简单按钮控件属性
图 1-4
简单按钮控制
当消息轮廓框架就位并处于选定模式时,右键单击将显示包含属性选项的菜单。选择菜单中的属性条目将打开属性窗口,允许在文本组件中显示字母条目。
图 1-6
文本框配置
在文本框中输入所需的文字并选择对齐、颜色、字体和大小后,可以使用确定按钮关闭主选项卡以放置文本消息。如图 1-7 所示的文本框可能需要扩展/调整大小以显示整个信息。
图 1-7
按钮图标的大小
为了与持续文档的哲学保持一致,现在可能是用页面属性框命名页面的好时机,如图 1-8 所示。右键单击页面列表中的当前 page_n 指示符,并选择中间的选项:页面属性,即可显示该框。
图 1-8
DAQFactory 页面命名框
按钮组件是从人民币-PUM 中选择的,按下 Ctrl 键,组件可以定位在适当的文本下面。
在屏幕上定位按钮组件后,可以通过完成“组件属性”对话框中的相应选项卡来配置它的实际用途。如图 1-9 所示,右键单击所选图标,调用属性对话框。为了将屏幕显示的按钮连接到实验环境中的一个动作,选择“Do Action”选项,如图 1-9 所示。
图 1-9
按钮属性对话框
如图 1-10 所示,按钮组件属性窗口包含两个选项卡,Main 和 Action。
图 1-10
“执行操作”按钮选择的主选项卡
默认情况下,主选项卡允许按钮用期望的字体和字符大小以及适当的字体颜色进行标记,这些字体在显示的图标中居中。如图 1-6 和 1-8 所示,在配置屏幕组件时,为了方便参考,在属性窗口下方显示了一个帮助屏幕。可以通过帮助框侧面的滚动控件查看帮助框的文本内容。
按钮组件盒如图 1-10 所示。
顾名思义,组件动作选项卡配置鼠标光标点击按钮图标时调用的动作。如图 1-11 所示,动作选项卡弹出一个动作或选项的下拉列表,从中可以进行所需的选择。可在窗口底部的组件帮助文件中找到下拉操作列表中各条目的详细信息
图 1-11
“执行操作”选项中的“操作”弹出列表
。
对于试验板上 LED 的手动控制,选择下拉列表中的切换选项。选择切换选项需要完成几个对话框,这些对话框精确地指定了要做的事情,如图 1-12 所示。
图 1-12
在动作自动填充列表之间切换
当选择了切换选项时,通常是一个通道在两个交变电压之间切换,例如 0 伏和 5 伏。如本软件配置部分开头所述,I/O 通道配置数据的完成和输入现在反映在图 1-12 的智能弹出选择列表中的红色 Led 条目中。双击红色 Led 频道条目将在框中输入频道名称。确保在框中输入的名称是正确的,并且没有无意中在所需名称中添加或删除额外的字符。
“操作”选项卡还有其他几个灰显的选项,可通过操作列表中的各种选项激活。在这种情况下,如图 1-13 所示,有用于输入“切换”值的框,这些框将作为轴出现在通道的图形显示上。
图 1-13
DAQFactory 按钮操作屏幕已完成
本练习的目的是将按钮直接耦合到 LED 电源控制器,无需任何脚本。
观察、测试和开发
将数字电压表的正极引线连接到 I/O 0 端子,将黑色引线连接到实验室插孔的 GND 端子。设置仪表刻度,以便能够测量 5 伏电压。打开仪表,确保读数为零。然后点击 LED 开/关按钮,读数应该上升到 5 伏。
如果没有出现 5 伏读数,则首先检查所有部件和表格中的通道名称拼写。红色 Led 区分大小写,在屏幕组件和频道表中出现的所有实例中,必须与拼写完全一致。确保“切换”值在 0 和 5 伏之间。
当在 LabJack 端子处获得 5 伏信号时,可以准备从端子到 CD4050 IC 芯片的接线。将 CD4050 六角同相缓冲芯片沿中央分隔槽插入试验板的方便位置。通常放置芯片时,从顶部看芯片时,1 号引脚位于左下角位置。
将 1 号引脚连接到试验板上的+电源线路,将 8 号引脚连接到试验板上的负电源或–电源,并将 I/O 0 线连接到 3 号引脚。将 LabJack 上的 GND 端子连接到试验板上的–电源。将外部电源连接到试验板的+和-线,将电压表的正极引线连接到 IC 的引脚 2,负极引线连接到试验板的负电源线。单击 LED 开/关按钮,电表电压应升至标称+5 伏。
如果 5 伏信号没有出现在仪表上,首先验证电源,然后按照图 1-3 中红色 Led 示意图所示,重新检查每个有线连接。
当在 CD4050 缓冲器的引脚 2 处获得标称 5 伏信号,并且可以通过屏幕图标循环开/关时,就可以对通过控制屏幕按钮激活的红色 led 进行功率限制计算。使用欧姆定律计算将通过本项目选择的红色 LED 的电流限制在制造商建议的中间范围所需的电阻大小。从数据手册中可以看出,作者的构建所使用的明亮 LED 源的额定最大电流为 30 mA,标称压降为 1.8。欧姆定律的应用表明,213ω的电阻值会将二极管电流限制在规定的最大允许值的一半。任何 220ω或更高的标准电阻都可以保护 LED,实验设置中使用了标称值为 470ω的电阻。
根据电源的标称电压,计算将 LED 电流限制在二极管数据手册中规定的最大值所需的电阻的理论尺寸是一种很好的做法。利用数据手册中的最大电流和电源电压标称值,可以为使用中的 LED 产生一个足以保护二极管的电阻值。如果使用高于“足够”计算值的下一个标准值电阻,二极管将被很好地点亮,并具有额外的电流过载保护裕度,这将进一步延长器件的使用寿命,并有助于将电源负载降至最低。
在确定限流电阻器的正确尺寸,然后将电阻器、二极管和 CD4050 缓冲器组装到电源连接之后,红色 LED 的照明现在应该由控制屏幕上的按钮图标控制。(确保二极管的阴极或短引线接地。)
简单的 DAQFactory 图形用户界面如图 1-14 所示。
图 1-14
用于 LED 照明控制的 DAQFactory 按钮
讨论
在本练习中,按钮是用 SCADA 软件创建的。然后,来自按钮状态的逻辑信号被传输到 LabJack 端子板,该端子板进而控制能够处理激活 LED 所需功率的 ic。LED 本身由外部电源供电,因此产生光所需的电流不会从 PC 电源获得。如果检查 CD4050 hex 缓冲器的电路,很明显电压信号正在控制双 CMOS 反相器配置(图 1-15 )。
图 1-15
独立 CMOS 缓冲电路
从前面的原理图中可以看出,在集成电路电子学的几篇参考文献 1 中也有详细讨论,内部互补金属氧化物半导体绝缘栅器件实际上阻止了任何 DC 电流流入 CD4050。来自 HMI 的电压变化是控制信号,实际上不会给 USB 系统带来任何电流负载。
Raspberry Pi,Python,屏幕按钮 LED 控件
利用 Raspberry Pi (RPi)单板计算机(SBC ),可以通过多种方法实现屏幕显示的 LED 照明。电源控制可以通过 Pi 的 Linux 操作系统的命令行来实现,也可以通过鼠标点击系统屏幕显示上的按钮图像来实现,该按钮图像是通过名为 easyGUI 的 Python 编程语言库、名为 tkinter 的 Python 图形用户界面库或 RPi 与 led 之间的 Arduino 微控制器板接口来实现的。
所有的 LabJack DAQ 设备都与 Linux 操作系统和 Python 语言兼容。
在第一个练习中,将演示使用 Python 解释器的交互或脚本模式点亮二极管的命令行方法。除了命令行控制之外,easyGUI 库还将创建一个简单的双按钮 LED 控制 GUI。
当模拟信号的数字化需要 10 位模数转换器(ADC)时,使用 Arduino 微控制器接口实现 LED 控制将在第四章中介绍。
基本的 Raspberry Pi 计算机的组装和配置在诸如 Apress 书籍中的 Practical Raspberry Pi 以及 Raspberry Pi Foundation 的在线文档中的最新详细信息中进行了讨论。
通过将图 1-16 中定义的 SBC 引脚直接连接到图 1-17 中描述的试验板,RPi GPIO 的实验可以以最小的复杂度完成。带状电缆可用于将 RPi SBC 上的一排双引脚连接到原型板,如果使用,研究者应确保带状电缆上的白色或红色条带连接到主板上双排引脚的左上引脚(从上方看)。
图 1-16
GPIO 引脚识别
实验的
在图 1-17 中,发光二极管(LED)上的长引线是阳极,连接到正电源。发光二极管是固态器件,只能单向传递电流。流经该器件的电流控制照明强度,但过大的电流会损坏二极管,因此在电路中使用限流电阻。
为了确保 Python 和 RPi 硬件引脚数组之间的通信,在 Raspbian 操作软件发行版中包含了一个名为 gpiozero 的库。要创建活动屏幕组件,可以下载一个非常简化的 GUI 创建库,名为 easyGUI,详见下文,并在这些介绍性练习中使用。
在 RPi 终端,输入“sudo apt-get install python 3-easy GUI”。为了获得 Python 3 版本的正确库,需要 3。
在图 1-17 中,描绘了 RPi SBC 早期 26 针模型的连接。
图 1-17
GPIO 引脚直接连接到原型板
如图 1-17 所示,阳极通过原型板上的一列连接引脚连接到 GPIO 阵列中的+5 伏引脚。二极管的阴极与接地的限流电阻串联。电阻值由所用 LED 器件的最大电流规格决定。
RPi 的 Linux 操作系统有一个 Python 编程语言解释器,调查人员可以用它来激活 GPIO 总线上的一些引脚或将其通电至+5 伏。Python 命令可以在交互模式下处理,每次处理一行,以响应在终端输入的代码,或者作为脚本编写的一系列自动执行的 Python 命令。
在交互模式中,我们可以认为这是“手动”模式,因为我们每次处理一行从键盘输入的代码。交互模式对于用键盘设置和测试电路非常有用,在交互模式下,我们可以根据需要在 Python shell 中打开和关闭 LED。当实验者只使用外壳和键盘来打开和关闭 LED 时,不会有任何系统之前动作的记录。
当 RPi 与 Python 脚本一起使用时,可以将显式的 print 语句写入代码,以记录所采取的每个操作,从而提供系统状态的历史记录。
观察
用 Python 编写的 easyGUI 库为实验者提供了创建一系列包含典型基本 GUI 应用程序的屏幕所需的代码。图 1-18 是一个“点亮 LED”动作框,它是由 easyGUI 库中的取消/继续对话框示例修改而来。点按“继续”会点亮 LED,点按“取消”会将其关闭。作者已经修改了库代码,以记录图标使用的先前动作或历史。文本记录显示在运行对话框的 Python shell 中。
图 1-18
用于 LED 控制的简单 GUI
LED 灯盒上的调整大小按钮可以工作,但停止程序按钮不行。双按钮对话框实际上是在 Python shell 中的一个窗口中运行,如图 1-19 所示,shell 停止程序按钮是有效的。
图 1-19
Python Shell 的“Kill”对话框
在本章末尾的代码清单中,有手动和基于 GUI 的清单,可用于激活连接到 GPIO 引脚的 LED。
讨论
DAQFactory 是商用 SCADA 成品软件。购买这种“开箱即用”的“交钥匙”系统,研究者可以连接到受控系统,并从复杂指令集计算(CISC)设备上提供的可配置图标组装所需的 GUI。Raspberry Pi 是一种成本非常低的精简指令集计算(RISC)设备,它使用免费的开源软件,能够进行物理计算。
如前所述,RPi 是一种成本非常低的入门方法,用于控制作为教育项目或实际科学研究调查一部分的实验过程或测量实验。RPi 可以被编程以实现简单或非常复杂的实验设置的管理,但是随着被管理的实验的复杂性增加,需要研究者增加开发时间。
代码列表
用适当的限流电阻(CLR)组装 LED 后,将串联有线设备连接到 GPIO 引脚 2 和地,然后将清单 1-1 中的代码输入 Python shell 或交互终端。
from gpiozero import LED
grnLed = LED(2)
grnLed.on()
grnLed.off()
Listing 1-1Manual LED Control
这一系列简单的代码将打开 gpiozero 库,并使 LED 对象可分配给 GPIO 引脚 2。grnLed.on()线将 GPIO 引脚 2 设置为高电平或真值,并点亮 Led。下一行 grnLed.off()将 GPIO 引脚 2 设置为低或假,并关闭 Led(清单 1-2 )。
# Exercise with easyGUI to turn a LED on and off
# an adaptation of the continue or cancel dual
# button message box.
#
from easygui import *
import time
from gpiozero import LED
#
redLed = LED(2)
#
# Use a while loop for continuous activation
while 1:
msg = "Light the LED"
title = "The Scientyst's Ayde"
#
if ccbox(msg, title): # show a Turn On/Off dialog box
print("LED is turned ON!")
redLed.on()
# LED power turned on
else: #user chose cancel
print("The Led has been turned off!")
redLed.off()
# LED power turned off
Listing 1-2A Button GUI LED Control
摘要
-
监视器上的基本按钮图标控制远离主机的电子设备。
-
计算机实验接口可以在更昂贵的系统中通过配置来实现,或者在不太昂贵的基于组件的系统中根据基本原理来编程。
-
在第二章中,将开发一个更具交互性的双向控制系统,带有多个按钮和一个实验数据显示。
1)CMOS 基础知识指南,电路,&实验,柏林,Howard W. Sams &有限公司,ISBN 0-672-21654-X
- CMOS 食谱第二版 Edn。SAMS 兰开斯特和柏林,ISBN 0 672-22459-3
二、电源控制、监控和专用图形用户界面的创建
SCADA 首字母缩略词中的“SC”代表监控,而“DA”代表数据采集。开发完整软件包(如 DAQFactory)的目的是监控真实世界的机电过程并监督或控制其运行。本章及其练习通过创建多个 LED 按钮,然后监控激活这些单独的 LED 所消耗的功率,对单按钮控制进行了扩展。因此,展示了响应于一个或多个单元操作激活的控制系统输入的过程操作值的读取。
led 的各种尺寸、构造方法、预期用途和颜色会导致其半导体结上的不同压降,详见“实验”部分。每个具有固定标称值的限流电阻(CLR)都有自己独特的电阻值,该电阻值在该类型器件的标准值容差范围内(即+/-10、5 或 1%)。当负载电阻变化与二极管压降和 CD4050 缓冲器的导通电阻结合起来考虑时,很明显,流经不同颜色 LED 电路的每个电流都是不同的。
表 2-1
典型 5 毫米 LED 参数
|二极管颜色
|
典型电压降(V)
|
波长(纳米)
|
电流(毫安)
|
| --- | --- | --- | --- |
| 红色 | 1.63–2.03 | 610–760 | Thirty |
| 格林(姓氏);绿色的 | 1.9–4.0 | 500–570 | Twenty-five |
| 柑橘 | 2.03–2.10 | 590–610 | Thirty |
| 黄色 | 2.10–2.18 | 570–590 | Thirty |
| 蓝色 | 2.48–3.7 | 450–500 | Thirty |
| 白色的 | 2.48–3.7 | 450–500 | Thirty |
| 紫罗兰 | 2.76–4.0 | 400–450 | Thirty |
| 紫外线的 | 3.1–4.4 | < 400 | Thirty |
本练习将测量不同颜色的发光二极管点亮所产生的单个电流,并通过监控单个操作和整个系统的功耗来提供关于整体系统性能的信息。
许多电子参考文献和科学中的 Arduino 测量中介绍了几种方法,可用于使用电流表、静电计、感应或霍尔效应设备测量直流电流。然而,在手稿的介绍阶段,为了便于实施,电阻压降测量和欧姆定律计算将用于监控通过被测系统的电流。
实验的
发光二极管(LED)的电流、电压、电阻和发光度特性可以根据欧姆定律和基尔霍夫电压定律更好地理解。
如图 2-1 所示,led 以各种形式制造。
图 2-1
各种形式的 LED
图 2-1 所示为一个 10 毫米绿色、一个 5 毫米蓝色和一个 3 毫米红色 LED 通孔装置。(led 有扁平、条形和表面贴装三种配置,如 Arduino 或 Raspberry Pi 电路板的任何照片中所示。为了便于使用第一章、图 1-1 中所示的原型板进行实验设置,最好使用双引线 led。)图 2-1 底部的星形圆盘是一个 3 瓦的照明二极管。顶部的三个指示器型器件可以由计算机或 USB 设备供电,但设计用于照明或照明服务的底部二极管通常消耗足够的电流,以保证用螺栓连接到散热器,因此在使用时通常需要一个特殊的高电流电源,远远超过计算设备的电流能力。
根据发光二极管产生的光的类型,它们通常被分类为指示器或照明器。指示器通常产生从各个角度可见的有色物体的漫射光,而照明器通常具有透明体,产生纵向或直接在装置前方最强的集中光束。LED 亮度以毫坎德拉(mcd)或辐射强度来衡量。一根普通的蜡烛发出大约 1 坎德拉的光。
表 2-2 和 2-3 是 LED 数据表上常见的电气和光学参数的典型列表。
表 2-3
典型 LED 光学参数
|  |表 2-2
典型 LED 电气参数
|  |查阅数据手册获取信息时,务必验证所检索的数据是否适合手边的正确封装尺寸。在选择 LED 电路中使用的电流时总是需要折衷,因为电流越高,光越亮,设备的使用寿命越短。数据手册中的列表给出了典型工作电流、短时最大电流和较长使用寿命的工作电流。
数据手册中也给出了二极管的光学特性,包括发射光的频率或波长、二极管压降以及给定二极管电流下的光输出亮度。
为了避免损坏实验应用中使用的二极管,限流电阻(CLR)与 LED 串联。电源的电压必须足够高,以打开 LED,电源电压和二极管电压之间的差值可以通过限流电阻下降,以调节指示器或照明电路中的电流。(基尔霍夫电压定律指出电路周围的总电压降为零,欧姆定律可以用来确定调节 LED 电路中的电流所需的电阻值。)
使用欧姆定律的理论计算和典型 5 mm LED 的数据表明,200ω电阻应足以限制我们电源标称电压的电流。作者的 6 V AA 电池电源和 30 mA 亮源二极管表明,6V/30mA = 200ω电阻足以保护二极管和 CD4050 缓冲器(见图 2-2 )。最接近的较大标准值电阻为 220ω,使用 5%容差元件,我们选择四个元件用于本练习。用数字伏特-欧姆表,我们测量四个电阻的单个值并记录数据。为方便简单起见,电阻以 LED 颜色命名。在作者的开发工作中,红色为 221ω,绿色为 219ω,黄色为 216ω,橙色为 216ω。确保识别单个电阻并记录其实际电阻值,因为计算单个负载电流时需要这些数值。
五金器具
四个各种颜色的明亮发光二极管和四个测量电阻 220ω的标准电阻器,对于预期的电流具有足够的额定瓦数,在练习的开发中使用。(标注的值基于作者的实验设置,使用标称 6 V 电源和 30 mA 二极管。)
在本练习中,CD4050 IC 缓冲芯片上可用的六个门电路中的四个用于将 led 与 USB 隔离,并从辅助电源获取电能。
一个扁平的矩形电池组,能够容纳四个串联的 AA 电池,为安装在原型板上的实验组件提供标称 6 伏电源。(有关使用电池作为辅助电源的更多详细信息,请参见“讨论”)。
图 2-2 显示了本练习的电路图。为了测量流经单个 LED-电阻器串联组合的电流,使用了 LabJack U12 的模拟输出和输入信号端子。
图 2-2
四 LED 显示屏的电路
软件
总共需要八个通道。四个配置为输出,以控制 led 的电源开关,四个通道监控单个测量值负载电阻上的电压。
输出通道可以像上一练习中一样标记为红色、绿色等。输入通道标记为 RedLedCurrent、GreenLedCurrent 等。本项目练习的 DAQFactory 通道表如图 2-3 所示。
图 2-3
DAQFactory 通道表
需要页面组件
如图 2-4 所示,四个按钮、五个变量值组件(VVC)和两个文本显示被放置在屏幕上,以形成所需控制屏幕用户界面的基本结构。
(DAQFactory 手册提供了有关创建屏幕组件、将它们定位在屏幕上以及创建文本标签和消息的详细信息,这些标签和消息用于识别不同的组件以及输入或显示的值。根据手册,一组独立的屏幕组件可以组合在一起,形成一个单一的单元,便于在屏幕上操作。单独的成组组件可以以独特的背景颜色显示,以向终端用户或操作过程控制器提供视觉上可理解的控制屏幕。将相关的组件组合在一起,并用单独的彩色背景将它们隔离,以吸引眼球,从而增加易用性,同时最大限度地减少操作人员出错的机会,这是一个好的设计。组背景的着色应在应用程序中实际使用之前留给控制屏幕的最终配置,因为背景颜色面板的创建会限制对组中各个组件的访问,从而在系统开发过程中造成不必要的复杂性。)
图 2-4
所需 GUI 组件的初步组装
四个按钮中的每一个都根据被激活的 led 的颜色进行标记,并且相应的变量值元件被设置为显示电流的 mA。第五个变量值显示可设置为显示总电流,单位也是毫安。
图 2-5 显示了电源监控面板,图 2-6 显示了彩色按钮电源输入的典型表示。
图 2-6
电源监控 GUI 的典型计算表达式
图 2-5
GUI 的初步着色
变量值表达式使用以下计算:
(RedLedCurrent [0]/221)*1000
其中,RedLedCurrent [0]是红色二极管限流电阻上的最新电压读数,221 是红色二极管限流电阻的实际 DVM 测得电阻值(单位为欧姆),*1000 乘法器将电流从小数安培转换为整数毫安。
总分量显示中的表达式将所有四个单独的当前表达式计算相加。
观察、测试和开发
一个或多个发光二极管的激活应该指示流过单个二极管的电流和汲取的总电流。显示屏上的值应该每秒更新一次,因为这是频道表中定时条目的默认值。通过在面板上控制按钮的左侧添加 LED 符号,可以创建视觉效果更好的彩色 LED 控制面板。LED 符号可以设置为被激活的 LED 的相应颜色,整个组件组合在一起形成一个连贯的单元,如图 2-7 所示。面板组件用于为分组提供背景(参见 DAQFactory 手册),识别号显示在分组面板上,将面板与显示在主显示屏底部的一组注释/说明联系起来,下面的面板可能是其中的一个组件。
图 2-7
电源监控图形用户界面
使用图 2-8 所示的 DAQFactory 线性规组件,可以实现不同的、或许更有效的功耗视觉显示。
图 2-8
仪表添加选项
如果线性仪表组件设置为通过将四个单独的彩色 LED 电流相加来显示消耗的总电流,它可以放在按钮控制面板旁边,以更直观全面的格式显示电源消耗的总电流。
讨论
新 AA 碱性电池的额定电流通常为每单位 2890 毫安时。如果所有四个 led 都亮起,则每个 led 消耗约 30 mA,这表明在作者的设置中,6 伏四电池支架的有效使用寿命约为 100 小时。估计 100 小时的使用寿命实际上是理论上的,必须认识到,发光二极管的压降约为 2 伏,电阻的压降约为 2 伏,CD4050、其余的接线以及电池本身的内阻占用了作者的标称 6 伏电源的剩余部分。在估计的 100 小时寿命之前的某个时间点,电池组的电压输出将下降到二极管太暗而看不见或根本不发光的程度。文献表明,原电池碱性化学电池以某种程度上线性的方式放电,随着使用量的增加,电压和电流输送能力都损失了。镍金属氢化物(NiMH)等二次电池化学电池的开路电压(OCV)明显低于一次电池系统(1.2 对 1.5 V),AA 型号的额定容量略低于 2500 mAh。然而,二次电池 NiMH 化学电池倾向于具有低得多的电压损失率,并且不是在其整个放电过程中逐渐失效,而是将所输送的电压保持在相对恒定的值,然后随着其电力耗尽,在非常短的时间内快速且完全地放电。使用电池供电的研究人员应该了解不同电池系统的特性。(碱性 AA 电池的 6 伏电压由 4 个单元提供,但镍氢电池的标称 6 伏电压需要 5 个可充电单元。)
很明显,对于电池组,随着电源上的负载增加,电压下降,并且提供给单个电流消耗负载的电流将减少。无论使用碱性电池还是镍氢电池,当每个二极管导通时,输送到每个二极管的电流都会下降。当绿色、橙色和黄色二极管被激活时,电源监控面板将显示红色 LED 消耗的电流减少。在更关键的现场或实验室操作中,为了最大限度地减少负载增加时的功率下降,可能需要稳压电源、并联电池组或更大的电池形式,如 C 或 D 电池,以维持实验负载下的电流和电压水平。
由面板显示器监控的逐渐减小的电流是从电池组输送的功率的实时指示器,并且逐渐减小的电流可以用于粗略估计电源剩余的使用寿命。
一般来说,可以说新的一次电池或新充电的二次电池将表现出最小的内阻,该内阻随着电池放电逐渐上升到最大值。充电监控可以通过确定开路电压(OCV)和电池单元本身的内阻来完成。OCV 是在空载条件下测量的,但电池组内阻的确定是一个动态过程,需要同时记录消耗的电流和瞬时电路电压。通过图形记录 I 和 V 的同时变化率,可以确定电池的电阻 R。绘制电池的 OCV 和内阻可以用来确定电池组的剩余使用寿命。(参见 Arduino 科学高级技术中的供电实验。)
使用价格低得多的微控制器可以减少功耗问题,该微控制器可用于开发类似于全功能工业级 LabJack DAQ 的实验接口。然而,需要大量的时间和精力来将手头任务的一部分功能实现到内置于市场上可买到的 HMI 设备中的微控制器中。为了用微控制器实现电源控制 GUI 练习,将介绍涉及 DAQFactory 和微控制器程序配置的基本步骤。(详见第十一章。)
与上一次使用 LabJack 的实验一样,从 lab 5%容差电源中选择四个标称值为 220ω的电阻,并用 DVM 测量其实际电阻。为了简化和便于组装,已知值电阻直接安装在原型板上,没有 CD4050 缓冲器,如图 2-9 和图 2-12 所示。
图 2-9
一组 led 的 Arduino 控制的 DAQFactory GUI 开发
为了使 DAQFactory 通过串行通信端口识别微控制器并与之通信,必须识别微控制器使用的 com 端口。通过在主机和微控制器之间连接 USB 电缆并启动微控制器集成开发环境(IDE)程序,可以在工具菜单的端口:条目中找到端口标识。识别端口后,通过加载并运行文件/examples/01 中的“Blink”程序来确认连接和板功能。IDE 的基础/闪烁菜单。微控制器的板载 LED 应以每秒一次“闪烁”的速度闪烁,从而确认通信链路。(板载 LED 是“RedBoard”徽标框旁边的发光绿点,如图 2-12 中描绘的红色电路板的左下角所示。)
DAQFactory 程序中的公共控制屏幕可以服务于 LJ DAQ 或微控制器实验接口。然而,图 2-3 中的通道配置使用的是已经为其编写了驱动软件的 LabJack U12 设备。必须在 DAQFactory 环境中创建一个新设备,以便将数据传输到微控制器上的低级通信端口。一个典型的低成本微控制器是“red board”Arduino 兼容设备,见第一章图 1-1 中的第 4 项,以及图 2-12 中的有线配置。DAQFactory com 设备是通过选择端口和协议创建的。(参见第章 11 、图 11-4 、 11-5 和 11-6 。)
在 DAQFactory 程序的通道创建表中出现可识别设备之前,必须先创建该设备。在 DAQFactory 页面上选择快速➤设备配置条目,将弹出设备配置窗口,其中包含可用设备列表和新的串行(RS232/485) /以太网(TCP/IP)设备条目。(参见图 11-4 ,章 11 。)要创建新的串行设备,请单击新的串行(RS232/485) /以太网(TCP/IP)设备条目以将其高亮显示,并单击窗口右上角的选择按钮以调出以太网/串行设备配置窗口。(参见图 11-5 ,章 11 。)在配置窗口中,输入新的设备名称。(数据工厂名称必须以字母开头,并且只能包含字母、数字或下划线。)正在使用的设备被命名为“ardyRb ”,作为 RedBoard 和 Arduino 的助记符。要创建新的串行端口,请单击“新建串行端口”按钮,弹出串行端口配置窗口。(参见图 11-6 ,章节 11 。)
端口的名称部分是通过查询 Arduino 的通信端口号来定义的,该端口号被发现是 com 端口 4,因此诸如 COM4 之类的名称将足以作为新的连接名称。DAQFactory 配置表中的串行端口号条目必须为 4,以与连接的微处理器的串行端口号相对应。(参见第章 11 ,图 11-6 。)串行端口配置窗口的其余默认设置最好按输入的那样接受,并且当单击窗口保存按钮时,复选框“COM4”应该出现在以太网/串行设备窗口的串行端口列表中。必须为正在创建的设备分配一个协议,因为数据流是由 DAQFactory 序列或脚本控制的,所以选择空协议。空或无协议允许使用序列中的低级通信功能。选择协议并选中“COM4”框,当单击以太网/串行设备窗口右上角的 OK 按钮时,可以保存“ardyRb”设备以供需要时使用。
为了开发具有更简单的微控制器接口设备的电源监控设施,同时坚持从简单系统开始并逐步发展到更复杂系统的基本概念,最初将从 DAQFactory 中的控制屏幕控制单个 Arduino 供电的 LED。然后,单个 LED 可以扩展为一组四个 LED。LED 按钮控制的最简单形式是在 DAQFactory 控制屏幕上创建两个按钮,如图 2-10 左上角所示。
图 2-10
用于控制 Arduino LEDs 的 DAQFactory GUI
DAQFactory 通常运行在基于 PC 的计算平台上,而 ATmega328 芯片则托管 Arduino 操作系统。这些程序可以通过串行端口软件相互通信,但是一次只有一个程序可以使用串行端口。实际上,DAQFactory 显示中的“屏幕图标”的视觉激活启动了到串行端口的低级命令流。串行端口的另一端是 ATmega328 控制的 Arduino 微控制器,它基本上运行在 C 语言上,可以通过编程来处理串行端口上出现的低级命令。
在图 2-10 中,顶部的两个按钮是“红色 led 开启”和“红色 led 关闭”图标。这两个按钮按照第一章的说明在屏幕上配置,在点击“红色 led on”按钮时可能启动的动作列表中,如第一章的图 1-11 所示,选择“快速序列”。快速序列选择调出如图 2-11 所示的窗口。
图 2-11
快速序列窗口
快速序列条目的独特之处在于,只有当它所绑定的按钮被激活时,它才被访问和执行。图 2-11 中的单行代码是将字符 1 写入串行端口的完整序列。
DAQFactory 串行端口通过硬件和软件连接到 USB,USB 也通过硬件和软件连接到 Arduino 的基于 C 的操作系统。这两种软件系统都具有处理低级通信的设施,这些通信是基于比特和字节格式的字符的串行发送和接收。
在图 2-10 和 2-11 的简单例子中,在激活“红色 led on”按钮时,十进制表示数值 1 的 ASCII(美国信息交换标准代码)值 49 被发送到串行监视器。
Arduino 程序被称为草图,用于接收串行端口上的“1”字符和打开数字引脚的代码在清单 2-1 中(本章末尾的“代码清单”部分提供了所有代码清单)。检查清单 2-1 会发现代码将在串行端口上接受一个 0,一个 ASCII 码 48,并关闭 LED。双按钮控制方案简单明了,使用一张草图来管理两种可能的 LED 功率水平。
在控制屏幕的第二行中有一个标记为“切换红色 led”的按钮,该按钮在第一次点击时打开 led,在第二次点击时关闭。清单 2-2 中的 Arduino 草图包含标志变量“oofR”形式的“切换动作”逻辑,记录 LED 的状态为开或关,从而使代码能够切换或改变设备的当前电源状态。
控制屏幕左下角的四个彩色按钮将电源控制功能扩展到四个按钮,Arduino 代码如清单 2-3 所示。
控制屏幕上的每个彩色按钮都与一个快速序列动作相关联,该动作将 R、G、O 或 Y 字符写入串行监视器。在连接的 Arduino 端,代码将到达串行端口的新字符与称为“case”结构的四个字符的集合进行比较。当找到匹配时,执行与所识别的“案例”相关联的代码。在清单 2-3 中,动作包括打开或关闭对应于 DAQFactory 控制屏幕按钮颜色的彩色 LED。
图 2-10 中的控制屏幕包含七个屏幕图标,称为变量值组件,可用于提供过程值的可视化数字显示或读数。
该练习旨在演示设备的远程激活,并测量活动设备消耗的电流形式的过程变量。清单 2-4 、 2-5 和 2-6 列出了可用于单向传递动作发起请求并传回结果效果的快速序列代码。清单 2-6 是一个较短的快速序列 DAQFactory 端方法,用于声明电流已经停止。
Arduino 微控制器配备了一个 6 通道、10 位模数转换器(ADC),能够将 0–5 伏信号转换为 0 到 1023 之间的数字值(2 10 或 1024)。当 5 伏特被分成 1024 个单位时,每个数字计数单位产生 4.8828 毫伏。如图 2-9 所示,四个二极管压降中的每一个都由 ADC 的模拟输入测量。电压降测量完成后,使用 Arduino 的“serial . println(iRed);”将计算出的二极管电流写入串行端口将回车换行(CR-LF) ASCII 字符附加到发送到串行端口的二极管电流值字符的格式。\013\010 用作描述或识别字符结尾的标记,向 DAQFactory 快速序列数据解析逻辑提供测得的二极管电流数值。
清单 2-9 和 2-10 是 DAQFactory 快速脚本,当图 2-10 左下角的四个面板中的一个或多个彩色按钮被激活时运行。每个按钮都有一个快速序列脚本,可清除串行输入缓冲区,并将代表按钮颜色的大写字母和相应的 LED 发送到 DAQFactory 入口或串行端口的内存位置。Arduino 微处理器 C 代码检查从 DAQFactory 控制屏幕发送的字符,并执行所需的操作,将返回数据参数发送到串行端口。清单 2-7 和 2-8 是支持 DAQFactory 动作请求的 Arduino 代码。快速序列在发送激活请求后开始延迟,然后开始处理出现在串行端口上的字符。
清单 2-9 仅处理活动 LED 消耗的电流,而清单 2-10 处理任何活动 LED 消耗的单个电流和所有活动 LED 消耗的总电流。
这些简单的系统展示了 SCADA 系统的巨大优势之一,即两个计算平台之间的串行通信不需要错误检查或错误处理能力。发送和接收的字符在软件中是固定的,只需要激活屏幕图标即可实现所需的活动和测量。控制屏幕的操作者需要最少的动作,因为不需要数据输入,只需要点击控制屏幕上的正确图标。在电噪声工业或实验环境中,这些简单的程序可能需要错误检查和错误处理能力。
双按钮开/关控制面板尽可能简单,操作员有两种选择来打开和关闭 LED。如图 2-12 所示,安装在原型板上的任何二极管的照明提醒操作者系统的状态,以及哪个按钮在改变系统的状态时处于活动状态。
图 2-12
Arduino 控制的四 led 阵列
电流的测量已经自动化,并且在实际实验应用中进行额外的工作,可以验证该过程以确定电流测量的准确性和再现性。
Arduino 和 DAQFactory 程序都有丰富的功能来帮助开发串行通信。串行通信系统非常简单,广泛应用于工业制造和实验研发项目。本练习中使用的两个软件系统都有串行端口窗口,这些窗口允许可视化驻留在串行监视器接口上的数据,并允许研究人员从主机程序接收串行数据或向主机程序发送串行数据。
除了程序之间的串行端口通信之外,Arduino 串行端口还用于许多应用,并且可以从连接到微控制器的多种类型的传感器传输数据。各种可能的测量方法的详细信息可在科学杂志的 Arduino 测量中找到。
在图 2-13 中,DAQFactory 串行端口已经扩展到测试和监控串行端口上发生的动作。
图 2-13
DAQFactory 串行端口监视器
为了查看运行中的 DAQFactory 串行端口监视器,主机必须运行与所用控制屏幕兼容的微控制器程序。当微控制器程序在最小化或后台配置中运行时,包含控制屏幕的 DAQFactory 程序可以在前台运行,如图 2-13 中标记的 1。从“快捷”菜单中选择“设备配置”,出现如图第十一章图 11-4 所示的设备配置窗口。从设备配置窗口的条目中,选择“ardyRb ”;点击窗口右上角的选择按钮,出现“以太网/串行设备”窗口,即图 2-13 中的面板 2。输入正确的名称和通信端口后,可激活“监视器”按钮,调出 COM4 的串行端口监视器,在图 2-13 中标为面板 3。
激活后,COM4 的串行端口监控器现在控制进出串行端口的数据流。在面板 1 的底层控制屏幕上仍然可见的两个按钮不再响应,并且只有通过将二极管颜色的正确的大写首字母发送到串行端口,才能激活 Arduino 阵列上的相应二极管。从面板 3 的活动记录中可以看出,记录为“Tx R”的 R 的传输之后是脚本 Rx 10.69\013\01010.69\013\010。Rx 是“接收到一个传输”的符号,10.69 是一个数字序列,附加了 ASCII 码 013(回车)和 010(换行符)。紧接在换行符号之后的是一个数字序列,它再次附加了一对打印指令。
如前所述,Arduino 代码已识别 R 并激活红色二极管,发回单个二极管电流和总电流,两个数值后跟 CR-LF 组合。如果一个大写的 O 被发送到端口,预期的动作发生;如果在大写字母 O 仍位于发送区间时使用 Enter 键,Arduino 代码将触发橙色二极管,如面板 3 所示。
如前所述,用非常便宜的微控制器板代替工业研究级接口是一种实践,可以利用显著增加的开发时间作为实践学习的优势。
使用 Raspberry Pi 进行电源监控和控制
对于不熟悉 Raspberry Pi (RPi)教育计算机及其通过通用输入和输出(GPIO)引脚阵列在物理计算中的使用的研究人员、实验人员或教育工作者,有几个文本可供参考。 2 当前信息和软件可从树莓派基金会在线获得,在尝试以下练习之前应先阅读这些信息和软件。
虽然 Raspberry Pi 单板计算机(SBC)最初是作为一种非常便宜的教学辅助工具,但它可以作为 SCADA 应用程序的物理计算平台使用,但有一些限制。RPi SBC 不具备模数转换功能,但有几种方法可以解决这一电压测量限制。实验传感器的电压可以通过外部 ADC 芯片或与 Arduino 微控制器板的 USB 连接来测量,并通过使用 Python 库和 RPi 来测量已知值电阻-电容串联的时间常数。为 gpiozero Python 库编写的文档指出,RPi 操作系统本身并不完全兼容物理计算的“实时”要求。注意,在诸如 led 之类的设备上使用 GPIO 引脚进行编程脉宽调制(PWM)的尝试可能遭受“抖动”,因为 Pi 操作系统可能涉及减损或干扰脉宽的定时处理的内部过程。
RPi 和 Arduino 微控制器之间的 USB 连接非常类似于前面的 DAQFactory-LabJack 练习中演示的易用性和组装。Arduino 板的成本与 RPi 相当,Arduino 集成开发环境(IDE)可从 Arduino 和 RPi 基金会下载,与 Linux 兼容。通过使用 Arduino 微控制器板作为 RPi 和实验仪器或装置之间的智能接口,可以实现显著的可重复和可预测的物理计算。然而,将 Arduino 微控制器作为 RPi 的智能 I/O 外设的实施涉及到大量的脚本来连接两个系统,这将在下一章的脚本练习中探讨。
在利用 RPi 测量电压并计算电流的各种选项中,最便宜的选项是使用独立的模数转换器(ADC ),如微芯片 MCP3008 集成电路(IC)。IC 芯片成本约为 5 美元(CDN ),是一款 10 位逐次逼近型寄存器(SAR)器件。LabJack、Arduino 和 MCP3008 中使用的 10 位分辨率将输入电压划分为 1024 个量化单位。IC 连接到 RPi GPIO 引脚,如图 2-14 所示,并使用 py-spidev Python 库实现的 Python 串行外设接口(SPI)协议。见拉斯伯里皮。org/documentation/hardware/raspberrypi/spi/README了解在 GPIO 引脚阵列上实现 SPI 协议的 RPi 设置说明。
图 2-14
用于 led 电源监控的 RPi 电路
实验的
如前所述,RPi 的廉价电压测量能力可以用微芯片技术 MCP3008 来实现。该芯片是一个 16 引脚、塑料双列直插式封装(PDIP)、集成电路、10 位模数转换器。该 IC 有 8 个输入通道,可以用来对电路中相对于公共地的最多 8 个不同点的电压进行数字化处理,或者测量电路中 8 个点之间的最多 4 个差分电压降。(有关数字概念和 10 位或 12 位 ADC 的详细信息,请参见第章 4 、章 5 和章 6 ,10 bit = 2 10 或 1024,12 bit = 2 12 或 4096。)
图 2-14 是从 RPi GPIO 阵列到 MCP3008 的连接示意图,以及可用于测量流经彩色二极管的电流的四个通道的示意图。
为了简化图 2-14 的图形,GPIO 引脚和 MCP3008 引脚之间的连接线没有画出来。GPIO 阵列左上角引脚上 RPi 的 3.3 V 电源连接到 IC 上的引脚 16 和 14。其余的连接以相同的方式指定和连接。
清单 2-11 中列出了用于选通(激活)ADC 芯片进行转换,然后读取并显示 10 位电压值的 Python 代码。
与所有复杂的实验系统一样,研究人员从简单的组件开始,测试每个组件并验证其作为独立实体的单独性能。一个复杂的系统是通过一次增加一个部件来组装的,如果可能的话,每次增加一个部件都要对组件进行测试,直到完成一个完整的操作装置。
据报道,RPi 的早期型号设计为在 3.3 V 逻辑电平下提供 3 mA 的输出电流,因此可用的总功耗为 17 引脚× 3 mA = 51 mA。微小的 3 毫米指示灯 led 的最大电流限制为 20 毫安,应在 16–18 毫安范围内工作。5 毫米和 10 毫米 led 汲取 20-40 毫安范围内的电流,为了获得更长的使用寿命,应在低于其最大短期电流处理能力 15-20%的情况下工作。
LED 辐射与流经二极管的电流成正比。数据手册中的电流建议是针对以最大亮度或接近最大亮度工作的器件给出的,实验工作并不总是需要这种亮度。5–10mA 的 LED 电流通常为实验工作提供充足的亮度,可用于避免 GPIO 引脚上的 RPi 电源连接过载。
为了适应 RPi GPIO 引脚提供的有限电流,图 2-14 的电路可以用现成的 5 mm 发光二极管、合适的 clr 和单独的手动电源控制开关组装,所有这些在组装期间都设置在打开位置。电源监控练习的初始测试中使用的配置是一组打开的开关。
阵列中的四个 led 应单独测试,然后一起测试,以确认电源供电时它们的亮度。(有关手动激活 LED 的命令行终端方法,请参见第一章。)一旦所有 LED 二极管都成功点亮,然后关闭电源,二极管及其 CLR 的连接点连接到 ADC 的适当输入通道。ADC 正确连接到 LED 阵列后,就可以连接 MCP3008 和 RPi GPIO 引脚,并运行 Python 程序。系统的初始输出应该显示每个通道没有输出电流,总和也没有。系统的简单性需要手动操作模式来查看从 LED 照明系统的功率加载和分配得到的数据。由于每个 LED 都是手动打开和点亮的,因此应运行电源监控程序来计算和显示汲取的单个电流及其总和。
通过将流经 led 的电流保持在 12–16mA 范围内,RPi 应该可以轻松地完全点亮三个 led,并能够在短时间内点亮第四个二极管,同时电源监控程序会收集并显示更高的功耗数据。对于使用比 GPIO 引脚更多功率的实验,可以使用一个辅助电源和几个 CMOS 4050 缓冲芯片。
观察
本练习的目的之一是向使用 RPi GPIO 引脚为实验装置供电的研究人员传授一种绕过系统限制安全工作的方法。
图 2-15 描述了电源监控程序在 Python shell 中的典型输出。
图 2-15
电源监控程序输出的 RPi 显示
检查图 2-14 的原理图部分可以发现,限流电阻器(CLR)的测量电阻上的电压降是由流过二极管-电阻器组合的电流引起的。MCP3008 通道用于直接测量接地电阻上的压降,从而间接测量整个电路中的恒定电流。
讨论
作为一台教育计算机,Raspberry Pi 不仅能够以信息处理模式运行,还能够作为一个物理计算平台。然而,当用于物理计算模式时,必须认识到紧凑、廉价系统的局限性。简而言之,RPi 操作系统是进程驱动的,如果处理器内核中正在运行更高优先级的进程,它可能不会立即响应 GPIO 引脚上的事件。图形处理是计算资源的一个非常大的消费者,因此当在物理计算模式中使用时,RPi 应该尽可能使用最实用或最小的屏幕显示。
Raspberry Pi Foundation 编写并提供了几个 Python 库,允许计算机与各种硬件设备接口,以扩展与外部设备和传感器的通信,如 MCP 系列模数转换器。
代码列表
print("RPi 4 Led Array Power Monitoring Program")
print() # a blank line for output screen spacing
print("ADC reading of LED voltage value is normalized from 0 to 1 by gpiozero library.")
print("The true value of the monitored voltage is the product of the normalized ADC value and the reference voltage.")
print()
# a single normalized value is printed each time the module is run
from gpiozero import MCP3008
# create an object representing the device and assign the input channels
ADC_vlu = MCP3008(0) # the number in brackets is the channel on the device
ADC_vlu1 = MCP3008(1)
ADC_vlu2 = MCP3008(2)
ADC_vlu3 = MCP3008(3)
#
print("ADC Channel 1")
print('Normalized ADC value = %.3f'%ADC_vlu.value,' Volts') # the blue LED in the author' circuit
#
# convert object, value into a numerical parameter
ledVltg = float(ADC_vlu.value) * 3.3
print('LED CLR voltage value = %.3f'%ledVltg, ' Volts')
# calculate the LED current from Ohms law
blue = (float((ADC_vlu.value) *3.3) / 329) * 1000
print('Blue LED current = %.3f'%blue,' mA')
#
print()
#
print("ADC Channel 2")
print('Normalized ADC value = %.3f'%ADC_vlu1.value) # the yellow LED in the author's circuit
#
# convert object, value into a numerical parameter
led1Vltg = float(ADC_vlu1.value) * 3.3
print('LED1 CLR voltage value = %.3f'%led1Vltg)
# calculate the LED1 current from Ohms law
yellow = (float((ADC_vlu1.value) *3.3) / 220) * 1000
print('Yellow LED current = %.3f'%yellow,' mA')
#
print()
#
print("ADC Channel 3")
print('Normalized ADC value = %.3f'%ADC_vlu2.value) # the red LED in the author's circuit
#
# convert object, value into a numerical parameter
led2Vltg = float(ADC_vlu2.value) * 3.3
print('LED2 CLR voltage value = %.3f'%led2Vltg)
# calculate the LED2 current from Ohms law
red = (float((ADC_vlu2.value) *3.3) / 220) * 1000
print('Red LED current = %.3f'%red,' mA')
#
print()
#
print("ADC Channel 4")
print('Normalized ADC value = %.3f'%ADC_vlu3.value) # the green LED in the author's circuit
#
# convert object, value into a numerical parameter
led3Vltg = float(ADC_vlu3.value) * 3.3
print('LED3 CLR voltage value = %.3f'%led3Vltg)
# calculate the LED3 current from Ohms law
green = (float((ADC_vlu3.value) *3.3) / 219) * 1000
print('Green LED current = %.3f'%green,' mA')
#
print()
#
ttl_Currnt_drw = blue + yellow + red + green
print('Total current draw = %.3f'%ttl_Currnt_drw, ' mA')
Listing 2-11Python Code for the Raspberry Pi Monitoring the Power Draw of a Four-LED Array
device.ardyRb.Purge() // clear the serial buffer
device.ardyRb.Write('R') // send R to serial port for repeat activation
delay(0.1) // allow for code execution
global iRed // declare diode current as global variable
global iTotal // declare total current as global variable
private string datain1 // declare private variable for 1st data value
private string datain2 // declare private variable for 2nd data value
datain1 = device.ardyRb.ReadUntil(13) // parse out 1st value
datain2 = device.ardyRb.ReadUntil(13) // parse out 2nd value
iRed = strToDouble(datain1) // convert characters to numerical values
iTotal = strToDouble(datain2) // and assign to declared variables
Listing 2-10Toggle Red LED DAQFactory Quick Sequence with Diode Power Draw
device.ardyRb.Purge() // clear serial buffer
device.ardyRb.Write('R') // initiate repeat activation
delay(0.1) // allow code to execute
global ldCurrnt // declare global variable in DAQFactory code
private string datain // define local variable in DAQFactory code
datain = device.ardyRb.readUntil(13) // parse out character codes for numeric value
ldCurrnt = strToDouble(datain) // convert character codes to numeric value
Listing 2-9Toggle Red LED DAQFactory Quick Sequence
// Toggle leds on/off from DAQFctry button icons on COM4
// The DAQF QS sends an R, G, O or Y to the serial port on com // 4\. On the
arduino side the status of the appropriate led dp is // determined and
toggled as required through a switch construct.
//
// power drawn calculations, each led has a CLR and the voltage // on the
junction of the resistor and led is measured and used to // calculate diode
// current by A0 to A3 respectively. Current calcln only done // when diode activated.
//
const int RedLedPin = 3; // red led is on dig pin 3
const int GreenLedPin = 4; // green led on dp 4
const int OrangeLedPin = 5; // orange led on dp 5
const int YellowLedPin = 6; // yellow led on d pin 6
//
int oofR = 0; // on off flags initialized
int oofG = 0;
int oofO = 0;
int oofY = 0; // on off flags initialized
//
char incomingByte = ' '; // define incoming character
//
float iRed = 0; // red led current in decimal float format
float iGreen = 0;
float iOrange = 0;
float iYellow = 0;
float itotal = 0;
//
void setup() {
Serial.begin(9600); // start the serial port
}
//
void loop()
{
if (Serial.available()) // check for incoming data
{
char incomingByte = Serial.read(); // set char value for switch branching
// Serial.print(incomingByte); // diagnostic
switch(incomingByte) // branch to desired location/option
{
case 'R': // Red Led Activation
if (oofR == 0 ) {
pinMode(RedLedPin, OUTPUT); // set pin I/O
digitalWrite(RedLedPin, HIGH); // turn led on
oofR = 1; // set flag
iRed = ((analogRead(A0)* 4.8828)/216); // calc i when led on
//Serial.print(analogRead(A0)); // diagnostics
//Serial.print("iRed = "); // diagnostics
Serial.println(iRed); // add CR-LF
itotal = iRed + iGreen + iOrange + iYellow; // calculate total power consumption
//Serial.print("itotal = "); // diagnostics
Serial.println(itotal); // add CR-LF
}
else { // flag is set to 1 so led is on
pinMode(RedLedPin, OUTPUT); // set pin mode to output
digitalWrite(RedLedPin, LOW); // turn led off
oofR = 0; // re-set flag to off
iRed = 0; // turn iRed contribution to itotal off
Serial.println(iRed); // send data to DAQFtry
itotal = iRed + iGreen + iOrange + iYellow; // calculate total current draw
//Serial.print("itotal = "); // diagnostics
Serial.println(itotal); // send to serial port with CR-LF
}
break;
//
case 'G': // Green Led Activation
if (oofG == 0 ) { // check status flag
pinMode(GreenLedPin, OUTPUT); // set pin I/O
digitalWrite(GreenLedPin, HIGH); // turn led on
oofG = 1; // reset status flag
iGreen = ((analogRead(A1)*4.8828)/215); // calc diodecurrent
//Serial.print("iGreen = "); // diagnostics
Serial.println(iGreen); // send data with CR-LF
itotal = iRed + iGreen + iOrange + iYellow; // calculate total current draw
//Serial.print("itotal = "); // diagnostics
Serial.println(itotal); // send with CR-LF
}
else {
pinMode(GreenLedPin, OUTPUT); // set pin I/O mode
digitalWrite(GreenLedPin, LOW); // turn green led off
oofG = 0; // set green status flag
iGreen = 0; // turn green contributionto total off
Serial.println(iGreen); // send green current value with CR-LF
itotal = iRed + iGreen + iOrange + iYellow; // calculate total current draw
//Serial.print("itotal = "); // diagnostic
Serial.println(itotal); // send total current with CR-LF
}
break;
//
case 'O': // Orange Led Activation
if (oofO == 0 ) { // check status flag
pinMode(OrangeLedPin, OUTPUT); // set pin I/O
digitalWrite(OrangeLedPin, HIGH); // set pin I/O
oofO = 1; // set orange flag to led on
iOrange = ((analogRead(A2)*4.8828)/215); // calculate orange led current draw
//Serial.print("iOrange = "); // diagnostic
Serial.println(iOrange); // send to serial port with CR-LF
itotal = iRed + iGreen + iOrange + iYellow; // calculate total current draw
//Serial.print("itotal = "); // diagnostic
Serial.println(itotal); // send total to serial port with CR-LF
}
else { // orange led is on
pinMode(OrangeLedPin, OUTPUT); // set pin I/O
digitalWrite(OrangeLedPin, LOW); // turn orange led off
oofO = 0; // reset orange status flag to off
iOrange = 0; // turn orange contribution to total off
Serial.println(iOrange); // send out orange current with CR-LF
itotal = iRed + iGreen + iOrange + iYellow; // calculate total current draw
//Serial.print("itotal = "); // diagnostics
Serial.println(itotal); // send out total current draw with CR-LF
}
break;
case 'Y': // Yellow Led Activation
if (oofY == 0 ) { // led is off
pinMode(YellowLedPin, OUTPUT); // set pin I/O
digitalWrite(YellowLedPin, HIGH); // turn yellow led on
oofY = 1; // re-set lag to led on
iYellow = ((analogRead(A3)*4.8828)/217); // calculate yellow led current
//Serial.print("iYellow = "); // diagnostic
Serial.println(iYellow); // yellow led value to serial port with CR-LF
itotal = iRed + iGreen + iOrange + iYellow; // calculate total current draw
//Serial.print("itotal = "); // diagnostic
Serial.println(itotal); // send to serial port with CR-LF
}
else { // yellow led on
pinMode(YellowLedPin, OUTPUT); // set pin I/O mode
digitalWrite(YellowLedPin, LOW); // turn yellow led off
oofY = 0; // re-set flag to yellow led off
iYellow = 0; // set yellow led current to 0
Serial.println(iYellow); // send value to serial port with CR-LF
itotal = iRed + iGreen + iOrange + iYellow; // calculate total current and send with CR-LF
//Serial.print("itotal = "); // diagnostic
Serial.println(itotal); // send total current with CR-LF
}
break;
}
}
}
Listing 2-8Arduino Sketch for a DAQFactory Four-Button Control Screen and Power Consumption Indicators
// Toggle an led on/off from one DAQFctry button icon on COM4
// The DAQF QS sends an R to the serial port on com 4\. On the
// arduino side the status of the RedLed dp is determined and
// toggled as required.
//
const int RedLedPin = 3; // red led is on dig pin 3
int oofR = 0; // power state of red diode
char incomingByte = ' '; // declare incoming byte
float iRed = 0; // red led current
//
void setup() {
Serial.begin(9600); // start the serial port
pinMode(RedLedPin, INPUT); // must initially read the dig. pin
}
//
void loop() {
if (Serial.available()) { // check for incoming data
char incomingByte = Serial.read();
//Serial.print(incomingByte); // diagnostic for code de-bugging
if (incomingByte == 'R' && oofR == 0) { // check action required and status
pinMode(RedLedPin, OUTPUT); // set pin I/O mode
digitalWrite(RedLedPin, HIGH); // turn diode current on
iRed = ((analogRead(A0) * 4.8828)/216 ); // calculate diode current
Serial.println(iRed); // send value to serial port with LF-CR
oofR = 1; // set status flag to "diode on"
}
else {
if (incomingByte == 'R' && oofR == 1){ // alternate action toggle to off
pinMode(RedLedPin, OUTPUT); // set pin I/O mode
digitalWrite(RedLedPin, LOW); // turn power off
iRed = 0; // set red diode current to 0
Serial.println(iRed);
oofR = 0;
}
}
}
}
Listing 2-7Arduino Code for Single Button Icon Toggling LED On/Off with Power Measurement
device.ardyRb.Purge() // clear old data from the serial port buffer
device.ardyRb.Write('0') // write a zero to the serial port to switch led off
delay(0.1) // allow code to be processed
global ldCurrnt // declare individual diode current to be global
ldCurrnt = 0 // set individual diode current to 0
Listing 2-6Quick Sequence Alternate Code for Off Button
device.ardyRb.Purge() // clear residual data from input buffer
device.ardyRb.Write('1') // write to serial port
delay(0.1) // delay to allow processing
global ldCurrnt // declare variable to be visible throughout
// DAQFactory program
private string datain // declare datain variable
datain = device.ardyRb.readUntil(13) // parse data up to line feed and carriage return
ldCurrnt = strToDouble(datain) // convert character to numeric value
Listing 2-5Quick Sequence Code for On Button
// Arduino code for a single led illumination on the red board // Arduino
the pgm waits for an incoming character on com port 4 // if a 1 the led is turned on
// if a 0 it is turned off.
// A0 is wired to Rd led junction and the Arduino calculates // the led current and
prints the value to the serial port.
//
const int RedPin = 3; // red board dig. pin with red led and clr
int incomingByte; // a variable to hold incoming byte
float iRed = 0; // the led current through the CLR
//
void setup() {
Serial.begin(9600); // start the serial port
pinMode(RedPin, OUTPUT); // set the pin function
}
void loop() {
if(Serial.available()> 0) { // check port for last data byte
incomingByte = Serial.read(); // read serial port value
if (incomingByte == '1') { // if is 1, turn the led on
digitalWrite(RedPin, HIGH); // set I/O of pin
// calculate led current and print to the serial port
iRed = ((analogRead(A0) * 4.8828 )/216);
Serial.println(iRed); // note the line feed indication to append 013\010
// to the transmitted character to aid in the DAQFactory parsing of the incoming code
.
}
//
if (incomingByte == '0') {
digitalWrite(RedPin, LOW); // if 0, turn the led off
// calculate led current and print to the serial port
iRed = ((analogRead(A0) * 4.8828 )/216); // ensures the LED is off
Serial.println(iRed); // \013\010 for DAQFactory parsing code
}
}
}
Listing 2-4Arduino Sketch to Turn Red LED On or Off and Measure the Diode Current Draw for Display on the DAQFactory Control Screen
// Toggle leds on/off from DAQFctry button icons on COM4
// The DAQF QS sends an R, G, O or Y to the serial port on com 4\. // On the
arduino side the status of the appropriate led // dp is determined and
toggled as required through a switch construct.
//
const int RedLedPin = 3; // red led is on dig pin 3
const int GreenLedPin = 4; // green led on dp 4
const int OrangeLedPin = 5; // orange led on dp 5
const int YellowLedPin = 6; // yellow led on d pin 6
//
int oofR = 0; // on off flags initialized
int oofG = 0;
int oofO = 0;
int oofY = 0; // on off flags initialized
//
char incomingByte = ' '; // define incoming character
//
void setup() {
Serial.begin(9600); // start the serial port
}
//
void loop()
{
if (Serial.available()) // check for incoming data
{
char incomingByte = Serial.read(); // set char value for switch branching
Serial.print(incomingByte); // diagnostic for use in debugging code
switch(incomingByte) // branch to desired location/option
{
case 'R': // Red Led Activation
if (oofR == 0 ) { // check status flag
pinMode(RedLedPin, OUTPUT); // set pin I/O
digitalWrite(RedLedPin, HIGH); // turn led on
oofR = 1; // re-set flag
}
else { // flag is set to 1 so led is on
pinMode(RedLedPin, OUTPUT); // set pin mode to output
digitalWrite(RedLedPin, LOW); // turn led off
oofR = 0; // re-set flag to off
}
break;
//
case 'G': // Green Led Activation
if (oofG == 0 ) { // check status flag
pinMode(GreenLedPin, OUTPUT); // set pin I/O
digitalWrite(GreenLedPin, HIGH); // turn led on
oofG = 1; // reset status flag
}
else {
pinMode(GreenLedPin, OUTPUT);
digitalWrite(GreenLedPin, LOW);
oofG = 0;
}
break;
//
case 'O': // Orange Led Activation
if (oofO == 0 ) {
pinMode(OrangeLedPin, OUTPUT); // set pin I/O
digitalWrite(OrangeLedPin, HIGH);
oofO = 1;
}
else {
pinMode(OrangeLedPin, OUTPUT);
digitalWrite(OrangeLedPin, LOW);
oofO = 0;
}
break;
case 'Y': // Yellow Led Activation
if (oofY == 0 ) {
pinMode(YellowLedPin, OUTPUT); // set pin I/O
digitalWrite(YellowLedPin, HIGH);
oofY = 1;
}
else {
pinMode(YellowLedPin, OUTPUT);
digitalWrite(YellowLedPin, LOW);
oofY = 0;
}
break;
}
}
}
Listing 2-3Arduino Sketch to Toggle Multiple Colored LEDs from a DAQFactory Control Screen
// Toggle an led on/off from one DAQFctry button icon on COM4
// The DAQF QS sends an R to the serial port on com 4\. On the
// arduino side the status of the RedLed dp is determined and
// toggled as required.
//
const int RedLedPin = 3; // red led is on dig pin 3
int oofR = 0; // power state of red diode
char incomingByte = ' '; // declare incoming byte
//
void setup() {
Serial.begin(9600); // start the serial port
pinMode(RedLedPin, INPUT); // must initially read the dig. pin
}
//
void loop() {
if (Serial.available()) { // check for incoming data
char incomingByte = Serial.read(); // read the port
//Serial.print(incomingByte); // diagnostic
if (incomingByte == 'R' && oofR == 0) { // check flag for led status
pinMode(RedLedPin, OUTPUT); // set pin for output
digitalWrite(RedLedPin, HIGH); // if off turn on
oofR = 1; // set status flag
}
else {
if (incomingByte == 'R' && oofR == 1){ // check flag for led status
pinMode(RedLedPin, OUTPUT); // set pin mode
digitalWrite(RedLedPin, LOW); // turn led off
oofR = 0; // set status flag
}
}
}
}
Listing 2-2Arduino Sketch for Toggling the Red LED on the Arduino RedBoard from the DAQFactory Single-Button Control Screen
// Arduino code for a single led illumination on the red board // Arduino
the pgm waits for an incoming character on com port 4, // if a 1 the led is turned on
if a 0 it is turned off.
const int RedPin = 3; // red board dig. pin with red led and clr
int incomingByte; // a variable to hold incoming byte
//
void setup() {
Serial.begin(9600); // start the serial port
pinMode(RedPin, OUTPUT); // set the pin function
}
void loop() {
if(Serial.available()> 0) { // check port for last data byte
incomingByte = Serial.read(); //
if (incomingByte == '1') { // if is H (ASCII 72), turn the led on
digitalWrite(RedPin, HIGH);
}
if (incomingByte == '0') {
digitalWrite(RedPin, LOW); //if L (ASCII, 76), turn the led off
}
}
}
Listing 2-1Arduino Code for a Two-Button On and Off Control Screen
摘要
-
开发了能够在外部实验中激活多个组件并显示来自该实验的数据的交互式控制面板 GUI。
-
微控制器可以与强大的工业预配置 SCADA 系统一起使用,或者与容易获得的廉价元件和适当的编程一起使用。
-
在第三章,将会介绍更详细的脚本和编程技术。
-
建筑科学仪器第四版。,Moore,Davis 和 Coplan,剑桥大学出版社,ISBN 978-0-521-87858-6 精装本
-
电子艺术第二版 Edn。,Horowitz 和 Hill,剑桥大学出版社,ISBN 13 978-0-521-37095-0 精装本
-
发明家实用电子学第三版 Edn。,谐谑曲与蒙克,麦格劳希尔,ISBN 978-0-07-177133-7
2
-
树莓派用户指南,厄普顿和哈尔法克里,约翰威利父子公司,ISBN 978-1-11846446-5
-
实用树莓派,霍兰,阿普瑞斯,ISBN 978-1-4302-4971-9
-
用 Linux 学习 Raspberry Pi,Membrey and Hows,Apress,ISBN 978-1-4302-4821-7
三、脚本简介
SCADA 是一个工业概念,其中收集关于活动过程的信息,然后用于监视和控制该操作。在工业规模的应用和这些科学测量实验中,脚本允许过程控制或数据采集的自动化。在本章中,在 DAQFactory (DF)软件中汇编成称为序列的小程序的代码将用于控制和监控在前面练习中装配在试验板上的 led 电路。
DF 用户手册指出,用于创建序列的脚本语言语法类似于大多数标准语言,如 C、Python、Visual Basic、Pascal 和其他语言(如 Fortran)的变体。
前面关于通道命名的符号也适用于 DF 中使用的脚本语言。该语言区分大小写,因此在命名通道、变量、脚本和页面时避免键入错误和拼写不匹配非常重要。建议使用 C 风格的命名或变体,如这些 MySpecialName、My_Special_Name、My_Spcl_Nm 和 MySpclNm 示例中所示。选择有表现力和有意义的名字以减少错误。
强烈建议使用注释和代码段缩进形式的文档,以使脚本代码清晰易懂。当 Tab 键被按下时,用于创建序列的 DAQFactory 代码编辑器缩进,一条垂直虚线描述代码块。其他研究人员必须能够遵循代码脚本和复制任何科学工作。
可用于脚本序列的数学运算在 DAQFactory 软件手册的“表达式”一节中有所描述表达式是从一些初始值计算结果的公式。在之前的练习中,变量值屏幕组件中使用了表达式来计算单个彩色 LED 电流和汲取的总电流。
与大多数语言一样,变量或数组在序列脚本中使用之前,必须用声明语句声明,有适当的名称,并创建实例。
对于大多数研究人员来说,创建和运行编程或脚本代码所需的技能最好通过实践来培养。几乎所有当今广泛使用的流行编程语言都可以通过大量的在线教程来学习。教程和语言文档可以以研究者感到舒适的速度复习和练习。DAQFactory 手册有一个介绍性的教程和一个详细的文档,当研究人员开发 DAQFactory 序列代码的脚本时,应该将它们放在手边作为参考。
实验的
一旦机电系统配置完成,硬件在试验板上得到验证,请不要犹豫,在全新的 DAQFactory 页面上尝试脚本代码。科学本质上是实验性的,这篇手稿有望帮助掌握物理计算的基础知识,并尽快应用它们进行实验测量。
五金器具
使用之前练习中的多色 LED 电路作为过程操作,其控制将从直接手动屏幕控制转移到编码脚本或序列。
电路原理图见第章 2 ,图 2-2 。
软件
需要页面组件
对于所需的基本屏幕配置,文本消息应该放在按钮控件上。文本内容应该表明该按钮控制一个脚本的开始和停止,该脚本在四个彩色 led 上产生一个简短的“灯光秀”。
在前面使用频道的练习中,频道必须已经创建并输入到频道表中,才能出现在弹出的键入帮助列表中。对于必须命名并输入到序列汇总表中的脚本序列也是如此。一旦命名并输入汇总表,在按钮配置期间可从列表中选择合适的序列,如图 3-1 和 3-2 所示。
图 3-2
命名序列条目列表
图 3-1
按钮操作标签条目
脚本
DAQFactory 有一个脚本输入和编辑程序,用于汇编代码,如图 3-3 所示。
图 3-3
LED 灯光秀脚本
灯光表演的脚本使用一组编码语句,将各个彩色 led 的通道输出电压值切换到 5 伏,然后将其重置为 0 伏。通过在光激活线之间嵌入延迟语句,并在迭代“for 循环”中封装代码块,可以创建“光表演”。图 3-3 代码中的文档有望一目了然。作者的二极管从左起依次为红色、绿色、橙色和黄色。因此,2 和 4 的偶数二极管是绿色和黄色,而奇数二极管是红色和橙色。
开始显示按钮可以与描述性文本组件组合在一起,形成一个面板,如图 3-4 所示。
图 3-4
脚本激活按钮
观察
当鼠标点击启动显示按钮时,四个 LED 灯组上出现灯光显示。
当灯光秀序列在一台 CPU 运行频率为 1.48 GHz、内存为 736 MB 且配有高分辨率显卡的旧台式机上运行时,在之前的练习中创建的电源监控面板只能跟上半秒延迟的灯光秀计时,而图形显示则不能。
讨论
本练习演示了 SCADA 软件通过软件编程和 HMI 设备控制电子电路激活的能力。
用户手册中的详细信息描述了描述性文本屏幕组件与其他几个组件的使用,这些组件具有选项卡式属性窗口,允许设置某些属性和选择操作。描述性文本组件能够显示运行/停止消息,指示附加到屏幕组件的选定序列的状态。
脚本化的代码序列实际上以计算机的时钟速度运行,因此比屏幕显示快速变化的能力、HMI 的速度或人类视觉能够跟随的速度快得多。
图形电源监视器显示无法跟上 LED 电流的脚本顺序切换,这表明了系统的局限性。DAQFactory 程序是一个视频显示密集型软件,如果没有足够的时间来绘制屏幕,显示就会滞后甚至不更新。在极少数情况下,就像旧的台式电脑一样,降低屏幕分辨率可以让反应迟钝的屏幕正常工作。
高速数据传输是光谱学、反应动力学和物理学中经常需要的专业领域。目前的工作重点是开发使用以秒或更长时间计量的时间尺度的方法。在适当的硬件或软件用户手册以及本文后面的章节中,将讨论更高速度的“数据流”以获得更快的捕获速率。
DAQFactory 序列:Arduino LED 阵列
在第二章中,一个廉价的微控制器板被用来代替一个稳定的工业级接口,以响应 SCADA 系统中设置的控制屏幕。如果实验者能够花时间重新编写串行通信代码,以监控安装 Arduino 的 LED 阵列的功耗,从而适应脚本化的灯光表演,那么使用 Arduino 的低成本优势就可以在这个脚本练习中实现。
实验的
Arduino 微控制器配有四个不同颜色的二极管,如第二章、图 2-9 所示。Arduino 保存清单 3-1 的 C 程序,提供所需的 LED 照明,而以下程序清单 3-2 中的常规或快速序列 DAQFactory 代码将适当的字符写入串行端口(本章末尾的“代码清单”部分提供了所有代码清单)。
DAQFactory 控制面板的设置如图 3-5 所示。
图 3-5
双按钮脚本激活屏幕
虽然快速序列码和常规序列码相同,但快速序列码仅通过快速序列选择可见。常规序列可以在 DAQFactory 的任何地方使用,并且可以在所有序列选择列表中看到。
讨论
图 3-3 中描述的 DAQFactory 代码列表利用通道在 5v 和地之间改变 LabJack 输出连接。发送到串行端口的大写或小写序列码由 Arduino 逻辑收集,并直接为连接到相应 LED 的数字引脚供电,而无需使用复杂的通道。
树莓派
RPi 使用 Python 以及 gpio 和 gpiozero Python 库与 GPIO 阵列的各个引脚进行通信,并直接控制这些引脚。RPi 只能在输出模式下将引脚设置为高电压或低电压,或者在输入模式下读取引脚状态为高电压或低电压。
通过仔细的设计和精心的编程,可以组装一个“灯光秀”,直接从 GPIO 引脚运行,而不需要任何中间硬件。如第章第一部分图 1-16 中所述,GPIO 阵列有两个版本:早期型号有 26 个引脚,而新型号有 40 个。第一个 26 针阵列为所有型号所共有,而较新版本的 RPi 有额外的 14 针,如图 1-16 所示。总之,40 针阵列由 26 个 GPIO 针、2 个 3.3 伏和 2 个 5 伏电源针、8 个接地针和 2 个串行输入输出针组成,其分配和位置详见表 3-1 。
表 3-1
RPi GPIO 引脚阵列的分配和定位
|  |清单 3-3 中是一个用 Python 代码编写的简单的四 LED“灯光秀”程序。
在第二章中,RPi 能够短时间为四个 led 供电,而 Python 程序读取 ADC 电压并计算 GPIO 阵列的总功耗。在本练习中,脚本创建照明的定时序列,以产生简单的“灯光秀”如果原型板上增加更多光源,以增加显示器的视觉吸引力,引脚输出应进行缓冲,以避免 RPi 的电流供应能力过载。
第一章中使用的 CD4050 六路同相集成电路等高输入阻抗缓冲芯片可以用来缓冲 GPIO 引脚,以处理许多小电流负载,而 ULN2803 达林顿晶体管阵列等芯片可以处理 8 个缓冲 GPIO 引脚中每个引脚的最高 500 mA 电流。(CMOS 4050 高阻缓冲芯片 0.50 CDN,ULN2803 芯片 2.50 CDN。)
表 3-1 显示了 SBC 上 0.1 英寸(2.45 毫米)间距阵列中的 GPIO 引脚名称及其位置。(从 RPi 板的顶部看,阵列在右侧,1 号引脚在顶部,而左右两列的 20 号引脚在底部,与 USB 连接器相邻。)
借助足够强大的辅助电源和 RPi 引脚的 CMOS 或达林顿对缓冲,脚本应该能够控制多达 26 个 led。
代码列表
列表 3-1 到 3-3 提供了本章的完整程序。
# Led "Light Show" Ex. 3 Scripting on Raspberry Pi
# Pins are numbered sequentially from the top down in the right
# and left columns for ease of assignment and counting when
# wiring jumpers
from gpiozero import LED
from time import sleep
# Define and assign the leds
redLed = LED(2) # left column pin 2
grnLed = LED(3) # left column pin 3
orngLed = LED(4) # left column pin 4
yelLed = LED(5) # left column pin 15
# repeat code for flashing 4 times
for i in range(4):
redLed.on()
grnLed.on()
orngLed.on()
yelLed.on()
sleep(1)
redLed.off()
grnLed.off()
orngLed.off()
yelLed.off()
sleep(1)
# reoeat code for streaming to left 4 times
for i in range(4):
redLed.on()
sleep(0.1)
redLed.off()
grnLed.on()
sleep(0.1)
grnLed.off()
orngLed.on()
sleep(0.1)
orngLed.off()
yelLed.on()
sleep(0.1)
yelLed.off()
# repeat code for streaming to the right 4 times
for i in range(4):
yelLed.on()
sleep(0.1)
yelLed.off()
orngLed.on()
sleep(0.1)
orngLed.off()
grnLed.on()
sleep(0.1)
grnLed.off()
redLed.on()
sleep(0.1)
redLed.off()
# repeat code for alternate pair flashing 4 times
for i in range(4):
redLed.on()
orngLed.on()
sleep(1)
redLed.off()
orngLed.off()
grnLed.on()
yelLed.on()
sleep(1)
grnLed.off()
yelLed.off()
Listing 3-3Raspberry Pi Scripted “Light Show”
// Scripted Control of 4 Leds on an Arduino MC for a Simple
// Light Show DAQFactory script uses serial port transmission
// to control MC. Buttons on a DAQFactory control screen
// activate a quick sequence or regular sequence scripting, to
// transmit the led activation codes to the serial port where
// the Arduino resident C code parses the commands and
// activates the appropriate diode.
// Main loop iterates four times. May 21, 2019
//
for (Private.Counter = 0, Counter < 4, Counter ++)
// even diodes lit
device.ardyRb.Write('G') // light the green led
device.ardyRb.Write('Y') // light the yellow led
delay(0.5) // leave the lights on for 1/2 sec.
device.ardyRb.Write('g') // green led off
device.ardyRb.Write('y') // yellow led off
delay(0.5) // keep lights off for 1/2 sec
// odd numbered diodes lit
device.ardyRb.Write('R') // red on
device.ardyRb.Write('O') // orange on
delay(0.5) // time delay
device.ardyRb.Write('r') // red off
device.ardyRb.Write('o') // orange off
delay(0.5) // time delay
endfor
// run lights to right
for (Private.Counter = 0, Counter < 4, Counter ++)
device.ardyRb.Write('R') // red on
delay(0.1) // on for 1/10 sec
device.ardyRb.Write('r') // red off
device.ardyRb.Write('G') // green on
delay(0.1) // on for 1/10 sec
device.ardyRb.Write('g') // green off
device.ardyRb.Write('O') // orange on
delay(0.1) // on for 1/10 sec
device.ardyRb.Write('o') // orange off
device.ardyRb.Write('Y') // yellow on
delay(0.1) // on for 1/10 sec
device.ardyRb.Write('y') // yellow off
endfor
//
delay (0.5)
// run lights to left
for (Private.Counter = 0, Counter < 4,Counter ++)
device.ardyRb.Write('Y') // yellow on
delay(0.1) // on for 1/10 sec
device.ardyRb.Write('y') // yellow off
device.ardyRb.Write('O') // orange on
delay(0.1) // on for 1/10 sec
device.ardyRb.Write('o') // orange off
device.ardyRb.Write('G') // green on
delay(0.1) // on for 1/10 sec
device.ardyRb.Write('g') // green off
device.ardyRb.Write('R') // red on
delay(0.1) // on for 1/10 sec
device.ardyRb.Write('r') // red off
endfor
Listing 3-2DAQFactory Regular Sequence Code for Light Show
// Arduino code for multiple led illumination on the red board
// Arduino the prgrm waits for an incoming character on com
// port 4 and then processes the data to identify which led is
// to be turned on or off. R, G, O and Y turn the diode ON and
// r, g, o, and y turn the diode OFF.
//
int RedPin = 3; // red board dig. pin with red led and clr
const int GreenPin = 4; // red board dig. pin with green led and clr
const int OrangePin = 5; // red board dig. pin with red led and clr
const int YellowPin = 6; // red board dig. pin with yellow led and clr
char incomingByte = ' '; // variable to hold incoming byte
//
void setup() {
Serial.begin(9600); // start the serial port
pinMode(RedPin, OUTPUT); // set the pin function
pinMode(GreenPin, OUTPUT);
pinMode(OrangePin, OUTPUT);
pinMode(YellowPin, OUTPUT);
}
void loop() {
//
while (Serial.available() == 0) // wait for a character
{
// do nothing until data arrives
}
if (Serial.available() > 0) // a char has arrived
{
char incomingByte = Serial.read(); // set character comparison variable to new char
//Serial.print(incomingByte); //diagnostic
if (incomingByte == 'R' ) { // R sets the red led power to high
// Serial.print("logic OK"); // logic diagnostic
digitalWrite(RedPin, HIGH); // turn red led on
}
if (incomingByte == 'r' ) { // turn red led off
//Serial.print(incomingByte); // diagnostic
digitalWrite(RedPin, LOW); // r sets the red led power to low
}
if (incomingByte == 'G') { // G sets the green led power to high
digitalWrite(GreenPin, HIGH);
}
if (incomingByte == 'g') {
digitalWrite(GreenPin, LOW); // g sets the green led power to low
}
if (incomingByte == 'O') { // O sets the orange led power to high
digitalWrite(OrangePin, HIGH);
}
if (incomingByte == 'o') {
digitalWrite(OrangePin, LOW); // o sets the orange led power to low
}
if (incomingByte == 'Y') { // Y sets the yellow led power to high
digitalWrite(YellowPin, HIGH);
}
if (incomingByte == 'y') {
digitalWrite(YellowPin, LOW); // y sets the yellow led power to low
}
}
}
Listing 3-1Arduino LED Illumination Code
摘要
-
商业 SCADA 软件有一个脚本工具来增强内置的控制功能,并实现与远程过程或实验装置的通信。
-
由廉价易得的组件组装的 SCADA 系统需要使用计算平台的编程语言进行更详细的程序开发。
-
当主机屏幕用于输入和显示数据时,将在第四章中进一步开发脚本或编程技术。
四、从屏幕输入数据
控制系统必须包括从屏幕输入数据的能力,以便能够修改或改变序列或过程的操作。在本章中,从键盘输入的数值用于修改程序代码的脚本序列,该程序代码使 led 在预定的周期数内开关。此外,为功率控制模式创建了两个选项,其中二极管的照明从全开到全关循环,并且二极管输出强度从关到全亮度递增,以创建“渐变”或“渐隐”效果。LED 亮度由流经器件的电流决定。通过 LED 的最大电流由与二极管、电源和地串联的限流电阻(CLR)设置。通过 LED 的电流可以通过改变电源电压来调节。然而,通过电压变化控制二极管强度只能在器件导通所需的电压水平以上有效,通常为 1.8-3 伏。
在本章中,通过输入数据值的屏幕确认,创建了一个 DAQFactory 序列码,该序列码以固定数量的电压增量增加施加到二极管上的功率。本练习还展示了软件在通常称为“线程化”应用程序中同时运行两个序列的能力。
然后,各种控制和监测选项被组合在一个简单的图形用户界面(GUI)数据输入、过程控制面板中。
使用一种称为脉宽调制(PWM)的技术可以更有效地控制二极管强度。在 PWM 控制操作中,全功率被施加到被驱动的负载,作为一系列方波电压脉冲,其时间宽度以受控方式改变或者相对于增加或减少的时间周期被调制。方波电源的频率和功率应用的脉冲宽度都可以数字控制或从屏幕输入值调制。(有关 PWM 的应用和实施的更多详细信息,请参见第七章。)
已经开发了使用便宜得多的微控制器的替代屏幕数据输入练习。微控制器练习与之前的练习一样,对实现该练习所需的更复杂代码的解释很少。微控制器使用的细节将在手稿的后面介绍,在这一点上,这些相对便宜的设备的一些优点和缺点可以得到充分的理解。
五金器具
如第一章图 1-3 所示,为先前练习接线的红色 LED 电子电路将用于本次练习的一部分;绿色 LED 连接到 LabJack 端子板上的第一个模拟输出通道(A0)。根据图 4-1 中的示意图,AO 0 信号连接到 2N3904 晶体管的基极。
图 4-1
带数据输入设备的 DAQFactory 控制屏的原型电路
晶体管的集电极和发射极相连,因此基极电压通过限流电阻控制从+5 V 电源流向红色 LED 的电流大小。回想一下,晶体管是电流控制器件。进入晶体管的基极电流的大小由施加在基极电路中串联电阻上的电压决定。施加的电压由 LabJack HMI 的脚本控制 AO 0 输出设置。
如果修改了图 4-1 中所示的电路,实验者应确保限流电阻不允许电流超过所用二极管的最大规定值。
软件
需要页面组件
如图 4-5 所示的 DAQFactory 数据输入面板由总共八行组成,包括四个文本组件、两个用于数据输入的编辑框、两个用于启动序列的按钮和两个描述性文本组件。
为了组装控制面板,可以使用之前用于创建、定位和配置屏幕组件的技术,必要时,可以参考 DAQFactory 用户手册来放置和配置本练习中使用的新屏幕图标。
下表列出了组成成品控制面板的页面组件,如图 4-5 所示:
图 4-7
按钮组件多动作选择面板
图 4-6
描述性文本组件的配置面板
图 4-5
控制面板可改变 LED 照明重复次数
图 4-4
编辑框准备调整大小
图 4-3
编辑框主选项卡已完成
图 4-2
编辑框主选项卡,用于设置通道或变量值
-
文本信息用于识别面板/分组及其功能(顶行,黄色背景,黑色字体;从组件配置窗口的框中选择的文本和背景颜色;参见第章 1 ,图 1-6 。
-
配置一个编辑框(见图 4-2 和 4-3 )并贴上标签以识别和接收要输入面板的数据。“闪烁 led 重复次数”是图 4-1 中红色 LED 的闪烁次数。DAQFactory 中保存闪存编号索引的变量定义为 flsh_Rpts,并声明为自动启动序列中的全局变量(清单 4-1;本章末尾的“代码清单”部分提供的所有代码清单)。为了使正确的变量名自动出现在下拉列表中,如图 4-2 所示,突出显示“cycles”条目,变量名必须用一个短序列声明为全局变量,该序列在页面加载时自动运行(参见第三章中的图 3-1 )。面板中的第 2 行完全通过标题框和勾选“设置按钮按下时设置”复选框进行配置,如图 4-3 所示。“设置按钮标题”被输入到适当的框中,以出现在面板第二行的按钮上。当配置和编辑编辑框组件时,确保光标尖端在编辑框活动区域内,并且在单击鼠标左键之前按下 Ctrl 键,然后用图 4-4 所示的粗阴影边框突出显示编辑框本身。在编辑框高亮显示的情况下,点击鼠标右键可以调出图 4-2 和 4-3 的属性对话框。编辑框中输入的值将根据需要放入通道或变量中。
-
创建第二个编辑框以接收可变数值,该数值将使图 4-1 中绿色 LED 的强度或亮度从关闭循环至全亮度,然后返回关闭。保存衰落周期数的变量被声明为 fd_Rpts,其自动启动序列与 flash 索引相同。
-
在面板的第四行,数据输入框下面的静态文本标识输入的变量。为了增强对比度,黑色文本在绿色背景下书写。(参见章节 1 中的选项,图 1-6 )。
-
描述性文本组件构成了面板的第五行,用于直观地确认编辑框中输入的值,该值已被设置为脚本代码使用的脚本变量“flsh_Rpts”的值。图 4-6 显示了描述性文本组件的属性窗口。描述性文本组件需要标题、表达式和比较表。对照表中的条目在左栏中是一个数值,在右栏中是一个文本串。添加和删除按钮用于组装所需的表。组装和配置完成后,如果在单击 Enter 按钮时在编辑框中输入 0,描述性文本组件将查找变量 flsh_Rpts 的值,并在文本列中打印相应的条目“未输入值”在编辑框中输入 4 将导致在标题“闪光重复”后打印消息“四次”,本质上,比较表具有手边变量的数值范围,并且当变量落在定义的范围内时显示相应的文本消息。
-
第六行由左边的描述性文本和右边的按钮组成。一个贴有适当标签的按钮用于启动脚本控制的“n”次重复,红色二极管开/关闪烁。在图 4-5 中,DAQFactory 控制屏幕被配置为运行清单 4-2 中列出的常规序列码。LED 的电源由 RedLed 通道控制,其输出可在 LabJack I/O 0 引脚上找到。
-
在控制面板的第七行中是第二描述性文本组件,其被配置为报告绿色 LED 重复淡入/淡出振荡的次数。
-
描述性文本和第二个按钮构成了控制面板的第八行,也是最后一行。该按钮控制一对序列,第一个是红色 LED 闪烁序列,而第二个,在清单 4-3 中,是绿色 LED 的淡入/淡出代码。在图 4-7 中,显示了按钮组件的动作选项卡。下拉编辑框动作列表将显示一长串单项选择。如果在点击按钮时需要一个以上的动作,那么可以使用添加/删除上/下箭头来添加当点击按钮时要调用的动作。在本演示练习中,闪烁和淡入淡出序列同时运行。
为了视觉上的清晰,面板组件可用于为构成特定操作控制屏幕的各组活动屏幕组件创建背景。位于面板底部的粗体数字可用于关联主控制屏幕上注释和说明表中的条目。
脚本
清单 4-1 是使用屏幕组件改变变量内容时要采用的一个重要程序。清单 4-2 和 4-3 中详细说明了红色和绿色 led 的闪烁和渐变顺序,而在最小开启电压和最大电源电压之间改变绿色 LED 电压的 27 步过程可在清单 4-4 中找到。清单 4-3 通过调用 AnalogUp()和 AnalogDwn()函数,根据淡入/淡出效果的需要,逐步增加二极管的电压电平,从而改变绿色二极管的亮度。该序列用于从低到高的转换,第二个例程用于从高到低的转换,使系统能够在 1.3 秒内增加和降低 LED 亮度。
观察
红色二极管的开关周期是决定性的,因为灯处于最大亮度或关闭。施加于 2N3904 基极的电压或电流以一系列增量步进,会在半导体照明中产生“噪声”增加和减少。(参见“讨论”)
描述性文本组件应用程序是图标用法的一个非常简单的说明,但是提供了如何组装和操作比较表的概述。
变量循环索引中的数据输入是该技术的一个简单说明,两个不同脚本从一个按钮的双重启动说明了 DAQFactory 编程演示“线程”的能力,其中两个程序似乎同时执行。(线程是一个高级编程主题,如果实验控件需要,可以从 Python 编程的文献中详细研究。)
讨论
正如在打开和关闭红色 LED 电源的脚本代码中可以看到的,变量“flsh_Rpts”被声明为一个全局实体。当脚本被键入并且“应用和编译”按钮被成功点击时,全局变量“flsh_Rpts”出现在弹出的通道、变量和序列脚本的键入帮助列表中。
晶体管是电流的放大器。 1 任何由 DAQFactory 软件产生并最终表示为施加在 LabJack 的 AO 0 端子上的电压电平的信号都含有噪声。该噪声叠加在 DAQFactory 脚本产生的外加 DC 信号电平之上,通过保护晶体管基极免受过大电流影响的 10kω电阻放大。施加在 2N3904 基极上的信号噪声被晶体管的增益或放大系数 h fe 放大,通常值在 35 到 100 之间,以产生容易看到的闪烁和不规则的淡入或淡出过渡。
虽然在练习中使用了 PNP 晶体管,但是经过图 4-8 所示的改变,也可以使用 NPN(2n 3904 和 2N2222 是合适的 NPN 器件)。
图 4-8
NPN 和 PNP 功率控制
二极管开关的脚本代码包含在一个循环中,该循环的索引声明为全局类型变量,其值在屏幕编辑框中设置。相同的代码可用于标有 AnalogUp()和 AnalogDwn()的两个函数,在简单的开/关循环中,调用这两个函数来代替将模拟输出通道 A0 设置为 5v 或 0 的赋值语句。通过电压调节使照明强度上下步进的功能是改变输送给绿色二极管的功率的非常简单的方法。可能有许多更好的代码序列可以用来控制照明强度。(注:如果 PNP 和 NPN 晶体管互换,模拟向上和模拟向下将改变功能。)
U12 LabJacks 可以配置为 PWM 输出,以提供平滑的功率控制应用,而不是本练习中使用的粗略演示方法。如第七章所述,较新的数据采集和接口设备通常配有内置 PWM 设备。
使用 Arduino 微控制器进行屏幕数据输入
数据的屏幕输入也可以采取从 DAQFactory 中的控制面板生成的一系列数字控制值的形式。来自在主机上形成控制屏幕的一组 DAQFactory 图标的数值可以通过串行端口传递给微控制器,以将输入的数据转换成过程变化或实验控制动作。数据必须在低级位和字节或开/关通信级别通过两个不同计算系统之间的串行端口入口。尽管开/关识别能力在两个系统中都是以 ASCII(美国信息交换标准码)字符的形式组织的,但信息必须被转换成用于数学运算的数字整数或数字浮点值,或用于识别目的的字母字符。
正如前面的练习所介绍的,Arduino 微控制器可以通过串行端口进行控制。除了微控制器板的成本低得多之外,微控制器还有许多更新的特性,例如可编程硬件定时器,可以用来改变 5 V 电脉冲的时间宽度,以实现脉宽调制。(详见第七章。)
Arduino 通常有 14 个数字 I/O 引脚,其中 6 个可以提供 PWM 电源控制。
通过串行端口将 SCADA 软件连接到 Arduino 微控制器会限制电子设备一次处理一个信号。无论有多少数据流在传输到串行端口之前被多路复用或混合,然后在微控制器端被解析回各自的数据流,一次只有一位通过串行连接。
第十一章介绍了串行连接及其使用的更多细节。
实验的
为了实现使用 Arduino 微控制器代替 LabJack U12 来演示用于控制和接收来自微处理器的显示数据的控制屏幕数值输入,可以使用第二章图 2-9 中描述的数字引脚和 ADC 输入。
在所有 DAQFactory-Arduino 编程中,只有一个程序可以控制串口。作者通常的做法是开发手头任务所需的 Arduino 代码,然后通过启动 Arduino 的串行监视器并将 DAQFactory 控制屏幕发送的字符串发送到微控制器代码来测试代码。一旦确认了正确的 Arduino 响应,Arduino 上的串行端口将被关闭,Arduino IDE 窗口将被最小化以在后台运行。一旦接收正确 DAQFactory 字符串以调用所需动作的 Arduino 代码在后台运行,就可以启动 DAQFactory 程序来开始控制会话,该 DAQ factory 程序包含要投入使用的控制屏幕页面。在本练习中,图中所示的 DAQFactory 屏幕驻留在 SCADA 软件中,该软件可以访问和控制串行端口,通过该端口可以发送字符,以便从连接到 Arduino I/O 连接的设备启动所需的操作。
创建了如图 4-9 所示的初始 DAQFactory 屏幕,以开始串行通信的渐进开发。
红色 led 开和红色 led 关按钮耦合到列表 4-5 的快速序列码,该快速序列码向串行端口发送 1 或 0,列表 4-6 的 Arduino 代码根据需要激活/禁用红色 led。Arduino 代码还读取测量的已知值限流电阻上的电压降,并将电流数据发送回串行端口,DAQFactory 快速序列码解析出电流数据,显示在控制屏幕上。
图 4-9
DAQFactory 控制屏幕,用于指导 Arduino 微控制器板上的操作
DAQFactory 控制屏幕左上角的第二个按钮耦合到第二个快速序列,该序列激活 Arduino 板上红色 led 的更复杂和更有效的切换。清单 4-7 和 4-8 的程序是管理切换效果的快速序列码和 Arduino 码。
图 4-10 描述了当 Arduino 板上的绿色和橙色 led 被 DAQFactory 控制屏幕上的相应按钮激活时获得的 DAQFactory 控制屏幕显示。COM4 监视器上串行端口动作的传输历史记录在图框的左下方。清单 4-9 和 4-10 控制控制面板的彩色按钮。
图 4-10
DAQFactory 控制屏幕,用于指导 Arduino 微控制器板上的多个操作
清单 4-9 和 4-10 包含通过点击彩色按钮激活的 DAQFactory 快速序列码,以及控制屏幕彩色按钮和变量值读数的响应 Arduino 码,如图 4-10 所示。
在图 4-5 中,使用 LabJack 设备和自动启动序列开发了 DAQFactory 屏幕数据输入面板,以声明保存要输入的弧线步进值所需的变量。DAQFactory 中的自动启动序列也会在加载包含数据输入面板的页面以供 Arduino 微控制器使用时激活。除了线程演示按钮之外,前面讨论的关于 DAQFactory 的所有功能在微控制器上都是活动的。(参见“讨论”)
如清单 4-11 所示,“带可变索引的 Tst 脚本”按钮与快速序列程序相连。快速序列依靠一个“for 循环”,执行“flsh_Rpts”次,向运行清单 4-6 中所列代码的 Arduino 发送开/关或“1”/“0”串行端口传输,以打开或关闭红色二极管。或者,清单 4-12 、 4-13 和 4-14 可用于操作 Arduino 的 PWM 功能,使微控制器板上的橙色二极管变暗和闪烁。(参见“讨论”)
观察
当屏幕数据输入使用带有 LabJack 的编辑框屏幕组件时,可以在闪烁和渐变编辑框中输入两个不同的值,当底部按钮“同步脚本”被激活时,两个二极管上的闪烁和渐变动作同时运行。(参见“讨论”)
LabJack 和 Arduino 屏幕和功能的其余部分按预期工作。
讨论
除了 DAQFactory 脚本语言,Python 是一种能够适应“线程化”的编程语言。线程的细节和应用是比这篇介绍性文章更高级的主题,要了解更多信息,可以参考 Python 的文献。
检查清单 4-8 中的 Arduino 代码会发现,为简单起见,用于确定红色 LED 状态的逻辑已经完全写入微控制器系统。如果微控制器位于远程位置,DAQFactory 控制屏幕的操作员需要了解红色 LED 或连接到数字引脚的设备的状态,则可以通过串行端口将一个标志连同当前提取的数据一起传回控制屏幕。
实验人员在 Arduino 上使用数字和 PWM 引脚时必须小心,因为 14 个数字 I/O 引脚中只有 6 个支持 PWM(即 Arduino 引脚 3、5、6、9、10 和 11 支持 PWM)。)
当使用 DAQFactory 串行端口时,如果 Arduino 代码要使用它来标记字符传输的结束,实验人员必须在每次传输的结尾手动添加一个换行 ASCII 标记。Arduino 串行监视器在视野的右下角有一个选择框,用于为当前的终端会话选择所需的行尾。
通过在 DAQFactory 序列脚本中计算时序和功率要求,并仅在需要时发送功率激活命令,使用 0–255 整数功率电平的 PWM 激活码可用于 LED 激活的渐变和闪烁模式。
Raspberry Pi:数据的屏幕输入
RPi 使用的 Python 语言的数据输入是通过 input 语句完成的。Python 中的 input 语句接受一个字符串值参数,然后可能需要将其转换为适当的整数或浮点数形式的数值。典型的屏幕输入代码如下(#标记注释行):
input_str = input("Enter the desired input characters", )
variable = int(input_str) # can only be used for non-floating point conversion of numbers
variable = float(input_str) # can only be used for floating point numeric strings
使用 Python 语言控制 led 可以使用名为 RPi 的基本库。GPIO 或更高级的功能库 gpiozero。这两个库的文档都可以在线获得。
由于 GPIO 阵列是一个数字输入/输出系统,如果不借助 PWM 和电容平滑,就不容易实现电压控制。(PWM 在第七章中介绍。)
演示使用 Python 和 RPi 在屏幕上输入数据的简单练习可以通过使 LED 闪烁来创建,闪烁长度根据屏幕输入值设置,重复次数也由屏幕输入设置。清单 4-15 中的 Python 程序产生图 4-11 的输出,并按照记录闪烁标称 LED。
图 4-11
数据程序 Python 屏幕输入的输出
有时,当外设连接到 GPIO 阵列时,打开 RPi 电源时,一些阵列引脚可能处于高电平或通电状态。清单 4-16 中列出了两个能够重置零或关闭活动引脚的实用程序。一个实用程序使用通道列表函数,第二个实用程序使用 Python 循环来处理 GPIO 阵列的各个引脚。
代码列表
# Clear, Turn Off or Reset the RPi GPIO array
#
import RPi.GPIO as GPIO
# set the pin identity mode
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
# Reset the array pins to off/false/0
chan_list = (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)
GPIO.setup(chan_list, GPIO.OUT)
GPIO.output(chan_list, GPIO.LOW)
Listing 4-16Python Code to Reset the GPIO Array
# Input of data from the control screen
#
import RPi.GPIO as GPIO
import time
# set the pin identity mode
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
# Reset the array pins to off/false/0
chan_list = (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)
GPIO.setup(chan_list, GPIO.OUT)
GPIO.output(chan_list, GPIO.LOW)
#
# Enter the number of the GPIO array pin connected to the LED to be activated
input_str = input("Input the GPIO pin number for the LED control exercise " , )
arry_pn_no = int(input_str)
#
# Input the number of times to repeat the flashing of the LED
input_str = input("Input the number of times to flash the LED ", )
rpts = int(input_str)
#
# Input the number of times to flash the LED in a second
input_str = input("Input the on time in seconds for the LED flash ", )
flsh_rt = int(input_str)
print("Array pin number = ",arry_pn_no, "Repeats = ", rpts, "Flash rate = ", flsh_rt)
#
print("Lighting the LED on GPIO pin ", arry_pn_no, "to flash ", rpts, "times for", flsh_rt, " seconds per flash")
#
for i in range(1, rpts + 1):
GPIO.output(arry_pn_no, GPIO.HIGH)
time.sleep(flsh_rt)
GPIO.output(arry_pn_no, GPIO.LOW)
time.sleep(flsh_rt)
# Clear the GPIO array
Print("GPIO array cleared")
GPIO.cleanup()
Listing 4-15Python Data Input from the Host Computer Screen
/* DAQFtry ardyRb PWM Led Control through serial port
Quick Sequence control of Orange led fade with arduino PWM
Arduino PWM requires a 0 - 255 integer to set the PWM DC.
This pgm uses the string to int function to convert a digit
based number into an integer to set the PMW value.
*/
String inString = " ";
byte pinOut = 5; // dig pin for orange led
int pwr_Vlu = 0;
//
void setup() {
Serial.begin(9600); // start serial port
pinMode(pinOut, OUTPUT); // set output pin
}
//
void loop() {
while (Serial.available() > 0) {
int inChar = Serial.read();
if (isDigit(inChar)) {
// cnvrt incoming byte to char and add to strng
inString += (char)inChar;
}
// if nuline convert accumulated to integer
if (inChar == '\n') {
pwr_Vlu = (inString.toInt());
Serial.print(pwr_Vlu);
//int twotimes = pwr_Vlu * 2;
//Serial.print(twotimes);
pinMode(pinOut, OUTPUT);
analogWrite(pinOut, pwr_Vlu);
// clear the string for new input
inString = " ";
}
}
}
Listing 4-14Arduino Code to Accept Digits from 0 to 255 to Be Used as PWM Power Application Requests
// Orng led flashed on/off with 255 and 0 PWM Arduino power level applications
global flsh_Rpts
//
for (Private.Counter = 0, Counter < flsh_Rpts, Counter ++)
//
device.ardyRb.Write("255" + Chr(10)) // turn led full on
delay (0.5) // delay 1/2 sec
device.ardyRb.Write("0" + Chr(10)) // turn led off
delay(0.5) // delay 1/2 sec
endfor
Listing 4-13DAQFactory Regular Sequence to Use the “flsh_Rpts” Screen-Entered Loop Index Counter
// Green Led on Arduino pin 5 to be cycled from full power to
// off from a DAQFctry script using the serial port and the
// edit box entry of the requested number of repeats, fd_Rpts.
// Start illumination decrease cycle
device.ardyRb.Write("255" + Chr(10))
delay(0.25)
device.ardyRb.Write("192" + Chr(10))
delay(0.25)
device.ardyRb.Write("128" + Chr(10))
delay(0.25)
device.ardyRb.Write("96" + Chr(10))
delay(0.25)
device.ardyRb.Write("64" + Chr(10))
delay(0.25)
device.ardyRb.Write("48" + Chr(10))
delay(0.25)
device.ardyRb.Write("32" + Chr(10))
delay(0.25)
device.ardyRb.Write("24" + Chr(10))
delay(0.25)
device.ardyRb.Write("16" + Chr(10))
delay(0.25)
device.ardyRb.Write("12" + Chr(10))
delay(0.25)
device.ardyRb.Write("8" + Chr(10))
delay(0.25)
device.ardyRb.Write("6" + Chr(10))
delay(0.25)
device.ardyRb.Write("4" + Chr(10))
delay(0.25)
device.ardyRb.Write("3" + Chr(10))
delay(0.25)
device.ardyRb.Write("2" + Chr(10))
delay(0.25)
device.ardyRb.Write("0" + Chr(10))
Listing 4-12DAQFactory Regular Sequence Code for Fading the Green LED on the Arduino Board
for (Private.Counter = 0, Counter < flsh_Rpts, Counter ++)
device.ardyRb.Write('1')
delay(0.5)
device.ardyRb.Write('0')
delay(0.5)
endfor.
Listing 4-11DAQFactory Quick Sequence for Flashing the Arduino-Mounted LED for the Number of Cycles Requested Through the Screen Data Entry Edit Box
// Toggle leds on/off from DAQFctry button icons on COM4
// The DAQF QS sends an R, G, O or Y to the serial port on com
// 4\. On the arduino side the status of the appropriate led
// digpin is determined and toggled as required through a
// switch construct.
//
// power drawn calculations, each led has a CLR and the voltage
// on the junction of the resistor and led is measured and used
// to calculate diode current by A0 to A3 respectively. Current
// calcln only done when diode activated.
//
const int RedLedPin = 3; // red led is on dig pin 3
const int GreenLedPin = 4; // green led on dp 4
const int OrangeLedPin = 5; // orange led on dp 5
const int YellowLedPin = 6; // yellow led on d pin 6
//
int oofR = 0; // on off flags initialized
int oofG = 0;
int oofO = 0;
int oofY = 0; // on off flags initialized
//
char incomingByte = ' '; // define incoming character
//
float iRed = 0; // red led current in decimal float format
float iGreen = 0;
float iOrange = 0;
float iYellow = 0;
float itotal = 0;
//
void setup() {
Serial.begin(9600); // start the serial port
}
//
void loop()
{
if (Serial.available()) // check for incoming data
{
char incomingByte = Serial.read(); // set char value for switch branching
// Serial.print(incomingByte); // diagnostic
switch(incomingByte) // branch to desired location/option
{
case 'R': // Red Led Activation
if (oofR == 0 ) {
pinMode(RedLedPin, OUTPUT); // set pin I/O
digitalWrite(RedLedPin, HIGH); // turn led on
oofR = 1; // set flag
iRed = ((analogRead(A0)* 4.8828)/216); // calc i when led on
//Serial.print(analogRead(A0)); // diagnostics
//Serial.print("iRed = "); // diagnostics
Serial.println(iRed); // add CR-LF
itotal = iRed + iGreen + iOrange + iYellow; // calculate total power consumption
//Serial.print("itotal = "); // diagnostics
Serial.println(itotal); // add CR-LF
}
else { // flag is set to 1 so led is on
pinMode(RedLedPin, OUTPUT); // set pin mode to output
digitalWrite(RedLedPin, LOW); // turn led off
oofR = 0; // re-set flag to off
iRed = 0; // turn iRed current contribution to itotal off
Serial.println(iRed); // send data to DAQFtry
itotal = iRed + iGreen + iOrange + iYellow; // calculate total current draw
//Serial.print("itotal = "); // diagnostics
Serial.println(itotal); // send to serial port with CR-LF
}
break;
//
case 'G': // Green Led Activation
if (oofG == 0 ) { // check status flag
pinMode(GreenLedPin, OUTPUT); // set pin I/O
digitalWrite(GreenLedPin, HIGH); // turn led on
oofG = 1; // reset status flag
iGreen = ((analogRead(A1)*4.8828)/215); // calc diodecurrent
//Serial.print("iGreen = "); // diagnostics
Serial.println(iGreen); // send data with CR-LF
itotal = iRed + iGreen + iOrange + iYellow; // calculate total current draw
//Serial.print("itotal = "); // diagnostics
Serial.println(itotal); // send with CR-LF
}
else {
pinMode(GreenLedPin, OUTPUT); // set pin I/O mode
digitalWrite(GreenLedPin, LOW); // turn green led off
oofG = 0; // set green status flag
iGreen = 0; // turn green contribution to total current off
Serial.println(iGreen); // send green current value with CR-LF
itotal = iRed + iGreen + iOrange + iYellow; // calculate total current draw
//Serial.print("itotal = "); // diagnostic
Serial.println(itotal); // send total current with CR-LF
}
break;
//
case 'O': // Orange Led Activation
if (oofO == 0 ) { // check status flag
pinMode(OrangeLedPin, OUTPUT); // set pin I/O
digitalWrite(OrangeLedPin, HIGH); // set pin I/O
oofO = 1; // set orange flag to led on
iOrange = ((analogRead(A2)*4.8828)/215); // calculate orange led current draw
//Serial.print("iOrange = "); // diagnostic
Serial.println(iOrange); // send to serial port with CR-LF
itotal = iRed + iGreen + iOrange + iYellow; // calculate total current draw
//Serial.print("itotal = "); // diagnostic
Serial.println(itotal); // send total current to serial port with CR-LF
}
else { // orange led is on
pinMode(OrangeLedPin, OUTPUT); // set pin I/O
digitalWrite(OrangeLedPin, LOW); // turn orange led off
oofO = 0; // reset orange status flag to off
iOrange = 0; // turn orange contribution to total off
Serial.println(iOrange); // send out orange current with CR-LF
itotal = iRed + iGreen + iOrange + iYellow; // calculate total current draw
//Serial.print("itotal = "); // diagnostics
Serial.println(itotal); // send out total current draw with CR-LF
}
break;
case 'Y': // Yellow Led Activation
if (oofY == 0 ) { // led is off
pinMode(YellowLedPin, OUTPUT); // set pin I/O
digitalWrite(YellowLedPin, HIGH); // turn yellow led on
oofY = 1; // re-set lag to led on
iYellow = ((analogRead(A3)*4.8828)/217); // calculate yellow led current
//Serial.print("iYellow = "); // diagnostic
Serial.println(iYellow); // yellow led current value to serial port wth CR-LF
itotal = iRed + iGreen + iOrange + iYellow; // calculate total current draw
//Serial.print("itotal = "); // diagnostic
Serial.println(itotal); // send to serial port with CR-LF
}
else { // yellow led on
pinMode(YellowLedPin, OUTPUT); // set pin I/O mode
digitalWrite(YellowLedPin, LOW); // turn yellow led off
oofY = 0; // re-set flag to yellow led off
iYellow = 0; // set yellow led current to 0
Serial.println(iYellow); // send value to serial port with CR-LF
itotal = iRed + iGreen + iOrange + iYellow; // calculate total current and send wth CR-LF
//Serial.print("itotal = "); // diagnostic
Serial.println(itotal); // send total current with CR-LF
}
break;
}
}
}
Listing 4-10Arduino Code Supporting DAQFactory Multiple-Button Colored Diode Selection with Power Consumption
device.ardyRb.Purge()
device.ardyRb.Write('R')
delay(0.1)
global iRed
global iTotal
private string datain1
private string datain2
datain1 = device.ardyRb.ReadUntil(13)
datain2 = device.ardyRb.ReadUntil(13)
iRed = strToDouble(datain1)
iTotal = strToDouble(datain2)
Listing 4-9DAQFactory Quick Sequence Code for Multiple-Button Control of Arduino LEDs
// Toggle an led on/off from one DAQFctry button icon on COM4
// The DAQF QS sends an R to the serial port on com 4\. On the
// arduino side the status of the RedLed digpin is determined
// and toggled as required. Led current calculated and written to
// Ser prt where DAQFtry parses out floating point current value.
//
const int RedLedPin = 3; // red led is on dig pin 3
int oofR = 0; // power state of red diode
char incomingByte = ' '; // declare incoming byte
float iRed = 0; // red led current
//
void setup() {
Serial.begin(9600); // start the serial port
pinMode(RedLedPin, INPUT); // must initially read the dig. pin
}
//
void loop() {
if (Serial.available()) { // check for incoming data
char incomingByte = Serial.read();
//Serial.print(incomingByte); // diagnostic
if (incomingByte == 'R' && oofR == 0) {
pinMode(RedLedPin, OUTPUT);
digitalWrite(RedLedPin, HIGH);
iRed = ((analogRead(A0) * 4.8828)/216 );
Serial.println(iRed);
oofR = 1;
}
else {
if (incomingByte == 'R' && oofR == 1){
pinMode(RedLedPin, OUTPUT);
digitalWrite(RedLedPin, LOW);
iRed = 0;
Serial.println(iRed);
oofR = 0;
}
}
}
}
Listing 4-8Arduino Code for Receiving the DAQFactory Control Screen Button Request to Toggle Red LED Illumination
device.ardyRb.Purge()
device.ardyRb.Write('R')
delay(0.1)
global ldCurrnt
private string datain
datain = device.ardyRb.readUntil(13)
ldCurrnt = strToDouble(datain)
Listing 4-7DAQFactory Quick Sequence Code to Toggle Red LED and Read the Power Consumption
// Arduino code for a single led illumination on the red board
// Arduino the pgm waits for an incoming character on com
// port 4 if a 1 the led is turned on if a 0 it is turned off.
// A0 is wired to Rd led junction and the Arduino calculates
// the led current and prints the value to the serial port.
//
const int RedPin = 3; // red board dig. pin with red led and clr
int incomingByte; // a variable to hold incoming byte
float iRed = 0; // the led current through the CLR
//
void setup() {
Serial.begin(9600); // start the serial port
pinMode(RedPin, OUTPUT); // set the pin function
}
void loop() {
if(Serial.available()> 0) { // check port for last data byte
incomingByte = Serial.read(); //
if (incomingByte == '1') { // if is 1, turn the led on
digitalWrite(RedPin, HIGH);
// calculate led current and print to the serial port
iRed = ((analogRead(A0) * 4.8828 )/216);
Serial.println(iRed);
}
//
if (incomingByte == '0') {
digitalWrite(RedPin, LOW); // if 0, turn the led off
// calculate led current and print to the serial port
iRed = ((analogRead(A0) * 4.8828 )/216); // ensures the LED is off
Serial.println(iRed);
}
}
}
Listing 4-6Arduino Code for DAQFactory Code of Listing 4-5
device.ardyRb.Purge()
device.ardyRb.Write('1')
delay(0.1)
global ldCurrnt
private string datain
datain = device.ardyRb.readUntil(13)
ldCurrnt = strToDouble(datain)
Listing 4-5DAQFactory Quick Sequence to Turn the Red LED On from the Button and Read the LED Current
//Analog Voltage is raised from 2.4 volts to 5.0
// in steps of 0.2v with a delay of 0.05 sec
// between increments. Rvn. Jan4/10
AnalogOut = 2.2
delay (0.05)
AnalogOut = 2.4
delay (0.05)
AnalogOut = 2.6
delay (0.05)
AnalogOut = 2.8
delay (0.05)
AnalogOut = 3.0
delay (0.05)
AnalogOut = 3.2
delay (0.05)
AnalogOut = 3.4
delay (0.05)
AnalogOut = 3.6
delay (0.05)
AnalogOut = 3.8
delay (0.05)
AnalogOut = 4.0
delay (0.05)
AnalogOut = 4.2
delay (0.05)
AnalogOut = 4.4
delay (0.05)
AnalogOut = 4.6
delay (0.05)
AnalogOut = 4.8
delay (0.05)
AnalogOut = 5.0
return()
Listing 4-4DAQFactory Regular Sequence for LED Illumination Intensity Variation
//Variable Intensity Flash varies the voltage of the
//AO 0 channel to raise and lower the intensity of the
//green LED
//Nov. 16/09
//
global fd_Rpts
AnalogOut = 0
for (Private.Counter = 0, Counter < fd_Rpts, Counter ++)
AnalogUp ()
AnalogDwn ()
endfor
AnalogOut = 0
Listing 4-3DAQFactory Code to Fade In and Out the Green LED Brightness
// Sequence Name --: TstSqncForLoopVariableReps
//Screen Entry of Alph-Numeric Values
//Oct9/09 and Nov. 13/09
//A screen Edit Box accepts entered values as a variable called
//flsh_Rps. The variable is declared as a global type with an
//auto-run sequence and is used as the loop counter value to
//vary the number of times the loop iterates.
//
global flsh_Rpts
//
for (Private.Counter = 0, Counter < flsh_Rpts, Counter ++)
//
RedLed = 5
delay(0.5)
RedLed = 0
delay(0.5)
//
endfor
Listing 4-2DAQFactory Code to Flash the Red LED a Variable Number of Times as Entered from the Control Screen
// Auto declare variables is a sequence that runs when the Main
// Screen page is run. Two variables are declared globally,
// flsh_Rpts and fd_Rpts representing the number of times to
// flash the red led and fade the green.
//
global flsh_Rpts
//
global fd_Rpts
Listing 4-1DAQFactory Auto-start Sequence Code to Declare Variables
摘要
-
在商业 SCADA 系统中需要脚本来输入从配置的主机屏幕 GUI 控制面板初始化和控制手边的过程所需的过程变量。
-
屏幕输入的过程或实验变量也可以输入到 SCADA 系统中,该系统由较便宜的组件和计算平台组装而成。
五、数字信号概念和数字信号输出
用于进行生物、化学或物理测量的大多数传感器产生连续可变的模拟电输出,而计算机和大规模集成电路使用高或低的电能水平来表示它们可以处理的二进制数字信号。监控和数据采集程序必须经常充当双向模数电子信号转换器。本章将开始开发二进制编码和数字电子学的应用,利用标准的 0 和+5 伏信号电平作为二进制 1 和 0 的表示。在许多表面贴装技术(SMT)器件中,逻辑电平为 0 和 3.3 V,无意中施加 5 V 信号常常会损坏 SMT 集成电路。
LabJack U12 人机界面(HMI)用户手册指出,U12 设备上提供了 20 条数字信号线,可设置为接收或输出 5 V 电信号。四条线通过 LabJack 上主螺丝端子板上的 I/O 0–I/O 3 连接提供,其余 16 条线通过机箱顶端的 DB-25 连接器提供。用户指南还建议实验者,主端子连接器上的四条 I/O 线由内部限流电阻保护,而 DB-25 连接器上的四条 I/O 线配有跳线引脚,以便在需要时绕过 1.5kω保护电阻。
可以通过几种方法物理访问 DB-25 行。LabJack 制造商可提供带有各线路连接端子的电缆和电路板。带焊接端子的 DB-25 连接器可从大多数电子供应商处购买。可以从旧的 DB-25 打印机电缆中创建一个便宜的接口,去掉不兼容的端连接器,并将单个线端镀锡以插入数字原型试验板。(参见第章 1 ,图 1-1 ,图 5-1 中的 1、3 项,HMI U12)。)
为了强调有关所用硬件的注意事项,请记住 LabJack 制造商为 DB-25 连接提供的电路板包含预安装的负载限制电阻,而从当地供应商处单独购买的 DB-25 连接器或使用从旧打印机拆下的电缆制造的连接器不包含负载限制电阻。在现场实验和 HMI 之间使用独立供电的缓冲连接(如 CMOS CD4050 hex 同相缓冲芯片)的理念消除了对 HMI 硬件瞬时损坏的担忧。
实验的
本练习将使用与之前练习相同的屏幕控制 led 照明程序,但此处扩展至 8 位,以演示基本的数字信号概念。
五金器具
LabJack Corporation 的 CB25 端子板(项目 2,39 美元)包括 DB-25 电缆(项目 3),用于将 U12 接口的附加终端数字 I/O 线连接到原型板上的 LED 阵列(项目 1)。
图 5-1 中的第 4 项是到显示图 5-3 中所示 DAQFactory 控制面板的主机的 USB 连接。连接设备顶部 LabJack DB-25 连接器的 DB-25 电缆提供了对 16 条数字 I/O 线的访问,其中前 8 条可用于本练习。
图 5-1
LabJack U12、CB25 端子板和 8 位 LED 阵列
如果由于易发生瞬变的电源而需要额外的硬件保护,八条数字信号线可以使用两个 CD4050 hex 缓冲/隔离芯片、八个 led 和限流电阻,如图 5-2 所示。
手边的部件应根据以下电路原理图进行组装。作者用旧的 DB-25 打印机电缆组装了一个初始原型,并将来自 U12 的隔离和识别的 D0–D7 数字 I/O 线直接插入原型板,以激活 CLR LED 阵列位。
图 5-2
8 位字节 LED 显示器原理图
软件
创建一个八按钮面板,每个按钮的标签如图 5-3 所示。本练习演示了使用 8 位字节的现场实验和主 SCADA 屏幕之间的单个数字线路连接的配置。
图 5-3
8 位字节 LED 显示控制面板
创建的每个按钮都被贴上标签,连接到其通道,然后设置为在 0 到 5 伏之间切换,如之前在第 1 ,图 1-9 到 1-11 中所做的那样。同时按下 Ctrl 键和单击鼠标左键允许实验者在单个屏幕组件的集合周围画一个框,这些组件可以通过编辑下拉菜单中的选择形成一个组。在组装更大更复杂的 GUI 屏幕时,可以根据需要使用鼠标、Ctrl 键和编辑菜单对组件进行分组和取消分组。
研究者也不应试图改变背景板上的成分。组件应该根据需要进行排列和配置、分组,然后根据需要通过使用 Ctrl 键和布局菜单中的顺序条目由背景面板支持。
计算被照亮位的十进制和的 DAQFactory 序列程序如清单 5-1 所示。
如图 1-10 所示,对于每个 DigOut_n 通道,代表 8 位的按钮链接到按钮组件动作选项卡中的“切换”选项。
观察
当二极管、DAQFactory 和屏幕配置正确时,单击任何一个按钮都会点亮或关闭代表一个字节数字数据的 8 位二极管组中的相应二极管。
图 5-3 描绘了点击评估按钮后,D1 (2 1 = 2)和 D2 (2 2 = 4)发光二极管亮起时的面板显示。要清除十进制和显示,请关闭所有二极管,然后单击评估按钮。
讨论
不从 HMI 设备或计算机电源为实验装置供电的总体原则与使用 LabJack 接口的无保护数字 I/O 线路特别相关。如第一章练习中所述,CD4050 缓冲器提供了一个虚拟零电流或“仅电压”感测电路,其中流入或流出数字线路的电流几乎为零,因为缓冲 IC 芯片的 CMOS 栅极电阻非常高。
这个练习演示了数字表示的基本原理,即能够以二进制格式直观地表示 0 到 255 之间的任何十进制值。从右边开始的字节发光二极管代表 2 0 或 1,2 1 或 2,2 2 或 4,2 3 或 8,以此类推直到 2 7 或 128。因此,通过手动点亮 1 和 2 或最右边的一对二极管中的 led 来表示十进制值 3,0000011 表示 2 0 和 2 1 。零表示没有发光二极管点亮,255 表示所有发光二极管都点亮。
为了与更大、更复杂的数据收集现场实验的装配的适当实验开发程序保持一致,在进行下一个使用 8 位和更大 led 组的练习之前,请确认所有 8 位都由按钮控制。
8 位字节可用于表示高达 255 的数值,当进行适当的软件调整时,阵列中每增加一个 LED 将使灯组所能显示的数值范围增加一倍。10 位系统可以表示 1024 个值,而 12 位系统可以显示 4096 个数值。
在处理模数转换时,理解二进制和十进制数值域的重要性变得显而易见。大量机电传感器是与数字数值处理系统不兼容的模拟信号发生器,它们的模拟输出需要被数字化,然后才能实现数字信号处理(DSP)的精选。
带微控制器 LED 演示阵列的 DAQFactory 数字输出练习
实验的
图 5-3 中描述的 DAQFactory SCADA 软件面板,经过一些修改,可以耦合到 Arduino 微控制器,以提供一个廉价的显示器。用图 5-4 的电路和清单 5-3 的 Arduino 代码可以实现 8 位、单字节、二进制、LED 照明的数值显示。
图 5-4
微控制器上 8 位字节 LED 照明逐位数字显示的连接
在图 5-4 中,标称 Arduino 数字引脚(ADPs 3–10)是连接到原型板的跳线,对于典型的 10 mm LED 具有 220ω限流电阻,以表示字节显示的各个位。
观察
代表 1 + 4 + 16 + 64 或 85 的 D0、D2、D4 和 D6 按钮的典型点击如图 5-5 所示。
图 5-5
用于微控制器 LED 照明逐位显示的 DAQFactory 8 位字节键盘
讨论
在数字可视化练习的实现中,微控制器用于点亮适当的二极管,只需要创建代码来通过串行端口激活正确的二极管。在 DAQFactory 侧,可以使用类似于清单 5-2 中的快速序列码,并针对每个单独的 D0–D7 按钮动作进行调整。该代码为数字求和程序(列表 5-1 )设置 DigOut_n 的加权包含标志变量,并发送所需的二极管编号,其数值等于二极管及其 CLR 在 Arduino 上连接的数字引脚编号。
图 5-3 和 5-5 的 DAQFactory 控制面板的不同之处在于,标有“清除字节”的额外按钮向微控制器发送数值“12”,微控制器进而触发代码将所有数字引脚返回到低电平状态,从而关闭 Arduino 上的所有二极管。该按钮激活 DAQFactory 快速序列,重置控制面板上的所有数字按钮,并将“12”传输到微控制器,如清单 5-5 中所述。
树莓派
在 Python 中,可以使用 RPi 的 GPIO 数组的前八个引脚来配置 8 位二进制显示表示。清单 5-4 提供了点亮 LED 二进制显示器的代码,并将点亮的 LED 转换成等效的十进制数值。图 5-6 展示了使用 tkinter 库创建图形用户界面的 Python 程序的输出。
图 5-6
程序输出和 8 位字节 LED 显示控制面板
图 5-6 显示点击评估 LED 按钮产生的输出,在 8 位二进制 LED 显示屏中,代表 1、4、16、64 和 128 的 LED 点亮。
在试验板上组装 8 位 LED 显示屏时,使用 330 或 470ω限流电阻来限制 RPi 电源的电流消耗,或者使用辅助电源并缓冲阵列输出。
通过使用“新文件”创建中运行菜单中的“运行模块 F5”选项来管理面板显示的控制。Python 程序在交互式解释器模式下打开,通过选择新文件选项,创建一个新文件,通过运行菜单中的“运行模块 F5”选项,可从该文件中定位、加载和运行 8 位 LED 显示程序。
图 5-6 的 GUI 将出现,当 RPi 启动时,GPIO 管脚上施加的杂散值所照亮的任何 led 将被清单 5-4 中的内部回路重置为 0。然后,GUI 按钮可用于点亮阵列中所需的位 led。单击“评估 led”按钮将切换到交互式 Python 显示模式,并打印“评估”和“十进制和=”以及由所选发光二进制位表示的数值总和的十进制值。
要重置程序,使用交互显示右上角的取消按钮(X ),并在弹出对话框中选择是/确定,返回程序代码列表,重新运行演示。
代码列表
//ClearByteDisplay
//Nov.14/09
//This sequence just re-zeros the 8 bit byte display
//
DigOut = 0
DigOut_1 = 0
DigOut_2 = 0
DigOut_3 = 0
DigOut_4 = 0
DigOut_5 = 0
DigOut_6 = 0
DigOut_7 = 0
Listing 5-5DAQFactory Regular Sequence to Clear Byte Display
# Event handlers join a widget to a type of event and a desired
# resulting action. Command is the method used to detect mouse
# "<Button-1>" events (clicks on the left mouse button) When a
# button is left clicked with the mouse, the self.buttonClick()
# method is invoked to initiate a LED illumination by setting
# the pin to high.
#
import tkinter # lower case t for current python installation
import RPi.GPIO as GPIO
from time import *
#
# the array of LEDs representing the 8 bit binary number must
# be cleared or re-set to low
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
for i in range(2, 18):
GPIO.setup(i, GPIO.OUT)
GPIO.output(i, GPIO.LOW)
#
# define the myWindow class in which to create the GUI window
class myWindow:
def __init__(self):
self.mw = tkinter.Tk()
self.mw.title("The Scientyst's Ayde")
self.mw.option_add("*font",("Arial", 15, "normal"))
self.mw.geometry("+250+200")
# GUI function title
self.lab_1 = tkinter.Label(self.mw, text = "Eight Bit Binary - Decimal Interconversions")
self.lab_1.pack() # place button widget/image mid window
#
# add eight buttons to the ui
self.btn_0 = tkinter.Button(self.mw, text = "DO", command = self.btn_0_OnClick)
self.btn_0.pack()
self.btn_1 = tkinter.Button(self.mw, text = "D1", command = self.btn_1_OnClick)
self.btn_1.pack()
self.btn_2 = tkinter.Button(self.mw, text = "D2", command = self.btn_2_OnClick)
self.btn_2.pack()
self.btn_3 = tkinter.Button(self.mw, text = "D3", command = self.btn_3_OnClick)
self.btn_3.pack()
self.btn_4 = tkinter.Button(self.mw, text = "D4", command = self.btn_4_OnClick)
self.btn_4.pack()
self.btn_5 = tkinter.Button(self.mw, text = "D5", command = self.btn_5_OnClick)
self.btn_5.pack()
self.btn_6 = tkinter.Button(self.mw, text = "D6", command = self.btn_6_OnClick)
self.btn_6.pack()
self.btn_7 = tkinter.Button(self.mw, text = "D7", command = self.btn_7_OnClick)
self.btn_7.pack()
# Create the evaluation button
self.btn_8 = tkinter.Button(self.mw, text = "Evaluate LEDs", command = self.btn_8_OnClick)
self.btn_8.pack()
#
self.mw.mainloop()
#
def btn_0_OnClick(self): # specify action desired on button click
GPIO.output(2, GPIO.HIGH)
#
def btn_1_OnClick(self):
GPIO.output(3, GPIO.HIGH)
#
def btn_2_OnClick(self):
GPIO.output(4, GPIO.HIGH)
#
def btn_3_OnClick(self):
GPIO.output(5, GPIO.HIGH)
#
def btn_4_OnClick(self):
GPIO.output(6, GPIO.HIGH)
#
def btn_5_OnClick(self):
GPIO.output(7, GPIO.HIGH)
#
def btn_6_OnClick(self):
GPIO.output(8, GPIO.HIGH)
#
def btn_7_OnClick(self):
GPIO.output(9, GPIO.HIGH)
#
def btn_8_OnClick(self):
print("Evaluating") # advise of action occurring
dcml_sum = 0 # define and initialize summing variable
if (GPIO.input(2)) == True: # test array bit status and add appropriate value to sum
dcml_sum = dcml_sum + 1
if (GPIO.input(3)) == True:
dcml_sum = dcml_sum + 2
if (GPIO.input(4)) == True:
dcml_sum = dcml_sum + 4
if (GPIO.input(5)) == True:
dcml_sum = dcml_sum + 8
if (GPIO.input(6)) == True:
dcml_sum = dcml_sum + 16
if (GPIO.input(7)) == True:
dcml_sum = dcml_sum + 32
if (GPIO.input(8)) == True:
dcml_sum = dcml_sum + 64
if (GPIO.input(9)) == True:
dcml_sum = dcml_sum + 128
#
print("Decimal sum = ", dcml_sum) # display result.
#
#
if __name__ == "__main__":
app = myWindow()
Listing 5-4Raspberry Pi Python Code for an 8-Bit Binary LED Display
// DAQFactory - Arduino LED Illuminated Digital Bits in Byte
// Register DAQFtry screen bttns D0 to D7 light LEDs in digital
// array. Total value of illuminated bits calculated and
// register cleared with buttons. DAQFtr uses scripting to
// evaluate digital bits and serial port transmissions to
// illuminate LEDs after selection by case statement.
//
// digital pins in use 3,4,5,6,7,8,9, and 10
int pv_one = 3;
int pv_two = 4;
int pv_four = 5;
int pv_eight = 6;
int pv_steen = 7;
int pv_threetwo = 8;
int pv_sixfour = 9;
int pv_onetwoeight = 10;
int diod_num;
String inString = "";
//
void setup() {
Serial.begin(9600);
//
}
//
void loop() {
while (Serial.available() > 0 ){ // read serial input
int inChar = Serial.read();
if(isDigit(inChar)){
// cnvrt incoming byte to char and add to string
inString += (char)inChar;
}
// if nuline convert accmlated to integer
if (inChar == '\n') {
diod_num = (inString.toInt());
Serial.println(diod_num);
inString = "";
}
}
switch(diod_num)
{
case 3:
pinMode(pv_one, OUTPUT); // units value 2 exp 0
digitalWrite(pv_one, HIGH);
Serial.println("Ones");
break;
//
case 4:
pinMode(pv_two, OUTPUT); // 2 exp 1 = 2
digitalWrite(pv_two, HIGH);
break;
//
case 5:
pinMode(pv_four, OUTPUT); // 2 exp 2 = 4
digitalWrite(pv_four, HIGH);
break;
//
case 6:
pinMode(pv_eight, OUTPUT); // 2 exp 3 = 8
digitalWrite(pv_eight, HIGH);
break;
//
case 7:
pinMode(pv_steen, OUTPUT); // 2 exp 4 = 16
digitalWrite(pv_steen, HIGH);
break;
//
case 8:
pinMode(pv_threetwo, OUTPUT); // 2 exp 5 = 32
digitalWrite(pv_threetwo, HIGH);
break;
//
case 9:
pinMode(pv_sixfour, OUTPUT); // 2 exp 6 = 64
digitalWrite(pv_sixfour, HIGH);
break;
//
case 10:
pinMode(pv_onetwoeight, OUTPUT); // 2 exp 7 = 128
digitalWrite(pv_onetwoeight, HIGH);
break;
//
case 12: // special case to clear array
pinMode(pv_one, OUTPUT);
digitalWrite(pv_one, LOW);
//
pinMode(pv_two, OUTPUT);
digitalWrite(pv_two, LOW);
//
pinMode(pv_four, OUTPUT);
digitalWrite(pv_four, LOW);
//
pinMode(pv_eight, OUTPUT);
digitalWrite(pv_eight, LOW);
//
pinMode(pv_steen, OUTPUT);
digitalWrite(pv_steen, LOW);
//
pinMode(pv_threetwo, OUTPUT);
digitalWrite(pv_threetwo, LOW);
//
pinMode(pv_sixfour, OUTPUT);
digitalWrite(pv_sixfour, LOW);
//
pinMode(pv_onetwoeight, OUTPUT);
digitalWrite(pv_onetwoeight, LOW);
break;
}
}
Listing 5-3Arduino Code for 8-Bit Binary Byte Display
// activation code for D0
global DigOut
device.ardyRb.Write('3' + Chr(10)) // light 1's digit
DigOut = 1 // add 1 to sum
Listing 5-2DAQFactory Quick Sequence Code for a Microcontroller LED Byte Display
DAQFactory Sequence Code for dcml_sum
// dcml_sum sums the binary values of the diodes illuminated
global dcml_sum = 0
//
// Examine each of the 8 bits represented by the buttons on the
// digital input panel
// D0 button action toggles the DigOut channel between value 0 or 1
if (DigOut == 1)
dcml_sum = dcml_sum + 1 // if the channel is active 20 = 1 is added to dcml_sum
endif
if (DigOut_1 == 1) // Activation of D1 toggles the channel between 0 and 1
dcml_sum = dcml_sum + 2 // if the channel is active 21 = 2 is added to dcml_sum
endif
if (DigOut_2 == 1) // Activation of D2 toggles the channel between 0 and 1
dcml_sum = dcml_sum + 4 // if the channel is active 22 = 4 is added to dcml_sum
endif
if (DigOut_3 == 1)
dcml_sum = dcml_sum + 8 // if the channel is active 23 = 8 is added to dcml_sum
endif
if (DigOut_4 == 1)
dcml_sum = dcml_sum + 16 // if the channel is active 24 = 16 is added to dcml_sum
endif
if (DigOut_5 == 1)
dcml_sum = dcml_sum + 32 // if the channel is active 25 = 32 is added to dcml_sum
endif
if (DigOut_6 == 1)
dcml_sum = dcml_sum + 64 // if the channel is active 26 = 64 is added to dcml_sum
endif
if (DigOut_7 == 1)
dcml_sum = dcml_sum + 128 // if the channel is active 27 = 128 is added to dcml_sum
endif
Listing 5-1DAQFactory Sequence Code to Sum Active Binary Digit Values
摘要
-
由比特和字节组成的数字数值的概念被直观地示出。
-
利用商业和低成本 SCADA 系统创建数字视觉演示。
-
在准备第六章讨论模拟和数字转换时,已经介绍了数字概念。
六、输入和输出的模拟或数字转换
在前一章中,开发了激活 8 位或二进制字节 LED 显示器的单个元件并查看由发光二极管表示的数字的十进制数字等价物的能力。DAQFactory 软件中的显示用 LabJack 硬件可视化。在本练习中,由屏幕按钮和数据输入编辑框激活的一系列 DAQFactory 序列显示输入编辑框的十进制值的二进制等效值。
在本练习中,将一个小于 255 的十进制值输入到主控制屏幕上的编辑框中,该编辑框位于一组组件面板中,这些组件控制转换和显示选项,如各个按钮上的标签所示。如图 6-2 所示,面板包含上下调节二进制值的功能和 LED 显示清除功能。实际的数值转换是通过调用基于标准数值分析的转换算法的脚本序列来完成的。顺序在章节代码清单的清单 6-1 中。
在本练习和之前的练习中,数值已经在由 2 n 和 10 n 组成的两个不同基数编号系统之间进行了转换。编号系统有不同的基础,并产生不同的数字序列来表示手头相同数量的单位或项目。改变所需数字的类型和数量是数学中的一个练习,它总是产生整数,这些数字代表两个基数中任何一个的整数单位的相同数量。
模拟和数字电信号值之间的转换通常不会产生完全相同的结果。理论上,当模拟信号从一个值变化到另一个值时,它平滑而连续地变化。模拟值的电子、数字表示必须将模拟信号的变化范围分成有限数量的间隔,该间隔等于数字显示器中可用的二进制基本位数。8 位二进制数字显示器可以表示 0 到 255 之间的十进制数。如果我们希望用以前开发的 8 位数字光显示器来表示从 0 到 5 伏变化的电信号,那么可用的 256 个二进制数字中的每一个必须表示 5.0/256 = 0.0195 V 或大约 19.5 mV。
如果二进制阵列中的 LED 数量从八个增加到十个,那么 10 位 LED 显示器可以表示 2 个 10 个或 1024 个十进制数值。10 位阵列能够将 5.0 V 信号分为 5.0/1024 = 0.004882812 伏或约 4.88 mV。
类似地,将阵列扩展至 12 位可以表示约 1.22mV/位的 5.0 V 信号。
可用位数通常称为转换器或转换分辨率。
需要在模拟和数字格式之间转换的实验必须仔细设计和组装,以补偿这些不精确的转换所引入的误差。
ADI 公司提供涵盖模数转换发展史的优秀参考书籍的硬拷贝版本或可下载的 pdf 章节系列(Walt Kester, Analog-Digital Conversion ,ADI 公司,2004 年,ISBN 0-916550-27-3)。ADI 公司提供两个出色的教程 MT-015 和 MT-016,它们解释了数模转换(DAC)的基础知识和更高级的主题,并附有大量可供进一步研究的参考资料。(参见“讨论”)
在数字表示中,最右边的位称为最低有效位(LSB),而最左边的位是最高有效位(MSB)。(基数的指数幂随着左边数字位置的增加而增加。)
许多模数转换器(ADC)集成电路器件可用于电子信号的转换。目前使用的转换机制有多种,如电平转换器或 flash 转换器、逐次逼近型寄存器、σ-δ型转换器,以及电子文献中详细讨论其优势和局限性的其它工艺。 1 (另见前面提到的 ADI 公司。)根据图 6-1 中的电路 A–D,用于降低电信号电压电平的电阻分压器电路也可用于将电压电平分成 n 个部分。从 A 到 D 越来越复杂的电路也被称为开尔文分压器,可以追溯到 19 世纪中期。
图 6-1
ADC 和 DAC 电阻网络
在图 6-1 的电路 A 中,如果电阻的值相等,分压电路将输入电压减半,如 Vout= Vin*(R/R+R)。
在图 6-1 的电路 B 中,输出电压为输入的三分之一,为 Vout= Vin*(R/R+R+R)。电路 C 和 D 中所示的四个和五个等值电阻分别将分压降低到输入电压的四分之一和五分之一。
一般来说,可以看出,输入电压和地之间的 n 个串联电阻将提供一系列连接点。输入电压为 n 时,每个电阻的压降是输入电压的 1/n。一串串联电阻和降压结是“flash”型模数转换器(集成电路)背后的机电基础。
从本质上讲,将数字信号转换成模拟信号是 ADC 过程的逆过程。与能够将输入划分为 256 个离散电压电平的二进制 8 位 ADC 一样,二进制 8 位数模转换(DAC)的反向过程也提供 2 8 或 256 个离散输出电压电平。DAC 不产生真正的模拟信号,而是产生模拟波形的阶跃电压近似值。
一种称为 R-2R“梯形”网络的高效转换架构已经开发出来,它仅使用同名的 R 和 2R 两个电阻值。从图 6-1 中的电路 E 可以看出,位 0 相对于地处于最低电压,而位 7 处于最高电压。随着电阻堆栈中位置的增加,位值的重要性或“权重”从最低到最高增加。
模拟和数字格式之间的信号转换可以用相对便宜的 IC 器件实现,如单通道、8 位 ADC0804 或 8 通道、10 位 MCP3008。ADC0804 可以与 5v 电池组一起使用,直接驱动 8 个 LED 显示屏,并提供简单、廉价的 ADC 演示。(一个 ADC0804 芯片成本 6 加元。)
除了 8 位 LED 阵列之外,使用 LabJack HMI 系列器件和 DAQFactory 软件监控模拟信号的便利性也通过 U12 系列中 10 位模数转换器的+/-10 伏输入限制和 U3 器件中 12 位转换器的 3.6 伏输入电压限制得到了证明。(参见第章 1 ,图 1-1 ,第 1 项。大多数表面贴装技术(SMT)集成电路在 3.5 伏下工作。)
使用一个简单的电阻分压器将实验范围缩小到转换器可接受的范围,就可以实现比 ADC 制造商指定的电压范围更大的电压范围测量。(见图 6-1 。)然后,研究者可以选择使用输入到第二章图 2-6 和图 6-10 和 6-11 中的变量值配置窗口的表达式框中的数学公式中的校正系数,来显示实验电压的当前值。如图 6-11 和 6-12 所示,如果研究人员需要,可以将第二个变量值显示添加到 GUI 中,用于监控施加到 ADC 输入端的实验电压和降低的电压。
数模转换
正如“练习路线图”和之前所指出的,数模转换(DAC)不会也不能再现真正的模拟电子信号。所产生的“模拟”信号在 DAC 上被分解成固定数量的离散数字值,并且从离散数量的数字值产生的模拟信号是阶梯波形,其形状类似于原始平滑且连续的模拟信号。
将数字信号转换为模拟输出有两种基本方法。一种方法使用二进制加权电阻值,其中各个数字位信号施加于电阻,电阻比与二进制幂级数成比例。构建这种类型的 DAC 经常需要非标准电阻值。DAC 的另一种方法更容易实现,如图 6-1 的电路 E 所示。R-2R 梯形电阻产生一个输出信号,该信号由梯形电阻体内的数字输入之和构成。第二种方法有几个优点,因为只需要两个电阻值。梯形电路可扩展至所需的位数,输出阻抗始终恒定,等于梯形电路中使用的较小电阻值。
实验:LabJack-DAQFactory 十进制到二进制的转换
五金器具
在前一章的练习中组装和测试的按钮控制、缓冲、8 位字节 led 显示器将作为转换后的十进制值的单个位显示或输出寄存器。
软件
-
A panel grouping consisting of the components depicted is assembled on the main screen.
图 6-2
十进制到二进制数转换器的 DAQFactory 面板
面板由两个文本组件配置而成,显示在面板的顶部两行。编辑框组件接收要处理的数值,并将该值输入所需的变量。第四行按钮组件激活转换序列,底部一行按钮执行标题上显示的操作。
-
必需的脚本
图 6-2 的分组“十进制到二进制转换器”面板需要四个脚本来激活转换器位显示,将二进制显示增加一,将数值减少一,转换输入的十进制数,然后显示二进制值,并清除显示。
本章末尾的清单 6-1 至 6-4 中提供了各个序列代码。
模数转换
为了演示利用 DAQFactory 软件和 LabJack HMI 实现模数转换的简易性,配置了如图 6-3 所示的 ADC 面板。该面板由三部分组成,一个作为标题的文本条目,一个显示通道[0]或感兴趣通道的当前值的变量值读数,以及灰色面板背景。
图 6-3
ADC 面板
模数转换电路如图 6-4 所示,其中电位计的两端连接在+5 V 和 U12 的接地端子之间,游标连接到 AI 0,模拟输入零点,螺丝端子。
图 6-4
可调模拟信号源
如前所述,通过使用基于电阻的分压器来降低施加于转换器的信号强度,可以使用 ADC 来跟踪超出 ADC 电子设备安全工作限值的电压波动。降低的电压 V out 可以通过以下分压器等式计算:
Vout= Vin*(R1/(R1+R2))
其中 R 1 和 R 2 是连接在 V in 和地之间的一对串联电阻的电阻值,V out 是在 R 1 与 R 2 的连接点和地之间观察到的电压。(参见图 6-1 中的电路 A)。)
实现 ADC LED 阵列照明二进制显示器的成本较低但更复杂,可以用德州仪器(ti)成熟的 ADC0804 芯片组装。(一份 57 页的 pdf 文档可从 www.ti.com/lit/ds/symlink/adc0804-n.pdf
获得)。)
pdf 数据表注明了以下内容:
- 无需接口逻辑,可作为完整的独立器件工作,访问时间为 135 ns,差分电压输入,MOS 和 TTL 电压电平兼容,能够使用 2.5 基准电压源、片内时钟、0–5v 输入范围和 5v 电源,无需调零,标准 20 引脚 DIP 封装,转换时间为 100 us
图 6-5
8 位 LED ADC 显示器
图 6-5 描述了作者使用德州仪器 ADC0804 8 位逐次逼近型模数转换器开发 ADC 硬件演示所用的电路。为了在原型板上实现,作者使用 3 mm 红色 led 和 1kω限流电阻作为显示器,使用 4 节 AA 电池镍金属氢化物电池组作为电源,并连接到 5kω电位计的游标引线,该电位计连接在标称 5v 电源和地之间,类似于图 6-4 中所示的电路。当电位计上的轴旋转时,游标电压提供变化的模拟电压信号,用于转换成数字格式来驱动二进制 led。
图 6-5 中描述的数字地全部连接到公共连接,然后接地到电源的负端。作者在原型板上实现的电路中包含一个开/关开关,但它并不是启动电路动作所必需的,因为当系统通电时,转换会立即开始。要转换的电压施加于 IC 的引脚 6。
观察
DAQ factory–lab jack HMI 模数基数转换
在编辑框中输入一个十进制值并点击输入脚本按钮,对应于输入的十进制数字值的二极管将会亮起。输入数值 25 时,应点亮 2 个 0 (1)、2 个 3 (8,即 2 × 2 × 2)和 2 个 4 (16,即 2 × 2 × 2 × 2)二极管的单元,以显示 25 的二进制等效值(即 1 + 8 + 16)。
单击增加或减少按钮应该将显示的二进制值增加或减少 1,而清除按钮应该清除显示。
模数电子信号转换
如果没有机械限制或不连续性,在 10kω电位计两端连接一个标称 5 V 信号,理论上应能产生一个平滑变化的模拟游标电压,范围为 0 至 5V。
安装在 U12 中的 10 位 ADC 理论上能够将 5 V 模拟信号分成 1024 个 0.0048828 伏特或 4.883 mV 的单元。变量值显示器被配置为读取如图 6-4 所示接线的 10kω电位计的游标上的电压。当旋转电位计轴时,当变量值显示设置为显示 AO 0、模拟输出零、通道上的数据至三位小数时,DAQFactory GUI 屏幕上显示 0.010 V 至 4.219 V 的值。下电压值显示从 0.005 波动到 0.020,上电压值显示从 4.209 波动到 4.365。
ADC0804: 8 位二进制 LED 显示器
图 6-6 是图 6-5 中电路的电池供电工作示例照片,装配在试验板上,用于直观演示 ADC。仔细检查视野右上角的二极管,会发现 4 位、8 位和 64 位指示灯亮起,指示总值为 76。电压源是视野底部中央可见的 5kω电位计的游标引线。
当图 6-5 的电路通电时,代表数字化游标电压电位的二进制等值的单个 led 立即亮起。将电位计的轴从一个极限位置旋转到另一个极限位置,显示二极管照明序列,其中二进制数从 0 增加到 255 或相反。通过缓慢旋转轴,可以在二进制显示中跟踪单个计数。如前所述,充满电后,电池组产生标称 5 V 电压,施加于电位计两端。
图 6-6
8 位 LED ADC 显示器照片
施加的电压和二进制显示之间的相关性是通过测量然后比较施加到输入引脚#6 的实际电压和由 8 位显示阵列的发光二极管显示的二进制值来建立的。
检查图 6-7 的列表数据和 ADC0804 数据手册发现,施加的电压与产生的数字值之间并不存在 1:1 的对应关系,因为施加的电压为 5.25 时产生的输出仅为 253。利用基准电压输入引脚(引脚#9)可以校正 ADC 差异,如 IC 数据手册所述。(参见“讨论”)
图 6-7
ADC0804 线性度
讨论
ADI 公司出版物模数转换第一章的图 1.1 有助于理解 ADC 和 DAC 的概念。模拟。com/media/en/training-研讨会/设计-手册/数据转换-手册/第一章。pdf 。该图的标题为“18 世纪早期二进制加权水计量系统”,包含十九世纪在土耳其伊斯坦布尔实施的水计量系统的一系列侧视图和俯视图。这些图表记录了 DAC 在重力驱动、水力配水和“加权平均”计量系统方面的实施情况。
模拟电子信号值和数字数值表示之间的相互转换很少是完全等价的。ADC0804 等传统 ADC 使用 8 条信号线的并行输出,每条信号线代表 2 的幂。并行输出有助于组装图 6-6 的电池供电的可视 LED 显示器。然而,LabJack 器件中使用的新技术不使用 ADC 的“并行”输出配置,而是依靠更简单的方式来实现串行数据输出。串行数据输出协议的速度足以监控多种类型的传感器,但高速仪器通常需要使用并行转换器来跟上数据生成速率。
应用分压器时必须小心,因为所选电阻值的比值必须将信号电压降至所需水平,但单个电阻值应尽可能低,以允许足够的电流通过,从而驱动“下游”器件或 ADC。
ADC 产生的数字由并行信号输出组成,以驱动“下游”器件的逻辑,如微处理器、七段 LED 数字显示器,或者在本初级练习中的 8 位二进制 LED 显示器,但其能够安全提供的电流输出通常有限。在一些高电流需求显示器中,可能需要缓冲 ADC 输出。
在使用 DAQFactory 软件的 LabJack HMI 和 ADC0804 8 位 LED 显示器的装配或显示器与 RPi 的接口之间,实现 ADC 和 DAC 演示所需的时间和精力有很大的不同。
ADC0804 是一款单通道器件,需要一个调整至 0–5v 范围的可变电压源输入。IC 有一个参考电压引脚#9,可用于调整 255 位电平的步长。默认设置为每数字增量 19.5 mV,因此整个 5 V 输入范围将产生 0–255 二进制数字输出。(有关使用步长调整的更多信息,请参见德州仪器 ADC0804 数据手册。)需要一个时钟来运行转换逻辑,为简单起见,使用内部时钟需要一个串联电阻和电容(RC)网络。所需的 RC 组合连接在时钟输入和时钟 R 引脚(引脚#4 和#19)之间。RC 时间常数最终决定 IC 对引脚#6 上的电压进行采样的频率,以便在输出引脚上产生转换结果。
除了时钟和输入布线之外,ADC0804 演示练习还需要一个 5 V 电源和八个 LEDS 及其限流电阻。
正如在前面的练习中所提到的,在系统组装时,研究人员应该尽可能地测试每个组件。每个 LED 和限流电阻都可以通过在将连接到 ADC0804 的电阻端施加 5 V 电压来测试。
在创建按钮代码的过程中,演示了使用以前编写的脚本作为函数的能力。如前一练习所述,较大数值的数值转换需要增加适当数量的数字输出线路、通道和二极管,并修改脚本代码。
一个 8 位字节提供 1/28或 1/256 的分辨率。图 6-2 中的十进制到二进制转换器面板包含表示一位数分辨率的增加和减少按钮。如果实验设置可能产生 0 至 10 伏的变化 DC 信号,则 8 位转换能够解析出 10/256 = 0.0390625 或 39.1 mV。在 LED 显示库中增加两个以上的位和相应的软件变化将允许 10 V 信号范围扩展到 1024 个二进制数字,提供 1024 或毫伏灵敏度或分辨率中的一部分的近似划分。将数字能力提高到 12 位将提供 4096 分之一或 0.0244% (244 ppm,百万分之一)的灵敏度或分辨率。
GUI 数字显示除了其当前值之外,不传达关于被监控电压值的任何附加信息。除非数字值有明显的稳定增加或减少的趋势,否则无法从监控变量值的数字显示中获得额外的信息。然而,在生产过程或实验测量被监控并且过程变量或测量结果的恒定性是 SCADA 系统的主要目标的许多情况下,诸如定时记录的不同形式的数据呈现可能具有更大的价值。
利用微控制器进行模数转换
如前五个练习中所述,更紧凑、更便宜的 SMT 设备能够在 DAQFactory SCADA 软件和实验传感器或过程管理硬件之间实现接口,并以微控制器的形式出现。(参见第九章。)
Arduino 微控制器是易于获得、易于使用的紧凑型设备,内置 10 位逐次逼近型 SMT 模数转换器,具有 6 个输入通道。ADC 芯片能够在其 16 MHz 时钟的 25 个周期内将输入电压转换为数字。每次转换 400 微秒)。
Arduino 之类的微控制器是一种串行设备,为了在 DAQFactory 控制屏幕和微控制器 LED 阵列之间来回传递信息,控制屏幕和控制器都必须像前面的练习一样读取和写入串行端口。
串行通信基于 1 和 0 的 ASCII 位模式,允许传输数字和字母控制字符,如换行和回车。虽然 ADC 硬件可以产生一位数或多位数的整数位计数,但串行端口的每一端都必须有一定数量的字符识别和解释逻辑软件,以便创建一个正常工作的通信系统。
实验的
通过 DAQFactory 控制面板和 Arduino 控制的二进制阵列之间的串行连接实现十进制到二进制显示可以从图 6-8 中描述的 DAQFactory 面板的创建开始。
图 6-8
用于串行控制 Arduino 二进制 LED 显示器的 DAQFactory 控制面板
图 6-8 中使用了一个文本组件、一个编辑框和四个按钮。
图 6-9 显示了编辑框配置窗口,该窗口为创建框标题和选择各种选项和动作提供了空间。
图 6-9
编辑框配置窗口
本章末尾的清单 6-5 到 6-9 中提供了实现串行面板连接到微控制器的支持 DAQFactory 脚本和 Arduino 草图代码。
为了将微控制器板上的 ADC 用作串行连接的传感器读数设备,可以如图 6-10 所示配置一对在 DAQFactory 屏幕上分组为一个面板的变量值显示组件。使用 SCADA 软件的通道功能,可以同时显示 ADC 的整数计数和与计数值相对应的计算电压值。(这里只粗略介绍了在两个系统之间提供数据流所需的更复杂的设置。更多细节在第十一章中提供。)
图 6-10
整数 ADC 计数和计算的 ADC 电压的可变值元件显示
如图 6-10 所示,DAQFactory 页面上有两个变量值显示。
图 6-11 和 6-12 显示了显示原始计数的整数 ADC 计数显示和使用表达式计算 ADC 即时电压值的 ADC 电压值显示的配置窗口。
图 6-12
计算出的 ADC 电压的变量值配置页面
图 6-11
串行传输的整数 ADC 计数的变量值配置页面
图 6-13 记录了建立串行微控制器 SCADA 软件通信所遵循的表格、窗口和条目的顺序。
图 6-13
一种实现 DAQFactory 串行通信的配置序列
图 6-13 显示了从微控制器 ADC 读取串行“数据流”到 DAQFactory 显示组件所需的主要程序动作的标题概要。(详见第十一章。)第 1 项标记了正在使用的 DAQFactory 程序的页面列表,在该页面上,图 6-10 的面板由所需部件组装而成。第 2 项表示通道列表,其中 ArduinoStream 通道由作者创建,用于接收从微控制器输出到串行端口的 ADC 数据(另见图 2-3 )。第 3 项和第 4 项是串行端口配置窗口和串行设备命名和配置窗口,将在第十一章中详细介绍(参见图 11-5 、 11-6 和 11-7 )。
要安装和使用清单 6-10 中的代码,点击快速➤设备配置并选择适当的设备,在作者的演示案例中是 Comm4。在“以太网/串行设备”窗口中,找到所需的设备并选中旁边的框(图中的 Comm4 ),然后单击“协议配置”按钮,调出“I/O 类型和功能”窗口。选择“接收时”功能,然后将清单 6-10 的代码复制并粘贴到空白处。(参见 11 章节中的图 11-7 。)
为了给本练习演示提供可变信号模拟,可以将电位计的两端连接在+5 V 电源和微控制器地之间,以及连接到 Arduino ADC A0 输入的游标引线之间。(参见图 6-4 的类似电路图,用于 LabJack HMI。)
根据从多个经过测试且功能正常的组件构建复杂系统的理念,我们可以从加载并启动清单 6-11 中的微控制器草图代码开始。草图运行后,可以从工具菜单打开 Arduino 串行监视器,在串行监视器视野的左侧应该可以看到 ADC 计数流。确认数据流生成后,串行监视器关闭,微控制器 IDE 最小化。
包含变量值面板的 DAQFactory 程序启动,如果所有配置正确,图 6-10 的屏幕组件应能响应系统噪音和电位计刮水器控制轴的任何重新定位。
观察
启动用于 ADC 读取和串行打印到所用端口的微控制器草图后,应该可以在微控制器 ide 的串行监视器窗口的视野左侧看到一串数值,其值介于 0 和 1024 之间。
微控制器在后台运行时,微控制器原型板上的电位计轴旋转一整圈,电压的整数显示从 0 到 1024 和 0 到 5.000 伏,如图 6-11 和 6-12 的配置窗口中的设置所定义。
讨论
无响应显示器的诊断
如果变量值面板没有响应输入数据流,展开通道表并确认数据被通道捕获,如图 6-14 所示。
图 6-14
一个活动信道时间戳数据列表
如果通道没有接收到 ADC 数据,则可以访问 DAQFactory 程序的串行端口监视器,以确认数据正在到达显示程序的串行端口。串行端口监视器通过快速➤设备配置菜单和设备选择列表面板进入以太网/串行设备窗口,如图 6-13 的第 4 项所示。点击配置按钮正下方的监视器按钮将调出 DAQFactory 串行监视器,如图 6-15 所示。
图 6-15
DAQFactory 串行监视器显示通过正在使用的端口的数据
如果数据正在到达 DAQFactory 串行端口,但没有被传输到正确的通道,可以通过确保在协议列表中选择了正确的协议,并且正确的解析代码(列表 6-10 )已经输入到协议配置窗口的“接收时”I/O 类型和功能条目中,来检查端口串行协议。 2
系统开发和编程
虽然建立 SCADA-实验串行连接的微控制器方法比使用市场上可买到的 DAQ 系统要便宜得多,但是所需的实验开发时间和精力是相当大的,并且该系统缺乏商业产品中的鲁棒性。
利用 Raspberry Pi 实现输入和输出的模拟和数字转换
由于之前提到的可用功率限制,可以使用 RPi 及其通用输入/输出引脚阵列创建电子数字编号的 LED 可视化。可以使用 Raspberry Pi Foundation 提供的两个 Python 库演示二进制和十进制之间的数字转换以及 ADC。RPi。GPIO 库允许对 40 针阵列的低级访问,而 gpiozero 库代码提供对许多硬件设备的访问。每个库的文档都可以从 RPi Foundation 网站上获得,两个库版本之间的差异将在接下来的几个练习中进一步阐述。
二进制-十进制转换
作为对前面练习中介绍的 RPi 编程和硬件使用的补充,在本练习的最初部分,我们组装了一个 12 位十进制到二进制转换 LED 可视显示器。转换器的代码在本章末尾的清单 6-12 中,转换的输出如图 6-16 所示。
图 6-16
十进制值的 RPi 12 位二进制显示
12 个 led 的银行组装和测试,以提供几个 Python 和 ADC 程序的视觉输出。十进制到二进制数字转换器、10 位 ADC 和 12 位 ADC 可以共用同一硬件,以图形方式直观显示这些类似程序的各种输出。
带树莓 Pi 的 ADC
如前所述,RPI 需要外部器件来对模拟信号进行数字化处理,RPi 基金会选择 MCP3008 和 MCP3201 ICs 作为 10 位和 12 位数字转换的合适器件。IC 通过串行外设接口(SPI)串行协议与 RPi 通信。ADC 数据以连续的位序列输出到 RPi,RPi 接收并解释 10 位转换值。MCP3008 输出可以格式化为 0 至 1.0 的浮点归一化值,与采样电压和施加于基准引脚的电压之差成比例,也可以格式化为 1 至 1024 的整数值。当 ADC 芯片以 RPi 的 3.3 电源为基准时,归一化输出必须乘以施加的标称电压,或者为了精确起见,乘以 VOM 测量的基准电压,以获得实际采样电压。MCP3008 的整数输出到采样电压值的转换包括将输出值除以 1024 并乘以参考值电压。
表示模拟转换的浮点归一化值不容易用于照亮 10 位二进制 LED 可视显示器。数字整数输出更容易与二进制 LED 显示器接口。
实验的
为了将清单 6-12 和 6-13 的代码用于十进制到 10 或 12 位二进制 LED 可视显示器和本练习中列出的 MCP 芯片,RPi 必须配置为使用串行外设接口(SPI)协议,如图 6-21 和 6-22 所示。
来自偏置在 RPi GPIO 阵列的 3.3 V 和地之间的 10kω电位计的游标的连续可变电压用于产生 MCP3008 ADC 集成电路的测试电压。RPi 从 IC 读取串行输出,解释流数据,并产生调整后的 10 位整数输出,随后用于激活 10 单元 led 显示器。
图 6-17 是用于实现显示的电路的半示意图。游标电压施加于 IC,IC 将信号从模拟格式转换为数字格式,并将数据以串行外设接口(SPI)形式输出至 RPi GPIO 引脚。RPi 接收流数据,解释转换后的数据,并解析整数输出,以驱动转换后的游标电压信号的适当二极管表示。
图 6-17
RPi-MCP3008 电路,用于电位计游标电压的 10 位二进制 LED 显示
图 6-17 的左边是控制和接收 IC 数据的 Raspberry Pi GPIO 引脚连接,右边是 RPi GPIO 二极管阵列连接。
图 6-18 显示了在模拟实验设置期间 RPi 的屏幕输出,其中诊断打印语句已被插入到代码中,以验证系统的运行。电位计游标已经旋转,以产生尽可能接近序列 123 的数字输出。
图 6-18
ADC 期间的 RPi 屏幕输出
观察
图 6-18 显示了 Python 代码的连续输出,该代码解析数字化、转换后的游标电压值,以驱动输出值的 10 位 LED 二进制表示的各个元素。对输出和正在处理的实际代码进行更仔细的检查将会确认,只有当余数变量“rem”具有正值或高值时,程序才会打印诊断输出。将电位计轴从一个极端旋转到另一个极端会使显示从 1 或 0 变化到 1023。从图 6-18 的数据变化中可以看出,系统在雨刷输出值中包含了一定量的噪声。
图 6-19 描述了 RPi GPIO 阵列与原型试验板上的 12 位 LED 显示器的接口。发光二极管对应于二进制位模式 2 + 4 + 8 + 16 + 32 或十进制 62。
图 6-19
十进制值 62 的 12 位二进制 LED 显示
讨论
图 6-19 描绘了一组 12 个 3mm led,可用于 10 位或 12 位转换演示。MCP3201 是一款 12 位转换 IC,可以设置为二进制可视化显示器。小型 3 mm 二极管和 1kω限流电阻用于最大限度地降低视觉显示器中有大量 led 的计算机的电流消耗。
RPi 电路、编程和布线的数字和照片反映了使用非常便宜的系统所需的复杂性。从图 6-19 的照片中可以看出,调查人员需要注意 RPi GPIO 线的跳线连接以及控制 MCP3008 或 MCP3201 转换功能所需的跳线连接。激活引脚阵列输出的 10 或 12 位二进制 LED 显示所需的 RPi GPIO 线连接可以在装配期间用清单 6-14 进行测试。当每个引脚名称出现在交互式屏幕上时,连接到标称引脚的相应 LED 应点亮 3 秒钟。图 6-20 描绘了引脚和 LED 测试的测试程序输出显示。
图 6-20
LED 阵列测试输出
列表 6-15 可用于将 GPIO 阵列电压值重置为零。
前面 ADC0804 所展示的并行 ADC 集成电路在一定程度上已被众多串行通信协议所取代。使用两根或少量导线进行长距离串行通信,比使用 8、10、12 或更多并行导线来传输高频数字数据位要实用得多。防止“串扰”的屏蔽、物理尺寸和费用只是在密集的平行线路上进行高速数据传输时会遇到的一些问题。
model 3 Raspberry Pi 可以配置为使用多种串行通信系统中的一种。图 6-21 和 6-22 显示进入首选项➤配置窗口,该窗口允许执行所需的协议。
图 6-22
接口选择窗口
图 6-21
RPi 首选项选择菜单
选择串行外设接口(SPI)协议允许 RPi 与以连续高低位脉冲流“流出”数据的器件通信。ADC 就是这样一种数据流设备。SPI 协议基于主从概念,三个或四个电气连接在主机和一个或多个从机之间形成一条电子总线。时钟同步数据传输。SPI 配置中的四条线路是主机输出从机输入(MOSI)、主机输入从机输出(MISO)、时钟线(SCLK)和芯片从机选择(CSS)。
如果有许多从机,SPI 可能会变得难以实现,第二种流行的协议是集成电路间(I 2 C 或 I2C)协议。I2C 是双线式实现,固定速度较慢,使用可寻址位置,比 SPI 功耗更高,噪声更低。I2C 是唯一确认数据传输的协议。
在图 6-22 中,有第三个通信接口,称为串行接口,实现通用异步接收和传输(UART)协议。异步通信只在两个设备之间进行,不需要外部时钟,但在两端使用一致同意的数据传输和接收速率。双线总线的每一端都有一个 IC,用于在并行和串行数据流之间进行转换。在 UART 传输中,定义的格式用开始和停止标记指定数据的开始和结束。串行协议广泛用于微处理器通信。
有关这三个协议的更多细节,请参见发明者实用电子学。??
代码列表
/*
AnalogReadSerial
Reads an analog input on pin A0, prints the result to the serial monitor.
Attach the center pin of a potentiometer to pin A0, and the outside pins to +5V and ground.
*/
// the setup routine runs once when you press reset:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
}
// the loop routine runs over and over again forever:
void loop() {
// read the input on analog pin 0:
int sensorValue = analogRead(A0);
// print out the value you read:
Serial.println(sensorValue);
delay(50); // delay in between reads for stability
}
Listing 6-11Arduino Sketch Code to Read A0 ADC Channel and Write Data to Serial Port on 50 ms Intervals
if (strIn == Chr(13))
private string datain = ReadUntil(13)
Channel.AddValue(strDevice, 0, "Input", 0, StrToDouble(DataIn))
Endif
Listing 6-10DAQFactory “On Receive” Code for the ArduinoStream Channel
// DAQFactory - Arduino LED Illuminated Digital Bits in Byte
// Register DAQFtry screen bttns D0 to D7 light LEDs in digtal
// array. Total value of illuminated bits calculated and
// register cleared with buttons. DAQFtr uses scripting to
// evaluate digital bits and serial port transmisons to
// illuminate LEDs after selectn by case statement.
//
// digital pins in use 3,4,5,6,7,8,9, and 10
int pv_one = 3;
int pv_two = 4;
int pv_four = 5;
int pv_eight = 6;
int pv_steen = 7;
int pv_threetwo = 8;
int pv_sixfour = 9;
int pv_onetwoeight = 10;
int diod_num;
String inString = "";
//
void setup() {
Serial.begin(9600);
//
}
//
void loop() {
while (Serial.available() > 0 ){ // read serial input
int inChar = Serial.read();
if (inChar == '3') {
pinMode(pv_one, OUTPUT); // units value 2 exp 0
digitalWrite(pv_one, HIGH);
}
if (inChar == '4') {
pinMode(pv_two, OUTPUT); // 2 exp 1 = 2
digitalWrite(pv_two, HIGH);
}
if (inChar == '5') {
pinMode(pv_four, OUTPUT); // 2 exp 2 = 4
digitalWrite(pv_four, HIGH);
}
if (inChar == '6') {
pinMode(pv_eight, OUTPUT); // 2 exp 3 = 8
digitalWrite(pv_eight, HIGH);
}
if (inChar == '7') {
pinMode(pv_steen, OUTPUT); // 2 exp 4 = 16
digitalWrite(pv_steen, HIGH);
}
if (inChar == '8') {
pinMode(pv_threetwo, OUTPUT); // 2 exp 5 = 32
digitalWrite(pv_threetwo, HIGH);
}
if (inChar == '9') {
pinMode(pv_sixfour, OUTPUT); // 2 exp 6 = 64
digitalWrite(pv_sixfour, HIGH);
}
if (inChar == 'a') {
pinMode(pv_onetwoeight, OUTPUT); // 2 exp 7 = 128
digitalWrite(pv_onetwoeight, HIGH);
}
if (inChar == 'z') {
// special case to clear array
pinMode(pv_one, OUTPUT);
digitalWrite(pv_one, LOW);
//
pinMode(pv_two, OUTPUT);
digitalWrite(pv_two, LOW);
//
pinMode(pv_four, OUTPUT);
digitalWrite(pv_four, LOW);
//
pinMode(pv_eight, OUTPUT);
digitalWrite(pv_eight, LOW);
//
pinMode(pv_steen, OUTPUT);
digitalWrite(pv_steen, LOW);
//
pinMode(pv_threetwo, OUTPUT);
digitalWrite(pv_threetwo, LOW);
//
pinMode(pv_sixfour, OUTPUT);
digitalWrite(pv_sixfour, LOW);
//
pinMode(pv_onetwoeight, OUTPUT);
digitalWrite(pv_onetwoeight, LOW);
}
}
}
Listing 6-9Arduino Sketch Code for Diode Array Illumination
/ClearByteDisplay
//Nov.14/09
//This sequence just re-zeros the 8 bit byte display
device.ardyRb.Write('z' + Chr(10))
Listing 6-8DAQFactory Sequence Code for Clearing the Display
//DecBinDisplay
//Nov.14/09
//This sequence decreases the screen entered global variable
//Number_To_Convert that was converted and displayed in
//sequence ConvertDecToBinary and runs the decreased value back
//through the original sequence.
//In the original conversion sequence the Edit Box value,
//variable, Number_To_Convert is iteratively divided by two till
//it vanishes so the entered number is saved in Orgnl_N_To_Cnvrt
//
global Orgnl_N_To_Cnvrt
//clear the register of any residual data
ClearByteDisplay()
//re-initialize the working variable to the desired value to be converted
Number_to_Convert = Orgnl_N_To_Cnvrt - 1
//convert and display the bit pattern
DecimalToBinaryCnvrsnRvn1()
Listing 6-7DAQFactory Sequence Code for Decreasing the Converted Value
//IncBinDisplay
//Nov.14/09
//This sequence increases the screen entered global variable
//Number_To_Convert that was converted and displayed in sequence
//ConvertDecToBinary. The value of the original variable was
//iteratively reduced to zero by the conversion code but was
//preserved in the global variable Orgnl_N_To_Cnvrt.
//The preserved number is augmented in value and passed back
//through the original sequence.
//
global Orgnl_N_To_Cnvrt
//
//any residual values on the byte register are cleared
ClearByteDisplay()
//
// the original value is augmented
Number_to_Convert = Orgnl_N_To_Cnvrt + 1
//
//the augmented value is converted and displayed
DecimalToBinaryCnvrsnRvn1()
Listing 6-6DAQFactory Sequence Code for Increasing the Converted Value
//Decimal to Binary Conversion
//Oct 14-16, Nov 14/2009, serial port display Jun7/19
//Program Algorithm
//8 LEDs are connected to the digital output channels D0 (#4)
//to D7 (#11) on the DB25 output of the LabJack. Each line is
//buffered/driven with a 4050 buffer.
//An EDIT box accepts the Number_To_Convert and the modulo of
// the value with respect to base 2 is determined for each bit
// of a byte. The bit values are then displayed on the LEDs. In
// this version a quick sequence writes the number of the
// digital pin on the Arduino connected to the diode to be
// illuminated on the serial port.
//A for loop executes 8 times to evaluate each bit of the
//binary digit
// On the control screen the researcher has the option to
// increase/decrease the conversion value and clear the byte
// register.
//
// Declarations
//
global Number_To_Convert
//Preserve original decimal value entered from the control screen
global Orgnl_N_To_Cnvrt = Number_To_Convert
//
global Converted_Number[0] = 0
global Converted_Number[1] = 0
global Converted_Number[2] = 0
global Converted_Number[3] = 0
global Converted_Number[4] = 0
global Converted_Number[5] = 0
global Converted_Number[6] = 0
global Converted_Number[7] = 0
//
//
//
//
for ( Private.Counter = 0, Counter < 8, Counter++)
Converted_Number[Counter] = Number_To_Convert %2
Number_To_Convert = Number_To_Convert/2
Number_To_Convert = Floor(Number_To_Convert)
endfor
//
if (Converted_Number[0] == 1)
//
// DigOut = 1
device.ardyRb.Write('3' + Chr(10))
endif
//
if (Converted_Number[1] == 1)
//
// DigOut_1 = 1
device.ardyRb.Write('4' + Chr(10))
endif
//
if (Converted_Number[2] == 1)
//
// DigOut_2 = 1
device.ardyRb.Write('5' + Chr(10))
endif
//
if (Converted_Number[3] == 1)
//
// DigOut_3 = 1
device.ardyRb.Write('6' + Chr(10))
endif
//
if (Converted_Number[4] == 1)
//
// DigOut_4 = 1
device.ardyRb.Write('7' + Chr(10))
endif
//
if (Converted_Number[5] == 1)
//
// DigOut_5 = 1
device.ardyRb.Write('8' + Chr(10))
endif
//
if (Converted_Number[6] == 1)
//
// DigOut_6 = 1
device.ardyRb.Write('9' + Chr(10))
endif
//
if (Converted_Number[7] == 1)
//
// DigOut_7 = 1
device.ardyRb.Write('a' + Chr(10))
endif
Listing 6-5DAQFactory Code for Decimal-to-Binary Conversion via Serial Connection
//ClearByteDisplay
//Nov.14/09
//This sequence just re-zeros the 8 bit byte display
//
DigOut = 0
DigOut_1 = 0
DigOut_2 = 0
DigOut_3 = 0
DigOut_4 = 0
DigOut_5 = 0
DigOut_6 = 0
DigOut_7 = 0
Listing 6-4DAQFactory Script Code to Clear Display
//DecBinDisplay
//Nov.14/09
//This sequence decreases the screen entered global variable
//Number_To_Convert that was converted and displayed in
//sequence ConvertDecToBinary and runs the decreased value back
//through the original sequence.
//In the original conversion sequence the Edit Box value,
//variable, Number_To_Convert is iteratively divided by two till
//it vanishes so the entered number is saved in Orgnl_N_To_Cnvrt
//
global Orgnl_N_To_Cnvrt
//clear the register of any residual data
ClearByteDisplay()
//re-initialize the working variable to the desired value to be converted
Number_to_Convert = Orgnl_N_To_Cnvrt - 1
//convert and display the bit pattern
ConvertDecToBinary()
Listing 6-3DAQFactory Script Code to Decrease the Converted Value
//IncBinDisplay
//Nov.14/09
//This sequence increases the screen entered global variable
//Number_To_Convert that was converted and displayed in
//sequence ConvertDecToBinary. The value of the original
//variable was iteratively reduced to zero by the conversion code
//but was preserved in the global variable Orgnl_N_To_Cnvrt.
//The preserved number is augmented in value and passed back through the original sequence.
//
global Orgnl_N_To_Cnvrt
//
//any residual values on the byte register are cleared
ClearByteDisplay()
//
// the original value is augmented
Number_to_Convert = Orgnl_N_To_Cnvrt + 1
//
//the augmented value is converted and displayed
ConvertDecToBinary()
Listing 6-2DAQFactory Script Code to Increase the Converted Value
//Decimal to Binary Conversion
//Oct 14-16, Nov 14/2009
//Program Algorithm
//8 LEDs are connected to the digital output channels D0 (#4)
// to D7 (#11) on the DB25 output of the LabJack. Each line is
// buffered/driven with a 4050 buffer.
//An EDIT box accepts the Number_To_Convert and the modulo of
// the value with respect to base 2 is determined for each bit
// of a byte. The bit values are then displayed on the LEDs
//A for loop executes 8 times to evaluate each bit of the
//binary digit
// On the control screen the researcher has the option to
// increase/decrease the conversion value and clear the byte
// register.
//
// Declarations
//
global Number_To_Convert
//Preserve original decimal value entered from the control screen
global Orgnl_N_To_Cnvrt = Number_To_Convert
//
Private Converted_Number[0] = 0
Private Converted_Number[1] = 0
Private Converted_Number[2] = 0
Private Converted_Number[3] = 0
Private Converted_Number[4] = 0
Private Converted_Number[5] = 0
Private Converted_Number[6] = 0
Private Converted_Number[7] = 0
//
//
for ( Private.Counter = 0, Counter < 8, Counter++)
Converted_Number[Counter] = Number_To_Convert %2
Number_To_Convert = Number_To_Convert/2
Number_To_Convert = Floor(Number_To_Convert)
endfor
//
if (Converted_Number[0] == 1)
//
DigOut = 1
endif
//
if (Converted_Number[1] == 1)
//
DigOut_1 = 1
endif
//
if (Converted_Number[2] == 1)
//
DigOut_2 = 1
endif
//
if (Converted_Number[3] == 1)
//
DigOut_3 = 1
endif
//
if (Converted_Number[4] == 1)
//
DigOut_4 = 1
endif
//
if (Converted_Number[5] == 1)
//
DigOut_5 = 1
endif
//
if (Converted_Number[6] == 1)
//
DigOut_6 = 1
endif
//
if (Converted_Number[7] == 1)
//
DigOut_7 = 1
Endif
Listing 6-1DAQFactory–LabJack U12 Decimal-to-Binary Sequence Codes
Raspberry Pi 代码清单
# Utility program to reset the GPIO pin values to 0
import RPi.GPIO as GPIO
# set the pin identity mode
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
# Reset the array pins to off/false/0
chan_list = (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)
GPIO.setup(chan_list, GPIO.OUT)
GPIO.output(chan_list, GPIO.LOW)delay)
Listing 6-15Utility Program to Reset the GPIO Pin Values to Zero
# Test the LED Array on the GPIO pins
#
import RPi.GPIO as GPIO
import time
# set the pin identity mode
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
# Reset the array pins to on for 3 sec then turn off
for i in range(2, 21):
GPIO.setup(i, GPIO.OUT)
GPIO.output(i, GPIO.HIGH)
print("Testing pin ",i)
time.sleep(3)
GPIO.output(i, GPIO.LOW)
Listing 6-14RPi Code for Testing LEDs on GPIO Pin Array
# An SPI based program to read an MCP3008 10 Bit ADC
# the referenced voltage range is divided into an integer from
# 0 to 1023 sampled voltage is ADC/1023 * 3.3 volts. A blend of
# GPIO and SPI code is used to run a 10 bit LED display of the
# ADC value.
#
# import the RPi.GPIO low level pin control library
import RPi.GPIO as GPIO
import spidev
import time
# setup the pin identification scheme
GPIO.setmode(GPIO.BCM)
# turn off the array use warnings
GPIO.setwarnings(False)
# ensure all the LED driver outputs are set to output and are zero
for i in range(2, 7):
GPIO.setup(i, GPIO.OUT)
GPIO.output(i, GPIO.LOW)
for i in range(13, 18):
GPIO.setup(i, GPIO.OUT)
GPIO.output(i, GPIO.LOW)
# create required variables
# the delay time
delay = 0.5 # the value of time variable delay is defined
# the channel to use
pot_chnnl = 0
# create a spidev object of the device connected to the
# channel in use
spi = spidev.SpiDev()
spi.open(0, 0)
# create the readadc function that checks for the correct channel assignment
# if the channel assignment is correct the adc value is read
# the function returns -1 a channel error or the adc value
def readadc(pot_chnnl):
# check channel
if pot_chnnl > 7 or pot_chnnl < 0:
return -1
r = spi.xfer2([1, 8 + pot_chnnl << 4, 0])
data = ((r[1] & 3) << 8) + r[2]
return data
#
# the while loop, print out and time delay
while True:
wpr_vlu = int(readadc(pot_chnnl))
print("--------------------------------")
print("Pot wiper value = ", wpr_vlu)
# The LED Display Code
# Although initially set to low each binary bit
# determination must be reset to low as the code cycles in
# the while loop.
#
# first or least significant bit of 2**0 or 1s
Q1 = wpr_vlu // 2
rem1 = wpr_vlu % 2
GPIO.output(2, GPIO.LOW)
if rem1 == 1:
print("Q1 = ", Q1, "rem1 = ", rem1)
GPIO.output(2, GPIO.HIGH)
# second significant bit of 2**1 or 2s
Q2 = Q1 // 2
rem1 = Q1 % 2
GPIO.output(3, GPIO.LOW)
if rem1 == 1:
print("Q2 = ", Q2, "rem1 = ", rem1)
GPIO.output(2, GPIO.HIGH)
# third significant bit of 2**2 or 4s
Q3 = Q2 // 2
rem1 = Q2 % 2
GPIO.output(3, GPIO.LOW)
if rem1 == 1:
print("Q3 = ", Q3, "rem1 = ", rem1)
GPIO.output(3, GPIO.HIGH)
# fourth significant bit of 2**3 or 8s
Q4 = Q3 // 2
rem1 = Q3 % 2
GPIO.output(4, GPIO.LOW)
if rem1 == 1:
print("Q4 = ", Q4, "rem1 = ", rem1)
GPIO.output(4, GPIO.HIGH)
# fifth significant bit of 2**4 or 16s
Q5 = Q4 // 2
rem1 = Q4 % 2
GPIO.output(5, GPIO.LOW)
if rem1 == 1:
print("Q5 = ", Q5, "rem1 = ", rem1)
GPIO.output(5, GPIO.HIGH)
# sixth significant bit of 2**5 or 32s
Q6 = Q5 // 2
rem1 = Q5 % 2
GPIO.output(6, GPIO.LOW)
if rem1 == 1:
print("Q6 = ", Q6, "rem1 = ", rem1)
GPIO.output(6, GPIO.HIGH)
# seventh significant bit of 2**6 or 64s
Q7 = Q6 // 2
rem1 = Q6 % 2
GPIO.output(13, GPIO.LOW)
if rem1 == 1:
print("Q7 = ", Q7, "rem1 = ", rem1)
GPIO.output(13, GPIO.HIGH)
# eighth significant bit of 2**7 or 128s
Q8 = Q7 // 2
rem1 = Q7 % 2
GPIO.output(14, GPIO.LOW)
if rem1 == 1:
print("Q8 = ", Q8, "rem1 = ", rem1)
GPIO.output(14, GPIO.HIGH)
# ninth significant bit of 2**8 or 256s
Q9 = Q8 // 2
rem1 = Q8 % 2
GPIO.output(15, GPIO.LOW)
if rem1 == 1:
print("Q9 = ", Q9, "rem1 = ", rem1)
GPIO.output(15, GPIO.HIGH)
# tenth or most significant bit of 2**9 or 512s
Q10 = Q9 // 2
rem1 = Q9 % 2
GPIO.output(16, GPIO.LOW)
if rem1 == 1:
print("Q10 = ", Q10, "rem1 = ", rem1)
GPIO.output(16, GPIO.HIGH)
# timing delay
time.sleep(delay)
Listing 6-13SPI-Based Program to Read an MCP3008 10-Bit ADC
# Decimal to 12 Bit Binary LED Visual Display
#
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
# Ensure all LEDS are OFF and set to output mode
for i in range(2, 15):
GPIO.setup(i, GPIO.OUT)
GPIO.output(i, GPIO.LOW)
#
# input dec number to process
#
input_str = input("Decimal to convert to a 12 bit binary display ",)
dec = int(input_str)
print()
# print out screen display headings
print("Quotient and remainder listing for conversion and display illumination.")
print()
# first binary digit of 2**0 or 1s
Q1 = dec // 2
rem1 = dec % 2
if rem1 == 1:
GPIO.output(2, GPIO.HIGH)
print("For LED 1 Q = ", Q1, "and rem = ", rem1)
#
# second binary digit of 2**1 or 2s
Q2 = Q1 // 2
rem2 = Q1 % 2
if rem2 == 1:
GPIO.output(3, GPIO.HIGH)
print("For LED 2 Q = ", Q2, "and rem = ", rem2)
#
# third binary digit of 2**2 or 4s
Q3 = Q2 // 2
rem3 = Q2 % 2
if rem3 == 1:
GPIO.output(4, GPIO.HIGH)
print("For LED 3 Q = ",Q3, "and rem = ", rem3)
#
# fourth binary digit of 2**3 or 8s
Q4 = Q3 // 2
rem4 = Q3 % 2
if rem4 == 1:
GPIO.output(5, GPIO.HIGH)
print("For LED 4 Q = ",Q4, "and rem = ", rem4)
#
# fifth binary digit of 2**4 or 16s#
Q5 = Q4 // 2
rem5 = Q4 % 2
if rem5 == 1:
GPIO.output(6, GPIO.HIGH)
print("For LED 5 Q = ",Q5, "and rem = ", rem5)
#
# sixth binary digit of 2**5 or 32s
Q6 = Q5 // 2
rem6 = Q5 % 2
if rem6 == 1:
GPIO.output(7, GPIO.HIGH)
print("For LED 6 Q = ",Q6, "and rem = ", rem6)
#
# seventh binary digit of 2**6 or 64s
Q7 = Q6 // 2
rem7 = Q6 % 2
if rem7 == 1:
GPIO.output(8, GPIO.HIGH)
print("For LED 7 Q = ",Q7, "and rem = ", rem7)
#
# eighth binary digit of 2**7 or 128s
Q8 = Q7 // 2
rem8 = Q7 % 2
if rem8 == 1:
GPIO.output(9, GPIO.HIGH)
print("For LED 8 Q = ",Q8, "and rem = ", rem8)
#
# ninth binary digit of 2**8 or 256s
Q9 = Q8 // 2
rem9 = Q8 % 2
if rem9 == 1:
GPIO.output(10, GPIO.HIGH)
print("For LED 9 Q = ",Q9, "and rem = ", rem9)
#
# tenth binary digit of 2**9 or 512s
Q10 = Q9 // 2
rem10 = Q9 % 2
if rem10 == 1:
GPIO.output(11, GPIO.HIGH)
print("For LED 10 Q = ",Q10, "and rem = ", rem10)
#
# eleventh binary digit of 2**10 or 1024s
Q11 = Q10 // 2
rem11 = Q10 % 2
if rem11 == 1:
GPIO.output(12, GPIO.HIGH)
print("For LED 11 Q = ",Q11, "and rem = ", rem11)
#
# twelfth binary digit of 2**11 or 2048s
Q12 = Q11 // 2
rem12 = Q11 % 2
if rem12 == 1:
GPIO.output(13, GPIO.HIGH)
print("For LED 12 Q = ",Q12, "and rem = ", rem12)
Listing 6-12RPi Python Code for Decimal to 12-Bit Binary LED Visual Display
摘要
-
已经介绍了 ADC 和 DAC 的局限性以及整数和浮点(包含十进制的数字)之间的差异。
-
串行和并行信号转换以及各种串行传输协议被引入商用和较便宜的组件组装系统。
-
第七章介绍了数模转换的一个重要应用,涉及可变亮度和功率控制。
-
建筑科学仪器第四版。,摩尔,戴维斯和科普兰,剑桥大学出版社,ISBN 978-0-521-87858-6 精装本
-
电子艺术第二版 Edn。,Horowitz 和 Hill,剑桥大学出版社,ISBN -13 978 -0-521-37095-0 精装本
-
发明家实用电子学第三版 Edn。,谐谑曲与蒙克,麦格劳希尔,ISBN 978-0-07-177133-7
2
https://www.azeotech.com/dl/serialguide.pdf
3
发明家实用电子学第三版 Edn。,Schertz 和 Monk,McGraw Hill,ISBN 978-0-07-177133-7
七、可变强度和功率控制
从显示屏上任意改变或调整实验设置或过程控制设置的能力是 SCADA 系统的一个组成部分。DAQFactory 软件提供可变控制图标,如旋钮或可移动滑块。旋钮或滑块页面组件可以耦合到模拟输出通道,其值将与控制旋钮的旋转位置或滑块索引标记的线性位置成比例。
通过适当的设计,页面组件控件可用于调节大量电压和电流,进而激活机电设备。
在 GUI 控制屏幕上对图像的操作必须在某一点上被转换成电信号,以在手头的实验或过程中提供期望的机电动作。如前所述,数字系统在二进制领域中运行,其中所需的系统动作以开或关的信号形式产生。然而,有许多系统需要不断调整所需动作量的能力,因此属于模拟领域。控制风扇、泵或混合器、加热元件、照明强度和旋转定位的电机速度是可能需要实验者或过程操作者调整的一些操作。
本练习演示了通过电压控制和脉冲宽度调制(PWM)技术对过程或实验设置进行可变控制的两种方法。
馈送到诸如电机、加热器或光源的设备的功率的 DC 电压的增加通常与通过负载的额外电流成比例地增加速度、产生的热量(温度增加)或亮度。已经说过,PWM 是一种通过数字方式向负载输送部分功率的方法。实质上,PWM 控制应用将电源的全部电压以可调宽度、较高频率(通常为数百 Hz)的矩形脉冲流的形式施加在负载上。然后,通过修改脉冲波形的全导通和全截止时间的宽度来确定对负载所施加和使用的功率的控制。导通时间和矩形脉冲宽度之间的比值称为占空比(参见第十章中的图 10-5 )。施加于负载的矩形、全开或全关波形的占空比变化是 PWM 功率控制的本质。
PWM 技术可以用软件实现,也可以用集成电路实现,如后面的电流控制练习中所述。对于许多需要精确控制和平滑功率转换的应用,基于硬件的 PWM 更受青睐。
在本练习中,PWM 以最简单的方式介绍,完全由软件实现。将 PWM 过程限制为基于代码的程序限制了可用于直观演示该过程的技术,本练习的以下部分将对此进行详细说明。
为 PWM 产生矩形波形的频率必须大大高于负载的响应时间。根据经验,频率应该是器件 RC 时间常数倒数的两倍,或者是控制系统频率的十倍。简而言之,PWM 频率应该足够高,以免与负载的 RC 时间常数发生谐振。(参见参考文献中的电阻-电容时间常数。 1
PWM 是一种广泛用于功率控制、数模转换、放大器设计和通信的技术,但需要复杂的电路,并可能产生射频干扰、电压尖峰和 EMI 噪声。(参见参考文献 1 和章节 10 中的 triac 控制。)
实验的
可变电压控制
五金器具
2N3904 NPN 晶体管、限制晶体管基极电流的 10kω电阻和带有适当限流电阻的 LED 组装在试验板上,配置如图 7-1 所示。
图 7-1
NPN 晶体管的 LabJack 模拟输出 0 控制
软件
从如图 7-2 所示的右键弹出菜单中选择旋钮控制。
图 7-2
DAQFactory 屏幕组件控制选择菜单
在显示屏的一页上定位控制图标并确定其尺寸后,选择属性选项以访问旋钮配置屏幕。
下面描述的缺省值未配置屏幕图标的视觉外观可以通过主选项卡中显示的框中的适当条目进行更改。与所有屏幕图标一样,相应的帮助文件可以显示在被操作的屏幕对象下方。“属性”窗口的“主”选项卡中显示的一些视觉效果选项仅在图标的较大显示图像上才明显。
旋钮控制指示器图像默认为显示的点,但是通过指示器子面板右侧的单选按钮,可以更改为选择三角形或传统的线索引来标记控制旋转的角度。通过左键单击图 7-3 中的颜色框,从可用的调色板中选择所需的颜色,可以更改默认的蓝色指示器图像。
图 7-3
DAQFactory 屏幕组件配置窗口
图 7-4 描绘了通过选择图 7-3 中的“刻度”选项卡打开的窗口。在 Ticks 窗口中,调查者可以选择圆形标尺的标称方面,并确定显示的分辨率及其在最终窗口中的外观。
图 7-4
旋钮刻度配置窗口
作者屏幕组件的最终配置如图 7-5 所示。
图 7-5
带指示面板的基础电流和 LED 亮度旋转控制旋钮
不需要编写脚本。滑块或旋钮根据旋钮或滑块的位置自动提供可变输出。在“属性”窗口的“主”选项卡的“设置通道”框中指定的通道被设置为,并输出所需的比例信号。
观察
如 DAQFactory 手册中所述,旋钮或滑块可设置为多种配置,以控制所选通道的激活。因为本练习中的控制设置为控制 LED 的电源,所以激活 LED 所需的最小电压会产生一定量的“死区”。
讨论
通过将起始位置配置在旋转的 225 o 处并将起始电压分配给所使用的 LED 的击穿电压,可以实现对施加到晶体管基极的电压以及最终由半导体输送的功率的更精细程度的控制。LED 中的正向电压降可以从红外线的 1.2 伏变化到蓝色和白色设备的 4 或 5 伏。作者的设置使用了绿色 LED,因此根据屏幕图标的预期用途,刻度盘可以配置为从 3 伏或 0 伏开始。如果要估计开启电压本身,可以使用 0-5 伏范围并进行校准,或者可以将刻度盘设置为 3.0-5.0 伏,以再现二极管强度/施加的功率设置。
实验的
电压的脉宽调制
介绍
通常,演示 PWM 基本原理的软件程序的实施使用一个具有高频可变占空比波形的 LED,如第十章所述。在本练习中,通过一个电子电气系统演示了 PWM 的基本概念和方法,该系统具有一个非常简单的软件信号发生器和一个由白炽灯泡组成的响应相对较慢的负载。
为了与使用 DAQFactory 软件创建序列的简单介绍保持一致,本章末尾的清单 7-1 中给出了一个基本程序序列,该序列粗略地改变了通过 12 伏电池供电的 DC 汽车灯的电流。
代码已经减少到生成典型矩形波形所需的最少语句数。占空比值必须由实验人员手动输入或更改,作为 DAQFactory 序列中两个延迟语句中的数值。代码清单中的默认设置是 0.005 和 0.095,这两个值组合在一起给出了 0.1 秒的总矩形脉冲宽度。
图 7-6 描述了用于为 PWM 演示提供慢响应负载的电路。
图 7-6
用于 PWM 演示的白炽灯负载
观察
图 7-7 、 7-8 、 7-9 和 7-10 描绘了白炽灯泡亮度的变化强度以及相应的高低占空比 PWM 波形。
图 7-10
白炽灯负载的高 PWM DC 波形
图 7-9
高 DC 功率下 PWM 的白炽灯泡负载
图 7-8
白炽灯负载的低 PWM DC 波形
图 7-7
低 DC 功率下 PWM 的白炽灯泡负载
如果脉冲代码与 LED 一起使用,则可以看到 5 ms 和 95 ms 时间段的闪烁速率不同,但是眼睛很难看到两种明显闪烁的功率设置之间的照明差异。然而,如图 7-7 和 7-9 所示,白炽灯对不同功率水平的可见响应要大得多。
讨论
在本练习的 PWM 介绍中,提出了一个要点,即承载功率的矩形波的频率需要大大高于施加功率脉冲的系统的时间常数。简而言之,可以说 PWM 频率必须高于负载的响应时间。白炽灯和电池的响应时间很慢,足以直观地说明一个非常简单、基本的 DAQFactory 序列在创建图形可视化 PWM 演示时的功率控制能力。本演示的 PWM 信号由 DAQFactory 序列生成,只有 6 行代码。图 7-8 和 7-10 的示波器记录对应的频率略低于 2 Hz。虽然频率不是那么高,但是对于灯丝加热并达到热和照明稳定性所需的时间来说,双循环 PWM 照明控制技术足以产生期望的结果。使用软件程序(如代码清单中的第一个条目)来改变信号开启和关闭的时间通常被称为“位碰撞”PWM。
PWM 技术是许多数字电子和机电系统中非常重要的一部分,在许多情况下,通过硬件设备来实现,在接下来的几个使用 Arduino 微控制器的练习中将会遇到这种情况。
几乎所有在之前练习中作为 DAQ 系统廉价替代品使用的微控制器板都配备了数字引脚输出,能够输出硬件实现的 PWM 信号(参见第十章)。
树莓 Pi 可变强度控制
介绍
RPi 的物理计算只能通过通用输入和输出引脚阵列来实现,在第一章的图 1-16 中,RPi 电路板上部有双排 13 或 20 个公引脚。阵列上可编程引脚的数字特性允许从实验者编写的代码或从包含各种形式的 PWM 操作的库中实现软件 PWM 操作。
强度变化屏幕控制图标或组件可从可从在线资源获得的 tkinter 图形图像库中获得,如 Raspberry Pi 和 Python 基金会在线提供的文档中所讨论的。 2
为了适应物理计算和“物联网”不断增长的兴趣和发展,RPi 基金会批准了三个开源 Python 库,以促进 GPIO 阵列在连接外部世界中的使用。最初的库版本是一个更基础或更低级的代码,导入名称为 RPi。GPIO,而后来更复杂的代码可以通过导入 gpiozero 和 pigpio 库来访问。RPi。GPIO 包含使用轮询方法或中断来检测机械运动(如按钮或开关触点闭合)、“去抖动”这些事件以及使用触点动作来启动 GPIO 引脚阵列上的电气活动)的机械设备所需的代码。据 RPi 文档报道,gpiozero 库是建立在 RPi 之上的。GPIO 库,包含许多非常易于使用的对象的元素,这些元素来自于使用非常好解释的、面向对象的编程代码。图书馆使用和可用对象的详细列表可以在网上找到。研究人员和教育工作者会发现 gpiozero 库中创建的对象列表非常广泛,并且详细描述它们的实现和连接的文档非常详细,以至于打印出的档案可能会对进一步的工作有很大帮助。
第三个也是最近发布的物理计算库,作为 pigpio 导入,与前两个工具有很大不同,因为它是用 C 编写的,可以在几种操作系统上实现。为了在 RPi 的 Linux 操作系统上使用,必须运行一个接口程序,以便 Python 解释器访问 pigpio 库。这个程序也称为守护程序,它是从 Linux 终端用 sudo pigpiod 命令启动的。
pigpio 设施拥有丰富的文档,包括详细的代码语法、众多可用的测试和可视化实用程序,以及各种简单和非常复杂的代码,可与各种传感器和硬件接口。该库还为一些更流行的通信协议提供了代码。
c 代码以其非常快的执行速度而闻名,pigpio 库使用软件和硬件为其 PWM 和矩形波形生成以及电压转换检测操作提供单位数微秒的时间分辨率。
PWM 应用程序可通过所有库获得,本章末尾提供了三个库中的几个 RPi 代码清单,以展示使用三种不同工具的软件 PWM 功率控制的各个方面。
实验的
使用 RPi 物理计算库的软件 PWM 信号生成和应用在一系列六个程序中介绍。通过使用空闲屏幕菜单中的运行模块访问和处理存储的程序代码,可以观察到所需的 PWM 效果。使用 Ctrl+C 组合键可以暂停程序执行。
除了演示程序,还提供了一个非常简短的实用程序来帮助开发和测试 pigpio 物理计算代码。
带 RPi 的 PWM 信号。GPIO 库
列表 7-2 可用于演示图 7-11 电路产生 PWM 波形的基本原理。
图 7-11
LED–GPIO 引脚连接示意图
在以前的演习中已经注意到。GPIO 阵列的电流有限,无法安全地提供给与引脚相连的任何外设。图 7-11 描述了显示正在研究和开发的程序所产生的效果所需的一般连接。留给实验者的是安全地配置电子组件,使得从使用中的 GPIO 引脚和阵列接地汲取的功率安全地处于计算机和 LED 的操作限制内。
程序代码 PWM_tst1 在 3.3 和 0 伏之间升高然后降低 GPIO 阵列的#6 引脚。开关脉冲的宽度或其占空比(DC)由研究者定义并作为变量 prcnt_on 输入程序代码。实际的 PWM 波形由两个环路产生,一个是由“while”语句控制的连续外部环路,该语句将#6 引脚设置为高值,另一个是内部“if 环路”,该环路在将引脚复位至 0 伏之前,计算 prcnt_on 变量中的单位数。
将 prcnt_on 变量设置为 5、50 和 95 可用于演示 LED 照明强度的变化。
用 RPi 编写的清单 7-3 给出了 PWM 信号的频率对观察到的 LED 照度的影响的演示。GPIO 库。在开发 RPi 时。GPIO PWM 频率效果演示程序,当使用五个不同的阵列引脚-LED 通道来演示 PWM 频率对观察到的 LED 照明的效果时,获得了合适的视觉效果。演示程序将占空比设置为 95%的恒定值,仅改变 PWM 功率信号的频率。
在加载和运行 PWM 频率效果代码之前,五个 led 和限流电阻必须连接到 GPIO 阵列,如图 7-11 所示。可以使用在物理位置找到的 GPIO 管脚 3、4、5、6 和 7(参见章节 1 ,图 1-16 ) 5、7、29、31 和 26 的 40 管脚阵列。
带 gpiozero 库的 PWM 信号
gpiozero 文档中的清单 7-4 是一个非常简单的单 LED PWM 照明变化程序,演示了 gpiozero 库可用的高级接口。脉冲 LED PWM 程序改变施加到 LED 的功率,从而改变其输出的强度或亮度,该程序由五行代码组成,其中两行是 import 语句。演示软件 PWM 的电路配置如图 7-11 所示。作者使用 5 mm LED 和 220ω限流电阻,并将电路连接到 GPIO 引脚 21(物理引脚#40)和地(物理引脚#34)。为了查看该技术可能实现的控制,将程序代码加载到 Python 空闲编辑器屏幕中,并使用运行菜单来启动或处理代码。
gpiozero 库包含许多用于连接 RPi 输出阵列和 MCP3008、8 通道、10 位 ADC 的对象,如第六章、图 6-17 的电路图所示,可用于使用 RGB LED 的 PWM 演示。
清单 7-5 和 7-6 使用偏置在正 RPi 电源输出和地之间的三个电位计,向 MCP ADC 的前三个通道提供三个信号,这三个信号又充当三个 PWM 信号,以改变 LED 的各个红色、绿色和蓝色输出的强度。这些程序的不同之处在于用于实现 PWM 功能的代码。理论上,任何想要的光的颜色都可以通过三电位器颜色控制电路配置产生,如图 7-12 所示。
图 7-12
PWM 三电位计 RGB LED 颜色控制电路
虽然 MCP3008 的前三个通道使用 220ω电阻来限制通过二极管的电流,但绿色 LED 的典型输出强度可能是红色和蓝色器件的五倍。为了“平衡”或均衡绿色通道的灵敏度,实验者可能需要更高的电阻值。
pigpio 库的 PWM 信号
要在操作系统映像或操作系统代码安装上加载、激活和访问 pigpio 库(代码中没有包含该库),必须在终端输入大量命令。(Raspbian Jessie 2016-05-10 或更新版本预装 pigpio C 库。)
作者的 RPi 已经使用了好几年,新的库必须在终端加载,如下所述:
-
在终端提示符下输入–wget abyz.co.uk/rpi/pigpio/pigpio.zip
-
猪小妹. zip
-
pipipipo CD
-
制造
-
进行安装
前两行下载压缩文件并解压缩 Python 代码,以便在 home / pi 目录中快速创建文件 PIGPIO。“make”和“make install”可能需要一分钟左右的时间来处理,这取决于安装库的 Pi 的速度。在/home / pi / PIGPIO 文件中创建了三个程序:“pigpio.py”是一个解释 Python pigpio 模块的文档程序,它有一百多页,定义并解释了所有的模块函数和变量,并提供了简短的典型编码应用程序。除了文档之外,还有“setup.py”和“x_pigpio.py”,前者是 pigpio 守护进程的 RPi 访问模块,后者是 pigpio 库中所有可用库函数的 15-20 页的完整测试程序代码。
清单 7-7 是一个演示 pigpio 基本操作的简单程序,通过运行界面(pigpiod)访问 pigpio 库,打开和关闭 LED,然后通过四步 PWM 照明强度增加和减少来改变亮度。清单 7-8 是一个 pigpio 测试实用程序,它打印出数组中 gpio 管脚的状态。
观察
PWM_tst1
当将三个模拟占空比值输入程序并运行代码时,可以很容易地看出低、中和高实验之间的 LED 照明强度变化。在三个照明周期期间,代码似乎循环得足够快,以产生不能立即察觉的闪烁速率。
图 7-13 是 RPi 的输出。GPIO PWM 频率效果演示程序。
图 7-13
RPi 对 PWM 频率变化的影响。GPIO 库代码
LED 能够在兆赫兹范围内开关;正如预期的那样,2、5 和 8 Hz PWM 信号闪烁不定,闪烁明显,而 11 和 14 Hz 信号相当稳定,闪烁或“抖动”极小。(抖动描述和来源见“讨论”。)
RGB LED 输出的 PWM 控制
可以看出,随着可变电阻器上的轴分别从关闭位置转到全开位置,各种二极管颜色主导了器件输出。尽管图 7-12 的电路具有三个相等的电阻值,允许绿色主导 LED 输出,但二极管输出强度的明显零星和不规则变化在视觉上是可辨别的。
当 LED 通电时,以及在四步增加和减少二极管照明强度的过程中,pigpio 库程序产生明亮稳定的照明。(参见“讨论”)
代码清单中包括测试 RPi GPIO 阵列前 32 个引脚状态的实用程序,并以表格形式打印出它们的状态,如图 7-14 所示。
图 7-14
GPIO 引脚的测试状态
测试程序不仅打印出阵列中 32 个引脚的状态,还确认了 Python 解释器与 C 代码库的 Linux 守护程序接口的操作。
在使用第六章的“将数组重置为零”实用程序清除 RPi 数组(在第六章中列出了 6-15 )后,运行前面的列表。引脚 1、2 和 29 是阵列的电源引脚。
讨论
RPi 的一部分。GPIO 库致力于实现底层设备的接口,比如机械开关到 RPi 的 GPIO 管脚阵列。该库具有用于确定任何引脚的当前状态是高还是低(+5 或 3.3 V 的系统逻辑电压电平或 0 V 的系统地电位)的功能,检测引脚状态的变化,以及用于确定何时或如何监控或检测转换。
RPi 使用基于 Linux 的多任务操作系统,该操作系统可能会暂时从较低优先级的输入/输出操作中夺走控制权。在鼠标或键盘操作中遇到的正常时间范围内,I/O 操作可能不会受到延迟的显著影响;但是对于较短时间尺度上的高精度操作,例如图形显示,它可能变得非常明显。在本练习中,不规则时序的视觉效果可以被视为 LED 亮度的闪烁,或者有时被称为“抖动”
当使用 RPi 的改变频率功能时。GPIO 库,为每个新频率使用不同的引脚阵列 LED 通道比尝试使用同一通道并改变其频率五次要容易得多。
在三个 GPIO 物理计算库的可能应用中,能力和灵活性的显著提高是显而易见的。pigpio 使用起来更复杂,但是比简单的库更强大。如前所述,基于 C 的库能够使用 Linux 操作系统和系统硬件在许多库操作上实现一位数微秒时间分辨率。本练习中使用的简单演示程序完全没有闪烁或抖动。
除了简单的 LED 照明功能程序之外,在代码清单的末尾还包括一个非常简短但有用的实用程序,它可以打印输出从 0 到 31 的每个 GPIO 引脚的状态。控制台的打印输出将所有引脚及其当前高/低值列为 1 或 0。除了显示单个引脚的高/低电压电平,该实用程序还确认 Python pigpio 接口守护程序的功能。
pigpio 库函数和功能的大部分将在后面 RPi 物理计算中处理高级 PWM 应用的高级练习中按要求遇到、演示和讨论;传感器启动或监控;串行、I2C 和 SPI 通信;和电机或伺服控制。
代码列表
# PWM Control of RGB Led Diode Pgm 2
# PWM Control of RGB Led Diode
from gpiozero import RGBLED, MCP3008
from signal import pause
#
led = RGBLED(2, 3, 4)
#
red_pot = MCP3008(channel=0)
green_pot = MCP3008(channel=1)
blue_pot = MCP3008(channel=2)
#
led.source = zip(red_pot.values, green_pot.values, blue_pot.values)
#
pause()
Listing 7-6PWM Control of RGB LED with Three ADC Channels and Pause()
# PWM Control of RGB Led Diode Pgm 1
from gpiozero import RGBLED, MCP3008
#
led = RGBLED(red=2, green=3, blue=4)
#
red_pot = MCP3008(channel=0)
green_pot = MCP3008(channel=1)
blue_pot = MCP3008(channel=2)
#
while True:
led.red = red_pot.value
led.green = green_pot.value
led.blue = blue_pot.value
Listing 7-5Control of a RGB LED with gpiozero PWM Library and Three Potentiometers
from gpiozero import PWMLED
from signal import pause
led = PWMLED(21)
led.pulse()
pause()
Listing 7-4Single-LED PWM with the gpiozero Library
# RPi PWM Frequency Demonstration with the RPi.GPIO Library
# 5 LEDs are used to illustrate the effects of the frequency of the carrier wave on PWM techniques. Different carrier
# frequencies are used at a constant duty cycle to illustrate
# the effects of frequency om PWM
import RPi.GPIO as GPIO
import time
# Array set up
GPIO.setmode(GPIO.BCM) # Use BCM pin reference
GPIO.setwarnings(False) # turn off the array use warnings
GPIO.setup(3, GPIO.OUT) # set pin #3 for output
#
pwm = GPIO.PWM(3, 2) # a PWM instance on pin 3 to operate at 2 Hz is setup
print("PWM carrier frequency set to 2 Hz")
print(" ")# print a blank line in the output
dc = 95 # the duty cycle is set close to full on
pwm.start(dc)# start the application of PWM power
time.sleep(5)# Keep the LED illuminated for 5 seconds
pwm.stop()# stop the power application
#
# carrier frequency increased to 5 Hz
print("PWM carrier frequency set to 5 Hz")
print(" ")
GPIO.setup(4, GPIO.OUT) # set pin #4 for output
pwm = GPIO.PWM(4, 5) # a PWM instance on pin 4 to operate at 5 Hz is setup
dc = 95 # the duty cycle is set close to full on
pwm.start(dc)
time.sleep(5)
#
# carrier frequency increased to 8 Hz
print("PWM carrier frequency set to 8 Hz")
print(" ")
GPIO.setup(5, GPIO.OUT) # set pin #5 for output
pwm = GPIO.PWM(5, 8) # a PWM instance on pin 5 to operate at 8 Hz is setup
dc = 95 # the duty cycle is set close to full on
pwm.start(dc)
time.sleep(5)
pwm.stop()
#
# carrier frequency increased to 11 Hz
print("PWM carrier frequency set to 11 Hz")
print(" ")
GPIO.setup(6, GPIO.OUT) # set pin #6 for output
pwm = GPIO.PWM(6, 11) # a PWM instance on pin 6 to operate at 11 Hz is setup
dc = 95 # the duty cycle is set close to full on
pwm.start(dc)
time.sleep(5)
pwm.stop()
#
# carrier frequency increased to 14 Hz
print("PWM carrier frequency set to 14 Hz")
print(" ")
GPIO.setup(7, GPIO.OUT) # set pin #7 for output
pwm = GPIO.PWM(7, 14) # a PWM instance on pin 7 to operate at 14 Hz is setup
dc = 95 # the duty cycle is set close to full on
pwm.start(dc)
time.sleep(5)
pwm.stop()
Listing 7-3RPI.GPIO PWM Frequency Effect Demonstration
Python Code for Raspberry Pi PWM_tst1
# A software PWM demonstration on GPIO - 6
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(6, GPIO.OUT)
#
# set the duty cycle
prcnt_on = 10
#
# the outer loop to provide the continuous application of the modulated power signal
while True:
# start the duty cycle loop and set the output pin to ON
GPIO.output(6, 1)
for i in range(1, 100):
if i == prcnt_on:
GPIO.output(6, 0)
Listing 7-2RPi Python PWM_tst1
while(1)
sftwr_pwm = 5
delay(0.095)
sftwr_pwm = 0
delay(0.005)
endwhile
Listing 7-1DAQFactory Sequence Program for PWM
# A simple demonstration of some basic pigpio capabilities.
# The PIGPIO library must be d/l, installed and available on the RPi in use.
# The requirements for use of the library code must be met and the interface
# often called a daemon must be running to provide an interface between the pigpio library written in C and the
# Python interpreter. (see PIGPIO documentation)
#
import pigpio
import time
#
pi = pigpio.pi()# create a instance of the pigpio class
#
# Simple LED illumination
pi.set_mode(4, pigpio.OUTPUT) #set gpio 4 for output
pi.write(4,1) # set gpio pin 4 high
time.sleep(0.5)# delay for 1/2 sec
pi.write(4,0) # turn LED off
#
time.sleep(2) # delay for 2 sec between displays
#
# simple PWM controlled variable brightness scaled from 0 – off to 255 – full on
pi.set_PWM_dutycycle(4, 0) #PWM off
time.sleep(0.5)# delay for 1/2 sec
pi.set_PWM_dutycycle(4, 64) # PWM power at 1/4 on
time.sleep(0.5)
pi.set_PWM_dutycycle(4,128) # PWM power at 1/2 on
time.sleep(0.5)
pi.set_PWM_dutycycle(4,192) # PWM power 3/4 on
time.sleep(0.5)
pi.set_PWM_dutycycle(4, 255) # PWM power full on
time.sleep(0.5)
pi.set_PWM_dutycycle(4,192) # PWM power 3/4 on
time.sleep(0.5)
pi.set_PWM_dutycycle(4,128) # PWM power 1/2 on
time.sleep(0.5)
pi.set_PWM_dutycycle(4, 64) # PWM power 1/4 on
time.sleep(0.5)
pi.set_PWM_dutycycle(4, 0) # PWM power off
#
pi.stop()
Listing 7-7pigpio Basic Operations Program
# pigpio pin status and test utility
# ensure that the pigpio daemon is running and run the following code from the run menu in the Python 3 IDLE facility.
#
import pigpio
pi = pigpio.pi() # create an instance of the library
for g in range(0, 32): # recall range must be the required number of iterations + 1
print("gpio {} is {}".format(g, pigio.read(g))) # print out a tabulated status report
pigpio.stop()
Listing 7-8pigpio Test Utility
摘要
-
商业软件中的可变强度控制用于在软件中实现 PWM 方法,以演示该技术如何工作。
-
介绍了几种利用廉价的 RPi 计算平台实现 PWM 技术的方法。
-
在第八章中,介绍了 SCADA 系统中主机计算机外部发生的事件的检测以及如何测量多个事件之间的时间。
发明家实用电子学第三版 Edn。,Schertz 和 Monk,McGraw Hill,ISBN 978-0-07-177133-7
2
docs/python.org/3/library/tk.html
3
http://gpiozero.readthedocs.org/
和 https://sourceforge.net/p/raspberry-gpio-python/wiki/Examples
八、计数事件和计时
本练习考虑在物理计算中处理时间测量的可用方法。软件或硬件可以用来直接测量时间或时间间隔。然后,通过计算固定时间单位内发生的事件数量,可以使用时间间隔进行频率测量,或者在评估时间间隔内行驶的距离时确定速度和加速度。
确定一天中的时间和事件之间的时间是收集科学数据和过程控制中的重要参数。开灯或从 9:05 到 9:35 收集数据可以被分类为“绝对”或“一天中的时间”格式,而测量球通过粘性液体下落固定距离所需的时间可以被称为差分时间测量。
DAQFactory 的用户手册建议不要试图用软件脚本在少于半秒或四分之一秒的时间框架内工作。几分之一秒大约是高级软件在保持系统状态显示屏用户界面的同时处理用于数据处理的代码线程的能力的极限。毫秒、微秒或更短时间帧的测量通常需要使用汇编语言编程进行软件计时,或使用物理计时设备进行硬件计时。有几种可用的硬件计时器件,如 LabJack HMI、Arduino 微控制器板或 555 定时器集成电路,所有这些器件都能够在毫秒和微秒的时间范围内工作。本章的第一部分研究了数字时间概念的基础,并展示了软件的局限性。本章的其余部分和练习涉及通过集成电路硬件可用的短时标和频率的介绍性概念。
在电子学中,时间是用产生固定电压(5.0 或 3.3 伏)方波信号的振荡器时钟来测量的,方波信号的频率可能在兆赫兹和千兆赫兹范围内。(MHz 为每秒 10 6 个周期,GHz 为每秒 10 9 个周期。)撰写本文的个人电脑的时钟速度为 1.48 GHz,而 Arduino 微控制器的时钟速度为 16 MHz,各种型号的 Raspberry Pi 的时钟速度为 800 MHz 至 1.5 GHz。
由石英晶体调节到高精度和准确度的电子振荡器可以被配置成产生方波的脉冲序列,该脉冲序列可以被单独计数以测量时间。凭借非常稳定的 MHz 和 GHz 振荡器频率以及独立脉冲计数能力,可以非常精确地测量微秒和纳秒时间帧。
当计算设备关闭时,台式、便携式和网络连接的计算设备能够通过网络连接或电池备用系统来跟踪一天中的时间。一些设备,如 Raspberry Pi 和 Arduino 微控制器,需要添加一个称为“实时时钟”(RTC)的附件,该附件具有备用电池,可以在设备断电时准确跟踪一天中的时间。
软件时间和计时
DAQFactory SCADA 软件是一个独立的程序,可在基于 Windows 的操作系统上运行。当计算机在操作会话之间关闭时,电池用于维持操作系统时间计数。自 1970 年以来,DAQFactory 中的时间以微秒为单位进行测量。当 DAQFactory 会话启动时,程序从操作系统获取日期和时间,初始化内部计数器,并从 1970 年 1 月 1 日开始以秒为单位保持时间计数。DAQFactory 时钟独立于操作系统定时器运行,并产生十进制秒时间分辨率。DAQFactory 中可用的时间函数在用户手册的“表达式”部分有详细说明。本练习的第一部分使用了可用的软件时间功能来创建两个分辨率为 1 秒的屏幕激活定时器,用于启动和停止外部试验板上的电子操作,并测量手动观察事件之间的经过时间或累计时间,就像手持秒表一样。基本软件时间评估屏幕如图 8-1 所示。
图 8-1
三种计时操作的图形用户界面
基本时间变量
图 8-2 描述了几个基本的当前 DAQFactory 时间值。
图 8-2
当前定时操作和值的 GUI
基本变量屏幕由六个变量值组件(VVC)组成,在以红色表示的日期和时间拍摄。配置面板上的条目将有助于理解计数器“时钟滴答”的数学操作及其与我们循环时间概念中的小时、分钟和秒钟的关系:
-
第一行变量值组件(VVC)表达式是 SysTime(),它显示自 1970 年 1 月 1 日以来经过的秒数。
-
第 2 行使用 VVC 表达式框中的语句“FormatDateTime("%c ",SysTime())”来生成显示的时间。用户手册中列出了大量可供使用的格式。为了突出视觉效果,显示屏上用了红色大字。
-
第 3–5 行使用模记数法将 SysTime()的总滴答计数转换为各种计时值,而第 6 行产生逻辑 1 和 0 之间的一秒调制切换。
预定事件计时器
标题为预定时间计时器的清单 8-1 (在本章末尾与所有其他清单一起提供)的脚本控制图 8-3 所示的 GUI 面板。
图 8-3
DAQFactory 预定事件定时器图形用户界面
秒表计时器
图 8-4 中描述的标称控件组实现秒表式计时,并由清单 8-2 的代码控制。
图 8-4
秒表计时器 GUI
秒表计时器由清单 8-3 和 8-4 中的三个 DAQFactory 序列激活。启动/停止按钮(列表 8-2 )启动或终止计时动作,从而定义间隔。鼠标点击累计时间按钮,将当前时间间隔加入累计时间总和。单击重置显示将 GUI 重置为零。
硬件时序、事件计数和频率确定
我们世界中的任何物理行为,如开门、进入房间和开灯,都可以被传感器转换为电子系统可以监控和记录的电转换。典型的灯开关的激活导致施加到光源上的电功率从打开极限跳到关闭极限。由开灯、让灯亮一段时间 t、然后关掉灯的动作产生的电波形可以被认为是“持续时间为 t 的脉冲”。电脉冲可以通过将机械、光学和电磁事件转换成电信号电平的急剧变化来产生。
脉冲计数通过使用双极晶体管或 CMOS 集成电路来实现,其中互连的晶体管开关能够以二进制格式记录施加到芯片输入引脚的电子信号中出现的 0 到 5(或 3.3)伏之间的转换次数。对给定时间段内的电子跃迁次数进行计数是对输入信号频率的测量,而对从过去的起始点开始已经发生的脉冲总数进行计数是对经过时间或总时间流逝的测量。
用于存储二进制信息的基本单元被称为“触发器”或“锁存器”触发器是稳定在两种状态之一的开关配置,其中锁存器或触发器的输入导致输出在 1 和 0 两个二进制逻辑状态之间变化。触发器、多谐振荡器或锁存器的基本电路如图 8-5 所示。
图 8-5
基极结晶体管触发器
简单的触发器最初由电流控制器件制成,如真空管,后来由双极晶体管制成,最后由极低电流消耗的电压控制器件制成,如场效应晶体管(fet)和金属氧化物半导体场效应晶体管(MOSFETs)。这些通常被称为多谐振荡器,并被称为双稳态电路。该电路保持在其两个稳定状态中的任一个,直到施加控制信号将其切换到另一个稳定状态。
为了理解基本电路是如何工作的,我们可以在图 8-5 中看到,如果 V1 接地,那么将没有基极电流通过 Q2 的基极。在双极晶体管中,通过集电极和发射极的电流(发射极箭头表示正电流)由基极电流控制。没有基极电流的晶体管 Q2 将没有集电极-发射极电流。从 Vcc 通过 R2 的电流全部流入 Q1 的基极,然后导致更大的电流流过 Q1。对于对称电路,如果 V2 接地,则 Q1 的基极电流被切断,并且通过 R1 的电流流入 Q2 的基极,当电路切换到第二稳定状态时,导致更大的电流流过 Q2。
简单的触发器是大量主要是极低电流消耗的互补金属氧化物半导体压控集成电路的基本构建模块,这些集成电路提供诸如存储器存储、逻辑和数学功能的功能。(参见第章 1 ,图 1-15 。)
锁存器、触发器、数字逻辑计数、振荡器时钟以及各种集成电路逻辑芯片组家族的应用的细节可以在几本参考著作中找到。 1
LabJack HMI 上标有 CNT 的端子提供对集成电路的访问,该集成电路能够计数电压电平从+5 变到 0 再变回+5 伏的次数。这种事件计数由设备的集成电路以二进制进行,结果以十进制格式显示在系统屏幕上。要计数的事件必须转换成前面提到的电压电平变化。LabJack 计数器具有 32 位容量,允许总共计数 2 个 32 个或 4,294,967,296 个事件。因为事件计数器是一个集成电路,它可以以高达 1 MHz 的频率计数。
在下面的练习中,将创建一个手动事件计数器,它可以由任意数量的传感器递增,例如日光级别的变化、物体经过某个点或单击系统屏幕图标。LabJack 和 DAQFactory 软件组合的实验设置或过程控制系统的配置涉及以秒或更长时间测量的时间跨度,这不是问题。用户手册中的文件表明,许多指令的执行时间为 20 ms,这为基于视觉的 SCADA 系统可合理预期的最短时间响应建立了下限。高速信号变化最好用称为流或突发模式操作的技术来记录。对于采集后处理,以非常快的硬件速度获取速度或频率远远超出肉眼分辨能力的高速信号变化。用户手册中详细介绍了高速操作,后续章节和练习中也会涉及到。
LabJack 计数器被视为硬件设备,因此不受软件执行时间的限制。被称为 555 定时器的集成电路设备可以与计数器结合使用,以在比软件执行开销所强加的时间跨度更短的时间跨度内工作。555 定时器也是一个基于“硬件”的集成电路,因此,像 LabJack 计数器一样,能够在从微秒到小时的时间范围内工作。双极性晶体管和 CMOS 555 定时器 IC 的详细信息可以在许多参考文献中找到,包括前面提到的参考文献。各种形式的定时器之间的差异在于它们的功率处理能力,双极形式是高电流类型,而 CMOS 形式是基于低电压的。
实验的
五金器具
1)简单的手动事件计数
2)简单的连续事件计数或频率确定
蓝色 LED 和 470ω限流电阻可用于演示屏幕启动事件的手动计数。
电路原理图
二极管和限流电阻器的配置如第章第一部分图 1-3 所示,红色二极管的串联对的连接点连接到 CB25 端子板上的 D9。
软件
为了演示计数器使用的两种模式,创建了图 8-6 中带有两个按钮、两个变量值组件和一个描述性文本组件的面板。
图 8-6
LabJack U12 计数器使用演示
对于手动激活的事件计数,创建一个屏幕按钮图标,适当标记,并链接到一个输出通道。作者的按钮标有“Initiate Event”,配置为激活清单 8-5“A _ Counter _ Event”的脚本,该脚本通过一个创建为“DigOut_9_EvntCntr”的通道应用 5–0–5 伏转换,该通道连接到 CB25 板端子上的输出引脚 9。点击屏幕按钮从而驱动 D9 输出从 5 伏到 0 伏,然后回到 5 伏,依次关闭蓝色 LED,然后再打开。计数器端子 CNT 用于监控蓝色 LED 与其 470ω限流电阻连接点处的电压电平。手动点击“启动事件”按钮,通过列表 8-5 DAQFactory 序列递增计数器。
DAQFactory LabJack 手册 2 第十部分详细说明了单个 LabJack 计数器的操作。由于只有一个计数器,所有数据都通过计数器通道 0 传递。默认情况下,每次读取通道时,计数器值都会重置为 0,因此在本练习的第一部分,必须关闭默认值,以便不断增加其值,直到手动将其重置为 0。在本练习中,我们创建一个名为“EventsCounted”的通道,其 I/O 类型为计数器;在“频道表视图”中,在 Quick Note / Special / OPC 标题下,应该可以看到一个位于单元格右侧的按钮,上面有三个点(…)。点击按钮,调出通道参数窗口,并在下拉列表中选择重置。唯一的参数是“重置?”,选择“是”或“否”、“确定”和“应用”将立即配置计数器通道,使其在读取通道值进行显示时不会重置为零。
第二个标记为“ResetCounter”的按钮被配置为启动清单 8-6 的简短“Reset Counter”脚本,该脚本将“EventsCounted”通道 events count[0]的最新值设置为零。事件计数[0]的可变值组件显示位于按钮下方,用于指示计数的事件数。图 8-6 中描述了为演示简单计数器用法而创建的面板。
点击启动事件按钮,激活练习代码列表中的列表 8-5 。
点击屏幕按钮“启动事件”,蓝色灯亮起,事件计数器增加。点击“重置计数器”(列表 8-6 )将“计数的事件”变量值显示设置回 0。
脚本
清单 8-5 和清单 8-6 是 DAQFactory 序列,其脚本创建用于频率测量的方波信号,带有 time - goto 脚本和用于手动清除计数器和关闭任何可能留在 on 配置中的 LED 的代码。
如前所述,“重置计数器”按钮激活列表 8-6 的短序列,该序列由单行活动代码组成,用于将计数器通道的值设置为 0。
通过将 LabJack 计数器通道切换回默认设置“读取计数值后复位”并以一秒的计数间隔配置新的计数器通道,新的计数器通道被配置为每秒读取一次频率。
电路
白色 LED 和 470ω限流电阻器的配置如第章第一部分图 1-3 所示,红色二极管的串联对的接点连接到 CB25 端子板上的 D8。
软件
必须产生一个脉冲序列,以形成具有可测频率的重复信号。清单 8-7 中的脚本是一个 PWM 或可变脉冲宽度发生器,可与图 8-7 中所示的屏幕按钮和指令文本结合使用,以启动和停止方波脉冲序列。
图 8-7
DAQFactory 脉冲序列发生器面板
启动/停止按钮用于激活本章代码列表中列出的 DAQFactory 序列 PWM _ Script8-7。
脚本和动作
虽然使用“time - goto”语句并不被认为是良好的编程实践,但它确实简化了方波信号的创建。创建计数练习的两个面板后,将“Delay (0.5)”语句中的值更改为 0.25 和 0.1。在产生方波和显示频率的同时,快速前后移动鼠标光标,观察 LED 和显示的频率值。
观察
在两个计数器练习中包含蓝色和白色发光二极管,作为跟踪系统操作的视觉辅助。(参见“讨论”)
讨论
时间测定
数字电子电路由晶体控制振荡器“时钟”激活晶体振荡器产生非常稳定的固定频率方波脉冲序列,提供纳秒时间分辨率(10 -9 s)。方波由从 0 到+5 伏或从逻辑 0 到逻辑 1 的一系列二进制转换组成。时间可以分为由时钟方波波前的间隔确定的相对时间和来自固定事件的绝对时间。DAQFactory 程序的绝对时间由 1970 年 1 月 1 日的秒数决定。用户手册中详细讨论了图 8-2 的时间变量及其语法。
手动和自动事件计数
按钮启动的手动事件在通常与 DAQFactory 软件计时不冲突的时间范围内创建。但是,可以证明,试图用脚本创建一个波形来生成一个以足够快的速度变化的信号,可能会与操作系统软件定时相冲突。
作为本练习的一部分,使用了一个脚本来生成递增 LabJack 计数器所需的电压波形。蓝色 LED 作为系统验证的可视指示器已包含在练习中。然而,计数器硬件将事件记录为双转换操作,其中高信号下降到低值,然后低信号上升到高电压值。双跃迁“事件”受一个脚本影响,该脚本将引脚电压电平保持在 5 V,进而为电路中的蓝色 LED 供电。为了关闭 LED,我们在由 Reset Counter 按钮激活的脚本中包含了一行代码,用于将引脚 9 设置回 0 V,而不会被记录为“事件”的一半。
通过改变 PWM_Script 中的延迟值,可以改变信号保持在 0 或标称 5 V 的时间宽度。LabJack 计数器仅将+5 至 0 至+5 伏的转换记录为单个事件,以实现计数器递增的目的,因此在 0 伏时停留时间的宽度是在频率测定中被计为单个事件或周期的参数。
任何图形显示都必须由二维元素阵列组成,这些元素可以被单独照亮以形成图像。GUI 的更新消耗了大量的计算资源,因为阵列的各个元素被不断地扫描以实现任何所需的改变。如果用于驱动图 8-7 的脉冲发生器面板的程序(如脉冲发生器)被调用,延迟时间不到一秒,软件冲突可能会出现。
当使用脉冲产生程序为白色 LED 供电时,延迟时间变得非常短,只有几分之一秒,LED 脉冲频率和光标移动都变得不稳定。观察到的硬件和软件冲突证明了在不到一秒的时间跨度内使用软件脚本的局限性。
硬件时间和计时
我们对时间的硬件控制的讨论集中在 555 集成电路定时器芯片上,该芯片已经制造、改进和使用了 40 多年。该芯片通过使其输出以受控的时间间隔从高电压电平变为低电压电平来工作。时序间隔可以很容易地在多个数量级上变化,以产生长延迟(单稳态工作模式)或产生高速脉冲序列(非稳态工作模式)。由电阻和电容组成的简单外部元件可以用来产生所需的时间间隔。555 芯片提供双极晶体管和 CMOS 两种格式,功耗、功率输出和高频操作各不相同。
555 芯片以三个 5kω电阻的串联串命名,这些电阻连接到电源电压和地,以建立电路逻辑使用的 1/3 和 2/3 电源电压参考电平。该电路包含两个比较运算放大器,其输出信号馈入数字置位-复位触发器。模拟比较器使用 1/3 和 2/3 电压参考点来改变它们的输出状态,这使得触发器基于比较器输入来改变状态。数字触发器输出控制输出驱动器电路。
图 8-8 和 8-9 显示了定时器电路的一些操作和配置。
图 8-9
一个 555 定时器 IC 框图
图 8-8
555 IC 定时器工作模式
当如图 8-8 右下角的非稳态模式示意图所示配置的 555 IC 通电时,随着电流流经 R1 和 R2,电容器开始充电。随着引脚 2 和 6 上的电压上升,然后达到电源电压的 2/3(由内部分压器确定),输出变为低电平。随着输出变低,NPN 晶体管导通,555 的放电引脚有效地接地,通过 R2 对 C1 放电。当电容上的电压降至电源电压的 1/3 时,晶体管关断,电容开始通过串联对充电,循环重复。因此,C 上的电压在电源的 1/3 和 2/3 之间循环,周期为 T = 0.693(R1 + 2R2)C 或频率为 f = 1.4/(R1 + 2R2)C。输出信号的时间周期可分为两部分,包括电压高的时间(见图 8-8 ,右下角)和信号低的时间。高电平时间通常被称为“标记时间”,低电平时间被称为“空时”,占空比被定义为高电平时间或标记时间与信号的总和或时间周期的比值,以百分比值表示。
555 定时器产生脉冲序列的能力在实验科学中具有非常重要的应用,该脉冲序列的电子特性由外部电阻和电容值决定。以下练习直观地演示了方波或时钟信号的概念,以及脉冲宽度随外部传感器物理变化的变化。
许多网站、参考资料和教科书都详细讨论了定时器芯片的特性以及电路设计参数值表。
在下面的练习中,将演示方波输出信号和占空比的概念,以及使用定时器芯片改变脉冲宽度的基础。利用主要 IC 供应商在线提供的数据手册中的设计程序,可以组装一个输出频率为 6–7 赫兹的电路,为不同颜色的 led 供电,以直观显示电路工作情况。
实验的
图 8-10
受控高低 555 定时器 ic 输出变化示意图
-
在非稳态模式下配置的 555 定时器芯片(见图 8-8 ,右下角)。
-
100kω可变电阻用作 R2,10kω电阻用作 R1。
-
一个 1 uF 电容用作定时电容或 C1。可以使用电解电容,因为该元件的较高漏电流率对电路性能并不重要。
-
带有 470ω限流电阻的两个不同颜色的 led 连接在芯片的输出引脚和电源轨之间,以便交替闪烁指示高低输出状态。
-
图 8-10 的原理图是用+5 V 电源绘制的,但是电路可以用 3 到 18v 之间的任何电源供电(调整电压> +5 的 CLR 值)。
图解的
观察
使用图 8-10 所示的电路,当 100kω电位计接近其最大极限时,红黄对在 13 秒内交替闪烁十次,当接近零值时,电路连续闪烁。
讨论
在“非稳态”配置中,定时器芯片能够改变输出开启或关闭的时间。开关时间或“占空比”之间的测量比值取决于 R2 的电阻,在这种情况下,该电阻是可变电阻器上的轴的机械位置。电位计轴的旋转机械运动因此可以转换成变化的电子方波信号。任何能够将物理现象转换成可变电阻的传感器也可以用来产生方波信号,其开/关时间的比率与被监测的物理现象产生的电阻成比例。热敏电阻是热敏电阻。负温度系数(NTC)热敏电阻对环境温度的升高表现出较低的比例电阻。将 NTC 热敏电阻插入 555 定时器 IC 的定时电路将导致输出方波的频率与热敏电阻珠的热环境成比例地变化,从而形成数字温度计。通过使用输出信号来打开和关闭功率晶体管,可以控制更大的电流。对于固定频率,可变“占空比”格式信号作为“脉宽调制”技术。PWM 电流控制可用于改变电机速度或控制施加到加热器的电流。
在非稳态模式下,555 产生的方波频率取决于 R1、R2 和 c 的值,频率由下式给出:
f = 1/ln(2)* c *(R1+R2)(ln(2)= 0.6931)
图 8-11 显示了 555 的非稳态循环。
图 8-11
555 不稳定循环
输出频率可以由 RC 网络的三个值控制。图 8-12 中显示了占空比或高电平时间与低电平时间之比的百分比值。
图 8-12
555 定时器输出的占空比变化
输出的高电平时间由下式给出
高时间= ln(2) * (R1 + R2) * C
低时间由下式给出
低时间= ln(2) R2 * C (ln(2) = 0.6931) (R 单位为ω,C 单位为 uF)
当电容器被流经 R1 和 R2 的电流充电时,输出信号为高。当它放电时,它仅通过 R2 放电,因此 R2 值引入占空比的变化是有限的。如果电阻对被电位计取代,电位计的游标端连接到定时器的引脚 7,则 R1 + R2 的总电阻是恒定的,而占空比可以通过改变游标的位置来改变。为避免 R2 设为 0 时出现不必要的问题,应在电容和可变电阻之间串联一个小电阻,以避免在低电位计电阻值时出现不可预测的结果。如果使用固定电阻器来建立小于 50%的所需占空比,则指向电容器的二极管将允许电容器在周期的高时间部分仅通过 R1 电阻器充电。
微控制器时钟、计时和事件计数
几乎所有与主机和外围设备(如传感器或过程控制)通信的微控制器都配备了板载晶体控制振荡器,用作系统时钟。通常,基于硬件的时钟信号可以通过微控制器软件访问,并用于计时和事件计数。
Arduino 微控制器使用晶体控制的 16 MHz 振荡器作为系统时钟。当操作系统通电时,它开始计算毫秒数(1/1000 或 10 -3 s)作为函数 millis()的值,以及微秒数(1/1000000 或 10 -6 s)作为函数 micros()的值。(由于调用 micros()时的二进制计数和硬件限制,微秒时间分辨率被限制在最接近的 4 μs 或 4 x 10 -6 s。)
这两个函数存储为无符号长整数,在滚动到 0 之前,其最大值为 4,294,967,295。最大值限制为毫秒()计数提供了大约 50 天的时间跨度,为毫秒()计数提供了 70 分钟的时间跨度。Millis 计数精确到最接近的一位数,但 micros 值表示到最接近的四位数(2 2 )。以毫秒()为单位的计时误差为 0.18 秒/小时、4.32 秒/天和 129.6 秒/月。
DAQFactory 提供了一种替代方法来演示微控制器(如 Arduino)的计时和事件计数概念。有许多公开的程序使用机械开关和相应的大量库以及用于处理机械“开关触点弹跳”的其他方法,为微控制器创建倒计时、秒表和其他计时应用。 3
DAQFactory 屏幕按钮和串行端口消除了对机械开关和机械开关触点反弹的需要。
实验的
为了演示廉价的串行端口连接微控制器的基本计时功能,需要两个程序,第一个程序在主机屏幕上显示 SCADA 软件中的计时器控制面板,第二个程序在微控制器上实现选定的计时功能。Arduino 代码见清单 8-8 ,DAQFactory 快速序列见清单 8-9 。
图 8-13 显示了 DAQFactory SCADA 软件中设置的简单秒表计时器的简单基本配置。
图 8-13
简单的 DAQFactory 秒表控制面板
三个按钮中的每一个都被配置为激活快速序列码,该快速序列码向串行端口写入“b”、“s”或“r”以开始计时会话、停止计时会话并将 millis()计数传输回 SCADA 软件,或者将计时器重置为零并开始另一个计时会话。
图 8-13 中看到的非活动(红色 X)变量值显示在使用“s”命令停止计时会话时接收来自 Arduino 的总 millis()计数。图 8-14 显示了在组合计时系统开发期间,为验证而运行的简单启动和停止计时会话期间的 DAQFactory 串行监视器活动。
图 8-14
简单 Arduino 定时会话的 DAQFactory 串行端口监视器记录
回想一下,数据发送到串行端口后,DAQFactory 串行端口代码需要回车(CR,ASCII 代码 13)和换行符(LF,ASCII 代码 10)。通过 DAQFactory 串行端口传递的数据可以读入通道或全局变量。在主要代码开发过程中,在 DAQFactory 软件中配置了一个名为 millisVlu 的通道,然后将变量值屏幕组件设置为将从 Arduino 接收的 millis()值除以 1000,以获得计时器记录的整秒数和毫秒数。后来的代码版本使用了全局变量“elapsed”和快速序列来实现简单的计时功能(参见“观察”中的图 8-15 以及清单 8-8 和 8-9 )。
图 8-15
用于秒表程序开发的 Arduino 串行端口输出
DAQFactory 串行端口使用第六章中清单 6-10 中的“接收”代码,从串行端口将数值读入通道或用作全局变量的值。
观察
在图 8-15 中,Arduino 串行端口已被编程以帮助验证微控制器的命令解释,典型的时序结果显示如图 8-16 所示。
图 8-16
典型的简单毫秒分辨率定时会话
简单秒表计时器的开发首先是通过使用 Arduino 自己的串行端口手动发送“b”、“s”和“r”命令来产生如图 8-15 所示的输出,使 Arduino 作为独立计时器运行。有了一个正常运行的 Arduino 程序,代码被精简为清单 8-8 中的代码,其中唯一的输出是 Arduino 代码行“Serial.println(elapsed)”,它将 elapsed time 变量的内容发送到带有必需的 CR 和 LF 的串行端口。
检查清单 8-8 和 8-9 会发现两个程序中的复位功能不同。更简单的方法是将复位代码分为串行端口两侧的两个动作。Arduino 代码重置开始计时循环和停止计时的状态标志,并打印到串行端口动作,而 DAQFactory 快速序列码实际上将运行时间变量和计时秒显示设置回零。
一旦进行了初始计时测量,应使用复位按钮来复位 Arduino 代码中的单个动作标志。
讨论
图 8-15 展示了一种开发 Arduino 代码的简单技术,该代码将对 SCADA 软件在本练习的最后一次迭代中写入串行端口的单字母命令做出响应。Arduino 代码的完整开发是通过使用 IDE 的串行监视器“发送”功能,并将图 8-15 中的响应写入开发代码,以在最终组合 SCADA 和微控制器操作之前验证代码的操作。通过重写或注释掉不需要的行,可以将功能秒表代码减少到与 DAQFactory 面板结合使用。
还可以通过将经过的毫秒时间值(可以延伸到接近 50 天)转换成分钟、小时和天来配置更复杂的定时器功能,这些时间值会受到前面提到的时间误差的影响。
在可能的情况下,可以使用屏幕激活按钮来避免由机械开关触点反弹引起的问题。
使用 Python 和 Raspberry Pi 计数事件和计时
Python 语言解释器的时间测量是从运行程序的主机上获得的。Raspberry Pi 的基本概念是面向互联网接入的。互联网连接可用于传输操作系统、应用软件和更新。互联网连接通过互联网时间服务器提供精确的计时。如果 RPi 用于时间相关的实验或测量应用,而这些应用又无法访问互联网,则需要安装一个实时时钟(RTC ),以提供精确的计时基础。 4
在图 8-17 中,一个非常简单的控制台请求显示了自 1970 年 1 月 1 日开始计时以来发生的“滴答”数。
图 8-17
对滴答计数的控制台请求
在 Unix/Linux 操作系统中,滴答数可以转换为秒、分、小时和天,以提供任何所需的计时操作。图 8-18 是刻度到当前时间显示的控制台转换。
图 8-18
来自滴答计数的当前时间的控制台请求
如图 8-19 所示,可以在交互式控制台上使用 asctime()函数获得更熟悉的时间格式。
图 8-19
熟悉的当前时间格式
已经发布了许多简单的按钮定时器 GUI 和定时器模块,用于用 Python 编写定时器应用程序,在图 8-20 中描述了一个使用 tkinter Python 模块的三按钮 GUI。代码在清单 8-10 中列出。该规范已对 2002 年出版的原版进行了修改。??
图 8-20
三按钮秒表计时器
安排事件
除了前面列出的时间显示函数,Python 还有几个库,如 sched 和 schedule,它们使用时间模块作为调度事件的基础。实质上,sched 和 schedule 模块为实验者提供了一个可编程的起点,从该起点可以在启动单个事件或编程代码序列之前指定延迟。
标题为 Scheduled_PgmCntrl_LED.py 的清单 8-11 将逻辑高电平和低电平编程应用于 GPIO 引脚 20 和 21(板引脚 38 和 40),通过限流电阻打开和关闭连接到引脚的两个 led 作为“事件”
对代码的检查显示了一个 scheduler 对象实例的典型创建,其代码行为 scheduler = sched . scheduler(time . time,time.sleep)。下面两行定义了将来要运行的两个事件:
-
scheduler.enter(2,1,actvt_GrnLed,(“绿色 Led 首先激活”,))
-
scheduler.enter(5,1,actvt_RedLed,(“红色 Led 第二个激活”,))
sched 模块的文档规定了 enter()函数的四个参数,包括从处理启动函数 start()开始的时间延迟(以秒为单位)的数值、指定事件优先级的数值、要调用的事件函数的名称,以及需要时要传递给被调用函数的数据。列表 8-11 是重叠事件的一个例子,其中 led 点亮的时间比事件的预期开始时间长。shed 模块执行所有被调用的函数,没有一个函数丢失,但是事件的时间会因为进程重叠的量而在时间上偏移得更远。
图 8-21 是调度程序的典型输出。要点亮的红色和绿色发光二极管的接线如图 8-22 中电路 A 所示。
图 8-22
用于 GPIO 编程演示的吸收或提供电信号的电路
图 8-21
重叠事件的调度程序输出
安排事件可能是一个复杂的问题,在使用这些模块时,应该检查 Python 参考文档以获得更多细节。 6
检测和计数事件
RPi 上外部事件的检测和计数既需要能够确定 GPIO 阵列的各个引脚上是否存在电压,也需要能够检测引脚电压的转换。GPIO 引脚上的电压转换可以通过两种技术来监控,称为“轮询”和使用“中断”。在任意时间点寻找电压变化称为“轮询”引脚。轮询的缺点在于,要监控的事件可能发生在进行引脚状态观察的时间帧之前或之后。轮询通常通过软件循环编码来实现,这会消耗大量的处理器时间,同时阻止 CPU 执行其他任务处理。
确定电压转换的第二种方法使用中断或“边沿检测”,其中记录从高到低(下降沿)或从低到高(上升沿)的变化。
简而言之,可以说大多数现代操作系统是分时操作,管理多个程序,这些程序对用户来说似乎是同时运行的。操作系统运行的每个程序在 Unix 中称为进程(在 Windows 中称为任务),并且只运行很短的一段时间。周期性地,当前运行的程序用完其分配的“时间片”,这由从硬件或软件定时器发送到中央处理单元的中断信号的产生来确定。中断使 CPU 暂停或“中断”手头的正常任务,以处理或服务于高优先级事件。中断使 CPU 保存其当前计算,切换到处理中断服务程序(ISR)(或事件处理程序),并在完成 ISR 后恢复正常操作。输入输出操作可以编码到 ISR 中,这样 CPU 就可以在监视 I/O 操作和正常计算功能之间分配处理资源。CPU 时间的第一个程序片的处理、中断信号的产生、ISR 的处理以及切换到下一个要处理的程序都发生在如此短的时间内,以至于对用户来说,几个程序似乎在同时运行。
线程是程序代码中较小的部分,可以交叉使用,以产生两个代码部分同时运行的预期效果。(实际上,只有多核处理硬件才能同时运行多个线程。)线程化可以用来避免 GPIO 操作中轮询的缺点。中断和 ISR 可用于检查 GPIO 引脚的状态,如果它无效,则继续正常程序处理。轮询包括对事件的连续检查,而中断则定期进行。轮询消耗所有的资源,而中断只消耗一部分。轮询使用专注于事件检测的单线程,但是 Python 和 RPi。GPIO 库允许创建两个或多个线程,其中事件检测代码可以独立运行。次级线程中事件的检测激活代码,该代码回调主线程以启动中断服务例程。有许多非常简单、易于实现的多按钮、线程回调演示程序已经发布,以支持描述 GPIO 阵列输入和输出使用的库文档。在线提供了一个简单线程库的文档。 7
在前面的练习中,可以使用三个库来处理 RPi。GPIO 引脚已被引入,这些不同的模块将根据需要用于生成简单的定时器程序或监控引脚状态,并在手稿的剩余部分记录引脚状态变化之间的时间。
RPi。GPIO 和 gpiozero 库非常易于使用,并得到大量代码文档的支持,这些代码是为大量可以通过 GPIO 引脚与 RPi 接口的常见设备开发的。
然而,前两个库不能精确、短时间地计时。在前面的练习中,在简单的 LED 电源控制应用中可以看到的“抖动”是由基于 Unix 的 Linux 操作系统暂停 GPIO 操作以处理优先级高于 RPi 输入输出代码的内部进程引起的。
作为 pigpio 导入的第三个库已经开发出来,用于能够提供微秒计时精度的 RPi。pigpio 模块中的计时精度是通过使用 C 代码编写库和使用运行在 RPi 上的 Python-Linux/Unix 接口程序访问 gpio 引脚来实现的。在系统后台运行的 Unix 实用程序或服务程序通常被称为“守护程序”
如上所述,RPi 上 GPIO 引脚的物理计算可以被视为计算机与外界的接口。RPi 能够通过在几乎任何时间点测量其电压来检测阵列中每个引脚的状态。当 Python 解释器使用 RPi 时,系统和软件开销限制了 RPi 对任何 GPIO 引脚上电压变化的响应时间。GPIO 或 gpiozero 库。如上所述,为了改善 RPi 对其 GPIO 引脚阵列的短时间响应,用执行速度非常快的 C 语言编写的库已经通过 Linux/Unix 守护程序或称为“pigpiod”的实用程序与 Python 解释器连接利用 C 库模块,可以在 GPIO 引脚操作中可靠地访问微秒级时标。
pigpio 库可供具有各种编程能力的研究者使用,如果需要,可以查阅大量的文档。
时序和低频事件计数可以通过 Python 程序来实现,Python 程序使用适当的 GPIO 引脚管理库来完成手头的任务。使用 RPi 可以实现简单、低级、易于编码和实现的接口。GPIO 库,而更复杂的传感器最好使用 gpiozero 库。使用 pigpio 库及其快速准确的时间管理功能和在 RPi 操作系统后台运行的接口实用程序守护进程需要中级到高级的编程技能。
实验的
基于 GUI 的软件秒表计时器的实现不需要与 GPIO 阵列交互。
调度事件既可用于普通 Python 代码应用程序编程,也可用于利用 GPIO 阵列的物理计算过程,如本练习的以下部分所述。
由于传感器、致动器、电机和开关的所有输入和输出动作必须以从 0 到 3.3 或 5 伏的转换形式进行,图 8-22 中所示的两个电路可分别用于以电路 A 或 B 的形式提供或接收电激活信号。
在图 8-22 的电路 B 中使用 LED,虽然不是激活 RPi GPIO 代码所必需的,但确实为研究者提供了额外的诊断能力,以防代码部分对点击按钮没有反应。一个按钮点击应该会导致 LED 灯和代码等待按钮点击按预期被激活。当点击按钮时,如果 LED 没有点亮,则可以确定代码没有响应的根本原因(参见“观察”和“讨论”)。
列表 8-12 使用按钮式机械开关,如图 8-22 电路 B 所示配置,提供“上升沿事件”来触发时间测量程序的动作。
该程序使用两个上升沿检测功能,在 RPi 等待上升沿出现时阻止所有计算操作。如果按钮开关的激活是程序关注的唯一操作,如定时器示例中的情况,那么阻塞功能实现起来既简单又足以解决手头的问题。图 8-24 是上升沿定时器程序的典型输出,而图 8-25 在编程代码开发期间捕捉到一个开关触点“弹跳”。
如前所述,监控机械开关(如按钮、触发器或磁激励簧片)的高速数字定时器和计数器必须适应开关提供连续闭合触点之前发生的触点弹跳。RPI。GPIO 库都有估计开关触点弹跳的规定,这种弹跳可能在即将进行的实验中遇到,并且将接受实验者忽略第二个事件的毫秒时间尺度。
当要监控高速事件时,例如在光束阻挡配置中遇到的事件,通常可以使用电容来抑制杂散噪声或电磁干扰。
轮询和中断事件检测虽然易于用按钮设备实现教育目的,但对于检测和计数更高速的事件来说价值有限。在随后的物理计算练习和测量中会遇到的电机旋转速度、高速物体计数和固定距离上的精确计时,都可以用中断光束光学技术来实现。在第十章中,红外中断光束探测器用于计算电机转数,以确定电机转速。
图 8-23 描绘了一个不可见红外(IR,波长为 940 nm)阻断光束电路,它可以配置在原型试验板上,并连接到 RPi 40 引脚 GPIO 阵列。(电路描述中提供了 BCM GPIO 值和 BN 或板号值。)
图 8-23
一种红外断路电路
断梁系统没有金属触点,不会“反弹”,但可能会出现电尖峰或噪声,从而产生虚假信号响应。因此,在为即将进行的实验汇编和编写代码时,实现第二信号抑制的反弹时间或使用电容吸收杂散信号是研究者的判断或实验问题。
以下中断光束程序设计用于连续操作,并使用 while 循环监控光束完整性。回路软件具有用于干净退出方案的内置代码,该方案使用 Ctrl+C 击键组合来终止主程序光束扫描回路,并执行适当的电路关闭程序,该程序通常关闭 IR 光束,移除不再使用的代码,并重置端口配置。
根据图 8-22 A,监控红外光束的三个 RPi 程序的清单 8-13 、 8-14 和 8-15 在主 while 循环中有额外的代码激活两个二极管,一个红色和一个绿色,连接到电路板阵列(BCM GPIO 20 和 21)上的引脚 38 和 40。红外光束不可见,添加的代码在光束中断时打开红色二极管,在光束中断时点亮绿色二极管这两个二极管用作不可见红外光束状态的远程指示器。
列表 8-13 和 8-14 是红外二极管和光电晶体管探测器可用于演示更高速事件监控的两种方式,并展示了该技术的实际应用。
观察
上升沿定时器程序的输出被大量注释,以描述在经过时间测量期间发生的事件,如图 8-24 所示。
图 8-24
典型的上升沿按钮定时器输出
有时,点击按钮不会产生预期的结果,代码等待电气转换。如图 8-25 所示,当试图激活定时器程序时,该程序使用按钮的两次连续点击来测量运行时间,代码确认并执行“开始计时会话”初始事件,然后几乎立即正确地结束计时会话,记录分数秒的运行时间。(参见“讨论”)
图 8-25
意外的短经过时间确定
在图 8-25 中,测得 0.049 或 49 ms(毫秒)的运行时间,这是机械开关“弹跳”的特征。
有时,初次点击按钮不会激活等待转换的代码。这种现象在本质上是随机的,并且有时偶然被追踪到开关触点没有闭合或没有闭合足够的表面积接触来提供点亮二极管或激活转换识别码所需的能量。(参见“讨论”)
图 8-26 是一个简单程序的输出,该程序使用简单的 while 循环轮询方法来监控红外光束的状态。从作者在光束中快速手动振动一支笔所产生的输出可以看出,当光束未中断时,光电晶体管或二极管充当接地短路,与 IR 敏感元件相连的 GPIO 引脚被拉低至几乎 0 V,光束阻断消除了接地短路,GPIO 引脚上升至 3.3 V,将输入引脚驱动至高电平。
图 8-26
轮询程序输出
while 循环以系统软硬件组合允许的最快速度不断循环,并输出引脚的高/低值。
通过使用“Ctrl+C”击键序列,仅轮询输入引脚的连续循环被正确终止。通过将 while 循环封装在一个 try-except 键盘中断组合中来识别该序列,该组合允许循环终止并将控制传递给程序的其余部分。循环终止后,程序代码关闭可能开启的二极管,复位可能已被修改的 GPIO 配置,并发出程序终止信号。笔在光束中的快速振动不会改变交互式终端中打印数据的速率。可以看到轮询遗漏了波束中断事件。
图 8-27 显示了列表 8-13 的上升沿检测中断代码的输出。
图 8-27
中断事件检测程序输出
红外光束中笔轴的快速振动导致数据输出速率相应快速增加。如前面的数据输出所示,输出失真似乎是由于交互式屏幕输出无法响应光束阻断事件的快速中断检测。
通过使用 RPi 的 add_event_detect 函数,在程序中创建了一个事件检测过程中断。GPIO 库。添加的函数接受几个指定 GPIO 管脚号的参数;动作的事件条件,上升/下降沿或两者;以及当接收到中断信号时转移到或“回调到”的函数的名称。
在所监控的 GPIO 引脚上遇到选定的电子转换之前,程序的主循环会打印出预期的“输入= 0 光电二极管开启”,并且程序控制不会转移到专为遇到转换时使用而创建的功能。由于 GPIO 引脚上没有活动,中断实际上在后台运行。然而,电子活动触发分支发生,程序控制转移到“跳转、执行和返回”功能,该功能执行研究者要求的动作。在图 8-27 中,输入引脚状态被打印出来,直到遇到一个上升沿,导致程序分支到专门创建的函数,该函数检查引脚状态,将其打印出来,并返回到原来的程序循环程序。RPI 的添加事件检测功能。GPIO 库执行速度如此之快,以至于控制输出显示的较慢代码无法跟上红外光束中笔的快速移动,从而导致图 8-27 所示的输出损坏。线程的使用使得高速中断技术成为可能。
清单 8-15 使用中断技术来驱动事件计数器。计数器在独立于主程序的线程中运行,并且仅当被监控的引脚上发生指定的电子转换时才被访问。计数器值存储在 Python 全局变量中,以便对主程序循环的输出部分可见,在计数器递增函数工作的线程之外。主循环根据程序代码的执行定期打印出计数器值,但是计数器值通过中断激活的事件检测增加,该事件检测分支到计数器递增的线程。如图 8-28 所示,通过在红外光束中振动一支笔,计数器记录光束中断的次数,并在主程序正常、几乎恒定的数据输出间隔期间将它们加到总计数中。
图 8-28
中断驱动的事件计数器输出
讨论
通过 GPIO 引脚和三个接口库之一将传感器与 RPi 直接接口,是收集数据或监控传感器的最经济、最简单的选择之一。这三个库都有独特的特性和不同程度的复杂性。GPIO 最适合简单的数字系统,gpiozero 适合集成电路、传感器或传感设备和机器人电机控制,而 pigpio 更复杂、速度非常快、时序准确,同时能够与各种机电系统接口。
所有三个 I/O 库都能够适应机械开关“反弹”,这可能是在手边的系统中根据经验估计的最佳结果。忽略第二或第三信号的时间窗口的大小由研究者希望测量的最小信号的时间宽度决定。
选择使用轮询循环监控事件的程序还是中断驱动的程序,只需考虑数据发送到 GPIO 引脚的速率。每秒一次或两次轮询对于监控开门传感器来说已经足够了,而中断驱动监控器应该用于高速旋转测量。
Python 基于时间的测量都是基于运行程序的系统的节拍数。系统的时基是从互联网时间服务器提供的互联网时间中获得的。如前所述,对于“离线”进行的时间测量,如现场测量,必须在 RPi 上安装实时时钟(RTC)。
在脚注 6 的 URL 中可以找到调度器模块使用的完整描述,在使用这些 Python 函数时应该参考这些描述。对于一周中的所有日子,使用具有分钟、小时、日和一天中的小时的模块需要在应用程序中小心谨慎,以根据需要运行,并且应该为真实世界的应用程序仔细设置。
通过使用适当缩放的感兴趣的信号,可以使用光学中断光束电路的快速响应速率来测量信号的频率,以给 IR 二极管源供电。然后,可以对计数器软件进行编码,以使用中断驱动的计数器来测量程序主输出循环的定时和定义的迭代次数所累积的事件数。
代码列表
// send begin signal b
device.ardyBluBrd.Write('b')
// send stop signal s
global Elapsed
device.ardyBluBrd.Write('s')
private string datain
datain = device.ardyBluBrd.ReadUntil(13)
Elapsed = strToDouble(datain)
// send re-set signal r
device.ardyBluBrd.Write('r')
Elapsed = 0
Listing 8-9DAQFactory Quick Sequences for b, s, and r
/* A stopwatch program using a DAQFactory panel and the serial port to avoid the debouncing problems associated with
mechanical switches. The program uses the letters b, s, and r
to branch in an Arduion case statement using b for begin,
s for stop and r for re-set. Always ensure that data sent
from the Arduino to the DAQFactory software code is
Serial.println(data);
*/
char incmngByte; // a variableto hold the incoming byte from the serial port
unsigned long start, finished, elapsed; // timing variables
bool tsipFlg = LOW; // timing session in progress flag
bool wtspFlg = LOW; // write to serial port once only flag
bool rstFlg = LOW; // re-set b and s flags
//
void setup() {
Serial.begin(9600); // start the serial port
}
//
void loop() {
if(Serial.available() > 0) { // check port for incoming character
incmngByte = Serial.read(); // set character into variable
}
switch(incmngByte) { // the case statement for decisions
case'b': // begin a timing session
if (tsipFlg == LOW) { // check the status flag
start = millis(); // set the start time
//Serial.print(start); // diagnostic
tsipFlg = HIGH; // set the status flag to timing in progress
}
rstFlg = LOW;
break;
case's': // stop the timer
if (wtspFlg == LOW) { // check the status flag
finished = millis(); // set the finish time
//Serial.println(finished); // diagnostic
elapsed = finished - start; // calculate the elapsed time
Serial.println(elapsed); // write the elapsed time to the serial port
wtspFlg = HIGH; // set the status flag to write only once
}
rstFlg = LOW;
break;
case'r': // re-set b and s functions
if (rstFlg == LOW) { // check the status flag
tsipFlg = LOW;
wtspFlg = LOW;
rstFlg = HIGH;
}
break;
}
}
Listing 8-8Arduino Stopwatch Timer Code
// PWM_Script (Pulse Width Manipulation) Script for pulse
// width variation - Oct. 21/09 Rvn. Jan. 2/11, Aug. 3/17
// A "time - goto" loop is used with delay statements to set // D8 to 1 then 0 thus raising and lowering the channel
// DigOut_8_PWM output between 0 and 5 volts in a continuous // manner. The continuously varying voltage creates a square
// wave train. The 0.002 and 0.098 can be considered as the // time on time off duty cycle. With the lower duty cycle the
// pulsing of a powered light source is quite evident. // Various duty cycles must be entered manually into the simple
// program which is started and stopped with the sequence
// pop-up menu displayed by right clocking on the sequence name.
time 0
DigOut_8_PWM = 1
Delay (0.002)
DigOut_8_PWM = 0
Delay (0.098)
goto 0
Listing 8-7DAQFactory Sequence PWM Script
// ResetCounter - Fall/09 Revn Jan 1/11 The script manually
// resets the displayed number of events counted, by the LabJack // counter after the defaut "Reset after polling" has been
// turned off. The counter is activated after it detects a
// falling edge waveform followed by a rising edge waveform. // The "event" counted thus consists of a 5 to 0 - 0 to 5 volt
// transition which leaves the Pin 9 at 5 volts. For the manually // activated counter exercise the blue LED thus remains ON as
// long as the manually activated counting session is in
// progress, re-setting the counter then turns the LED off.
//
EventsCounted[0] = 0
DigOut_9_EventCntr = 0
// By using the default setting of "Reset" after polling (reading) // the number of 5-0-5 volt transitions in a given period of
// time, the frequency can be determined.
RawCounts[0] = 0
DigOut_8_PWM = 0
Listing 8-6DAQFactory Sequence Reset Counter
// A_Counter_Event - Jan. 1/11 - The LabJack counter is activated // by a 5 to 0 volt falling edge followed by a 0 to 5 volt
// rising edge. The following script applies the 5 - 0 - 5 volt // profile to the DigOut_9_EventCntr channel that activates
// pin D9 onthe CB-25 board. This script is activated by
// clicking on the screen button labelled "Initiate Event".
//
// Set the pin voltage to 5 volts
DigOut_9_EventCntr = 5
// Create the falling edge by setting the pin voltage to 0
DigOut_9_EventCntr = 0
// Create the rising edge by setting the pin voltage back to 5
DigOut_9_EventCntr = 5
// For ease of configuration the voltage is left on for 1/2 a second so as the lit LED can be
// used to validate a functioning system.
//Delay(0.5)
//DigOut_9_EventCntr = 0
Listing 8-5DAQFactory Sequence Counting Events
// CumulativeTimeOfIntervals Nov. 27, 2010 is a summation of // the previous collected intervals Each interval timed is
// measured in clock ticks that are converted into sec, min // and hrs for display. When the current interval is to be
// summed into the accumulation the Cumulative Time button is // used to add the current interval’s total seconds to the
// accumulating sum of total seconds. The previous hrs, min.
// and seconds used for the previous display are discarded and // a new total time is calculated for an up-dated display.
//
global TtlHrs
global TtlMin
global TtlSec
global Hrs
global Minutes
global Sec
global TSixtySecTm
global TSxtyMinTm
global ElapsedTime
global IntrvlMin
//
TtlSec = TtlSec + Sec
TtlHrs = Floor(TtlSec/3600) // just divide total time in seconds by 3600 to get hours
TtlMin = Floor(TtlSec/60) // total minutes is calculated
TSxtyMinTm = (Floor(TtlSec/60))%60
Sec = (TtlSec - ((TtlSec - (TtlSec % 3600)) % 60))
TSixtySecTm = (TtlSec - ((TtlSec - (TtlSec % 3600)) % 60)) % 60
Listing 8-4DAQFactory Sequence Cumulative Time of Intervals
// Reset Stopwatch Display Oct. 6, 2010
// The sequence resets the timer variables
//
InitialTime = 0
CurrentTime = 0
ElapsedTime = 0
Hrs = 0
Minutes = 0
Sec = 0
SxtySecTm = 0
TSixtySecTm = 0
SxtyMinTm = 0
TSxtyMinTm = 0
TtlHrs = 0
TtlMin = 0
TtlSec = 0
Listing 8-3DAQFactory Sequence Reset Stopwatch
The Stopwatch Timer DAQFactory Sequence Code
// Stop Watch Timer Oct.6 - Nov. 17 2010 (Min is a reserved // word!) The timer sequence is started and stopped by a screen
// button that simultaneously sets a timing flag for a while
// loop and starts the sequence StopWatchTimer. The SysTime()
// function is used in a wait(0.05) delayed while loop, that // calculates the total number of clock ticks between the current
// value of SysTime() and the initial interval starting value. // The total elapsed time in seconds is calculated then divided
// into hours, minutes and seconds for display. The main screen // display provides the operator with two modes of timing
// operation that record either a single interval time or the // cumulative total of multiple intervals. The cumulative total
// option must determine the number of seconds that have elapsed // in the current interval and keep track of the sum of the
// accumulated interval times.
//
//
//
global InitialTime // the start of the current interval
global ElapsedTime // the elapsed time of the current interval
global Hrs = 0
global Minutes = 0
global SxtyMinTm
global Sec = 0
global SxtySecTm
global TimingFlg // the main while timer loop condition flag
//
//
InitialTime = SysTime() // Set the initial time value
//
while(TimingFlg) // start the main program loop
ElapsedTime = SysTime() - InitialTime
wait (0.05)
Hrs = Floor(ElapsedTime/3600) // just divide total time in seconds by 3600 to get hours
Minutes = Floor(ElapsedTime/60) // total minutes is calculated
SxtyMinTm = (Floor(ElapsedTime/60))%60
Sec = (ElapsedTime - ((ElapsedTime - (ElapsedTime % 3600)) % 60))
SxtySecTm = (ElapsedTime - ((ElapsedTime - (ElapsedTime % 3600)) % 60)) % 60
Endwhile
Listing 8-2DAQFactory Stopwatch Timer
// Scheduled Time Timer
// Oct. 2 to 15, 2010
// A screen Start/Stop button is used to initiate the // Schdld_Time_Tmr sequence. The sequence accepts a start and // stop time at which to run a "scheduled event" from two,
// labelled, date and time edit boxes.
// The sequence verifies that both times are in the future and // that the start time is before the finish time. Beneath the edit
// entry boxes a panel display shows the time left before event // activation together with the elapsed and remaining times of
// the scheduled event.
//
//
global EvStartTime // the starting time of the scheduled event
global EvEndTime // the ending time of the event
global EvElapsedTime // time the event has been running
global EvRemainingTime // the time remaining in the timed event
global CurrentTime // the current time
global TimeToGo // the variable for the count down timer
global HrsToGo
global MinToGo
global SecToGo
global EvHrsToGo
global EvMinToGo
global EvSecToGo
global EvElpsdHrs
global EvElpsdMin
global EvElpsdSec
//
// verify validity of entered time values
//if (EvStartTime < EvEndTime)
//if (CurrentTime < EvStartTime)
//
// Count down to start of timed event
//
CurrentTime = SysTime()
while (EvStartTime - CurrentTime > 0)
CurrentTime = SysTime()
TimeToGo = EvStartTime - CurrentTime
//Calculate the count down times for display
HrsToGo = floor(TimeToGo/3600)
MinToGo = floor(TimeToGo - (floor(HrsToGo) * 3600))/60
SecToGo = TimeToGo - (floor(HrsToGo * 3600) + (floor(MinToGo) * 60))
delay(0.01)
// zero count down timer display
HrsToGo = 0
MinToGo = 0
SecToGo = 0
endwhile
//
// Start Scheduled Event Timer
//
While (EvEndTime - CurrentTime > 0 )
CurrentTime = SysTime()
// Start actual event
RedLed = 5
//
TimeToGo = EvEndTime - CurrentTime
//Calculate the count down times to the end of the scheduled event for display
EvElapsedTime = CurrentTime - EvStartTime
EvElpsdSec = (EvElapsedTime)%60
EvElpsdMin = (EvElapsedTime/60)%60
EvElpsdSec = (EvElapsedTime/3600)
EvHrsToGo = floor(TimeToGo/3600)
EvMinToGo = floor(TimeToGo - (floor(EvHrsToGo) * 3600))/60
EvSecToGo = TimeToGo - (floor(EvHrsToGo * 3600) + (floor(EvMinToGo) * 60))
delay (0.01)
endwhile
// Stop Timed Event
RedLed = 0
Listing 8-1DAQFactory Sequence Scheduled Time Timer
Raspberry Pi 程序代码
# Break Beam Interrupt Driven Counter: counts & prints number of interruptions in beam
# Input from pin 7 (board) (GPIO 4) system ground at pin 6
# IR photodiode pull-up with 1M ohm pullup btwn 7 & 1 (3.3v)
# IR LED pin 11 supplies IR illumination gnd pin 6
#
import RPi.GPIO as GPIO # get GPIO library
import time
#
GPIO.setmode(GPIO.BOARD) # use RPi board pin numbers
GPIO.setup(11, GPIO.OUT) # set pin 11 (GPIO 17) as output to power IR LED
GPIO.setup(7, GPIO.IN) # set pin 7 (GPIO 4) as input
#
counter = 0 # declare and initialize counter variable
#
# Function "add_event_detect" runs at input change
def counterPlus(channel):
global counter # declared global to share with system & threads
if GPIO.input(channel) > 0.5: # pin 7 = 3.3v. photodiode off
counter += 1 # recognize blocked beam
else:
counter += 0 # 0v, no-op
#
# On input change, run input change function
GPIO.add_event_detect(7, GPIO.RISING, callback=counterPlus)
#
GPIO.output(11, True) # turn on the IR LED
time.sleep(1) # give LED time to turn fully on
try:
while True:
print("Count = ", counter) # output current counter value
time.sleep(1) # time delay before looping
except KeyboardInterrupt:
pass
#
print("Final counter value = ", counter) # output final counter value
GPIO.output(11, False) # turn IR source off
GPIO.cleanup() # reset ports
print("Diodes off and GPIO ports reset")
Listing 8-15An IR Break Beam Interrupt-Driven Counter
# PRi Detecting Input Events with Interrupts
# Program to get input from pin 7 (board) Gnd is pin 6
import RPi.GPIO as GPIO
import time
#
GPIO.setmode(GPIO.BOARD) # get library
GPIO.setup(11, GPIO.OUT) # set pin 11 as output to power IR LED
GPIO.setup(38, GPIO.OUT) # green led beam intact indicator
GPIO.setup(40, GPIO.OUT) # red led beam broken indicator
GPIO.setup(7, GPIO.IN) # set pin 7 as IR Photodiode input
#
# Function that "add event detect" runs at input change
def inputChng(channel):
print("Input pin status changed to ", GPIO.input(7))
#
# On input change, run input change function
GPIO.add_event_detect(7, GPIO.RISING, callback=inputChng)
#
GPIO.output(11, True) # turn IR LED on
time.sleep(1)
try:
while True:
if GPIO.input(7) > 0.5:
print("Input =", GPIO.input(7), "Photodiode OFF") # detects 3.3v power from pin 1
GPIO.output(38, 0) # grn led off as beam has been broken
GPIO.output(40, 1) # red led on to indicate beam is broken
time.sleep(0.5) # wait time before next iteration
else:
print("Input = ",GPIO.input(7), "Photodiode ON") # detects 0v (diode-on acts like short)
GPIO.output(40, 0) # red led off as beam restored
GPIO.output(38, 1) # grn led on as beam intact
time.sleep(0.5) # wait time before next iteration
except KeyboardInterrupt:
pass
#
#
GPIO.output(11, False) # turn OFF the LED
GPIO.remove_event_detect(7) # Turn off event detect interrupt
GPIO.cleanup() # reset ports
print("Led Off, event detect interrupt removed and GPIO cleanup run")
Listing 8-14An IR Break Beam Monitor with Interrupt Activity
# Code for PRi Detecting Input Events by Polling
# Program to get input from pin 7 (board) Gnd is pin 6
import RPi.GPIO as GPIO
import time
#
GPIO.setmode(GPIO.BOARD) # get library
GPIO.setwarnings(False)
GPIO.setup(11, GPIO.OUT) # set pin 11 as output to power IR LED
GPIO.setup(38, GPIO.OUT) # green led beam intact indicator
GPIO.setup(40, GPIO.OUT) # red led beam broken indicator
GPIO.setup(7, GPIO.IN) # set pin 7 as IR Photodiode input
#
# Main program loop
GPIO.output(11, True) # turn LED on
try:
while (1): # continuous loop
if GPIO.input(7):
print("Beam off, photodiode off input pulled hi ") # detects 3.3v power from pin 1
GPIO.output(38, 0) # grn led off as beam has been broken
GPIO.output(40, 1) # red led on to indicate beam is broken
time.sleep(0.5)
else:
print("Beam on, photodiode on, input pulled low ") # detects 0v (diode-on acts like short)
GPIO.output(40, 0) # red led off as beam restored
GPIO.output(38, 1) # grn led on as beam intact
time.sleep(0.5); # wait time before next loop
except KeyboardInterrupt:
pass
#
#
GPIO.output(11, False) # turn OFF the IR LED
GPIO.cleanup() # reset ports
print("Diodes off and ports reset ") # indicate end of pgm
Listing 8-13A Polling IR Break Beam Monitor Program
# A push button activated rising edge transition starts a timer and a second
# stops the elapsed time measurement. GPIO 21 is pin 40 on the pi board and
# is connected to the junction of the series connected PBS and LED CLR circuit
# A bounce time of 100 ms is used to avoid false triggering.
#
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
# set up the pin-channel, board is 40 bcm is 21
GPIO.setup(21, GPIO.IN)
#
GPIO.wait_for_edge(21, GPIO.RISING, bouncetime=100) # a blocking action while waiting
#
# wait for the event, print an alert and start a timer
#
if GPIO.input(21):
print("A rising edge was detected.")
# start a timer to count ticks
ticks_initl = time.time()
print("A timer was started at tick count ", ticks_initl)
GPIO.setup(21, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)# reset the GPIO pin low
#
# wait for the second event to occur and measure the elapsed time
GPIO.wait_for_edge(21, GPIO.RISING, bouncetime=100) # again a blocking action while waiting
#
if GPIO.input(21):
print("A second or stop timing event has been detected.")
ticks_fnl = time.time()
print("A stop timing event has been detected at tick count ", ticks_fnl)
#
# calculate and display the elapsed time.
print("The elapsed time = ", round(ticks_fnl - ticks_initl, 2), "seconds")
Listing 8-12A Raspberry Pi RPi.GPIO Push Button Timer
# Scheduled Program Control of LEDs, green and red LEDs wth CLRs are connected
# to GPIO pins 20 and 21 or pins 38 and 40 of the RPi array. Pgm calls two
# sequential events with defined delays between events to light the leds and
# print out tick time and current times.
#
import RPi.GPIO as GPIO
import sched
import time
#
scheduler = sched.scheduler(time.time, time.sleep) # create an instance of scheduler
#
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(20, GPIO.OUT)
GPIO.setup(21, GPIO.OUT)
#
# Activate green led for a measured length of time, timestamp event, pass in text
# and document actions
def actvt_GrnLed(name):
print(name) # text or data passed in --> Green led activated firt
print("Green LED on")
frstsched_tm = time.asctime(time.localtime(time.time())) # local time code processed
print("First scheduled event run at ", frstsched_tm)
print("Green led on at ", time.time()) # the tick count at grn led on
GPIO.output(20,GPIO.HIGH)
time.sleep(3)
print("Green LED off at ", time.time()) # tick count at grn led off
GPIO.output(20,GPIO.LOW)
print() # format spacing for output
#
# Activate red led for a measured length of time, timestamp event, pass in text
# and document actions
def actvt_RedLed(name):
print(name)
print("Red LED on")
scndsched_tm = time.asctime(time.localtime(time.time()))
print("Second scheduled event run at ", scndsched_tm)
print("Red Led on at ", time.time())
GPIO.output(21,GPIO.HIGH)
time.sleep(5)
print("Red LED off at ", time.time())
GPIO.output(21,GPIO.LOW)
print() # format output spacing
fnsh_tm = time.asctime(time.localtime(time.time()))
print("Program local finish time = ", fnsh_tm)
print("Finish time = ", time.time())
#
print("Start time in ticks = ", time.time())
pgm_strt_tm = time.asctime(time.localtime(time.time()))
print("Program local time start = ", pgm_strt_tm)
print()
#
scheduler.enter(2, 1, actvt_GrnLed, ("Green led activated first",))
scheduler.enter(5, 1, actvt_RedLed, ("Red led is activated second",))
#
scheduler.run() # start the program
Listing 8-11A Python Scheduled Event Program
from tkinter import *
import time
class StopWatch(Frame):
""" Implements a stop watch frame widget. """
def __init__(self, parent=None, **kw):
Frame.__init__(self, parent, kw)
self._start = 0.0
self._elapsedtime = 0.0
self._running = 0
self.timestr = StringVar()
self.makeWidgets()
def makeWidgets(self):
""" Make the time label. """
l = Label(self, textvariable=self.timestr)
self._setTime(self._elapsedtime)
l.pack(fill=X, expand=NO, pady=2, padx=2)
def _update(self):
""" Update the label with elapsed time. """
self._elapsedtime = time.time() - self._start
self._setTime(self._elapsedtime)
self._timer = self.after(50, self._update)
def _setTime(self, elap):
""" Set the time string to Minutes:Seconds:Hundreths """
minutes = int(elap/60)
seconds = int(elap - minutes*60.0)
hseconds = int((elap - minutes*60.0 - seconds)*100)
self.timestr.set('%02d:%02d:%02d' % (minutes, seconds, hseconds))
def Start(self):
""" Start the stopwatch, ignore if running. """
if not self._running:
self._start = time.time() - self._elapsedtime
self._update()
self._running = 1
def Stop(self):
""" Stop the stopwatch, ignore if stopped. """
if self._running:
self.after_cancel(self._timer)
self._elapsedtime = time.time() - self._start
self._setTime(self._elapsedtime)
self._running = 0
def Reset(self):
""" Reset the stopwatch. """
self._start = time.time()
self._elapsedtime = 0.0
self._setTime(self._elapsedtime)
def main():
root = Tk()
sw = StopWatch(root)
sw.pack(side=TOP)
Button(root, text='Start', command=sw.Start).pack(side=LEFT)
Button(root, text='Stop', command=sw.Stop).pack(side=LEFT)
Button(root, text='Reset', command=sw.Reset).pack(side=LEFT)
root.mainloop()
if __name__ == '__main__':
main()
Listing 8-10A RPi Three-Button Stopwatch Timer GUI
摘要
-
基于具有充当定时时钟的晶体调节振荡器的“锁存器”的集成电路能够以微秒的分辨率计数和确定事件之间的时间。
-
自 1970 年 1 月 1 日以来,时间测量基于“滴答”计数,并允许通过 SCADA GUI 确定白天时间、定时协调和安排未来事件。
-
秒表计时可通过商用和组件装配的 SCADA 系统进行配置。
-
提供了几种解决方案,用于在实验会话期间监视事件并补偿事件检测器的错误或错误触发。
-
在第九章中,介绍了图形数据记录的优势,在某些实验中,图形数据记录可以检测事件检测的错误触发。
1)供科学家和工程师使用的数字电子学,马尔姆施塔特和科恩,W. A .本杰明公司,纽约州纽约市,ISBN 0-80536899-X
-
CMOS 食谱第二版 Edn。,兰卡斯特,霍华德·w·萨姆斯&有限公司,国际标准书号 0 672-22459-3
-
电子艺术第二版,霍洛维茨和希尔,剑桥大学出版社,ISBN-13 978-0-521-37095-0
2
azeotech.com/dl/labjackguide.pdf
3
①https://github.com/j-bellavance/EdgeDebounceLite/blob/master/README.md
②https://www.allaboutcircuits.com/technical-articles/switch-bounce-how-to-deal-with-it/
4
-
树莓派食谱第二版 Edn。Monk,O'Reilly Media Inc .,ISBN 978-1-491-93910-9
-
实用树莓派,霍兰,阿普瑞斯,ISBN 978-1-4302-4971-9
5
HTTP://CODE.ACTIVESTATE.COM/RECIPIES/124894/
6
docspython.org/3/library/sched.html
②https://pypi.python.org/pypi/schedule - schedule 0.4.3
7
http://sourceforge.net/p/raspberry-gpio-python/wiki/BasicUsage
九、图形数据记录
一句古老的远东谚语说:“一幅画胜过千言万语。”这句谚语的真实性在化学分析和医学成像中得到了充分的体现,在这些领域中,不仅数值,而且记录数据的形状也传达着信息。医学、物理学和许多其他实验科学中的许多技术都依赖于数据的图形化表示。临床和化学分析传统上使用化学敏感传感器来产生毫伏信号,以响应变化的化学过程值。该小信号被电子放大,并与伺服电机一起使用,以机械方式驱动笔在纸质图表上划过,从而提供被监控的化学过程的视觉记录。尽管 x-y 和 x 对时间绘图系统在制造加工工业、化学分析和其他科学中被广泛使用,但是机电绘图仪器,很像打字机,已经被 PC 取代。
x-y 绘图广泛用于分析光谱和电化学分析,而 x 对时间绘图用于后续滴定、生化动力学以及色谱和光谱分析。
DAQFactory 被用于这个应用程序,因为它具有强大的图形记录和显示功能。《DAQFactory 用户手册》中包含图形显示教程,以及关于 DAQFactory 图形显示功能的详细章节。对于那些使用免费 Express 或完整版 SCADA 软件的研究人员来说,在开始这个练习之前,应该先阅读教程和用户手册。
在本练习中,演示了几个非常重要的概念和电路配置。配置为非稳态多谐振荡器的 555 定时器将用于创建方波、锯齿波和非对称三角波信号波形,作为直观研究脉冲宽度调制(PWM)这一非常重要概念的前奏。将演示电容器充电和放电产生的指数和线性电压波形,并将使用特殊 IC 产生的对称电压波形输出来创建图形数据记录。
在考察的第一个定时器配置中,两个电阻和一个电容将用于在振荡器芯片上形成一个“定时网络”。将选择 RC 元件值,以便定时器芯片产生与我们的记录软件兼容的波形。所选择的电阻元件之一将具有可变的性质,以模拟基于电阻的化学或物理换能器。传感器电阻随某些物理现象的变化,如落在传感器表面的光强度或温度,将导致定时器输出信号的频率和波长发生变化,输出变化将以图形格式显示在 PC 屏幕上。然后,信号变化可以转换为脉冲宽度变化,形成广泛使用的脉冲宽度调制(PWM)概念的基础。
在考察的第二种定时器配置中,将演示如何使用恒流源为定时电容充电,并以图形方式记录“锯齿”和三角输出波形的产生。三角波形或电压斜坡在一些传感器监控和化学分析中有重要用途。组装第三个电路是为了演示一种创建双斜率模拟斜坡的简化方法,该方法常用于电化学、腐蚀和生物物理学研究。
一个简单的 x-y 记录系统构成了本章的最后一部分。
实验:线性图形数据记录
第一部分:硬件和元件选择–方波输出
第八章中之前的工作已经显示了软件开销带来的限制,因此,信号发生时或“实时”显示的信号形状变化率受到计算机性能的限制。处于非稳态模式的双极晶体管 555 定时器可以产生一个输出信号,该信号可以从 70 kHz 变化到大约每分钟四个周期。CMOS 版本的定时器可以产生兆周范围内的频率。大多数 555 定时器制造商都在 IC 的定时网络中包含了电容与电阻的 R1-R2 值之间关系的标准诺模图。图 9-1 是一个扩展图,容纳了能够以更高频率振荡的基于 CMOS 的 ic 的新版本。图 9-1 描述了定时器非稳态配置的近似定时网络值和产生的自由运行输出频率。
图 9-1
555 定时网络值的定时器输出频率
为了将定时器芯片的输出信号保持在适合 DAQFactory 图形的低频范围内,电阻值应在兆欧范围内(百万或 10 6 欧姆),电容值应在 0.1–10 uF(微或 1/1,000,000 F)范围内。图 9-1 的图形数据是近似值,选择使用的实际电阻值在某种程度上取决于选择的或可用的电容值。
值为 1–10 uF 的电解电容应适用于此图形显示练习,但为了更精确地工作,可能需要更高质量的低泄漏型电容,如下文所述。
所需的电子元件
-
555 定时器集成电路
-
可变电阻器和固定电阻器相加得到低兆欧范围,优选地,固定值和可变值处于相同的电阻量级
-
1–10 uF 范围内的合适“定时”电容、0.01 uF 旁路电容和 9 V 电池电源
电路原理图
图 9-2 描述了非稳态 555 定时器的电路配置。
图 9-2
555 定时器不稳定配置
前面的电路显示了引脚 7 和引脚 2 之间的单个可变电阻。当可变电阻器处于其中间行程位置时,电路将工作,但是当处于其电阻值的低端时,将产生不稳定的结果。为了避免电路故障的任何问题,将固定值电阻器与可变单元串联,以限制第二定时网络电阻的下端值。作者使用 10kω值作为 1.2mωR1+R2 网络的和值。
软件
组装好非稳态振荡器后,将输出连接到 LabJack 上的差分输入通道,并配置一个接收方波输出的通道。然后可以创建图形页面组件。对于长时间的图形显示,确保频道存储容量足够大,以支持所需时间显示的长度。内存中值的数量由通道“历史”框中的值决定。(默认条目是 3600,当在几十分钟或几小时的实验时间范围内工作时,可以很快填满。)
需要页面组件
在新的页面上,从右键弹出的页面组件菜单中选择 2D 图条目,创建一个二维图形屏幕组件,如图 9-3 所示。
图 9-3
2D 图形记录器屏幕显示的数据工厂选择
x-y 图形的默认图形屏幕组件配置如图 9-4 所示。
图 9-4
默认的 X-Y 图形屏幕显示
通过按 Ctrl 键并将阴影边缘线中心的正方形拖动到页面上的所需位置,图形组件被定位并适合页面。一旦确定了组件的大小,就可以通过右键单击屏幕组件并选择“属性”条目来打开屏幕组件的属性菜单。图形的属性窗口是一个多选项卡显示,具有多个条目表单,用于向图形添加轨迹、定义用于图形显示的轴值、添加标识标题以及选择显示颜色。图 9-5 显示了六选项卡属性窗口的轨迹选项卡,用于定义名称条目。
图 9-5
记录器跟踪名称选择
“属性”窗口底部的帮助屏幕解释了图形屏幕组件中的所有数据输入框和选项卡。
对于由 9 伏电池供电的 555 定时器振荡器产生的方波,电压范围被偏移以显示从-1 到 9 伏的值,从而更清楚地描述波形处于 0 伏的时间。
第一部分:观察
使用 392kωR1、900kωR2 和 10kω串联电阻限制单元,从 9 V 电池对 22 uF、25 V 电解电容器充电,在 R2 上以中间设置获得 60 秒内 4 个高时间周期的图形显示。
在图 9-6 和 9-7 所示的两个极端之间,单位时间内产生的信号波形数量发生变化。
图 9-7
最小电阻时的定时器输出
图 9-6
最大电阻时的定时器输出
如果电路在没有串联电阻来限制 R2 最小值的情况下工作,则可能会产生如图 9-8 所示的波形。
图 9-9
输出信号不稳定或“混叠”
图 9-8
无最小电阻的波形
实验的
第二部分:硬件和元件选择——三角和“锯齿”输出
除了产生具有可变占空比的方波信号之外,非稳态 555 定时器还可以用来产生“锯齿”和不对称的三角波。基础电子学教导说,当电容器通过固定值电阻器充电或放电时,在电容器的端子上看到增加或减少的指数电压值。当用恒流源给电容充电时,电容两端的电压会线性增加。线性电压变化形成三角波形,该三角波形可用于产生在化学分析和其他实验工作中具有多种应用的电压斜坡。
通过组装图 9-10 所示的电路,可以产生两个额外的波形。
图 9-10
恒流充电电源
第二部分:观察
当电容器通过串联电阻充电时,在测试点 1 和 3 之间,观察并记录常见的指数电压变化,如图 9-11 所示。
图 9-11
典型 555 定时器“锯齿”输出电压波形
当测量 TP1 和 TP2 之间的电压时,会记录一个三角波形,如图 9-12 所示。
图 9-12
555 定时器三角波扩展刻度
用恒定电流对电容器充电会在电容器极板上产生线性电压斜坡,如波形左侧所示。当定时器的放电引脚通过 555 定时器 IC 中的内部 NPN 开关晶体管的发射极接地时,信号的右侧陡峭部分是由电容快速放电引起的。虽然放电轨迹看起来是一条直线,但实际上它是图 9-11 所示的正充电曲率的反指数曲线的初始部分。图 9-12 和 9-13 显示了在电容器和定时器 IC 的放电引脚之间增加一个固定串联电阻的效果。
图 9-13
增加放电电阻对定时器输出波形的影响
简单的逻辑表明,要获得线性三角波形,通过恒流源和吸电流对电容进行充电和放电可以实现所需的结果,但使用能够产生各种形状和频率信号的“函数发生器”可以找到更简单的解决方案。
第三部分:硬件和元件选择——双斜率三角波形
要获得对称的三角波形,使用“函数发生器”更容易与 741 和 555 ICs 一样,Exar XR-2209 也是一款非常成功的函数发生器,自 1975 年以来一直采用 8 引脚双列直插式封装(DIP)。集成电路是围绕一个称为压控振荡器(VCO)的电路构建的。XR-2209 VCO 可以从单个 8 引脚 DIP 同时产生方波和三角波电压波形信号。根据当前应用的需要,该芯片可以采用单电压或双电压电源供电。(参见“讨论”)使用单端或双电压电源时,函数发生器芯片需要特别小心,详情见制造商的数据手册。图 9-14 显示了使用双电压电源(在作者的案例中为+/-9 伏电池)的 2209 函数发生器的推荐电路原理图。
图 9-14
函数发生器配置示意图
为了产生正负电压斜坡,电路由一对 9 V 电池供电,配置为双极性+/–9V 电源,共地,如图 9-15 所示。
图 9-15
一种双电池双极电源
如果电路在试验板上正确组装后无法按预期工作,请查阅制造商的数据手册和“讨论”部分。
第三部分:观察
XR-2209 可以产生如图 9-16 所示的对称三角波形,并通过适当的“上拉”电阻产生如图 9-17 所示的方波。(参见“上拉”电阻选择的设计限制讨论。)
图 9-17
带上拉电阻的方波输出
图 9-16
XR-2209 函数发生器对称三角波输出
X-Y 数据记录
如图 9-4 所示,二维绘图的默认格式,图形显示屏组件是 x 对 y。图 9-10 的恒流充电电路可用于产生电容器两端电压及其平方的 x-y 绘图,用于测量电容器两端产生的能量。E = CV 2 /2。恒流电路可用于本演示练习,因为恒流充电和器件的指数放电会在电容上产生不对称的电压斜坡。
为了创建所需的显示,可以在为 PWM 数据采集显示创建的通道中记录恒流充电电路的 470 uF 电解电容两端的电压。当定时器芯片将充电的电容器接地时,试验板电子器件使电容器上的电压在电源电压的 1/3 和 2/3 之间线性循环,然后指数放电。因此,循环充电和放电电压的图形显示应该不同。在 x 轴框中输入通道名称,在 y 轴表达式框中计算电压的平方。显示在两个轴上的电压必须根据用于驱动电压变化的电源进行调整,在作者的情况下,图 9-18 、 9-19 和 9-20 中描述的值用于记录图 9-21 中的数据。
图 9-20
x-y 图形常规选项卡
图 9-19
x-y 图形轴选项卡
图 9-18
x-y 图形轨迹选项卡
记下此图形图像显示的数据。如图 9-18 所示,x 轴和 y 轴都不是 PlsWdthVrn555 而是 PlsWdthVrn555[0,20]。因为这是一种循环现象,我们只需要显示通道数据的有限部分,即有限的“视野”或“视觉暂留”
图 9-21
电容器电压和电压平方的曲线图
观察:x-y 绘图
图 9-21 中显示的轨迹是一个典型的记录,在记录失真或改变的轨迹之前,它可以保持稳定和可再现几分钟。图 9-22 和 9-23 已经捕捉到两个错误轨迹的实例。
在图 9-22 中,在 5 V 和 25 V 交叉处记录的电压方波放电轨迹已经一分为二。仔细检查图 9-23 会发现,放电轨迹不仅在循环的高压部分一分为二,而且在循环的 3v 和 9 V 低端也是如此。
图 9-23
高和低电压轨迹变化
图 9-22
更高的电压轨迹变化
讨论
记录数据的图形显示非常有价值,因为它能够显示趋势。实验科学依赖于再现性,数据的图形显示可以用来验证观察结果。
图形显示允许研究者看到隐藏在“实时”观察中的事件或趋势。然而,在检查数据记录的趋势时,在对数据有效性做出判断之前,必须考虑由材料缺陷、自热引起的温度变化、光诱导的变化、实验条件的不良选择以及大量其他误差源引起的偏差。在化学分析过程中,特别是在这项工作中,研究人员必须经常处理“模拟”数据的图形表示,这些数据涉及或需要很长或很短的记录时间。或长或短的时间范围可能需要特殊的部件、特殊的电子电路或配置,以及保护操作电路不受杂散电信号和干扰的影响。超长和超短时间常数可能会接近传统 IC 构建模块原始设计参数的极限,因此在极端工作条件下或接近极端工作条件下使用这些器件时必须格外小心。
从电子来源获得的数据中常见的变化来源包括:
-
由于电池或“电源”供电而产生的变化。
-
元件的缺陷和变化,如电阻噪声(线绕元件最小,金属薄膜次之,碳元件最大)、电容漏电流和记忆效应(电解元件最大,钽元件次之,塑料薄膜元件最小)。
-
由环境变化引起的温度效应和由通过电阻性电子元件的电流引起的内部加热都会导致电信号漂移。
-
长电线会积累射频干扰(RFI ),其作用类似于主电源线辐射的天线。电线应尽可能短,如有需要,可装入法拉第笼中。带有长条状金属触点和插入电路板的长元件引线的试验板只能用于实验开发,然后用印刷电路板替换,用于实际的实验服务。
-
通道存储的数字采样(或模数转换)中的混叠。这些练习中创建的实验设置的数据由 LabJack 或微控制器转换成数字格式,并由 DAQFactory 软件以输入通道计时值框的通道计时值控制的速率读取。可能由控制计算机电子设备、LabJack 电子设备、实验装置本身或实验可能位于其中的建筑物的干线布线拾取或产生的任何重复或周期性的电信号存在与由被监控的装置产生的真实信号“混淆”的可能性。当以长期图形格式显示时,长时间被监视的信号可能包含叠加在真实或原始信号上的“错误或人为”波形。
x 与时间记录
555 定时器的操作顺序已在第八章中概述,从该总结以及图 9-6 和 9-7 中的信息,我们可以看到在非稳态模式下产生的波形随着定时网络中 R2 电阻值的变化而改变频率。
当以非稳态配置接线时,电容充电时间由 RC 时序网络的总电阻决定,如下所示:
- t 1 = 0.693(R1 + R2)C(输出高电平时间)
并且放电时间是
- t 2 = 0.693(R2)C(输出低电平时间)
因此,总信号周期为
t = t1+t2= 0.693(R1+2r 2)C
并且操作的频率是
f = 1/T = 1.44/(R1 + 2R2)C
电容通过两个电阻充电,同时仅通过一个电阻放电。当网络中的 R2 电阻是可变电阻时,输出为低电平的时间与可变电阻值成正比。因此,555 定时网络中变化的模拟电阻可以通过测量输出为低的记录宽度来数字化。
图 9-2 所示电路产生的高电平和低电平时间的相对宽度存在限制。需要特殊的电路来保持振荡器频率相对恒定,而振荡器的高和低时间宽度(占空比)是变化的。
扩大或缩小图形显示的时间刻度可以改变所显示波形的分辨率。
图形显示需要大量的计算机处理资源,并且如前面处理时间和定时的练习中所述,对快速变化的信号的响应能力有限。快速变化的信号最好用硬件数字化,存储在存储器中,然后在收集后,转换成图形格式显示。
对于较慢的信号变化,DAQFactory 能够在其通道中存储图形数据,然后能够将其显示为带状图表记录,这在揭示隐藏信息方面非常有用。如果图 9-12 的三角波形被记录 8 到 10 分钟,然后图形显示的时间刻度被重新配置以显示 8 分钟的数据窗口(即,对于时基,8 分钟的窗口将是 8 分钟× 60 = 480),那么大量的变化变得明显,如图 9-24 和 9-25 所示。
在图 9-24 和 9-25 中,延长显示重复电压周期的时间尺度已经直观地显示了几个普遍存在的实验误差源的影响。
大多数人都熟悉水波在静止水体中的传播。在我们看来,由扔进池塘的石头引起的来自两个源头的水波互相穿过而没有干扰。然而,如果一个物体漂浮在水面上,与波浪在同一点穿过另一点,就会看到该物体剧烈的颠簸。剧烈的纵摇是由叠加原理引起的,叠加原理将穿过彼此的两个水置换波的振幅相加。图 9-24 中 7 . 14 . 30 和 7 . 19 . 00 处可见的失真可能是由幅值为伏特、频率为 4 1/2 分钟一个周期的第二个电压变化波与主信号混合或干扰引起的。
由更复杂的电子问题引起的图形失真的另一个来源是所谓的混叠,该电子问题涉及周期性模拟波形的定时重复数字采样,这在一些电子教科书中有所讨论。 1 (别名是 RPi-Python 编程代码的一个实用概念。)
图 9-24
长期信号失真
所记录的三角波形就其出现频率而言是可合理再现的,因为可以看到作者的试验电路在 5 分钟内产生 19 个周期。然而,可以看出电压电平的再现性是漂移和振荡的。电压的下限值从 3.0 到 3.4 不等,而上限值从 5.7 到 5.0 不等。尽管电压的上限值和下限值在变化,但显示有明显的模式,表明系统由于前面讨论的因素而漂移和振荡。
施加到恒流源中左侧和右侧晶体管的手指热量在周期时间内产生膨胀和压缩,如图 9-25 和 9-26 所示。
图 9-26
手指加热应用于电流镜的右晶体管
图 9-25
手指加热应用于电流镜的左侧晶体管
除了周期时间改变之外,不稳定的振幅也是热效应的结果。
制造电子元件的材料也会产生电子电路中的噪声。线绕电阻的噪声最小,金属薄膜次之,碳基元件对电阻电路噪声的贡献最大。
电解电容价格低廉,可提供更高的值,但几乎所有高值电解单元都有相当大的漏电流。在需要循环或重复再现性的系统中,漏电流会导致问题。传统的低泄漏电容通常没有高电容值,但是当有限的高值单元存在时,它们通常非常昂贵并且物理尺寸很大,如图 9-27 的照片所示。
图 9-27
各种类型的定值电容器
利用运算放大器和电容可以产生对称的三角波形,但一种称为压控振荡器的电路被设计用来同时产生方波和对称三角波形。Exar XR-2209 IC 是一个带有外部电容和电阻的模块,可由 4 至+/-13 伏的双电源或单电源供电,以产生所需的信号。图 9-16 和 9-17 是 IC 的典型输出。在作者的试验板设置中,可以看到三角波在所达到的峰值电压中有系统地变化。试验板设置也被证明对用于产生方波的“上拉”电阻值非常敏感。元件灵敏度可能是由于在接近电路设计极限的区域操作电路造成的。
X-Y 记录
当要记录的信号本质上是循环的时,通常使用 x-y 记录。由于信号的循环性质,最好从 x-y 屏幕上清除旧的轨迹,因为新的轨迹会覆盖在旧的数据轨迹上。通过使用[n]通道值符号指定要绘制的数据点数,可以显示信号周期的任何分数或倍数。
在图 9-22 和 9-23 中看到的不可再现信号的影响是由相同的原因引起的,这些原因在图 9-24 、 9-25 和 9-26 中记录的 x 与时间信号的变化中很明显。
微控制器数据绘图
开源、在线社区支持的可编程微控制器不断扩展其基本功能,从版本 1.6.6 开始,Arduino IDE 中增加了数据绘图功能。
在之前的练习中,Arduino 微控制器被用作智能数据采集设备、传感器或显示器的电源以及时钟;在本章中,它将用于提供数据的可视化图形显示。
自 Arduino IDE 版本 1.6.6 和 7 以来,工具菜单中提供了串行绘图仪选项,如图 9-28 所示,最初用于单个绘图,但从版本 7 开始,用于多数据点绘图。
图 9-28
Arduino IDE 工具菜单串行绘图仪选择
调用串行绘图仪输出会将串行端口窗口显示转换为 x-y 绘图仪。指向串行端口用于显示打印语句的各个数据点绘制在垂直 y 轴上。x 轴以 500 点移动窗口的形式从左向右自动滚动。x 轴的度量是使用换行符打印指令对代码行的处理。图 9-29 中的第 15 行包含 println 代码,该代码被视为已处理,其总值构成了显示在 x 轴上的数值。
对于多点绘图,用单独的轨迹显示的每个数据值都用打印空白指令或制表符指令与下一个数据值分隔开:(print(" ");或者打印("/t ");).图 9-29 中的第 10、12 和 14 行形成了图 9-30 中所示的四道图的分离标记。
实验的
图 9-29 中的代码绘制了两条不同频率的直线和两条正弦曲线,如图 9-30 所示。
图 9-29
Arduino IDE 典型绘图仪程序
观察
图 9-30
Arduino 串行绘图仪输出
对微处理器绘图仪演示代码和显示的正弦波频率的检查验证了正弦波和余弦波之间预期的 20:1 频率比。常数值绘制为预期的直线。
讨论
Arduino 的 IDE 中包含的绘图仪为实验研究者提供了一种非常强大的可视化技术。这个绘图仪既容易使用又有用。由微处理器控制的实验过程产生的图可以被记录,以便利用主机上可用的打印屏幕功能进行存档。实验图归档已用于涉及温度、运动和振动测量的实验工作以及光和光学研究。
虽然绘图仪是一个非常有用的功能,但它在原稿准备时在操作的几个方面受到限制。跟踪的颜色由 IDE 的操作系统决定,有时很难看到。标度是自动调整的,与 DAQFactory 绘图仪不同,它不能独立设置为不同的值。
偶尔在初次启动时,绘图仪会产生虚假图像,如图 9-31 左侧所示,或者不正确地自动缩放 y 轴。
图 9-31
Arduino 串行绘图仪启动噪音
如上图所示,绘图仪相当快地进入可再现状态,但有时可能会错误地绘图,直到 500 点窗口自行刷新,自动缩放功能也进入可再现绘图模式。
用 Python 和 Raspberry Pi 记录图形数据
介绍
如上所述,实验数据的图形绘制可以采取两种形式。如果数据是以高速率生成的,那么最好将它保存到内存中进行存储,并在以后以图形方式进行分析。以较低速率生成的实验数据通常可以在“现场”或“实时”显示中显示。Python 和 RPi 使用一个名为 matplotlib 的图形绘图库来显示实时和存储的数据。
本章末尾的清单 9-1 中提供了一个 Python matplotlib 代码示例,该代码绘出了偏置在 GPIO 阵列上的 3.3 伏 RPi 电源与其地之间的 10kω电位计的游标臂电压值。该代码已从带状图表记录器程序修改而来,可在 matplotlib 文档中找到“动画示例代码:strip_chart_demo.py”。该文档包含使用这种类型的动画图形显示的完整开发教程。
虽然 RPi 没有广泛的商业图形显示软件应用程序可供选择,但 matplotlib 可以为开发所需的应用程序提供坚实的基础。本练习中用于监控变化电位计电压的相对较短的程序配备了几个高级实用程序,用于深入检查记录的图形显示。matplotlib 文档中题为“交互式导航”的部分描述了绘图显示左下角的七个按钮的操作,如图 9-32 和 9-34 所示。当使用任何显示操作或存储按钮时,左按钮恢复显示焦点。按钮允许记录轨迹的部分被保存,如图 9-33 所示,并被放大,如图 9-34 、 9-35 和 9-36 所示。除了按钮激活的实用程序之外,库示例还显示鼠标光标的坐标,以便通过将光标指针放在跟踪中感兴趣的点上,并根据显示时间和绘图仪框架右下角显示的数值中的测量数据值读取该点的 x 和 y 坐标,可以识别精确的点。
matplotlib 程序也很容易改变任一标绘轴的比例,但由于之前练习中出现的时间比例不一致,显示的绘图仪时基需要按照以下实验部分所述进行校准。
实验的
为了演示 RPi 提供的绘图功能,可以从 gpiozero 库和 MCP3008 ADC IC 创建一个示例,从偏置电位计的游标读取电压。游标电压由一个 10 位 ADC MCP 3008 数字化,配置如第六章、图 6-17 所述。为了便于 ADC 编程,gpiozero 库通过访问在“pot = MCP3008(0)”行中实例化的对象的“pot.value”属性来提供绘图数据。通过 gpiozero 库创建 pot 对象,程序员可以访问连接到 ADC 芯片第一个通道的游标电压值。通过将要绘制的代码变量设置为等于 pot.value 属性,该值将自动规范化为 0 到 1 之间的无量纲浮点值。
使用 gpiozero 库访问 MCP3008 ADC 的 RPi 配置还允许修改绘图程序,以接受能够提供 3.3 V 或更低电压值的任何传感器或换能器。图 35 和 36 是 555 IC 定时器输出的两条迹线,该定时器连接到数字转换器的第一个或 0 通道。第八章、图 8-8 的右图和电路中说明了 555 IC 的配置。对于本实验,R1 和 R2 为 4.7kω,C1 为 555 定时器 RC 网络中的 100 μF 电解电容。输出电路还包括一个 LED 和限流电阻,以帮助电路组装、电气操作验证,以及通过观察 LED 以 60 秒 59 次的速率连续闪烁来验证记录仪显示。最后两个放大的比例图,图 9-37 和 9-38 ,是通过绘图仪显示左下角选项行的“保存轨迹”按钮制作的。
为了帮助开发已发布的 matplotlib 条形图表记录器代码(使用内部随机数生成器为绘图仪示例输出创建 y 值),作者在正在修改的代码中插入了许多诊断打印语句。print 语句将正在执行的代码中的某些点的某些变量值输出到 Python 控制台,以帮助开发不同的方法,使代码适应不同来源的数据。注释掉诊断打印行将清除控制台显示。流出的变量数据在控制台显示器中显示为图 9-32 至 9-34 中的左侧屏幕。当不再需要时,打印行可以被注释掉。
在 RPi 上使用图形数据显示时,必须考虑几个因素。正如在之前的练习中所提到的,系统的时基不是恒定的,因此绘图仪显示器底部的时间刻度可靠性有限,必须进行半定量校准才能半定量使用(见“讨论”)。
一旦建立了所需的实验时间框架,必须使用秒表来测量系统绘制标称所需窗口时间宽度的数据所需的实际时间。表 9-1 是作者在开发用于校准标称 2 分钟宽绘图窗口的程序时收集的数据示例。
表 9-1
调整绘图仪时基
|Dt 设置
|
时间宽度(秒)
|
| --- | --- |
| Zero point zero two | Twenty-five |
| Zero point zero one | Forty-one |
| Zero point zero zero five | One hundred and twenty-seven |
| 0.0055 | One hundred and sixteen |
| 0.00525 | One hundred and twenty-nine |
| Zero point zero zero five | One hundred and twenty-five |
观察
图 9-32
电位计游标电压的数据记录
图 9-32 显示了从实验开始 80 到 120 分钟的电压值轨迹,其中作者在显示器未校准的相对时间轴上记录的时间手动转动电位计轴。轨迹的响应速度相对较快,但轴的快速扭转会超出显示器跟上数据值变化的能力。
图 9-33 显示了点击选项行最右侧的“保存图形”图标时调用的操作。图形作为 png 图像保存在 RPi 的文档文件中。
图 9-33
“保存图形”选项窗口
在图 9-34 的屏幕截图中,鼠标的光标刚刚经过 17 分钟就被放置在垂直响应线上,然后该点的精确坐标被打印在显示屏的右下角。
图 9-34 显示了将鼠标绘制的框所包围的区域扩展为全屏显示的缩放选项。然后可以如前所述保存扩展的图像,或者可以使用“返回前一视图”图标来恢复或继续正常的绘图动作。
图 9-34
“规模扩张”选项
在下面的两个图中,使用“保存数字”选项以扩展的比例记录了详细配置的 555 定时器的输出。图 9-35 和 9-36 显示了软件保存来自外部电压发生源的较短时标绘制数据的能力。
图 9-36
555 定时器数据记录的一分钟时间刻度扩展
图 9-35
扩展时标 555 定时器数据记录
讨论
使用 Python matplot 库中的带状图记录器程序进行图形数据记录是一个适应性非常强、非常灵活的系统,可用于直接显示来自连接到 GPIO 阵列的传感器或来自 Python 串行端口的数据。
在图 37 中,配置为 R1 = 5.83kω、R2 = 4.7kω和 420 μF C1 定时电容的 555 定时器的输出在 33 秒内产生 10 次 LED 闪烁。计时器由 GPIO 阵列的 3.3 V 电源供电,并针对 4 分钟显示窗口进行校准,详见“实验”部分。
图 9-37
校准时基 555 定时器电压输出记录
图 9-38 显示了数据绘图程序显示选项按钮可用的比例扩展功能。
图 9-38
时间校准的标绘迹线展开
大量传感器已被编码到 gpiozero 库中,可用于为 matplotlib 绘图程序提供数据。
当矩形脉冲时间宽度的变化以图 9-38 的视觉格式呈现时,图形数据显示的优势之一变得明显。
表 9-1 展示了用于 RPi 图形数据显示的时基限制。Dt 值的逐步递增减半增加了时间测量,但是返回到 0.005 的值与原始测量值产生了 2 秒的差异。这种差异证实了手稿中提到的关于 RPi 操作系统优先级的警告,RPi 操作系统优先级会干扰计算机输入和输出操作的计时。
代码列表
# SCR Plotting of Normalized Potentiometer Voltage Value from an MCP3008 gpiozero used to configure MCP3008 and attributes
# for plotting
#
import matplotlib
import numpy as np
from matplotlib.lines import Line2D
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
from gpiozero import MCP3008
#
pot = MCP3008(0)
#
#
class Scope:
def __init__(self, ax, maxt=40, dt=0.02):
"""maxt time width of display"""
self.ax = ax
self.dt = dt
self.maxt = maxt
self.tdata = [0]
self.ydata = [0]
self.line = Line2D(self.tdata, self.ydata)
self.ax.add_line(self.line)
self.ax.set_ylim(0.0,1.0) # y axis scale
self.ax.set_xlim(0, self.maxt)
def update(self, y):
lastt = self.tdata[-1]
if lastt > self.tdata[0] + self.maxt: # reset the arrays
self.tdata = [self.tdata[-1]]
self.ydata = [self.ydata[-1]]
self.ax.set_xlim(self.tdata[0], self.tdata[0] + self.maxt)
self.ax.figure.canvas.draw()
t = self.tdata[-1] + self.dt
self.tdata.append(t)
self.ydata.append(y)
self.line.set_data(self.tdata, self.ydata)
return self.line,
#
#
def rd_data():
inPutln = pot.value
#print("inPutln = ", inPutln)
line = inPutln
#print(line)
yield (line)
fig = plt.figure()
fig.suptitle("Pot Wiper Voltage", fontsize = 12)
ax = fig.add_subplot(111)
ax.set_xlabel("Time")
ax.set_ylabel("Potentiometer Voltage")
scope = Scope(ax)
# uses rd_data() as a generator to produce data for the update func, the MCP3008 value is read by the plotting code in
# 40 minute windows for the animated screen display.
# Software overhead limits response speed of display.
ani = animation.FuncAnimation(fig, scope.update, rd_data, interval=50,
blit=False)
plt.show()
Listing 9-1Python Code for Live or Real-Time Data Plotting with Raspberry Pi
摘要
-
以 y 对时间或 x 对 y 的图形记录的实验数据可以显示许多电子产生的波形和传感器读数。
-
图形数据记录可以揭示信号漂移和信号偏差,并显示电气、机械和环境对信号输出的影响,这些影响在数字显示中通常是看不到的。
-
商业 SCADA 绘图易于配置、强大且非常灵活,而组件组装系统在显示能力方面更受限制,必须手动校准。
-
在第十章中,介绍了电流控制的各种方法,这是实验设备配置或设计的一个重要方面。
电子艺术,霍洛维茨和希尔,剑桥大学出版社,ISBN 0-521-37095-7
十、日常检查
电流控制和监测是许多实验设置和科学测量的重要组成部分。如前一练习所示,可能需要恒流控制来实现特定的结果。传感器测量、扫描仪器中的运动控制、机器人操纵器、电镀或电流分析以及加热控制操作只是需要电流控制的几个例子。led 应该由恒流源供电。已经发现,当来自一个供应商的一批发光二极管由消耗 4 到 39 mA 的恒定电压电源供电并且具有如此宽的电流差时,不能产生相同的发光或色度输出。电流控制可以不同于管理物理或化学变化的传感器,通常需要测量毫微安的 DC 电流,而加热、电沉积和电机控制应用通常涉及控制电流的安培数。
电流控制可以用分立晶体管实现,如第九章所述;为电流调节而配置的通用集成电路,如运算放大器;或者是专门为 DC 或交流电流控制应用而生产的专用集成电路(ASIC)芯片。
本章分为三个部分,涉及恒流 DC 电源、较大电流的控制以及潜在致命的市电交流电源的控制。在控制电流的同时,将演示电机带来的一些限制以及解决这些限制的方法。用于实验装置和这些练习的廉价电机可以从过时的计算机设备中回收,或者如果需要可以从电子供应源获得。我们将演示旋转电机对运动控制的一些限制,并开发为实验装置选择首选电机的过程。
用于电流或功率控制的脉宽调制(PWM)技术将在电机和白炽灯照明应用中进行回顾和演示。
交流电子学,由于它的循环性质,可能比 DC 要复杂得多。为了保持这些练习的简单介绍性质,只考虑无感或完全阻性负载应用。在严格阻性应用中,均方根(rms)、峰峰值或平均交流值可以像大多数电子基本定律中的 DC 值一样使用。高级通信、感应加热或光谱学中遇到的较高频率和相位敏感的交流电子设备不会在这些基础介绍练习中深入讨论。
恒流源
在许多类型的电子电路和许多实验测量仪器中,也可以找到以前使用的恒流源。如前所述,恒流源可以由一对作为“电流镜”的晶体管构成,或者由一个运算放大器(op-amp)和一些电阻构成。虽然前面提到的大部分电子书籍都详细讨论了分立式元件电流镜,但由于设计简单、电路控制范围宽,并且使用容易获得的廉价元件,所以本练习中使用运算放大器。
本练习中使用的运算放大器(如 LM741)由双电压电源供电,必须平衡或适当偏置才能使用。图 10-1 描述了 lm 741–2n 3906 PNP 晶体管接地负载电路的一般原理图,该电路可用于为已知负载提供恒定电流,如各种参考文献中所公布的。图 10-1 和 10-2 中描绘的电路可以组装在典型的原型试验板上,用于测试、电路操作验证和电流控制应用。
下面将针对负载接地、电流感测电阻连接到电源的配置来解释电路工作原理。如果需要相反的情况,则可以使用 NPN 晶体管来调节电流(LF411 可以直接替代 LM741)。
实验的
五金器具
一个+/-双电压电源和一个调整电位计用于为运算放大器供电和平衡。构建和验证恒流源需要一个功率晶体管、三个适当的偏置电阻和一个合适的预期实验负载的可调电阻模拟器。恒流运算放大器电路的典型实现在以下作者组装的测试电路的描述中有详细说明。一节 9 伏电池和 4 节串联的 AA 电池可以提供 15 伏的 DC 电源。串联连接两个 15 伏电池组可用于创建所需的+/-15 伏双极性电源,中间端子接地。双极性电源允许将运算放大器输出驱动至正电压或负电压。(参见 9 章节中的图 9-15 。)
电路原理图
图 10-2
运算放大器平衡的典型电路实现
图 10-1
典型运算放大器电流控制电路
如果一个特定的化学分析实验(如库仑滴定)有一个代表 20ω负载的滴定容器,并且发现 20 mA 的电流可以产生一个到达分析终点的合理时间,那么就具备了确定组装图 10-1 和 10-2 所示电路所需的电阻值的条件。
根据欧姆定律,通过 20ω负载维持 20 mA 电流需要 0.40 V 的电压,晶体管的 PN 结压降通常为 0.3 V,因此发射极应保持在 0.7 V,要将 15V 转换为 0.7V,需要一个数值为 0.0466 的分压器;因此,R2/(R1 + R2) = 0.046。LM741 可以使用 12 至 18 伏之间的任何双电压电源,因此可以相应地调整分压器网络和限流 R3 的实际电阻值,以保持所需的运算放大器基准电压或设定点,从而获得所需的电池电流。对于 20 mA 的调节电流,根据 I 2 R 的功率关系,我们可以估计电路的调节载流部分中的 1/8 瓦电阻足以满足手头的实验。
作为典型的电化学电池负载模拟,作者使用了一个 25ω、30 W、可调抽头的线绕电阻。调节负载电阻上中心抽头的位置模拟了在实验电化学电池或电阻加热元件中可能遇到的电导率变化。
开始练习时,实验者可以根据前面的原理图组装电路。验证布局后,将运算放大器的反相和同相输入端暂时接地,将调整电位计调整到其中点,并给运算放大器上电。监控放大器输出电压的同时,调整 10kω可变电阻以获得尽可能接近 0 V 的放大器输出,实际平衡运算放大器或将调整电位计游标定位在一个较低的非零电压值点,当实际电路工作期间建立反馈环路时,这将导致系统平衡。
由 R1 和 R2 构成的分压器产生一个参考电压 V Ref ,该电压被提供给运算放大器的同相输入端。现在,通过改变流经 R3 的电流,运算放大器将尝试将两个输入保持在 V Ref 的相同电压。流经负载、晶体管和 R3 的电流由注入 2N3906 晶体管基极的电流控制。流经负载和晶体管的整个电流通过 R3,R3 必须具有足够的瓦特数,以适应恒流配置可能需要的电流变化。如图 10-3 所示,当调节负载模拟电阻器的中心抽头以模拟模拟电池电阻的变化时,电流测量将确认电路通过电池模拟器提供标称 20 mA 电流的能力。
图 10-3
大电流试验电路的线绕负载模拟器
上图中的第 1 项是模拟的可变电阻负载,由垂直安装在螺杆上的 25ω30 W 线绕电阻组成。第 2 项是 5 W 电流检测电阻,第 3 项是 LM741 运算放大器塑料 DIP。项目 4 是 2N3906 功率晶体管,项目 5 是正极红色、负极黑色和绿色接地或中性电源引线。第 6 项是用于运算放大器平衡或偏置的调整电阻。
软件
这部分练习不需要页面组件和编程软件。
观察
当中心抽头的位置改变时,线绕电阻器产生非常粗糙的欧姆电阻,但是负载电阻值的改变足以证明所需恒流源的发展。大功率模拟负载在主端子之间测得的标称值 25ω和在裸露线芯匝上重新定位滑动抽头时的约 20 至 10ω之间变化。连接到正电源并向晶体管提供电流的大功率电阻器与可变负载一起决定了可在调节电路中流动的电流。当电路开始通电时,观察到的电流很高。随着电子设备达到大致的热平衡,调节电流稳定在接近所需设定点的最终值。通常电路需要 15-30 分钟达到恒定的热值。表 10-1 列出了通过作者的试验模拟测得的稳定电流。
表 10-1
负载电阻和调节负载电流
|负载电阻(ω)
|
电流(毫安)
|
| --- | --- |
| Six point seven | Twenty-one point four |
| Ten point eight | Twenty-one point four |
| Fourteen point three | Twenty-one point one |
| Fifteen point one | Twenty-one point four |
| Seventeen point six | Twenty point nine |
| Nineteen point three | Twenty point nine |
| Twenty point two | Twenty point nine |
| Twenty point three | Twenty-one |
上表中的负载电阻以欧姆为单位,电流以毫安为单位。
讨论
通过回忆运算放大器驱动其输出以均衡反相和同相输入端的电压,可以在反馈配置中解释电路操作,因此 V Sense = V in 。通过感应电阻的电流为 ISense= Vin/RSense,由于通过感应电阻的电流流经晶体管,所以通过负载 I Load 的电流为 V in /R Sense 加上晶体管极小的基极发射极电流。
对于流经 20ω负载的 20 mA 目标电流,该表显示了流经 6.7 至 20.3ω负载的约 21 mA 电流的 0.5 mA 变化。如果需要精确的 20 mA 电流,可以通过实验调整分压器,将基准电压调整到将晶体管电流调节到所需水平的值。
如第九章数据图形显示中所讨论和演示的,以及如前所述,热效应将导致测量信号漂移,直到建立热平衡。如果需要临界电流控制,那么可能需要在实验设置中引入某种形式的热控制或稳定。散热器、冷却气流、绝缘材料或大型金属热物质可以通过辐射或吸收过多的热量来保持或部分稳定温度。
电流调节也可以用专用集成电路来实现,例如美国国家半导体公司的 LM340/78xx 系列集成电路。然而,集成电路在特定的固定电压下工作,并且通常限于 1 安培的总电流。要调节的实际电流由适当阻值的传感电阻决定。具体配置和限制详见制造商数据手册中针对单个器件的应用部分。
由分立元件组装而成的运算放大器功率晶体管电路具有能够在任意电压和负载要求下控制电流的灵活性的优点。运算放大器功率晶体管配置也可以与 PNP“接地负载”配置或“浮动负载”一起使用,其中检测电阻接地,NPN 晶体管连接到电源和浮动负载。
运算放大器的特性和理论可以在前面提到的大多数教科书和许多介绍性的、深入的在线教程中找到。
控制较大的 DC 电流
介绍
无刷直流(BLDC)电机(无换向器或点火刷的电机)
在涉及加热、泵送、机械运动或运动控制的实验装置中,会遇到较大的 DC 电流操纵。对于每一种要控制的运动,通常有几种将电能转换成所需的物理运动或动作的方法。螺线管像在机器人系统中一样线性地来回运动,而马达则扭转或旋转;对于需要电机驱动的简单应用,以及液体混合或泵送、气体冷却或旋转光学扫描操作,我们将重点讨论电机的旋转运动控制。进一步的限制包括使用非常小的分马力电机,该电机设计用于现场或实验室使用,具有容易获得的、坚固的 12 V 铅酸电池系统或 12 V DC 电源,提供所需的更高电流。(电机的物理和机电方面以及更强大的电机控制在机器人和机械、化学或电气工程的文献中有更详细的讨论。)在化学分析和大部分生命科学的实验室工作中,经常使用易燃溶剂,因此,除非经过防爆认证,否则不应在实验操作中使用有刷 DC 电机。
驱动电机所需的较大电流可以由晶体管控制,而晶体管又可以由集成电路产生的小得多的基极电流控制。可变 555 定时器信号可用于控制更高电流的功率晶体管,该晶体管进而调节施加到电机的功率以控制电机速度。在这部分练习中,将使用一个 555 定时器来产生一个方波脉冲序列,该脉冲序列的占空比将以受控方式变化,以改变功率晶体管向风扇电机供电的时间,该风扇电机能够汲取高达 200 mA 的电流。因此,风扇电机的转速将由 555 定时器网络中的电位计控制。风扇盘转速将通过 LabJack 计数器进行光学测量,DAQFactory 程序将计算风扇盘转速并显示在屏幕上。
实验的
五金器具
对于电机控制电路,需要一个试验板来安装一个 555 定时器和将 IC 配置为非稳态模式所需的无源元件。从图 10-4 中可以看出,作者将 100kω电位计的安装支架、TIP-122 功率晶体管的散热器和带无刷直流风扇电机的风扇组件的支架组合到一个定制钻孔的 1 英寸(2.5 厘米)乘 8 英寸(15 厘米)的铝制角钢上,标记为项目 1。散热器安装角用螺栓固定在一个大约 1/16 英寸(1 毫米)厚的 8 英寸乘 6 英寸(20.4 厘米乘 15.2 厘米)的铝板上,标记为项目 1a,试验板用双面胶安装带(地毯带)固定在铝板上。最简单的风扇电机(无刷直流,BLDC)有两条 DC 电源引线。三引线和四引线风扇电机很常见,通常增加了用于监控轴位置的内部霍尔传感器(磁场检测器)的连接。将光电二极管和光电晶体管装配并固定到两个 1 英寸(1.2 厘米)乘 1 英寸(2.5 厘米)的定制钻孔铝板上,铝板上带有室温硫化(RTV)硅酮。编号为 4 的项目是传感器安装标签。标签背面的粘合剂嵌条将传感器二极管牢牢固定,同时不会干扰光学活性表面。如图 10-4 所示,用螺栓和蝶形螺母将调整片安装在风扇电机框架的一角。调整片安装有由光电二极管产生的窄红外光束,该光束指向风扇盘叶片的旋转平面。因此,七叶片风扇盘(项目 2)的旋转在电机每转一圈时将光电二极管和光电晶体管之间产生的红外光束切断七次。
图 10-4
实验冷却风扇电流负载测试设置
支撑光束源和检测器的两个板在作者的装置上固定到位,螺栓穿过板和电机框架,用蝶形螺母固定到位。项目 3 是翼形螺母紧固件的臂,其允许光束更容易对准。
作者的设置由一个更大的+/-12V、2 A 电源供电,该电源连接到安装在铝板金属底座上的试验板后部的端子板。
许多从过时或损坏的计算机设备中回收的风扇马达被用来驱动七叶片磁盘。如果用于这些练习的风扇没有七个叶片,在 DAQFactory 变量值表达式框中输入正确的叶片数,如图 10-7 所示。
电路原理图
在图 10-6 中,555 定时器被配置为非稳态模式,以产生连续的方波格式。定时器 IC 周期开始时,电容 C 放电,引脚 2 为低电平,输出引脚 3 为高电平。引脚 3 为高电平时,C 通过 R1 左侧和左侧二极管充电,直到引脚 6(阈值)达到 2/3 V+,此时引脚 3(输出)和引脚 7(放电)变为低电平。引脚 3 为低电平时,电容通过 R1 右侧和右侧二极管放电,直到 C 降至 1/3 V+以下,此时输出引脚 3 和放电引脚 7 变为高电平,循环重复。因此,C 从 R1 的左边充电,从右边放电。通过将充电和放电电阻之和保持在恒定值,输出信号波长也是恒定的,只有占空比改变。根据以下公式,输出频率是固定的
频率= 1.44/(R Varbl * C 定时
图 10-5 用图形显示了占空比的概念及其与频率或波长的关系,如红色箭头所示。对于驱动电机等功率控制应用,根据非常快的脉冲宽度快速打开和关闭电源的能力提供了一种在功率应用范围的高端控制电机速度的方法。然而,如果通过脉冲宽度变化被供电的负载工作在调节范围的低端,则较长的频率将为所施加的功率的控制提供较高程度的分辨率。步进电机中的加热电路和低转速可能需要更长的波长或更低的频率来提供足够的控制调节范围。
图 10-6
一种基于 555 定时器 IC 的带光电中断电路的电机控制器
图 10-5
占空比概念
软件
图 10-7 描述了用于显示风扇转速的变量值屏幕组件的属性主选项卡。将风扇盘每转的七个光束中断转换为风扇电机转速的计算结果输入到屏幕组件的表达式框中。子窗口的变量值组件选项卡中的各种条目生成弹出属性窗口上方的 RPM 显示框。
图 10-7
用于测量风扇电机转速的 DAQFactory 变量值组件配置
不需要任何脚本,因为计数器每秒钟被读取、重置一次并被输入到 RawInputCounts 通道中。作为 RawInputCounts[0]返回的值根据磁盘上的风扇叶片数进行了校正,并规范化为分钟。
观察
当电位计设置在中间位置时,双线风扇电机以大约 2950 rpm 的速度旋转,在电机停止之前,速度可以在 3300 到大约 100–150 rpm 之间变化。
在光学转速表的最初开发阶段,光电二极管-光电晶体管对产生的脉冲序列无法触发 LabJack 计数器,因此需要增加信号强度。使用图 10-6 的原理图中所示的两个分压器,从+12 伏电源中单独提取用于驱动光电二极管-光电晶体管对的功率。光电二极管分压器产生标称 5 V,光电晶体管分压器产生标称 6 V (5.91 V)。图 10-8 显示光束斩波器输出的示波器显示。
图 10-8
光束斩波器输出的示波器显示
从大型 CPU 芯片回收的三线冷却风扇电机取代了双线系统,高端风扇转速范围测量为 5,200 rpm。仔细调整后的风扇速度可以降低到 200 转/分的范围内,偶尔在电机停转前降低到 150 转/分的范围内。
芯片冷却风扇上的第三根电线通常是电机内置霍尔效应传感器的输出。霍尔效应传感器检测变化的磁场,第三根导线的输出产生一系列由旋转磁场产生的小尖峰信号,可用于测量电机的转速。
讨论
在本练习中,演示了为电机供电所需的较高电流的脉宽调制控制。基于非稳态配置 555 定时器芯片的有限脉冲宽度调制方案已在第章第八部分中讨论过,相对恒定的频率、可变占空比模式的功率传输现在正通过适当的二极管修改来使用,以扩展占空比范围,同时保持输出方波的频率或波长恒定。
在第八章、图 8-10 和图 10-6 中,可以看到电容器通过 R1 和 R2 的一部分充电,但仅通过 R2 放电,因此循环的充电部分只能降低到 R1 的值,而 R2 的值在循环的放电部分实际上可以降低到零。因此,占空比的可能变化由 R1 的值控制。
简而言之,我们可以看到,我们用于向 BLDC 电机供电的 PWM 技术将占空比从 100%降低到 0%。从 100%时的 12 V 到 50%占空比时的 6 V,再到 0%占空比时的 0 V,总的电压变化是低速旋转时不稳定行为的原因。在占空比较低的部分,12 V 电源没有在足够长的时间内提供平均功率来驱动电机,因此电机“失速”换句话说,在控制电位计的低设置下观察到的控制损失和失速是传递给电机的功率不足的结果。为了更好地控制低速电机运行,需要不同类型的电机和功率控制。
这个 555 PWM,光学转速电路,DAQFactory SCADA 软件显示系统是用一个回收的双线计算机塔冷却风扇开发的。第二个冷却风扇来自较新的大型 CPU 芯片,带有三线控制电路板连接器,被替换到试验板设置中进行比较。第三根电线通常为黄色(见图 10-4 ),用于监控内置于电机转子/定子部分的霍尔效应检测器的输出。霍尔效应传感器对变化的磁场作出响应,可用于指示电动机控制系统中磁铁相对于线圈的位置。
从作者的旧设备中回收的芯片冷却风扇电机是一个 2 1/4 平方英寸(5.7 厘米)的七叶片、12 伏、180 毫安的装置,当通过红/黑电源线供电时,其转速上限为 5200 转/分,转速在 200 到 150 转/分之间停止。
正如我们将在本研究的后面部分看到的,风扇电机的回收和再利用可以廉价地解决一些实验室程序中出现的实际问题。
步进电机
需要对电机功率进行电子控制的 BLDC 电机有两种形式:用于驱动风扇的连续负载型电机和称为步进电机的电机。BLDC 风扇电机在施加满电压的较高速度下产生最大功率,并随着转速降低而停止,因为电压/功率水平降低,而步进电机在不旋转时产生最大功率,并随着转速增加而损耗功率。步进电机的名字和用途源于它们以离散的“步长”移动或旋转的能力。通过控制电机的“步进”动作,可以实现精确的旋转定位和精确的低速旋转速率,以及显著的扭矩。
步进电机有多种形式,并根据用于装配电机的结构类型而具有不同的性能。对于那些需要更多细节的实验设备开发项目,在线(Jones on Stepper Motors)和机器人和工程的文献中都有许多好的教程。 1
出于电流控制练习的目的,我们将讨论和实验限制在称为双极永磁(PM)系统的电机类别。这些电机提供连续的低速旋转,在机器人、简单物理、化学和生物实验室程序中有明确的实际应用。用 555 定时器等 IC 可以相对容易地实现和控制连续旋转。单步执行和往复步进动作中的振荡需要额外的电机绕组知识、稍微高级的编程能力和专业硬件。在接下来的几个练习中提到了实现向下到单步点的受控旋转的方法,但是在这个简单的介绍性部分中不考虑振荡和分数圆周旋转动作。
为了使电机功率控制单元的装配保持简单、廉价和熟悉的形式,本练习中的电子功率控制电路同样基于可调节的、不稳定的 555 定时器电路脉冲产生,但是具有一些相对简单的附加数字逻辑电路。555 定时器 IC 可以由任何能够产生 PWM 控制中使用的低电压、可调占空比、方波脉冲序列的计算机或微控制器代替。
永磁(PM)步进电机的特点是转子轴不能像高速 BLDC 风扇电机那样自由旋转。PM 步进电机轴,当用手转动时,电机不与电源连接,在平衡或静止位置之间“嵌齿”或“步进”。完成一次完整旋转所需的步数表示步进电机旋转或振荡可被控制的“分辨率”或“精细度”。
步进电机的设计使得单个线圈绕组可以单独通电,从而产生内部电磁场,通过转子的旋转,可以与内部永磁磁场建立平衡位置。通过以编程的顺序激励线圈,可以使电机轴以编程确定的任何方式旋转。电磁线圈以顺序方式通电,以使电机轴沿任一方向平稳旋转的程序是这一简化练习的主要焦点。
步进电机的转速比传统的绕线式无刷电机慢得多,在低速时产生的扭矩也大得多。步进电机不会在它们的结构上移动大量的空气,因此会在它们的外部金属外壳周围聚集更多的热量。
实验的
五金器具
步进电机、速度控制、正向/反向开关和电机电源连接需要牢固安装。电机的电源连接必须能够互换单个电机线圈引线与电子控制电路输出功率晶体管的连接。集成电路可以组装在试验板上,以便于构建、原型开发和实验演示。图 10-10 中描述了一个实验性电机和硬件安装架,该安装架由易于获得的材料制成的手动工具组装而成,并在下文中进行了详细描述。
电路原理图
如上所述,为了大多数实验实验室或野外工作的目的,四线圈步进电机的控制电路只需要驱动电机平稳、可控地顺时针或逆时针低速旋转。因此,电源应该能够产生一系列连续的四个可调宽度的电流脉冲,这些电流脉冲具有足够的安培数,以使电机连续步进通过转子的完整旋转。
在将步进电机的电源连接分配给电子电源控制器的输出晶体管时,必须小心谨慎。如果五线电机中的中心抽头或六线电机中的抽头连接到正电源,则“第一”组抽头线圈的末端应连接到功率输出晶体管 1 和 2,第二组线圈末端连接到 3 和 4。如果电机旋转不平稳,对速度控制电位计的响应不符合预期,则依次交换第三个和第四个线圈连接,并重新测试电机。如果交换不能解决问题,则交换第一个和第二个线圈连接,重新测试,如果需要,颠倒第二个和第三个线圈连接。
图 10-9
基于 555 定时器 IC 的步进电机控制器
在图 10-9 中,一个由电位计控制的非稳态 555 定时器用于产生一个可调占空比脉冲序列。该脉冲序列用于触发(或供电)一对 D(数据)型触发器,该触发器已被配置为分频器,以产生所需的四个晶体管基极驱动信号序列,从而产生所需的高电流功率脉冲。数据触发器包含在单个 CMOS 4013、14 引脚 DIP 中,与 555 定时器芯片一起可以由更大电流的 12 V 电源供电。
步进电机可以由许多不同的线圈激励序列控制,这些序列可以产生高扭矩的单步或分步旋转、来回步进运动或平滑的连续旋转。低速、高扭矩、平稳的连续旋转是生物/化学实验室工作中应用最多的运动,即搅拌和泵送液体。为了通过四个输出晶体管实现电机线圈的正确排序,线圈的公共中心抽头连接到正电源。因此,线圈的通电包括将绕组端部接地,以使电流从中心抽头流向地面。因此,通过顺序地将绕组的端部接地,可以使电机旋转。用于本练习的电机上的接线颜色代码可能与图 10-9 中显示的不同。典型的电机在绕组末端有四个连接,或者一个公共连接到中心抽头,或者两个连接到中心抽头,从而形成五线和六线电机到电源的连接。
流经单个线圈的电流受到线圈自身电阻的限制。反转流经电机线圈的 DC 电流的方向将反转旋转方向。
电机旋转是通过顺序激励电机的内部绕组以产生磁场来实现的。瞬态电磁场与永久磁场相互作用,导致电机旋转。切断通电线圈的电流会产生反电动势,该反电动势由穿过电机内部绕组的崩溃磁场感应到线圈中。必须保护开关晶体管免受电机线圈高压反电动势的影响,否则它们的 PN 结会被损坏。led 已经放置在图 10-9 的开关装置的收集器中,以保护输出功率装置并提供单个线圈电流通道的视觉确认。
图 10-10
一种步进电机测试组件
项目 1 是安装在 1 英寸(1.2 厘米)的铝直角挤压框架上的科帕尔伊莱克特电机,其具有附加的黑色橡胶联轴器以移动和部分抑制共振频率。霍华德电机可以看到右边的安装适配器。项目 2 是用于电机绕组线和试验板接口的端子。第 3 项是 555 定时器芯片,数字 4 标记的是 4013 数据触发器芯片的位置。项目 5 是一排功率晶体管,项目 6 是“速度”控制电位计。项目 7 是旋转方向开关,项目 8 是大电流电源输入线。
软件
需要页面组件
用于切换 4013 D 触发器和驱动晶体管序列的脉冲序列也可用于创建电机转速的转速表显示(参见“讨论”)。作者的实验室有 12 V 永磁 75ω四相(线圈)步进电机,由 Howard Industries 制造,零件号 1-19-4200,每步 3.6 度,还有来自伊莱克特 Copal 的 SP-57B 电机,零件号 85086780,具有 36ω线圈电阻和 7.5 度步进旋转。因此,霍华德汽车公司完成一次完整的旋转需要 100 步,而科帕尔·伊莱克特汽车公司只需要 48 步。与风扇电机一样,转速可以通过 DAQFactory 变量值组件显示在屏幕上。速率的计算方法是,在 1 秒钟内计算施加于触发器逻辑的脉冲数,然后除以完整旋转所需的步数,并归一化为每分钟转数。不需要编写脚本,因为计算可以输入到显示为图 10-7 顶行的变量值屏幕组件的表达式框中。
观察
适度的步进速度导致平稳的旋转,而在低速下,单个的步变得清晰可见。使用发光二极管保护电机线圈免受反电动势的影响,还可以作为电机活动的指示灯。
转子空载的两种步进电机都通过一定的转速,这导致电机和安装结构“共振”3.6 度的霍华德电机在 113 转/分时开始振动,在 100 转/分时产生剧烈振动,在 92 转/分时发出非常大、令人讨厌的嗡嗡声,但在 85 转/分时运行平稳、安静。当来自三件式橡胶“三通”电机驱动连接的 in (0.6 cm)金属轴联轴器被添加到 Howard 电机轴上时,电机在 92 rpm 时显示出强度大大降低但仍然明显的高音调振动,并且在 60 rpm 时还显示出较低强度和较深音调的振动,以及在 30 rpm 时甚至更低音调和较低强度的共振。在共振“峰值”之间的任何转速下,电机都能平稳运转。
科帕尔伊莱克特电机空载转速范围为 60–300 转/分,在 89–90 转/分时会产生共振“震颤”。如果电机轴装有驱动连接器联轴器,共振速度会略微上升至 92–95 rpm 范围,电机会在 250 rpm 时完全停转。
在转子上没有联轴器的情况下,不可能用力挤压轴来停止电机,但是通过联轴器底座提供的额外杠杆作用,转子可以停止;当失速的转子脉动时,RPM 指示器仍然记录了 37 rpm 的等效脉冲率。随着转速的增加,扭矩的损失对于 Howard 马达来说是明显的,但是如果选择了超过 250 转/分的速度设置,则带有附加质量的联轴器的 Copal 伊莱克特会失速。
除了在实验室实验工作中进行低速混合和搅拌之外,步进电机还特别适合控制蠕动泵的输送速度。通过改变 555 非稳态配置中 RC 定时常数的值,利用图 10-9 中的 1.0 uF 电容和 27kω电阻,作者的 Copal 伊莱克特可以降低到 5–6 rpm;通过将电阻和电容都改为 296kω和 1 uF,电机以 1 rpm 的速度连续单步执行 48 次增量。
讨论
步进电机的灵活性和所需的低速性能是以控制电路的复杂性显著增加为代价实现的。可以使用本练习中使用的 D 型触发器或设计为移位寄存器的芯片(如 74LS194 和 CMOS 4035)来产生连续平滑电机旋转所需的连续电流脉冲。使用移位寄存器的详细信息可在网上 2 以及标称 IC 的印刷资料中找到。
步进电机的设计使得转子静止时产生最大扭矩。借助专为该练习设计的系统,转速可降至 1 rpm,在此转速下,电机的每一步只需 1/48 分钟或 1.25 秒。在 1 rpm 的慢速下,练习中使用的科帕伊莱克特电机已接近其最大可用扭矩。离散的、任意的时间步进或保持位置在机器人控制系统中是有用的,并且在动力控制系统中需要比在本部分练习中使用的电位计控制的 555 脉冲发生器所提供的更复杂的编程能力。
共振是步进电机的一个问题,并且电机在共振模式下的持续运行将通过增加旋转部件的机械退化率而大大降低电机的使用寿命。电机应在共振速度下运行,或者如果共振速度很重要,则可能需要在实验装置中内置特殊的机械安装技术、传动装置或旋转质量的变化,以处理系统共振。
电机轴的旋转速度可以通过使用 DAQ factory(lab jack 计数器通道的默认数据收集速率为 1 秒)进行电子测量,应用转子轴旋转一周所需的步数,并将显示的数据值调整为所需的时间单位。在屏幕“变量值”组件中将脉率转换成数字显示所需的公式可以输入到屏幕组件的公式框中,如图 10-7 所示。使用脉冲率来确定电机轴转速使用了电机在旋转时不“打滑”的隐含假设,这可能发生在电机转速范围的较高端,或者如果被驱动的“负载”显著增加。如果滑动是一个问题,或者要监控实验装置的实际旋转,那么应该在移动负载上使用光电断续器转速计方法。
步进电机在低速旋转或作为可旋转的定位代理时最有用,如果需要较低的旋转速度,可以增加 555 非稳态的时间常数,以扩大施加到触发器的脉冲之间的间隔。然而,转速越慢,电机旋转的“步进”作用越明显。
交流电源的控制
介绍
交流电(AC)源通常被称为“电源”在北美,市电或“家用电流”以标称的 120 伏峰峰值或 115 伏均方根(rms)60 赫兹提供,而在欧洲和世界其他地区,50 赫兹为 220 伏(rms)。大量的早期交流电源是由水力发电设备产生的,在水力发电设备中,水轮机旋转发电机,在这些相对较低的频率下产生正向和反向电流脉冲。
低压 DC 电流实际上是无害的,但“电源”电压和电流很容易引发火灾,将建筑物烧毁,造成严重的疼痛烧伤,并产生潜在的致命电击。
与高电压、高电流电能的致命性相一致,固态系统已经被开发出来,用于控制危险的高功率水平并将其与低电压控制电路隔离。
当与控制电路适当隔离时,交流电的优势在于能够直接为电机、照明设备和加热元件供电,而无需在使用前转换成 DC。主电源或交流电源从零到最大正向值循环,然后下降回零,在下降回零值之前上升到其最大反向值,从而完成循环。交流电源通常由晶闸管或四层 P-N-P-N 半导体元件控制,这些元件被制成两种类型的器件:可控硅整流器(SCR ),其导通可以在半个交流周期内进行调节;或者三端双向可控硅开关,可以在整个交流周期内控制导通。SCR 和三端双向可控硅开关的频率响应有限,因此主要用于 50–60Hz,但可用于高达 400 Hz 的交流电源频率。SCR 是具有“门”的多层二极管,该“门”允许二极管在其 AC 波形的正常导通部分的任何点从阻断模式切换到导通模式。通过将两个 SCR 并联但以相反的方向导通来形成硅双向开关或三端双向可控硅开关元件,可以根据施加到公共栅极的信号来控制交流电源波形的正向和反向周期。
如上所述,SCR 和三端双向可控硅开关都是二极管型器件,其栅极允许电路设计者控制交流电源周期通过器件本身以及负载的一个或两个方向。已经开发了使用光耦合到硅双向开关的光电二极管的 IC,以提供光隔离的手段,用于将栅极控制电路与流过硅开关器件的高能 AC 电源分开。光隔离器芯片有两种形式,包括传输控制信号以随机开始导通的器件和能够检测主电源信号过零点的器件。如果交流电源周期被随机“斩波”,可能会产生射频干扰(RFI)或更常见的电磁干扰(EMI),因此必须使用滤波器来抑制不需要的辐射。过零检测电路仅在过零点开启和关闭交流控制器件,可最大限度地减少显著射频干扰的产生。
随机相位或过零、光隔离集成电路由低压 DC 源供电,因此,如果标准数字逻辑电路与市电交流电源的三端双向可控硅开关控制一起使用,则必须提供所需的 DC 电源。可以使用电池或图 10-11 所示的简单专用 5 V 电源。
图 10-11
典型的交流至 5 伏 DC 输出电源
使用固态设备控制交流电源为研究人员提供了两种利用能量的方法。在第一种也是最简单的方法中,可以将全交流电压施加于负载,并且允许全电流流动的时间是可变的。第二种控制形式包括改变施加到负载上的电压。交流电压在 0 至 120 伏之间循环,固态开关器件可用于将循环中的任何部分(0 至 120 伏)施加到负载上,每秒 60 次。交流波形是一种正弦现象,其中电压幅度遵循正弦曲线。通过选择波形的一部分向负载施加电压,功率控制被称为“相角控制”电压变化有些复杂,只应在需要时使用。通过向负载施加全交流电压并改变全功率施加的时间,可以控制输送到负载的功率,并且当使用过零开关时,所产生的射频噪声大大降低并最小化。
第七章中介绍的脉冲宽度调制概念以及之前在电机控制练习中应用的概念通常应用于 DC 电力系统,但也可用于对交流电源进行初步粗略控制。
本部分练习中使用的光隔离器是随机和过零单元。
实验的
五金器具
一个 60 到 15 瓦的白炽灯泡是一个很好的交流电源控制练习的视觉演示负载。如图 10-12 所示,灯泡插座和用于固定电位计控制器的安装支架,同时作为三端双向可控硅开关安装的散热器,以及用于安装控制电路的端子板和试验板,都应固定在坚固的木头或金属底座上。回顾与交流电源电压和电流相关的危险,根据当地的电气建筑规范,所有输送电源的线路必须牢固固定、适当绝缘并覆盖。应小心切割输送主电源的电线上的绝缘层,以确保在端子板上的螺丝端子连接拧紧后,没有裸露的电线表面暴露出来。所有暴露的电线表面或承载主电源的焊接连接必须用液体塑料绝缘体、硅酮密封剂或热缩管进行绝缘。千万不要给任何有裸露导体输送电源电流的电路通电。
下文解释了作者实验装置中的数字名称(图 10-12 )。
项目 1 是一个 60 瓦灯泡,装在符合电气规范的插座中,正确安装在 1 英寸(1.8 厘米)高密度纤维板上。项目 2 是一个 250 VAC 端子板,配有批准的电线和插头(注意:为清晰起见,移除了盖子)。第 3 项是一个 400 VAC、6 A、BTA06 意法半导体,其散热器上有 TO-220 标签式三端双向可控硅开关,带有热收缩包裹导体,消除了暴露的导体表面。第 4 项是占空比控制电位计,第 5 项是演示练习中使用的光隔离三端双向可控硅开关控制 IC。数字 6 标记双极 555 定时器的位置,第 7 项是 9 V 电池供电。
最初用作视觉活性实验负载的 60 W 灯泡随后被表面更冷的 15 W 灯泡取代,后者通常用作家用电器的室内灯。
图 10-12
一种带白炽灯泡的交流电流控制测试装置
电路原理图
图 10-13 是一个典型的框图,显示了用三端双向可控硅开关和光隔离器控制交流电源的实验设置。
图 10-13
三端双向可控硅开关和光隔离器交流电流控制
在本练习中,可通过电位计二极管网络,从一个 555 定时器组装一个手动可控的 DC 脉冲源,该定时器配置为非稳态运行,其占空比由手动控制,如之前用于 90%范围的占空比变化。
图 10-13 中的电路图是在使用飞兆半导体 MOC 3022 的配置中绘制的,当 MOC 3022 内部 LED 照明电路中使用合适的限流电阻时,该 MOC 3022 可以由 5 V 电源或 9 V 电池供电。通过给低压脉冲发生电路通电,然后将交流电源线插入主电源,将受控的交流电源施加到负载电路。
图 10-13 的电路是用 MOC 3022 随机相位光隔离器三端双向可控硅开关驱动器绘制的。MOC 3061 过零装置的使用方法与图 10-14 所示的方法相同。
为了对将脉冲宽度变化技术应用于交流电流控制所产生的效果进行重要的可视化显示,图 10-12 、 10-13 和 10-14 中所示的电路应使用图 10-14 中的元件构建在原型试验板上。
组装并检查了在计时网络中使用 0.1 μF 电容改变 555 定时器的占空比对灯泡灯丝的影响后,应将电容改为 1.0 μF 单位,占空比再次在可用范围内变化。
虽然正在组装的电路仅用于演示,但在计时操作中使用低泄漏塑料薄膜或陶瓷电容是一种良好的做法。
图 10-14
一个 5 伏 555 定时器 IC 控制线路电源
软件
本练习不需要屏幕显示页面组件或脚本。
观察
使用能够测量频率(单位为 Hz)的手持式万用表测量 555 定时器电路方波的输出频率,发现 0.1 μF 电容的输出频率约为 75–124Hz,1.0 μF 单元的输出频率约为 8–12Hz。
初始设置组装在计时电路中具有 0.1 μF 的试验板上,并使用 MOC 3022,能够将灯的亮度从大约半功率改变到全开。在接近电位计旋转结束时的半开启功率设置下,灯闪烁,并且发现定时器输出在 75 和 82 Hz 之间不稳定地移动。
用一个大得多的 1.0 μF 单位替换定时电容,大大降低了 555 定时器脉冲频率范围,从 12.6 Hz 降至 7.6 Hz。当电位计完全旋转到 12.6 Hz 位置时,灯完全点亮且不闪烁。将电位计旋转到频率计读数为 7.8 Hz 的点,导致灯的亮度在视觉上平滑地降低,并且伴随着不稳定的灯闪烁的增加,直到在频率范围的低端,灯基本上关闭,但是以不稳定的、非常低水平的亮度闪烁。
虽然通过光隔离器的交流线电压的 PWM 开/关功率应用能够粗略地调节传递给灯的能量,但是该系统不能用作平滑的灯调光器,但是可以用于非照明应用。
讨论
世界上大多数电网输送由水、蒸汽或最近的风力涡轮机驱动的旋转发电机产生的能量。交流电能可以通过变压器转换成高电压形式进行远距离传输,然后再转换成高电流相对较低的电压形式供消费者使用。世界上大多数电网都在 50 或 60 赫兹的交流频率下运行。
对于在白炽灯或荧光灯照明、加热或转动电动机中的消耗性使用,交流电源通常可以在从配电网接收到的情况下进行最小的改变。
演示电路中应用于白炽灯泡负载的三端双向可控硅开关控制电源由电池供电的 555 定时器提供的脉冲速率控制。图 10-14 所示的 555 定时器非稳态运行模式中使用的定时网络能够允许占空比在大约 5%到 95%之间变化。因此,输送给负载的功率在相当大的范围内是可变的,但既不会完全关断也不会完全接通。
对于交流电流的无感或严格阻性使用这一简单目的,可以在基本电气计算公式中使用各种形式的均方根(rms)、峰峰值或平均值来描述交流,但必须一致使用,不能混用。如果欧姆定律中使用 rms,那么电压和电流的所有值都必须以 rms 为单位。
如果将灯替换为封闭绝缘容器内的加热元件,则产生的热量可以通过由电位计控制的开/关比来粗略调节,电位计反过来粗略调节温度。回想图 10-5 的图形数据,可以看出占空比的跨度是使用脉冲宽度变化可获得的控制跨度。
在本练习中,仅检查非常简单的交流电路分析和电磁干扰(EMI)。交流电子设备依赖于频率,随着频率的增加变得非常复杂。在任何涉及更高频率的实验工作中,如通信、感应加热、核磁或电子自旋共振光谱学,必须查阅文献以获得更具体和详细的信息。
如果要防止电路产生或拾取电磁干扰,电路必须完全封闭在接地金属盒中,与辐射完全隔离。如果电路从电网获取电力,那么为了安全起见,接地金属盒及其布线必须符合当地的电气建筑规范。
在迄今为止检查的大多数功率控制应用中,都涉及到 DC 电流,因此不需要考虑频率分量。然而,试图将 PWM 技术与 50 或 60 Hz 的固定频率的交流电力传输一起使用会立即对 PWM 方法的性质造成限制和约束。
在图 10-14 所示的电路图中,使用中心抽头电位计构建了一个电路,该电位计允许大约 50kω的电阻变化。对图 9-1 中展开的诺模图的检查表明,对于 50kω–0.1μF 的组合,实验者应该期望定时器在数百 Hz 范围内振荡。
回想一下,电位计二极管配置用于允许占空比的变化,而振荡频率只有微小的变化。如果功率通过一个三端双向可控硅开关器件输送到负载,该器件允许电流以 50 或 60 Hz 的振荡方式流动,那么在十分之一秒内,负载将经历五或六个功率周期。如果 555 定时器每秒钟开关光隔离器中的红外二极管数百次,三端双向可控硅开关将在相当长的时间内处于开启状态。
如果定时器脉冲序列的频率降低到 10 Hz,则占空比变化可以跨越通过三端双向可控硅开关振荡的电源的五个或六个电源周期。图 10-15 描绘了十分之一秒的时间跨度,其中五个电源周期标有在标称占空比设置下关闭红外二极管的点。
图 10-15
50 Hz 交流电源和 10 Hz 555 定时器 IC 输出占空比变化的第二图形表示的十分之一
如前所述,在较高的频率下,灯泡大部分时间都是明亮的,只能稍微变暗并不规则地闪烁。在大约低 10 倍的频率下,灯泡可以在其整个照明范围内变暗,但随着亮度降低到零而闪烁。
交流系统的 PWM 方法的进一步扩展用于功率控制应用,其中开/关切换的频率以秒和分钟来测量。长持续时间 PWM 功率控制通常用于加热控制应用中,在加热控制应用中,大的热物质在向加热元件施加功率和在被加热的物质中观察到温度升高之间表现出大的时滞。可以校准具有大时间延迟的交流供电加热系统,并建立 PWM 控制系统。
PWM 系统可用于精确控制输送给负载的功率,通过使用半导体,在一种称为相角控制的技术中,只将功率周期的一小部分传递给负载。然而,相角控制涉及在功率周期和 PWM 控制信号中建立和协调过零点,这超出了本练习的简单介绍性质。
使用 Raspberry Pi 和 Python 的当前控件
介绍
控制较大的 DC 电流
正如在前面的练习中已经指出的,RPi 从 GPIO 引脚提供任何相当大的电流的能力有限。然而,来自外部源的较高电流可以通过 RPi 阵列上的一些引脚来控制。
在线收集了当前控制硬件和电路的优秀摘要。 3
30–60 A 范围内的大电流 DC 可以用金属氧化物半导体场效应晶体管(MOSFETs)来控制,如 ON Semiconductor 或 Fairchild 的 FQP30N06L。FET 半导体通常需要强信号才能进入导通模式,因此使用 GPIO 阵列作为控制源的实验人员必须确保所选的 FET 晶体管与阵列引脚提供的 3.3 V 兼容。FQP30N06L 中的 L 表示器件的栅极与低压控制信号兼容。
利用图 10-16 中示意性描绘的达林顿对晶体管,可以实现适度的电流处理能力。
图 10-16
NPN 达林顿晶体管对
双极结型晶体管(BJT)是电流控制器件。通过器件的主电流在集电极和发射极之间流动。集电极和发射极之间的电流由小得多的基极电流控制。基极电流的放大使 BJT 成为一种敏感设备,能够放大来自太阳能电池和热电偶等传感器的非常微弱的信号。由达林顿晶体管对制造的器件能够与晶体管对的两个放大因子的乘积成比例地放大电流。双晶体管组件对基极电流也比用于制造器件的单晶体管敏感得多。
两个常见的达林顿对晶体管是 TIP 120 和 122,它们是在 TO-220 封装中作为标签式三端器件提供的。这两款器件分别能够在高达 60V 和 100 V 的电压下工作,并且通过适当的散热器,可以在 5 A 电流下工作。
实验的
非感性负载
为了演示 RPi 的 GPIO 阵列的电流控制技术,将使用 Python-tkinter GUI 屏幕滑块控件来管理照明汽车白炽灯的 12 V 电源。白炽灯纯粹是电阻性负载,因此不需要二极管来旁路由电感性负载(如电机线圈绕组)产生的破坏性电压尖峰。
将 TIP 122 达林顿对晶体管和电阻器安装在原型板上,并连接到 12 V 电池电源和汽车灯。电流控制演示电路如图 10-17 所示。
图 10-17
白炽灯电流控制电路
发现汽车灯在亮红色热源下从 6 V 电源汲取 1.25 A,对应于 4.80ω的灯丝电阻。因此,采用 12 V 电源时,全功率下的预期电流消耗应约为 2.5 A,完全在制造商为 TO-220 封装推荐的 5 A 容量范围内。
通过清单 10-1 创建的 Python-tkinter GUI 滑块控件如图 10-18 所示。
图 10-18
一个用于白炽灯负载的 Python-tkinter GUI 滑动功率控制器,带有 PWM 百分比的控制台值显示
图 10-19 是 tkinter 标尺或滑块功率控制图标的详细视图。作者已将箭头标题和百分比数字量标识符应用于图像,而文本和直接滑块数值由清单 10-1 中的适当条目创建。图 10-19 中增加了更多细节,以帮助描述“讨论”中 tkinter 图标的嵌入特征
图 10-19
RPi 的 t 中间刻度或滑动屏幕图标控制器。GPIO PWM 库函数
观察
图 10-19 中描述的滑动标尺微件在滑块位于槽的最左端时,灯丝完全关闭,滑块位于槽的最右端时,显示黄红色白炽度。灯丝强度的变化类似于第七章、图 7-7 和 7-9 中观察到的变化。
必须缓慢移动标尺或滑块控件,以便跟随变化的位置值。图标有一个更好的控制度,详见“讨论”部分。
讨论
承载数百毫安和数百安培电流的所有电路接线应使用焊接接头或紧密的机械连接进行正确连接,并进行绝缘处理以防止短路。即使在低电压下,短路产生的高电流放电也会产生非常高的热电弧,熔化金属,点燃可燃物,造成痛苦的烧伤。
Tkinter 是一个 Python 库,它实现了许多图标,用于为 RPi 组装一个活动的、定制的 SCADA GUI。tkinter 集合中的图标内置了大量功能。 4
在许多关于使用 tkinter 和其他 GUI 创建库的教程中,按钮、滑块、滚动条和其他屏幕图标设备等图标通常被称为“小部件”,并在编程代码中被赋予符号 w。
在清单 10-1 中,一个窗口的 tkinter 实例被创建并被设置为 master 的指示符。mainloop()函数扫描主窗口内部的鼠标激活事件。窗口小部件是在屏幕上创建的,或者在主窗口的主动扫描区域内用代码实例化,以完成手头的任务。如果窗口中的小部件必须与运行 tkinter 窗口的 Python 程序通信,则必须调用“回调”函数来与主窗口活动区域之外的代码通信。
简而言之,可以说每个微件都显示在监视器显示屏上的一个小窗口内。tkinter 程序扫描小窗口内的空间,寻找可能在帧内发生的鼠标点击“事件”。鼠标单击或鼠标按钮单击可用于拖动屏幕对象的边缘来调整它们的大小,激活它们的显示控件,以及最小化、最大化或退出程序。
与运行 tkinter 窗口和小部件的 Python 代码进行通信的能力允许实验者访问 RPi 串行端口,并如第十一章所述,将屏幕小部件连接到机电系统,以进行 SCADA 操作。
图 10-18 描绘了当清单 10-1 在回调函数中的诊断打印语句激活的情况下启动时获得的 RPi 屏幕。print 语句会将出现在滑块按钮索引线上的滑块索引的数字位置打印到 Python 控制台。在验证代码操作之后,或者在错误跟踪诊断不需要时,可以注释掉 scale 或 slider 程序回调函数中包含 print 语句的行。
图 10-19 有箭头标题,表示两个极限值,可以用鼠标光标和点击鼠标左键将指示器滑块拖动到这两个极限值。如果实验者需要从屏幕图标上精细设置 PWM 值,可以将索引按钮拖动到所需的近似位置,然后用光标提示调整到最终所需的位置。如果滑块索引按钮的值要增加一位数,光标尖端放置在滑块按钮和滑块刻度的 100%端之间的滑块槽的顶部边缘,并单击索引按钮位置的每个所需的一位数增量。如果从粗略定位开始,步进值将减少,光标尖端将置于步进滑块按钮和刻度的 0%端之间,并单击至所需的最终位置。
感性负载的功率控制
介绍
如图 10-17 所示的控制尖端 122 的 RPi 可用于控制输送到如图 10-4 所示的无刷 DC 电机的功率,但是电机中的线圈是感性负载,当包围线圈的磁场消失时,会产生电压尖峰形式的反电动势(EMF)。为防止反电动势损坏功率晶体管的 PN 结,应在负载上并联一个大小合适的二极管。
来自之前描述的标尺滑块 GUI 的 RPi PWM 信号可用于取代晶体管的 555 定时器-电位计功率控制,该晶体管用于调节输送至无刷 DC 电机的电流,从而控制电机的速度,如图 10-4 所示。也可以使用第八章中描述的电路设置 Python 断梁 RPM 监控器,以监控电机速度,从而根据 RPi-Python 组合复制 DAQFactory 练习。
然而,可以利用 RPi 和 Python 开发一个非常有用的不使用 PWM 的高电流传输演示,为步进电机供电,而不是从一个系统转换到另一个系统。
实验的
步进电机可以用一个 ULN2803 或 ULN2804、8 个达林顿对阵列 IC(3 CDN)从 RPi 进行廉价控制。ULN280n 由 8 个达林顿对功率晶体管阵列组成,采用 18 引脚双列直插式封装。(DIP)IC 阵列制造时已经安装了旁路二极管,用于驱动感性负载。
每个达林顿阵列晶体管对都是“集电极开路”配置,其中晶体管充当开/关开关。在集电极开路配置中,要供电的器件连接到电源的正极和晶体管的集电极开路。如图 10-20 所示,所有对的发射极共用一个公共连接,连接到 RPi GPIO 阵列的负极或“接地”端子以及 ULN280n IC 上的#9 引脚。在高电平和低电平之间切换 GPIO 引脚可以打开和关闭流经电机线圈和指示灯的电流。
正如之前多次练习中所建议的那样,使用 ULN2803 的电路应从基本的基本原理开始构建、测试和验证,直至完成最终工作的电子电源控制器配置。RPi 程序应开发为运行连接到 ULN280n 的一系列四个 led,然后用于连接到实际的步进电机并为其供电。
ULN280n IC 阵列的标准应用已经进行了修改,在用于控制步进电机的基本电路中增加了四个 led。四个 LED 阵列直观地帮助组装和验证这个复杂系统的逐步实施。
图 10-20
ULN2803 步进电机控制原理图
图 10-20 是一个连接的半示意图,其中 RPi GPIO 阵列通过功率控制 ic 连接到一个示例性的固定四线圈多永磁转子步进电机。
如上图所示,使用 Python 代码切换 GPIO 18,使 ULN2803 上的#1 引脚变为高电平,然后变为低电平,这将导致电流脉冲通过 A-E 固定线圈,从而产生磁场并点亮第一个二极管。通过线圈的瞬时电流脉冲将产生局部磁场,该磁场将使转子旋转到一个位置,在该位置,最近的相反符号的永久磁极与静止电机线圈中的瞬时磁极对齐。如果控制 GPIO 阵列的 Python 代码现在延迟一小段时间以允许转子旋转和磁场对准,然后在 GPIO 23 上重复逻辑高/低切换动作以使电流脉冲通过线圈 E-B 并点亮第二个二极管,则转子旋转的第二个“步骤”将发生。对于 GPIO 阵列引脚 24 和 25 来说,交替进行短时间延迟的切换动作的重复将在步进电机轴完成一次旋转时点亮第三和第四二极管。
简而言之,可以说,为了用图 10-20 的电路从 RPi GPIO 阵列控制步进电机,实验者必须汇编一个 Python 程序来顺序点亮 LED 阵列,如第三章以及本章末尾提供的清单 10-2 和 10-3 中所述。
图 10-21 描述了作者在原型板上的一个实验配置。
图 10-21
步进电机驱动 Python 程序的实验设置
在图 10-21 中,项目 1 是 3 型 Raspberry Pi,其适当的 GPIO 输入引脚连接到安装在原型板(项目 2)中央的 ULN2803 黑色 DIP 的前四个通道。电源控制 IC 输出引脚依次连接到原型板右上角的步进电机线束的四个输入引脚,同时四根额外的导线为电路板左上角的绿色 LED 阵列供电。箭头指向阵列中亮起的 LED。清单 10-2 显示为点亮指定 LED 的诊断实用程序,但实际上,使用图 10-20 的电路,它“单步执行”步进电机。清单 10-3 实际上是清单 10-2 的连续“单步执行”扩展,提供了 RPi GPIO 阵列对步进电机动作的基本控制。
项目 3 是带有拇指驱动器的 USB 集线器,项目 4 是低功率 28BYJ-48 齿轮步进电机。缓慢移动的电机轴有一个透明的带黑色笔标记线的磁带“标志”,以帮助显示轴的旋转运动。图中还显示了 GPIO 引脚位置和识别辅助,有助于在图 10-21 所示的复杂连接布线过程中进行引脚定位。
图 10-22 显示了一个非常便宜的(19 CDN)板载步进电机和 IC 驱动器。板上的 28BYJ-48 电机(项目 1)被爱好者广泛使用,可用于本练习。这种小型步进电机模块在大多数业余爱好或电子商店和在线供应商处都有售。项目 2 是电机线束,项目 3 是连接到 RPi 上 GPIO 阵列的输入引脚阵列。
图 10-22
市场上可买到的 SMT 步进电机驱动模块
虽然以非常简单的方式提出,步进电机控制不是一件小事;为了与这项工作的介绍性质保持一致,研究者可以参考书面和在线文献,以获得解释和计算机代码,用于处理本主题“讨论”部分中列出的步进电机控制的高级主题。
作者最初使用由四个碱性电池充电的四单元 D 电池电池组来提供 6 V 输出和 12,000 mAh 额定值,以便在接线验证和旋转测试期间为更大的步进电机供电。随后,齿轮步进电机(大约。1:64,实际上是 63.6839:1),通常用于机器人领域,可以从许多邮购渠道获得,在本练习的代码清单中,型号 28 byj-48(5 美元)用于开发电机驱动器程序。这款小型步进器可以采用 5–12V 电源供电,据报道,其转速为 15 rpm,DC 线圈电阻为 50ω,在 5 V 电压下,RPi 5 V 输出电流能力为 1/10 A,略低于 1 安培。该电机重 30 克,装在一个金属外壳中,带有两个螺丝安装凸耳,便于在实验装置上定位。
观察
为了与前面提到的原理保持一致,即一个工作的复杂系统是由更简单的、经过测试的和经过操作验证的子组件组装而成的,调用了以下程序。
在根据图 11-20 所示对 ULN2803、电机和 GPIO 阵列物理引脚连接进行初始配置后,列表 10-2 中的 LED 照明程序针对 18、23、24 和 25(阵列物理引脚 12、16、18 和 22;回想一下,数组是跨行计数的,而不是沿行计数的)。当每个 GPIO 连接被枚举时,相应的 LED 照明被目视确认,以验证 RPi GPIO 连接到适当的电机线圈引线。
在使用单个 led 进行系统验证后,使用 while 循环逐步通过四个线圈连接的清单 10-3 的第二个程序启动,与之前使用的 2 s 延迟相同,以验证电机线圈的正确顺序激活并确认步进电机的旋转。
2 秒的时间延迟会在高速齿轮电机中产生一个非常小的步进,但是将时间延迟减少到 1/8 秒会导致一个缓慢但明显的步进动作。
讨论
清单 10-2 和 10-3 是基本代码,旨在向调查人员演示步进电机如何工作和控制。为了扩展电机的应用,实验者可以通过以与旋转程序中相反的方式顺序激活线圈来反转电机的连续旋转。为了将步进电机驱动到位置服务,其中转子以顺时针或逆时针旋转步进固定数量的增量,可以将正确的线圈激励代码包含在 Python do 循环结构中。
除了这里介绍的简单代码之外,还有大量不同的动作可以编程到步进电机中,以实现正向和反向连续旋转和速度变化,这在大量关于步进电机的工程和机器人技术文献中有详细说明,应该针对更复杂的步进电机应用进行研究。
对于较大步进电机的应用或试验,例如从过时设备中回收的步进电机,可能需要单独的电源和较大的电流来实现电机旋转。GPIO 阵列可用于激活达林顿对晶体管或较重的 MOSFET 器件,但这两种类型的晶体管都必须用旁路二极管保护,以免电机线圈的反电动势损坏半导体。较大系统的较大电流消耗也可能需要使用半导体散热器。
交流电流控制
介绍
Python 和 RPi 可用于演示交流电能的有限 PWM 控制,使用与前面列出的 DAQFactory 程序相同的非常便宜的 IC。能够在 400 VAC 以上工作的多安培三端双向可控硅开关器件和光隔离器件可以从邮购处以不到 2 美元的价格买到。如图 10-12 所示,由 110 V 交流线路供电的白炽灯泡可用作 RPi 演示的电力负载,并配有这些便宜的组件。
为了安全和符合法律规定,当按照当地电气布线和建筑规范组装时,所有涉及线路电能的布线必须完全覆盖或绝缘。
实验的
图 10-23 描述了用于控制输送到白炽灯泡负载的交流电源的电路。BTA06 三端双向可控硅开关元件应安装在一个散热器上,该散热器足以让练习所选负载中使用的电流通过。用于家庭烹饪炉内部的小白炽灯可以以低至 15 瓦的小瓦特获得。
图 10-23
树莓 Pi 的 110 VAC 线路控制
图 10-23 的电路已经用 RPi 提供的脉宽调制控制功能取代了在 DAQFactory 线路功率控制练习中使用的图 10-13 的 555 定时器和电位器。GPIO 引脚具有足够的功率,不仅可以驱动 triac 栅极,还可以同时驱动可选的 LED“pilot”灯及其限流电阻,该限流电阻连接到 GPIO 21 引脚和地,紧接在保护 MOC 3061 输入的 220ω电阻之前。
如前所述,RPi。GPIO 库包含应用来自 GPIO 阵列的 PWM 信号所需的函数,该信号具有足够的功率来点亮 5 mm 的 LED。MOC 3061 中的内部 LED 用于打开三端双向可控硅开关,从而将功率传递给灯丝以点亮灯泡。
清单 10-1 是该功率控制的 PWM 程序。所列程序生成如图 10-18 和 10-19 所示的标尺或滑块小部件,占空比由标尺或滑块水平位置决定。
RPi。PWM 功率控制操作的 GPIO 库实现涉及许多可变参数的选择和设置。操作员必须选择 PWM 脉冲序列的频率和控制信号的初始占空比,并在下面列出的两行代码中手动插入选择。一旦选择被输入到代码中,程序就可以运行,并且滑动按钮将把定制配置的 PWM 功率控制信号应用到三端双向可控硅开关来控制灯的照明功率:
-
pwm = GPIO。引脚 18 上的 PWM(18,500) # PWM 信号设置为 500Hz
-
pwm.start(0) #占空比的初始初始值
为了演示 DC 和交流电源的 PWM 功率控制之间的差异,滑块或标度程序从 500 Hz 的典型默认 PWM 频率开始运行。当显示滑块图标时,灯应通过其 0–100%的功率循环进行循环,并注明功率控制技术实现的效果。
为了积累更多的数据来评估不同的 PWM 频率对提供给灯的 AC 功率的影响,可以手动将 PWM 信号的频率减半,从大约 64 Hz 的线路频率降低到 8 Hz,并且可以收集在灯泡照明上看到的影响的记录。
观察
表 10-2 列出了在各种标称 PWM 频率下,小型交流电源灯泡照明的半定量效果。
表 10-2
标称 PWM 频率下的 110 VAC 灯泡发光输出
|  |光度是用照相测光表半定量测量的,方法是在离照明灯泡固定距离处测量刻度盘指示器从其零位的位移。
讨论
使用 RPi 和 RPi 可以进行 PWM 频率变化研究。GPIO 库,因为 PWM 频率是软件控制的,而不是硬件控制的。
从表 10-2 中可以看出,PWM 频率和灯泡功率传输之间的最佳相关性出现在 16 至 32 Hz 之间。
当 PWM 信号波长等于或大于输送给负载的功率波长时,将 PWM 控制方法应用于 60 或 50 周期交流电源的尝试仅粗略有效。
如表 10-2 和图 10-15 中所示的数据所示,当 PWM 信号施加到一个波长比控制信号的波长短的交流电源时,PWM 功能可以对输送到负载的功率进行粗略控制。在低 PWM 值下的低强度灯闪烁可能是由控制信号在功率周期的最大点开启功率信号引起的。随着 PWM 信号长度的增加,功率信号能够循环通过一个或多个完整的周期;如实验中观察到的,当 PWM 信号接近 100%占空比时,闪烁或闪光消失。
在几乎所有进行的 PWM 频率变化实验中,研究人员将看到不同强度的光闪烁和从完全关闭到完全打开的明显闪烁。两个信号的相位角之间缺乏协调,这在非常灵敏的低质量灯丝中产生完全随机的闪烁和灯强度的循环脉冲。
如前所述,为了使 PWM 信号用于调制或控制仅一部分 AC 波形(称为相角控制)施加到负载,必须有复杂的附加电路来检测和协调电源波和 PWM 信号的过零点。交流电源的 PWM 控制的变体可以用于加热和其他长期应用,其中多个全功率周期以定时脉冲施加到负载。
代码列表
raspberry Pi–Python 代码
# Illuminate LEDS repeatedly in sequence
import RPi.GPIO as GPIO
import time
#
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
#
# A while loop repeats the cycle till terminated from the keyboard
#
while True:
# illuminate the first LED on GPIO 18
GPIO.setup(18, GPIO.OUT)
GPIO.output(18, GPIO.HIGH)
time.sleep(0.125)
GPIO.output(18, GPIO.LOW)
#
# illuminate the second LED on GPIO 23
GPIO.setup(23, GPIO.OUT)
GPIO.output(23, GPIO.HIGH)
time.sleep(0.125)
GPIO.output(23, GPIO.LOW)
# illuminate the third LED on GPIO 24
GPIO.setup(24, GPIO.OUT)
GPIO.output(24, GPIO.HIGH)
time.sleep(0.125)
GPIO.output(24, GPIO.LOW)
# illuminate the fourth LED on GPIO 25
GPIO.setup(25, GPIO.OUT)
GPIO.output(25, GPIO.HIGH)
time.sleep(0.125)
GPIO.output(25, GPIO.LOW)
Listing 10-3Continuous Stepper Motor Rotation
# Locate Physical Pins and GPIO Designations with LEDs
import RPi.GPIO as GPIO
import time
#
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
#
GPIO.setup(25, GPIO.OUT)
GPIO.output(25, GPIO.HIGH) # caution open collector - ULM2803
time.sleep(2)
GPIO.output(25, GPIO.LOW)
Listing 10-2RPi GPIO Pin Identification Utility
# A Horizontal Sliding Current Control Icon for the Raspberry Pi GPIO Array
# In RPI.GPIO pin 18 in BCM numbering or pin 12 in BOARD numbering has
# a PWM function of 0-100%. A slider is a standard Tkinter icon with a
# call back function to send slider position data 0 to 100% back to the python
# program running the RPI.GPIO library to adjust / alter the PWM values.
#
from tkinter import *
import RPi.GPIO as GPIO
import time
#
# library set up
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(18,GPIO.OUT)
pwm = GPIO.PWM(18, 500) # PWM signal on pin 18 set to 500Hz
pwm.start(0) # initial starting value for the duty cycle
#
# tkinter scale or slider control icon set up
#
# set up call back function to process the slider value. Print statement
# in callback is a development/error diagnostic utility
#
def Val(val): # callback function definition (outside Tk() window instance)
val = w.get() # the get function reads the slider value
print(val) # diagnostic utility comment out when not in use
pwm.ChangeDutyCycle(val) # RPI library function to alter the PWM power applied to the load
#
master = Tk() # window instance
master.title("Arduino in Science") # a title for the main window that holds the widget
w = Scale(master, from_=0, to=100, orient=HORIZONTAL, label="PWM Controller", command=Val) # creates widget, scale, text and names callback function
w.pack() # display scale or slider icon instance
#
mainloop() # main loop over window construct.
GPIO.cleanup() # reset GPIO pins to low.
Listing 10-1A Horizontal Sliding Current Control Icon
摘要
-
许多电子和实验科学操作都需要恒流源。
-
精确的 DC 电流控制可以用分立电子元件或集成电路以模拟形式实现,也可以用脉宽调制技术以数字形式实现。
-
正弦交流电流控制使用固态器件作为受控二极管,将正弦波功率分布的选定部分传递给负载,这由器件栅极激活决定。
-
需要精确的电流控制来调节典型负载,如连续的电机转速或激活离散的感应式步进电机动作。
-
练习演示了使用基本的 DC 脉宽调制技术作为交流电源控制所固有的问题,作为理解使用 PWM 变量控制市电电源的高级微控制器技术的前奏。
-
第十一章介绍了微控制器,并展示了其作为“智能”I/O 设备和传感器接口的功能。
十一、微控制器和串行通信
在 555 定时器、741 运算放大器和 Exar XR-2209 生产的 40 年间,用于数字计算的完整中央处理器(CPU)以微处理器的形式发展起来。在某些情况下,微处理器已经被用于创建更加灵活的控制系统,与传统的、分立的、基于多芯片的组件相比,其部件更少。
微处理器是 IC 芯片形式的计算机中央处理单元,而微控制器可以被认为是为嵌入式应用而设计的“微型计算机”。嵌入式系统通常是专用于特定任务的系统,可以用汇编语言或 C 语言编写以获得最佳速度和效率,并且可能具有有限的 I/O 能力。微控制器包含一个微处理器、存储器和可编程输入/输出外设,所有这些以印刷电路板格式或 IC 芯片形式组合在一起形成一个单元。
微处理器已经使用了许多年,Parallax 公司的“BASIC Stamp”和 Microchip 公司的 PIC 系列微处理器是高级爱好者和专家已经使用了许多年的两个系统。Stamp 和 PIC 系列微控制器都需要一些计算机科学和电子学的详细知识才能用于重要的应用。
不断改进的廉价微处理器芯片、软件的进步和互联网的发展已经导致了相当规模的物理计算爱好者“在线”社区的建立。对物理计算的兴趣已经增长到商业企业能够为快速增长的基于互联网的在线社区提供电路板和集成电路的程度。在论坛中,个体社区成员交换想法和信息,从而开发“开放源代码”系统,成员为该系统贡献书面代码软件和硬件配置开发,以改进和扩展现有系统的应用。
对使用 PC 来控制机电系统的物理计算的兴趣已经增长到在线、开源物理计算平台已经出现的程度,允许非工程人员或新的计算机实验人员开始创建和使用微控制器设备来控制机电系统。
来自意大利的名为 Arduino project 的开源平台已经被专门开发,其中 Atmel 系列微处理器芯片已经被用于构建一系列非常小且便宜的微控制器电路板。最初设想为非专业人员提供赋予设计、艺术和业余爱好者项目以交互能力的能力,该系统也已成为受过训练或有经验的机电开发人员和认真的实验研究人员的流行快速原型技术。
微控制器能够接受编码指令,处理这些指令,并操纵其片上输入和输出外设,以执行编码所需的任务。通常编写编码指令,然后在运行被称为“集成开发环境”(IDE)的程序的主机 PC 上汇编。IDE 中开发的无错误代码随后通过 USB 连接传输(或上传)到微控制器,以便实际执行。
Arduino 项目已经生产了几个电路板,这些电路板使用 ATmega 系列的 8 位微处理器芯片,加上时钟振荡器和附加电路,形成一个 USB 可访问的可编程微型计算机。基于处理编程语言,从 PC 托管的 IDE 对板进行编程。Arduino 和 Processing language 项目都是开源项目,有免费下载的软件、教程、项目和来自用户论坛的在线帮助。这些系统得到大量教科书、手册和商业可用硬件资源的完全支持,并通过在线论坛不断发展和完善。
在撰写本文时,Arduino 板的最新版本是 Uno 修订版 3。图 11-1 描述了一个原始的 Uno 板,它使用 Atmel AT328 (8 位)微处理器、闪存、SRAM(静态随机存取存储器)和 EEPROM(电可擦除可编程只读存储器),带有 16 MHz 时钟和串行端口 I/O。时钟速度提供微秒范围的时间分辨率,并且串行端口 I/O 可以由托管 DAQFactory 的 PC 的 COM(串行通信)端口访问。Uno 板的各种变体可与 Microchip PIC 32 位微处理器一起使用,该微处理器使用显著更高的时钟速度,具有极大扩展的 I/O 能力,并且与先前为 Arduino 8 位系统开发的代码完全兼容。(在图 11-1 中,注意主芯片的插座安装。较新的器件都是表面贴装技术(SMT)。)
微处理器具有灵活性,能够在输入和输出模式下提供编程定时功能,从而极大地改善传感器或运动控制设备的控制和读取。
开源平台的概念对实验科学非常有用。由各种 I/O 接口电路、晶体控制时钟和其它支持硬件支持的微处理器芯片,全部安装在一个非常小、便宜、容易获得的电路板上,可以作为“智能”外设。智能外设可以通过高速时间平均大大增加数据收集的灵敏度和范围,这通常可以揭示趋势,否则实验者可能会隐藏这些趋势。
图 11-1
Arduino Uno 微控制器
一个开放源码的概念也把许多来自不同学科的思想集中到一个单一的问题上,这给知识的发展带来的好处实际上是无法衡量的。在以下练习中,将展示微处理器读取传感器和控制运动设备的基本能力,作为实际实验科学测量中更复杂和更集中的应用的基础。在 Arduino 监测一个简单的光敏电阻(LDR)和主计算机之间建立通信联系后,将展示微处理器通过数字信号处理和增加计时能力等概念扩展实验探究功能的能力。
在为随后的实验测量和实验室设备组装做准备时,本章将开发双向串行通信的基础知识,包括简单的电光光检测和流式传感器数据的 PC 图形显示。
实验:微处理器到主机的通信——“上传”
在进行这个练习之前,读者应该熟悉微处理器及其应用的基础知识。如上所述,Arduino 项目是那些没有物理计算或电子背景的人开始学习和应用使用微处理器所需的基本技能的绝佳场所。开源网站上有足够的书籍、教程和项目描述,如果阅读或复习,将使实验研究人员能够轻松设计和创建微处理器控制的实验设置。
当前的练习主要涉及微处理器与运行或托管 DAQFactory SCADA 软件的 PC 之间的接口。一旦接口建立,微处理器的灵活性将显而易见,因为模数转换后的数据通过传感器和 PC 之间的串行连接流向 PC,用于辅助数据处理和非常灵活的图形数据显示。
要开始组装使用微处理器所需的实用程序,请从 Arduino 网站下载并展开所用操作系统的压缩文件(作者使用 Windows 和 RPi)。在安装 DAQFactory 软件的 PC 上安装 Uno 板的驱动程序。一旦 PC 能够看到 Uno 板,启动 Arduino 的 IDE 并运行“闪烁”测试软件(称为“草图”),以确保 PC 和 Uno 板之间的基本硬件-软件连接正常工作。
为了将数据从 Uno 板传输到 PC,并最终传输到 DAQFactory 软件进行图形显示,必须在两个计算设备的软件之间建立串行通信协议。通信协议必须双向操作,允许数据从 UNO 板“上传”到 PC 托管的 DAQFactory 软件,以及从 PC DAQFactory 软件“下载”指令和控制命令到 Uno。通过连接在 PC 和 Uno 板之间的 USB 进行双向数据传输。USB 的两个软件“末端”是两个系统中任何一个的通信(COM)端口。必须小心确保微控制器与 PC 上软件使用的正确 COM 端口通信。PC 通常有几个 COM 端口,而微控制器可能只有一个。
COM 端口通信包括在 PC 和外设之间来回传递 1 和 0。由于构成二进制信息的电脉冲是以线性方式一个接一个地传输和接收的,因此数据传输被称为串行传输。更复杂、更强大的 PC 称为主机/主控器,更小的专用微控制器称为客户机/从机。二进制信息从客户端/从设备上传到主机/主设备,并从主机/主设备下载到客户端/从设备。两台设备之间的所有二进制信息传输都是在一套称为“串行协议”的标准规则下进行的有许多标准串行协议在使用,如果需要,可以创建一个简单的特殊串行协议。DAQFactory 软件手册包含一整章关于串行通信的内容,可从 AzeoTech 网站获取单独的“串行/以太网通信指南”。通过使用建议的代码遵循这些指南,实验人员将能够创建和配置一个简单的协议,在用户协议的“接收”事件中接收流数据,如图 11-7 所示。一旦建立了接收流数据的能力,并使其作为命名的 DAQFactory 通道可用,DAQFactory 强大的统计和图形功能可用于显示输入的数据。DAQFactory 可以使用几种方法来实现串行通信,这些方法将在本文的后面部分进行开发。
由于 Uno 型板用作“智能”传感器或外设,并且可以在不同的固定位置工作站、移动无线膝上型电脑、笔记本电脑或其他计算设备之间移动,因此可能需要不同的 COM 端口来支持串行通信。COM 端口选择可以从 Arduino IDE 的工具菜单中进行管理,而 PC 上正在使用的 COM 端口的位置可以通过操作系统实用程序(如基于 Windows 的系统中的“设备管理器”)进行定位。
如第八章计数和计时所述,DAQFactory 软件的响应时间是有限的。如果 Arduino 板软件产生的数据流速度太快,DAQFactory 无法处理,那么主屏幕的光标响应就会变得缓慢而不稳定。要减慢过快的数据流,可以在 Arduino 草图的主循环中输入一个 delay 语句,以降低输出数据的传输速率。
当 DAQFactory 程序正在接收数据流时,Arduino IDE 监视器不会显示从 Uno 板到 PC 的数据流。到达 PC 的流可以用图形显示或“广播”以输入到电子表格如 Excel 中。
在本练习的主要部分,光敏电阻将用于为 Arduino Uno 板提供模拟、变化的输入,该输入将通过模数转换器(ADC)传递,然后串行传输到主机,通过 DAQFactory 软件实现几乎实时的图形显示。
五金器具
Arduino Uno 等 USB 微控制器板将用于监控 5 V 偏置分压器的输出,该分压器由光敏电阻和 10kω电阻组成。Arduino 在非常小的 2.75 英寸(7 厘米)× 2 英寸(5 厘米)× 0.5 英寸(1.2 厘米)电路板中提供 10 位模数转换值,而不是大得多的鲁棒的 4 英寸(10.3 厘米)× 6 英寸(15.2 厘米)× 1 英寸(2.45 厘米)LabJack。
电路原理图
图 11-2
一种 LDR 偏置电路
5 V 电源和接地来自 Arduino 板。模数转换通过将传感器和电阻的连接点连接到微控制器上的第一个模拟输入引脚 A0(零)来完成。模拟信号被转换成 1023 和 0 之间的数字值(1024 或 2 个 10 个数据点)。光敏电阻是一个安装在平面上的硫化镉半导体薄带,外面包着一层透明保护涂层。硫化镉光敏电阻可从许多地方、邮购或在线电子供应源获得,通常价格在 1-2 美元之间。
软件
用于本练习“上传”部分的软件分为两部分。第一个对 Arduino 板进行编程,第二个从 DAQFactory 程序提供“带状图记录器”图形输出显示。
清单 11-1 (所有清单都在本章末尾)提供了一份 Arduino 草图,该草图监控 LDR-10kω电阻结处的电压。(sketch 是 Arduino 文档名称,表示在 PC 或 RPi 上运行的集成开发环境(IDE)程序中汇编和验证的程序指令集,微控制器将遵循该指令集。)
实际上,Arduino 代码利用系统 10 位模数转换器的 A0 输入读取结电压值,然后每隔 500 ms 将该值打印到 Arduino COM 3 端口,并在每个值后提供换行指令。
为了使 DAQFactory 程序能够读取 Arduino 放置在 COM 3 端口上的数据,必须在 SCADA 绘图软件中创建并配置一个标识为“com_3”的端口。
作者的 DAQFactory 程序通道表配置了通道名“ArduinoStream ”,它被设置为从名为 com_3 的设备接收数据。com_3 端口已经根据图 11-3 到 11-8 中描述的以下选择顺序进行了命名、配置和设置。
最初,图 11-3 的快捷菜单用于启动配置过程。
图 11-3
快速设备配置
选择设备配置选项,调出图 11-4 的设备配置窗口。
图 11-4
设备配置窗口
在图 11-4 的窗口中,选择新的串口,调出以太网/串口设备端口配置窗口,如图 11-5 所示。
图 11-5
串行设备命名和配置选择
在图 11-5 的窗口中,新的 com 设备必须以 DAQFactory 可接受的名称命名(名称必须以字母开头,只能包含字母、数字和下划线)。在输入一个可接受的名称后,必须点击配置按钮来调出串口配置窗口,如图 11-6 所示。
图 11-6
串行端口配置窗口
出于本介绍性练习的目的,应接受默认选项,并使用保存按钮返回图 11-5 的以太网/串行设备窗口。
要完成 Arduino 串行端口和 DAQFactory 端口之间的连接,必须使用以太网/串行设备窗口的新建协议按钮指定串行通信方法或协议。
图 11-7 显示协议配置窗口。
图 11-7
协议配置窗口和“接收时”事件数据解析脚本
打开此窗口时,协议名称和文件名为空,I/O 类型和功能选项默认位于列表顶部。应该为协议指定一个名称,还必须为协议指定一个文件名和位置,该协议与其他普通文档分开存储。协议的单独存储允许共享协议,但也意味着如果主机计算机被更新或改变,协议必须被移动。
为了完成 Arduino 独立输出数据的连接,必须准备一个脚本,以便在每次完整的数据实体到达 com_3 端口时执行。该脚本被输入到 I/O 类型和功能列表的“接收时”选项中。完整的代码在清单 11-2 中。
一旦创建并保存了 com 端口协议,通道表视图可用于填写建立通道所需的条目,以接收流 Arduino 数据,如图 11-8 所示。
图 11-8
为流式数据创建通道
填写通道表视图中的条目并点击图 11-8 中的“应用”按钮后,通道应开始填充带有时间戳的数据。
通过展开工作区面板中的通道标题,并双击所需的通道以打开带有五个选项卡的通道详细信息窗口,可以以表格形式查看数据。选择表格选项卡将显示到达端口的时间戳和数据,最新值显示在顶部(参见第章第 6 ,图第 6-14 )。图形选项卡显示数据的图形。事件选项卡显示可应用于通道值数据操作的任何代码。主选项卡和详细信息选项卡包含许多命名的通道配置和选项。
需要页面组件
频道数据可以用二维或 2D 图形屏幕组件显示,该组件被扩展以尽可能多地使用屏幕。合适的显示如图 11-9 所示,时间轴设置为 5 分钟间隔(300 秒),接收到的 Arduino ADC 转换器值在图表输出上从 0 到 1000 单位缩放。
观察
在黑暗条件下,硫化镉光敏电阻器(LDR)的电阻为 75kω。在近距离非常强的白光 LED 照射下,LDR 电阻降至 250ω。在图 11-2 所示的电路配置中,随着照明从黑暗变为明亮,观察到的电压应该在大约 0.6 到 5 伏之间变化。Arduino ADC 是一款 10 位器件,可在 5 V 输入下将电压调整至 1024 单位或 4.9mV/分度。当 LDR 电阻几乎降至零时或者在非常强的光照条件下,实现 5 伏输入。组装 LDR 和固定电阻器的配置使得图形轨迹与落在检测器上的光的强度或亮度成比例地上升。因此,图形显示反映了眼睛所看到的,因为较高的照度朝向图形的顶部,而加深的黑暗导致轨迹减少。流式 Arduino 数据的简单绘图与照明不是线性的。(参见“讨论”)
落在安装了 Arduino 的 LDR 上的光线变化导致了图 11-9 中描述的响应变化。
图 11-9
Arduino 安装 LDR 上照明变化的图形记录
在图 11-9 中,当照射在 LDR 上时,一个强 LED 使第一部分的监控系统饱和。在第二部分中,头顶上的灯被关闭,在第三部分中,房间的灯被关闭,只留下漫射的窗户光来照亮探测器。第四部分是在黑暗的房间中,用放置在 LDR 上的盖子记录的,而第五部分是在房间灯重新打开时,测量进入盖子的光泄漏。第六部分显示了恢复正常室内照明时监视器的再现性。
尽管 LDR 的响应迅速而灵敏,但是落在 LDR 上的光的快速、闪烁阻碍导致显示滞后于照明变化。
讨论
微处理器上的时钟运行速度提供了更高的速度,以监控实验科学中一些快速发生的物理化学事件。然而,串行端口很容易以远高于计算机屏幕更新速率的速率接收数据。如果数据流入 PC 端口的速率太高,光标响应将会变慢,并且它实际上已经冻结了作者系统上的光标。如果数据速率太高,Arduino 可能需要在微处理器的主循环中使用延迟语句来降低速度。或者,数据可以以每秒 20 点(20 Hz)的速度流入 DAQFactory 程序和用于将数据存储在文件中的记录功能,以供以后检索和检查。LabJack 设备(U3 型)能够以高达 2500 样本/秒的数据速率进行全分辨率采样,以这些速率传输或采集的数据必须保存在内存中,以便在数据流关闭后进行处理。
光敏电阻是一层沉积在透明保护层下的半导体薄膜。落在探测器上的环境光导致电子从半导体材料中被撞出,当电流流过长条和连接到其两条引线的电路时,设备的电阻下降。对于作者的设置,暗电阻通常在 75kω或更高的范围内测量,而在强光照下,电阻可能降至仅数百ω。ldr 可以在兆欧范围内的暗电阻下获得,通常表现出与人眼相似的绿色光谱响应。
硫化镉是一种更常见、更便宜的光敏电阻。当图 11-2 所示的电路用于产生变化的电压并且信号连接到 Arduino 板的模拟输入引脚 A0 时,10 位模数转换器向 USB–COM 端口串行输出提供输入模拟信号的 1/1024 分辨率的数值。
在分压器电路中配置光敏电阻提供了一种非常简单的将光强转换成可测量电压的方法。然而,这种转换不是线性的。
在图 11-2 的电路中,LDR 已经连接到 5 V 电源,一个 10kω“下拉”电阻连接在 LDR 和地之间。在 LDR-下拉电阻结点观察到的模拟电压由分压器公式给出
V 模拟 = V +5 * ( R 下拉 /(R LDR + R 下拉))
典型的硫化镉 LDR 可以在 75kω的黑暗电阻和 1kω的明亮电阻之间变化。可以使用 Excel 电子表格,如图 11-10 所示,计算并显示 LDR 电阻变化的模拟电压输出图,如图 11-2 所示电路。
图 11-10
10kω–LDR 分压器电路的模拟输出
图 11-10 中的曲线是传感器位于电压源和接地下拉电阻之间时的典型曲线。该曲线将是相同的指数形状,但是对于传感器连接在地和到正电压电源的上拉电阻之间的电路来说,该曲线是反向的。由于光电管和电阻的制造差异,针对单个 LDR 和固定值电阻生成的每条曲线都会略有不同。前面的曲线最好用 y = -1.053ln(x) + 12.173 形式的对数曲线来表示,其方差为 R 2 = 0.9939。
如图 11-10 所示,模拟输出的曲率在两个区域减小,并开始趋向线性。在图的左上象限,LDR 电阻的微小变化会导致较高的模拟输出变化。在图的右下象限,LDR 电阻的大变化导致电压输出低值的小变化。实验者可能希望改变上拉或下拉电阻的值,并重新绘制之前所示的曲线,以找到在分压器配置中使用电阻式传感器的最佳条件。为了准确定量地使用分压器配置进行传感器测量,研究人员应在感兴趣的传感器范围内,用尽可能多的数据点校准手边的系统。
实验:主机到微处理器的通信-“下载”
介绍
在本练习的第一部分,数据由微处理器采集并传送到主机,用于实时图形显示、存档存储和可能产生的硬拷贝格式。
在本节中,将为 Arduino 配置主机和微处理器,以便通过串行端口接收来自主机的命令。主机将配置有一个 DAQFactory 控制屏幕,其中包含一些按钮,这些按钮将激活一个 LED,并启动一个非常简单的脚本来循环打开和关闭 LED 几次。
五金器具
为简单起见,将一个 LED 和一个适当的限流电阻插入原型板,并串联在 Arduino 板上的引脚 13 和地之间。
软件
作者的 DAQFactory 程序用于流 Arduino 数据的图形显示,提供了一个新的空白页,上面安装了几个按钮,如图 11-11 所示。
图 11-11
Arduino LED 控制按钮
标记为“开”和“关”的每个按钮都按照前面练习中的描述进行了配置。为简单起见,在下载指令需要简单动作的情况下,从动作列表中选择快速序列,如图 11-12 所示。
点击高亮显示的快速序列条目,图 11-13 的文本屏幕打开,可以输入所需的指令。
图 11-13
快速序列文本输入面板
图 11-12
快速序列选择
通过展开工作区中的“序列”菜单选项访问的正式编程序列列表中不显示“快速序列”。当列表 11-4 的阿尔杜伊诺代码接收到改变发光二极管状态的命令时,激活开/关按钮只需要将单行快速序列文本链接到发送“H”或“L”上。
可以用 DAQFactory 软件的正常方式准备一个程序序列,以实现一系列传输的开/关动作。根据清单 11-3 ,图 11-11 中最左边的按钮以 3 秒的间隔使 Arduino LED 闪烁 5 次。
观察
单击 DAQFactory 控制屏幕上的按钮时,Arduino 板上的 LED 会根据按钮标签激活或不激活。
讨论
在进行“下载”命令练习时,随着系统的建立,通信链路的每一部分都可以独立测试。通过加载和运行所需的草图,并从 Arduino 串行端口发送大写的 H 或 L,可以测试 LED 及其限流电阻。LED 将根据指示点亮和熄灭。
Arduino 上的串行端口显示必须关闭,以便 DAQFactory 程序可以使用该端口。一旦建立了从屏幕按钮代码到 Arduino 的连接,Arduino 在故障排除中使用端口的任何尝试都将从 Arduino 调用“端口正在使用”错误响应。Arduino 必须关闭并重新启动才能重新访问端口。
双向链接的 DAQFactory 侧和 Arduino 草图的正确操作也可以通过访问 DAQFactory 中的 com_3 监视器(早期程序中的 com3 ),并使用监视器窗口上的输入框和发送按钮手动发送大写 H 来确认。手动传输应激活 Arduino 安装的 LED,大写 L 的手动传输应关闭 LED。
两个端口监视器接受并传输 H/L,但是脚本命令必须使用引号将“H”和“L”指定为大写 ASCII 字符。
树莓派和 Arduino
在前面的练习中,Raspberry Pi 为其 GPIO 引脚数组使用了不同的库来与外界通信。这三个库各有不同的功能和限制,通过将 Arduino 微控制器用作智能外设,几乎可以消除这些功能和限制。
回想一下,Arduino 程序是在集成开发环境(IDE)中编写的,IDE 是从 Arduino 网站下载的程序。RPi 和 Arduino 在 USB 上通信,当下载 IDE 程序并使用终端条目安装时,不应连接该 USB
$ sudo apt-get install arduino
软件安装完成后,可以连接 USB 电缆,并从 IDE 的“工具”菜单中选择“电路板”,然后将类型设置为 Arduino Uno。应选择串行端口选项/ dev / ttyACM0 来完成配置过程。
RPi 和 Arduino 之间最安全、最简单的通信方式是通过 USB 连接。(参见“讨论”)
图 11-14 显示了安装 Arduino IDE 后 RPi 上的微控制器开始菜单。
图 11-14
RPi 上的 Arduino 菜单
检查图 11-14 中的菜单项可以发现,已经为 Arduino 微控制器编写了大量的开源代码,允许它连接硬件和软件。RPi 和 Arduino 之间的 USB 连接使得 RPi 的计算能力可以访问这些模拟和数字接口代码。
实验的
配置完成后,可以通过示例➤ Basic 选择闪烁程序,编译并上传到 Arduino,arduino 应每秒闪烁一次 led。
一旦 Arduino 上的 LED 按程序闪烁,连接两个设备的简单过程就完成并通过验证。
Arduino-RPi 连接的一个更重要的实验方面在于使用 RPi 作为绘图仪的计算能力,以图形方式显示由与 Arduino 接口的传感器收集的数据。可用的图形显示能力的一个简单例子可以通过绘制传感器所处环境中各种照明条件变化时来自 LDR 的信号来演示。
来自 Arduino 的 5v 电压用于偏置 LDR,其具有 5.49kω1%金属膜、下拉电阻,类似于图 11-2 中所示的电路。Arduino 活动代码与清单 11-1 中列出的代码基本相同,仅在程序注释中有微小变化,以适应不同的下拉电阻值。
来自 LDR 的信号可以通过连接到 Arduino 的 10 位 ADC 的 A0 输入端进行数字化,然后发送到微控制器的串行端口,供 Python 绘图程序查看或读取。列表 11-5 中列出了串行绘图仪代码。
观察
在配置 LDR 传感器和 Arduino 并在 RPi 上启动绘图程序后,记录了图 11-15 的轨迹。初次启动时,绘图仪程序会在显示器的右侧创建一个小窗口,交互式屏幕位于左侧。在交互式屏幕左侧看到的数字和字符的流式打印列是传输的字符和要绘制的数字。(参见“讨论”)
如第九章所述,matplotlib 绘图程序显示在活动显示左下角的一个按钮面板上,用于调用多种功能,如比例扩展、在帧中前进和后退,或保存适用于所显示数据类型的绘图。
回想一下,为了定量使用,必须校准图形标绘显示器上的定时标记。
图 11-16
由光敏电阻监测的室内照明强度变化的记录器跟踪
图 11-15
从 Arduino 绘制数据
在图 11-16 中,全屏选项按钮用于从图 11-15 中可见的小窗口展开记录的图。在图的最左边,追踪记录了 LDR 上的盖子下的房间光线泄漏。当房间的窗户被盖住,房间的灯光被关掉时,这种痕迹下降到几乎为零。大约一分半钟时的第一次大位移是由于打开了窗户。第二次上升是由于大约两分钟半时打开头顶上的白炽灯,接着是三分钟和四分钟时打开左侧和右侧的台灯。通过从大约一英寸(2.5 厘米)的距离在传感器上照射明亮的 LED 光来产生最大的痕迹值。借助显示屏左下角的工具按钮,可以访问自会话开始以来的确切相对时间以及跟踪的扩展部分。(参见第九章的。)
讨论
LED 的闪烁和闪烁程序经常被认为是物理计算上的“Hello World!”所有学生在学习一门新的计算机语言时运行的程序。实质上,RPi 正在向 Arduino 发送命令以供执行。为 Arduino 和 RPi 开发和发布的免费开源软件数量庞大,需要实验人员或研究人员不断审查,以跟上这一快速发展的技术。
由连接到 Arduino 并可能由 Arduino 控制的传感器产生的数据的绘图是通过稍微修改版本的 matplotlib 带状图记录器程序来完成的。为了使绘图仪能够读取串行端口,需要对原始代码稍加修改。串行端口传输涉及 1 和 0 的模式,必须将其转换为可传输的数据包,接收并解析回数值以便绘图。Arduino 是用 C 语言编程和操作的,而 RPi 使用 Python。作为绘图前配置解析代码和验证数据传输的辅助手段,串行连接的 Python 端接收到的字符会在控制台显示器上“按原样”打印出来,然后以适合识别的格式再次打印,作为绘图数据。当软件按预期运行时,可以很容易地注释掉打印语句。
在正常使用中,matplotlib 带状图记录器程序在 x、y 和时间轴缩放的注释中标识了变量和标签。轴标签也可能需要修改,以绘制和识别手边的数据。
RPi 和 Arduino 都能够使用 3.3 或 5 伏电源,对于一些使用直接串行通信的应用,可能需要电压电平调整电路以避免损坏电子元件。电平调节电路在几个出版的和在线的资源中有详细描述。 1
代码列表
# A Strip Chart Recorder for Raspberry Pi with Serial Input
# SCR Plotting of changing LDR data from room environment. LDR data from 5 volt
# 5.49 K 1% MFR pull-down cct on A0 and output on Arduino serial port for plotting
#
import matplotlib
import numpy as np
from matplotlib.lines import Line2D
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
import serial
#
#
#
class Scope:
def __init__(self, ax, maxt=10, dt=0.02):
"""maxt time width of display"""
self.ax = ax
self.dt = dt
self.maxt = maxt
self.tdata = [0]
self.ydata = [0]
self.line = Line2D(self.tdata, self.ydata)
self.ax.add_line(self.line)
self.ax.set_ylim(0.0, 1024.0) # y axis scale
self.ax.set_xlim(0, self.maxt)
def update(self, y):
lastt = self.tdata[-1]
if lastt > self.tdata[0] + self.maxt: # reset the arrays
self.tdata = [self.tdata[-1]]
self.ydata = [self.ydata[-1]]
self.ax.set_xlim(self.tdata[0], self.tdata[0] + self.maxt)
self.ax.figure.canvas.draw()
t = self.tdata[-1] + self.dt
self.tdata.append(t)
self.ydata.append(y)
self.line.set_data(self.tdata, self.ydata)
return self.line,
#
ser = serial.Serial("/dev/ttyACM0", 9600)
#
def rd_data():
while True:
inPutln = ser.readline()
print("inPutln = ", inPutln)
line = int(str(inPutln)[slice(2,-3)]) # convert arduino serial output stream
# to a Python string, parse out the numerical symbols and convert to a value
print(line)
yield (line)
fig = plt.figure()
fig.suptitle("The Scientyst's Ayde", fontsize = 12)
ax = fig.add_subplot(111)
ax.set_xlabel("Time")
ax.set_ylabel("Arduino LDR ADC Units")
scope = Scope(ax)
# uses rd_data() as a generator to produce data for the update func, the Arduino LDC
# value is read by the plotting code in 10 minute windows for the animated
# screen display. Software overhead limits response speed of display.
ani = animation.FuncAnimation(fig, scope.update, rd_data, interval=50,
blit=False)
plt.show()
Listing 11-5RPi-Python Code for Reading and Plotting Serial Port Data
//Simple DAQFactory - Arduino Serial Communications Program, Mar. 3, 2012
//An LED with an appropriate CLR is connected between pin 13 and ground on the Arduino
//The pgm below waits for an incoming character. If the character is an upper case H, the LED is
//turned on. If the character is an upper case L the LED is turned off. The state of the LED is
//thus determined by the nature of the character in the serial import buffer.
//
//
const int ledPin = 13; // the pin with the LED and CLR
int incomingByte; // a variable to hold the incoming data
//
//
void setup(){
Serial.begin(9600); // initialize communication
pinMode(ledPin, OUTPUT); // set the pin function
}
//
void loop() {
// check for incoming serial data
if (Serial.available() > 0) {
// read the last byte in the serial buffer
incomingByte = Serial.read();
// if the byte is H (ASCII 72), turn on the LED
if (incomingByte == 'H') {
digitalWrite(ledPin, HIGH);
}
// if character is an L (ASCII 76) turn the LED off
if (incomingByte == 'L') {
digitalWrite(ledPin, LOW);
}
}
}
Listing 11-4Arduino Code to Be Run on DAQFactory Screen Button Command
for (Private.Counter = 0, Counter < 5, Counter ++)
device.com_3.Write("H")
delay(3)
device.com_3.Write("L")
delay(3)
endfor
Listing 11-3DAQFactory Sequence Code for Writing to com_3 Port
if (strIn == Chr(13))
private string datain = ReadUntil(13)
Channel.AddValue(strDevice, 0, "Input", 0, StrToDouble(DataIn))
Endif
Listing 11-2DAQFactory “On Receive” Serial Port Parsing Script
// Single LDR readings with serial transmission for DAQFactory SCR display.
// The voltage at the junction of an LDR biased by +5 v and with a 10K ohm
// resistance to ground is monitored by the A0 input.
//
//
void setup()
{
// initialize serial port
Serial.begin(9600);
}
//
void loop()
{
// read A0
int val1 = analogRead(0);
// read A1
// print to serial port
Serial.println(val1);
//Serial.print(" ");
// delay
delay(500);
}
Listing 11-1Arduino Code
摘要
-
微控制器可以被视为 SCADA 架构中的智能接口,它使用串行端口通信在主机和远程过程之间上传和下载指令和数据。
-
微控制器可以大大增强主机的数字信号处理和 I/O 能力。
-
一个微控制器和一个单板计算机可以构成一个最便宜的 SCADA 实现的基础。
本书前十章中描述的微控制器、技术和软件的广泛应用将在本系列的下一篇文章 Arduino 科学测量中用于实施实验测定。
Footnotes 1①elinux.org/RPI_GPIO_Interface_Circuits
-
树莓派食谱第二版 Edn。Monk,O'Reilly Media Inc .,ISBN 978-1-491-93910-9
-
电子烹饪书,蒙克,奥赖利媒体公司,ISBN 978-1-491-95340-2