Visual Basic与数字输入输出适配卡控制


Visual Basic 与数字输入输出适配卡控制 目录表 第一章 工业计算机与控制适配卡………………………………….1 第二章 Visual Basic 6 及与适配卡的沟通…………………………41 第三章 数字输出入接线方式………………………………………78 第四章 数字输入输出卡……………………………………………92 第五章 计时计数卡…………………………………………………175 第六章 数字输入的轮询及中断的使用……………………………262 第七章 监控系统设计………………………………………………300 第八章 其它 DIO 适配卡及 ISA 接口……………………………...355 第一章 工业计算机与控制适配卡 计算机辅助自动化在近几年来有相当大的进展,相当多的工业 控制采用了 PC-Based 作为发展的方向,除了在硬件发展上的进步 是有目共睹外,软件上的发展也是一日千里。Wintel(Windows+Intel) 架构是现在的大多数计算机所采用的组合,非常多的系统就以这个 架构为基础实现了计算机辅助自动化的目的,本书基于此架构,说 明在此架构下使用 Visual Basic 及数种的延伸适配卡,扩充计算机 功能而达到控制的要求。本章先就工业计算机及扩充适配卡作一个 说明。 1-1 系统的组成 当我们确定要建立一个计算机辅助系统时,一定需要一部计算 机,个人计算机是相当普遍的设备,家庭或个人均经常使用桌上型 计算机处理相关的事宜;工业上使用的计算机和个人的家用计算机 有一些不一样,由于工业环境可能不会太好(高温、高噪音、高振 动、高湿度、高粉尘…),也因此其设计上的要求会比一般的家用 计算机来得高,本书使用工业计算机作为说明的平台,本节主要介 绍工业计算机的组装过程,以让读者了解一部工业计算机系统的产 生。 1-1-1 工业计算机 工业级计算机和一般家用型的计算机有一些不一样,价格也比 较高一些,主要是考虑到工业使用的场合中,噪声较大,环境也不 是像家用计算机所处的环境那么好,因此工业计算机所使用的零组 件通常就必须经过一些测试,必须保证其适用的环境温度、湿度及 耐振能力等等参数在一定的范围内,以便能适应未来在工业环境下 的运作。 市面上针对工业的应用所产生的相关零组件很多,一个计算机 的组成中,也和一般的家用计算机般需要一个机壳,空的机壳内部 如图 1-1-1 所示(以威达电 Rack-300A 为例)。 第 1 页,共 386 页 图 1-1-1 工业计算机的空机壳 由图 1-1-1,除了电源供应器有一个散热器外,一般在工业计 算机机壳中还另外设计有散热风扇,达到充份散热的目的,例如图 1-1-1 中的左下方就还有一个散热风扇;部分高负载的场合中甚至 还会采用更多的风扇在机壳中,以期能排出更多的热能,达到降低 机壳内部温度的目的,避免内部因高温而影响硬件正常的运作。温 度是使得设备运作失效的常见因素,而工业环境的温度也可能比较 高,再加上控制用的相关适配卡若使用能量较高者,也会产生较大 的热能,因此散热在工业级计算机中相当重要。 工业计算机中的主机板也和一般认知的主机板不一样,如图 1-1-1 中所示的空机箱,中间的空白处是装设底板用的,所谓的底 板也称之为背板(BackPlane),如图 1-1-2 所示(威达电 PCI-13SD) 第 2 页,共 386 页图 1-1-2 工业计算机用背板 一部工业机壳就装有一片背板(通常依需求而选择不同型号的 背板),而不同的背板设计会使得一个工业机壳含有不同数量的系 统,每个系统有一片主机板含在其中,例如图 1-1-2 中的背板就被 分为「SYSEM-1」、「SYSEM-2」二个系统,分列在背板上的左右 二边,二边的构造非常类似,而右边的系统则多了一个 PCI 插槽。 当然也是有单一系统的主机板,图 1-1-3 就是其中的一种(威达电 PX-1455)。 图 1-1-3 单一系统的工业计算机背板 如果在一个背板上安装的系统数较多,由于耗电量增加,可能 会使得系统的电力不够,在每天 24 小时的操作,极有可能使得系 统的电力负荷不了而导致电源供应器损坏;所以在多系统共处一机 的情形下,笔者建议使用的电源供应器的 Power 就要大一些,一般 使用的电源供应器如果是 250W 的话,在多系统共处于一个机壳时, 可能就要采用到 350W 的电源供应器,以便系统的所需的能量可以 获得支持。 一般的工业用背板中的插槽均含有 PCI 及 ISA 二种接口,适用 在不同的卡片上;ISA 接口的使用已经有相当的一段时间了,现今 还是有不少的卡片使用 ISA 接口作为资料的传输接口;PCI 接口是 继 ISA 接口而兴起,是由 INTEL 制定其规格,现在已经有愈来愈 多的卡片是支持 PCI 接口了,此接口的传输速度比起 ISA 接口要快 上很多,多数在需要极快的传输速度的场合是以 PCI 接口较能达到 第 3 页,共 386 页要求。PCI 适配卡是本书在后面即将说明的重点,第八章则会针对 ISA 适配卡作重点式的介绍。 在图 1-1-2 及图 1-1-3 工业计算机使用的背板右边有二个用来接 电源的地方,在上面的一个是 AT 电源,而下面的则是 ATX 的电源, 安装时只要选择其中的一种来安装就可以了。由于选用的电源可能 会有不同,因此背板在制作也考虑到使用者可能会选用不同型式的 电源供应器,故设计了二种规格,使得选择的电源供应器均可使用 此背板上。 有了工业计算机的外壳及底板后,当然还需要主机板了,一般 的家用计算机也会有主机板,但是工业级主机板是将很多的功能集 合在一起,并且将 Slot 也作在一起,形成了主机板和扩充槽在一起 的情形;工业用的主机板一般被称为单板主机板,和扩充用的背板 是分开的,主机板的外观如图 1-1-4 所示(威达电 3702EV)。 图 1-1-4 单板主机板 我们必须特别注意到图 1-1-4 中的主机板的接口,各位读者是 否发现其接口接点有一点不一样呢?在图中主机板下方是其接口接 脚的位置(俗称金手指),其接脚的右方看起来是 ISA 的接口,但是 左方看起来却又是 PCI 接口,到底它是 ISA 还是 PCI 呢 ?二个都是, 因为主机板含有这二种接口的功能在其中。由于二种接口都有,因 此我们在选择背板时,也要选择含有这二种接口在一起的插槽才 行,例如图 1-1-2 及图 1-1-3 的背板就含有此种接口插槽。工业上 的需求其实是多样化的,我们甚至也可以看到既不是 ISA 接口,也 不是 PCI 适配卡插槽的主机板哦! 现在的个人计算机设计朝向 ALL-IN-ONE 的设计,也就是将所 有可能的功能都作在一个主机板中,使用者不需要另外再去连接其 它的接口,即可很快地组合及使用一部计算机;同样的,现在的工 业计算机主机板也是集合了许多的功能在其中,例如屏幕显示卡、 第 4 页,共 386 页网络卡、IO 卡、USB…的功能芯片均已含在其中,这样的设计也使 得单板主机板可以在最小的空间就可以拥有最大最多的功能,表 1-1-1 是威达电所出品的单板主机板 ROCKY 3705EV 中的规格 表 1-1-1 ROCKY-3705EV 主机板的规格 • CPU : Socket-370 base 66/100/133MHz FSB Celeron™ / Pentium® III up to 1GHz • BIOS : Award PnP BIOS • System Chipset : SiS630E • System Memory : Two 168-pin DIMM sockets up to 1GB SDRAM • Display : o Integrated in SiS630E Chipset o Bus : AGP 66MHz o V-RAM : Share with system memory up to 64MB RAM o Resolution : 1600 x 1200 (12-bit colors) o Connector : DB-15 for CRT display • Ethernet : 10/100Mbps by SiS630E Chipset Support WOL function • SSD : Support CompactFlash Type II Socket • Audio : AC'97 compliant Audio CODEC • I/O : o 2 x RS-232 serial port (16C550 UARTs compatible) o 1 x parallel port (SPP/EPP/ECP) o 2 x USB 1.1 (Pin header) o 1 x IrDA (SIR) o 1 x FDD port, support 1.44MB, 2.88MB, 3-mode function o 2 x ATA-66 IDE channels • WDT : software programmable, support 1 ~ 255 sec. system reset • Support ATX power control function • Power Consumption : 5V@6A, 12V@70mA (Pentium® III 500MHz CPU and 512MB SDRAM) • Operating Temp : 0~60oC (CPU need cooler) • Relative Humidity : 5~95%, non-condensing • GW : 900g 由表 1-1-1 也就可以看得出来,如果我们将工业机壳和背板装 好,再加上单板主机板,再配上其它的外围(软盘机、硬盘机、光 驱、键盘、鼠标…),就可以形成一部计算机了,工业级的计算机 在组装的过程中除了这些配备稍微不一样之外,其它的外围其实也 和一般的家用计算机相去不远(除了工厂现场常用的触摸式屏幕)。 工业计算机的主要诉求是稳定,发展上不是以速度为最高 指导原则,这和家用的个人计算机 CPU 动不动就升级是不太一样 的;所以在工业计算机的 CPU 规格上,通常要比现行的桌上型计 算机的速度慢一点,但均符合工业使用的需求。 第 5 页,共 386 页1-1-2 操作系统 利用上一小节的各配备,一个基本的计算机主机构架就差不多 要浮现了,如果再上硬盘机、软盘机、光驱,将每个设备的排线都 装上固定处的话,一个主机就可以完成,其外观如图 1-1-5 所示 图 1-1-5 只含有一片主机板的系统 此时这部计算机和一般我们家用的计算机除了外观上不一样 之外,其所能执行的功能是完全一样的。 使用单板主机板装入工业机壳中,加上其它的硬件配备后,形 成了一个最基本的工业计算机;接下来要作的事情就是安装操作系 统,此操作系统的安装和一般的家用计算机是一样的,这部份就请 读者参考一般的计算机书籍执行操作系统的安装程序,本书不再叙 述;在安装的过程中,会需要由原厂提供的系统光盘片,该系统光 盘片中含有该片主机板所需的驱动程序,而操作系统在安装的过程 中经常需要这些驱动程序才能顺利地将各个装置安装到系统中,这 些程序中包含了显示芯片、音效芯片、网络芯片…,例如笔者使用 的是威达电所出产的主机板,那么就要使用其提供的光盘片,所有 其所出品的主机板上所使用的芯片驱动程序就全部含在其中(亦适 用在不同的操作系统)。 将操作系统安装完成后,我们可以在「开始」\「设定」\「控 制台」中的系统图标中查看我们所安装的各个装置;在顺利安装的 第 6 页,共 386 页情形下,应会出现和一般的家用计算机类似的一些设定出来,如图 1-1-6 所示,在尚未加入其它的控制适配卡之前,其内容基本上和 一般的家用计算机没什么二样。 图 1-1-6 工业计算机中的系统内容 1-1-3 卡片的安装 经由上述的各个步骤,一个操作系统的工业级计算机已经出来 了,但是通常工业级的计算机中还会有其它的配备,为的是执行基 本计算机配备所含功能以外的其它功能,例如本书所介绍的数字输 出入功能,要含有此种额外的数字输出入功能就必须在计算机系统 中装入数字输出入卡片。 因为使用场合的不同,额外加入的卡片也有各种不同的接口, 以现在的情形看来,PCI 接口的卡片应是主流,而由于 ISA 接口的 存在时间也相当久远了,因此也有一定的市场在,本书所使用的是 数字输出入板是 PCI 接口,以下就介绍此种 PCI 接口的板子如何放 入我们的计算机系统中,第八章再简要说明 ISA 适配卡。 我们以 PIO-D56(泓格科技出品)为例予以说明,取出 PIO-D56, 其外观如图 1-1-7 所示,由此板也可知道其采用的是 PCI 接口的插 槽,只要使用的是 PCI 接口的卡片,其安装步骤均是一样的。 第 7 页,共 386 页 图 1-1-7 PIO-D56 数字输出入适配卡 通常背板上的插槽中,ISA 所使用的插槽是黑色的,而 PCI 的 插槽则是白色的,由于这二种接口的差异还算挺大的,一般是不容 易搞混才对(细心的读者也会发现 ISA 和 PCI 适配卡的零件面是左 右相反)。由背板上选择一个空白的 PCI 插槽,准备将此适配卡插 入此槽中,步骤程序如下: 1、 将计算机关闭,电源拔掉。 2、 找一个空白的插槽(白色的),将卡片中的文件板部份靠近工 业机壳后方的排洞,另一端则向机壳内部。 3、 将卡片的金手指接口部份接近插槽,确定前后方均位置适当 后,将卡片由上往下压入插槽中。 4、 如果上一步骤不是很好作的话,可以选择前后一端先使其进 入插槽中,一端稍微进入后,再使力将另一端插入,双手同 时对卡片使力,即可完全地将卡片置入卡槽中。务必确定插 入,并且查看档板是否已经完全地平贴在后方的机板上。 5、 锁上固定片。一般卡片均会附有一片固定片,该固定片可以 锁在计算机后的机板上,螺丝使用的是计算机机壳所附的螺 丝即可。 透过上述的几个步骤,适配卡便可以固定在机壳上了,而完成 的样子如图 1-1-8 所示。 第 8 页,共 386 页 图 1-1-8 接口安装完成图 有些卡片会有一些接头是在卡片上,大部份是由机壳后面延伸 出来,如果在卡片上有其它的接座的话,必须使用额外的排线将其 延伸出来;例如上述的卡片,在其后方就二个额外的接座(分别用 来作数字输入和数字输出),一般的应用都会使用所谓的延伸板或 端子板(也称 Daughter Board/Termial Board),延伸板会附有排线将 接脚讯号拉出,使用时必须注意其引脚编号。一般的排线上均有防 错装置,反方向是插不进去的,而正确放入后就会如图 1-1-9 所示。 图 1-1-9 安装 IO 延伸排线的完成图 不同型号卡片的接座数目和型式均不相同,这得视读者们所使 用的适配卡而定。如图 1-1-9 中的延伸排线延伸出去后,通常会再 加上一些外部的测试板或装置,在上述的图片中,延伸出去的排线 是用来延伸数字输出入讯号用的,因此也经常需要加上一些外部的 装置来达到数字输出入的功能,而厂商也会作出一些延伸板装置特 别符合适配卡的需求,图 1-1-10 就是一个使用的例子 第 9 页,共 386 页 图 1-1-10 延伸出来后再接上的端子板 完成以上的各个步骤后,就可以将工业机壳盖上,并且重新开 启电源,让计算机重新激活。 1-1-4 适配卡的驱动程序 以上的步骤可以将适配卡成功地安装到计算机的插槽中,硬件 的部份已经完成了;接下来还必须让操作系统知道此适配卡的存 在。在上一小节的重新开启计算机的步骤后,操作系统会在激活时 告知侦测到一个装置(由于是 PCI 接口芯片的关系),为了说明的方 便,请读者暂时将安装的程序取消,我们要重头来作一次,看看一 个硬件是如何一步一步地被操作系统识别的。 现在的操作系统相当地聪明,只要有新的东西出现在计算机的 插槽中,在计算机中马上就会出现相关的画面,准备安装驱动程序 了 —这是我们使用计算机安装新的设备时经常会经历到的事情。 当硬件安装完成后,在操作系统中还需要有软件的帮忙才能使 得该硬件可以和操作系统一起运作,而不会出问题;类似于其它的 硬件(打印机、显示卡、声卡…),控制适配卡也需要驱动程序,让 操作系统可以辨认出这个硬件,才能在窗口操作系统中看到这个硬 件的存在。 所谓的驱动程序(Driver)就是为了让操作系统知道该硬件存在 所需要的一个档案。驱动程序中含有这个被新增硬件的相关信息, 以及当操作系统欲对此硬件进行操作(通常是输出入的动作)所需要 第 10 页,共 386 页的程序代码或是操作方式的指引。除了标准设备的驱动程序(例如 软盘机和硬盘机)会由操作系统执行之外,额外设备的驱动均会由 厂商提供驱动程序供操作系统识别和操作,驱动程序所含的程序会 由操作系统呼叫而执行相关的存取作业。 请选择「开启」\「控制台」中的「加入新的硬件」项目,执行 它,如图 1-1-11 所示。 图 1-1-11 选择硬件的新增向导 使用硬件向导后的下几个步骤如图 1-1-12 所示,图中的第二个 步骤是由系统自动为我们找寻装置,只需要一小段时间,硬件向导 便会找到名为”PCI Multimedia Device”的装置,这是笔者所使用的 数字输出入卡片所出现的名称,不同的厂商及不同的卡片可能会出 现不同的名称,另外也有可能出现类似”PCI Communication Device” 或是”PCI NetWork Device”的名称,虽然出现这种名称,但不见得 就是执行该名称所表示的功能,在很多的情形下是使用此名称而执 行其它的 IO 功能,特别是在工业用的控制卡片上,实际出现的名 称取决于厂商使用 PCI 控制芯片时所使用的子功能名称。 第 11 页,共 386 页 图 1-1-12 自动搜寻并找到 PCI 的卡片 装置被找到后,其驱动程序的安装有自动及手动二种方式,一 般常见的设备都可以使用操作系统所提供的预设驱动程序而执行 安装的功能,这种预设的方式不太适合工业上的应用,通常在工业 场合中所使用的卡片都不会有预设的驱动程序可用,必须依赖由厂 商所提供的驱动程序。 在接下来的安装过程中,选择的驱动程序必须指定由原厂所提 供的驱动程序,以本章的卡片为例,我们就必须选择光盘片中该卡 片的驱动程序所在的位置,安装向导将会解读该位置中的安装信息 (*.inf),并显示在画面中,进而遵从该信息的指示将必要的档案复 制到计算机,其程序如图 1-1-13 所示。笔者使用 Windows98 的操 作系统,因此驱动程序采用的也是 98,如果读者使用的是其它的操 作系统,则应选择其它适合档案夹中的驱动程序,以符合操作系统 的要求(如 Windows 2000 或 Windows NT)。 第 12 页,共 386 页 图 1-1-13 适配卡的驱动程序安装 在上图中按「下一步」后,就会开始进行程序的安装工作,最 后也会告知已经安装完成,如图 1-1-14 所示。 图 1-1-14 驱动程序已经安装完成 经由上述的种种繁琐的步骤后,终于将适配卡的驱动程序安装 到操作系统中,而检查安装是否正确的方法则是由「开始」\「控制 第 13 页,共 386 页台」\「系统」中查看各个装置的情形,如果正确的话,应该出现像 图 1-1-15 的情形;由图 1-1-15 可以看出 DAQCard 已经出现在系统 中,而其子项目则出现了 PIO DI/DO Series Card 的信息,表示有一 此系列的卡片被安装到系统中。 图 1-1-15 检视卡片是否成功被安装 虽然笔者主要使用的是 Windows 98 操作系统,但所有的程 序均可在 Win2000 及 NT 上执行,这取决于驱动程序的底层运作, 驱动程序会依不同的操作系统执行不同的程序代码以符合窗口操 作系统 IO 存取要求,但我们所写的程序代码完全不受影响。 第 14 页,共 386 页1-2 系统中的各种输出入 我们的计算机系统中存在着各式各样的设备,其目的除了给中 央处理器使用之外,大部份的设备是为了和其它的设备沟通之用 的,本节将说明这些沟通的管道。 1-2-1 I/O 的种类 何谓输入(Input)?何谓输出(Output)?当我们讨论到输出及输入 时,必须先有一个讨论的对象,以这个对象为讨论的主题,所有的 观点以这个对象为主,当然我们要讨论的是计算机上的输出及输 入,因此输出及输入就是针对计算机而言;所谓的输入讯号就是由 外界进入计算机的讯号,输入的讯号让计算机了解到外界的状态; 输入的讯号可能是单纯的高低电位改变的讯号(如开关讯号),也有 可能是连续变化的模拟讯号(如温度讯号); 输出讯号就是由计算机 产生而传送到计算机之外的讯号,最简单的输出方式通常是一个准 位改变讯号,例如将原本低准位状态改变为高准位状态;或是输出 连续改变的电压讯号。将各式不同的输出入样式集合到计算机中, 造就了计算机的多样能力展现。 计算机系统为了要和外界的设备作沟通,必须存在有各式的信 道,不同的信道会有不同的沟通进行方式,图 1-2-1 是经常见到的 情形。 图 1-2-1 计算机以不同的方式连接周边 第 15 页,共 386 页不同的接口是为了不同的需求而设计,这些接口简要说明如 下: 1、 RS-232 接口:这是存在相当久的接口,资料的传输使用单一 位接着单一位的方式传输,在一个时间点内只传输一个位的 资料;由于一个字节是 8 位,因此一个字节的资料就必须在 传输资料线上变化 8 次才能传输完毕,若加上其它的检测讯 号,还要多加几个位。工业界经常使用这种接口和设备作联 机,例如可程序逻辑控制器(PLC)、温度记录器…等等。有关 RS-232 的相关信息请参考附录中所列之参考文献 4、 5、 6。 2、 RS-485 接口:工业上经常使用的除了RS-232 接口外,还有 RS-485 接口。此种适配卡也是串行通讯的一种,但其规格和 RS-232 不同,它对于讯号传输的抗噪声能力较佳,传输距离 也较长。如果使用的是工业级计算机,有些配备RS-485 的接 口,其应用原理及技巧请参考附录参考文献 5 之说明。RS-485 应用之设备也相当多,www.vbio.com.tw中的DMC是其中的一 种应用。 3、 LPT 接口:打印机最常使用的传输接口,一个时间点可以传 送一个字节(8 个位),以前的计算机只有 RS-232 或 LPT,如 果 RS-232 的速度无法达到要求时,通常就采用 LPT 接口。 部份的工业级设备厂商也采用 LPT 接口作为其设备和计算机 作连接的信道。 4、 USB/IEEE-1394: USB 是万用串行总线(Universal Serial Bus) 的简称,采用串行传输的方式进行资料的交换,速度比 RS-232 快上很多,且分为低速和高速二种传输速度,在 USB1.0 中的,低速与高速分别是 1.5 MBits 和 12 MBits, USB2.0 的速度又大幅提升;IEEE-1394 也是串行传输接口, 采用和 USB 一样的方式作传输,但其速度更快。USB 的设备 现在非常地多,包括了鼠标、键盘、网络摄影机…等等的设 备都已出现了此接口,而 1394 则常见于需要传输大量影像资 料的数字式摄影机的传输接口上。 5、 GPIB:通用接口总线(General Purpose Interface Bus)的简称, 最早由 HP 所研发,现列为 IEEE-488 标准。此适配卡经常使 用在仪器级的设备上,它的速度可以达到 10Mb 的速度。 6、 Digital I/O 适配卡:如果要求的是标准接口以外的输出管道, 就得另外安装适配卡,适配卡中以数字输出入适配卡最为常 第 16 页,共 386 页见,这种适配卡提供数字式的输出入管道给计算机使用,此 种卡片主要处理明确的高低准位讯号。这方面的课题是本书 讨论的重点。 7、 Analog I/O 适配卡:另外安装的适配卡除了数字式的输出入 外,还有一些适配卡是针对外界的模拟(Analog)讯号作处理; 例如电压、电流…等等的连续(Continuous)讯号处理。 1-2-2 输出入接头外观 不同的接口所使用的输出入外观均不相同,以下简要说明各种 常见的输出入接口外观,通常透过不同的连接外观也可以得知其所 属的接口,而且不容易发生接错的情形。 RS-232 是存在很久的接口,其外观有二种,一为 25 针式(如图 1-2-2),一 为 9 针式(如图 1-2-3),不 管 是 25 针还是 9 针,其均为公 头 (一根一根的接针)设计,这点是不会改变的;随着计算机及设备 的演进,25 针式的 RS-232 渐渐地不使用,而只剩下 9 针式的 RS-232 接头,部份的笔记型计算机的标准接头已没有 RS-232,若要使用还 必须使用其外接盒(Ducking)。 图 1-2-2 25 针的 RS-232 接头外观 图 1-2-3 9 针式的 RS-232 接头外观 LPT 的接口外观也是 25 针 (如图 1-2-4),但和 25 针 RS-232 不 同的是,LPT 是母头(一洞一洞的接座)的设计,如果较为早期的计 算机,如何辨别 RS-232 和 LPT 呢 ?就是透过公母头的差别来辨别。 图 1-2-4 Printer Port 接头 USB 进年来相当流行,它可以有效地减少配线,其外观如图 1-2-5 所示,左图所示的是计算机上的 USB 接座,它是母头的设计 第 17 页,共 386 页(A 式接头),而且只采用四线式的接法(此四线分别是正讯号、负讯 号、电源及地线),右方则是一般 USB 设备所使用的 USB 接座,它 是公头的设计(B 式接头)。 图 1-2-5 计算机上的 USB 接座(左 )及设备上常见的 USB 接座(右 ) 数字输出入和模拟输出入适配卡需要另外的安装程序(后述), 其对外的接头设计因不同的厂商、不同的输出入通道数而不同,图 1-2-6 是几种常见的接头型式,这些接头型式有双排型式(Head Type)、 D 型型式(D-Type)等等,接脚数由 25 脚至 100 脚皆有,愈 是多功能的适配卡,其接脚数愈多;如果使用这些另外安装的适配 卡,通常也会连带采用原厂商所设计的连接线及延伸板,以方便连 接外部的讯号接线。 图 1-2-6 数种不同的接头型式 1-2-3 接口的使用时机 计算机上已提供的接口包括了 RS-232、 LPT、 USB 等,这些接 口可以提供一般的计算机使用者所需,一般的使用者可以用这些接 口连接常用的家用或娱乐设备,笔者尝试由工业控制的角度说明这 些接口的使用时机,不过,这些只是笔者工作上的心得,其实每位 工程师在使用上或多或少会有不同的想法,只要是符合工作上的要 求,什么样的想法都可以,不是吗?! 1、 串行通讯端口(RS-232):这种接口最常使用在传输速度要求 不快的设备上,这些设备包括了 PLC、温度记录器、出票/ 验票机、发票机、转速计…等等,使用相当地频繁。以现在 的计算机可以提供的速度来说,其最高速度是 921.6Kbps; 第 18 页,共 386 页而且此接口在大多数的工业级计算机上均会配置,有些工业 计算机甚至配置了 4 个串行通讯端口(部份为 RS-485),使 用 上极为方便。 串行通讯端口的控制也很容易实现,操作系统本身就提供有 API(Application Programming Interface)可以呼叫,如果采用 Visual Basic 的话,里面还有一个微软提供的 MSComm 控件, 可以很方便地实现串行通讯(参考文献 4、5、6)。以厂商的角 度来说,RS-232 串行通讯的硬件不仅容易实现,而且价位 也是相当地合理。 2、 并列通讯端口(LPT):由于串行端口的速度比较慢,而且其 传输也是一次一个位,若是考虑到大量的数据传输时,就会 捉襟见肘;并列通讯端口提供了一次 8 位的传输接口,所以 速度上可以较串行通讯大幅提升,其接脚数有 25 个,而可 用的接脚数也达到 17 个 (其它的为地线),应用的范围可以扩 大 (参考文献 6),但其距离比串行通讯来得短,太长的距离 会使得传输线上的噪声增加,不利数据的传输。 如果单纯地控制打印机作打印的工作,现在的应用程序发展 工具都提供有相对应的组件可以呼叫,以 Visual Basic 来说, Printer 对象就可以简单地达到打印工作的执行。如果想要控 制并列埠上的各个引脚,就必须自行撰写控制程序, Windows 系统不提供这样的低阶控函式,这部份可用 Visual C++撰写低阶的函式,并封装后给 Visual Basic 使用。笔者 的方式是采用硬件厂商所提供低阶 Inport 及 Outport 函式达 到控制的功能,而且大部份的工业控制卡制造厂商都会提供 有这样的函式供使用者控制其适配卡,这样的函式我们也可 以拿来控制 LPT 上的引脚。 3、 万用串行总线(USB)/IEEE-1394:有别于以往的计算机周边 需要各式各样的连接线,造成计算机接线的复杂,也使得外 围的扩充不易,因此有 USB 的出现,现在的计算机也将 USB 列为标准配备。现在的一般性周边都提供了 USB 的接口和计 算机连接,依设备的不同,其传输速度有低速的 1.5Mb 和高 速的 12Mb 二种(USB 1.0),现在的诸多设备均提供了 USB 的接口,针对通用的设备,Windows 操作系统和设备商也提 供了驱动程序方便设备连上操作系统。至于自行撰写程序用 以操作 USB 设备方面,主要的考虑还是在驱动程序上,只要 厂商的驱动程序和函式库可以支持,使用 Visual Basic 作控 制也是可行的;而为了支持一般化的 USB 设备,操作系统也 提供了所谓的 HID(Human Interface Device)设备 API 函式, 第 19 页,共 386 页例如 SetupDiGetClassDevs、SetupDiEnumDeviceInterfaces、CreateFile 等等函式,让操作 HID 型式的 USB 设备以类似档案操作的 方式进行读取/写入的动作。 HID 设备包括了鼠标、键盘、摇杆…等直接输入资料至计算 机的装置。 4、 数字输出入适配卡:除了上述在一般的计算机中提供的输出 入信道外,如果我们希望透过计算机侦测外界的数字讯号(单 纯的高电位或低电位的讯号,或是开关状态、位置侦测),我 们就需要在计算机中加装数字输出入适配卡。安装适配卡需 要有空间,这个空间就是计算机所提供的扩充总线(BUS), 我们必须依照计算机所能提供的总线规格来选择所要使用 的适配卡。数字输出入适配卡针对不同运用的范围会有不同 的通道数,所谓的通道数就是这片适配卡可以侦测(方向是 Input)或是控制(方向为 Output)的 点数有多少,因此一般也有 以点数的多寡来选择及讨论输出入适配卡。 使用另外加入的适配卡在计算机系统中,如果不告诉操作系 统的话,其实操作系统也不知道如何和这片适配卡作沟通; 沟通的进行还需要有厂商提供的驱动程序和呼叫函式来因 应,这部份笔者后面还会谈到。 5、 模拟输出入适配卡:相对于数字式的讯号,模拟式的输出入 适配卡是为了取得外界的模拟讯号(连续存在),以及输出连 续的讯号。模拟式的讯号不单是高电位及低电位二种状态, 它的讯号拥有连续的数值,例如温度,我们可以说现在是 30 度 C,而 1 小时前的读值是 28 度 C,这种随时间会有不断变 化的数值是为模拟讯号;模拟讯号在某一个时间一定会有一 个数值产生,而不会只有一个状态产生。由以上的讨论可知, 处于现实的环境下,不同的情形必须选择不同的讯号适配 卡,以符合需求。 6、 IEEE-488 卡 :此种适配卡通常用来和仪器取得沟通,专门 用来作数据传输交换之用。如果我们使用了一部仪器作事 情,例如拿一部频谱分析仪作讯号分析、拿示波器作讯号量 测,甚至拿一部医学仪器作血液分析,这些分析的结果可以 透过这些仪器上的 IEEE-488 接口,连接计算机上的 IEEE-488 作资料的传输,当所需的资料传到计算机后,再由计算机作 接下去的其它处理(如打印、储存…)。将仪器上的专业分析 结果传送到计算机的作法,一般就是采用 IEEE-488 接口。 第 20 页,共 386 页 1-2-4 接口的引脚定义 当我们考虑使用某一种接口进行资料的交换及讯号的取得前, 通常会先了解某一种接口所能提供的功能在那里,除了查看该接口 的规格列表外,我们也会从接口本身的接脚定义了解其是否适合我 们使用。以下简要讨论几种常见的接引脚定义。首先是 RS-232 的 接脚定义,如表 1-2-1 所示。 表示输入,而 则表示输出。 表 1-2-1 9 针 RS-232 的接脚定义 编号 简称 讯号方向 意义 1 CD 载波侦测 2 RXD 接收资料 3 TXD 传送资料 4 DTR 资料端备妥 5 GND 接地 6 DSR 资料备妥 7 RTS 要求传送 8 CTS 清除以传送 9 RI 响铃侦测 由表 1-2-1,实际的资料接收/传送是由第 2 脚及第 3 脚负责, 第 5 脚是讯号地线,以提供统一参考点,其它的引脚则分别有输出 及输入。 表 1-2-2 为并列埠的各引脚定义,其接脚数较多,资料的传输 速度也较快。同一个时间点可以传输 8 个位的资料,这是串行通讯 的 8 倍,但也由于同时传输的资料量较大,如果传输时有一个资料 点发生错误(例如传输线受到干扰),整个字节的数据就全错了;为 了避免传输线的干扰,并列传输线的长度就不适合太长(RS-232 的 可传长度就比 LPT 来得长)。 表 1-2-2 并列埠的引脚定义 编号 简称 讯号方向 意义 1 /STROBE 触发 2 D0 资料位 0 3 D1 资料位 1 4 D2 资料位 2 第 21 页,共 386 页5 D3 资料位 3 6 D4 资料位 4 7 D5 资料位 5 8 D6 资料位 6 9 D7 资料位 7 10 /ACK 认可讯号 11 BUSY 忙碌讯号 12 PE 缺纸讯号 13 SEL 选择讯号 14 /AUTOFD 自动走纸 15 /ERROR 错误 16 /INIT 初始化 17 /SELIN 读取资料 18 GND 接地 19 GND 接地 20 GND 接地 21 GND 接地 22 GND 接地 23 GND 接地 24 GND 接地 25 GND 接地 由表 1-2-2,除了资料位之外,其它的讯号线还可以作为数字 输出入之用;有的厂商还使用 LPT 作为其设备和计算机的连接管 道,除了考虑数据传输的快速之外,还可以利用其它的线路作交握 (HandShaking)之用,确保资料的正确性。 表 1-2-3 是 USB 四支接脚定义,它的传输方式是差动式的传输 (和串行通讯中的 RS-485/RS-422 一样),除了讯号线之外,还备有 电源线及接地线,可以提供设备所需的电源(总电流是 500mA)。资 料的传输是此种接口唯一的工作,并无其它的接线可用作其它用 途。 表 1-2-3 USB 接脚定义 编号 简称 讯号方向 1 VCC +5 VDC 2 D- Data - 第 22 页,共 386 页3 D+ Data + 4 GND Ground 为了执行另外的输出入工作所要安装的适配卡之接脚定义因 各厂商及适配卡功能而不同,表 1-2-4 是本书将用来作范例的数字 输出入卡的接脚列表,它是 D 型 37 针的设计,有 24 个双向的输出 埠可供使用,其它的细节请参考第四章的说明。 表 1-2-4 PIO-D56 数字输出入接脚定义 引脚编号 定义 引脚号码 定义 1 N.C. 20 Vcc 2 N.C. 21 GND 3 PB7 22 PC7 4 PB6 23 PC6 5 PB5 24 PC5 6 PB4 25 PC4 7 PB3 26 PC3 8 PB2 27 PC2 9 PB1 28 PC1 10 PB0 29 PC0 11 GND 30 PA7 12 N.C. 31 PA6 13 GND 32 PA5 14 N.C. 33 PA4 15 GND 34 PA3 16 N.C. 35 PA2 17 GND 36 PA1 18 Vcc 37 PA0 19 GND XXX XXX 表 1-2-5 是一种模拟输出入卡的接脚定义,由此定义表可知这 种模拟适配卡提供了 32 个单接点式的模拟输入通道,或是 16 个差 动式的模拟输入通道,另外也提供了 2 个通道的模拟输出通道。 表 1-2-5 模拟输出入适配卡的定义 引脚编号 定义 引脚号码 定义 1 Analog input 0/0+ 20 Analog input 16/0- 2 Analog input 1/1+ 21 Analog input 17/1- 3 Analog input 2/2+ 22 Analog input 18/2- 第 23 页,共 386 页4 Analog input 3/3+ 23 Analog input 19/3- 5 Analog input 4/4+ 24 Analog input 20/4- 6 Analog input 5/5+ 25 Analog input 21/5- 7 Analog input 6/6+ 26 Analog input 22/6- 8 Analog input 7/7+ 27 Analog input 23/7- 9 Analog input 8/8+ 28 Analog input 24/8- 10 Analog input 9/9+ 29 Analog input 25/9- 11 Analog input 10/10+ 30 Analog input 26/10- 12 Analog input 11/11+ 31 Analog input 27/11- 13 Analog input 12/12+ 32 Analog input 28/12- 14 Analog input 13/13+ 33 Analog input 29/13- 15 Analog input 14/14+ 34 Analog input 30/14- 16 Analog input 15/15+ 35 Analog input 31/15- 17 Analog ground 36 Analog output 1 18 Analog output 0 37 Digital ground 19 External trigger 以上介绍了几种不同的接口所使用的接脚定义,在实际使用 上,我们必须依情况来选择所使用的接口,并且依照此接口的接脚 定义从事相关的接线。 1-2-5 适配卡讯号的种类 在不同的时机使用不同的适配卡须考虑到所要量测的讯号型 态为何,依据不同的讯号型态选择不同型态的适配卡。真实世界的 讯号分类如图 1-2-7。 第 24 页,共 386 页图 1-2-7 讯号的分类 一般将讯号分为模拟讯号(Analog Signal)及数字讯号(Digital Signal)二大类,模拟讯号是真实世界中最常感受到的连续讯号,随 着时间的不同,模拟读值也会跟着改变;数字讯号则较为清楚可辨, 只要是状态以二个不同电位的讯号予以表示,就可以被认为是数字 讯号,图 1-2-7 中所列的数字讯号只有几个,但实际上只要是讯号 的电位可以用高电位及低电位予以分离的,就可以被当成是数字讯 号处理,例如开关的讯号,在某一个时间点只会有开或关二种状态 之一会存在,因此其被归类为数字讯号;很多的数字状态组合而成 的讯号还是可以被归类为数字讯号,例如图 1-2-7 中的脉冲及方波 讯号,它们其实是很多个连续的高低电位讯号的组合,还是被归为 数字讯号。由于数字讯号和计算机中的处理方式极为相近,计算机 也是以处理数字式的讯号为核心,因此数字讯号在计算机处理上是 比较理想的,其应用也相当地广泛。这类的数字讯号就要使用前述 的数字输出入卡作讯号的撷取及输出。 模拟讯号是连续型的讯号,没有办法单独以一个高电位或低电 位表示一个模拟讯号的数值,这样的讯号就必须用模拟数字转换芯 片将模拟的讯号数值转换为计算机可以辨认的数据,才能让计算机 接下去作其它的处理程序。这类的模拟讯号必须使用模拟讯号适配 卡作撷取及输出。例如温度值的量测,除了需要使用传感器量取外 界的温度外,传感器还会输出代表此时温度的电压值,透过电压值 的不同,可以表示不同的温度;如果希望在计算机上可以得知此温 度值,就需使用模拟/数字转换卡将代表温度的电压以 12 个位或更 高的字节合表示,组合而成的表示数值比数位情况中仅使用二个位 表示要来得复杂且精确多了。 第 25 页,共 386 页1-3 控制适配卡 1-1 节已经将硬件的部份作过说明,相信读者应该了解到使用适 配卡时,在硬件组装上应该注意事项,以及如何让这片接口下可被 操作系统所认识;而 1-2 节则是说明了计算机经常使用的各种接口。 接下来的一节中,我们将继续讨论和适配卡有关的软件部份。 1-3-1 控制的方式 安装适配卡的目的是为了延伸计算机的功能,也由于适配卡是 由使用者自行加上的,操作系统不可能为每位不同需求的使用者准 备所有的解决方案;而使用者也不太可能在窗口操作系统的环境下 自行针对某一个特殊功能进行程序的撰写。如果由最基础的程序一 直写到硬件卡片控制的话,将会使得工程师花费太多的时间而无法 顺利完成计画。这时就需要由硬件制造厂商为他的客户们(也就我 们拿这些适配卡在作控制的工程师们)准备一些软件工具了,而这 些准备的软件工具通常也适用在很多种的程序语言上,例如 Visual Basic、 Delphi、 C++…等等常用的语言,部份的厂商还会针对特别 的应用软件提供特别的软件接口,让厂商所开发的适配卡可以适用 在不同的软件语言中,例如部份厂商就提供了美国 National Instrument 所开发的图形语言 LabView 使用的*.vi 档案,使其卡片 可以在 LabView 的环境下控制(本书所使用的卡片之光盘片就含有 此 vi 文件)。 vi 是 Virtual Instrument 的简写,其意为虚拟仪器,LabView 中的子程序就是众多的 vi。 就我们使用的 Visual Basic 来说,厂商可以为他们的适配卡作 出 ActiveX 让 Visual Basic 的环境可以使用;也可以作出动态链接 库 (Dynamic Link Library, DLL),让 Visual Basic 的程序呼叫,这 二种方式是最常使用的方法。二种方式在使用上不太一样,ActiveX 如同一般 Visual Basic 中的控件,可以由工具箱中方便地自由取用; 而 DLL 则必须利用 Declare 的方式作宣告,才能在程序中使用;笔 者的实作经验中显示,使用 ActiveX 的速度要比 DLL 来得慢,因为 ActiveX 的骨子里其实也是 DLL,只不过它将 DLL 藏在里面,再透 过程序的转换将信息传给 DLL,当然了,DLL 的信息也是透过 第 26 页,共 386 页ActiveX 再传送给应用程序,这一来一回就使得 ActiveX 的速度比 纯使用 DLL 慢了许多, 其过程如图 1-3-1 所示。 图 1-3-1 由 Visual Basic 控制卡片的过程 厂商对于驱动适配卡的方式也不见得完全一样,由图 1-3-1 中, 提供 OCX 控制方式的适配卡,有些也是将原来 DLL 的呼叫方式改 为由 OCX 呼叫,再转而呼叫 DLL,形成了二层的呼叫;另一方面, 也有厂商直接将 OCX 中的呼叫直接就跳过 DLL,而直达驱动程序。 在其它的语言中,DLL 也可以被其使用(或是使用*.lib),该如 何使用,则视使用的语言而定。以下将说明适配卡控制软件的安装, 而 Visual Basic 上的使用则集中在其动态链接库。 1-3-2 动态链接库的安装 既然需要 DLL 的帮忙,当然就需要执行 DLL 的安装。同样的, 我们要以上一节所使用的适配卡为例,说明动态链接库(DLL)的安 装步骤。厂商在每一片适配卡的包装内都会附有一片光盘片,该光 盘片会含有此片适配卡所需要的 DLL,当我们依上一节的步骤将卡 片安装至计算机后,就可以重新开机并进行 DLL 的安装工作。 依照厂商的建议程序,放入适配卡所附的光盘片,此时在屏幕 上出现在如图 1-3-2 的画面,光盘片提供了相当多的功能,我们将 进行软件的安装,故选择该画面中的第一个选项。 第 27 页,共 386 页 图 1-3-2 控制软件安装的开始画面 我们采用的是 PCI 接口的卡片,所以接下来要选择「PCI Bus DAQ Card」,当光标在画面上移动时,也可以看到在画面的右方出 现该项目所支持的卡片,如图 1-3-3 所示。 图 1-3-3 选择 PCI 卡片系列 再次进入下一层的选项后,我们可以发现 PCI 类型的卡片相当 多,根据选用的适配卡,此时必须选择 PIO-DIO 的项目(当选用不 同的适配卡时,这项目也必须跟着改变),同样的,在画面的右方 也是列出了此项目下所支持的适配卡名称,如图 1-3-4 所示。 第 28 页,共 386 页 图 1-3-4 选择相同的卡片模块 如果基于相同的架构,同一组程序代码还可以使用在多种卡片 上,这些卡片基本上只是通道数目的不同而已,其内部的原理类似; 一进入接下去的选项时,出现可以用在不同平台及不同软件语言的 选择项目,如图 1-3-5 所示。基于不同的平台,其控制适配卡的驱 动程序会不太一样,因此在该画面中必须依照自己的需求选择适用 的选项,例如以 Windows 2000 平台作系统开发,那么就选择「 Install Toolkit for Windows 2000」该项。 图 1-3-5 选择适用的作业平台项目 选定所要进行安装的环境后,程序开始会进行必要的安装工 作,其程序如图 1-3-6 所示。最后则是重新激活计算机后完成 DLL 的安装。 第 29 页,共 386 页 图 1-3-6 实际的控制软件安装过程 在使用预设的目录安装时,全部的必需档案会拷贝 至 ”C:\DAQPro\PIO-DIO Win95”的目录下,当检视其内容时,可见 到其内容如图 1-3-7。在安装完成的目录下,Demo 的目录可以看得 出来,厂商已经就一些常见的发展语言作了范例程序,一开始使用 接口片时,通常也是藉由这些范例程序的导读而逐一进行更深入的 研究。 图 1-3-7 安装完成的目录中的说明文件 第 30 页,共 386 页 1-4 硬件2018香港马会开奖现场、中断及 DMA 插在计算机插槽中的适配卡必须使用规定的方式才能被程序所 存取,本节将讨论相当重要的存取基本观念—2018香港马会开奖现场、中断、直接内 存存取,这些观念是使用适配卡时相当重要而且必备的观念。 1-4-1 硬件2018香港马会开奖现场(Address) 计算机中含有相当多的外围设备,中央处理单元(CPU)对于所 有的设备必须要有一定的方式才能存取其内容,就像我们寄信一 样,当信息来来回回时,一定要有一个住址,才能使信件正常地往 来于二地之间;计算机中的设备也必须要有一个2018香港马会开奖现场(Address),才 能使信息交换得以进行,而且每一个设备所给的2018香港马会开奖现场一定是独一无 二,否则将使得信息传递错误。计算机中的部份 Address 是固定的, 其它的设备不能占用,也有部份的 Address 是开放的,并不特别指 定给那一样设备使用,而是由计算机系统的使用者来决定给那一项 设备使用,例如当我们使用 ISA 适配卡时,该适配卡的设计经常就 是由使用者自行决定所使用的适配卡 Address,如果该适配卡的设 计中使用到中断时,也是由使用者决定所要使用的中断号码。 计算机是一个数字的世界,所有的信息基本上使用无穷的 0 和 1 组合起来,计算机内部就是很多的 0、1 的组合,每一个能够记录 0 或 1 的空间称之为位(Bit),而 8 个位称为一个字节(Byte),通常 也以字节为单位来说明储存的空间。计算机开始运作后,所有的资 料必须在内存上进行运算,CPU 欲对内存进行操作时,必须要有地 址才行;在之前的说明中也提到要对硬件操作时也需要有一个硬件 的2018香港马会开奖现场才能顺利而正确地控制硬件;如果内存和硬件均需要有2018香港马会开奖现场 才能被 CPU 所正确控制的话,那么到底2018香港马会开奖现场指的是内存的2018香港马会开奖现场还 是硬件的2018香港马会开奖现场呢?实际上,所有的2018香港马会开奖现场是由大家一起共享的,包括 BIOS、硬件2018香港马会开奖现场、内存2018香港马会开奖现场,而排列的位置则明显分开。使用硬件 2018香港马会开奖现场时以字节为单位,最小是一个字节,也有硬件使用到 4 个、8 个、16 个或 32 个字节,大于一个以上的字节通常是分配于连续的 空间。 使用计算机对适配卡作输出入的动作时,所有的信息流动必须 指定一个2018香港马会开奖现场,对于 ISA 适配卡而言,CPU 对于2018香港马会开奖现场的存取使用原 来 PC XT/AT 时的规定,原来有2018香港马会开奖现场由 00000H~0FFFFH 一共有 第 31 页,共 386 页65536 个输出入的2018香港马会开奖现场可以使用,硬件的存取就对应到这些2018香港马会开奖现场, 内存的使用则在此2018香港马会开奖现场以上;虽定义了 64K 的2018香港马会开奖现场,但是其实只有 规划了其中的 00000H~03FFF 之间的 1024 个输出入埠(Port)可以使 用,在扣掉了系统主机板所保留使用的 0000H~01FFH,那么只有 0200H~03FFH 这中间的 512 个埠是可以由使用者自行规划的区域, 而且这些区域中还有外围要使用的2018香港马会开奖现场,所以实际可以被我们所使 用的就愈来愈少了,指定适配卡所使用的2018香港马会开奖现场时,还要特别注意到 该2018香港马会开奖现场是否已经被其它的设备所占用,如果二个设备占用了同一个 2018香港马会开奖现场的话,将使得信息的传送出现问题;查看计算机中的系统,也 可以看到现在计算机中的2018香港马会开奖现场使用情形,如图 1-4-1。由图中可以 看出2018香港马会开奖现场的分配是相当地有效率,几乎是全被用上了;从另一角来 看,如果我们采用了 ISA 接口的控制卡时,在这些2018香港马会开奖现场的安排上也 必须费些功夫,在允许的2018香港马会开奖现场空间中找出一个还没有被使用的空间 来作适配卡的2018香港马会开奖现场。 图 1-4-1 计算机中的2018香港马会开奖现场分配 但对于 PCI 接口的控制卡,则可以突破最高 03FFH 2018香港马会开奖现场的限制。 PCI 的接口可以使用最高 FFFFH 的2018香港马会开奖现场(通常是不会使用到极限), 也就是原规划的 65536 个2018香港马会开奖现场;因此新式的适配卡在2018香港马会开奖现场的选择上 就显得宽敞许多,而且实际的安装上更是不需使用者费心,操作系 统和卡片上的芯片会自动作出最适当的安排。 1-4-2 中断(Interrupt) 什么叫中断?当程序开始运作时,通常是一直到最后的指令执行 完毕后,CPU 才会回到休息的状态,并执行下一个指令组群;但在 第 32 页,共 386 页其正常执行的过程中,如果这时候有一个突发状况产生,CPU 必须 暂时停止正在执行的程序,转而处理另一段程序代码时,我们称一 个「中断」发生了。中断发生后,CPU 会处理引发中断的要求,待 此中断要求被处理完成后,再回到原来前一个任务被中止的地方, 继续其未完的任务。 现在所讨论的中断是由硬件线路来引发,在计算机中的中断线 路有十六条,卡片可以利用这些中断的硬件线路达到通知 CPU 处 理事情的目的;同样的,我们也可以由「开始」\「控制台」\「系 统」选项中查看计算机系统中断线路的使用情形,如图 1-4-2 所示。 图 1-4-2 系统中断分配表 由图 1-4-2 中,我们特别注意到部份中断号码的使用是重复的, 在使用 ISA 适配卡的情形下,这种情形是不允许的,但在 ISA 接口 里,每一个单独的 ISA 适配卡使用的中断不可和其它的适配卡共 享;在使用 PCI 适配卡的情形下,中断是可以共享的。只要是使用 PCI 接口的卡片,在引发中断的同时,都可以正确地得到应有的通 知,而不必担心中断是否会被其它的设备夺去,这是因为 PCI 接口 芯片在处理的关系。 ISA 适配卡在取得中断讯号后,并不会马上释放此中断讯号(也 就是不会将此讯号的状态转换,使其可以再接受中断触发),也因 此使得中断在此情形下就被该取得中断的设备所独占,而且此项设 备要使用多久也是无法掌握的,故其它的设备不能再使用相同的中 断;而 PCI 适配卡利用 PCI 芯片在取得中断后,随即进行应有的后 续程序流程,但在同时,此 PCI 适配卡也将中断讯号释放,这使得 其它的适配卡还是有机会可以藉由这条中断讯号而达到通知事情 第 33 页,共 386 页发生的目的,故 PCI 适配卡在中断的使用上要比 ISA 适配卡方便和 弹性许多。 除了适配卡外,包括串行通讯端口、键盘、磁盘驱动器…等等 的装置,为了资料的传输和处理所发生的错误,也都透过中断来要 求 CPU 处理。 1-4-3 直接内存存取(Direct Memory Access-DMA) 通常适配卡和内存之间的资料传递是经由 CPU 的帮忙而达到, 但需要处理大量的资料时,这种传输的方式也会使得效率大打折 扣,为了改善这种缺点,遂产生了直接内存存取的机制。 我们也可以自系统内容中查看到计算机系统对于 DMA 的分配 情形,如图 1-4-3。现在的计算机中共有 8 条的 DMA 线路可以使用, 而由图 1-4-3 的情形看来,其中只使用到 3 条。 图 1-4-3 系统中的 DMA 使用情形 实际上,DMA 是一组二条的讯号线,一条是做为 DMA 请求 (DMA Request , DRQ) ,另外一条做为 DMA 确认(DMA Acknowledgment,DACK),运作的过程一定是二个一组,DMA1 表 示使用编号 1 的 DMA 信道,而且其中就含有 DRQ 和 DACK 二条 线路。 由于 DMA 的使用是跳过 CPU 而直接在内存和设备之间传输资 料,运作的过程中一定是占用了总线,所以 CPU 在这时候不能对 总线作其它的动作,也就是说此时的 CPU 是无法存取资料的;因 此,使用 DMA 的时间就不能太长,否则可能使得 CPU 无法处理内 第 34 页,共 386 页存,而形同当机;另一方面,也由于资料的传输不经过 CPU 的管 理,完全是由硬件独立完成,它的传输速度非常快速,比 CPU 或 软件的操作都还要快,在传输大量实时数据时,此种方式是相当适 合的,最常见的就在音效资料的传输上(图 1-4-3 就见到了 DMA1 被用在声卡上),音乐、语言的资料传送到声卡时,如果速度不够 快,将使得声音的播放显得迟顿或变调。 1-4-4 如何调整2018香港马会开奖现场、中断及 DMA 如果使用的适配卡是 ISA 接口,那么这三项都得自己来调整, 买来的适配卡上一般都不会固定在某一个2018香港马会开奖现场、中断或 DMA,厂 商会提供一些2018香港马会开奖现场范围的选项供使用者针对自己计算机中的空白 2018香港马会开奖现场来作选择设定,而中断和 DMA 也是选择计算机系统中尚未被 使用到的号码来使用,这是 ISA 接口安装时必须注意的事情。 PCI 接口的好处就在于不需要特别地去指定什么范围的2018香港马会开奖现场, 在 PCI 卡片上的控制芯片会自动配置,不会和其它的卡片设备有冲 突。中断的号码也是自动配置,即使是配到相同的中断号码也无所 谓,因为 PCI 接口的芯片会自动调适中断给共享的 PCI 装置。不过, 自动调整的中断不会使用到 ISA 适配卡所使用到的中断,如果在开 机时 PCI 由于自动配置的关系而使用到 ISA 适配卡所指定的中断号 码时,将会使得 ISA 适配卡的动作失效或不正常;此时的解决方式 则是在系统内容中选择保留系统资源,将 ISA 接口会使用到的中断 先保留下来,那么 PCI 芯片执行自动配置时,将会跳过系统保留的 中断资源,而配置其它尚没有被保留的资源,如此即可避开此种问 题。 就如同在本章开头所说明的,当卡片顺利地安装进系统后,驱 动程序的安装也必须进行,以便系统可以辨识该卡片;ISA 卡片在 开机时并不会自动搜寻,而 PCI 卡片则会自动被辨识,不管是否被 自动辨识,使用者都必须正确地安装驱动程序,以保证卡片可以正 常地运作。 第 35 页,共 386 页 1-5 缓存器(Register) Register 缓存器是位在中央处理器(CPU )的芯片(Chip) 中,暂 时而快速存取的记忆储存空间,帮助 CPU 执行算数、逻辑或转移 运算,只储存处理过程中的资料或是指令,之后再把资料或指令送 回随机存取内存 (RAM ),这是计算机运作时最常使用的。 缓存器有下列几种:累积器(ACC ) 、储存缓存器(Storage Register),这两种缓存器位于算数逻辑单元,一般放在 CPU 中;指 令缓存器 (Instruction Register )、2018香港马会开奖现场缓存器(Address Register)则 位于适配卡中,本节将讨论位于适配卡中的缓存器之相关事项。 1-5-1 硬件上 IO 控制方法 适配卡的运作均是透过缓存器的帮助而进行,缓存器分成控制 缓存器、状态缓存器二种,负责不同的功能,一为 Input,一为 Output; 缓存器在适配卡上就是某一个芯片的控制中心所在,当利用程序下 达询问的指令时,某一个缓存器就会将资料传回(实际上是放在总 线上供 CPU 读取);同样的,当程序欲控制芯片作某一个动作时(例 如更改设定,或是输出讯号),也是将指令写入某一个规定的缓存 器,该缓存器的值一经改变,就反应到真实的硬件动作上,如图 1-5-1。 图 1-5-1 缓存器和实际的控制间的关系 第 36 页,共 386 页由图 1-5-1,程序的控制指令或是查询状态的指令会由总线(此 图使用的是 PCI 总线)进入卡片中的某一个缓存器,若是控制的动 作,则会由缓存器再直接去控制实际的开关动作,例如此图是由缓 存器再去控制 Relay 的开关。 缓存器写入数值的过程中,如图 1-5-2 所示,一个数值由程序 发出写入的指令,最后还是写入到缓存器里,由缓存器中的数值决 定最后的硬件输出状态。 图 1-5-2 数值写入缓存器的过程 而当使用者透过程序欲取得缓存器中的数值,其流程则如图 1-5-3 所示。 图 1-5-3 读缓存器的过程 所以实际的控制流程进行时,程序必须和缓存器打交道,要取 得卡片的任何状态,必须读取缓存器中的数值;而要控制状态时, 也是写入控制的数值到缓存器,因此程序的对象就是缓存器,一片 适配卡可能会有很多的缓存器,实际的数目由功能来决定,功能愈 愈多,则使用到的缓存器数量也必定不少。 缓存器的存取必须要有一定的方法,存取动作的进行必须使用 到之前提到的2018香港马会开奖现场,我们提过2018香港马会开奖现场是在计算机中存取资料的必备条 件,同样的,插在计算机中的适配卡也必须被配置2018香港马会开奖现场,那么当程 序来存取卡片上的资料时才可以对应到正确的缓存器,并进而执行 正确的功能。缓存器的2018香港马会开奖现场通常也是以一个字节为单位,如果卡片 上的缓存器较多的话,就会使用到比较多的2018香港马会开奖现场,因此我们可以看 到不同的卡片使用的2018香港马会开奖现场范围均不相同。 虽然说明透过缓存器可以完全控制适配卡,但现在很多的适配 卡所提供的函式都没有看到所谓的缓存器的指定,最多也就只有地 第 37 页,共 386 页址和中断的设定,这又是什么情形?其实,这样的情形也很多,厂 商主要是考虑到使用者的方便而将原本需要较复杂的呼叫过程包 装起来,函式使用起来更精简,工程师只知道呼叫某一个函式,就 可以成功地控制或是读取信息,其它的细节就由厂商的 DLL 或是 OCX 代劳了。不过,笔者还是认为工程师最好了解这些相关基本原 理,未来碰到其它厂商的适配卡时,才能够运用自如,而不会局限 在某一种的适配卡。 第 38 页,共 386 页常问问题集(FAQ) Q:适配卡的种类这么多,怎么选择呢? A: 通常是由系统需求产生选择方式。例如所要进行的开关状态 的侦测,这时就要选择数字输入卡;若要进行温度量测,就 要选择模拟输入卡。如果二者皆有,那就选择多功能适配卡, 这种卡片含有数字及模拟二方面的规格。 Q: 不同的厂商,会使得我的使用程序产生不同吗? A:应用程序的差异不大。虽然制造厂商很多,但是大家的硬件 基本理论都是一样的,而采用的软件技术也是同样在 Windows 作业平台上执行,其程序几乎是一样的,可能的不 同会在于函式的名称定义这些小细节上,大方向是不会改变 的。 Q: 我可以使用家用型计算机取代工业级计算机吗? A:可以的,而且家用计算机也有扩充槽可以用。不过,工业级 计算机的稳定性是经过测试的,比较适合在工业应用里的不 良操作环境里。 Q: 驱动程序可以自己制作吗? A:嗯 !不容易。这需要了解硬件设计细节,也须了解微软的 SDK 及 DDK,还有 Visual C++,撰写驱动程序就可以自己来; 不过,由于程序相当地复杂,通常是由适配卡制造厂商进行 驱动程序的开发,我们买了适配卡后,厂商会卡附上驱动程 序。如果一定要自己作的话,市面上的一个 WinDriver 这类 软件可以帮忙。 第 39 页,共 386 页本章习题 1、 请说明输出与输入,并举出例子。 2、 一个工业计算机的基本组成有那些? 3、 请检查您计算机中的适配卡槽种类有几种。 4、 列举出您计算机中的输出入接口种类。 第 40 页,共 386 页第二章 Visual Basic 6 及与适配卡的沟通 第一章针对重要的硬件观念作了基本的说明后,本章是接下去 作软件的引进。由于 Visual Basic 被设计得非常容易使用,但在应 用到卡片的控制上则较少着墨,本章略述 Visual Basic6.0 及适配卡 间的关系,实际的控制则留待后续的卡片个别介绍时再予以说明, 并佐以实验。 2-1 窗口程序概念简述 自从微软推出了 Visual Basic 后,使得窗口程序的设计难度大 大降低,使用 Visual Basic 进行系统开发的人数也是笔直地增加, 它提供了相当简单的途径给想设计窗口程序的设计师。 本书使用 Visual Basic 6.0 来作适配卡的控制,但并不专门讨论 Visual Basic 的详细操作细节,仅重点式地列出部份内容;若与程序 有关的部份,会作详细的讨论与研究,但对基本的 Visual Basic 的 操作则着墨较少,若读者尚不了解 Visual Basic 的使用,请参考本 书附录所列的部份参考书籍,这些书应该对读者会有相当的帮助。 自从窗口操作系统兴起后,计算机的使用就改观了,一切的作 业都是在图形化的操作系统中进行。不仅由于窗口环境与以前的 DOS 作业环境非常不一样,就连概念上都大相径庭。 2-1-1 对象的概念 在真实的世界中,我们时时刻刻都在接触『对象』(Object),所 谓的对象就是一个真实存在的物品;例如,我们想组装一台计算机, 我们会到计算机配套场去买一些必须的零件,如屏幕、主机板、内 存、硬盘、软盘、键盘…等等,全部带回家后,拿起起子就准备组 装了。在这样的例子里,所买的零件全部都是『对象』,而被我们 组装完成的整部计算机也是『对象』,所以一个大对象是可以由其 它的小对象组装而成。 软件开发也愈来愈趋近真实世界,现在窗口操作系统的构成也 是依照真实世界的模式建构;我们看到的操作系统可以被看成是一 第 41 页,共 386 页个大对象,里面就蕴藏了无数的小对象,我们看到的 Mouse、窗体、 按钮、打印机、图形、日期显示…都是对象,而且这些是在 Windows 里经常看到的。只要是在 Windows 系统中可以被叫得出来的,就可 以被称为对象。 在构想系统时,我们会想到一个系统应该有一个表现其外观的 地方,那会是一个窗体;也会想到需要放几个命令按钮,以便让使 用者可以按下后选择执行一些工作;再安排一个安放项目的地方 (那会是一个 ListBox 或 Combo Box);也许再来一个文字书写及显 示的方框(就是文字框)…;以上所提到的都是经常在各个应用程序 中所看到的各种外观,这些都是『对象』。虽然都是程序所创造出 来的,可是都被看成是对象一般地控制着。接下来的就是利用程序 来操作这些所谓的『对象』了,让这些对象随着使用者的意思而进 行既定的工作。 想要在窗口操作系统中写程序,也和以往在 DOS 下完全地不一 样,现在的程序设计必须考虑对象,而对象的考虑上也必须考虑属 性 (Property)、 事件(Event)与 方法(Method)等三个与对象息息相关 的名词。 实际上,不管使用那一种程序语言,建立系统的程序是大同小异 的,它们是分析问题、设计解决步骤、 撰写程序、验证程序等四个 步骤。就 Visual Basic 来说亦是如此,不过,由于它是一个窗口程 序的开发工具,因此虽然程序是一样,但与以前的 DOS 下的程序 设计比较,在实作上就必须考虑以下的问题了: 1、 应给使用者看什么样的画面? 2、 使用者如何操作画面? 3、 如何让使用者的意向和程序产生关联? 4、 如何作程序设计? 在使用 Visual Basic 设计窗口程序时,其设计的方向大约分成 二个部份:画面设计和 程序撰写,其中的画面设计是与以前的设计 程序最大不同的地方,程序撰写则和以前没什么差异。这二个部份 的先后顺序是先作画面设计,再作程序撰写—意思也就是说,你必 须先考虑到使用者应该先看到的画面外观,接着才去设计动作流 程。 第 42 页,共 386 页2-1-2 接口成员 车子是一个对象,当描述这部车子的相关特性时,我们也许会 使用诸如『这是四门房车』、『它拥有 ABS』、『具安全气囊』… 等等的叙述,这些叙述都是用来描述该部车子的,我们称这些是该 部车子的特性,以 Visual Basic 的语言来说,它们就叫作『属性』。 车子被创造出来后,当然是被拿来给人们使用的,而人们在使 用的过程中,就和车子有了互动,例如我们将车门打开、踩下油门、 转动方向盘…等等,这些动作都是由操作车子的人员给予车子的一 种输入,这种施加到车子的动作,对于车子来说就是一种『事件』; 针对这些驾驶施加到车子的事件,车子在被设计之初也都设想到 了,诸如当油门被踩下后,连杆动作发生,拉索被拉动,接着一连 串的动作后引擎的转速就增加了;而方向盘转动时,也是在一连串 的动作后,轮胎就顺着一定的方向作转动…,这些种种的呈现,都 是因为驾驶对车子作了一些输入,对于车子产生事件,而车子针对 这些事件所作的最后响应。这些响应用程序的语言来说,就是子程 序,或是函数,也称之为『方法』。 以上所提到的『属性』、『事件』、『方法』就是在 Visual Basic 里面经常使用的字眼—接口成员。以下再将此四个重要名词予以说 明: 1. 对象(Object):只要确实存在的东西,可以被拿来使用,有一个特 定的名字(Name),这就是一个对象。对象存在任何的一个角落, 所有的动作与信息必定和对象有关。例如”我们移动鼠标在画面 上点选一个图片”,这样子的一个描述里面就有”鼠标”和 ”图片” 二个对象(它们也分别有各自的名字)。而窗口系统中只要是被程 序设计者设计出来后,放在系统中予以使用的,就是对象。所 以一个窗口是对象、一个鼠标也是一个对象、一个图片也是一 个对象、一个按钮也是一个对象…,它们都是对象。 2. 属性(Property):每一个存在的对象所具备的特性称为属性。比方 太阳是一个恒星,它的表面温度是摄氏 6000 度,半径是地球的 多少倍…等等,这些都是用来说明太阳的一些特性,它们可以 被称为是太阳这个对象的属性。又比方说,有一个人,他叫张 第 43 页,共 386 页三,身高 180 公分,男性,黄色皮肤…等等,用来描述此人的 一些特性,这些特性也叫作这个人的属性。 3. 事件(Event):每一个对象总会与外界产生互动,而当外界(其它的 对象)与此对象有交互作用时,就是这个对象有一个事件被引发 了。一个对象可以被引发的事件有很多,举例来说,一个鼠标 可以有按一下(Click 事件)、按二下(Double Click)、拖移(Drag Over)、移动(Move)…等等的事件;其它的对象也有一样或是类 似的事件,不同类型的对象可能会有不同的事件可以被外界所 引发。 4. 方法(Method):当该对象被引发了某个事件之后,系统(或程序) 应该采用的相应步骤就称之为方法,例如鼠标移动到一张图片 内部时,程序显示一张不同的图片;鼠标按一下图片时,图片 旋转 90 度或是播放一段音乐等等。有的时候针对对象本身的特 定动作也称为方法,例如移动、绘线、绘圆、删除…等等。 一个系统程序的建立,就在于这四个步骤的重复执行与考虑。 一整个系统可以被看成是一个大的对象,而再去分析此对象中的特 性 (属性)、如何接收外界的信息(事件)、如何响应外界的信息(方法), 当此大对象的分析程序完成后,再将此大对象细分为比较小的对 象,再依照上述的方式作一个分析的程序;不断地细分下来后,我 们可以发现,其实系统已经被我们分成一个个的小系统了,分析与 实作上又更容易了一些。 例如:我们有一个温度监控系统要实作,此系统使用 Pt100 温 度传感器量测锅炉的温度,此系统会每隔 2 分钟作一次读取温度的 动作;当温度达到 100 度 C 时会输出一个电压激活继电器,并进而 引起警戒声响。 以上的例子中,如果我们以对象、属性、事件、方法的程序分 析的话,也许可以分为以下数项: (1) 对象:本监控系统即为一个大对象。 (2) 属性:本系统温度计是 Pt100;每隔 2 分钟读取一次资料…。 (3) 事件:温度达到 100 度 C 即为本系统的一个事件。 (4) 方法:系统会因事件的发生而激活继电器,并进而引起警戒声响。 第 44 页,共 386 页以上是相当大概地将系统依设计程序分类成四项,同样地,我 们也可以将此系统中的另外几个子系统再依对象、属性、事件、方 法四个项目分类,直到读者认为已经可以了为止。 Visual Basic 的线上说明中也经常会出现这些相关的信息,例 如 MSDN 光盘片中的画面里,如果我们查看某一对象的说明,就会 看到如图 2-1-1 的画面 图 2-1-1 组件相关说明 了解接口成员的意义,再去看线上说明时,就比较能够知道程 序设计的大概;而透过这些接口成员的运作,程序设计就像是在操 作实际的对象一般,由最实际的方面去想象,最后当然还是要由程 序来完成我们脑中的想象,不过,如此的对象化思考过程其实对于 系统设计人员来说是相当地重要而直接。将对象、属性、事件、方 法四个程序反复地思考,就是建立 Visual Basic 程序的基本步骤, 这些步骤若以叙事的方法说来会像是: 我该用什么控件在画面上呢?这个控件应具备的特性有那些呢? 需要作变更吗?这个控件如果和使用者互动时,我希望可以处理那 些使用者的动作呢?针对这些动作,我该如何撰写那些程序代码来 因应? 将上面的这些话放在不同的控件上,反复地思考、设定、写程 序,一一地将问题提出,也一步步地解决,这是设计 Visual Basic 程序上的一个重要程序。 第 45 页,共 386 页2-1-3 以 Visual Basic 发展系统 执行 Visual Basic 光盘片上的 Setup.exe 执行档后,依照 VB 安 装向导的指引,一步步作完安装项目的选择,便可以成功地将 Visual Basic 安装至计算机里面;欲执行 VB,必须在计算机左下角的”开 始 ”\”程序集” \”Microsoft Visual Basic 6.0”中选择 Microsoft Visual Basic 6.0 的程序如图 2-1-2 所示: 图 2-1-2 激活 Visual Basic 6.0 Visual Basic 是一个所见即所得的快速开发软件工具,在实际 的发展流程上,它分成二个部份:画面设计及程序撰写。画面设计 的结果就会是未来程序执行时使用者可以看到的样子,现在的设计 中,引用了大量的可视化组件,这些可视化组件可以有效地简化我 们的设计工作,完成了可视化的画面设计后,接下来只要将相关的 动作流程以循序的程序代码予以完成即可。选择 Microsoft Visual Basic 6.0 后,随即出现如图 2-1-3 的选择画面。 图 2-1-3 项目建立选项 第 46 页,共 386 页在上图中有许多的选项,在不同的目的下必须选择不同的发展 项目,通常我们选择『标准执行档』作为开发的项目,此类项目被 开发出来后可以编译成窗口环境下的执行文件,并且可以作成安装 程序,散发到其它的计算机上执行。 选择了标准执行档后,接着出现设计环境的画面如图 2-1-4。 图 2-1-4 项目发展环境 图中的最上方是菜单及工具列,用来执行相关的功能;另外图 中标出 5 个区域,以下分别说明此 5 个区域: 『 工具箱』:设计系统时所需要的可视化对象及功能性对象的集中 摆放处。当我们设计画面或是功能时,一定会需要各式各样由微软 或是其它的协力厂商(3rd-party)所制作的对象,我们称这些被用来 设计系统的对象为控件,透过这些控件的运作,我们就可以轻松地 设计所需的画面或功能。新增多个控件后,工具箱中的控件就会多 起来,看起来就可能像图 2-1-5。 第 47 页,共 386 页 图 2-1-5 工具箱及控件的新增 项目一开始并不会出现所有 Visual Basic 所提供的控件,只会出现 部份基本的控件(称为预设控件),其它的控件在需要引用时,必须 透过『设定使用组件』的方式来加入到工具箱里面。这些控件大概 分为可视化及功能性二类,如果是可视化控件的话,在设计初期就 可以看到控件所造成对象的外观,以后程序执行后也会是如此的样 子;如果是功能性控件的话,则设计时期只能看到一个代表的图标, 无法了解真正的执行过程,必须等到程序执行后才能看到真正的结 果。 『 项目管理区』:现在的系统设计不像以前那样使用一、二支程序 就可以搞定;通常一个系统程序需要的内容是相当多的,这些内容 被项目总管所管理。其内容包含有专门处理画面的窗体(Form)集 合、专门处理函数的模块(Module)集合、专门处理类别(Class)的集 合,也包含有处理资源、自制的使用者控件等等,每个不同的部份 都被分开,以便于程序设计者可以一目了然。当欲处理某一部份时, 只要在项目总管里双击该项目即可。系统发展一大起来,项目总管 就可能会像图 2-1-6 的样子。 第 48 页,共 386 页 图 2-1-6 项目总管内的各区 『 实时运算区』:写程序总是会遇到困难或是瓶颈,一旦有问题时, 必须能够在必要的地方中断程序的执行,然后找到问题的所在点, 予以解决,这是侦错环节的一部份。要找到问题点,通常需要进行 一些程序代码的测试,或是表达式的结果查验,这时就需要实时运 算窗口提供的环境来作程序代码的试算。其中,断点的设定在进行 侦错时是相当重要的,所谓的断点就是在程序的某一行设定一个卷 标点,当于 Visual Basic 环境下执行程序时,遇到此卷标点就停下 来,等待设计者的下一个步骤或指令;在断点处之前存在(指的是 还在内存中而未被消灭)的任何变量都可以拿来作运算,结果也会 马上出来,这提供了程序开发者一个良好的测试工具,是开发系统 程序所不可或缺的项目。运算过程中的画面就如图 2-1-7。 图 2-1-7 实时运算窗口 『 画面设计区』:真正的画面就在这里被系统设计者摆放上去。这 上面会摆放着设计者由工具箱中所选择的各种控件,各种的控件有 第 49 页,共 386 页各式各样不一样的功能及画面,设计时必须了解到自己的需求及控 件的内容,才会设计出比较符合需求的画面来。每一个画面设计区 都称一个『窗体』(Form),也相当于在 Windows 环境中所看到的一 个画面(一般称为窗口)。如果设计的系统所需的不同画面为 10 个, 那么在设计时就需要设计 10 个画面来符合系统的需求;需要增加 时,只要在项目总管中按鼠标右键,选择新增窗体即可。通常每个 窗体中的控件都是完全独立,互不干扰,如图 2-1-8 的画面。当需 要跨越不同的窗体作操作时,必须在程序代码别指明所要操作的窗 体的名称,否则一律以程序代码所在的窗体(或其上的控件)作为操 作的标的。 图 2-1-8 二个不同功能的窗体 『 程序设计区』:此部份是真正写入程序代码的地方,不管画面设 计得如何,最终总是要在这个地将系统要执行的步骤写入。程序代 码的写作并不是类似一张流程图般从头写而尾,一个开始,最后一 个结束;而是针对某一个对象的某一个事件发生时,该对象应该要 有什么样的反应或作为来写程序代码,程序代码的写作方式完全采 用 事件驱动的方式,一旦该事件引发后便执行此段我们预先写入的 程序代码,当此段程序代码执行完毕后,系统就处于闲置状态。例 如一个按钮被按下了,我们也许在这个按下的动作事件里头写了一 个激活声霸卡播放一段音乐的程序,当这段程序被执行完毕,除了 原本就安排的固定动作外(如定时器的固定转询动作),系统便不再 执行其它的程序了。设计的样子如图 2-1-9。每一个程序区段以 Sub…End Sub 分隔开。 第 50 页,共 386 页 图 2-1-9 程序编辑窗口 在程序中最常发生的事件莫过于鼠标(Mouse) 和键盘 (Keyboard)二者,这是使用应用程序时最常使用的二个输入装置; 因此我们也最常针对这二类事件作处理,例如 Click、 DbClick、 MouseMove、MouseDown、MouseUp、KeyPress、KeyDown、KeyUp 等等,由名称大约可以知道其事件发生的时机;将这些事件配上对 象后,就是针对该对象的事件程序了。 2-1-4 Visual Basic 的环境模式 Visual Basic 分成几种模式,设计模式、执行模式及 中断模式。 设计模式:设计画面,撰写程序代码时所处的环境称之为设计模式, 这很类似于我们在以前 DOS 发展程序时的整合发展环境,设计者 想法均是在此环境中先形成。 执行模式:当程序发展到一个阶段后,就可以进入执行模式测试一 下执行的结果,此模式下的结果一般会与编译成为执行档后执行的 结果一样(部份 API 例外),Visual Basic 执行模式下所执行的程序代 码并不是先编译成执行档,而是直接利用 Visual Basic 环境执行, 如果确定程序完成后,还是要执行编译的动作,让最后的执行档可 以被建立。 中断模式:此模式则是当设计者在程序中设下断点,而执行的过程 中遇到此行时,程序即在此中止,等待设计者的下一步指令,设计 第 51 页,共 386 页者可以继续执行、单步执行、跨函数执行或进行断点前的变量运算。 如图 2-1-10 所示。 图 2-1-10 三种模式 整个系统的发展过程中,就是在这三种模式间不断交错进行 着,直到确定项目的发展成功。 2-1-5 发展项目步骤 面对 Visual Basic 看似复杂的环境,也许会有令人望之却步的 感觉,其实了解程序后,很容易上手的。Visual Basic 的开发环境 分成二个部份—设计与执行;程序在设计模式下被开发至一定程度 后,就可以在执行模式下观看程序执行的结果,以作为修正的参考。 面对开启的项目,以及上述的几个区域,脑中想着对象、属性、 事件、方法四个步骤,以下是一个发展的顺序,供读者作参考: 1、 拉动窗体的画面至适当的大小。窗体是我们主要的门面,也会 是我们最早接触到的,首先是将此窗体拉至我们希望执行起来 所想看到的样子。需要的外观控件可由工具箱中取出来,当在 工具箱中找到希望的控件时,首先点选,接着在画面上以拖拉 (Drag-Drop)的方式拉出希望的外观。 2、 对象在画面上被拉出外观,并确定大小位置后,接着按下 F4 叫 出属性窗口改变重要属性。首先是 Name 属性的变更,程序撰 写过程中控件的操作就要依此名称而决定。其它的属性依需要 第 52 页,共 386 页而定;例如,外观颜色希望不一样时,此时需要改变 Color 的 相关属性;显示在控件的文字需改变时,此时要改变 Caption 属性…等等。通常只需要改变部份的属性即可,很多的属性都 只要保持默认值就可以了。属性数量众多,一般常用的属性可 由属性名称即可知道其作用,其它少见的属性就必须查看资料 才能得知用法,笔者建议查看 Visual Basic 的线上说明,可以得 到更多的说明。线上说明必须经常使用,安装 Visual Basic 时最 好也将 MSDN 装好,以便使用线上说明。 3、 不断地将需要的控件以拖拉的方式放到窗体上,并且设定各个 对象的重要属性、外观。什么是所需要的控件呢?读者自行设计 的系统,当然必须在心里先有个底,有个大概的样子后,再由 工具箱中取出;需要的控件包含有外观性的控件及功能性的控 件。 4、 考虑画面上的各个控件之间的排列关系,排出自己认为最好看 的样子;不妨也问问身旁的人或是未来的使用者,听听他人的 看法。 5、 每一个画面上的对象的设计都是为了可以和系统的使用者产生 互动,构思每一个对象的动作就是接下来的步骤了。 6、 把动作写入程序代码编辑窗口中。在程序代码的编写过程中, 均是以对象和外界的互动为第一考虑的重点,再以此发展下去。 例如,某一个按钮被按下后,画面上绘图一个曲线,当使用者 在曲线上点一下鼠标的左键时,出现一个十字光标,而点下鼠 标的右键时,出现一个弹出式的选单…诸如此类的考虑。在上 述的例子中,我们就必须在按钮的 Click 事件中写入绘图的函 数,而在图形的 MouseDown 事件中写入绘线及弹出式菜单的程 序代码。依照这样的思绪,一步步地把程序代码完成。写作的 过程中,可以程序代码编辑窗口中的左上方先选择对象,再由 右上方选择事件,以对象搭配事件,成为事件驱动程序撰写的 主轴。笔者建议读者可以多看看其它的应用程序,注意其人机 接口的考虑,可以作为我们发展的参考,学习永远是进步的踏 脚石。 第 53 页,共 386 页7、 完成后的项目,可以按下 F5 或菜单上的执行选项将项目执行起 来,就可以看到项目执行后的画面结果,试着操作各控件及其 事件的反应,也可以了解设计的事件程序是否恰当。 8、 测试的过程通常需要不少的时间,在修改与测试的过程中,中 断是一项经常被使用到的功能。将光标停留在需要中断的某一 行,按下 F9 即可订定断点,一旦程序被执行至此,即会产生中 断而停止执行,这时可在实时运算窗口中进行各项参数的测试, 也可按下 F8 使程序单步执行下去,或直接按下 F5 立刻让程序 执行下去。 9、 需要修改程序时,可从执行模式回到设计模式,待修改完毕后, 再按 F5 进入执行模式。通常我们需要在二种模式之间不断切 换,直到系统的执行结果符合我们的要求。 10、 完成项目的设计工作后,先编译成执行档,再利用 Visual Basic 提供的『封装暨部署向导』制作项目的安装程序。至此才 算完成项目的制作。 2-1-6 Visual Basic 和操作系统的关系 基本上,使用 Visual Basic 作程序设计,并不需要特别注意 Windows 的细节,因为 Visual Basic 都帮设计人员处理好一些背景工 作了。不过,稍微了解窗口操作系统可以帮助我们在设计的过程中 更加清楚系统的运作方式,对于功力的提升有绝对的帮助,使用 Visual Basic 会更加得心应手;一段时间的设计工作后,可能也会需 要作一些比较深入的题目,也许会碰到程序流程的瓶颈,了解它们 之间的关系后,对于解决问题可以有不小的帮助。 首先谈谈 Windows 的操作系统。计算机开机后,均会进入窗口 操作系统,此操作系统实际上是将程序代码加载内存中,接着就由 操作系统掌控整部计算机,所有的动作必须先经由操作系统处理 后,才进行实际的动作。我们可以想象一个情景,一部窗口操作系 统的计算机其实就是存在一个总管在掌控所有的事情,它就是一个 管理中心(也就是操作系统),管理中心负责将整个系统的信息作一 个分派的工作,该是那一个程序的信息,就将该信息丢给该程序, 该程序若需响应该信息,该应用程序自然会有程序代码被执行。 第 54 页,共 386 页同样地,我们希望透过计算机帮我们作事情时,一定须要一些 计算、显示、发出声音…等等的动作,这些动作的形成必须由管理 中心藉由 CPU 予以实现,因此,我们所希望的动作就必须先向管 理中心(也就是 Windows 的操作系统)申请,如果管理中心许可的话, 再将我们的工作排入它的工作流程中,接着系统会控制计算机依次 地完成工作,我们就可以等着我们要求的工作被执行。 考虑如图 2-1-11 的情形,当外界的讯号(如图中的 Mouse 和 Keyboard)进入操作系统时,并不是那一个应用程序先得到该信息, 而是由操作系统先拦截该信息,接着由操作系统判断该信息应该属 于那一个程序处理,接着将这个状态以信息的方式传给该应用程 序,并由该应用程序接收处理。 图 2-1-11 周边、操作系统与程序的关系 在 Visual Basic 撰写的过程序,经常会疑问的是:我的程序如 何知道按钮被按下了?它又是在什么情形下被按下的?我的程序如 何判断到底 Mouse 是不是位于某一个特定的范围内?…等等;如果 了解图 2-1-11 后,我们尽管针对 Visual Basic 程序中的各个对象的 事件写下希望的程序代码,而若该事件的参数列中含有参数时,直 接拿来用,不需考虑其它的检测或是参数传递的问题,因为 Windows 操作系统已经帮我们作好事件检测和参数传递的事件了, 只要是属于我们的程序事件,操作系统就会送来给我们的程序作处 理,程序平常只要等着 Windows 通知就可以了。 由 Windows 传递过来的信息 Visual Basic 还会帮忙作翻译,让 我们的 Visual Basic 程序可以很简单地就取得所传来的资料,此资 料和原先就预备好的程序(若预先写入该事件的程序代码)就一起执 行了。这也是以前使用 SDK(Software Development Kit)和 C 语言写 窗口程序时享受不到的好处。 第 55 页,共 386 页2-2 Visual Basic 的组件引用 开启 Visual Basic 后出现的工具箱通常只有一些预设的控件, 如果建立的是简单的项目,也许这些就够了;如果要建立的项目比 较复杂,或希望项目的成果可以像一般的窗口程序一样光彩耀眼, 功能较多时,那就有必要引入较多的其它控件,这就是组件的引用。 2-2-1 引用步骤 刚开启的工具箱中的『工具』不够用时,必须在『项目』\『设 定使用组件』选择适当的控件档案,程序如图 2-2-1。 图 2-2-1 引用控件步骤 透过上述的 3 个步骤,工具箱中就会出现我们选择的控件的图 标,点选此图标并在窗体上拖拉,即可在系统中设计出所需的画面 或功能。这些控件在 MSDN 上都有详细的说明,使用进阶的控制时, 就必须由 MSDN 中得知其说明和范例。 2-2-2 如何了解控件 Visual Basic 中可以使用的控件非常地多,针对不同的功能或 是画面,都会有相对的控件可以运用。Visual Basic 的版本中,除 第 56 页,共 386 页了普及版可使用的控件较少外,专业版及企业版都相当多;其实任 何的参考书都不容易将所有的控件介绍详尽,不过常见及使用率高 的控件都会在一般的参考书上找到解释;部份的参考书针对书本上 的特殊功能也会介绍一些特殊的控件。最好的参考资料,笔者认为 还是微软的线上使用手册,其中对于内建控件及扩充控件的基本概 念介绍得相当仔细,而对于属性、事件、方法也都有详尽的列举及 介绍,当然也会举部份的例子作为使用范例,想要彻底了解各个控 件的话,还是看看 MSDN 吧 !只要把握住基本概念,再加上牢记『对 象 』、『属性』、『事件』、『方法』的思考顺序和努力测试,控 件的使用应该是不成问题的。 2-2-3 组件的分类 Visual Basic 内的使用组件大概可以分成执行时期可见组件及 执行时期不可见组件二大类。 所谓的执行时期可见组件指的是在设计模式及执行模式下均 可看见其样子的控件,这类的控件通常被用来设计画面,诸如卷标、 文字框、图形框、按钮、工具列…等等,均属于此类组件;执行时 期不可见组件指的是在设计模式可以被设计者看见其代表图标(否 则设计者如何知道已经引入控件?),而在执行模式下看不见其样子 的控件,这类的控件通常为系统提供特定的功能,此功能不需画面 的特别支持,即可完成其份内的工作,诸如定时器、通讯控件、网 络传输控件…等等,均属于此类组件;如图 2-2-2。 图 2-2-2 可见与不可见组件 第 57 页,共 386 页图 2-2-2 中的通讯控件(长得像香港马会开奖资料盒的样子)和定时器控件(长 得像马表的样子)在设计阶段时均可看见,但在执行模式下就看不 见了;除了这二个控件外,此画面中的其它控件在设计时期与执行 时期均为可见。 2-3 常用组件介绍 在本书的实验范例中,经常使用到的控件,将会在本节中予以 介绍,而在各个实验中可能就不会花太多的篇幅在这些基本的控件 上,希望能读者有帮助。重要组件的说明部份可附录 2 所列之文献 【 3】。 2-3-1 卷标控件 Visual Basic 工具箱中的卷标控件(Label),专用于提供显示文 数字之用,在工具箱中的位置及外观如图 2-3-1 所示。 图 2-3-1 Label 的位置及外观 使用卷标控件设计在画面上的步骤如图 2-3-2。 第 58 页,共 386 页 图 2-3-2 Label 的使用过程 表 2-3-1 Label 常用属性 常用属性 说明 Name 对象名称,物放摆放完成后给予。是 第一个必须要设定的属性。 Caption 卷标的标题。 Font 显示字型的设定 BackColor 卷标的背景颜色 BorderStyle 标鑯的外框型式 卷标控件通常配合其它的对象一起使用,而提供其它对象的简 要说明标示之用,由于通常被用来标示之用,事件的使用就比较不 频繁。如图 2-3-3 即是用以标示一个文字框的用途。 图 2-3-3 Label 和文字框的搭配 2-3-2 按钮控件 此控件是使用率最高的控件,用于提供一个给系统使用者操作 用的按钮,在工具箱中的位置及外观如图 2-3-4。 第 59 页,共 386 页 图 2-3-4 按钮控件的位置及外观 其拖拉设计的步骤与上一小即的控件相同,而重要的属性则如 表 2-3-2。 表 2-3-2 按钮控件常用属性 常用属性 说明 Name 对象名称,物放摆放完成后给予。是第一个必须要设定 的属性。 Caption 出现在按钮上的文字标题。 Picture 希望出现在按钮上的图片,一般使用 ICON(32*32 像素) 较多。设定图文件后,还需设定 Style 为图形外观。 Style 决定是否出现图形外观,或只是文字外观。 Picture 摆放在按钮上的图形 DownPicture 当按钮被是按下时出现的图形 DisablePicture 按钮处于失效状态时出现的图形 调整好外观及相关属性后,由于按钮的目的是让使用者按下, 并进而执行某些功能,因此还需在按钮控件的 Click 事件中写入程 序代码,执行一些系统的动作。 图 2-3-5 是一个设计的例子,画面将一个按钮安排妥当后,双 击按钮即可在其事件程序中选择一个事件区段,将欲执行的程序代 码写入。 第 60 页,共 386 页 图 2-3-5 按钮控件的设计及事件 2-3-3 定时器控件 定时器的功能用于达到程序的自动化,其最大的效果在于固定 的时间内会执行所设定好的程序代码一次。比较特别的是,它只提 供定时的功能,但没有相关的画面,因此属于执行时期不可见组件。 它在工具箱中的位置及外观如图 2-3-6 所示。 图 2-3-6 定时器控件的外观及位置 常用属性说明如表 2-3-3。 表 2-3-3 定时器常用的属性 常用属性 说明 第 61 页,共 386 页Name 对象名称,物放摆放完成后给予。是第一个必须要设定 的属性。 Interval 定时器的间隔时间,每隔此一时间,定时器内的程序代 码就会被执行一次。 Enabled 决定是否激活定时器。 一旦设定其 Enabled 属性为 True 后 (一开始就设定或程序过程 中设定均可),定时器内的程序代码就会被定时执行,而程序代码 必须是写在定时器的 Timer 事件程序中的,如图 2-3-6。 图 2-3-6 定时器及其唯一事件 定时器的 Interval 属性所设定的值虽然是以毫秒为单位,但是 实测的结果并无法真正达到所希望的数值,通常会比设定的数值来 得久一些,例如设定 50 毫秒作一次扫描的工作,但因操作系统无 法实时的缘故,通常时间会在 50 毫秒以上,正常情形下不会差异 太多,第六章会有一个例子讨论到此点。 若设定间隔时间 1 毫秒,结果会如何呢?实际上是达不到 1 毫秒的。 2-3-4 图片框控件 当系统必须有部份的结果需要使用图形的方式表现时,图片框 是一个很好的选择,它允许我们以程序的方式在其内部绘图。此控 件在工具箱中的位置及外观如图 2-3-7。 第 62 页,共 386 页 图 2-3-7 图片框控件的位置及外观 常用属性说明如下表 2-3-4。 表 2-3-4 图片框控件常用属性 常用属性 说明 Name 对象名称,物放摆放完成后给予。是第一个必须要设定 的属性。 BackColor 图形框的背景颜色,以能明显表现差异为原则。 ForeColor 图形框的前景颜色,以能明显表现差异为原则。 大部份的绘图工作是在程序中完成的,只要在程序中指明绘图 的对象为此图片框,则绘图的结果均将显示在此图中,如图 2-3-8 所示。 图 2-3-8 图片框之设计、程序代码及绘图结果 第 63 页,共 386 页2-3-5 选项(Option)控件 有时候我们也需要在程序设计时,让使用者可以针对项目作必 要的选择,如果众多的选择中只能选其中的一个,这时必须使用 Option 控件,让众多的选择中只择其一。此控件在工具箱中的位置 及外观如图 2-3-9 所示。 图 2-3-9 Option 控件及位置及外观 常用属性说明如表 2-3-5。 表 2-3-5 Option 控件常用的属性 常用属性 说明 Name 对象名称,物放摆放完成后给予。是第一个必须要设定 的属性。 Caption 显示在此对象的标题文字,用以提醒使用者。 Value 决定是否选中此项,当为 True 时,圆圈中出现实心黑点 表示选择此项;若其它项被选中,则为空白圆。 通常很多项目一起作选择时,习惯把 Name 属性设成一样,使 这些控件变成数组,程序上也会比较简洁。设计上如图 2-3-10。 图 2-3-10 相关的选项放在一起 第 64 页,共 386 页图 2-3-10 就是用以选择 COM1 或 COM2 的一个设计例,利用程 序中检查 Value 值是否为 True,可以确定是否该选项被选中。由图 中也可看出,大部份的情形下,这些选项会用一个 Frame 控件将它 们框住,便于辨认其为一组相同的选项。同理,如果一个画面中有 数个群组的选项必须选择的话,一般也是使用数个 Frame 控件将其 分开来,免得混在一起了。 2-3-6 框架(Frame)控件 系统设计时所使用的控件一般说来都不少,控件一多就使得画 面看起来比较杂乱,使用框架控件可以将部份的控件集合起来,感 觉比较整齐。此控件在工具箱中的位置及外观如图 2-3-11 所示。 图 2-3-11 Frame 控件的位置及外观 此控件又被称为 Container,意为包含,也就是其它的控件可以 被含在这个控件里面。常用属性如表 2-3-6。 表 2-3-6 Frame 常用属性 常用属性 说明 Name 对象名称,物放摆放完成后给予。是第一个必须要设定 的属性。 Caption 出现在 Frame 上方的标题文字。 Font 决定 Frame 标题文字的字型。 ForeColor 决定标题文字的颜色。 上一小节中的通讯端口选择图例中,将二个通讯端口选择项框 住的就是 Frame 控件。 第 65 页,共 386 页2-3-7 几何图形(Shape)控件 程序设计中若需使用到几何图形,例如圆形、矩形、正方形、 椭圆形等等,就需引用 Shape 控件,此控件在工具箱中的位置及外 观如图 2-3-12。 图 2-3-12 Shape 控件的位置及外观 常用属性如表 2-3-7。 表 2-3-7 Shape 控制常用的属性 常用属性 说明 Name 对象名称,物放摆放完成后给予。是第一个必须要设定 的属性。 FillColor 该控件的填满颜色。 FillStyle 填满的型式选择。 Shape 决定此控件的几何形状为何。 以本书而言,最常使用此控件作为灯号的显示(以实心表示), 以改变 FillColor 的方式就可以动态地改换显示的颜色,相当方便。 设计及使用如图 2-3-13。 第 66 页,共 386 页 图 2-3-13 Shape 控件的使用 2-3-8 文字框控件 让使用者最方便的输入方式,就属文字框了,其主要的工作就 是输入文字或显示文字的结果。此控件在工具箱中的位置及外观如 图 2-3-14。 图 2-3-14 文字框控件的位置及外观 常用属性如表 2-3-8 所示。 表 2-3-8 文字框常用属性 常用属性 说明 Name 对象名称,物放摆放完成后给予。是第一个必须要设定 的属性。 Font 决定字型。 ForeColor 决定字型颜色。 第 67 页,共 386 页Text 决定文字框内的内容。 MultiLine 是否允许多行显示。 ScrollBar 是否出现滚动条(只在 MultiLine 为 True 时成立)。 使用 Text 属性,就可以设定文字框的内容或读取其内容。部份 时机还会使用到 SelStart、SelLength、SelText 等属性,用来表示使 用者选中的内容。 2-3-8 影像(Image)控件 程序设计中若需使用到几何图形,例如圆形、矩形、正方形、 椭圆形等等,就需引用 Shape 控件,此控件在工具箱中的位置及外 观如图 2-3-12。 图 2-3-15 Image 控件的位置及外观 常用属性如表 2-3-9。 表 2-3-9 Image 控制常用的属性 常用属性 说明 Name 对象名称,物放摆放完成后给予。是第一个必须要设定 的属性。 Picture 欲加载控件中的图片。 Stretch 是否调整 Image 控件至图片大小。 以本书而言,经常透过 Image 控件 Picture 属性的改变达到变换 状态的目的。 第 68 页,共 386 页2-4 程序的编译与安装 如果已经使用 Visual Basic 将所需要的功能完全实作出来,也 在 Visual Basic 环境下执行过了,确定没有问题时,就应该将其先 编译成执行档;当散发给他人使用时,更要将程序制作成安装程序, 以正常的安装程序安装到他人的计算机中。本节大略说明编译及安 装。 2-4-1 程序的编译 一旦使用 Visual Basic 将所需的功能实作成功后,应该先将其 编译为执行档,在编译为执行档时,Visual Basic 还为将程序从头 到尾仔细地作检查,看看有什么地方写错,若依然有部份程序代码 不对,Visual Basic 会要求你再作修正;当你修正完毕后,一样再 次选择编译,使 Visual Basic 再次帮你作编译的工作,选择编译的 过程如图 2-4-1。 图 2-4-1 编译的选择 如果程序不大,一般只要几次的修正就可以过关;若程序比较 大,工程师通常会有部份地方没有作好,而进行编译就会发生错误, 编译程会停在错误发生的地方,而且会停在第一个错误所在的地 方,当这个错误修正后,可能还会有第二个错误,所以编译过程会 因写程序的多少及错误的多寡而有不同的时间;如果在以 Visual Basic 环境测试时,可以多测试几次,将可能的情形均测试过,比 较可以减少不断地在编译和修改之间的时间,不过能一次就 OK 的 第 69 页,共 386 页Visual Basic 程序也不多见就是了(纯笔者看法,不代表其它的工程 师 )。 2-4-2 程序的封装及散发 测试过及编译过的程序如果要散发到其它使用者的计算机上 时,通常采用的方式是制作安装程序,如果我们自行制安装程序是 相当不容易的事情,在 Visual Basic 中有一个专门帮我们作安装程 序的工具,那就是『安装和布署向导』,此向导存在于 Visual Basic 菜单中,如图 2-4-2。 图 2-4-2 向导所在位置 执行此向导后,只要随着向导的步骤一步步地往下作,就可以 完成安装程序的制作,将此安装程序放置于磁盘或是烧到光盘片中 交给客户或其它的使用者就可以了。 一开始的执行的画面如图 2-4-3,首先必须在上方的文字框中 输入所要编译的项目名称,我们也可以在其右方的『浏览』按钮中 去寻找所要编译的项目;确定所要编译的项目后,必须选择图左上 方的『封装』按钮,随即开始进行一连串的程序。 图 2-4-3 封装-1 第 70 页,共 386 页若执行不是最新的执行文件,程序会要求重新编译,此时就可 以看到如图 2-4-4 左方的图形,而若一切正常则是进行如图 2-4-4 右方的动作—寻找需要的档案。 图 2-4-4 编译或搜寻档案 接下来的几个步骤如图 2-4-5,只要依照指示选择即可。 图 2-4-5 接下去的几个步骤 接下去只要依照向导画面的指示,选择几个项目,一般说来均 可顺利地完成安装程序的制作。 第 71 页,共 386 页2-5 软件与硬件的沟通 前面的部份是针对 Visual Basic 里面的观念和常用控件作一个 简要的说明,在接下来的部份将把软件和硬件间的关系作一个说 明。 2-5-1 程序到硬件 程序如何由总线接触到适配卡,并使适配卡执行控制或回传卡 片状态,是一个相当不容易的问题,在以往 DOS 的时代里,利用 Input/Output 指令可以直接控制到计算机上的2018香港马会开奖现场(其实也就是第一 章提到的缓存器),但在 Windows 操作系统中已经不能直接这样作 了。在 Windows98 中,也许我们可以使用 Visual C++,利用 inp 和 outp 的输出入函数指令,达到和 DOS 模式下相同的缓存器控制功 能;但在较高阶的系统中(如 Windows NT、Windows2000),这样的 作法也是不行的,这是由于 Windows 操作系统是一个受保护的系 统,微软认为,如果随便就可以下达硬件控制指令的话,将会危害 到整个系统的稳定性,但总要有个方法可以让他人发展硬件控制的 程序,所以硬件的操作通常是利用微软的 SDK(Software Development Kit)和 DDK(Device Development Kit)来完成。 Visual Basic 本身的程序无法直接控制到适配卡,必须透过 DLL 或是额外 OCX 控件的协助才行(如第一章所述),当然啦!若由使用 者使用这些复杂的程序才能直接控制适配卡的话,可能只有少部份 的使用者愿意花偌大的精神和时间去挑战;如第一章所述,通常厂 商会提供一些方便的途径供其使用者可以快速地达到控制的目的, 而 DLL 就是一般厂商最常提供的程序了。 因此透过厂商所提供的 DLL 或是 OCX,我们所写的控制程序 码就经过层层的转译,一直到卡片上的缓存器;而检测程序代码则 经由相反的管道将状态传回到我们所写的程序里。在 Visual Basic 的程序中,这些额外的 DLL 必须事先告诉 Visual Basic 编译器,以 便 Visual Basic 知道如何建立程序和 DLL 的连结,通常这些 DLL 被宣告在模块中,情形如图 2-5-1 所示。 第 72 页,共 386 页 图 2-5-1 DLL 宣告在模块中 接着在程序里,当需要呼叫到某一个厂商提供的函数时,只要 输入函数的名称,该函数中所需的参数也会和一般的 Visual Basic 函数一般地显示出来,供程序设计师了解状况,如图 2-5-2 所示。 图 2-5-2 使用 DLL 函数时,也会出现参数提示文字框 透过以上的程序,当我们利用函数库中的函数和适配卡沟通 时,完全不必担心中间的流程,操作系统和函数库自然会帮我们作 好转换的工作,程序也会依设计的流程及函数而执行。 2-5-2 控制的程序 在本书主要是说明 PCI 的适配卡,控制的程序上有一些必要的 步骤,这些步骤主要是考虑到 PCI 控制芯片的操作。 第 73 页,共 386 页PCI 接口的设计相当地好,使用 PCI 芯片的适配卡,其内部均 会记录这片卡片的相关信息,这些信息均以数值的方式记录在芯片 内,不同厂商的记录是完全不同的,因此不会有相冲突的情形发生。 在操作系统发现卡片而安装驱动程序时,也可以由这些记录得到必 要的安装信息,表 2-5-1 是部份 PCI 适配卡的内部信息(取自泓格科 技 )。 表 2-5-1 部份适配卡的 PCI 芯片信息 卡片编号 卡片描述 Sub_vendor Sub_device Sub_AUX PIO-D144 144 * D/I/O 80 01 00 PIO-D96 96 * D/I/O 80 01 10 PIO-D64 64 * D/I/O 80 01 20 PIO-D56 24* D/I/O + 16*D/I + 16*D/O 80 01 40 PISO-P32C32 32 + 32 80 08 20 PISO-725 8*DI + 8*D/O 80 08 40 由表 2-5-1,每个卡片的相关编号均不相同,利用表中的信息 后,PCI 适配卡使用前就要经过以下的二个重要程序: 1、 卡片的初始化。这部份是使用厂商提供的初始化函数,以表 2-5-1 为例,如果使用到表中的卡片,那么在执行初始化函数 时就要给定表中所列的 Sub_vendor、 Sub_device、 Sub_AUX 等信息,以便针对该卡片作初始化。更先进的驱动程序中, 如果确定卡片号码的话,参数部份就可以省略,这在大多数 的驱动程序已经见到这种作法了。 2、 取得接口的2018香港马会开奖现场及中断。在第一章讨论到资料的交换和2018香港马会开奖现场 及中断有着密切的关系,而 PCI 接口型式的卡片乃是由芯片 和操作系统合作而取得可用的2018香港马会开奖现场及中断,透过厂商所供的 DLL 正可以正确地取得这二个信息而实行控制。 其它和卡片相关的输出入动作必须在上述的二个程序之后,这点 必须要特别注意,否则极有可能造成操作系统的当机。 2-5-3 基本函数 透过上述的讨论,本小节再将卡片控制时的函数作说明。以本 书所使用的适配卡为例,将卡片作初始化及取得卡片的相关2018香港马会开奖现场、 中断是最先必须被处理的事宜,这个部份大多以下列的三个函数处 理: 第 74 页,共 386 页‘初始适配卡驱动程序,并配置资源 Declare Function PIODIO_DriverInit Lib "PIODIO.dll" () As Integer ‘取得适配卡的相关信息,尤其是2018香港马会开奖现场及中断 Declare Function PIODIO_GetConfigAddressSpace Lib "PIODIO.dll" (ByVal wBoardNo As Integer, wAddrBase As Long, wIrqNo As Integer, wSubVendor As Integer, wSubDevice As Integer, wSubAux As Integer, wSlotBus As Integer, wSlotDevice As Integer) As Integer ‘关闭驱动程序的资源 Declare Sub PIODIO_DriverClose Lib "PIODIO.dll" () 以上的三个函数分别是资源的初始化、建立及关闭,是控制适 配卡必须使用到的三个函数;适配卡一开始使用初始化及建立函数 作为开端,而结束适配卡的控制时则使用关闭指令清除驱动程序所 使用的资源。函数的宣告分成了几个部份,以第二个函数为例,简 述如下: 1、 Declare Function:Visual Basic 规定的宣告开端,Function 表 示被宣告的是一个函数,它会有传回值;若 Function 改为 Sub 则表示没有传回值。 2、 PIODIO_GetConfigAddressSpace:函数名称,此名称可以在 Visual Basic 函数中被呼叫。此名称的定义通常有意义,可以 由名称看出函数的定义。 3、 Lib “PIODIO.DLL”:表示上述的函数是由 PIODIO.DLL 动态 连结函数库所提供,当此函数被 Visual Basic 程序呼叫后,实 际的执行程序代码是位于该动态连结函数库中,操作系统也 会动态地自该函数库中提取执行码至内存执行。 4、 括号中为参数列表。不同的函数所需的参数并不相同,若欲 函数成功执行,必须给规定的参数,而且参数型态也必须正 确 (宣告为 Long 就不能给 Interger)。 5、 As Integer:此函数的传回值。传回值有二种情形,一种是状 态数值的取回,一种是函数执行状态的传回,至于是那一种 传回值,必须依函数的说明而定。 实际控制适配卡时一定使用超过此数目的函数,各个函数的意 义均不相同,使用的时机也视情形而定,笔者将在各章节中依实际 的实验需求说明各函数的意义。 第 75 页,共 386 页常问问题集(FAQ) Q:我可以安装普及版的 Visual Basic 而使用其它版本的组件吗? A: 通常是不行的,笔者建议直接购买专业版或企业版。 Q: 我如何知道控制的事件及时机? A: 一般由事件名称中可以窥见端倪,部份事件必须参考线上说明 了解何时会发生;一些专讲 C 语言的书也会说明事件,你可以 找些 C 语言的书看看。 Q: 控件的使用说明应如何取得? A:安装 Visual Basic 时,系统会要求你一并安装 MSDN 光盘,这 二片光盘片中就含有所有 Visual Basic 提供的控件说明;若你 买的是其它厂商所开发的控件,该厂商也会提供使用说明。 Q: 如果定时器无法准确计时,我的延时程序如何撰写? A:控制时经常使用延时(TimeDelay)无法以 Visual Basic 本有的误 能达到,笔者是由 API 中的 GetTickCount 函数达到此功能, 这在第五章以后会使用得很多。 Q: 安装程序拿去别拿计算机安装后,因为少了某个 DLL 而无法 执行,怎么回事? A: 这是因为你使用了其它的 DLL,但在制作安装程序时忘了加入 这个档案,你必须重新制作安装程序,并在安装程序中加入此 档案。 第 76 页,共 386 页本章习题 1、 ”有一个控制系统可以监视并控制某一锅炉的温度,监控 乃是透过 RS-232 为之,当控制者透过 RS-232 下达升温时, 监控系统会激活升温设备使系统升温”,在以上的系统描 述中,请分离出对象、属性、事件、方法各为何。 2、 试说明对象、属性、事件、方法用于系统设计的程序。 3、 说明引用延伸控件的程序。 4、 设计一简单的画面,上面有按钮、卷标、文字框,执行 时的现象是在文字框中输入文字,而按下按钮后,文字 框中的文字出现在卷标上。 第 77 页,共 386 页第三章 数字输出入接线方式 实验时所使用的接线方式有的是使用延伸板,有的则是直接连 接讯号的来源,不同的讯号连接方式其实也应有不同的考虑,而且 和实际的讯号来源有关,本章将讨论一些常见的讯号连接方式。不 管所接的方式为何,我们的接线都必须考虑到电压、电流对于适配 卡或是系统的影响,通常必须将电压、电流限制在一定的范围内, 使得适配卡及计算机可以接受;面对实际的多样化自动化作业环 境,也衍生出很多种不同的接线方式。 数字输出入最主要的就在于检测到外界的状态变化,或是传送 表示状态变化的电压状态到外界,在不同的输入及输出环境下,就 产生了不同的接线方式,每种方式都有其应用的场合。一般生产工 业控制卡的厂商也会为了其产品提供各种不同的延伸板,以适应不 同的应用场合。 3-1 TTL 接线方式 一般的适配卡最常使用的输出入接线方式是直接将讯号接入适 配卡的接点,让适配卡直接感测讯号的电位,本节讨论此种接线方 式。TTL 是 Transistor-Transistor-Logic 的缩写,意为晶体管-晶体 管逻辑。 3-1-1 讯号范围 数字输出入的讯号只有二个状态,不是 0 就是 1,从电位上来 说,不是高电位就是低电位,其额定电位是 0V~5V。高电位的范围 是 2V~5V 之间,而低电平则是在 0V~0.7V 之间。高低电平的讯号 范围的规定是考虑到芯片的可接受范围,由于芯片的设计使用的是 晶体管,希望晶体管能够了解状态,就必须以其可以了解的状态予 以输入。状态的改变最简单的情形是「有」、「无」之变化,使用 TTL 电平作实验时,我们理论上以 0V 表示一个状态,而以 5V 表 示另一个状态;例如以 0V 表示”关闭”,而 以 5V 代表”开启”,也 可 直接用低电位和高电位表示二个实际的状态,只要是沟通双方的语 言一至,自己定义的二种状态如何表示都可以,不混乱就好了。 第 78 页,共 386 页另外要考虑的是电流的范围,TTL 电平的电流范围必须落在 20 毫安(mA)以内,超过范围可能会对设备接点后面的 TTL 电子零件 造成影响,为了保证电流的范围,通常会在线路加上限流电阻,以 保护 IC。 3-1-2 接线方式 输入时: 若欲由适配卡上直接量测 TTL 电平的讯号,我们假设在适配卡 量测接点的设计上并无电平上拉电路或其它的特别设计,单纯就接 入的讯号作测试,那么在输入讯号方面的接线应如图 3-1-1 所示。 图 3-1-1 简单的 TTL 讯号输入接法 由图 3-1-1,+接点是正讯号的接点,而 -接点则是负讯号的接点, 经常可见的单接点讯号型式就是将负接点直接接到地线;为了造成 一个高电位,我们利用一个正 5 伏的电源接到正接点,如此的作法 将使得正接点和负接点形成了 5 伏的差距,自然就可以让接点后方 的适配卡检测到电位是高电位;特别注意到线路中使用了一个 330 欧姆的电阻连接电源和正接点,如果线路接通的话,这个 330 欧姆 的电阻所形成的阻抗将使得电路中的电流变成 I=V/R=5/330=0.015(A)=15(mA) 因此这个流通的电路中只存在 15 毫安的电流,保持在 TTL 规定的 范围内。 再看看图 3-1-1,如果将图中的接点切换到下方,将使得正接 点和负接点均连到地线,如此将使得接点后面的适配卡量到的正负 接点电位差是 0,自然就得到低电位的状态了。 第 79 页,共 386 页在实验室使用适配卡作相关的实验时,最常使用的接线方式就 是 TTL 的方式,其线路相当简单,以输入来说,如图 3-1-1 的方式 就可以形成一个数字输入的接点,当我们改变接点至 5V 或是 GND, 就相当于改变数字输入的状态,而在计算机端读取此接点的状态就 可以得到高电位或低电位的表示数值。通常适配卡上会提供 5V 的 电源,利用此 5V 的电源就可以连接相关的电路来做实验( 如图 3-1-1);另外也可以利用其它的电源来源供给实验所需的电源。 实验的过程中也可以使用一些诸如微动开关、按钮开关、极限 开关…等等的接点组件和图 3-1-1 中的正负接点作连接,便可透过 这些开关控制输入讯号的电压,从而控制该接点的状态,由适配卡 所读取的状态也因此而发生变化,图 3-1-2 就是一般常见的近接开 关,连接时可将 A 接点接到 GND, B 接点连接到电阻,再连接到 电源,C 接点可以连接到适配卡上的数字输入接点,当 P 接点未往 下压时,A、C 接点导通,适配卡上的输入接点和 GND 接通,故其 电压差为 0;当 P 接点被往下压时,B、 C 接点就会导通,5V 的电 源就会导到数字输入接点,因而形成高电位差状态。 图 3-1-2 常见的近接开关 330 欧姆和 470 欧姆都是经常使用的限流电阻,如果在电 路中看到这二个欧姆,通常就是拿来作限流电阻的。 输出时: 另外也要考虑到适配卡将讯号输出到外界的情况。若要求数字 适配卡上的数字输出接点输出 TTL 电平的讯号,那么当程控输出状 态为 High 或 Low 时,被控制的波道之电位应如图 3-1-3 所示。 图 3-1-3 数字输出时的电位情形 第 80 页,共 386 页首先看看输出高电位的情形,图 3-1-3 的左方是输出高电位的 状况,当命令该波道输出高电位时,在正接点和负接点之间的电位 差就是+5 伏,这个电压可以用示波器或是电表量到;另一方面,如 果命令该波道输出低电位时,在正接点和负接点之间就没有电位差 了,因此量到的就是 0 伏 (如图 3-1-3 的右边)。 TTL 数字输出的电流相当地小(最大 20mA),它不能拿来推动其 它的设备,所以当我们希望利用这种输出去推动其它的设备时,必 须先将讯号输出到讯号放大器去,再透过放大器去推动其它的设 备;例如我们要使用适配卡上的数字输出接点推动一个 220V 马达, 使其作开关的动作,在低电平时切断马达的电源,而于高电平时激 活马达电源,直接的联机是不可能的,我们必须有间接的方式;相 当常用的方式就是使用继电器,继电器的二端是互不连接,二端是 各自独立的电路,我们可以在继电器的控制端使用数字输出的接点 作控制,而其控制的结果则传送到继电器上的另一端,如此一来就 可以控制 220V 的马达,此种情形只要在我们所使用的适配卡上加 一片规格符合的延伸继电器板,就可以达到上述的要求,在后面的 章节中,笔者也会介绍到继电器板的使用。 由于 TTL 是电子电路上使用相当多的一种方式,由于其仅适合 在讯号等级的传送上,而且其为电路的基础,因此下面几节的方式 都还是以 TTL 为基础发展出来的,通常其推动下一级设备或电路的 能力都要来得强大。 以本书所讨论的适配卡来说,此种数字输出的电流也是相当 小,虽然其输出接点和 GND 之间有 5V 的电位差,但推动能力是不 足的;实验室实验时可以在输出接点和 GND 之间连接一个发光二 极管,透过发光二极管来验证输出动作是否成功,如图 3-1-4。 图 3-1-4 连接发光二极管至数字输出引脚 第 81 页,共 386 页3-2 O.C.门门接线方式 O.C.门门是 Open Collector 的缩写,意为集电极开路式的接线方 式,其利用晶体管的特性达到控制状态的目的,它可控制的电流比 TTL 要来得大,一般可以达到 100mA。 3-2-1 讯号范围 一般使用的晶体管有基极(B)、发射极(E)、集 电 极 (C)三支引脚, 依晶体管特性,只要在基极、发射极加入正向电压,而在基极、集 电极间加入反向电压即可达到放大的目的。由于晶体管属于双端口 输入组件(在晶体管上同时具有输入端和输出端),而讯号必须要有 二支引脚才能形成,因此晶体管三支引脚的其中一支就必须当成共 享端;依不同的应用情形,分别有共基极、共集电极及共发射极三 种情况,在我们使用的场合中,以共发射极是最常用的,以下的相 关解释也以共发射极为主。 依晶体管的特性,使用晶体管时可以利用其饱和区 (Saturation)、顺向活性区(Forward Active)及截止区(Cut Off)三个区 域的特性加以应用,如图 3-2-1。 图 3-2-1 晶体管的使用区域 在讯号使用的范围里,晶体管可以作为讯号放大之用,也可以 作为单纯开关之用;在数字输出入的情形下,当成单纯的开关是最 常见的情形,而且与机械式的开关比较起来,晶体管所形成的开关 之开闭动作要来得快多了。 以 NPN 型的晶体管为例,当 BE 极之间的电压大于 0.7V,他 们 之间就导通,只要 BE 之间流过极小量的电流,就可以使 CE 之间 第 82 页,共 386 页流过大量的电流(此电流也是由 C 极所提供的,并不是无中生有), 而此时的 CE 间的压降只有 0.2V(假设工作在饱和区)。 另一方面,当晶体管处于截止区时,BE 间及 CB 间均没有电流 流动,此时形同断路;如果当开关时,这时就是断路了。 因此,当透过基极和发射极之间的电压控制及他们之间所流动 的电流,就可以成功地控制集电极和发射极之间的电流流动;BE 间的电压大于 0.7V 以上就是导通,而小于 0.5V(最好更小),就是 开路(此时晶体管位于截止区)。至于在导通时在集电极和发射极(CE) 之间流有多少的电流除了要看集电极所在的电位之外,也要考虑到 所使用晶体管的放大倍率(β ),一般的简单算法就是以 I c/IB 来得到 这个倍率值,例如图 3-2-1 中的情形,在 I B 为 10μ A 的情形下,可 以得到的 I c 是 1mA,所以β是 1mA/10μ A=100,所以放大的倍率 约为 100。 3-2-2 接线方式 通常当成数字输出的情形下才会使用 O.C.门的型式,而使用这 种方式的数字输出也通常是因为需要推动的后端设备或电路所需 要的电流较大,或者负责输出控制讯号的引脚之电流过小。 最常见的 O.C.门门输出的电路图如图 3-2-2 所示。标示为 DO 的部份是由适配卡(本书说明适配卡,故用此为说明标的)所送出的 讯号,控制讯号就是送到晶体管的基极引脚上,而当基极上的电位 大于发射极 0.7V 以上时,集电极和发射极之间就导通了(由上一小 节的说明可得)。 图 3-2-2 以 Open Collector 的方式作数字输出 第 83 页,共 386 页由图 3-2-2,DO 的讯号通常会在 2.1V 以上至 5V 之间,在进入 到 B 极后,其和 E 极之间的电压已大于 0.7V,则 CE 之间就会导通, 这个导通的动作将使得电压 V 的电流流过 R,再经由 C 到 E 而到地。 由于 CE 间的压降约 0.2V,因此其损耗的电压降是相对来说是 相当小的;再如上述的方式考虑到放大倍率β,我们就可以知道在 C 极上可以流动的电流量,也可据以设定负载(R)及供应电压(V)。 如果所需控制的电流很大,单单使用一颗晶体管可能其放大倍 率还是不够,这时还可以使用达林顿晶体管,如图 3-2-3。由图中 可以看出达林顿的内部是由二级晶体管所组成,所以其放大倍率是 相乘的,故可得更大的电流控制效果。 图 3-2-3 利用达灵顿控制更大的电流 透过 O.C.门门的方式作数字输出控制也有其限制,每一个晶体 管都有一定的电压规定,使用此晶体管时必须在此电压范围内,以 免晶体管损坏。当所需要的电流比较大时,可以透过这种接线方式, 利用额外电源所提供的电流来推动设备点;而步进马达所需的电流 更大,所驱动器则使用达林顿晶体管为主要的组件。 第 84 页,共 386 页3-3 继电器的接线方式 继电器的使用时期已经相当久了,其稳定度很高,经常出现在 各式的工业控制场合中;透过继电器的作用,控制端和被控端可以 分开来,其作用是提供一个接点给电路使用。 3-3-1 讯号范围 继电器是由一端施于控制的电气讯号,此电气讯号将会使得线 圈动作,此动作导致簧片被吸合或弹开,继而使得接点接上或断开, 其示意图如图 3-3-1。图中的编号(1)是控制端,在其通过电流后, 编号(2)线圈产生力量,此磁力将会把编号(3)的开关吸附到左边, 这样的作用将使得原来 COM 和 NC 接点(Normal Close)相接的情形, 变成是 COM 和 NO(Normal Open)二点相接。 图 3-3-1 继电器的示意图 不同规格的继电器使用的电气范围也不同,可在市面看到的继 电器之使用电压有 6V、 9V、 12V、 24V、 48V、 100V、 110V、 200V、 220V 等等不一而足,当我们要给控制讯号时,首先就要查看所需的 控制讯号电压,免得电压不足而无法推动;每一种继电器也有使用 上的电流限制,有 1 安培、3 安培,甚至到几十安培的也有,它们 都有各自应用的场合;只要考虑到在继电器所接的情形下,工作电 压和限制电流可以容许的范围内就可以了,不见是愈大愈好;我们 在设备的规格说明上通常就可以看到类似 0.5A/120VAC 或 1A/30VDC 的文字,这段文就说明了该继电器工作于最高 120V 交流电源、0.5 安培 的情况下,或是最高 30V 直流、1 安培的情况下。 在数字输出入适配卡的应用场合中,继电器会有二种型式,我 们可以在一般适配卡或延伸板的说明书上看到所谓的 Form A 或 Form C 二种,Form A 型式的继电器在控制端有二个接点,分别是 用来接输出控制讯号用的,而被控端也有二个接点,这二个接点一 第 85 页,共 386 页般的标示是 COM 及 NO。 Form C 型式的继电器在被控制端就有三 点,除了 COM、 NO 外,还有一点 NC,图 3-3-1 就是属于 Form C 型式的继电器。 3-3-2 接线方式 由于继电器所需要的电压和电流已经超过了我们在 3-1 节所讨 论的 TTL 所能提供的电压和电流,所以我们通常也不会将继电器直 接上我们的适配卡输出波道上;不管使用的是厂商所提供的延伸板 或是自行找继电器作相关的接线,其接线的方式可以如图 3-3-2 所 示。 R3 R3 2SC1130 1 2 3 K1 RELAY SPST 4 3 1 2 (3) +V +V (1) (2) D/O 图 3-3-2 以继电器的方式作数字输出 我们之前已经讨论过使用晶体管可以使得数字输出的引脚所 输出的小电流可以控制在晶体管集电极上所通过的大电流,再加上 继电器需要较大的电压和电流,如此一来我们就可以连接晶体管和 继电器形成一个控制的回路。在这个控制的回路中是以 Form A 的 继电器为例,图 3-3-2 中,控制的流程如下所述: 1、 编号(1)是由数字输出入适配卡的数字输出引脚,当高电位讯号 输出后,电流会流入晶体管的 B 极。 2、 由于 DO 的输出是 5V,这已使得晶体管的 BE 极的所需导通电 压差(0.7V)达到要求,故会使得晶体管的 CE 极导通。 3、 继电器上的线圈因此而流过电流(所需电流由编号(2)的 V 提 供 ),其产生的力量将会使得继电器上的接点编号 3、4 接起来。 4、 由于线圈的作用,使用编号(3)电路形成通路,结果是电流流过 电阻,经继电器中的接点 3、4 而到地线;我们也可以将编号(3) 第 86 页,共 386 页处的电阻换成负载(例如马达或其它设备),那么接点接通时, 也等于电流流过负载。 继电器使得控制端和被控制端隔开,如果采用不同的供电回 路,将会使得二端互不干扰,只要被控端的电流变化不要很大,通 常控制端的电路也不会受到被控端的电流干扰(部份情形就算使用 了继电器也会因被控端的过大电流变化而使得控制端被干扰,严重 时将使得控制端当机)。 另一个考虑的重点则是在于动作时间。继电器的实际作动在于 机械,而机械的动作通常要比纯电子开关的动作来得慢,在继电器 的规格中也会说明该继电器在接上或离开时的所需时间,这个时间 通常是几个毫秒;如果要求这个开关的动作要很快,通常不能使用 继电器,而是使用电子式的开关(如晶体管或固态继电器)。 当继电器动作频繁时,如果被控端的电流比较大,在继电器簧 片的接触瞬间将会产生火花,如果经常产生的话,此继电器的接点 最后就会熔接在一起或烧蚀而坏掉。 第 87 页,共 386 页3-4 Photo-Couple 接线方式 不管是工业计算机或是家用计算机,其内部使用的电子零件都 是非常精密的,操作的电压一般也都是在正负 5V 或是正负 12V 的 情形;实际的外在环境情形可能不会那么完美,噪声到处都是,我 们所接的电路中的讯号也很有可能带有高电压,如果将这些讯号 (包括电源)直接接到我们的数字输出入适配卡,高电压就会往适配 卡方向打入,如此将会造成适配卡的损坏,甚至造成计算机损坏, 为了避开这种问题,讯号的传送可以使用光讯号来进行。 3-4-1 讯号范围 光耦合器(Photo-Couple)透过光的传递将高低电位状态传送到 另一端,即使输入的端的电压过高,由于是透过光的方式作传送, 光绝对不会对接点讯号的一端造成损坏,因此二端被分开,仅透过 光线传递,而达到保护的作用,这就是使用光耦合器的好处。图 3-4-1 是光耦合器的图标。 U3A Photo-Couple 2 3 41 图 3-4-1 光耦合器图标 一颗光耦合器可以看成是二个部份的组合,一是发光二极管, 它负责发出光线,另一端光敏晶体管,它是透过发光二极管的光将 晶体管基极作一个触发的动作,只要达到晶体管的要求电位,该光 敏晶体管的集电极、发射极之间就会导通。 光耦合式的接线方式在规格上还会写有如表 3-4-1 的各项。 表 3-4-1 光耦合器的几种规格项目 规格项目 规格数值 说明 Input voltage 5V to 30V DC 输入电压范围 Input impedance 1.2K 输入阻抗 Isolation voltage Using internal power:3000V Using external power:3750V 可隔离电压(使用电源 不同时的效果也不同) Response time 1K Hz Max. 输入讯号的最高频率 第 88 页,共 386 页由表 3-4-1 可知,由外界所输入的电压范围可由正+5V~+30V, 这样的范围都可以让光耦合器内部的发光二极管发光,进而导致另 一端的光敏晶体管导通。输入阻抗这项说明了内部的阻值,其值是 1.2K,如 果依 V=IR 的公式,假设输入的电压是 5V,而发光二极管 的压降是 1.2V 的话,流过光耦合晶体管的电流就是 I=(5-1.2)/1200=3.2(mA) 如果是输入 30V 的电压的话,流过光耦合晶体管的电流就是 I=(30-1.2)/1200=24(mA) 上述的二个式子表示该光耦合晶体管可在这样子的电流范围内正 常工作。 规格中还有一项 Response Time,直译就是反应时间,这个意 思的是当发光二极管的光线产生后,光敏晶体管需要多久时间才会 反应过来,也就是说,使用这种机制来当作输出入的时,输入的频 率就不能大过此值,否则光耦合晶体管将无法正确地反应出实际的 状况;例如表 3-4-1 中指出反应时间是最高 1KHz,表示我们所输入 的讯号变化频率不可高过每秒 1000 次,超过此值将不保证光敏晶 体管可以正确反应。 光耦合的主要目的就在隔离输出入之间的直接接触,以达到保 护的作用,规格中的 Isolation Voltage 就是说明了该零件在使用上 可以达到的保护范围,通常这个数值都是 KV 级以上(通常是 3000V 以上),一般情形下的输入讯号应不易使得接收端受到损伤。 以上是就数字输出入适配卡上经常使用的予以说明,其规格因 不同的型号,而有其它很多不同的地方,选择时应再详细查看零件 的规格。 3-4-2 接线方式 光耦合晶体管的内部包含了二个部份,一为发光二极管,一为 光敏晶体管,二者合而为一成为单独的一样东西。使用光耦合晶体 管接上我们的电路时,不能只接一个光耦合晶体管,还必须考虑到 各零件的阻抗匹配而组合。 输入时: 如果应用在输入讯号的检测上,图 3-4-1 是其中的一种接法。 第 89 页,共 386 页R1 (1) R3 (2) DI+ R2 VCC U3A Photo-Couple 23 4 1 COMP DI- D/I + - 图 3-4-1 数字输入采用光耦合隔离之线路图 图 3-4-1 是使用在数字输入讯号的取得上,数字输入讯号由 DI+ 及 DI-二支引脚输入,通常会在标示上出现正负号,由图中也可以 看出,如果图右的输入讯号反接,并不会将负责传送光讯号的 LED 击穿,电流是由另一颗相反方向的 LED 通过,虽然其所发的光线 无法驱动受光组件,但这样可以达到保护组件的目的,避免反接所 造成的组件伤害。 一般的工业控制的场合中,PLC 是经常使用的控制器,我们可 以发现其所使用的工作电压几乎都是 24V,此种电压不能直接接到 我们所使用的适配卡输入接点上,为了工业应用,在此情形下通常 使用光耦合器作为输入的中继器,让 24V 的电压讯号透过耦合器传 送至接点,就可以达到保护接点及其后适配卡的目的,也不需要为 了适应适配卡而改变所使用的电压范围。 输出时: 如果应用在数字输出上,图 3-4-2 则是其中一种接法,这个部 份则是将原来输入的电路反转过来,利用数字输出的电位直接驱动 光耦合器的发光二极管,并进而驱动光敏晶体管,使得图右的电路 导通而作动。 D/O +V(2) (3) U3A Photo-Couple 2 3 41 (1) R3 图 3-4-2 数字输出采用光耦合隔离之线路图 同样的,在图 3-4-2 也显示出左右二边透过光传送控制的讯号, 而使得二端的讯号互不干扰,达到讯号隔离保护的目的。 以上介绍的都是基本的数字输出会使用到的接线,在厂商所提供 第 90 页,共 386 页的硬件说明书中也会看到部份的建议接线图,我们使用时必须考虑 到实验的应用场合而选择必要的接线,或是使用厂商所开发的数字 延伸板来作讯号的接线。电压和电流是使用各电路时一定要考虑的 因素,务必使这二个要素值落在适配卡规定的范围内。 第 91 页,共 386 页第四章 数字输出入卡 计算机自动化的主要目的是由计算机代替人类作事情,计算机 不像人类一般具备感官,可以对外界的变化产生感应而生成不同的 反应,再针对反应而作出相应的控制动作;因此人类必须开发出计 算机可以具以反应的设备,这些设备称为传感器(Sensor),传感器 用来感应自然界的现象,这些现象包括了电压、电流、振动、温度、 压力、速度、流量…,这些自然界的现象到了计算机后,再由计算 机作接下去的分析及处理,计算机所接收的这些现象称为讯号 (Signal),讯号的来源型态可能如上述的几种,予以分类后,约略 可概分为数字讯号(Digital Signal)及 模拟讯号(Analog Signal)。 数字讯号是计算机作容易处理的讯号,它的型态只有开 与 关 二 种状态(套句祖先的话,万物乃由阴阳和合而生),计算机内部也只 知道高电位及低电位二个状态而已;模拟讯号为真实世界的讯号, 包括了电压、电流、声音…等等。数字讯号在某一个时间点唯一存 在一种状态,以计算机来说,不是 1 就是 0;模拟讯号就不一样了, 每一个时间点所有存在的代表数字都不相同,例如现在温度是摄氏 20 度,昨天是摄氏 18 度。由于数字和模拟是如此地不同,使用的 设备也就不一样,在接下来的内容里,笔者将讨论数字输出入的一 般化课题,而模拟输出入的部份则留待另一本书讨论。 4-1 卡片介绍 本章开始进入硬件及实验的部份,首先说明的是数字输出入卡 片。开关、高低、有无…等等的状态均是明确的二位状态,这些状 态呈现的是绝对的不同,二者只有其中之一会存在,也不会有中间 状态值的存在;例如电灯的开关,在某一个时间点,该电灯不是开 着就是关着,因此开关的二个状态只有一个会存在。计算机内部也 是很多的二位状态所组成,数学上以 0、 1 表示二个不同的状态, 计算机内部的记录也是如此,透过很多的 0 和 1 的组合来表示很多 的状态;而相同的观念也可以用到外界的状态侦测和对外的输出控 制上。 PIO-D56 是数字输出入适配卡(由泓格科技出品),其所提供的数 字输出入通道利用以上所提的观念,将外界的状态读取进来(数字 第 92 页,共 386 页输入),或是控制外界的状态(数字输出)。本章首先介绍此卡片,在 往后的实验中也将以此卡片探讨如何实现数字输出入的控制。 4-1-1 谈谈规格 以下就针对此 PIO-D56 的相关重要规格描述如下,了解了相关 的规格才方便继续作其它的应用。卡片的外观如图 4-1-1 所示。 图 4-1-1 数字输出入卡 PIO-D56 卡片外观 PIO-D56 提供了 24 点的可编程数字输出入通道,这 24 点的输 出入方向可以由使用者依需要而设定为输出或输入;另外,还有 16 点的单向数字输出通道及 16 点的单向数字输入通道。全部的可使 用通道共有 56 个,也因此在名称上使用了 D56 作为代表。 有时候设备有 24 个接点可以使用,会说此设备有 24 个通 道,点、接点、通道等名词都有人使用,只要意思清楚即可。 本卡片的规格我们由二个方面来说明,此卡片的主要功用是执 行数字输出入,也就是由外界得知状态,或是将希望的状态输出到 外界作相关的控制,因此规格上就集中在数字输入及数字输出二个 方面。 首先是数字输入的部份:规格上书明此卡片兼容于 TTL 电平, 其高低电位的定义如表 4-1-1 所示。 表 4-1-1 高低电位的定义 电位 定义 第 93 页,共 386 页Hogh Voltage 2.4V(Min) Low Voltage 0.8V(Max) 如表 4-1-1,所谓的 TTL 电平也就是逻辑上的高电位(有时我们 会说状态 1)被定义为 2.4 伏以上;如果希望卡片所读取的状态是高 电位的话,一定要给正 2.4 伏以上的电压,小于此电压将不被认可 为高电压;同样的,逻辑上的低电位必须小于 0.8 伏,在此电压以 上也同样不被认可为低电压。上述的二个电压将高低电位的范围作 了明确的分野,如果某一时间点的电压是落于 2.4 伏和 0.8 伏之间, 读取进来的状态将会不明确,不能确定此时的状态,不管读进来的 状态是 1 或 0 均不可信,因此,外界欲造成 1、 0 的状态给数字输 入卡片读取时,必须注意到所使用的电压范围。在 PIO-D56 来说, 不管是双向的输出入通道被设定成数字输入方向,或是使用单向的 数字输入通道,以上的数字输入规格均适用。 数字输出的部份:在 PIO-D56 的数字输出规格上,双向输出入 通道被设定成数字输出的表现和单向的数字输出通道的表现不同。 如表 4-1-2 所示。 表 4-1-2 数字输出规格 通道型式 沉入(Sink) 电流 涌出(Source) 电流 双向被设为输出 64mA(Max) 32mA(max) 单向输出 8mA(Max) 0.4mA(max) 所谓的 Sink 电流指的是一个电路形成后,该电路的电流最后会 流入该卡片,这种情形下,该卡片会有一个可容许的最大流入电流, 此即为 Sink 电流,超过此电流的限制,可能会对卡片造成损伤。 所谓的 Source 电流指的是一个电路形成后,该电路的电流由卡 片流出,这种情形下,该卡片会有一个最大流出电流,此即为 Source 电流,是否可以推动其它的后端设备就视此 Source 电流而定。 如果所接的电路之电流流动方向是由外向内流至适配卡,则须 考量此适配卡的 Sink 电流;反之,若是电流的流动方向是由适配 卡流向外部的话,就必须考量到 Source 的大小。 第 94 页,共 386 页4-1-2 PIO-D56 的引脚定义 PIO-D56 的接脚共有三组,分别被标示为 CON1、CON2、CON3, 分布如图 4-1-2。 图 4-1-2 接引脚置图 以上的各引脚,分成几个部份说明如下: 1、 CON1:此接脚用于单向的数字输出,引脚数目为 20 支脚(一般 也称为 Header),一般使用 20Pin 的排线作连接;其中的十六支 脚为数字输出通道,其它的引脚为电源或接地,引脚的定义如表 4-1-3 所示。 表 4-1-3 CON1 数字输出引脚定义 引脚编号 定义 引脚号码 定义 1 DO0 2 DO1 3 DO2 4 DO3 5 DO4 6 DO5 7 DO6 8 DO7 9 DO8 10 DO9 11 DO10 12 DO11 13 DO12 14 DO13 15 DO14 16 DO15 17 GND 18 GND 19 Vcc 20 +12V 数字输出的编号从 DO0 开始递增到 DO15,总共有十六个通道; 第 17 和 18 二支引脚为接地,所有的讯号引脚均以此二支引脚为 第 95 页,共 386 页参考引脚。第 19 脚为 Vcc,所谓的 Vcc 即是正 5 伏的电压(也是 和 GND 比较而得),第 20 引脚则是正 12 伏;这二支引脚所提供 的电压也是来自于总线,由计算机系统供应,通常用来供给外部 设备所需的电源;不过需要特别注意到的是,使用总线所提供的 5 伏或 12 伏作为电源时,一定要计算电流量的负荷,总线所提 供的电源有其电流限制,绝不可超过其负荷,否则可能伤害计算 机。 一般的 Header 接头上的引脚编号有其规则,例如在 CON1 接头上会有一边有个缺口,另一边则无;看着卡片,将缺口边朝上, 其缺口边的最右边就是编号 1,而同一边的引脚依序被编成单号引 脚;另一个没有缺口的一边则被编号为双号引脚。 2、 CON2:此接脚用于单向的数字输入,引脚数目为 20 支脚,一 般使用 20Pin 的排线作连接;其中的十六支脚为数字输入通道, 其它的引脚为电源或接地,引脚的定义如表 4-1-4 所示。 表 4-1-4 CON2 数字输入引脚定义 引脚编号 定义 引脚号码 定义 1 DI0 2 DI1 3 DI2 4 DI3 5 DI4 6 DI5 7 DI6 8 DI7 9 DI8 10 DI9 11 DI10 12 DI11 13 DI12 14 DI13 15 DI14 16 DI15 17 GND 18 GND 19 Vcc 20 +12V 数字输出的编号从 DI0 开始递增到 DI15,总共有十六个通道; 第 17 和 18 二支引脚亦为接地,所有的讯号引脚均以此二支引脚 为参考引脚。第 19 脚为 Vcc,所谓的 Vcc 即是正 5 伏的电压(也 是和 GND 比较而得),第 20 引脚则是正 12 伏;这二支引脚所提 供的电压也是来自于总线,由计算机系统供应,通常用来供给外 部设备所需的电源。 使用排线和 Header 作连接时,在排线通常会有一个和 Header 的缺口相对应的突起,二者只有一个方向可以平顺地作连 第 96 页,共 386 页接,也因此可以保护设备或卡片在不注意的情形下因为错接而造成 系统伤害;若无突起可资判别时,通常以排线上的红色线边为第 1 脚。 3、 CON3:此接脚可由使用者自行定义其方向,故可规划为数字输 入或数字输出,引脚数目为 37 支脚,此接脚则使用 37Pin 的 D 型接头予以连接,一般不会认错引脚;其中的 24 支脚为数字输 出入通道,其它的引脚为电源、接地或未接,引脚的定义如表 4-1-5 所示。 表 4-1-5 CON3 数字输出入引脚定义 引脚编号 定义 引脚号码 定义 1 N.C. 20 Vcc 2 N.C. 21 GND 3 PB7 22 PC7 4 PB6 23 PC6 5 PB5 24 PC5 6 PB4 25 PC4 7 PB3 26 PC3 8 PB2 27 PC2 9 PB1 28 PC1 10 PB0 29 PC0 11 GND 30 PA7 12 N.C. 31 PA6 13 GND 32 PA5 14 N.C. 33 PA4 15 GND 34 PA3 16 N.C. 35 PA2 17 GND 36 PA1 18 Vcc 37 PA0 19 GND XXX XXX 此接头提供有 24 个通道的输出入接点,以 8 个位为一组,共 分为三组,分别以 PA、PB、PC 表示(通常念成 Port A、Port B、 Port C);部份引脚为接地,所有的号讯号引脚均以接地为参考 引脚。第 18 脚和 20 脚为 Vcc,所谓的 Vcc 即是正 5 伏的电压(也 是和 GND 比较而得),此电压来自于总线,由计算机系统供应, 通常用来供给外部设备所需的电源。和另二个接头不一样的 是,此接头的部份未放入正 12 伏的电源。其引脚的定义上有 第 97 页,共 386 页些标示为 N.C.,其意为 Not Connect,表示此引脚不使用,并 未作任何的连接。 D 型接头的引脚编号和 Header 不同,其引脚分为二排,一 排的引脚比另一排多一支脚,而且多数引脚的一排其编号由 1 开始 递增,编号后再由下一排接着递增编号引脚,此与上述的 Header 的单双号分开排编号是不一样的。 上面说明了各接脚的意义,当我们实际上作接线时,讯号必须 接到正确的位置上;讯号线通常有二条,一条为正讯号线,一条为 负讯号线;当使用的是单接点式(Single-Ended)的讯号时,正讯号接 在上述的各个通道接点上,而负讯号就接在地线(GND)上;如果是 差动式讯号的话,就不能这样接,此种讯号也必须使用差动式 (Differential)的输入端子才行,而且这样子的板子也和上述的板子 不一样。此种讯号型式的差异在模拟转数字的讯号撷取技巧上特别 地重要,笔者将另一本书中讨论模拟讯号的撷取。 第 98 页,共 386 页 4-2 输出入原理及缓存器配置 适配卡能够进行输出入的功能必须依据一定的理论基础,惟有 在了解其基本原理后,架构实验时才能据以设计出正确的系统,本 节将说明 PIO-D56 适配卡在输出入原理上的相关议题。 缓存器上的配置及控制依卡片的功能而有所不同,个数也不相 同,了解缓存器的功能,并对其进行读写是控制上相当重要的课题。 4-2-1 卡片的初始化(Initialization) 所有放进计算机接口槽内的卡片在电源一激活时,卡片会将其 内部的所有芯片作初始化的动作,初始化的动作依卡片设计及功能 而有所不同。PIO-D56 由于用来作数字输出入,因此其初始化状态 也就是针对各通道的数字输出入而作,针对 CON1~CON3 三组引脚, 电源激活时的各状态如表 4-2-1 所示。 表 4-2-1 电源激活时的 Port 状态 编号 I/O 方向 状态 CON1 输出 失效 CON2 输入 失效 CON3 输入 失效 由表 4-2-1,所有的通道在刚激活时(计算机刚激活或执行卡片 重置),所有的通道均为失效状态(Disable),亦即所有的通道即使送 出命令,也不会有任何的响应或输出动作的执行,此举保护了输出 入埠的安全,也保护了初始化时所接设备的安全。同样的,由于一 开始就将所有的埠设成失效状态,因此在使用前也要记得将这些被 关闭的功能打开,才能对其进行控制的动作。 4-2-2 数字输出入端口(CON3)的架构 CON3 可双向使用,不但可以用来输出数字讯号,也可以读入 数字讯号,至于是用于输出讯号或是读入讯号则由程序予以定义。 此组接脚共有 24 个通道,此 24 个通道被区分为 3 个群组,习惯上 被分成 Port0、Port1、Port2 等三个群组,也有部份场合将此 3 个埠 第 99 页,共 386 页分成 PortA、 PortB、 PortC;每一个群组有 8 个通道,而在实务上 的运用中,每个群组中的每个通道均是一样的动作,换句话说,如 果 Port0 被设定成数字输入,那么 Port0 中的每个通道均会设定成 数字输入,反之亦然。 CON3 的每个输出入引脚的内部电路示意图如图 4-2-1 所示。 图 4-2-1 CON3 每个通道的功能示意图 根据图 4-2-1,以下是此组接脚的相关讨论: 方向选择:由图中的编号(1),我们可以设定三个埠中个别埠的 输出入方向,一旦设定后,讯号的接法就必须以该作用应有的方 式来接。 重置讯号:开机时,所有的输出入芯片会被设成不可操作,解开 的方式就是送出一个指令解除 Disable,使芯片开始处于可操作 的状态,如图中标示(2)处。 输出锁存电路:数字输出的讯号送出后,并不是直接送到外部的 其它电路,而是由芯片将状态保持住,令其不改变,直到下一个 不一样的状态到达时为止,此即为锁存(Latch);这个作法也让输 出的部份可以维持到其它的外部设备读取到这个状态为止(不管 是 High 或是 Low)。如图中标示(4)处。 输入缓冲电路:外界讯号进入后也不是直接进到适配卡,而是先 到一芯片的缓冲区中,再由缓冲区将讯号往适配卡内部送。当外 第 100 页,共 386 页界讯号发生改变时,由缓冲区往内送的讯号也会实时地发生改 变,这个部份的讯号就不具锁存的功能。如图中标示(5)处。 对外通道:真正的对外连接线路接点由图中标示(6)的地方执行, 这也是我们在卡片上所看到的接脚。虽然是一个接脚,但是一个 最单纯的单接点式讯号包含有讯号和地线二条线路,所以除了这 一条讯号线外,其实还有一条地线也是必须被注意到的。 由以上的讨论可以发现,一个讯号不管是由内而外地控制,或 是侦测由外而内的状态,都不是直接就进到计算机中,而是藉由适 配卡上的种种芯片的交互作用和协调而达到输出控制和输入侦测 的功能。 4-2-3 数字输出端口(CON1)的架构 CON1 用来输出数字讯号,提供 TTL 电平的讯号给外界。这组 接脚的每个输出引脚的内部电路示意图如图 4-2-2 所示。 图 4-2-2 数字输出端口功能方块图 图 4-2-2 和图 4-2-1 中的数字输出部份一样的,当我们要利用 CON1 作数字输出控制时,也要先激活该引脚,才能将数字输出通 道设为 1 或 0。输出 0 时,量测该输出引脚和地线之间的电位,会 发现其电位是 0 伏;而输出 1 时,量测该输出引脚和地线之间的电 位,则会是 5 伏 (标准会是如此,若发现不是如此,可能板子有问 题 )。 第 101 页,共 386 页4-2-4 数字输入端口(CON2)的架构 CON2用来读取数字输入的讯号,在不外接延伸板的情形下, 仅能读取 TTL 电平的讯号。这个端口的每个输入引脚的内部电路示 意图如图 4-2-3 所示。 图 4-2-3 数字输入端口功能示意图 图 4-2-2 和图 4-2-1 中的数字输入部份一样的,当我们要利用 CON2 读取数字输入状态时,也要先激活该引脚,才能读取数字输 入通道。当接脚和地线之间的电位差在 0.7 伏以下时,此时的状态 为低电位;若它们之间的电位差在 2.1 伏以上,则此时的状态为高 电位。 第 102 页,共 386 页 4-3 缓存器配置及其操作 第一章时我们提过适配卡的控制是透过缓存器的存取而达到, 卡片的设计者也会根据不同的功能而配置为数不同的缓存器,本节 将讨论此卡片上的缓存器配置。 4-3-1 2018香港马会开奖现场映射(Address Mapping) 卡片的操作乃是透过缓存器而达到,2018香港马会开奖现场就是为了成功地存取 缓存器而必须配置的,在 PIO-D56 上的2018香港马会开奖现场配置如表 4-3-1。 表 4-3-1 PIO-D56 2018香港马会开奖现场映像表 2018香港马会开奖现场 读取时 写入时 Wbase+0 初始化指令控制缓存器 初始化指令控制缓存器 Wbase+2 辅助控制缓存器 辅助控制缓存器 Wbase+3 辅助资料缓存器 辅助资料缓存器 Wbase+5 中断屏蔽控制缓存器 中断屏蔽控制缓存器 Wbase+7 辅助状态缓存器 辅助状态缓存器 Wbase+0x2a 中断极性控制缓存器 中断极性控制缓存器 Wbase+0xc0 读取 Port0 的资料 资料写入 Port0 Wbase+0xc4 读取 Port1 的资料 资料写入 Port1 Wbase+0xc8 读取 Port2 的资料 资料写入 Port2 Wbase+0xcc 读取 CON3 的组态 设定 CON3 的组态 Wbase+0xd0 读取 CON2 的低字节 写入 CON1 的低字节 Wbase+0xd4 读取 CON2 的高字节 写入 CON1 的高字节 (Wbase 表示基2018香港马会开奖现场) 由表中可以看出一片数字输出板所拥有的缓存器是相当多,功 能愈多时,所需要的缓存器也愈多;每一个缓存器中所含的数值是 一个字节(8 个位),因此当我们去读取或是写入时,均是以一个字 节为一个单位。 位是计算机的基本储存单位,一个位称为一个 Bit;四 个位称为一个 Nibble; 8 个位就称为一个 Byte;而 16 个位则称为 一个 Word。 第 103 页,共 386 页就使用者的角度而言,并不是所有的缓存器都会使用到,但却 必须对某些重要的缓存器有些了解,才方便对其作控制。以下的各 小节中将会针对使用到的缓存器作一说明。 4-3-2 功能缓存器 PIO-D56 上的缓存器分为功能性的缓存器和 IO 缓存器,本小节 先说明功能缓存器的部份。 初始化指令控制缓存器(RESET\Control Register):此缓存器的 2018香港马会开奖现场由表 4-3-1 可知,位于基2018香港马会开奖现场,位的排列如表 4-3-2 所示。 表 4-3-2 初始化控制缓存器位定义 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 保留 保留 保留 保留 保留 保留 保留 RESET\ 表 4-3-2 中的 8 个位只用到最低位,该位用于控制所有 DIO 引 脚的致能(Enable),我们在 4-2-2 节中谈过 CON1、 CON2、 CON3 接头中的 DIO 引脚在开机时是处于无效(Disable)的状态,必须在激 活后才能对其进行 IO 的动作,而激活的方法就是将此2018香港马会开奖现场中的 Bit0 设成 1。此缓存器中的其它位没有用到,因此标示为保留。 中断屏蔽控制缓存器(INT Mask Control Register):此缓存器的 2018香港马会开奖现场由表 4-3-1 可知,位于基2018香港马会开奖现场+5,位的排列如表 4-3-3 所示。 表 4-3-3 中断屏蔽控制缓存器位定义 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 EN3 EN2 EN1 EN0 表 4-3-3 中的 8 个位用到了低四位,这四个位分别控制了四个 中断产生源的激活与否,设为 0 时表示关闭中断,而设成 1 时,表 示激活中断的侦测。 所谓的中断是由外界讯号的触发而引起程序反应,PIO-D56 共 允许四个中断的产生,可以由 CON3 中的 PortC 里的四个引脚(PC0、 PC1、PC2、PC3)产生,表 4-3-3 中的 EN0~EN3 分别对应到 PC0~PC3, 我们可以在程序中写入中断处理程序,那么当这些引脚有预定的讯 号电平进入时,我们所写的中断处理程序就会开始执行。PIO-D56 卡片中的中断是属于高电平触发型式,也就是当中断通道的讯号处 于高电位时即会引发中断程序的执行。第六章将会对于中断再以一 卡片作特别的说明。 第 104 页,共 386 页辅助状态缓存器(AUX Status Register):此缓存器的2018香港马会开奖现场由表 4-3-1 可知,位于基2018香港马会开奖现场+7,位的排列如表 4-3-4 所示。 表 4-3-4 辅助状态缓存器位定义 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 AUX3 AUX2 AUX1 AUX0 表 4-3-4 中的 8 个位用到了低四位,这四个位和表 4-3-3 是对应 的,AUX0~AUX3 分别对应到 PC0~PC3。当中断产生后,PC0~PC3 的状态就被记录在这四个位中,程序可以将这四个位取出作判断。 中断极性控制缓存器(Interrupt Polarity Control Register):此缓 存器的2018香港马会开奖现场由表 4-3-1 可知,位于基2018香港马会开奖现场+&H2A,位的排列如表 4-3-5 所示。 表 4-3-5 中断极性控制缓存器位定义 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 INV3 INV2 INV1 INV0 表 4-3-5 中的 8 个位用到了低四位,这四个位和表 4-3-3 是对应 的,INV0~INV3 分别对应到 PC0~PC3,分别用来将讯号作极性反 转。位设成 0 时表示低电平触发,而位设成 1 时表示维持在高电平 触发。 由于中断的触发是高电平触发,因此只要是中断通道处于高电 位的话,就会引发中断处理函式的执行,但在实际的应用情况中, 工程师也很有可能是希望在低电平的时候触发,这样一来就和卡片 原先的设计相冲突了,因此,透过这个缓存器的设定可以让系统设 计者自行决定维持高电平触发(此时选择 non-Intverted),或是另选 低电平触发(此时选择 Intverted)。 输出入选择控制缓存器(I/O Selection Control Register):此缓存 器的2018香港马会开奖现场由表 4-3-1 可知,位于基2018香港马会开奖现场+&HCC,位的排列如表 4-3-6 所示。 表 4-3-6 输出入选择控制缓存器位定义 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 0 0 0 0 0 Port2 Port1 Port0 表 4-3-6 中的 8 个位用到了低三位,这三个位分别用来指示出 CON3 中的三个 Port 被设成输出或是输入。该位设成 0 时,表示该 端口被当成数字输入使用;该位设成 1 时,表示该端口被当成数字 输出使用。 第 105 页,共 386 页4-3-3 输出入缓存器 和输出入直接相关的部份就是输出入缓存器,由于输出入的部 份还分为 CON1~CON3 三者,因此缓存器也就针对这三个埠而有所 不同。 CON1 作为数字输出之用,其输出入控制缓存器所在2018香港马会开奖现场由表 4-3-1 可知是基2018香港马会开奖现场+&HD0 及基2018香港马会开奖现场+&HD4,这二个2018香港马会开奖现场分别控制 数字输出的低八位和高八位,每个字节的位排列如表 4-3-7 所示。 表 4-3-7 输出控制缓存器位定义 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 D7 D6 D5 D4 D3 D2 D1 D0 表 4-3-7 中的每一个位就代表了一个通道。由于 CON1 的数字 输出的缓存器有二个2018香港马会开奖现场,当输出控制时就必须将数值分别送到二 个2018香港马会开奖现场,通常是由低字节先送,再送高字节,故上面的二个2018香港马会开奖现场中, 当我们要送句柄到 CON1 时,就先送一个字节到(基2018香港马会开奖现场+&HD0), 接着再送一个字节到(基2018香港马会开奖现场+&HD4),二个步骤才算完成一个十六 通道的数字输出控制。 CON2 作为数字输入之用,其输出入控制缓存器所在2018香港马会开奖现场由表 4-3-1 可知是基2018香港马会开奖现场+&HD0 及基2018香港马会开奖现场+&HD4(这二个2018香港马会开奖现场和数字输 出的2018香港马会开奖现场是一样的),这二个2018香港马会开奖现场分别对应数字输入的低八位和高 八位,每个字节的位排列如表 4-3-8 所示。 表 4-3-8 输入控制缓存器位定义 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 D7 D6 D5 D4 D3 D2 D1 D0 表 4-3-8 中的每一个位就代表了一个通道。由于 CON2 的数字 输入的缓存器有二个2018香港马会开奖现场,当读取数字输入时就必须将数值分二次 读取,通常是读取低字节,再读取高字节,故上面的二个2018香港马会开奖现场中, 当我们要读取 CON2 的数字输入状态时,就先自(基2018香港马会开奖现场+&HD0)读 取一个字节进来,接着再由(基2018香港马会开奖现场+&HD4)读取一个字节进来,二 个步骤才算完成一个十六通道的数字输入读取动作。 由上述的说明可知,数字输出和数字输入均使用到相同的地 址,那如何知道是谁使用的呢?其实,如果是数字输出的话,程序 必须使用输出指令,而若是数字输入的话,就必须使用输入指令, 第 106 页,共 386 页二种指令不一样,在适配卡上走的路径也就不同,不会互相干扰, 而且会各自完成工作。 CON3 可作为数字输入或数字输出之用,其输出入控制缓存器 所在2018香港马会开奖现场由表 4-3-1 可知是基2018香港马会开奖现场+&HC0、基2018香港马会开奖现场+&HC4 及基2018香港马会开奖现场 +&HC8,这三个2018香港马会开奖现场分别对应到 Port0~Port2 等三个输出端口,每 个字节的位排列如表 4-3-9 所示。 表 4-3-9 输入控制缓存器位定义 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 D7 D6 D5 D4 D3 D2 D1 D0 表 4-3-8 中的每一个位就代表了一个通道。当该端口被设定为 数字输出时,就由对应的2018香港马会开奖现场输出控制的数值;若该端口被设定为 数字输入时,也是由对应的2018香港马会开奖现场读取数字输入的数值。输出及读取 的方向和 CON1、 CON2 是一样的。 4-3-4 函式库介绍 了解各缓存器的意义和作用后,我们将透过动态连结函式库和 适配卡取得连系;在第一章提过的驱动程序安装后,这些必要的动 态连结函式库将会被拷贝到计算机中,位置通常是在 Windows\System 目录下(若是 NT 或 2000 的操作系统,则其目录位 于 Windows\System32 之下),在预设的安装目录中也会有示范的模 块宣告,如图 4-3-1。 图 4-3-1 安装目录及模块位置 第 107 页,共 386 页图中所示的 Piodio.bas 里面有针对 PIO-D56 这块卡片所作的函 式宣告(当然也包含了其它相类似卡片的宣告),和 PIO-D56 相关的 部份如下 ‘以下定义错误代码 Global Const PIODIO_NoError = 0 Global Const PIODIO_DriverOpenError = 1 Global Const PIODIO_DriverNoOpen = 2 Global Const PIODIO_GetDriverVersionError = 3 Global Const PIODIO_InstallIrqError = 4 Global Const PIODIO_ClearIntCountError = 5 Global Const PIODIO_GetIntCountError = 6 Global Const PIODIO_RegisterApcError = 7 Global Const PIODIO_RemoveIrqError = 8 Global Const PIODIO_FindBoardError = 9 Global Const PIODIO_ExceedBoardNumber = 10 Global Const PIODIO_ResetError = 11 Global Const PIODIO_IrqMaskError = 12 Global Const PIODIO_ActiveModeError = 13 Global Const PIODIO_GetActiveFlagError = 14 Global Const PIODIO_ActiveFlagEndOfQueue = 15 ' 卡片所属的编号(PCI 侦测接口使用) Global Const PIO_D56 = &H800140 ' D24 + 16I + 16O '测试用的函式,通常用于测试函式是否可在特定 OS 上使用 Declare Function PIODIO_ShortSub Lib "PIODIO.dll" (ByVal a As Integer, ByVal b As Integer) As Integer Declare Function PIODIO_FloatSub Lib "PIODIO.dll" (ByVal a As Single, ByVal b As Single) As Single '驱动程序函式 Declare Function PIODIO_DriverInit Lib "PIODIO.dll" () As Integer Declare Sub PIODIO_DriverClose Lib "PIODIO.dll" () Declare Function PIODIO_SearchCard Lib "PIODIO.dll" (wBoards As Integer, ByVal dwPIOPISOCardID As Long) As Integer Declare Function PIODIO_GetDriverVersion Lib "PIODIO.dll" (wDriverVersion As Integer) As Integer Declare Function PIODIO_GetConfigAddressSpace Lib "PIODIO.dll" ( _ ByVal wBoardNo As Integer, wAddrBase As Long, wIrqNo As Integer, _ wSubVendor As Integer, wSubDevice As Integer, wSubAux As Integer, _ wSlotBus As Integer, wSlotDevice As Integer) As Integer Declare Function PIODIO_ActiveBoard Lib "PIODIO.dll" (ByVal wBoardNo As Integer) As Integer Declare Function PIODIO_WhichBoardActive Lib "PIODIO.dll" () As Integer '数字输出入函式 Declare Sub PIODIO_OutputByte Lib "PIODIO.dll" (ByVal address As Long, ByVal dataout As Integer) Declare Function PIODIO_InputByte Lib "PIODIO.dll" (ByVal address As 第 108 页,共 386 页 '中断函式 Declare Function PIODIO_IntInstall Lib "PIODIO.dll" (ByVal wBoard As Integer, hEvent As Long, _ ByVal wInterruptSource As Integer, ByVal wActiveMode As Integer) As Integer Declare Function PIODIO_IntRemove Lib "PIODIO.dll" () As Integer Declare Function PIODIO_IntGetCount Lib "PIODIO.dll" (dwIntCount As Long) As Integer Declare Function PIODIO_IntResetCount Lib "PIODIO.dll" () As Integer 透过厂商所提供的函式库就可以达到控制卡片的目的。以 PIO-D56 而言,使用函式的顺序则必须如图 4-3-2 所示(其它的卡片 通常也是如此,可能只有极小的差异): 图 4-3-2 函式使用流程 图 4-3-2 所使用的各个部份再予以说明如下: 驱动程序初始化:使 用 PIODIO_DriverInit 函式,此函式无参数。 驱动程序必须使用系统的资源,例如中断、内存的配置等等,这 些动作必须在使用驱动程序前先行准备好,因此这个函式也必须 在其它的函式之前先执行,确定配置成功后才能进行其它的操 作。 第 109 页,共 386 页检查卡片是否存在:使用 PIODIO_SearchCard 函式,参数有二, 第一个是所欲寻找的卡片数量,第二个参数是卡片所属的编号, 这在卡片的说明书上会载明,通常也已记录在模块文件中了,直 接引用模块中的常数定义即可。也必须卡片存在,才能进行输出 入的动作;如果找不到所指明的这块卡片,当然就没有执行下去 的必要了。 取得卡片的组态:使用 PIODIO_GetConfigAddressSpace 函式, 此函式的参数有 8 个,分别是卡片号码(此为输入项,若只有一 片适配卡,就输入 0,若不只一片,则由 0 开始编号,选择一个 适配卡)、基2018香港马会开奖现场(此为输出项,函式将会传回此卡片的基2018香港马会开奖现场回 来给程序,程序也透过此基2018香港马会开奖现场继续其它的操作)、中断号码(传 回值,指出此卡片所使用的中断号码为何)、其它的 5 项参数也 均是传回值,记载了 PCI 适配卡的相关编号(程序中通常用不到 此 5 项 )。之前说明了各相关的缓存器功能,均需使用到基2018香港马会开奖现场, 而 PCI 适配卡之基2018香港马会开奖现场是由系统自动配置的,因此透过此一函式 就可以取得由系统配给这片适配卡的2018香港马会开奖现场,我们也就知道到什么 地方去操作缓存器了。 激活所有的输出入装置:使 用 PIODIO_OutputByte 函式,此函式 有二个参数,第一个是2018香港马会开奖现场,指出数值所要传送到的地方;第二 个是所欲传送的数值,此数值在宣告时虽然是整数(Integer,拥 有二个字节空间),但是其实际上只能传送一个字节大小的数值 (0~255 之间)。依 PIO-D56 的定义,必须送给基2018香港马会开奖现场 1,将所有 的 DIO 芯片激活。 数字输出入动作的执行:使用 PIODIO_OutputByte 函式(如前一 项的说明)及 PIODIO_InputByte 函式。PIODIO_InputByte 函式的 参数有一个,即为2018香港马会开奖现场,通常是缓存器的所在2018香港马会开奖现场;其传回值虽 然也是整数,但实际也是只有一个字节的大小而已。 关闭驱动程序:使用 PIODIO_DriverClose 函式。当所有的输出 入动作均已完成后,已经不再需要作其它的动作了,就可利用此 一函式将驱动程序关闭掉,释放所使用的资源。 数字输出入适配卡的控制就是依照上述的程序执行,笔者在本 书中建立各系统程序时,也将依循此一原则处理。 第 110 页,共 386 页 4-4 CON1 数字输出实验 PIO-D56 的 CON1 专门用来作数字输出,本节先针对此埠讨论 实验的相关作法,并从实验中了解此埠的实际用法。 4-4-1 延伸板 DB-16R 介绍 利用之前说明过的引脚定义,实验中可以自行建立实验设备, 但由于自 PIO-D56 中延伸出线路后,还是必须自行建立测试的线路 的话,总是比较麻烦,使用匹配的延伸板是比较方便的作法。延伸 板的作用在于减少配线的麻烦,而且对于适配卡也可以有一定程度 的保护作用;笔者首先介绍使用于 CON1 的其中一款延伸板 (DB-16R)。 DB-16R 是配有 16 颗继电器的延伸板,其外观如图 4-4-1 所示。 DB-16R 透过图左边的牛角接头和延伸自 PIO-D56 适配卡的排线相 连接而将讯号导入;由于继电器本身也需要使用电源,DB-16R 本 身提供有内部电源(PIO-D56 提供)或由外部接入电源,当我们的计 算机所使用的设备较多时,仅由计算机本身提供电源会使得计算机 本身电源供应器的负载较重,此时以外部接入工作电源较佳。 图 4-4-1 DB-16R 延伸板 图 4-4-1 中的接线座用来连接所欲控制的外部线路,图中共有 上下二区,上下区各座的接线编号如表 4-4-1,上下区各有 8 个继 第 111 页,共 386 页电器可供控制,每一个继电器的编号就对应到 CON1 上的各数字输 出的编号,每个数字输出部份有三个继电器的接点可用。 表 4-4-1 接线座的继电号编号及各脚定义 (上区) 8 NO NC CM 10 NO NC CM 12 NO NC CM 14 NO NC CM 9 NO NC CM 11 NO NC CM 13 NO NC CM 15 NO NC CM (下区) 1 NO NC CM 3 NO NC CM 5 NO NC CM 7 NO NC CM 0 NO NC CM 2 NO NC CM 4 NO NC CM 6 NO NC CM 表 4-4-1中每个继电器的控制上均有三个接线位置,分别是NO、 NC、 CM,这三点的意义如下: NO:常 开(Normal Open),在继电器未受控制的情形下,此点和 CM 点 一直是未接触的,亦即二点间不导通,工业上也称为 A 接点。 NC:常闭(Normal Close),在继电器未受控制的情形下,此点和 CM 点一直是接触的,亦即二点处于导通状态,工业上也称为 B 接点。 CM:共点(Common),用来和 NO 或 NC 点相接。 这三个点和设备的连接方法以图 4-4-2 表示,图中笔者以红灯 和绿灯表示外接的二个设备,在继电器尚未受控制的情形下是红灯 亮起,绿灯灭掉;在继电器被控制的情形下,则是绿灯亮起,红灯 灭掉。 图 4-4-2 继电器控制示意图 由图 4-4-2,我们由编号(1)的位置开始依次加以讨论如下: 由 CON1 的数字输出引脚所送出的讯号由图中的(1)的位置进入, 该位置连接一个 NPN 型晶体管,当 DO 讯号升至高电位时,将 导致此晶体管的导通,而使得直流的电源通过图中(2)的线圈。 当编号(2)的线圈导通后,将使得编号(3)的开关产生切换,原来 在未受控制的情形下,接点的位置是 NC 和 COM 导通的;但在 第 112 页,共 386 页此时编号(3)的导通位置移至左边,这使用 NC 部份的接点断开, 而 NO 和 COM 间的电路则随即导通。 在 DO 讯号尚未提升到高电位时,NC 和 COM 导通,因而在图中 的右边编号(4)的红灯因为电路导通,而被点亮。 同理,当 DO 讯号提升到高电位时,NC 和 COM 不再导通,使得 红灯部份的电路失效,红灯便熄灭;但同时 NO 和 COM 随即导 通,这个导通的动作使得编号(5)的绿灯电路导通,绿灯因而亮 起。 由此延伸板所控制的电路必须自行准备电源,继电器仅提供接点 的接合与断开,本身不提供受控电路任何的工作电源,这点必须 注意。 一个完整的电路一定是电流由电源提供者开始流动,透过一 连串的传输线路一直流向地线;随着电流的流动,在此电路上的组 件就会因电流所提供的能量而作动。 透过上述几个步骤的了解,利用 DO 通道的控制,DB-16R 上的 继电器就可以被控制,只要将外部的设备连接上正确的 NO、 NC 及 COM,我们就可以成功地控制外部设备的开或关的动作。 DB-16R 延伸板中的其它规格列如表 4-4-2。 表 4-4-2 DB-16R 的规格 额定负载 0.5 A/120VAC , 1A /24VDC 最大切换功率 60VA,24W 最大切换电压 120VAC , 60VDC 最大切换电流 1A 开关次数 Electrical (20 Millions Times ) 动作时间 6ms 释放时间 3ms 控制逻辑 Input TTL high (+5V) , relay on 消秏功率 12 V /0.53A ; 5V /0.2 A 尺寸 8 inch ( 205mm) x 4.5 inch (114mm) 环境温度 0 - 60°C 保存温度 0-60°C 环境湿度 5% to 90% Non condensing 第 113 页,共 386 页由表 4-4-2,最大切换电压有二种情形,当我们用来控制电路 时,所使用的电压最好是落在这些电压以下(亦即不要在交流电压 不要大于 120V,而直流不要大于 60V)。另外也要考量一下在线路 上的电流,务必让瞬间的电流及工作电流在容许的范围内,以保护 组件。 4-4-2 数字输出的单点控制 依据上一小节的作法将 DB-16R 延伸板正确地和 PIO-D56 中的 CON1 连接妥当后,即可进行实验,本小节首先进行各别引脚的输 出控制。 画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立画 面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 F4 叫出属性窗口,更改其 Caption 属性为” Digital Output Control (CON1) - Point”,表示我们将进行单点的输出控制。 2、 安排二个 Image 控件,按下 F4 叫出其属性窗口,Name 属性分 别改为 imgON 及 imgOFF,分别按下 Picture 属性的选择键,加 载代表开启灯号的红灯及灯号灭掉的灰色灯,另外,将这二个 控件的 Stretch 属性设为 True,如此会使得加载的影像文件符合 我们所建立的 Image 外观,同时也将它们的 Visible 属性设为 False。这二个影像将用来动态地显示数字输出的状态。 3、 安排十六个 Image 控件,这十六个控件的 Picture 属性先行加载 imgOFF 的图形文件,同时设定其名称为 imgDO, Stretch 属性 设为 True。在设计的技巧上,通常先建立第一个 imgDO 的控件, 将相关的属性均设定完毕后,利用「复制」及「贴上」二个功 能作出其它的十五个 imgDO 即可,透过此法作出的控件,也可 以成为一连串的控件数组,程序的设计可以简化。我们将透过 点选灯号的方式控制数字输出,由于驱动程序未开启及卡片地 址未取得前,不可以对卡片作输出,因此这十六个 Image 控件 的 Enabled 属性全部设为 False,以防止程序错误。 4、 安排十六 Label 控件,其 Caption 属性输入”DO-1”~”DO-15”等 十六个,它们将用来标示代表数字输出灯号的十六个 Image 控 件。 5、 安排二个按钮控件,其 Caption 属性分别输入「激活卡片」及 第 114 页,共 386 页「结束程序」,用来初始化卡片及结束程序。 6、 依上述步骤所作出的画面如图 4-4-3。 图 4-4-3 数字输出的画面设计 功能设计:程序的程序必须参考到函式使用的顺序(请参考 4-3-4 小 节 ),接下来就依下列步骤作出相对应的功能: 1、 在「激活卡片」的 Click 事件程序程序中键入以下程序代码 Dim wRetVal As Integer Dim InVal1 As Integer Dim InVal2 As Integer Dim InVal3 As Integer Dim i As Long '适配卡的初始化 wInitialCode = PIODIO_DriverInit() If wInitialCode <> PIODIO_NoError Then MsgBox "驱动程序初始化失败!", , "PIODIO Card Error" Exit Sub End If '搜寻计算机中是否存在 PIO_D56 适配卡 (若使用在其它地方,此程 序也可不作) If PIODIO_SearchCard(1, PIO_D56) <> PIODIO_NoError Then MsgBox "PIO_D56 适配卡不存在!!", , "PIODIO Card Error" Exit Sub End If '取得卡片的组态信息(由于只有一片,故第一个参数给 0,代表第一 片 ) wRetVal = PIODIO_GetConfigAddressSpace(0, wBaseAddr, wIrq, wSubVendor, _ wSubDevice, wSubAux, wSlotBus, wSlotDevice) If wRetVal <> PIODIO_NoError Then MsgBox "组态信息错误 !!" Exit Sub End If DOValue = 0 第 115 页,共 386 页 PIODIO_OutputByte wBaseAddr, 1 '激活所有的 DIO 功能 '激活 Image,允许作实验 For i = 0 To 15 imgDO(i).Enabled = True Next i 激活卡片的程序是必须在其它的动作之前,上面的程序就将 4-3-4 小节及图 4-3-2 所说明的程序依次地写入。wBaseAddr 变量 中存放着函式所传回的适配卡2018香港马会开奖现场,在其它地方直接引用即 可。 2、 双击 Image 控件,在其 Click 事件程序中写入以下的程序代码 fDO(Index) = Not fDO(Index) '转态 '判断状态,并将记录的数值作运算 If fDO(Index) Then '若为激活,则加上数值 DOValue = DOValue + 2 ^ Index '将灯号换为红色 imgDO(Index).Picture = imgRed.Picture Else '若为关闭,则减掉数值 DOValue = DOValue - 2 ^ Index '将灯号换为灰色 imgDO(Index).Picture = imgGray.Picture End If If Index <= 7 Then '将数值输出到低八位的2018香港马会开奖现场 PIODIO_OutputByte wBaseAddr + &HD0, DOValue And &HFF ' DO (Low-byte) End If If Index > 7 And Index <= 15 Then '将数值输出到高八位的2018香港马会开奖现场 PIODIO_OutputByte wBaseAddr + &HD4, DOValue / 2 ^ 8 ' DO (High-byte) End If 每一次点选灯号后,就利用 fDO 的旗标数组将状态记录下来, 接着将状态的数值以 DOValue 变量予以储存,最后则是使用 PIODIO_OutputByte 函式将数字输出的数值输出到 CON1 的2018香港马会开奖现场; 由于 CON1 的数字输出端口是十六位,因此 PIODIO_OutputByte 函式送的字节数值必须送二次,一次是送给低字节,第二次送 给高字节。 如何在所送出的数值中,正确地得而应该送出的数值呢?每一个 输出的2018香港马会开奖现场均是对应到一个位,8 个位形成一个字节,我们也 是送出一个字节同时控制这 8 个通道,在数值表示的方法上可 以使用 2 的次方数来代表不同的位,由低位到高位分别可以用 2 的 0 次方到 2 的 7 次方予以表示(以十进制表示的话,其组合 数值的范围就是 0~255),这也是上述的程序中所使用的方法。 上述的程序中,不管是低 8 位或是高 8 位均使用数组及 DOValue 记录这些改变的数值,那么在数值的分配上当然就要考虑到低 第 116 页,共 386 页8 位和高 8 位的2018香港马会开奖现场不同所形成的数值差异;高 8 位被记录的 数值处于 2 的 8 次方到 2 的 15 次方之间,当我们送数值到代 表高 8 位的2018香港马会开奖现场时,就必须将这些高了 8 位的数值再送回低 8 位,以便成为字节后送到高 8 位的2018香港马会开奖现场;当数值必须由高 8 位 移至低 8 位的时候,必须再位移位(Bit Shift),而且是右移 8 位, 以 Visual Basic 的作法来说,最简单的作法就是除上 2 的 8 次 方,如此便是右移 8 位。那么左移呢?如果是左移 8 位的话,就 是将数值乘上 2 的 8 次方即可。其它的左右移位就同理可证了。 3、 双击”结束程序”按钮,在其 Click 事件中写入以下程序代码 '关闭驱动程序 PIODIO_DriverClose '结束程序 End 先关闭驱动程序,再结束系统程序的执行。 在这个项目进行实验时,我们将 DB-16R 和 PIO-D56 的 CON1 接在一起,使得该卡片的数字输出可以直接就传到继电器延伸板 上,省却动手作测试接点的问题。测试的步骤如下: 程序执行,首先必须选择激活卡片,接着才能在画面上的十六 灯中去点选,实验发现,点选灯号时会成功地控制 DB-16R 上的继 电器动作,板子上的发光二极管也对应地亮起或灭掉。 实验结果如图 4-4-4 所示,在点选灯号的同时,若实验设备完 整的话,也可以听到继电器的开关声响。 图 4-4-4 DB-16R 数字输出的测试结果 详细的项目程序代码请参考”EX\CH4\ CON1_DO_Ctrl”,双击档 案夹中的 Project1.vbp 项目即可进行以上的实验。 第 117 页,共 386 页详细的程序代码如下: Option Explicit '基2018香港马会开奖现场 Dim wBaseAddr As Long '中断号码 Dim wIrq As Integer 'PCI 芯片讯息 Dim wSubVendor As Integer, wSubDevice As Integer, wSubAux As Integer 'PCI 插槽信息 Dim wSlotBus As Integer, wSlotDevice As Integer Dim wInitialCode As Integer Dim fDO(0 To 15) As Boolean '数字输出的旗标 Dim DOValue As Long '数字输出数值 '*********************************************** '以下是结束程序的按钮所引发的动作 '首先关闭驱程序,接着再以 End 结束程序 '*********************************************** Private Sub cmdEnd_Click() '关闭驱动程序 PIODIO_DriverClose '结束程序 End End Sub '*********************************************** '以下是卡片初始化的动作 '任何对于卡片的操作之前,必须先执行初始化的动作 '*********************************************** Private Sub CmdInitial_Click() Dim wRetVal As Integer Dim InVal1 As Integer Dim InVal2 As Integer Dim InVal3 As Integer Dim i As Long '适配卡的初始化 wInitialCode = PIODIO_DriverInit() If wInitialCode <> PIODIO_NoError Then MsgBox "驱动程序初始化失败!", , "PIODIO Card Error" Exit Sub End If '搜寻计算机中是否存在 PIO_D56 适配卡 (若使用在其它地方,此程 序也可不作) If PIODIO_SearchCard(1, PIO_D56) <> PIODIO_NoError Then MsgBox "PIO_D56 适配卡不存在!!", , "PIODIO Card Error" Exit Sub End If '取得卡片的组态信息(由于只有一片,故第一个参数给 0,代表第一 片 ) wRetVal = PIODIO_GetConfigAddressSpace(0, wBaseAddr, wIrq, wSubVendor, _ wSubDevice, wSubAux, wSlotBus, wSlotDevice) If wRetVal <> PIODIO_NoError Then MsgBox "组态信息错误 !!" Exit Sub 第 118 页,共 386 页 End If DOValue = 0 PIODIO_OutputByte wBaseAddr, 1 '激活所有的 DIO 功能 '激活 Image,允许作实验 For i = 0 To 15 imgDO(i).Enabled = True Next i End Sub '*********************************************** '当表示灯号的 Image 控件被按到时,就执行以下的程序 '每个 Image 对应到一个 Bit,状态用 fDO 记录 '数值的部份使用 DOValue 作记录 '*********************************************** Private Sub imgDO_Click(Index As Integer) fDO(Index) = Not fDO(Index) '转态 '判断状态,并将记录的数值作运算 If fDO(Index) Then '若为激活,则加上数值 DOValue = DOValue + 2 ^ Index '将灯号换为红色 imgDO(Index).Picture = imgRed.Picture Else '若为关闭,则减掉数值 DOValue = DOValue - 2 ^ Index '将灯号换为灰色 imgDO(Index).Picture = imgGray.Picture End If If Index <= 7 Then '将数值输出到低八位的2018香港马会开奖现场 PIODIO_OutputByte wBaseAddr + &HD0, DOValue And &HFF ' DO (Low-byte) End If If Index > 7 And Index <= 15 Then '将数值输出到高八位的2018香港马会开奖现场 PIODIO_OutputByte wBaseAddr + &HD4, DOValue / 2 ^ 8 ' DO (High-byte) End If End Sub 模块文件 Module1.bas 的内容如下 Public Declare Function GetTickCount Lib "kernel32" () As Long '延时函式,输入的参数以毫秒为单位 Sub Delay(DT As Long) Dim TT As Long '先取得现在的 Tick 读值 TT = GetTickCount() Do DoEvents '若长时间使用,可能会导致取到 Tick 的初始计算值,故作以下的判 断 If GetTickCount - TT <= 0 Then TT = GetTickCount Loop Until GetTickCount - TT >= DT End Sub 第 119 页,共 386 页模块文件 PIODIO.Bas 是卡片所使用的函式宣告档,由于每个 项目均需使用此模块,在此仅列出内容如下 Global Const PIODIO_NoError = 0 Global Const PIODIO_DriverOpenError = 1 Global Const PIODIO_DriverNoOpen = 2 Global Const PIODIO_GetDriverVersionError = 3 Global Const PIODIO_InstallIrqError = 4 Global Const PIODIO_ClearIntCountError = 5 Global Const PIODIO_GetIntCountError = 6 Global Const PIODIO_RegisterApcError = 7 Global Const PIODIO_RemoveIrqError = 8 Global Const PIODIO_FindBoardError = 9 Global Const PIODIO_ExceedBoardNumber = 10 Global Const PIODIO_ResetError = 11 Global Const PIODIO_IrqMaskError = 12 Global Const PIODIO_ActiveModeError = 13 Global Const PIODIO_GetActiveFlagError = 14 Global Const PIODIO_ActiveFlagEndOfQueue = 15 ' define the interrupt signal source Global Const PIOD144_P2C0 = 0 ' pin29 of CN1(37 pin D-type, pin1 to pin37) Global Const PIOD144_P2C1 = 1 ' pin28 of CN1(37 pin D-type, pin1 to pin37) Global Const PIOD144_P2C2 = 2 ' pin27 of CN1(37 pin D-type, pin1 to pin37) Global Const PIOD144_P2C3 = 3 ' pin26 of CN1(37 pin D-type, pin1 to pin37) ' Interrupt Channel for PIO-D48 Global Const PIOD48_INTCH0 = 1 ' INT_CHAN_0 Global Const PIOD48_INTCH1 = 2 ' INT_CHAN_1 Global Const PIOD48_INTCH2 = 4 ' INT_CHAN_2 Global Const PIOD48_INTCH3 = 8 ' INT_CHAN_3 ' Interrupt ActiveMode for PIOD48_XXX functions Global Const PIOD48_ActiveLow = 1 ' Active When Low Global Const PIOD48_ActiveHigh = 2 ' Active When High ' to trigger a interrupt when high -> low Global Const PIODIO_ActiveLow = 0 ' to trigger a interrupt when low -> high Global Const PIODIO_ActiveHigh = 1 ' ID Global Const PIO_D144 = &H800100 ' 144 * D/I/O Global Const PIO_D96 = &H800110 ' 96 * D/I/O Global Const PIO_D64 = &H800120 ' 64 * D/I/O Global Const PIO_D56 = &H800140 ' D24 + 16I + 16O Global Const PIO_D48 = &H800130 ' 48 * D/I/O Global Const PIO_D24 = &H800140 ' 24 * D/I/O 第 120 页,共 386 页 ' The Test functions Declare Function PIODIO_ShortSub Lib "PIODIO.dll" (ByVal a As Integer, ByVal b As Integer) As Integer Declare Function PIODIO_FloatSub Lib "PIODIO.dll" (ByVal a As Single, ByVal b As Single) As Single Declare Function PIODIO_GetDllVersion Lib "PIODIO.dll" () As Integer ' The Driver functions Declare Function PIODIO_DriverInit Lib "PIODIO.dll" () As Integer Declare Sub PIODIO_DriverClose Lib "PIODIO.dll" () Declare Function PIODIO_SearchCard Lib "PIODIO.dll" (wBoards As Integer, ByVal dwPIOPISOCardID As Long) As Integer Declare Function PIODIO_GetDriverVersion Lib "PIODIO.dll" (wDriverVersion As Integer) As Integer Declare Function PIODIO_GetConfigAddressSpace Lib "PIODIO.dll" ( _ ByVal wBoardNo As Integer, wAddrBase As Long, wIrqNo As Integer, _ wSubVendor As Integer, wSubDevice As Integer, wSubAux As Integer, _ wSlotBus As Integer, wSlotDevice As Integer) As Integer Declare Function PIODIO_ActiveBoard Lib "PIODIO.dll" (ByVal wBoardNo As Integer) As Integer Declare Function PIODIO_WhichBoardActive Lib "PIODIO.dll" () As Integer ' DIO functions Declare Sub PIODIO_OutputByte Lib "PIODIO.dll" (ByVal address As Long, ByVal dataout As Integer) Declare Sub PIODIO_OutputWord Lib "PIODIO.dll" (ByVal address As Long, ByVal dataout As Long) Declare Function PIODIO_InputByte Lib "PIODIO.dll" (ByVal address As Long) As Integer Declare Function PIODIO_InputWord Lib "PIODIO.dll" (ByVal address As Long) As Long ' Interrupt functions Declare Function PIODIO_IntInstall Lib "PIODIO.dll" (ByVal wBoard As Integer, hEvent As Long, _ ByVal wInterruptSource As Integer, ByVal wActiveMode As Integer) As Integer Declare Function PIODIO_IntRemove Lib "PIODIO.dll" () As Integer Declare Function PIODIO_IntGetCount Lib "PIODIO.dll" (dwIntCount As Long) As Integer Declare Function PIODIO_IntResetCount Lib "PIODIO.dll" () As Integer ' PIOD48 Counter functions Declare Sub PIOD48_SetCounter Lib "PIODIO.dll" _ (ByVal dwBase As Long, ByVal wCounterNo As Integer, _ ByVal bCounterMode As Integer, ByVal wCounterValue As Long) Declare Function PIOD48_ReadCounter Lib "PIODIO.dll" _ (ByVal dwBase As Long, ByVal wCounterNo As Integer, _ ByVal bCounterMode As Integer) As Long Declare Sub PIOD48_SetCounterA Lib "PIODIO.dll" _ 第 121 页,共 386 页 (ByVal wCounterNo As Integer, ByVal bCounterMode As Integer, _ ByVal wCounterValue As Long) Declare Function PIOD48_ReadCounterA Lib "PIODIO.dll" _ (ByVal wCounterNo As Integer, ByVal bCounterMode As Integer) As Long ' PIOD48 Interrupt functions Declare Function PIOD48_IntInstall Lib "PIODIO.dll" _ (ByVal wBoardNo As Integer, hEvent As Long, _ ByVal wIrqMask As Integer, ByVal wActiveMode As Integer) As Integer Declare Function PIOD48_IntRemove Lib "PIODIO.dll" () As Integer Declare Function PIOD48_IntGetActiveFlag Lib "PIODIO.dll" _ (bActiveHighFlag As Integer, bActiveLowFlag As Integer) As Integer Declare Function PIOD48_IntGetCount Lib "PIODIO.dll" _ (dwIntCount As Long) As Integer ' PIOD64 Counter functions Declare Sub PIOD64_SetCounter Lib "PIODIO.dll" _ (ByVal dwBase As Long, ByVal wCounterNo As Integer, _ ByVal bCounterMode As Integer, ByVal wCounterValue As Long) Declare Function PIOD64_ReadCounter Lib "PIODIO.dll" _ (ByVal dwBase As Long, ByVal wCounterNo As Integer, _ ByVal bCounterMode As Integer) As Long Declare Sub PIOD64_SetCounterA Lib "PIODIO.dll" _ (ByVal wCounterNo As Integer, ByVal bCounterMode As Integer, _ ByVal wCounterValue As Long) Declare Function PIOD64_ReadCounterA Lib "PIODIO.dll" _ (ByVal wCounterNo As Integer, ByVal bCounterMode As Integer) As Long ' PIOD48 Frequency Measurement Functons Declare Function PIOD48_Freq Lib "PIODIO.dll" _ (ByVal dwBase As Long) As Long Declare Function PIOD48_FreqA Lib "PIODIO.dll" () As Long 4-4-3 数字输出的连续控制 上一小节已经利用了基本的 PIODIO_OutputByte 函式测试 PIO-D56 卡片上的 CON1 接脚之数字输出功能,透过单一的点选可 以成功地控制 CON1 上的数字输出,本节将把这个数字输出的部份 利用程序达到连续控制的功能,使 DB-16R 上的继电器可以往复式 地动作。 画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立画 面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 第 122 页,共 386 页F4 叫出属性窗口,更改其 Caption 属性为” Digital Output Control (CON1)-Scan”,表示我们将进行单点的输出控制。 2、 安排十六个 Shape 控件,这十六个控件的 FillStyle 属性设为「实 心」,且将 FillColor 先设为白色,表示尚未激活。在设计的技 巧上,通常先建立第一个 Shape 的控件,Name 属性给定为 spDO, 将相关的属性均设定完毕后,利用「复制」及「贴上」二个功 能作出其它的十五个 spDO 即可,透过此法作出的控件,也可 以成为一连串的控件数组,程序的设计可以简化。我们将透过 程控的方式将这十六个代表的灯号逐次点亮(设定其 FillColor 属性的颜色为红色),同时输出指令控制 CON1。 3、 安排十六 Label 控件,其 Caption 属性输入”DO-0”~”DO-15”等 十六个,它们将用来标示代表数字输出灯号的十六个 Shape 控 件。 4、 安排三个按钮控件,其 Caption 属性分别输入「激活卡片」、 「扫描输出」及「结束程序」,用来初始化卡片、顺序控制输 出状态及结束程序。若在尚未激活卡片时,就执行输出的功能, 极易使得计算机系统当机,因此在「扫描输出」的按钮中,必 须将其 Enabled 属性设为 False,使其在程序一激活时暂时不可 使用,当卡片初始化后再解开这个限制。 5、 安排一个定时器控件,Enabled 属性设为 True, Interval 属性设 为 100(亦即每 100 毫秒执行一次定时器内的程序代码),我们将 使用此控件来达到不断扫描输出的目的。 6、 依上述步骤所作出的画面如图 4-4-5。 图 4-4-5 数字输出的画面设计 功能设计:同样的,程序的程序必须参考到函式使用的顺序(请参考 4-3-4 小节),接下来就依下列步骤作出相对应的功能: 1、 在「激活卡片」的 Click 事件程序程序中键入以下程序代码 第 123 页,共 386 页 Dim wRetVal As Integer Dim InVal1 As Integer Dim InVal2 As Integer Dim InVal3 As Integer Dim i As Long '适配卡的初始化 wInitialCode = PIODIO_DriverInit() If wInitialCode <> PIODIO_NoError Then MsgBox "驱动程序初始化失败!", , "PIODIO Card Error" Exit Sub End If '搜寻计算机中是否存在 PIO_D56 适配卡 (若使用在其它地方,此程 序也可不作) If PIODIO_SearchCard(1, PIO_D56) <> PIODIO_NoError Then MsgBox "PIO_D56 适配卡不存在!!", , "PIODIO Card Error" Exit Sub End If '取得卡片的组态信息(由于只有一片,故第一个参数给 0,代表第一 片 ) wRetVal = PIODIO_GetConfigAddressSpace(0, wBaseAddr, wIrq, wSubVendor, _ wSubDevice, wSubAux, wSlotBus, wSlotDevice) If wRetVal <> PIODIO_NoError Then MsgBox "组态信息错误 !!" Exit Sub End If PIODIO_OutputByte wBaseAddr, 1 '激活所有的 DIO 功能 '激活按钮,允许作实验 cmdScan.Enabled = True 激活卡片的程序是必须在其它的动作之前,上面的程序就将 4-3-4 小节及图 4-3-2 所说明的程序依次地写入。wBaseAddr 变量 中存放着函式所传回的适配卡2018香港马会开奖现场,在其它地方直接引用即 可。在上述程序的最后面,原来的「扫描输出」按钮这时才被 激活,这样子就可以防止在尚未激活卡片时就对卡片作输出的 动作,保护计算机而不致当机。 2、 双击 「扫描输出」的按钮控件,在其 Click 事件程序中写入以下 的程序代码 fScan = Not fScan If fScan Then cmdScan.Caption = "停止扫描" Else cmdScan.Caption = "开始扫描" End If 此程序将透过 fScan 旗标的改变而达到控制定时器内的程序动 作与否的目的,并且将此按钮上的文字改变,让使用者得知现 在的状况。 3、 双击定时器控件,在其 Timer 事件程序中写入以下的程序代码 第 124 页,共 386 页 Dim i%, j% Dim DT As Long DT = 50 Do While fScan '扫描前 8 个通道 For i = 0 To 7 PIODIO_OutputByte wBaseAddr + &HD0, 2 ^ i ' DO (Low-byte) spDO(i).FillColor = RGB(255, 0, 0) '灯号为红色 Delay DT spDO(i).FillColor = RGB(255, 255, 255) '灯号为白色 Next i PIODIO_OutputByte wBaseAddr + &HD0, 0 ' DO (Low-byte) If Not fScan Then Exit Do '扫描后 8 个通道 For i = 0 To 7 PIODIO_OutputByte wBaseAddr + &HD4, 2 ^ i ' DO (High-byte) spDO(i + 8).FillColor = RGB(255, 0, 0) '灯号为红色 Delay DT spDO(i + 8).FillColor = RGB(255, 255, 255) '灯号为白色 Next i PIODIO_OutputByte wBaseAddr + &HD4, 0 ' DO (High-byte) If Not fScan Then Exit Do '反方向扫描后 8 个通道 For i = 7 To 0 Step -1 PIODIO_OutputByte wBaseAddr + &HD4, 2 ^ i ' DO (High-byte) spDO(i + 8).FillColor = RGB(255, 0, 0) '灯号为红色 Delay DT spDO(i + 8).FillColor = RGB(255, 255, 255) '灯号为白色 Next i PIODIO_OutputByte wBaseAddr + &HD4, 0 ' DO (High-byte) If Not fScan Then Exit Do '反方向扫描前 8 个通道 For i = 7 To 0 Step -1 PIODIO_OutputByte wBaseAddr + &HD0, 2 ^ i ' DO (Low-byte) spDO(i).FillColor = RGB(255, 0, 0) '灯号为红色 Delay DT spDO(i).FillColor = RGB(255, 255, 255) '灯号为白色 Next i PIODIO_OutputByte wBaseAddr + &HD0, 0 ' DO (Low-byte) Loop 透过检查 fScan 旗标的改变而决定是否让 Do… Loop 内的程序 代码动作。程序中对于扫描的方式是先扫描低 8 位,而且是由 最低的位扫描至最高位;接着扫描高 8 位,同样是由高 8 位中 的低位扫描至高位;作为上述的二个动作,再将原来的动作反 过来。因此这个的程序的结果将会是 DB-16R 延伸板上的灯号 由低往高闪动,再高往低闪动。 第 125 页,共 386 页由于一次只移动一个位,所需送出的数值运算上就比上一小节 来得简单,看看是需要致能那一个位就是送出 2 的那一个次方 数值,例如我们希望是使得第 3 个通道亮起,那么就将数值 2 的 3 次方送到缓存器规定的2018香港马会开奖现场就可以了。 程序中也设定了时间延迟的数值(50 毫秒),目的在于让硬件有 时间作出反应,各位读者可以尝试不同的数值,看看实际的硬 件反应是如何。 如果是希望使得第 14 个通道亮起,就必须先将数值减掉 8, 那么就是送出 2 的 6 次方的数值到高字节的2018香港马会开奖现场去。为什么不是直 接送 2 的 14 次方呢?因为前后各 8 个位位于不同的2018香港马会开奖现场,它们虽然 在硬件接线上是接在一起,都叫 CON1 上的通道,但是实际的2018香港马会开奖现场 不同,必须将其分开处理。实际的处理以缓存器的2018香港马会开奖现场为准,而不 是以硬件上的排列为准。 4、 双击”结束程序”按钮,在其 Click 事件中写入以下程序代码 '关闭驱动程序 PIODIO_DriverClose '结束程序 End 先关闭驱动程序,再结束系统程序的执行。 在这个项目进行实验时,我们将 DB-16R 和 PIO-D56 的 CON1 接在一起,使得该卡片的数字输出可以直接就传到继电器延伸板 上,省却动手作测试接点的问题。程序激活后,依序地按下「激活 卡片」、「扫描输出」后就可以得到实验的结果。 实验结果如图 4-4-6 所示,读者以实验的方式验证时,除了可 以看到画面上的灯号交替外,也可以听到继电器的切换声。 图 4-4-6 DB-16R 数字输出的扫描结果 第 126 页,共 386 页灯号的变换还可以让我们的眼睛稍微分得出来,这是因为我 们在程序中使用了 Delay 函式,这个函式会暂停一小段时间,参数 是暂停的毫秒数,因为程序的执行速度通常很快,如果不用 Delay 的话,可能一闪即过。实际的延迟时间必须视各位读者的实际应用 情形而定,不见得都一样。 详细的项目程序代码请参考”EX\CH4\ CON1_DO_Scan”,双击档 案夹中的 Project1.vbp 项目即可进行以上的实验。 详细的程序代码如下: Option Explicit '基2018香港马会开奖现场 Dim wBaseAddr As Long '中断号码 Dim wIrq As Integer 'PCI 芯片讯息 Dim wSubVendor As Integer, wSubDevice As Integer, wSubAux As Integer 'PCI 插槽信息 Dim wSlotBus As Integer, wSlotDevice As Integer Dim wInitialCode As Integer Dim fScan As Boolean '激活扫描的旗标 '*********************************************** '结束程序引发下列的程序区段 '先关闭驱动程序后,再以 End 结束整个程序的运作 '*********************************************** Private Sub cmdEnd_Click() '关闭驱动程序 PIODIO_DriverClose '结束程序 End End Sub '*********************************************** '以下是卡片初始化的动作 '任何对于卡片的操作之前,必须先执行初始化的动作 '未初始化就执行动作的话,通常会使得计算机当机 '*********************************************** Private Sub CmdInitial_Click() Dim wRetVal As Integer Dim InVal1 As Integer Dim InVal2 As Integer Dim InVal3 As Integer Dim i As Long '适配卡的初始化 wInitialCode = PIODIO_DriverInit() If wInitialCode <> PIODIO_NoError Then MsgBox "驱动程序初始化失败!", , "PIODIO Card Error" Exit Sub End If 第 127 页,共 386 页 '搜寻计算机中是否存在 PIO_D56 适配卡 (若使用在其它地方,此程 序也可不作) If PIODIO_SearchCard(1, PIO_D56) <> PIODIO_NoError Then MsgBox "PIO_D56 适配卡不存在!!", , "PIODIO Card Error" Exit Sub End If '取得卡片的组态信息(由于只有一片,故第一个参数给 0,代表第一 片 ) wRetVal = PIODIO_GetConfigAddressSpace(0, wBaseAddr, wIrq, wSubVendor, _ wSubDevice, wSubAux, wSlotBus, wSlotDevice) If wRetVal <> PIODIO_NoError Then MsgBox "组态信息错误 !!" Exit Sub End If PIODIO_OutputByte wBaseAddr, 1 '激活所有的 DIO 功能 '激活按钮,允许作实验 cmdScan.Enabled = True End Sub '*********************************************** '以下是用来切换开始或停止扫描 '每一次使用者按下按钮后,fScan 状态均会发生改变 '*********************************************** Private Sub cmdScan_Click() fScan = Not fScan If fScan Then cmdScan.Caption = "停止扫描" Else cmdScan.Caption = "开始扫描" End If End Sub '*********************************************** '以下是定时器控件的动作 '16 个通道分成二个部份分别输出,而在输出后也将代表的灯号作 '改变,以便在屏幕上表示出数字输出的状态 '*********************************************** Private Sub Timer1_Timer() Dim i%, j% Dim DT As Long DT = 50 Do While fScan '扫描前 8 个通道 For i = 0 To 7 PIODIO_OutputByte wBaseAddr + &HD0, 2 ^ i ' DO (Low-byte) spDO(i).FillColor = RGB(255, 0, 0) '灯号为红色 Delay DT spDO(i).FillColor = RGB(255, 255, 255) '灯号为白色 Next i PIODIO_OutputByte wBaseAddr + &HD0, 0 ' DO (Low-byte) If Not fScan Then Exit Do '扫描后 8 个通道 For i = 0 To 7 PIODIO_OutputByte wBaseAddr + &HD4, 2 ^ i ' DO (High-byte) 第 128 页,共 386 页 spDO(i + 8).FillColor = RGB(255, 0, 0) '灯号为红色 Delay DT spDO(i + 8).FillColor = RGB(255, 255, 255) '灯号为白色 Next i PIODIO_OutputByte wBaseAddr + &HD4, 0 ' DO (High-byte) If Not fScan Then Exit Do '反方向扫描后 8 个通道 For i = 7 To 0 Step -1 PIODIO_OutputByte wBaseAddr + &HD4, 2 ^ i ' DO (High-byte) spDO(i + 8).FillColor = RGB(255, 0, 0) '灯号为红色 Delay DT spDO(i + 8).FillColor = RGB(255, 255, 255) '灯号为白色 Next i PIODIO_OutputByte wBaseAddr + &HD4, 0 ' DO (High-byte) If Not fScan Then Exit Do '反方向扫描前 8 个通道 For i = 7 To 0 Step -1 PIODIO_OutputByte wBaseAddr + &HD0, 2 ^ i ' DO (Low-byte) spDO(i).FillColor = RGB(255, 0, 0) '灯号为红色 Delay DT spDO(i).FillColor = RGB(255, 255, 255) '灯号为白色 Next i PIODIO_OutputByte wBaseAddr + &HD0, 0 ' DO (Low-byte) Loop End Sub 另外的二个模块内容和上一节相同,请参阅上一小节。 4-4-3 步进马达控制 透过上二个小节的实验,相信读者已经对于如何使用数字输出 达到控制的目的已经有了初步的了解。除了使用外接的继电器延伸 板外,我们也可以将数字输出的引脚直接依需求而自行设计电路而 予以应用,并且应用在不同的场合中,本小节将介绍如何使用 CON1 上的数字输出达到控制步进马达的目的。 相较于其它种类的马达,步进马达有以下的特征: 1、 步进马达的旋转角度和输入的脉波数成正比。通常市面上所 售的步进马达会载明其步进角(也就是一个脉波所产生的转 动角度),如果是 1.8 度的步进角,则送给它 200 个脉波会使 步进马达正好转动一圈。 第 129 页,共 386 页2、 步进马达的误差很小,没有累进误差;因此控制步进马达正 逆转还是会回到原来的位置,不会因为误差的累积而使得原 始位置愈来愈远。 3、 通常不需要对步进马达作维护。这是因为马达部份的转子(会 转动的部份)由磁性材料作成,而负责产生磁场推动马达的线 圈则固定在马达内,故信赖度相当高。 4、 步进马达的响应(Response)相当好。 5、 具有自保持的特性。加上电源后,马达本身即具备有自保持 的力矩,在不送脉波的情形下会停止在一定的位置,不会改 变。 步进马达的原理简述:最简单的永久磁铁式步进马达的内部如 图 4-4-7 所示。透过将电流流过线圈的方式而使得马达磁力线偏转, 因偏转所产生的磁力线会产生扭力而使得转子转动。 图 4-4-7 PM 步进马达内部图 依序地改变电流所流过的线圈,就可以让马达依序转动;如果 顺序相反的话,当然马达就会反方向转动。 现在常用的步进马达有二相及五相二种,常见的二相步进马达 有 A、 /A、 B、 /B 四组线圈,最简单的方式就是依序地将电流导入 这四组线圈,不同的导入方式就会产生不同的结果,此种电流的导 入称之为激磁;常见的激磁方式有以下的三种: 1、 1 相激磁:此种激磁方式是将电流一次只导入一组线圈中, 每次可移动一个步进角,激磁的顺序如表 4-4-3 所示,其中 H 表 示激磁,L 表示未激磁。 表 4-4-3 1 相激磁的顺序 相 顺序 1 2 3 4 A H L L L B L H L L /A L L H L 第 130 页,共 386 页/B L L L H 依照表 4-4-3 的方式依 1、2、3、4 的顺序分别将 4 组线圈激磁 后,步进马达会转动 4 个步进角;如果反方向激磁(也就是以 4、 3、2、1 的顺序)的话,步进马达就依相反的方向转动 4 个步进 角。 2、 2 相激磁:此种激磁方式是将电流一次导入二组线圈中,每 次可移动一个步进角,由于同时有二个线圈被激磁,因此其产生 的力矩较大,振动也较小。激磁的顺序如表 4-4-4 所示,其中 H 表示激磁,L 表示未激磁。 表 4-4-4 2 相激磁的顺序 相 顺序 1 2 3 4 A H L L H B H H L L /A L H H L /B L L H H 依照表 4-4-4 的方式依 1、2、3、4 的顺序分别将 4 组线圈中的 二组线圈激磁,步进马达会转动 4 个步进角;如果反方向激磁 (也就是以 4、3、2、1 的顺序)的话,步进马达就依相反的方向 转动 4 个步进角。 3、1-2 相激磁:此种激磁方式是上述二种激磁方式的综合,将 A、 B 二相采交互激磁的方式进行,将电流第一次导入一组线圈中, 在第二次则导入二组线圈,每次可移动半个步进角,振动是最小 的一种激磁方式。激磁的顺序如表 4-4-5 所示,其中 H 表示激磁, L 表示未激磁。 表 4-4-5 1-2 相激磁的顺序 相 顺序 1 2 3 4 5 6 7 8 A H H L L L L L H B L H H H L L L L /A L L L H H H L L /B L L L L L H H H 依照表 4-4-5 的方式依 1、 2、 3、 4、 5、 6、 7、 8 的顺序分别将 4 组线圈激磁,步进马达会转动 4 个步进角(虽然激磁 8 次,但 因每次都只有前进半个步进角,总合还是 4 个步进角);如果反 方向激磁(也就是以 4、3、2、1 的顺序)的话,步进马达就依相 反的方向转动 4 个步进角。 第 131 页,共 386 页驱动线路:有了上述的概念后,我们还需要一个驱动的电路,利用 数字输出的引脚控制步进马达的动作,图 4-4-8 是最常见的驱动电 路,也是一般步进马达实验中经常使用到的电路,使用的零件是达 灵顿晶体管、稽纳二极管及二极管。 图 4-4-8 步进马达驱动电路 图中的线圈就是由步进马达中的线圈,通常马达使用 6 线供连 接之用,其中的 4 条不同颜色的线对应到 4 组线圈,而同色的二条 是共线。图 4-4-8 的电路中,笔者使用 CON1 数字输出引脚中的第 0 脚至第 3 脚,分别驱动不同组的线圈,达到驱动马达的目的。 读者可以使用面包板作出如上的电路,笔者所完成的驱动器外 观如图 4-4-9 所示。图中使用了 20Pin 的接头连接来自 CON1 的接 线,所需的连接 Pin 和电源均来自 CON1,所以和适配卡的接线相 当简便。 图 4-4-9 步进马达驱动电路完成图 第 132 页,共 386 页如何测试步进马达的接线呢?首先假设步进马达某一条线 为 COM, 把 COM 接到电源供应器的正端, 电源的另一端分别碰触 其余四线,这时马达会转动,若碰触四次然后马达也依同一方向走四 步,则所碰触的顺序即为正确的顺序,分别为 A、 B、 /A、 /B,若 其中有一步反相,则要把碰触的顺序重新组合过,直到出现正确移动 方向为止。 建立了步进马达所需的知识和驱动器后,以下是项目的建立。 画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立画 面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 F4 叫出属性窗口,更改其 Caption 属性为” Digital Output Control (CON1)-Step Motor”,表示我们将进行步进马达的控制。 2、 安排四个 Shape 控件,这四个控件的 FillStyle 属性设为「实心」, 且将 FillColor 先设为白色,表示尚未激活。 3、 安排四个 Label 控件,其 Caption 属性输入”DO-0”~”DO-3”等十 六个,它们将用来标示代表数字输出灯号的十六个 Shape 控件。 4、 安排三个按钮控件,其 Caption 属性分别输入「激活卡片」、 「激活马达」及「结束程序」,用来初始化卡片、控制马达转 动及结束程序。若在尚未激活卡片时,就执行输出的功能,极 易使得计算机系统当机,因此在「激活马达」的按钮中,必须 将其 Enabled 属性设为 False,使其在程序一激活时暂时不可使 用,当卡片初始化后再解开这个限制。 5、 安排一个 Frame 控件,在其内部增加二个 Option 控件(可用组件 数组命名),Caption 属性分别输入”正转”和 ”逆转”,这二个作为 使步进马达正转或逆转的选择。 6、 再安排一个 Frame 控件,在其内部增加三个 Option 控件,Caption 属性分别输入”1 相激磁”、 ”2 相激磁”和 ”1-2 相激磁”,用来选 择激磁的方式为何。 7、 依上述步骤所作出的画面如图 4-4-10。 第 133 页,共 386 页 图 4-4-10 步进马达控制的画面设计 功能设计:同样的,程序的程序必须参考到函式使用的顺序(请参考 4-3-4 小节),接下来就依下列步骤作出相对应的功能: 1、 在「激活卡片」的 Click 事件程序程序中键入以下程序代码 Dim wRetVal As Integer Dim InVal1 As Integer Dim InVal2 As Integer Dim InVal3 As Integer Dim i As Long '适配卡的初始化 wInitialCode = PIODIO_DriverInit() If wInitialCode <> PIODIO_NoError Then MsgBox "驱动程序初始化失败!", , "PIODIO Card Error" Exit Sub End If '搜寻计算机中是否存在 PIO_D56 适配卡 (若使用在其它地方,此程 序也可不作) If PIODIO_SearchCard(1, PIO_D56) <> PIODIO_NoError Then MsgBox "PIO_D56 适配卡不存在!!", , "PIODIO Card Error" Exit Sub End If '取得卡片的组态信息(由于只有一片,故第一个参数给 0,代表第一 片 ) wRetVal = PIODIO_GetConfigAddressSpace(0, wBaseAddr, wIrq, wSubVendor, _ wSubDevice, wSubAux, wSlotBus, wSlotDevice) If wRetVal <> PIODIO_NoError Then MsgBox "组态信息错误 !!" Exit Sub End If PIODIO_OutputByte wBaseAddr, 1 '激活所有的 DIO 功能 '激活按钮,允许作实验 cmdControl.Enabled = True 激活卡片的程序是必须在其它的动作之前,上面的程序就将 4-3-4 小节及图 4-3-2 所说明的程序依次地写入。wBaseAddr 变量 第 134 页,共 386 页中存放着函式所传回的适配卡2018香港马会开奖现场,在其它地方直接引用即 可。在上述程序的最后面,原来的「激活马达」按钮这时才被 激活,这样子就可以防止在尚未激活卡片时就对卡片作输出的 动作,保护计算机而不致当机。 2、 双击 「激活马达」的按钮控件,在其 Click 事件程序中写入以下 的程序代码 Dim i%, DOValue%, TVal% Running = Not Running If Running Then cmdControl.Caption = "停止马达" Else cmdControl.Caption = "激活马达" End If 'N = 0 Do DOValue = CTL(OP_Mode, N) TVal = DOValue '以下找出那一个数字输出激活,并使灯号亮起 If (TVal >= 8) Then spDO(3).FillColor = RGB(255, 0, 0) '灯号为红色 TVal = TVal - 8 End If If (TVal >= 4) Then spDO(2).FillColor = RGB(255, 0, 0) '灯号为红色 TVal = TVal - 4 End If If (TVal >= 2) Then spDO(1).FillColor = RGB(255, 0, 0) '灯号为红色 TVal = TVal - 2 End If If (TVal Mod 2) <> 0 Then spDO(0).FillColor = RGB(255, 0, 0) '灯号为红色 Delay 50 '延迟 TVal = DOValue '以下找出那一个数字输出激活,并使灯号灭掉 If (TVal >= 8) Then spDO(3).FillColor = RGB(255, 255, 255) '灯号为白色 TVal = TVal - 8 End If If (TVal >= 4) Then spDO(2).FillColor = RGB(255, 255, 255) '灯号为白色 TVal = TVal - 4 End If If (TVal >= 2) Then spDO(1).FillColor = RGB(255, 255, 255) '灯号为白色 TVal = TVal - 2 End If If (TVal Mod 2) <> 0 Then spDO(0).FillColor = RGB(255, 255, 255) '灯号为白色 '数字输出 DO_Set (DOValue) '转换另一个通道 If CW_Mode Then N = N - 1 第 135 页,共 386 页 If N < 0 Then N = 7 Else N = N + 1 If N > 7 Then N = 0 End If DoEvents Loop While Running '数字输出设成 0 DO_Set 0 此程序将会实际地控制步进马达的动作;在使用者改变不同选 项的同时,程序内部的执行程序代码也会对应地改变。程序中 的 DO_Set 函式只是将原来的数字输出部份包装一下而已,其 函式如下: Public Sub DO_Set(ByVal wDO&) PIODIO_OutputByte wBaseAddr + &HD0, CInt(wDO) ' DO (Low-byte) End Sub 将原来的函式包装一下,只需传入数值。 3、 双击窗体,在其 Form_Load 事件程序中写入以下的程序代码 CTL(0, 0) = 1: CTL(0, 1) = 2: CTL(0, 2) = 4: CTL(0, 3) = 8 CTL(0, 4) = 1: CTL(0, 5) = 2: CTL(0, 6) = 4: CTL(0, 7) = 8 CTL(1, 0) = 3: CTL(1, 1) = 6: CTL(1, 2) = 12: CTL(1, 3) = 9 CTL(1, 4) = 3: CTL(1, 5) = 6: CTL(1, 6) = 12: CTL(1, 7) = 9 CTL(2, 0) = 1: CTL(2, 1) = 3: CTL(2, 2) = 2: CTL(2, 3) = 6 CTL(2, 4) = 4: CTL(2, 5) = 12: CTL(2, 6) = 8: CTL(2, 7) = 9 参考表 4-4-5,由于我们有三种不同的驱动方式,在这里使用一 个二维数组 CTL 记录了三种不同驱动方式中的激磁顺序;激磁 顺序元素有 8 个,这是考量到 1-2 相的激磁方式需要激磁 8 次 才算一个循环所设计的。 数值的填入依各相所占的位而定,各相依次占有位 0、位 1、位 2 及位 3,因此将每一个步骤所需的 0 和 1 的数值代入各位,组 合后就成为必须填在数组中的数值了。 4、 双击运转方向选项控件,在其 Click 事件中写入以下程序代码 CW_Mode = Index '改变运转方向 将方向变量填入 CW_Mode 变量中。 5、 双击激磁选项控件,在其 Click 事件中写入以下程序代码 '改变激磁的方式, 不同的 Index 会产生不一样的激磁方式 OP_Mode = Index 将方向变量填入 OP_Mode 变量中。 6、 双击”结束程序”按钮,在其 Click 事件中写入以下程序代码 '关闭驱动程序 PIODIO_DriverClose 第 136 页,共 386 页 '结束程序 End 先关闭驱动程序,再结束系统程序的执行。 在「激活马达」的程序片断中,程序中的中间部份使用了不少 的程序代码改变灯号的颜色,由于每次变换的数值必须经过计算才 能得到其所对应的画面上的灯号编号,因此程序代码会利用 CTL 数组中的数值去作运算;运算的法则是:如果数值大于 8 的话,显 示这个数值中就含有 DO3 的数值(因为位 3 若设为 1 时,其值为 8), 这时当然就要使这个灯号亮起,亮起后,就可以减去这个值,接下 去判断其它的灯号;同理,剩下的数值若大于 4 的话,这也表示数 值中含有 DO2 的数值(因为位 2 若设为 1 时,其值为 4),当然也要 将这个灯号亮起,以下的依此类推…;等到该亮起的灯号延迟一下 子,让使用者看到灯号的变化后,再利用相同的方法让灯号减掉, 如此即完成了一个灯号的判断及闪示的效果。 由二进制的排列可知,DO0 为 HI 的情形下,对应到数值 1; DO1 为 HI 的情形下,对应到数值 2;DO2 为 HI 的情形下,对应到 数值 4; DO3 为 HI 的情形下,对应到数值 8。 在这个项目进行实验时,使用作好的步进马达驱动电路结合步 进马达,使用 CON1 中的 DO0~DO3 这四支引脚即可进行此实验, 步进马达则可选择市面上所售的二相步进马达(电子材料行有售小 型步进马达约 300 元左右)。 实验结果如图 4-4-11 所示。 图 4-4-11 步进马达控制的实验结果 第 137 页,共 386 页由图 4-4-11 的实验结果可以看出,当我们选择 2 相激磁时,画 面上的数字输出灯号就会同时亮出二个灯号,这也验证了二相激磁 时必须同时激磁二组线圈的理论。 详细的项目程序代码请参考”EX\CH4\ CON1_DO_Motor”,双击 档案夹中的 Project1.vbp 项目即可进行以上的实验。 详细的程序代码如下: Option Explicit Dim wInitialCode As Integer Dim CW_Mode% ' 0:CW, 1:CCW Dim OP_Mode% ' 0:1-Phase, 1:2-Phase, 2:1-2 Phase Dim CTL%(0 To 3, 0 To 7) '激活/停止的旗标 Dim Running As Boolean, N% '*********************************************** '结束程序引发下列的程序区段 '先关闭驱动程序后,再以 End 结束整个程序的运作 '*********************************************** Private Sub cmdEnd_Click() '关闭驱动程序 PIODIO_DriverClose '结束程序 End End Sub '*********************************************** '以下是卡片初始化的动作 '任何对于卡片的操作之前,必须先执行初始化的动作 '未初始化就执行动作的话,通常会使得计算机当机 '*********************************************** Private Sub CmdInitial_Click() Dim wRetVal As Integer Dim InVal1 As Integer Dim InVal2 As Integer Dim InVal3 As Integer Dim i As Long '适配卡的初始化 wInitialCode = PIODIO_DriverInit() If wInitialCode <> PIODIO_NoError Then MsgBox "驱动程序初始化失败!", , "PIODIO Card Error" Exit Sub End If '搜寻计算机中是否存在 PIO_D56 适配卡 (若使用在其它地方,此程 序也可不作) If PIODIO_SearchCard(1, PIO_D56) <> PIODIO_NoError Then MsgBox "PIO_D56 适配卡不存在!!", , "PIODIO Card Error" Exit Sub End If '取得卡片的组态信息(由于只有一片,故第一个参数给 0,代表第一 片 ) 第 138 页,共 386 页 wRetVal = PIODIO_GetConfigAddressSpace(0, wBaseAddr, wIrq, wSubVendor, _ wSubDevice, wSubAux, wSlotBus, wSlotDevice) If wRetVal <> PIODIO_NoError Then MsgBox "组态信息错误 !!" Exit Sub End If PIODIO_OutputByte wBaseAddr, 1 '激活所有的 DIO 功能 '激活按钮,允许作实验 cmdControl.Enabled = True End Sub '*********************************************** '以下是用来使步进马达运转的程序 '依据不同的模式,会引用不同的数值 '*********************************************** Private Sub cmdControl_Click() Dim i%, DOValue%, TVal% Running = Not Running If Running Then cmdControl.Caption = "停止马达" Else cmdControl.Caption = "激活马达" End If 'N = 0 Do DOValue = CTL(OP_Mode, N) TVal = DOValue '以下找出那一个数字输出激活,并使灯号亮起 If (TVal >= 8) Then spDO(3).FillColor = RGB(255, 0, 0) '灯号为红色 TVal = TVal - 8 End If If (TVal >= 4) Then spDO(2).FillColor = RGB(255, 0, 0) '灯号为红色 TVal = TVal - 4 End If If (TVal >= 2) Then spDO(1).FillColor = RGB(255, 0, 0) '灯号为红色 TVal = TVal - 2 End If If (TVal Mod 2) <> 0 Then spDO(0).FillColor = RGB(255, 0, 0) '灯号为红色 Delay 50 '延迟 TVal = DOValue '以下找出那一个数字输出激活,并使灯号灭掉 If (TVal >= 8) Then spDO(3).FillColor = RGB(255, 255, 255) '灯号为白色 TVal = TVal - 8 End If If (TVal >= 4) Then spDO(2).FillColor = RGB(255, 255, 255) '灯号为白色 TVal = TVal - 4 End If If (TVal >= 2) Then spDO(1).FillColor = RGB(255, 255, 255) '灯号为白色 TVal = TVal - 2 End If 第 139 页,共 386 页 If (TVal Mod 2) <> 0 Then spDO(0).FillColor = RGB(255, 255, 255) '灯号为白色 '数字输出 DO_Set (DOValue) '转换另一个通道 If CW_Mode Then N = N - 1 If N < 0 Then N = 7 Else N = N + 1 If N > 7 Then N = 0 End If DoEvents Loop While Running '数字输出设成 0 DO_Set 0 End Sub '*********************************************** '窗体的启始动作 '窗体激活后,首先是将每个模式下所对应的输出数值先写入 '当需要使用时,再将数组的内容代入输出函式中 '*********************************************** Private Sub Form_Load() CTL(0, 0) = 1: CTL(0, 1) = 2: CTL(0, 2) = 4: CTL(0, 3) = 8 CTL(0, 4) = 1: CTL(0, 5) = 2: CTL(0, 6) = 4: CTL(0, 7) = 8 CTL(1, 0) = 3: CTL(1, 1) = 6: CTL(1, 2) = 12: CTL(1, 3) = 9 CTL(1, 4) = 3: CTL(1, 5) = 6: CTL(1, 6) = 12: CTL(1, 7) = 9 CTL(2, 0) = 1: CTL(2, 1) = 3: CTL(2, 2) = 2: CTL(2, 3) = 6 CTL(2, 4) = 4: CTL(2, 5) = 12: CTL(2, 6) = 8: CTL(2, 7) = 9 End Sub '*********************************************** '运转方向改变的子程序 '将选中的 Optional 控件索引写入 CW_Mode 变量 '实际运转方向的选择中以 CW_Mode 作判断 '*********************************************** Private Sub optDIR_Click(Index As Integer) CW_Mode = Index '改变运转方向 End Sub '*********************************************** '激磁方式改变的子程序 '将选中的 Optional 控件索引写入 OP_Mode 变量 '实际运转的选择中以 OP_Mode 作判断 '*********************************************** Private Sub optType_Click(Index As Integer) '改变激磁的方式, 不同的 Index 会产生不一样的激磁方式 OP_Mode = Index End Sub 模块 Module1.Bas 的内容如下 'TimeDelay Function Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) Public Declare Function GetTickCount Lib "kernel32" () As Long '基2018香港马会开奖现场 第 140 页,共 386 页Public wBaseAddr As Long '中断号码 Public wIrq As Integer 'PCI 芯片讯息 Public wSubVendor As Integer, wSubDevice As Integer, wSubAux As Integer 'PCI 插槽信息 Public wSlotBus As Integer, wSlotDevice As Integer '延时函式,输入的参数以毫秒为单位 Sub Delay(DT As Long) Dim TT As Long '先取得现在的 Tick 读值 TT = GetTickCount() Do DoEvents '若长时间使用,可能会导致取到 Tick 的初始计算值,故作以下的判 断 If GetTickCount - TT <= 0 Then TT = GetTickCount Loop Until GetTickCount - TT >= DT End Sub '*********************************************** '数字输出的子程序 '其中只是将程序省略了2018香港马会开奖现场的输入及换名称而已 '*********************************************** Public Sub DO_Set(ByVal wDO&) PIODIO_OutputByte wBaseAddr + &HD0, CInt(wDO) ' DO (Low-byte) End Sub 另外的 PIODIO.Bas 模块内容和上几节相同,请参阅上一小节。 第 141 页,共 386 页 4-5 CON2 数字输入实验 PIO-D56 的 CON2 专门用来作数字输入,本节先针对此埠讨论 实验的相关作法,并从实验中了解此埠的实际用法。 4-5-1 延伸板 DB-16P 介绍 利用之前说明过的引脚定义,实验中可以自行建立实验设备, 但由于自 PIO-D56 中延伸出线路后,还是必须自行建立测试的线路 的话,总是比较麻烦,使用匹配的延伸板是比较方便的作法。延伸 板的作用在于减少配线的麻烦,而且对于适配卡也可以有一定程度 的保护作用;笔者首先介绍使用于 CON2 的其中一款隔离式延伸板 (DB-16P)。 DB-16P 是具有隔离保护功能的数字输入延伸板,其外观如图 4-5-1 所示。 图 4-5-1 DB-16P 延伸板 DB-16P 透过牛角接头和来自 PIO-D56 的排线相连接,外部的 讯号则由讯号输入接点进入卡片,当外部的讯号进入后,会使得 DB-16P 上的 LED 发光;另外,当我们使用隔离式(Isolated)的输入 型态时,跳接排针必须转插到固定的位置上(一般使用隔离式输入, 以保护系统)。 讯号输入接点的部份分为上下二排,其接点编号如表 4-5-1, 每排各 8 组接点,其编号中的 3H 和 3L 表示第三组数字输入的高电 位输入引脚和低电位输入引脚。 第 142 页,共 386 页表 4-5-1 输入接点编号 (上排) 引脚 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 编号 0H 0L 1H 1L 2H 2L 3H 3L 4H 4L 5H 5L 6H 6L 7H 7L F.G. F.G (下排) 引脚 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 编号 8H 8L 9H 9L 10H 10L 11H 11L 12H 12L 13H 13L 14H 14L 15H 15L +5V. +12V 由于比较能保护系统,因此数字输入的引脚采用隔离式的输入 方式较为常见,DB-16P 在输入时的电路图如图 4-5-2 所示。 VCC R2 COMP U3A Photo-Couple 23 4 1 DI- (2) DI+R1 (1) R3 D/I + - 图 4-5-2 每个隔离式数字输出通道的电路图 由图 4-5-2,我们由编号(1)的位置开始依次加以讨论如下: 外界的数字状态最后会进到 CON2,程序也可以判断其逻辑状态, 而进到 CON2 的接线位置则在图中的(1)位置。 图中的(2)位置是光耦合器,在此芯片的 1、 2 引脚是外界讯号的 输入点,在图上被标示为 DI+及 DI-(相当于表 4-5-1 中每个通道 的 H 及 L);当电流顺利地由 1 进入,由 2 流出时,表示电路形 成,此时将使得光藕合芯片中的发光二极管发光。 承上述,当光耦合芯片中的发光二极管被激发后,此光线将使得 芯片内部的光敏晶体管导通,而使得芯片上的 3、 4 二支引脚接 通。 原来 3、 4 引脚不接通时,Vcc 所流出的电流由 R2 经 R1 而流向 比较器;但在 3、4 引脚接通后,电流则经由 R2 流向光敏晶体管, 而比较器中则无电流流过,因此其输出的状态发生改变。 因此 DI+与 DI-是否流过足够的电流,而使得发光二极管被激发, 就决定了 DB-16P 是否改变状态,也使得我们在 PIO-D56 读取 CON2 时,是否得到不同状态的决定因素。 第 143 页,共 386 页4-5-2 数字输入的基本侦测 依据上一小节的作法将延伸板正确地和 PIO-D56 中的 CON2 连 接妥当后,即可进行实验,本小节首先进行各别引脚的输入侦测。 画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立画 面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 F4 叫出属性窗口,更改其 Caption 属性为” Digital Input Detect (CON2)”,表示我们将进行数字输入的侦测。 2、 安排十六个 Shape 控件,这十六个控件的 FillStyle 属性设为”0- 实心”,而 FillColor 则设为白色。 3、 安排十六 Label 控件,其 Caption 属性输入”DI-0”~”DI-16”等十 六个,它们将用来标示代表数字输入灯号的十六个 Shape 控件。 4、 安排一个定时器控件,Interval 属性设为 100,表示每 100 毫秒 执行一次内部的程序代码;Enabled 属性设为 False,暂时不予 激活,等到初始化卡片后再允许激活。 5、 安排三个按钮控件,其 Caption 属性分别输入「激活卡片」、 「扫描输入」及「结束程序」,用来初始化卡片、开始扫描数 字输入的状态及结束程序。扫描输入的按钮之 Enabled 属性先 设为 False,我们必须等到激活卡片之后才允许侦测的工作。 6、 依上述步骤所作出的画面如图 4-5-3。 图 4-5-3 数字输入侦测的画面设计 功能设计:同样的,数字输入的工作开始进时,必须遵循和数字输出 一样的步骤,以便顺利开启卡片,进行输入的侦测工作;接下来就 依下列步骤作出相对应的功能: 第 144 页,共 386 页1、 在「激活卡片」的 Click 事件程序程序中键入以下程序代码 Dim wRetVal As Integer Dim InVal1 As Integer Dim InVal2 As Integer Dim InVal3 As Integer Dim i As Long '适配卡的初始化 wInitialCode = PIODIO_DriverInit() If wInitialCode <> PIODIO_NoError Then MsgBox "驱动程序初始化失败!", , "PIODIO Card Error" Exit Sub End If '搜寻计算机中是否存在 PIO_D56 适配卡 (若使用在其它地方,此程 序也可不作) If PIODIO_SearchCard(1, PIO_D56) <> PIODIO_NoError Then MsgBox "PIO_D56 适配卡不存在!!", , "PIODIO Card Error" Exit Sub End If '取得卡片的组态信息(由于只有一片,故第一个参数给 0,代表第一 片 ) wRetVal = PIODIO_GetConfigAddressSpace(0, wBaseAddr, wIrq, wSubVendor, _ wSubDevice, wSubAux, wSlotBus, wSlotDevice) If wRetVal <> PIODIO_NoError Then MsgBox "组态信息错误 !!" Exit Sub End If PIODIO_OutputByte wBaseAddr, 1 '激活所有的 DIO 功能 '激活按钮,允许作实验 cmdScan.Enabled = True 激活卡片的程序是必须在其它的动作之前,上面的程序就将 4-3-4 小节及图 4-3-2 所说明的程序依次地写入。wBaseAddr 变量 中存放着函式所传回的适配卡2018香港马会开奖现场,在其它地方直接引用即 可。原先的激活按钮之 Enabled 被设为 False,在此段程序的最 后面也设为 True,以便激活侦测。 2、 双击定时器控件,在其 Timer 事件程序中写入以下的程序代码 Dim i%, j%, DIStatus% '扫描前 8 个通道 DIStatus = PIODIO_InputByte(wBaseAddr + &HD0) ' DO (Low-byte) For i = 0 To 7 If (DIStatus And 2 ^ i) <> 0 Then spDI(i).FillColor = RGB(255, 0, 0) '灯号为红色 Else spDI(i).FillColor = RGB(255, 255, 255) '灯号为白色 End If Next i '扫描后 8 个通道 DIStatus = PIODIO_InputByte(wBaseAddr + &HD4) ' DO (Low-byte) 第 145 页,共 386 页 For i = 0 To 7 If (DIStatus And 2 ^ i) <> 0 Then spDI(i + 8).FillColor = RGB(255, 0, 0) '灯号为红色 Else spDI(i + 8).FillColor = RGB(255, 255, 255) '灯号为白色 End If Next i 数字状态的侦测透过 PIODIO_InputByte 函式进行,十六个数字输 入的通道以二个2018香港马会开奖现场进行(wBaseAddr + &HD0 及 wBaseAddr + &HD4),取得的数据再进行比对,看看是那一个通道(也就是相 对于那一个位)被激活。 位的比对透过 And 运算(如 DIStatus And 2 ^ i)来进行,如果传回的 值不为 0,表示该位的值为 1,亦表示数字输入状态为高电位; 反之,若为 0,即表示该通道的状态为低电位。 3、 双击”结束程序”按钮,在其 Click 事件中写入以下程序代码 '关闭驱动程序 PIODIO_DriverClose '结束程序 End 先关闭驱动程序,再结束系统程序的执行。 在这个项目进行实验时,我们将 DB-16P 和 PIO-D56 的 CON2 接在一起,利用 DB-16P 上的接点进行数字输入的侦测。我们可以 有如下的作法: 1、 将延伸板上的+5V 电压接到 DB-16P 上任一通道的+输入, 再用一条线将-输入接到 DB-16P 上的 GND 接点。如此的 接法将使得延伸板上该通道的代表灯号亮起,表示一个高 电位的数字输入状态。 2、 依据上述的作法,我们可以将十六个通道都作成一样的接 线,即可进行实验。 3、 另外一个作法是将+5V 电压一样接到每个通道的+输入引 脚,但是另一端的接地线则是先接到一个开关装置,再将 此开关装置连接到地线,如此便可使用开关控制数字输入 的状态,如图 4-5-4。 第 146 页,共 386 页 图 4-5-4 数字输入控制开关板 程序执行后,透过开关的切换,我们可以看到延伸板上的灯号 发生变化,定时器中的程序就会将这个变化侦测到,并且运算后显 示到画面上来。实验结果如图 4-5-3 所示,图中显示 DI0、DI7、DI9、 DI11、 DI15 为高电位状态,其余为低电位状态。 图 4-5-3 DB-16P 数字输入的侦测结果 详细的项目程序代码请参考”EX\CH4\ CON2_DI_Scan”,双击档 案夹中的 Project1.vbp 项目即可进行以上的实验。 详细的程序代码如下: Option Explicit '基2018香港马会开奖现场 Dim wBaseAddr As Long '中断号码 Dim wIrq As Integer 'PCI 芯片讯息 Dim wSubVendor As Integer, wSubDevice As Integer, wSubAux As Integer 'PCI 插槽信息 Dim wSlotBus As Integer, wSlotDevice As Integer Dim wInitialCode As Integer '*********************************************** 第 147 页,共 386 页'结束程序引发下列的程序区段 '先关闭驱动程序后,再以 End 结束整个程序的运作 '*********************************************** Private Sub cmdEnd_Click() '关闭驱动程序 PIODIO_DriverClose '结束程序 End End Sub '*********************************************** '以下是卡片初始化的动作 '任何对于卡片的操作之前,必须先执行初始化的动作 '未初始化就执行动作的话,通常会使得计算机当机 '*********************************************** Private Sub CmdInitial_Click() Dim wRetVal As Integer Dim InVal1 As Integer Dim InVal2 As Integer Dim InVal3 As Integer Dim i As Long '适配卡的初始化 wInitialCode = PIODIO_DriverInit() If wInitialCode <> PIODIO_NoError Then MsgBox "驱动程序初始化失败!", , "PIODIO Card Error" Exit Sub End If '搜寻计算机中是否存在 PIO_D56 适配卡 (若使用在其它地方,此程 序也可不作) If PIODIO_SearchCard(1, PIO_D56) <> PIODIO_NoError Then MsgBox "PIO_D56 适配卡不存在!!", , "PIODIO Card Error" Exit Sub End If '取得卡片的组态信息(由于只有一片,故第一个参数给 0,代表第一 片 ) wRetVal = PIODIO_GetConfigAddressSpace(0, wBaseAddr, wIrq, wSubVendor, _ wSubDevice, wSubAux, wSlotBus, wSlotDevice) If wRetVal <> PIODIO_NoError Then MsgBox "组态信息错误 !!" Exit Sub End If PIODIO_OutputByte wBaseAddr, 1 '激活所有的 DIO 功能 '激活按钮,允许作实验 cmdScan.Enabled = True End Sub '*********************************************** '以下是用来切换开始或停止扫描 '每一次使用者按下按钮后,fScan 状态均会发生改变 '*********************************************** Private Sub cmdScan_Click() Timer1.Enabled = Not Timer1.Enabled If Timer1.Enabled Then cmdScan.Caption = "停止扫描" Else 第 148 页,共 386 页 cmdScan.Caption = "开始扫描" End If End Sub '*********************************************** '以下是定时器控件的动作 '16 个通道分成二个部份分别输出,而在输出后也将代表的灯号作 '改变,以便在屏幕上表示出数字输出的状态 '*********************************************** Private Sub Timer1_Timer() Dim i%, j%, DIStatus% '扫描前 8 个通道 DIStatus = PIODIO_InputByte(wBaseAddr + &HD0) ' DO (Low-byte) For i = 0 To 7 If (DIStatus And 2 ^ i) <> 0 Then spDI(i).FillColor = RGB(255, 0, 0) '灯号为红色 Else spDI(i).FillColor = RGB(255, 255, 255) '灯号为白色 End If Next i '扫描后 8 个通道 DIStatus = PIODIO_InputByte(wBaseAddr + &HD4) ' DO (Low-byte) For i = 0 To 7 If (DIStatus And 2 ^ i) <> 0 Then spDI(i + 8).FillColor = RGB(255, 0, 0) '灯号为红色 Else spDI(i + 8).FillColor = RGB(255, 255, 255) '灯号为白色 End If Next i End Sub 另外的二个模块和数字输出项目中的模块一样,请参考前几个 项目。 第 149 页,共 386 页4-6 CON3 数字输出入实验 前面提到的 CON1 专作数字输出,而 CON2 则专作数字输入; 本节所讨论的 CON3 这个接口则专门要拿来作数字输出入—至于是 输出或输入则由使用者透过程序予以决定。 4-6-1 输出及输入 透过之前讨论的缓存器,我们了解到使用 CON3 时,其所提供 的 24 个输出入引脚除了数字输出的功能外,也可以拿来作数字输 入之用,其方向由2018香港马会开奖现场(基2018香港马会开奖现场+&HCC)的缓存器决定。由 4-3-2 节 的讨论,24 个通道被分为 3 组输出入埠,8 个通道一组,可以分别 定义它们的输出入方向,不过,每次定义是同一组的 8 个通道都相 同设定,如果设定为数字输出,则该组 8 个通道全部是数字输出; 同理,若设定为数字输入,同一组的 8 个通道就全部是数字输入。 将全部的 24 个通道用于同一种方向是最简便的作法,当然也 可以找到像之前讨论的 CON1 及 CON2 所使用的延伸板,如图 4-6-1 及图 4-6-2 就分别是 24 通道的数字输出延伸板及 24 通道的数字输 入延伸板。 图 4-6-1 数字输出延伸板(DB-24PRD) 第 150 页,共 386 页 图 4-6-2 数字输入通道(DB-24PD) 分别使用图 4-6-1 及图 4-6-2 中的延伸板可以如 CON1 及 CON2 般地作数字输出及数字输入的工作,在硬件接线上和 3-4 节所讨论 的是完全一样的,所不同的就是软件的设定上,而设定上的不同也 就只有缓存器2018香港马会开奖现场的差异而已。DB-24PRD 并没有提供 12V 或 24V 的工作电源,因此在使用 DB-24PRD 的习惯上,通常就利用外部的 电源来提供工作电压。笔者也建议采用外部电源,避开使用内部的 电源,免得计算机的负荷过重。 4-6-2 CON3 数字输出 相对于 CON1 所提供的数字输出,CON3 所提供的通道比 CON1 多出了 8 个通道,因此必须处理的缓存器也比 CON1 多了一个。使 用 CON3 作数字输出的程序如图 4-6-3 所示,和 CON1 的程序相当 类似,差别就在方向的选择。 图 4-6-3 CON3 数字输出程序 其它的数字输出相关课题和 3-4 节一样,本小节将建立数字输 出的项目作为范例。本项目将利用程序达到连续控制的功能,使 DB-24PRD 上的继电器可以往复式地动作,就像之前透过 CON1 控 制 DB-16R 一样。 第 151 页,共 386 页画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立画 面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 F4 叫出属性窗口,更改其 Caption 属性为” Digital Output Control (CON3)”,表示我们将进行 CON3 的输出控制。 2、 安排 24 个 Shape 控件,这 24 个控件的 FillStyle 属性设为「实 心」,且将 FillColor 先设为白色,表示尚未激活。设计上我们 也将这些 Shape 控件采用数组方式命名。我们将透过程控的方 式将这 24 个代表的灯号逐次点亮(设定其 FillColor 属性的颜色 为红色),同时输出指令控制 CON3。 3、 安排 24 个 Label 控件,其 Caption 属性输入”DO-0”~”DO-23”等 24 个,它们将用来标示代表数字输出灯号的 24 个 Shape 控件。 4、 安排三个按钮控件,其 Caption 属性分别输入「激活卡片」、 「扫描输出」及「结束程序」,用来初始化卡片、顺序控制输 出状态及结束程序。若在尚未激活卡片时,就执行输出的功能, 极易使得计算机系统当机,因此在「扫描输出」的按钮中,必 须将其 Enabled 属性设为 False,使其在程序一激活时暂时不可 使用,当卡片初始化后再解开这个限制。 5、 安排一个定时器控件,Enabled 属性设为 True, Interval 属性设 为 100(亦即每 100 毫秒执行一次定时器内的程序代码),我们将 使用此控件来达到不断扫描输出的目的。 6、 依上述步骤所作出的画面如图 4-6-4。 图 4-6-4 数字输出的画面设计 功能设计:同样的,程序的程序必须参考到函式使用的顺序(请参考 4-3-4 小节),接下来就依下列步骤作出相对应的功能: 1、 在「激活卡片」的 Click 事件程序程序中键入以下程序代码 第 152 页,共 386 页 Dim wRetVal As Integer Dim InVal1 As Integer Dim InVal2 As Integer Dim InVal3 As Integer Dim i As Long '适配卡的初始化 wInitialCode = PIODIO_DriverInit() If wInitialCode <> PIODIO_NoError Then MsgBox "驱动程序初始化失败!", , "PIODIO Card Error" Exit Sub End If '搜寻计算机中是否存在 PIO_D56 适配卡 (若使用在其它地方,此程 序也可不作) If PIODIO_SearchCard(1, PIO_D56) <> PIODIO_NoError Then MsgBox "PIO_D56 适配卡不存在!!", , "PIODIO Card Error" Exit Sub End If '取得卡片的组态信息(由于只有一片,故第一个参数给 0,代表第一 片 ) wRetVal = PIODIO_GetConfigAddressSpace(0, wBaseAddr, wIrq, wSubVendor, _ wSubDevice, wSubAux, wSlotBus, wSlotDevice) If wRetVal <> PIODIO_NoError Then MsgBox "组态信息错误 !!" Exit Sub End If PIODIO_OutputByte wBaseAddr, 1 '激活所有的 DIO 功能 '设定 CON3 中的三个端口全部作为数字输出 PIODIO_OutputByte (wBaseAddr + &HCC), &H7 '激活按钮,允许作实验 cmdScan.Enabled = True PIODIO_GetConfigAddressSpace 函式中的 wBaseAddr 变量中存放着函 式所传回的适配卡2018香港马会开奖现场,在其它地方直接引用即可。在上述程 序的最后面,原来的「扫描输出」按钮这时才被激活,这样子 就可以防止在尚未激活卡片时就对卡片作输出的动作,保护计 算机而不致当机。由于 CON3 的三个埠的输出方向控制在(基地 址 +&HCC)的缓存器上,而且必须将最低的 3 个位(位 0、位 1 及位 2)均设为 1 才能使得三个埠成为输出埠,所以使用了 PIODIO_OutputByte (wBaseAddr + &HCC), &H7 将该三个位均设为 1, 使得可以作数字输出。 2、 双击 「扫描输出」的按钮控件,在其 Click 事件程序中写入以下 的程序代码 fScan = Not fScan If fScan Then cmdScan.Caption = "停止扫描" Else cmdScan.Caption = "开始扫描" End If 第 153 页,共 386 页 此程序将透过 fScan 旗标的改变而达到控制定时器内的程序动 作与否的目的,并且将此按钮上的文字改变,让使用者得知现 在的状况。 3、 双击定时器控件,在其 Timer 事件程序中写入以下的程序代码 Dim i%, j% Dim DT As Long DT = 50 Do While fScan '扫描第 0 个端口 For i = 0 To 7 PIODIO_OutputByte wBaseAddr + &HC0, 2 ^ i 'Port0 输出 spDO(i).FillColor = RGB(255, 0, 0) '灯号为红色 Delay DT spDO(i).FillColor = RGB(255, 255, 255) '灯号为白色 Next i PIODIO_OutputByte wBaseAddr + &HC0, 0 ' Port 0 不输出 If Not fScan Then Exit Do '若旗标改变,则跳出 '扫描第 1 个端口 For i = 0 To 7 PIODIO_OutputByte wBaseAddr + &HC4, 2 ^ i ' Port1 输出 spDO(i + 8).FillColor = RGB(255, 0, 0) '灯号为红色 Delay DT spDO(i + 8).FillColor = RGB(255, 255, 255) '灯号为白色 Next i PIODIO_OutputByte wBaseAddr + &HC4, 0 'Port 1 不输出 If Not fScan Then Exit Do '若旗标改变,则跳出 '扫描第 2 个端口 For i = 0 To 7 PIODIO_OutputByte wBaseAddr + &HC8, 2 ^ i ' Port2 输出 spDO(i + 16).FillColor = RGB(255, 0, 0) '灯号为红色 Delay DT spDO(i + 16).FillColor = RGB(255, 255, 255) '灯号为白色 Next i PIODIO_OutputByte wBaseAddr + &HC8, 0 Port 2 不输出 If Not fScan Then Exit Do '反方向同时扫描 3 个端口 For i = 7 To 0 Step -1 PIODIO_OutputByte wBaseAddr + &HC0, 2 ^ i 'Port 0 输出 PIODIO_OutputByte wBaseAddr + &HC4, 2 ^ i 'Port 1 输出 PIODIO_OutputByte wBaseAddr + &HC8, 2 ^ i 'Port 2 输出 spDO(i).FillColor = RGB(255, 0, 0) '灯号为红色 spDO(i + 8).FillColor = RGB(255, 0, 0) '灯号为红色 spDO(i + 16).FillColor = RGB(255, 0, 0) '灯号为红色 Delay DT spDO(i).FillColor = RGB(255, 255, 255) '灯号为白色 spDO(i + 8).FillColor = RGB(255, 255, 255) '灯号为白色 spDO(i + 16).FillColor = RGB(255, 255, 255) '灯号为白色 Next i PIODIO_OutputByte wBaseAddr + &HC0, 0 'Port 0 不输出 第 154 页,共 386 页 PIODIO_OutputByte wBaseAddr + &HC4, 0 'Port 1 不输出 PIODIO_OutputByte wBaseAddr + &HC8, 0 'Port 2 不输出 If Not fScan Then Exit Do Loop 透过检查 fScan 旗标的改变而决定是否让 Do… Loop 内的程序 代码动作。程序中对于扫描的方式是依序扫描第 0 个端口、第 1 个埠及第 2 个埠;扫描完后,再同时扫描 3 个端口,但方向 是由高位往低位。上述所造成的实验结果就是 DB-24PRD 的灯 号依序点亮后,接着每一次亮 3 个灯。 三个端口所使用的2018香港马会开奖现场均不相同,程序中可以见到将控制的资 料分别送到三个不同的2018香港马会开奖现场,这些2018香港马会开奖现场就是在 3-3 节所讨论的 2018香港马会开奖现场。 4、 双击”结束程序”按钮,在其 Click 事件中写入以下程序代码 '关闭驱动程序 PIODIO_DriverClose '结束程序 End 先关闭驱动程序,再结束系统程序的执行。 在这个项目进行实验时,我们将 DB-24PRD 和 PIO-D56 的 CON3 接在一起(它们的连接使用的是 D 型 37 针的连接线,这和 CON1、 CON2 不同,需注意此点),并且外接工作电压(可为 24 伏或 12 伏, 视选择的延伸型号而定,以工业上来说,以 24 伏是最常用的电压)。 程序激活后,依序地按下「激活卡片」、「扫描输出」后就可以得 到实验的结果。实验结果如图 4-6-6 所示,此图所显示的结果是正 好执行到三个输出埠同时输出高电位的情形。 图 4-6-5 DB-24PRD 数字输出的扫描结果 第 155 页,共 386 页程序中使用的 Delay 函式,可以明显地看出继电器的变化; 在程序中三个端口同时往回点亮时,由于未使用 Delay 函式,虽然 程序是依序执行,但是我们看继电器的输出还是几乎同步。 详细的项目程序代码请参考”EX\CH4\ CON3_DO_Scan”,双击档 案夹中的 Project1.vbp 项目即可进行以上的实验。 详细的程序代码如下: Option Explicit '基2018香港马会开奖现场 Dim wBaseAddr As Long '中断号码 Dim wIrq As Integer 'PCI 芯片讯息 Dim wSubVendor As Integer, wSubDevice As Integer, wSubAux As Integer 'PCI 插槽信息 Dim wSlotBus As Integer, wSlotDevice As Integer Dim wInitialCode As Integer Dim fScan As Boolean '激活扫描的旗标 '*********************************************** '结束程序引发下列的程序区段 '先关闭驱动程序后,再以 End 结束整个程序的运作 '*********************************************** Private Sub cmdEnd_Click() '关闭驱动程序 PIODIO_DriverClose '结束程序 End End Sub '*********************************************** '以下是卡片初始化的动作 '任何对于卡片的操作之前,必须先执行初始化的动作 '未初始化就执行动作的话,通常会使得计算机当机 '*********************************************** Private Sub CmdInitial_Click() Dim wRetVal As Integer Dim InVal1 As Integer Dim InVal2 As Integer Dim InVal3 As Integer Dim i As Long '适配卡的初始化 wInitialCode = PIODIO_DriverInit() If wInitialCode <> PIODIO_NoError Then MsgBox "驱动程序初始化失败!", , "PIODIO Card Error" Exit Sub End If '搜寻计算机中是否存在 PIO_D56 适配卡 (若使用在其它地方,此程 序也可不作) If PIODIO_SearchCard(1, PIO_D56) <> PIODIO_NoError Then 第 156 页,共 386 页 MsgBox "PIO_D56 适配卡不存在!!", , "PIODIO Card Error" Exit Sub End If '取得卡片的组态信息(由于只有一片,故第一个参数给 0,代表第一 片 ) wRetVal = PIODIO_GetConfigAddressSpace(0, wBaseAddr, wIrq, wSubVendor, _ wSubDevice, wSubAux, wSlotBus, wSlotDevice) If wRetVal <> PIODIO_NoError Then MsgBox "组态信息错误 !!" Exit Sub End If PIODIO_OutputByte wBaseAddr, 1 '激活所有的 DIO 功能 '设定 CON3 中的三个端口全部作为数字输出 PIODIO_OutputByte (wBaseAddr + &HCC), &H7 '激活按钮,允许作实验 cmdScan.Enabled = True End Sub '*********************************************** '以下是用来切换开始或停止扫描 '每一次使用者按下按钮后,fScan 状态均会发生改变 '*********************************************** Private Sub cmdScan_Click() fScan = Not fScan If fScan Then cmdScan.Caption = "停止扫描" Else cmdScan.Caption = "开始扫描" End If End Sub '*********************************************** '以下是定时器控件的动作 '24 个通道分成三个部份分别输出,而在输出后也将代表的灯号作 '改变,以便在屏幕上表示出数字输出的状态 '输出的部份分成二个阶段,首先是依序控制各个接点 '接着是三个埠同时控制 '*********************************************** Private Sub Timer1_Timer() Dim i%, j% Dim DT As Long DT = 50 Do While fScan '扫描第 0 个端口 For i = 0 To 7 PIODIO_OutputByte wBaseAddr + &HC0, 2 ^ i 'Port0 输出 spDO(i).FillColor = RGB(255, 0, 0) '灯号为红色 Delay DT spDO(i).FillColor = RGB(255, 255, 255) '灯号为白色 Next i PIODIO_OutputByte wBaseAddr + &HC0, 0 ' Port 0 不输出 If Not fScan Then Exit Do '若旗标改变,则跳出 '扫描第 1 个端口 For i = 0 To 7 PIODIO_OutputByte wBaseAddr + &HC4, 2 ^ i ' Port1 输出 第 157 页,共 386 页 spDO(i + 8).FillColor = RGB(255, 0, 0) '灯号为红色 Delay DT spDO(i + 8).FillColor = RGB(255, 255, 255) '灯号为白色 Next i PIODIO_OutputByte wBaseAddr + &HC4, 0 'Port 1 不输出 If Not fScan Then Exit Do '若旗标改变,则跳出 '扫描第 2 个端口 For i = 0 To 7 PIODIO_OutputByte wBaseAddr + &HC8, 2 ^ i ' Port2 输出 spDO(i + 16).FillColor = RGB(255, 0, 0) '灯号为红色 Delay DT spDO(i + 16).FillColor = RGB(255, 255, 255) '灯号为白色 Next i PIODIO_OutputByte wBaseAddr + &HC8, 0 'Port 2 不输出 If Not fScan Then Exit Do '反方向同时扫描 3 个端口 For i = 7 To 0 Step -1 PIODIO_OutputByte wBaseAddr + &HC0, 2 ^ i 'Port 0 输出 PIODIO_OutputByte wBaseAddr + &HC4, 2 ^ i 'Port 1 输出 PIODIO_OutputByte wBaseAddr + &HC8, 2 ^ i 'Port 2 输出 spDO(i).FillColor = RGB(255, 0, 0) '灯号为红色 spDO(i + 8).FillColor = RGB(255, 0, 0) '灯号为红色 spDO(i + 16).FillColor = RGB(255, 0, 0) '灯号为红色 Delay DT spDO(i).FillColor = RGB(255, 255, 255) '灯号为白色 spDO(i + 8).FillColor = RGB(255, 255, 255) '灯号为白色 spDO(i + 16).FillColor = RGB(255, 255, 255) '灯号为白色 Next i PIODIO_OutputByte wBaseAddr + &HC0, 0 'Port 0 不输出 PIODIO_OutputByte wBaseAddr + &HC4, 0 'Port 1 不输出 PIODIO_OutputByte wBaseAddr + &HC8, 0 'Port 2 不输出 If Not fScan Then Exit Do Loop End Sub 另外二个模块的程序和之前相同,请参考前面项目。 4-6-3 CON3 数字输入 前一小节作了 CON3 的数字输出的实验,当我们改变输出入端 口的缓存器方向定义时,也可以将三个埠全部变为输入,本小节就 来看看这个部份。将 CON3 的埠变更为输入的条件是更改缓存器中 的数值,原来(基2018香港马会开奖现场+ &HCC)中的最低三个位代表三个端口的方向 定义,定义为输入的话要全部设为 0 ,因此必须使用 PIODIO_OutputByte 函式将数数值 0 送到该2018香港马会开奖现场去。 第 158 页,共 386 页画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立画 面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 F4 叫出属性窗口,更改其 Caption 属性为” Digital Input Detect (CON3)”,表示我们将进行数字输入的侦测。 2、 安排 24 个 Shape 控件,这 24 个控件的 FillStyle 属性设为”0-实 心 ”,而 FillColor 则设为白色。 3、 安排 24 个 Label 控件,其 Caption 属性输入”DI-0”~”DI-23”等 24 个,它们将用来标示代表数字输入灯号的 24 个 Shape 控件。 4、 安排一个定时器控件,Interval 属性设为 100,表示每 100 毫秒 执行一次内部的程序代码;Enabled 属性设为 False,暂时不予 激活,等到初始化卡片后再允许激活。 5、 安排三个按钮控件,其 Caption 属性分别输入「激活卡片」、 「扫描输入」及「结束程序」,用来初始化卡片、开始扫描数 字输入的状态及结束程序。扫描输入的按钮之 Enabled 属性先 设为 False,我们必须等到激活卡片之后才允许侦测的工作。 6、 依上述步骤所作出的画面如图 4-6-6。 图 4-6-6 数字输入侦测的画面设计 功能设计:同样的,数字输入的工作开始进时,必须遵循和数字输出 一样的步骤,以便顺利开启卡片,进行输入的侦测工作;接下来就 依下列步骤作出相对应的功能: 1、 在「激活卡片」的 Click 事件程序程序中键入以下程序代码 Dim wRetVal As Integer Dim InVal1 As Integer Dim InVal2 As Integer Dim InVal3 As Integer 第 159 页,共 386 页 Dim i As Long '适配卡的初始化 wInitialCode = PIODIO_DriverInit() If wInitialCode <> PIODIO_NoError Then MsgBox "驱动程序初始化失败!", , "PIODIO Card Error" Exit Sub End If '搜寻计算机中是否存在 PIO_D56 适配卡 (若使用在其它地方,此程 序也可不作) If PIODIO_SearchCard(1, PIO_D56) <> PIODIO_NoError Then MsgBox "PIO_D56 适配卡不存在!!", , "PIODIO Card Error" Exit Sub End If '取得卡片的组态信息(由于只有一片,故第一个参数给 0,代表第一 片 ) wRetVal = PIODIO_GetConfigAddressSpace(0, wBaseAddr, wIrq, wSubVendor, _ wSubDevice, wSubAux, wSlotBus, wSlotDevice) If wRetVal <> PIODIO_NoError Then MsgBox "组态信息错误 !!" Exit Sub End If PIODIO_OutputByte wBaseAddr, 1 '激活所有的 DIO 功能 '将 CON3 的输出入端口全部设为输入模式 PIODIO_OutputByte (wBaseAddr + &HCC), 0 '激活按钮,允许作实验 cmdScan.Enabled = True 激活卡片的程序和上一小节是一样的,但是在指定输出埠方向 上是不同的,我们注意到程序中使用 PIODIO_OutputByte (wBaseAddr + &HCC), 0 指定三个埠全部作输入(也就将 0 送到指定 的缓存器)。 2、 双击定时器控件,在其 Timer 事件程序中写入以下的程序代码 Dim i%, j%, DIStatus% Dim DT As Long '扫描 Port0 DIStatus = PIODIO_InputByte(wBaseAddr + &HC0) ' 读 取第 0 个埠 For i = 0 To 7 If (DIStatus And 2 ^ i) <> 0 Then spDI(i).FillColor = RGB(255, 0, 0) '灯号为红色 Else spDI(i).FillColor = RGB(255, 255, 255) '灯号为白色 End If Next i '扫描 Port1 DIStatus = PIODIO_InputByte(wBaseAddr + &HC4) ' 读 取第 1 个埠 For i = 0 To 7 If (DIStatus And 2 ^ i) <> 0 Then spDI(i + 8).FillColor = RGB(255, 0, 0) '灯号为红色 Else spDI(i + 8).FillColor = RGB(255, 255, 255) '灯号为白色 第 160 页,共 386 页 End If Next i '扫描 Port2 DIStatus = PIODIO_InputByte(wBaseAddr + &HC8) ' 读 取第 2 个埠 For i = 0 To 7 If (DIStatus And 2 ^ i) <> 0 Then spDI(i + 16).FillColor = RGB(255, 0, 0) '灯号为红色 Else spDI(i + 16).FillColor = RGB(255, 255, 255) '灯号为白色 End If Next i 数字状态的侦测透过 PIODIO_InputByte 函式进行,24 个数字输入 的通道以三个2018香港马会开奖现场进行(wBaseAddr + &HC0、wBaseAddr + &HC4、 wBaseAddr + &HC8),取得的数据再进行比对,看看是那一个通道 (也就是相对于那一个位)被激活,程序中的粗体字显示出它们 的不同。 位的比对透过 And 运算(如 DIStatus And 2 ^ i)来进行,如果传回的 值不为 0,表示该位的值为 1,表示数字输入状态为高电位; 反之,若为 0,即表示该通道的状态为低电位。 3、 双击”结束程序”按钮,在其 Click 事件中写入以下程序代码 '关闭驱动程序 PIODIO_DriverClose '结束程序 End 先关闭驱动程序,再结束系统程序的执行。 在这个项目进行实验时,我们将 DB-24PD 和 PIO-D56 的 CON3 接在一起,利用 DB-24PD 上的接点进行数字输入的侦测。我们可 以使用和 CON2 的数字输入相同的作法: 1、 将延伸板上的+5V 电压接到 DB-16P 上任一通道的+输入,再用 一条线将-输入接到 DB-24PD 上的 GND 接点。如此的接法将使 得延伸板上该通道的代表灯号亮起,表示一个高电位的数字输 入状态。 2、 依据上述的作法,我们可以将 24 个通道都作成一样的接线,即 可进行实验。 3、 另外一个作法是将+5V 电压一样接到每个通道的+输入引脚,但 是另一端的接地线则是先接到一个开关装置,再将此开关装置 连接到地线,如此便可使用开关控制数字输入的状态,读者可 以参考图 4-5-4。 第 161 页,共 386 页程序执行,透过开关的切换,我们可以看到延伸板上的灯号发 生变化,定时器中的程序就会将这个变化侦测并运算后显示到画面 上来。实验结果如图 4-6-7 所示,图中显示 DI0、DI8、DI10、DI17、 DI19 为高电位状态,其余为低电位状态。 图 4-6-7 DB-24PD 数字输入的侦测结果 读者也许会发现到 DB-24PRD 和 DB-24PD 均有个 50Pin 的 排线接座,是否可以将二者利用所附的排线作连接,然后其中一片 再以 37 针的 D 型排线连至 CON3 的接头呢?不行哦!50Pin 的连接座 是专给使用该型接头的适配卡所用,不可在二个延伸板间直接连 接,就算连接起来也无法顺利连通。 详细的项目程序代码请参考”EX\CH4\ CON3_DI_Scan”,双击档 案夹中的 Project1.vbp 项目即可进行以上的实验。 详细的程序代码如下: Option Explicit '基2018香港马会开奖现场 Dim wBaseAddr As Long '中断号码 Dim wIrq As Integer 'PCI 芯片讯息 Dim wSubVendor As Integer, wSubDevice As Integer, wSubAux As Integer 'PCI 插槽信息 Dim wSlotBus As Integer, wSlotDevice As Integer Dim wInitialCode As Integer '*********************************************** '结束程序引发下列的程序区段 '先关闭驱动程序后,再以 End 结束整个程序的运作 '*********************************************** Private Sub cmdEnd_Click() '关闭驱动程序 PIODIO_DriverClose 第 162 页,共 386 页 '结束程序 End End Sub '*********************************************** '以下是卡片初始化的动作 '任何对于卡片的操作之前,必须先执行初始化的动作 '未初始化就执行动作的话,通常会使得计算机当机 '*********************************************** Private Sub CmdInitial_Click() Dim wRetVal As Integer Dim InVal1 As Integer Dim InVal2 As Integer Dim InVal3 As Integer Dim i As Long '适配卡的初始化 wInitialCode = PIODIO_DriverInit() If wInitialCode <> PIODIO_NoError Then MsgBox "驱动程序初始化失败!", , "PIODIO Card Error" Exit Sub End If '搜寻计算机中是否存在 PIO_D56 适配卡 (若使用在其它地方,此程 序也可不作) If PIODIO_SearchCard(1, PIO_D56) <> PIODIO_NoError Then MsgBox "PIO_D56 适配卡不存在!!", , "PIODIO Card Error" Exit Sub End If '取得卡片的组态信息(由于只有一片,故第一个参数给 0,代表第一 片 ) wRetVal = PIODIO_GetConfigAddressSpace(0, wBaseAddr, wIrq, wSubVendor, _ wSubDevice, wSubAux, wSlotBus, wSlotDevice) If wRetVal <> PIODIO_NoError Then MsgBox "组态信息错误 !!" Exit Sub End If PIODIO_OutputByte wBaseAddr, 1 '激活所有的 DIO 功能 '将 CON3 的输出入端口全部设为输入模式 PIODIO_OutputByte (wBaseAddr + &HCC), 0 '激活按钮,允许作实验 cmdScan.Enabled = True End Sub '*********************************************** '以下是用来切换开始或停止扫描 '每一次使用者按下按钮后,fScan 状态均会发生改变 '*********************************************** Private Sub cmdScan_Click() Timer1.Enabled = Not Timer1.Enabled If Timer1.Enabled Then cmdScan.Caption = "停止扫描" Else cmdScan.Caption = "开始扫描" End If End Sub 第 163 页,共 386 页 '*********************************************** '以下是定时器控件的动作 '24 个通道分成三个部份分别输出,而在输出后也将代表的灯号作 '改变,以便在屏幕上表示出数字输出的状态 '*********************************************** Private Sub Timer1_Timer() Dim i%, j%, DIStatus% Dim DT As Long '扫描 Port0 DIStatus = PIODIO_InputByte(wBaseAddr + &HC0) ' 读 取第 0 个埠 For i = 0 To 7 If (DIStatus And 2 ^ i) <> 0 Then spDI(i).FillColor = RGB(255, 0, 0) '灯号为红色 Else spDI(i).FillColor = RGB(255, 255, 255) '灯号为白色 End If Next i '扫描 Port1 DIStatus = PIODIO_InputByte(wBaseAddr + &HC4) ' 读 取第 1 个埠 For i = 0 To 7 If (DIStatus And 2 ^ i) <> 0 Then spDI(i + 8).FillColor = RGB(255, 0, 0) '灯号为红色 Else spDI(i + 8).FillColor = RGB(255, 255, 255) '灯号为白色 End If Next i '扫描 Port2 DIStatus = PIODIO_InputByte(wBaseAddr + &HC8) ' 读 取第 2 个埠 For i = 0 To 7 If (DIStatus And 2 ^ i) <> 0 Then spDI(i + 16).FillColor = RGB(255, 0, 0) '灯号为红色 Else spDI(i + 16).FillColor = RGB(255, 255, 255) '灯号为白色 End If Next i End Sub 模块内容和之前的项目相同,请参考前面项目的部份。 4-6-4 CON3 的输出入混合作法 除了像前面二小节的作法(全部当成数字输出或全部当成数字 输入)之外,CON3 中的三个埠也可以个别指定其作用方向;当然这 也是必须在其方向控制缓存器中指定必要的数值才行。为了实验的 方便,笔者还是要介绍一种延伸板 DB-16P8R,此延伸板具有十六 个数字输入接点(光隔离式),以及 8 个数字输出接点(继电器式)。 其外观如图 4-6-8 所示。 第 164 页,共 386 页 图 4-6-8 数字输出入延伸板(DB-16P8R) 使用这款延伸板也是需要外接电源(类似于 DB-24PRD),且其 使用的外接电源必须是直流 24 伏 (工业上最常使用的直流电源)。继 电器的部份就如之前 CON1 所讨论的,我们可以利用继电器开关的 特性去连接其它电路中的一环;数字输入的部份也可以利用 CON2 的实验方式作实验,当然啦!也可以设计到某一个电路中的一段, 使电路中的电流流经数字输出接点,使其导通,而使得数字输入接 点激活。 画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立画 面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 F4 叫出属性窗口,更改其 Caption 属性为” Digital Input Detect and Digital Output Control(CON3)”,表示我们将进行数字输入 的侦测。 2、 安排 2 个 Image 控件,目录下有二个代表灯号的图标,分别加 载 Iamge 的 Picture 属性中,一个命名为 imgGrey,另一个命名 为 imgRed,其 Visible 属性均设为 False;我们将用来改变画面 上的灯号状态。 3、 安排 1 个 Frame 控件,Caption 属性改为 Port0,并在其内部安 排 8 个 Image 控件,Picture 属性先加载 imgGrey,此 8 个将用 来代表 Port0 的数字输出状态。 4、 安排 2 个 Frame 控件,Caption 属性改为 Port1 及 Port2,各安排 8 个 Shape 控件到这二个 Frame 控件中;这十六个控件的 FillStyle 属性设为”0-实心”,而 FillColor 则设为白色。它们将 用来代表数字输入状态的灯号。 第 165 页,共 386 页5、 安排 24 个 Label 控件,其 Caption 属性输入”DO0~DO7” 及 ”DI-0”~”DI-15”等 24 个,它们将用来标示代表数字输出和数 字输入灯号的 24 个控件。 6、 安排一个定时器控件,Interval 属性设为 100,表示每 100 毫秒 执行一次内部的程序代码;Enabled 属性设为 False,暂时不予 激活,等到初始化卡片后再允许激活。 7、 安排三个按钮控件,其 Caption 属性分别输入「激活卡片」、 「扫描输入」及「结束程序」,用来初始化卡片、开始扫描数 字输入的状态及结束程序。扫描输入的按钮之 Enabled 属性先 设为 False,我们必须等到激活卡片之后才允许侦测的工作。 8、 依上述步骤所作出的画面如图 4-6-9。 图 4-6-9 数字输入侦测的画面设计 功能设计:程序的设计将包含有数字输出及数字输入二个部份;接下 来就依下列步骤作出相对应的功能: 1、 在「激活卡片」的 Click 事件程序程序中键入以下程序代码 Dim wRetVal As Integer Dim InVal1 As Integer Dim InVal2 As Integer Dim InVal3 As Integer Dim i As Long '适配卡的初始化 wInitialCode = PIODIO_DriverInit() If wInitialCode <> PIODIO_NoError Then MsgBox "驱动程序初始化失败!", , "PIODIO Card Error" Exit Sub End If '搜寻计算机中是否存在 PIO_D56 适配卡 (若使用在其它地方,此程 序也可不作) If PIODIO_SearchCard(1, PIO_D56) <> PIODIO_NoError Then 第 166 页,共 386 页 MsgBox "PIO_D56 适配卡不存在!!", , "PIODIO Card Error" Exit Sub End If '取得卡片的组态信息(由于只有一片,故第一个参数给 0,代表第一 片 ) wRetVal = PIODIO_GetConfigAddressSpace(0, wBaseAddr, wIrq, wSubVendor, _ wSubDevice, wSubAux, wSlotBus, wSlotDevice) If wRetVal <> PIODIO_NoError Then MsgBox "组态信息错误 !!" Exit Sub End If PIODIO_OutputByte wBaseAddr, 1 '激活所有的 DIO 功能 '激活 Image,允许作实验 For i = 0 To 7 imgDO(i).Enabled = True fDO(i) = False '所有的状态设为 OFF Next i '将 CON3 中的 Port1 和 Port2 设为 Input, Port0 设为 Output PIODIO_OutputByte (wBaseAddr + &HCC), &H1 '将输出全部设为 Off PIODIO_OutputByte (wBaseAddr + &HC0), 0 '激活按钮,允许作实验 cmdScan.Enabled = True 激活卡片的程序和上一小节是一样的,但是在指定输出埠方向 上是不同的,我们注意到程序中使用 PIODIO_OutputByte (wBaseAddr + &HCC), 1 指定 Port0 为输出,而另二个埠则为输入。 完成输出入方向的指定后,我们也将数字输出的部份先设为 OFF,以免激活继电器。 2、 双击定时器控件,在其 Timer 事件程序中写入以下的程序代码 Dim i%, j%, DIStatus% Dim DT As Long '扫描 Port1 中的 8 个通道 DIStatus = PIODIO_InputByte(wBaseAddr + &HC4) ' Port 1 For i = 0 To 7 If (DIStatus And 2 ^ i) <> 0 Then spDI(i).FillColor = RGB(255, 0, 0) '灯号为红色 Else spDI(i).FillColor = RGB(255, 255, 255) '灯号为白色 End If Next i '扫描 Port2 中的 8 个通道 DIStatus = PIODIO_InputByte(wBaseAddr + &HC8) 'Port2 For i = 0 To 7 If (DIStatus And 2 ^ i) <> 0 Then spDI(i + 8).FillColor = RGB(255, 0, 0) '灯号为红色 Else spDI(i + 8).FillColor = RGB(255, 255, 255) '灯号为白色 End If Next i 数字状态的侦测透过 PIODIO_InputByte 函式进行,由于是 Port1 第 167 页,共 386 页和 Port2 被指定为输入方向,故针对2018香港马会开奖现场(wBaseAddr + &HC4)、 (wBaseAddr + &HC8)二个2018香港马会开奖现场作读取的动作,取得的数据再进行 比对,看看是那一个通道(也就是相对于那一个位)被激活,程 序中的粗体字显示出它们的不同。 位的比对透过 And 运算(如 DIStatus And 2 ^ i)来进行,如果传回的 值不为 0,表示该位的值为 1,表示数字输入状态为高电位; 反之,若为 0,即表示该通道的状态为低电位。 3、 双击 Port0 中的 Image 控件,在其 Click 事件中写入以下程序代 码 fDO(Index) = Not fDO(Index) '转态 '判断状态,并将记录的数值作运算 If fDO(Index) Then '若为激活,则加上数值 DOValue = DOValue + 2 ^ Index '将灯号换为红色 imgDO(Index).Picture = imgRed.Picture Else '若为关闭,则减掉数值 DOValue = DOValue - 2 ^ Index '将灯号换为灰色 imgDO(Index).Picture = imgGray.Picture End If '将数值输出到 Port0 的2018香港马会开奖现场 PIODIO_OutputByte wBaseAddr + &HC0, DOValue And &HFF 'Port0 数字输出 使用者可以透过点选画面上的 Image 控件达到控制 Port0 数字输 出的目的,同样的,点选的过程也使用数组及 DOValue 的变量 记录应有的数字输出数值,并改变灯号的显示状态。程序中的未 端也使用 AND 运算,将数值和&HFF 作运算,此举的目的在于 保证数值一定会处于 8 位以内,亦即数值绝不会超过 255,以免 超过 Port0 的范围。 如果不使用 AND 运算使其数值落于 255 以内也可以,只 不过就程序来看,作了运算比较安全就是了。 4、 双击”结束程序”按钮,在其 Click 事件中写入以下程序代码 '关闭驱动程序 PIODIO_DriverClose '结束程序 End 先关闭驱动程序,再结束系统程序的执行。 在这个项目进行实验时,揉合了 DB-24PD 和 DB-24PRD 的实验, 所以在接线就比较不一样;就数字输出的部份,我们可以外接设其 第 168 页,共 386 页它的电路,而数字输入的部份可以和 DB-24PD 一样使用相同的接 线方式作实验。 程序执行后,透过开关的切换,我们可以看到延伸板上代表数 字输入的微型 LED 灯号发生变化,定时器中的程序就会将这个变 化侦测并运算后显示到画面上来;而点选 DO-0~DO-7 也可以成功 地控制继电器的状态。实验结果如图 4-6-10 所示,图中显示 DO-0、 DO-2、 DO-4、 DO-5 是被激活的,而输入的部份 DI7、 DI9、 DI10、 为输入状态,其余为非输入状态。 图 4-6-10 DB-16P8R 数字输入的侦测结果 这个实验也显示了一个结果:数字输入的部份在未使得正负引 脚接通时,所有的显示是红色的,这表示程序读到的该通道数值是 1;当我们接通电路后,画面上显示的结果又不一样了,被接通的 通道显示白色,表示该通道的数值是 0。这样的结果和之前我们讨 论 DB-24PD 不一样,这也表示二种不同的延伸板在电路设计上有 其差异性,使用时必须考虑到这点,免得取到了相反的数据。 详细的项目程序代码请参考”EX\CH4\ CON3_DIO”,双击档案夹 中的 Project1.vbp 项目即可进行以上的实验。 详细的程序代码如下: Option Explicit '基2018香港马会开奖现场 Dim wBaseAddr As Long '中断号码 Dim wIrq As Integer 'PCI 芯片讯息 Dim wSubVendor As Integer, wSubDevice As Integer, wSubAux As Integer 'PCI 插槽信息 Dim wSlotBus As Integer, wSlotDevice As Integer Dim wInitialCode As Integer 第 169 页,共 386 页Dim fDO(0 To 7) As Boolean '数字输出的旗标 Dim DOValue As Long '数字输出数值 '*********************************************** '结束程序引发下列的程序区段 '先关闭驱动程序后,再以 End 结束整个程序的运作 '*********************************************** Private Sub cmdEnd_Click() '关闭驱动程序 PIODIO_DriverClose '结束程序 End End Sub '*********************************************** '以下是卡片初始化的动作 '任何对于卡片的操作之前,必须先执行初始化的动作 '未初始化就执行动作的话,通常会使得计算机当机 '*********************************************** Private Sub CmdInitial_Click() Dim wRetVal As Integer Dim InVal1 As Integer Dim InVal2 As Integer Dim InVal3 As Integer Dim i As Long '适配卡的初始化 wInitialCode = PIODIO_DriverInit() If wInitialCode <> PIODIO_NoError Then MsgBox "驱动程序初始化失败!", , "PIODIO Card Error" Exit Sub End If '搜寻计算机中是否存在 PIO_D56 适配卡 (若使用在其它地方,此程 序也可不作) If PIODIO_SearchCard(1, PIO_D56) <> PIODIO_NoError Then MsgBox "PIO_D56 适配卡不存在!!", , "PIODIO Card Error" Exit Sub End If '取得卡片的组态信息(由于只有一片,故第一个参数给 0,代表第一 片 ) wRetVal = PIODIO_GetConfigAddressSpace(0, wBaseAddr, wIrq, wSubVendor, _ wSubDevice, wSubAux, wSlotBus, wSlotDevice) If wRetVal <> PIODIO_NoError Then MsgBox "组态信息错误 !!" Exit Sub End If PIODIO_OutputByte wBaseAddr, 1 '激活所有的 DIO 功能 '激活 Image,允许作实验 For i = 0 To 7 imgDO(i).Enabled = True fDO(i) = False '所有的状态设为 OFF Next i '将 CON3 中的 Port1 和 Port2 设为 Input, Port0 设为 Output PIODIO_OutputByte (wBaseAddr + &HCC), &H1 '将输出全部设为 Off 第 170 页,共 386 页 PIODIO_OutputByte (wBaseAddr + &HC0), 0 '激活按钮,允许作实验 cmdScan.Enabled = True End Sub '*********************************************** '以下是用来切换开始或停止扫描 '每一次使用者按下按钮后,fScan 状态均会发生改变 '*********************************************** Private Sub cmdScan_Click() Timer1.Enabled = Not Timer1.Enabled If Timer1.Enabled Then cmdScan.Caption = "停止扫描" Else cmdScan.Caption = "开始扫描" End If End Sub Private Sub imgDO_Click(Index As Integer) fDO(Index) = Not fDO(Index) '转态 '判断状态,并将记录的数值作运算 If fDO(Index) Then '若为激活,则加上数值 DOValue = DOValue + 2 ^ Index '将灯号换为红色 imgDO(Index).Picture = imgRed.Picture Else '若为关闭,则减掉数值 DOValue = DOValue - 2 ^ Index '将灯号换为灰色 imgDO(Index).Picture = imgGray.Picture End If '将数值输出到 Port0 的2018香港马会开奖现场 PIODIO_OutputByte wBaseAddr + &HC0, DOValue And &HFF 'Port0 数字输出 End Sub '*********************************************** '以下是定时器控件的动作 'Port1 和 Port2 共有 16 个通道分成二个部份分别输出,而在输出后也将代 表的灯号作 '改变,以便在屏幕上表示出数字输出的状态 '*********************************************** Private Sub Timer1_Timer() Dim i%, j%, DIStatus% Dim DT As Long '扫描 Port1 中的 8 个通道 DIStatus = PIODIO_InputByte(wBaseAddr + &HC4) ' Port 1 For i = 0 To 7 If (DIStatus And 2 ^ i) <> 0 Then spDI(i).FillColor = RGB(255, 0, 0) '灯号为红色 Else spDI(i).FillColor = RGB(255, 255, 255) '灯号为白色 End If Next i '扫描 Port2 中的 8 个通道 DIStatus = PIODIO_InputByte(wBaseAddr + &HC8) 'Port2 第 171 页,共 386 页 For i = 0 To 7 If (DIStatus And 2 ^ i) <> 0 Then spDI(i + 8).FillColor = RGB(255, 0, 0) '灯号为红色 Else spDI(i + 8).FillColor = RGB(255, 255, 255) '灯号为白色 End If Next i End Sub 模块程序和之前的项目相同,请参考之前的说明。 第 172 页,共 386 页常问问题集(FAQ) Q:我的程序执行后就当机,这怎么回事? A: 这种情形最常出现在不当存取内存,会出现这样的情形必须 检查一下你是否在未开启卡片后就输出,导致数值送到不正 确的2018香港马会开奖现场。程序就最重要的是就是作一个初始化的动作。 Q: 数值的送出可以使用十进制的方式吗? A:当然可以,程序中只是为了方便说明及对照,所以采用大量的十六进制 表示法;不管使用那一种方式表示,其实数值是一样的。 Q: 一定得用延伸板吗?我是否可以自己接线呢? A:可以的。延伸板的目的在于节省配线和组装材料,但在特殊的场合中, 也有可能是要自行制作所需的接线盒或接线电路。不管采用什么样的方 式,先决条件是必须符合该适配卡的规格,不可输入太大的电流、电压或 要求其输出无法提供的推动力。 Q: 如果我有其它的适配卡,DLL 函式可以通用吗? A:部份情形下可以。如果我们直接透过 IO 2018香港马会开奖现场的方式进行卡片的控制,那 么二个不同的卡片也可以使用相同的I/O函式—因为它们都是透过2018香港马会开奖现场进 行资料的交换;但是如果函式已被再包装过,而不只是单纯的 I/O 指令的 话,可以会造成某些使用上的困扰。 第 173 页,共 386 页本章习题 1、 请说明适配卡控制的步骤。 2、 简述适配卡上的缓存器。 3、 请写一个程序利用 CON1 控制三组步进马达,且使用不同的推 动方式(分别是 1 相、2 相及 1-2 相 )。 4、 请写一程序,当侦测到 CON2 的数字输入状态为 High 时,驱动 CON1 上的数字输出? 5、 如果不使用 DB-16R 延伸板,请绘出一个简单的数字输出电路, 用以推动外部的设备(例如 LED)。 6、 设计一程序,由 CON3 侦测 8 个数字输入状态,控制 CON3 上 的 16 个数字输出通道。 第 174 页,共 386 页第五章 计时计数卡 笔者在第四章已经介绍了数字输出入适配卡的使用方法,除了 数字的输出入外,工业应用上也使用相当多的计时或是计数的功 能。计时及计数虽是二个不同的名词,但是实际上它们却是一样的 架构,只是展现的时机不同而已,而这也是本章即将讨论的课题。 5-1 卡片介绍 在第四章已经作过说明,计算机只认得高电位或是低电位,也 就是只知道 1、 0 二种状态,其余一概不知;计算机之所以可以作 那么多的事情,是因为人类将 1、 0 作了处理,使得 1、 0 不仅仅是 二个数字而已,透过 1、 0 的组合,才能将众多信息加以整合,形 成现在多功能的计算机。计时、计数功能是数字输出入功能的提升, 将基本的数字输出入再加以处理后,就可以利用芯片算出有多少个 高电位状态被侦测到,或有多少个低电至状态被侦测到,藉由这个 数字的取得,也就自然取得数目或是经过时间,当然所谓的计数或 是计时的功能就达到了。工业界最常使用的计时计数芯片是 8254 兼容芯片,本章将讨论集合了四颗 8254 兼容芯片的 PCI-TMC12 适 配卡,此适配卡提供了 12 组的计时计数模块,可以作相当多和计 时计数相关的工作呢! 5-1-1 谈谈规格 以下就针对此 PCI-TMC12 的相关重要规格描述如下,了解了相 关的规格才方便继续作其它的应用。卡片的外观如图 5-1-1 所示。 第 175 页,共 386 页 图 5-1-1 计时计数卡 PCI-TMC12 卡片外观 PCI-TMC12 提供了 12 组计时计数通道,由四颗 8254 组成;另 外,还有 16 点的单向数字输出通道及 16 点的单向数字输入通道。 由图 5-1-1 中可以看出,图右方的二个接座就是数字输出和数字输 入的连接处,这二个数字接点和我们在第四章所讨论的 CON1 和 CON2 是完全相同的。 本卡片的规格必须分二个方面来加以说明,数字输出和数字输 入的部份类似于第四章 4-1 所讨论的;另一部份则是计时计数器, 此部份就必须讨论到 8254 芯片的规格。数字输出和数字输入的部 份规格由于和第四章是一样的,笔者在此就不赘述,请读者参考第 四章 4-1 节的说明。 8254 芯片专门用于计时及计数之用,一般也被称为计时计数芯 片,一颗 8254 芯片内含有 3 组计时计数器,每一组的计数能力是 16 位长度,所谓的十六位计数长度就是当其计数开始后,其最大的 计数能力由 FFFF 开始往下数,一直数到 0,再接着回到 FFFF;平 常提到的计数也有从 0 一直往上数的情形,如图 5-1-2。 第 176 页,共 386 页 图 5-1-2 递增与递减 由图 5-1-2 可以看出,利用十六个位作为计算的范围,使用加 1 及减 1 达到计数的目的,也由于记录的范围有限,所以一旦计算 到了尽头,再算下去就得重头再来。 8254 芯片中的动作一律是由上往下数,需要达到由下往上 数的话,需要再配合程序作运算。 上述是计时计数的最基本原理,8254 透过这样的基本原理再加 上其它的功能,整合出计时计数功能适配卡,在以下的各节中也将 再说明其原理及相关定义。 5-1-2 计时计数卡使用时机 那么多的应用场合中,到底什么时候是我们使用计时计数卡的 适当时机呢?这得由卡片的功能来考虑了,看看是不是符合应用上 的需求。 上一小节已经简单地介绍了计时计数卡,也从其规格中了解到 卡片上的芯片主要就是透过计数来达到输出(或者说是一种通知)的 目的,由这个计数的想法出发,理论上只要是和计数有相关的应用 场合中就可以使用计时计数卡。例如: 1、 转速侦测:如何计算转速呢?当一个对象转动时,在转动轴 心上经常被安装有编码器(Encoder),编码器会将转动的状 态利用连续的高低电压准位输出,透过计算这高低准位的 个数及其与时间的关系,就可以反推转动对象的转速。 第 177 页,共 386 页2、 步进马达控制:在第四章,我们使用数字输出所输出的电 压控制电路的开启与关闭,使得步进马达依特定的方式转 动,从另一个角度来看,这就好象一直给步进马达脉冲 (Pulse),而使其转动;高精度的步进马达会有专用的控制 器,该控制器的使用是透过脉冲数目而决定步进马达的转 动角度(或说是移动步数),我们就可以透过计时计数卡给 予控制器脉冲,控制马达的转动。 3、 位置侦测:使用光学尺侦测位置是经常使用的方式,而光 学尺的输出就是使用计数器来作译码,再反推位置。此种 位置的侦测相当准确,但使用计时计数卡也需要较高位数 的卡片。 4、 频率计算:这里所谓的频率就是所高低电位变化的速率, 以 Hz 为单位。如果每秒产生 100 个方波,就称频率是 100Hz;这个频率可以透过卡片上的计数芯片而计算出单 位时间内的电位变化次,从而得知其频率。 5、 方波产生:由于计时计数器的输出端可以在其内部计数终 止时产生输出,透过这样的机制,我们就可以设计让其计 算固定的数量,一旦时间到,就会使得输出的状态发生变 化,只要输出的高低准位不断变化,自然就产生了方波。 6、 个数计算:数量的改变可以透过高低准位的切换而达到, 当然也就可以利用计时计数卡侦测到这个高低准位的变 化,自然得到所欲计算的个数了。 7、 硬件延迟:通常计算机上的各式语言都会提供有时间延迟 的函数,依函数的不同,或多或少会产生些许的误差;透 过计时计数卡上的功能,可以得到非常准确的时间间隔, 当然就可以得到准确的硬件延迟时间。 上述的几种情形再加以组合应用,又可以创造出很多的使用时 机,只要了解了基本的原理,再加上实验的验证,自然可以充 份地应用计时计数适配卡。 第 178 页,共 386 页5-1-3 PCI-TMC12 的引脚定义 PCI-TMC12 的接脚共有三组,分别被标示为 CON1、 CON2、 CON3,分布如图 5-1-3。 图 5-1-3 接引脚置图 以上的各引脚,分成几个部份说明如下: 1、 CON1:此部份的接点用来将 12 组计时计数器的各引脚接出, 引脚的定义如表 5-1-1 所示。 表 5-1-1 CON1 数字输出引脚定义 引脚编号 定义 引脚号码 定义 1 ECLK1 20 EXTG1 2 COUT1 21 ECLK2 3 EXTG2 22 COUT2 4 ECLK3 23 EXTG3 5 COUT3 24 ECLK4 6 EXTG4 25 COUT4 7 ECLK5 26 EXTG5 8 COUT5 27 ECLK6 9 EXTG6 28 COUT6 10 ECLK7 29 EXTG7 11 COUT7 30 ECLK8 12 EXTG8 31 COUT8 第 179 页,共 386 页13 ECLK9 32 EXTG9 14 COUT9 33 ECLK10 15 EXTG10 34 COUT10 16 ECLK11 35 EXTG11 17 COUT11 36 ECLK12 18 EXTG12 37 COUT12 19 GND 每一组的计时计数器有 CLK、EXTG、COUT 三支引脚(将于下一 节说明),由于共有 12 组,所以接头上的 37 支脚用掉了 36 支引 脚,剩下的(Pin19)就拿来当接地点。 2、 CON2:此接脚用于单向的数字输入,引脚数目为 20 支脚,一 般使用 20Pin 的排线作连接;其中的十六支脚为数字输入通道, 其它的引脚为电源或接地,引脚的定义如表 5-1-2 所示。 表 5-1-2 CON2 数字输入引脚定义 引脚编号 定义 引脚号码 定义 1 DI0 2 DI1 3 DI2 4 DI3 5 DI4 6 DI5 7 DI6 8 DI7 9 DI8 10 DI9 11 DI10 12 DI11 13 DI12 14 DI13 15 DI14 16 DI15 17 GND 18 GND 19 Vcc 20 +12V 数字输入的编号从 DI0 开始递增到 DI15,总共有十六个通道; 第 17 和 18 二支引脚为接地,所有的号讯号引脚均以此二支引脚 为参考引脚。第 19 脚为 Vcc,所谓的 Vcc 即是正 5 伏的电压(也 是和 GND 比较而得),第 20 引脚则是正 12 伏;这二支引脚所提 供的电压来自于总线,由计算机系统供应,可用来供给外部设备 所需的电源。 使用排线和 Header 作连接时,在排线通常会有一个和 Header 的缺口相对应的突起,二者只有一个方向可以平顺地作连 第 180 页,共 386 页接,也因此可以保护设备或卡片在不注意的情形下造成系统伤害; 若无突起可资判别时,通常以排线上的红色线边为第 1 脚。 3、 CON3: 此接脚用于单向的数字输出,引脚数目为 20 支脚,一 般使用 20Pin 的排线作连接;其中的十六支脚为数字输出通道, 其它的引脚为电源或接地,引脚的定义如表 5-1-3 所示。 表 5-1-3 CON3 数字输出引脚定义 引脚编号 定义 引脚号码 定义 1 DO0 2 DO1 3 DO2 4 DO3 5 DO4 6 DO5 7 DO6 8 DO7 9 DO8 10 DO9 11 DO10 12 DO11 13 DO12 14 DO13 15 DO14 16 DO15 17 GND 18 GND 19 Vcc 20 +12V 数字输出的编号从 DO0 开始递增到 DO15,总共有十六个通道; 第 17 和 18 二支引脚为接地,所有的号讯号引脚均以此二支引 脚为参考引脚。第 19 脚为 Vcc,所 谓 的 Vcc 即是正 5 伏的电压 (也是和 GND 比较而得),第 20 引脚则是正 12 伏;这二支引脚 所提供的电压来自于总线,由计算机系统供应,通常用来供给 外部设备所需的电源。 D 型接头的引脚编号和 Header 不同,其引脚分为二排,一 排的引脚比另一排多一支脚,而且多数引脚的一排其编号由 1 开始 递增,编号后再由下一排接着递增编号引脚,此与上述的 Header 的单双号分开排编号是不一样的。 三种不同功能的接头在使用时通常也会接上延伸板,这将使得 接线的动作易于进行;此适配卡的 CON1 所接的通常就是一个含有 37 个接点的端子板,而 CON2 及 CON3 所接的延伸板和第四章所讨 论的数字输出板上的数字输出及数字输入延伸板则是一样的。 第 181 页,共 386 页 5-2 计数/计时原理 适配卡能够进行输出入的功能必须依据一定的理论基础,惟有 在了解其基本原理后,架构实验时才能据以设计出正确的系统,本 节将说明 PCI-TMC12 适配卡在输出入原理上的相关议题。 本卡片是透过 8254 芯片进行计时计数的工作,因此必须先了解 到芯片的基本原理及功能,才能将这个芯片的功能发挥出来。 5-2-1 8254 的工作模式 一颗 8254 芯片内部配有 3 组计数器,以方块图来说,可以简单 地表示如图 5-2-1,图左及图右的表示方法均正确,不同的厂商可 能会有不同的标示方式,但其基本意义是相同的:二个输入,一个 输出。控制栅(Gate,或称 EXTG)引脚用来控制该计数器是否计数, 而计数器的计数速度则是由输入(Source,或称 ECLK)引脚的脉冲予 以控制(或是使用卡片上的内建脉冲),当计数完成后,输出(Out, 或称 COUT)引脚则会发生变化。 图 5-2-1 计数器示意图 芯片内的每一组计数器,其最基本的工作流程则如图 5-2-2 所 示,虽有六种不同的工作模式(后述),但均是以此为基础而发展。 第 182 页,共 386 页图 5-2-2 计数器的工作流程 8254 芯片的工作模式共有 6 种,可依据不同的需求而指定工作 的模式,各模式简要说明如下: 1、 模式 0(计数完成后中断):计数时输出为低电位,当计数完 毕后,输出升至高电位,然后一直维持在高电位,除非写 入新的计数值。若写入新的计数值,则计数器的输出引脚 降为低电位,并开始计数。 2、 模式 1(硬件可重复触发):此模式通常由控制栅作计数器的 控制;当控制栅由低电位升至高电位时开始计数,且此时 的输出状态为低电位;当计数完成后,输出升至高电位, 且计数终止。在计数完成后,可以再次将控制栅的讯号由 低电位升往高电位,而且不需要重新写入计数值,计数的 动作会再一次地进行。 3、 模式 2(脉冲产生器):当控制栅为高电位时开始计数,计数 过程中的输出为高电位;当计数完成(此模式乃是减到 1, 而不是 0)后,输出降为低电位,且经一个脉冲周期后(这时 的计数值就是 0 了 )再度升为高电位,然后继续重新计数。 由于此模式具有不断重数的特性,因此可被用来产生脉冲; 所产生的脉冲宽度是一个脉冲的宽度,如果使用的脉冲是 2MHz,则产生的脉冲宽度就是 1/2000000 秒。 4、 模式 3(方波产生器):此模式和模式 2 很类似,但是输出的 部份是成对称的波形,故为方波。当控制栅为高电位时开 始计数,且以 2 递减,计数完成后,输出的准位高低交换(原 来高电位就变成低电位,原来低电位就变成高电位),并 加 载计数初始值后,重新计数。如此形成一个周期为(计数值 /脉冲)的方波。 5、 模式 4(软件触发单击):一般情形下,控制栅维持在高电位; 计数时输出通道为高电位,当计数完成后,输出降为低电 位,并在一个脉冲周期后升为高电位,同时停止计数工作, 这会产生一个负向的脉冲,可以作为触发讯号的来源。若 欲再次计数,需维持控制栅为高电位状态,并写入计数值, 计数完成,会再产生一个负向的脉冲,故此模式为一软件 触发的模式。若控制栅为低电位时停止计数。 6、 模式 5(硬件触发单击):写入计数值后,输出通道为高电位, 而控制栅由低电位升至高电位时开始计数,计数过程输出 通道维持为高电位,当计数完成后,输出降为低电位一个 第 183 页,共 386 页脉冲周期后升为高电位,并停止计数。欲再次计数时,必 须再将控制栅的升至高电位,其它就会重复以上的过程, 因此这种模式是硬件的触发。 上述的几种模式里计数的基础在于脉冲,这个脉冲决定了计数 过程的快慢,脉冲较快时,计数值很快就会终了;若脉冲较慢的话, 同样的计数值,计数完毕的时间就会比较久,后面还会针对脉冲作 说明。 以上的六种模式的输出端来看,不外乎二种情形:计数器工作 和定时器工作。作为计数器时,芯片在 Gate 引脚的控制下持续地 减 1,直到终值时,输出一个讯号(OUT 引脚),而计数的工作便结 束;若作为定时器,在 Gate 引脚的控制下进行减 1 计数,减到终 值时,自动加载初值,又开始下数的工作,如此周而复始,因此在 OUT 输出端就产生了脉冲周期的波形。工业上的应用就以这二种情 形为基础而发展。 每一颗 8254 芯片提供 3 个独立的计数器,每一个计数的功能都 相同,也都适用在上述的不同模式中,每一次对 8254 芯片作动作 时,必须选定一个计数器,再依指令控制计数器。为了控制计数器, 必须将指令送到芯片中,使其遵照我们的指令工作,最重要且基础 的指令就是控制字组(Control Word),它是长度为 8 个位的一个字节, 用来写入控制的指令,下一小节就将讨论这个重要的字节。 利用计数芯片的特性,当我们用来作计数工作时,其程序如图 5-2-3 所示。 图 5-2-3 计数芯片的使用流程 透过图 5-2-3 的顺序即可控制计数芯片,以下各小节就分别说 明如何完成计数芯片的设定及数值的读取。 5-2-2 8254 的控制字组 透过控制字组(Control Word),我们可以控制 8254 的各种工作 模式,控制字组是一个字节大小,其各位的排列及其简写如表 5-2-1。 D0~D7 分别代表位 0 至位 7。 第 184 页,共 386 页表 5-2-1 控制字组位定义 D7 D6 D5 D4 D3 D2 D1 D0 SC1 SC0 RW1 RW0 M2 M1 M0 BCD 笔者将控制字组分成四个部份讨论如下: 首先是 D7、D6 二个位,此二个位用于选择 8254 芯片中的计数 器,它们的意义如表 5-2-2。 表 5-2-2 计数器的选择 SC1 SC0 意义 0 0 选择计数器 0 0 1 选择计数器 1 1 0 选择计数器 2 1 1 读回命令 透过设定控制字组中的 SC1 及 SC0,我们可以选择 8254 芯片中三 个计数器中的一个,并对它们执行读出或写入的指令;如果将这二 个位均设为 1 时,这个控制字组将作为读回指令,其它的各位的定 义也会跟着改变,这部份将在下一小节中讨论,此小节仅讨论控制 字组的部份。 D5 及 D4 位用来指定读取计数器的方式,如表 5-2-3。 8254 只 有一个字节的资料线和程序沟通,而其可以作十六位的计数,那么 就要有个方法可以决定程序读取的字节是十六位中的高 8 位 (高字 节 )或低 8 位 (低字节)。 表 5-2-3 计数值的读写方式 RW1 RW0 意义 0 0 定时器闩锁指令 0 1 只读/写低字节 1 0 只读/写高字节 1 1 先读/写低字节,再读写高字节 表 5-2-3 中的第一个设定值是用来闩锁计数器之用,当我们欲 读取计数器中的数值时,可以使用这个设定值将我们有兴趣的计数 器先锁住,使其不再计数,维持现阶段的计数值,程序再将被锁住 的数值读入。 D3、 D2、 D1 三个位用来决定该计数器被使用在那一模式,如 表 5-2-4 所示。上一小节曾经说明每一个计数器都有 6 种不同的工 作方式,不同的工作方式用在不同的应用场合中。 第 185 页,共 386 页表 5-2-4 工作模式的设定 M2 M1 M0 意义 0 0 0 工作模式 0 0 0 1 工作模式 1 无效 1 0 工作模式 2 无效 1 1 工作模式 3 1 0 0 工作模式 4 1 0 1 工作模式 5 D0 用于决定计数器内的计数方式,共有十六进制或十进制二种 方式,使用者可以任选其一,如表 5-2-5。十六进制的计数方式, 其最高的计数值是 65535;如果使用十进制的话,最高的计数则只 有 9999,因此应用的场合中,以十六进制的计数方式是弹性比较大 的。 表 5-2-5 计数器的计数方式 BCD 意义 0 十六进制格式 1 十进制格式 了解上述的控制字组内容后,当我们决定使用计数器侦测讯号 时,就必须先将所希望的控制方式写成控制字组所规定的格式,并 且将此字组传送给 8254 芯片,芯片一接收到我们所传送的指令后, 随即将以指令的内容,对芯片作组态设定。 例如:现在我们将使用计数器 1,计数器中的十六位均使用, 以工作模式 0 去侦测讯号,且使用十六进制格式作计数的工作;此 时控制字组的内容排列就成为 D7 D6 D5 D4 D3 D2 D1 D0 0 1 1 1 0 0 0 0 以数值表示上述的组合结果的话就是&H70,当我们利用程序将 &H70 送到 8254 芯片,芯片就会依此内容作设定。整个设定的过程 再表示如图 5-2-4,依图中的顺序确定控制字组的内容后,便可将 控制字组传入芯片中执行。 图 5-2-4 8254 设定流程 第 186 页,共 386 页 5-2-3 计数值的读取 上一小节讨论了如何对计数器作设定的工作,当计数器设定完 成后,计数器便可以开始依设定值工作了;透过外部的接入脉冲或 是适配卡本身提供的脉冲,芯片内的计数器开始依脉冲的速度及所 设定的工作模式作倒数计时,一旦计数完了,就由输出引脚输出讯 号 (输出方式亦由工作模式决定)。 在计数工作尚未结束时,我们的程序有可能要读取现在计数器 的数值,读到此计数值再继续作其它的判断。读取计数值的方法有 三种:使用 EXTG 引脚控制、计数器锁定指令、使用回读指令锁住 计数器的读值,以下分别说明: EXTG 引脚的控制:此方法属于硬件线路的控制方式。由于计 数工作的进行是由 EXTG 引脚升到高电位时开始,若想停止计数工 作的进行,只要在此引脚输入低电位就可以了。 计数器锁定指令:回顾表 5-2-3,当 RW1、 RW0 均为 0 时,控 制字组变成计数器闩锁指令,这也意谓着某一个计数器将会被锁住 而停止计数;此时的控制字组变成如下的样子: D7 D6 D5 D4 D3 D2 D1 D0 SC1 SC0 0 0 X X X X 其中的 D5 及 D4 固定是 0,而 SC1 及 SC0 的组合决定了欲锁住的 计数器(此二个位不可同时为 1,否则将成为读回指令格式);D3~D0 位在此时没有作用,一般设为 0 即可。 此锁住指令下达后,该计数器会停止计数,而其计数值也会一 直维持住,直到计数值被读取或是重新设定。读取计数值的方式是 由控制字组中的 RW1 和 RW0 决定,可能是只读取高低字节中的一 个字节,也可能是读取高低二个字节(先读取低字节,再读取高字 节 )。 回读指令:当控制字组的最高 2 个位均设为 1 时,此时的控制 字组将变为读回指令,读回的部份包含了计数值及状态值;透过这 个指令的传送,我们可以得到 3 个计数器的数值及状态,可作为判 断之用;其各位的排列及其简写如表 5-2-6。 D0~D7 分别代表位 0 至位 7。 表 5-2-6 读回指令的位定义 第 187 页,共 386 页D7 D6 D5 D4 D3 D2 D1 D0 1 1 /Count /Status CNT2 CNT1 CNT0 0 读回指令字组的最高 2 个位一定是 1,其它各位的定义说明如 下: D5(/Count): 0 时表示锁住被选取的计数器之计数值,1 时则不锁 住。如果此值为 0,且 D3、 D2、 D1 均为 1,则表示 3 个计数器同 时锁住。 D4(/Status):0 时表示锁住被选取的计数器之状态,1 时则不锁住。 当锁住状态值时,其状态字节的格式如表 5-2-7 所示,D0~D7 分别 代表位 0 至位 7。 表 5-2-7 状态字节的位定义 D7 D6 D5 D4 D3 D2 D1 D0 Out Null Count RW1 RW0 M2 M1 M0 BCD 读回状态字节后,照对表 5-2-7,当其中的 Out 为 1 时,表示该计 数器的 COUT 引脚为高电位,反之则为低电位;当 Null Count 为 1 时,表示该计数器中的数值是空的,不可读取,反之则是允许读取 计数值。状态字节中的 D5~D0 则和控制字组完全一样(参考 5-2-2 节 ),我们通常用这些数值来比较我们之前的设定值,看看是否设 定正确。有时也会利用此法检查输出引脚是否已达高电位,以便作 后续的处理。 D3(CNT2): 0 时表示不选取第 2 个计数器,1 时表示选取。 D2(CNT1): 0 时表示不选取第 1 个计数器,1 时表示选取。 D1(CNT0): 0 时表示不选取第 0 个计数器,1 时表示选取。 D0:保留未使用,一般给 0。 有时候我们可以看到资料中对于部份的引脚或位定义的文 字里,会在文字的前方加上一个斜线(/),或是直接在文字的上方加 了一条横线(如 ),这符号通常表示该引脚或位是低准位动作(Low Active),也就是说,当该引脚是低准位或位值为 0 时为激发状态。 回读指令和计数器锁定指令的差别在那里呢?同样是为了读取 计数值,不同的方式适用在不同的场合中;计数器锁定指令只能一 次锁定一个计数器,并接着去读取这个计数器中的计数值;回读指 令则可以同时锁住三个计数器,再分别去读取三个计数器中的计数 值。如果三个计数器中的计数值是彼此相关联的话,最好使用回读 指令,因为它可以将三个计数器在同一时间点锁住,计数器之间的 第 188 页,共 386 页关系不会改变;如果只是想读取某一个计数器中的数值,而与其它 的计数器并无特别相依性的话,当然使用计数器锁住指令是比较适 当的。 我们由以上的讨论也可以发现,回读指令除了可以锁住计数器 之外,也可以读回某一个计数器的状态,透过状态值的检查,我们 也可以比较其中的设定数值和之前的设定是否相符。 第 189 页,共 386 页5-3 适配卡上的各功能架构 PCI-TMC12 计时计数卡由 8254 芯片延伸而来,自然遵循该芯 片的规划;本节介绍此适配卡上的三个接头之架构。 5-3-1 计时计数输出入埠(CON1)的架构 由于每一颗 8254 配有 3 组计时计数器,再加上 PCI-TMC12 适 配卡上配有 4 颗 8254 芯片,合起来就使得此适配卡拥有 12 组十六 位的独立计时计数器,整个卡片上的 8254 芯片及计时计数器编号 如图 5-3-1 所示,四颗 8254 芯片分别将其中的计数器编号由 1 开始 编号到 12,因此透过编号也可以知道所使用的计时计数器属于那一 颗 8254 芯片。 图 5-3-1 计时计数编号及其与 8254 的关系 为了充份利用这 12 组计时计数器,卡片有一些设定是必须注意 的,首先是计数脉冲的设定。 脉冲选择: 第 190 页,共 386 页计数功能的进行一定是透过不断的计数来达到目的,脉冲是计 数讯号的来源,而计数的脉冲输入则有二种方式:一种是透过计数 器中 ECLK 引脚将脉冲接入(此为外接式);另一种则是透过卡片提 供的振荡器来提供计数脉冲(此为内接式)。外接式与内接式的脉冲 选择只能择其一,在决定所使用的脉冲方式时必须注意此点。 就内接式脉冲来说,TMC12 适配卡(如图 5-1-3)提供一颗 8M 的 振荡器作为计数脉冲的来源,并将其画分为二个不同脉冲源,简称 CLK1 及 CLK2;利用卡片上的 J27 及 J26 二个插针位置的设定可以 指定每一个计时计数器所使用的计数脉冲来源;由于每个使用者的 脉冲使用场合可能不尽相同,TMC12 对于 CLK1 及 CLK2 二个脉冲 的选择各有二种,在 J27 及 J26 的选择上就有如图 5-3-2 的方式。 图中显示出 CLK1 使用的脉冲是比较高的,可选择 8MHz 或 1.6MHz 二种,而 CLK2 所提供的脉冲就比较低,可选择 800KHz 或 80KHz。 不同的脉冲也意谓着计时计数进行的速度,不同的情形下所选择的 脉冲会不同,这点是在应用上要加以考虑的。 图 5-3-2 不同的 CLK 脉冲选择 除了 CLK1 及 CLK2 之外,12 组计时计数器还可以设定所使用 的脉冲为前一个通道的输出或是独立的外部脉冲;在 TMC12 上的 脉冲设定的选择乃是透过 Jumper 的设定,共有四种选择方式,如 图 5-3-3 所示,图中的黑色方框表示 Jmuper 所插的位置。 第 191 页,共 386 页 图 5-3-3 四种不同的脉冲选择 透过每个计时计数器所代表 Jumper 位置就可以进行每个计时 计数器的脉冲选择工作,每个计时计数器所用的 Jumper 号码如表 5-3-1,由于有 12 组计数器,故有 12 组 Jumper 须作设定。 表 5-3-1 各计数器所使用的 Jumper 号码及可设定的脉冲 计数器 Jumper 编号 可选择的脉冲 1 JP22 CLK1, CLK2, COUT6, ECLK1 2 JP23 CLK1, CLK2, COUT1, ECLK2 3 JP24 CLK1, CLK2, COUT2, ECLK3 4 JP13 CLK1, CLK2, COUT3, ECLK4 5 JP14 CLK1, CLK2, COUT4, ECLK5 6 JP15 CLK1, CLK2, COUT5, ECLK6 7 JP10 CLK1, CLK2, COUT12, ECLK7 8 JP11 CLK1, CLK2, COUT7, ECLK8 9 JP12 CLK1, CLK2, COUT8, ECLK9 10 JP1 CLK1, CLK2, COUT9, ECLK10 11 JP2 CLK1, CLK2, COUT10, ECLK11 12 JP3 CLK1, CLK2, COUT11, ECLK12 表中所显示出来的脉冲选择里,如果选择的是内部脉冲的话, 由 CLK1、 CLK2 选择其一;须注意到选择另一个计数器的 COUT 输出当成计数脉冲的情形下,前文提到使用前一个计数器输出当成 脉冲输入,但在第 1 个计数器使用的是第 6 个计数器的输出,而第 7 个计数器所使用的,则是第 12 个计数器的输出,这是比较不一样 的地方。如果既不是使用内部脉冲,也不是利用前一个计数器的输 出当成脉冲,那么当然就是使用外部脉冲的方式了。 控制栅选择: 第 192 页,共 386 页另一个须设定的是计时计数器中 Gate 的来源,上文曾经提到过 这个引脚可以用来控制计数动作进行或终止,它的设定有二种,也 是透过 Jumper 切换,如图 5-3-4(适用在第 1 组计数器至第 6 组计数 器 ),可以设定为外部接入,或前一个计数器的 COUT 输出反相后 当成此控制栅控制讯号来源。图 5-3-5 则适用在第 7 组计数器至第 12 组计数器,其可设定为外部接入,或前一个计数器的 COUT 输 出当成此控制栅控制讯号来源(未反相,和前 6 组计数器的选择项 不同)。 图 5-3-4 控制栅来源的设定(外部或前一组 COUT 反相) 图 5-3-5 控制栅来源的设定(外部或前一组 COUT) 每一个计时计数器的控制栅控制由一组 Jumper 选择,其号码如 表 5-3-2 所示,特别注意到 COUT 讯号的反相及未反相所适用的计 数器。 表 5-3-2 控制栅讯号来源的选择 计数器 Jumper 号码 控制栅讯号来源 1 J19 Inverted COUT6, EXTG1 2 J20 Inverted COUT1, EXTG2 3 J21 Inverted COUT2, EXTG3 4 J16 Inverted COUT3, EXTG4 5 J17 Inverted COUT4, EXTG5 6 J18 Inverted COUT5, EXTG6 7 J7 COUT12, EXTG7 8 J8 COUT7, EXTG8 9 J9 COUT8, EXTG9 10 J4 COUT9, EXTG10 11 J5 COUT10, EXTG11 第 193 页,共 386 页12 J6 COUT11, EXTG12 至此已将一个计时计数器所需要的设定说明过了,这些设定在 我们正式上线操作适配卡前必须完成,因此在使用此适配卡设计我 们的系统前就必须针对系统功能详细地考虑所需的脉冲、控制栅及 工作模式,以免作了一半才发现设定不符合需求,而重新再来过。 了解上述的各项设定后,我们先将 TMC12 适配卡作一些基础 的 Jumper 设定。针对内部提供的脉冲,我们参考图 5-3-2,将设定 CLK1 的插针 J27 跳成 1.6MHz,而将设定 CLK2 的插针 J26 跳成 80KHz。这二种脉冲只是笔者预先设定的,如果读者有需要使用到 高速的脉冲,还是透过 J27 及 J26 再作变更。 脉冲的选择如表 5-3-3,其中我们将 Counter1、 Counter6 及 Counter7 设定为使用外部脉冲,亦即这二个计数器所使用的工作脉 冲必须接自其 CLK 引脚;Counter2~Counter5 设定为使用 CLK1 作 为计数脉冲的基准;而 Counter8~Counter11 则以 CLK2 作为计数脉 冲的基准;比较不一样的是 Counter12,此计数器我们使用 COUT11 作为其计数脉冲的基准,如此一来,此计数器就可以拿来和 Counter11 作串接计数使用,串接计数的好处是可以加大计数的范 围,原来是 16 位的计数可以因此而变成 32 位的计数器。 表 5-3-3 各计数器的脉冲设定 计数器 Jumper 编号 设定的脉冲 1 JP22 External Clock 2 JP23 CLK1 3 JP24 CLK1 4 JP13 CLK1 5 JP14 CLK1 6 JP15 External Clock 7 JP10 External Clock 8 JP11 CLK2 9 JP12 CLK2 10 JP1 CLK2 11 JP2 CLK2 12 JP3 COUT11 控制栅讯号(Gate)的来源选择除了 Gate6 设定为 Inverted COut5(J18 的 Jumper 需作设定)之外,其它的控制栅讯号一律设定 第 194 页,共 386 页为 External Input (参考表 5-3-2),亦即讯号由外部接入,而不是使 用前一个计数器的直接输出或反相输出。 5-3-2 数字输入端口(CON2)的架构 CON2端口用来读取数字输入的讯号,在不外接延伸板的情形 下,仅能读取 TTL 准位的讯号。这个端口的每个输入引脚的内部电 路示意图如图 5-3-6 所示。 图 5-3-6 数字输入端口功能示意图 CON2 提供的数字输入通道有 16 个,当我们要利用 CON2 读取 输位输入状态时,须先激活该引脚,才能读取数字输入通道。 5-3-3 数字输出端口(CON3)的架构 CON1 端口用来输出数字讯号,提供 TTL 准位的讯号给外界。 这个端口的每个输出引脚的内部电路示意图如图 5-3-7 所示。 图 5-3-7 数字输出端口功能方块图 第 195 页,共 386 页CON3 提共了 16 个通道的数字输出,当我们要利用 CON3 作输 位输出控制时,必须先激活该引脚,才能将数字输出通道设为 1 或 0。 第 196 页,共 386 页 5-4 缓存器配置、操作及组态设定 第一章时我们提过适配卡的控制是透过缓存器的存取达到,卡 片的设计者也会根据不同的功能而配置为数不同的缓存器,本节将 讨论 TMC12 计时计数卡上的缓存器配置。 5-4-1 2018香港马会开奖现场映像(Address Mapping) 卡片的操作乃是透过缓存器而达到,2018香港马会开奖现场就是为了成功地存取 缓存器而必须配置的,在 PCI-TMC12 上的2018香港马会开奖现场配置如表 5-4-1。 表 5-4-1 PCI-TMC12 2018香港马会开奖现场映像表 2018香港马会开奖现场 读取时 写入时 wBase+0 第 0 个 Counter 的读值 写入第 0 个 Counter wBase+4 第 1 个 Counter 的读值 写入第 1 个 Counter wBase+8 第 2 个 Counter 的读值 写入第 2 个 Counter wBase+&H0C 控制字组 控制字组 wBase+&H10 保留未用 选择 8 254 芯片 (wBase 表示基2018香港马会开奖现场) 每一个缓存器中所含的数值是一个字节(8 个位),因此当我们 去读取或是写入时,除非函数库支持双字节一次读取,否则均以一 个字节为一个单位。以下各小节再将各缓存器作一个说明。 5-4-2 选择操作的 8254 芯片 TMC12 上的计时计数通道共有 12 组,分属 4 个不同的 8254 芯 片,每一次动作的进行,必须先选择所要操作的 8254 芯片,而使 用的缓存器2018香港马会开奖现场是(wBase+&H10)。 wBase+&H10 的缓存器之位排列如表 5-4-2 所示。 表 5-4-2 8254 选择缓存器位定义 Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0 保留 保留 保留 保留 保留 保留 D1 D0 第 197 页,共 386 页表 5-4-1 中的 8 个位只用到最低的二个位,此二个位的排列如 下 (此缓存器中的其它位没有用到,因此标示为保留): D0=0, D1=0:操作第一颗 8254 芯片。 D0=1, D1=0:操作第二颗 8254 芯片。 D0=0, D1=1:操作第三颗 8254 芯片。 D0=1, D1=1:操作第四颗 8254 芯片。 例如我们将操作第 5 组计数器,而第 5 组计数器属于第二颗 8254 芯片,因此我们必须先选择第二颗 8254 芯片,才能继续对它 作其它的操作,此时就要将数值&H2 送到 wBase+&H10 的2018香港马会开奖现场。 5-4-2 读写计数器 透过上一小节选择了 8254 芯片后,就可以开始对其中的计数器 操作。须特别注意到次序的关系,在卡片上的计数器编号是由 1 编 号到 12,而这 12 组计数器分别属于 4 颗不同的 8254 芯片;再则, 每一个颗 8254 芯片又含有 3 组独立的计数器。因此,当我们决定 对某一个计数器操作时,必须能够将某一个编号的计数器反推到是 属于那一颗 8254 芯片里面的那一组计数器,如此才能正确地下达 控制的指令到特定的计数器中;再以第 5 组计数器为例,它是属于 第二颗 8254 芯片,但此颗芯片含有 3 组计数器,而第 5 组计数器 是这 3 组计数器中的第 2 组。同理,卡片上编明的第 9 组计数器就 是第 3 颗 8254 芯片中的第 3 组计数器,其它的计数器实际位置依 此类推。 由表 5-4-1, (wBase+0)、 (wBase+4)、 (wBase+8)三个缓存器的 2018香港马会开奖现场分别用来存取被指令操作的 8254 芯片中的第 0 个、第 1 个、 第 2 个计数器;写入时就是将计数初值写入计数器中,让计数器从 这个数值开始往下数,而读出时就是将计数器中的现在数值读出, 看看计数值已经被计算到什么地方了。 由于计数器是十六位,可分成高低二个字节,再请读者参考 4-2 节中对于控制字组的讨论,控制字组可以决定对于计数器的读写动 作是单单读写二个字节中的一个字节,或是二个字节组均读写(均 读写的情形下是先读写取低字节,再读写高字节);利用上述的三 个缓存器2018香港马会开奖现场,我们可以分别读写同一个 8254 芯片中的 3 个计数 器;在单单读写一个字节的情形下,只要下达一次读写的指令即可 完成动作,若是须读写二个字节的话,就要连续二次的读写动作, 第 198 页,共 386 页第一次的读写动作会读写到低字节,而第二次的读写动作则会读写 到高字节。 5-4-3 读写控制字组 控制字组所在2018香港马会开奖现场是 wBase+&H0C,由 4-2 节所讨论的过程, 透过控制字组的设定,我们可能下达有关的设定或是读回状态指 令,而透过同一个2018香港马会开奖现场,则可以将状态字组读回,用以判断相关的 设定是否符合期望。 5-4-4 函数库介绍 了解各缓存器的意义和作用后,我们将透过动态连结函数库和 适配卡取得连系;在第一章提过的驱动程序安装后,这些必要的动 态连结函数库将会被拷贝到计算机中,位置通常是在 Windows\Sysytem(若为 NT 或 2000,则 为 Windows\Sysytem32)目录 下,在预设的安装目录中也会存有示范的模块宣告,如图 5-4-1。 图 5-4-1 安装目录及模块位置 图中所示的 Tmc12.bas 里面有针对 PCI-TMC12 这块卡片所作的 函数宣告如下: ‘以下是常数的宣告 Global Const TMC12_NoError = 0 第 199 页,共 386 页Global Const TMC12_DriverOpenError = 1 Global Const TMC12_DriverNoOpen = 2 Global Const TMC12_GetDriverVersionError = 3 Global Const TMC12_InstallIrqError = 4 Global Const TMC12_ClearIntCountError = 5 Global Const TMC12_GetIntCountError = 6 Global Const TMC12_RegisterApcError = 7 Global Const TMC12_RemoveIrqError = 8 Global Const TMC12_FindBoardError = 9 Global Const TMC12_ExceedBoardNumber = 10 Global Const TMC12_ResetError = 11 '以下是测试函数,用以测试 Driver 是否可以在 OS 中运作正常 Declare Function TMC12_ShortSub Lib "TMC12.dll" (ByVal a As Integer, ByVal b As Integer) As Integer Declare Function TMC12_FloatSub Lib "TMC12.dll" (ByVal a As Single, ByVal b As Single) As Single Declare Function TMC12_GetDllVersion Lib "TMC12.dll" () As Integer '驱动程序相关函数 Declare Function TMC12_DriverInit Lib "TMC12.dll" (wTotalBoards As Integer) As Integer Declare Sub TMC12_DriverClose Lib "TMC12.dll" () Declare Function TMC12_GetDriverVersion Lib "TMC12.dll" (wDriverVersion As Integer) As Integer Declare Function TMC12_GetIrqNo Lib "TMC12.dll" (wIrqNo As Integer) As Integer Declare Function TMC12_GetConfigAddressSpace Lib "TMC12.dll" (ByVal wBoardNo As Integer, wAddress0 As Integer, _ wAddress1 As Integer, wAddress2 As Integer) As Integer Declare Function TMC12_ActiveBoard Lib "TMC12.dll" (ByVal wBoradNo As Integer) As Integer Declare Function TMC12_WhichBoardActive Lib "TMC12.dll" () As Integer ' DIO 的相关函数 Declare Sub TMC12_Do Lib "TMC12.dll" (ByVal wOutData As Integer) Declare Function TMC12_Di Lib "TMC12.dll" (wDiData As Integer) As Integer Declare Sub TMC12_OutputByte Lib "TMC12.dll" (ByVal address As Integer, ByVal dataout As Byte) Declare Sub TMC12_OutputWord Lib "TMC12.dll" (ByVal address As Integer, ByVal dataout As Integer) Declare Function TMC12_InputByte Lib "TMC12.dll" (ByVal address As Integer) As Integer Declare Function TMC12_InputWord Lib "TMC12.dll" (ByVal address As Integer) As Integer '计数器相关函数 Declare Sub TMC12_Select8254 Lib "TMC12.dll" (ByVal bWhich8254 As Byte) Declare Sub TMC12_CounterSet Lib "TMC12.dll" (ByVal wWhichCounter As Integer, ByVal wConfig As Integer, ByVal dwSettingValue As Long) Declare Function TMC12_CounterRead Lib "TMC12.dll" (ByVal wWhichCounter As Integer, ByVal wConfig As Integer) As Long '中断相关函数 第 200 页,共 386 页Declare Function TMC12_InstallIrq Lib "TMC12.dll" _ (ByVal wIrq As Integer) As Integer Declare Function TMC12_RemoveIrq Lib "TMC12.dll" () As Integer Declare Function TMC12_RegisterApc Lib "TMC12.dll" _ (ByVal FuncRegisterToVxd As Long) As Integer Declare Function TMC12_ResetIntCount Lib "TMC12.dll" () As Integer Declare Function TMC12_GetIntCount Lib "TMC12.dll" _ (dwIntCount As Long) As Integer 透过厂商所提供的函数库就可以达到控制卡片的目的,上述的 粗体字部份的函数是我们即将使用到的函数,也是一般使用频率最 高的几个函数。以 TMC12 而言,使用函数的顺序则必须如图 5-4-2 所示(和第四章所讨论的 PIO-D56 适配卡稍微不同,基本观念一样, 但又加强了使用上的方便性): 图 5-4-2 函数使用流程 图 5-4-2 所使用的各个部份再予以说明如下: 驱动程序初始化:使 用 TMC12_DriverInit 函数,此函数有一个 参数,执行成功后将传回本部计算机中所拥有的 TMC12 适配卡的 数量。驱动程序必须使用系统的资源,例如中断、内存的配置等等, 这些动作必须在使用驱动程序前先行准备好,因此这个函数也必须 在其它的函数之前先执行,确定配置成功后才能进行其它的操作。 激活某个 TMC12 卡片:使用 TMC12_ActiveBoard 函数,参数 有一,是欲操作的 TMC12 的卡片编号。原本应该还有取得适配卡 组态的函数,而由上述的函数宣告中也可以看到,但在此只要使用 TMC12_ActiveBoard 函数即可将其取代。TMC12 适配卡也含有数字 输出入通道,连同这些输出入通道都使用此函数而一并激活;因此, 第 201 页,共 386 页所有的输出入动作之前,均利用此函数先将这些功能激活,以利后 续的操作。 关闭驱动程序:使用 TMC12_DriverClose 函数。当所有的输出 入动作均已完成后,已经不再需要作其它的动作了,就可利用此一 函数将驱动程序关闭掉,释放所使用的资源。 第 202 页,共 386 页 5-5 CON3 数字输出实验 TMC12 计时计数适配卡上的 CON3 用于数字输出控制之用,共 有 16 个通道,透过数字输出的控制,我们可以知道基本控制函数 的使用是否正常。本节讨论数字输出控制方面的项目。 5-5-1 十六通道数字输出 透过之前讨论的缓存器,我们了解到控制数字输出时,可以将 指令送到2018香港马会开奖现场(基2018香港马会开奖现场+&H14)的缓存器;一旦数值被卡片的缓存器 接收到后,随即控制卡片上相关通道的状态。欲达到此目的,所使 用的控制函数可以是以下的二个函数之一 Declare Sub TMC12_OutputByte Lib "TMC12.dll" (ByVal address As Integer, ByVal dataout As Byte) Declare Sub TMC12_OutputWord Lib "TMC12.dll" (ByVal address As Integer, ByVal dataout As Integer) 在第四章讨论 PIO-D56 的数字输出入卡片时,我们使用了最基 本的函数(OutputByte)达到控制输出的目的,在本章当然也可以透 过此函数将数值送到相关的缓存器;但在 TMC12 所提供的函数中 还有更精简的作法,它将上述的二个函数再作一次包装,并将2018香港马会开奖现场 的信息也隐含在其中,于是产生了以下的函数 Declare Sub TMC12_Do Lib "TMC12.dll" (ByVal wOutData As Integer) TMC12_Do 函数就包含了原来的二个输出函数而达到数值传送 的目的,由于更加地方便,本小节就使用此函数作范例。 和在第四章讨论的 PIO-D56 数字输出入适配卡一样,TMC12 上的数字输出和数字输入的架构和 PIO-D56 的数字输出入引脚控 制是一样的,所以在本小节也和第四章介绍数字输出一样的方式使 用一块继电器延伸板 DB-16R,此延伸板已于 4-4-1 节介绍过,请各 位读者直接参考该节,笔者不再赘述。 将卡片安装完成后,使用排线将 TMC12 及 DB-16R 的延伸板连 接妥当,即可开始进行本部份的程序设计;笔者也要再次强调,一 定要依照函数的使用程序来建构程序,也就是驱动程序初始化->激 活卡片->执行输出函数->关闭驱动程序->结束程序。 第 203 页,共 386 页画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立画 面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 F4 叫出属性窗口,更改其 Caption 属性为” TMC12 Digital Output”,表示我们将进行 TMC12 适配卡数字输出的控制。 2、 安排 2 个 Image 控件,目录下有二个代表灯号的图标,分别加 载 Iamge 的 Picture 属性中,一个命名为 imgGrey,另一个命名 为 imgRed,其 Visible 属性均设为 False;我们将用来改变画面 上的灯号状态。 3、 安排 16 个 Image 控件,Picture 属性先加载 imgGrey,此 16 个 控件将用来代表数字输出状态。为避免在激活卡片之前就误按 各灯号进行控制动作,故将这些 Image 控件的 Enabled 属性先 设为 False,而在激活卡片动作成功后,再将属性设回 True。 4、 安排 16 个 Label 控件,其 Caption 属性输入”DO0~DO15”,它 们 将用来标示代表数字输出的灯号。 5、 安排二个按钮控件,其 Caption 属性分别输入「激活卡片」及 「结束程序」,用来初始化卡片及结束程序。激活卡之后,我 们就可以针对各个灯号进行押按输出的控制动作。 6、 依上述步骤所作出的画面如图 5-5-1。 图 5-5-1 数字输出控制的画面设计 功能设计:接下来就依下列步骤作出相对应的功能: 1、 在窗体的 Form_Load 事件程序程序中键入以下程序代码 Dim wTotalBoard As Integer '记录卡片的数目 Dim Ret As Integer '传回值宣告 Dim i As Integer '初始化卡片,并传回卡片数目 Ret = TMC12_DriverInit(wTotalBoard) 第 204 页,共 386 页 If Ret <> TMC12_NoError Then '检查初始化是否成功 MsgBox "开启驱动程序失败!!!", vbCritical + vbOKOnly, "TMC12 Card Error" End '结束系统 End If bCardActive = False '将旗标设为 False For i = 0 To 15 imgDO(i).ToolTipText = "尚不可控制此输出,需先激活卡片!!" Next i 由于卡片的控制必须先初始化驱动程序,所以在窗体加载时就 呼叫 TMC12_DriverInit 函数建立驱动程序所需的计算机资源,只 有在建立了驱动程序所需的资源后,才能再接下去作其它的程 序动作;此初始化函数的参数会传回计算机中被侦测到的 TMC12 适配卡的总数量,使用者可以依需要选择所要控制的卡 片,只要给定编号即可,由于本书所使用的各类型适配卡均只 有一片,所以此参数只会传回 1。 2、 在「激活卡片」的 Click 事件程序程序中键入以下程序代码 Dim i% If Not bCardActive Then Command2.Caption = "停止侦测" '判断激活过程是否成功 If TMC12_ActiveBoard(0) <> TMC12_NoError Then MsgBox "激活卡片失败", vbCritical + vbOKOnly, "TMC12 DIO Control/Detect" bCardActive = False Exit Sub End If '将结束的按钮功能关闭 Command1.Enabled = False bCardActive = True For i = 0 To 15 imgDO(i).Enabled = True '激活灯号的控制权 imgDO(i).ToolTipText = "按下此灯号即可控制第" & CInt(i) & "个数字输出通道!!" Next i Else Command2.Caption = "激活卡片" bCardActive = False For i = 0 To 15 imgDO(i).Enabled = False '取消灯号的控制权 Next i For i = 0 To 15 imgDO(i).ToolTipText = "尚不可控制此输出,需先激活 卡片!!" Next i Command1.Enabled = True End If 在激活的程序中,我们使用到 TMC12_ActiveBoard(0)函数激活编 号 0 的 TMC12 适配卡,在此函数中的参数是给 0(代表第 0 片 第 205 页,共 386 页TMC12 适配卡),如果我们的计算机中所使用的 TMC12 不只一 片时,可以有比较多的选择。在第四章我们都使用基2018香港马会开奖现场来传 送数据或是取得资料,而在此处使用的方式则是不需要基地 址,当初始化成功后,自然就存在基2018香港马会开奖现场的数值在程序里,而 我们只要呼叫相关的函数,当里面需要2018香港马会开奖现场时,自然会引入存 在内存中的2018香港马会开奖现场。这是因为函数功能增强的关系。 3、 双击代表数字输出的 Image 控件,在其 Click 事件中写入以下程 序代码 Dim i% Dim J As Long fDO(Index) = Not fDO(Index) '改变旗标状态 If fDO(Index) Then imgDO(Index).Picture = imgRed.Picture '亮红灯 Else imgDO(Index).Picture = imgGrey.Picture '灯灭掉 End If J = 0 For i = 0 To 15 '检查每个旗标数值,若为 True,表示应输出 High,将其相加 If fDO(i) Then J = J + 2 ^ i Next i '先将数值转为十六进制,再转为 Int,以避开可能的 Overflow TMC12_Do CInt("&H" & Hex(J)) 使用者可以透过点选画面上的 Image 控件达到控制数字输出的 目的,点选的过程使用数组变量记录每一个通道的输出状态,并 改变灯号的显示状态。接着使用加法运算将每个通道的数值相 加,再用 TMC12_Do 函数把欲控制的数值输出到 TMC12,而此 函数的宣告型态是整数。 在上述程序中的最后一列里,数值(宣告定义为长整数)的指定先 转换为十六进制,再将十六进制转换为整数,乍看之下似乎是画 蛇添足的作法,不过,在 Visual Basic 中却是相当重要的一个观 念转折;Visual Basic 对于整数的最大值是到 32767,但是如果 十六个数字输出通道均激活的话,其数值势必是大于此最大值, 如果直接转换为整数的话,将有可能发生溢位(OverFlow),例如 图 5-5-2 的情形,该情形是使用 Visual Basic 中的实时运算窗口 所作的结果。 第 206 页,共 386 页 图 5-5-2 整数及长整数的转换 由图中,直接将十六进制的&HFFFF 数值作转换及显示时,直接 的显示和以 CInt 函数的转换,都不会发生问题(因为 Visual Basic 会将此数值当成整数型态来处理);但是在此值的后面加上一个& 符号时,此数值就成为长整数(也就是程序中所宣告的 J 之型态), 在以 CInt 转换的过程中,溢位的讯号就产生了,所以我们在程 序才会先行处理掉这个属于”边界”的问题,免得发生溢位错误。 4、 双击”结束程序”按钮,在其 Click 事件中写入以下程序代码 '关闭驱动程序 TMC12_DriverClose '结束程序 End 先关闭驱动程序,再结束系统程序的执行。 在这个项目进行实验时,连接 TMC12 和 DB-16R,点选 DO-0~DO-15 可以成功地控制继电器的状态。实验结果如图 5-5-3 所示,图中显示 DO-1、 DO-2、 DO-3、 DO-10、 DO-11 是被激活的 状态,读者也可以透过延伸板上的灯号了解到输出的情形,并可作 个比对。 图 5-5-3 DB-16R 数字输出的控制结果 第 207 页,共 386 页详细的项目程序代码请参考”EX\CH5\ DigitalOutput”,双击档案 夹中的 Project1.vbp 项目即可进行以上的实验。 详细的程序代码如下: Option Explicit Dim bCardActive As Boolean '卡片是否激活的旗标 Dim fDO(0 To 15) As Boolean '记录数字输出的状态旗标 '*********************************************** '结束按钮被按下时的程序 '接着会关闭驱动程序,以释放资源 '最后使用 End 结束程序 '*********************************************** Private Sub Command1_Click() TMC12_DriverClose End End Sub '*********************************************** '激活按钮被按下时的程序 '程序判断是否需要以 ActiveBoard 函数激活卡片 '改变 bCardActive 旗标 '*********************************************** Private Sub Command2_Click() Dim i% If Not bCardActive Then Command2.Caption = "停止侦测" '判断激活过程是否成功 If TMC12_ActiveBoard(0) <> TMC12_NoError Then MsgBox "激活卡片失败", vbCritical + vbOKOnly, "TMC12 DIO Control/Detect" bCardActive = False Exit Sub End If '将结束的按钮功能关闭 Command1.Enabled = False bCardActive = True For i = 0 To 15 imgDO(i).Enabled = True '激活灯号的控制权 imgDO(i).ToolTipText = "按下此灯号即可控制第" & CInt(i) & "个数字输出通道!!" Next i Else Command2.Caption = "激活卡片" bCardActive = False For i = 0 To 15 imgDO(i).Enabled = False '取消灯号的控制权 Next i For i = 0 To 15 imgDO(i).ToolTipText = "尚不可控制此输出,需先激活 卡片!!" Next i Command1.Enabled = True End If End Sub 第 208 页,共 386 页'*********************************************** '窗体的 Form_Load 事件,系统激活时被执行 '先初始化驱动程序,若失败则结束系统 '*********************************************** Private Sub Form_Load() Dim wTotalBoard As Integer '记录卡片的数目 Dim Ret As Integer '传回值宣告 Dim i As Integer '初始化卡片,并传回卡片数目 Ret = TMC12_DriverInit(wTotalBoard) If Ret <> TMC12_NoError Then '检查初始化是否成功 MsgBox "开启驱动程序失败!!!", vbCritical + vbOKOnly, "TMC12 Card Error" End '结束系统 End If bCardActive = False '将旗标设为 False For i = 0 To 15 imgDO(i).ToolTipText = "尚不可控制此输出,需先激活卡片!!" Next i End Sub '*********************************************** 'Image 被按时,执行 DO 的动作 '先改变代表的旗标,再依旗标作实际的输出 '*********************************************** Private Sub imgDO_Click(Index As Integer) Dim i% Dim J As Long fDO(Index) = Not fDO(Index) '改变旗标状态 If fDO(Index) Then imgDO(Index).Picture = imgRed.Picture '亮红灯 Else imgDO(Index).Picture = imgGrey.Picture '灯灭掉 End If J = 0 For i = 0 To 15 '检查每个旗标数值,若为 True,表示应输出 High,将其相加 If fDO(i) Then J = J + 2 ^ i Next i '先将数值转为十六进制,再转为 Int,以避开可能的 Overflow TMC12_Do CInt("&H" & Hex(J)) End Sub 模块程序引用的是在 5-4-4 节所列的函数,请读者直接参考该 节的函数。 第 209 页,共 386 页 5-6 CON2 数字输入实验 TMC12 计时计数适配卡上的 CON2 用于侦测数字输入的讯号, 共有 16 个通道,本节讨论数字输入侦测的项目。 5-6-1 十六通道数字输入 透过之前讨论的缓存器,我们了解到侦测数字输入时,可以读 取2018香港马会开奖现场(基2018香港马会开奖现场+&H14)的缓存器,传回值就对应到十六个通道上的 状态,数字输入时所使用的函数可以是以下的二个函数之一 Declare Function TMC12_InputByte Lib "TMC12.dll" (ByVal address As Integer) As Integer Declare Function TMC12_InputWord Lib "TMC12.dll" (ByVal address As Integer) As Integer 在第四章讨论 PIO-D56 的数字输出入卡片时,我们使用了最基 本的函数(InputByte)达到控制输出的目的,在本章当然也可以透过 此函数将数值送到相关的缓存器;但在 TMC12 所提供的函数中还 有更精简的作法,它将上述的二个函数再作一次包装,并将2018香港马会开奖现场的 信息也隐含在其中,于是产生了以下的函数 Declare Function TMC12_Di Lib "TMC12.dll" (wDiData As Integer) As Integer TMC12_Di 函数就包含了原来的二个输入函数而达到状态侦测 的目的,由于更加地方便,本小节就使用此函数作范例。 在本小节也和第四章介绍数字输入一样的方式使用一块隔离 式数字输入延伸板 DB-16P,此延伸板已于 4-5-1 节介绍过,请各位 读者直接参考该节,笔者不再赘述。 将卡片安装完成后,使用排线将 TMC12 及 DB-16P 的延伸板连 接妥当,即可开始进行本部份的程序设计;笔者也要再次强调,一 定要依照函数的使用程序来建构程序,也就是驱动程序初始化->激 活卡片->执行输入侦测函数->关闭驱动程序->结束程序。 画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立数 字输入画面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 F4 叫出属性窗口,更改其 Caption 属性为” TMC12 Digital 第 210 页,共 386 页Input”,表示我们将进行数字侦测的工作。 2、 安排 2 个 Image 控件,目录下有二个代表灯号的图标,分别加 载 Iamge 的 Picture 属性中,一个命名为 imgGrey,另一个命名 为 imgRed,其 Visible 属性均设为 False;我们将用来改变画面 上的灯号状态。 3、 安排 16 个 Image 控件,Picture 属性先加载 imgGrey,此 16 个 将用来代表数字输出状态。 4、 安排 16 个 Label 控件,其 Caption 属性输入”DI0~DI15”,它们 将用来标示代表数字输入的灯号。 5、 安排一个定时器控件,Enabled 属性设为 False,Interval 属性设 为 100(亦即激活后,每 100 毫秒执行一次定时器内的程序代码), 我们将使用此控件来达到不断扫描输入的目的。 6、 依上述步骤所作出的画面如图 5-6-1。 图 5-6-1 数字输入的画面设计 功能设计:程序的程序必须参考到函数使用的顺序,接下来就依下列 步骤作出相对应的功能: 1、 在窗体的 Form_Load 事件程序程序中键入以下程序代码 Dim wTotalBoard As Integer '记录卡片的数目 Dim Ret As Integer '传回值宣告 Dim i As Integer '初始化卡片,并传回卡片数目 Ret = TMC12_DriverInit(wTotalBoard) If Ret <> TMC12_NoError Then '检查初始化是否成功 MsgBox "开启驱动程序失败!!!", vbCritical + vbOKOnly, "TMC12 Card Error" End '结束系统 End If bCardActive = False '将旗标设为 False 在窗体加载时就利用 TMC12_DriverInit 函数激活驱动程序所要使 第 211 页,共 386 页用到的计算机资源。 2、 在「激活卡片」的 Click 事件程序程序中键入以下程序代码 If Not bCardActive Then Command2.Caption = "停止侦测" '判断激活过程是否成功 If TMC12_ActiveBoard(0) <> TMC12_NoError Then MsgBox "激活卡片失败", vbCritical + vbOKOnly, "TMC12 DIO Control/Detect" bCardActive = False Exit Sub End If '将结束的按钮功能关闭 Command1.Enabled = False Timer1.Enabled = True '激活定时器 bCardActive = True Else Command2.Caption = "激活卡片" bCardActive = False Timer1.Enabled = False '关闭定时器 Command1.Enabled = True End If 我们只有在计算机中装设一片 TMC12 ,因此使用 TMC12_ActiveBoard(0)函数激活编号 0 的适配卡,在成功激活适 配卡后,也致能定时器,让定时器中的程序每隔 100 毫秒执行 一次。 3、 双击定时器控件,在其 Timer 事件程序中写入以下的程序代码 Dim i%, J% Dim wRtn As Integer wRtn = TMC12_Di(J) '取得数字输入的状态 '将状态反应到 Image 控件上 For i = 0 To 15 imgDI(i).Picture = IIf(J And 2 ^ i, imgRed.Picture, imgGray.Picture) DoEvents Next i 透过 TMC12_Di 读取数字输入的状态,接着以 AND 运算一一地 检查每一个通道上的状态,若是状态为 1,则将代表的灯号设 为红色;若否,则设为灯灭。 判断的程序使用的是 IIF,此函数的第一个参数是判断式,当 此判断式成立时,传回第二个参数;若判断式不成立,就传回 第三个参数;所以状态的判断只用一个函数即可解决。 4、 双击”结束程序”按钮,在其 Click 事件中写入以下程序代码 Timer1.Enabled = False '关闭定时器 TMC12_DriverClose End 第 212 页,共 386 页先关闭驱动程序,再结束系统程序的执行。 在这个项目进行实验时,我们将 DB-16P 和 TMC12 的 CON2 接 在一起,依序地按下「激活卡片」、「扫描输出」后就可以侦测到 延伸板上各个通道上的状态。实验结果如图 5-6-2 所示,此图所显 示的结果是 DI-0、 DI-1、 DI-6、 DI-15 呈现 High 的状态,表示这几 个通道有讯号进入,而其它的通道则无。 图 5-6-2 TMC12 数字输入的扫描结果 详细的项目程序代码请参考”EX\CH5\DigitalInput”,双击档案夹 中的 Project1.vbp 项目即可进行以上的实验。 详细的程序代码如下: Option Explicit Dim bCardActive As Boolean '卡片是否激活的旗标 '*********************************************** '结束按钮被按下时的程序 '首先关闭定时器,使其不再侦测 '接着会关闭驱动程序,以释放资源 '最后使用 End 结束程序 '*********************************************** Private Sub Command1_Click() Timer1.Enabled = False '关闭定时器 TMC12_DriverClose End End Sub '*********************************************** '激活按钮被按下时的程序 '程序判断是否需要以 ActiveBoard 函数激活卡片 '改变 bCardActive 旗标 '*********************************************** Private Sub Command2_Click() If Not bCardActive Then Command2.Caption = "停止侦测" '判断激活过程是否成功 If TMC12_ActiveBoard(0) <> TMC12_NoError Then 第 213 页,共 386 页 MsgBox "激活卡片失败", vbCritical + vbOKOnly, "TMC12 DIO Control/Detect" bCardActive = False Exit Sub End If '将结束的按钮功能关闭 Command1.Enabled = False Timer1.Enabled = True '激活定时器 bCardActive = True Else Command2.Caption = "激活卡片" bCardActive = False Timer1.Enabled = False '关闭定时器 Command1.Enabled = True End If End Sub '*********************************************** '窗体的 Form_Load 事件,系统激活时被执行 '先初始化驱动程序,若失败则结束系统 '*********************************************** Private Sub Form_Load() Dim wTotalBoard As Integer '记录卡片的数目 Dim Ret As Integer '传回值宣告 Dim i As Integer '初始化卡片,并传回卡片数目 Ret = TMC12_DriverInit(wTotalBoard) If Ret <> TMC12_NoError Then '检查初始化是否成功 MsgBox "开启驱动程序失败!!!", vbCritical + vbOKOnly, "TMC12 Card Error" End '结束系统 End If bCardActive = False '将旗标设为 False End Sub '*********************************************** '定时器事件,每 100 毫秒执行一次内部的程序代码 '程序将取得现在的数字输入状态,再显示到画面上 '*********************************************** Private Sub Timer1_Timer() Dim i%, J% Dim wRtn As Integer wRtn = TMC12_Di(J) '取得数字输入的状态 '将状态反应到 Image 控件上 For i = 0 To 15 imgDI(i).Picture = IIf(J And 2 ^ i, imgRed.Picture, imgGray.Picture) DoEvents Next i End Sub 模块程序引用的是在 5-4-4 节所列的函数,请读者直接参考该 节的函数。 第 214 页,共 386 页 5-7 CON1 计时计数实验—输出控制 前面已就 TMC12 适配卡上的数字输出和数字输入进行过范例 的讨论,除了函数的使用比较不同外,读者也可以发现和第四章的 数字输出入是非常类似的观念。TMC12 本身是专用于计时计数的功 能,也可以应用在各种的输出入情形中,本节将首先讨论到如何利 用 TMC12 上的计数器达到输出控制的目的,看看计数功能可以应 用在那些场合中。 计数器的接脚上有二个输入的引脚(CLK 及 Gate),有一个输出 的引脚(COUT),因此输出的部份一定和这支引脚有关系,这节我们 将从这支引脚讨论输出的控制部份,并延伸至进阶的步进马达控制 上。 5-7-1 脉冲的产生 所谓的脉冲就是在一段时间内平稳讯号突然有一个不一样准 位的讯号产生,而且这个讯号的存在时间很短,接着又回到一般的 讯号准位,理论上的脉冲图形如图 5-7-1 所示。 图 5-7-1 理论上的脉冲 第 215 页,共 386 页由于其为固定时间产生一个负向的讯号出来,对照一下 5-2-1 节可以了解到,当使用 TMC12 上的计数器欲达到输出脉冲讯号的 功能时,必须使用到模式 2 的设定;模式 2 的设定将在计数器计数 完成后,输出一个脉冲周期的讯号,接着又加载初始值,回到计数 的状态,因此可以产生不间断的脉冲讯号,此脉冲的讯号可以用来 作触发,很多的低准位连续触发的场合就可以使用脉冲输出达到目 的。 在程序设计上就要充分地了解到计数器里面的各个模式设定 及计数的方式,才能针对脉冲的产生作出设计,以下是一个产生脉 冲的项目设计。 画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立画 面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 F4 叫出属性窗口,更改其 Caption 属性为” TMC Pulse Generator”,表示我们将进行脉冲的输出。 2、 安排二个 Combo 控件,按下 F4 叫出其属性窗口,Name 属性 分别改为 cmbCounter 及 cmbFreq,并改变其 Font 属性至适当的 大小。 3、 安排二个 Label 控件,按下 F4 叫出其属性窗口,Caption 属性 分别改为”选择计数器”及 ”除频数值”,用以标示二个 Combo 控 件。 4、 安排二个按钮控件,其 Caption 属性分别输入「Pulse 输出」及 「结束系统」,用来作脉冲的输出及结束程序之用。 5、 依上述步骤所作出的画面如图 5-7-2。 图 5-7-2 产生脉冲的画面设计 第 216 页,共 386 页功能设计:程序的程序必须考虑模式的设定及计数值的指定,接下来 就依下列步骤作出相对应的功能: 1、 在窗体的 Form_Load 事件程序程序中键入以下程序代码 Dim wTotalBoard As Integer '记录卡片的数目 Dim Ret As Integer '传回值宣告 Dim i% '初始化卡片,并传回卡片数目 Ret = TMC12_DriverInit(wTotalBoard) If Ret <> TMC12_NoError Then '检查初始化是否成功 MsgBox "开启驱动程序失败!!!" & ErrStr(Ret), vbCritical + vbOKOnly, "TMC12 Card Error" End '结束系统 End If cmbCounter.Clear For i = 0 To 11 cmbCounter.AddItem "Counter->" & CInt(i + 1) Next i cmbCounter.ListIndex = 0 '显示第一个选项(计数器 0) For i = 0 To 15 cmbFreq.AddItem CStr(2 ^ i) Next i cmbFreq.ListIndex = 0 '显示第一个选项(除频为 1) 驱动程序的资源配置在这里进行,而且将 Counter 的号码及计 数值的显示部份也在这时填入 Combo 控件中。这二个 Combo 中的选项可用来让使用者选择所要输出的计数器及输入的计 数值。计数值的部份,我们使用了 2 的次方数给计数器。通常 2 的倍数是比较容易产生对称性结果的,因此笔者在使用计数 值时也经常使用 2 的倍数作为计数值。 2、 双击「Pulse 输出」的按钮控件,在其 Click 事件程序中写入以 下的程序代码 Dim ChipNo As Integer Dim CounterNo As Integer If TMC12_ActiveBoard(0) <> TMC12_NoError Then MsgBox "激活卡片失败", vbCritical + vbOKOnly, "TMC12 DIO Control/Detect" Exit Sub End If '决定 8254 芯片号码及使用的计数器号码 ChipNo = Int(cmbCounter.ListIndex / 3) CounterNo = cmbCounter.ListIndex Mod 3 TMC12_Select8254 ChipNo '选择 8254 芯片 '设定模式 2(Pulse 输出) TMC12_CounterSet CounterNo, &H34 + CounterNo * 2 ^ 6, CLng(cmbFreq.List(cmbFreq.ListIndex)) 就模式 2 的计数功能来说,只要指定的动作完成,一旦工作脉 冲进入到计数器后,计数器就会开始工作,而这段程序代码主 第 217 页,共 386 页要的就是作设定的工作。 欲使计数器开始工作,必须先致能适配卡,如程序中的 TMC12_ActiveBoard(0)就是激活 TMC12 适配卡,由于我们的计算 机中只有一片适配卡,在此函数中的参数当然就是给 0。 成功地激活适配卡后,还要设定所要使用的计数器,在 TMC12 上的计数器共有 12 组,平均分配给 4 个 8254 芯片,因此程序 中透过计算的方式决定出由 Combo 选择的计数器属于那一个 8254 芯片,并使用函数 TMC12_Select8254 选定。 最后一步就是将控制字组写入该计数器中,在此使用的函数是 TMC12_CounterSet,此函数的三个参数分别是计数器号码、控制 字组的数值、计数值。计数器的号码指的不是由 Combo 中所选 定的号码,而是所使用的计数器是所属 8254 芯片中的 0、1、2 三个计数器中的那一个计数器,这点容易弄错,必须特别小心。 控制字组的部份就是依据我们在 5-2-2 节中所讨论的方式,一 一地将各个位填入,再将整合起来的字节填入这个参数即可。 最后一个参数必须给定欲给计数器下数的计数值,当计数器数 完此值后就会产生输出。 3、 双击”结束程序”按钮,在其 Click 事件中写入以下程序代码 TMC12_DriverClose End 先关闭驱动程序,再结束系统程序的执行。 决定计数芯片及所在的计数器可以透过计算的方式取得,程序 所使用的是以下的二列 ChipNo = Int(cmbCounter.ListIndex / 3) CounterNo = cmbCounter.ListIndex Mod 3 至于控制字组的部份就必须检视每一个位,以取得正确的数 值;就此项目来说,我们分析的控制字组应该有如图 5-7-3 的内容。 图 5-7-3 控制字组的可能内容 第 218 页,共 386 页由图 5-7-3,除了最高的二个位可以因为选择的计数器不同而有 所改变外,其它的位均不会改变,因此可能的组成数值以二进制的 表示可能会有 00110100B、 01110100B、 10110100B 三种情形,而 以十六进制表示时,则会有&H34、 &H74、 &HB4;在程序中笔者 以 &H34 + CounterNo * 2 ^ 6 为之,当不同的计数器被选中时,自然 变换不同的控制字组数值。 当写入计数器中的数值被倒数到 0 时,在该计数器的 OUT 输出 引脚即会产生一个脉冲宽度的脉冲;为了解其输出的情形,建议读 者可以使用示波器或是频谱分析仪查看其输出的波形,藉以比较理 论与实际上的差异。 图 5-7-4 是使用 Counter3,使用的计数值是 4096,由于计数器 3 使用的是 1.6MHz 的脉冲作为工作脉冲,因此算完 4096 所需的时 间约为 4096/1600000=0.00256(秒 ),所以每隔 2.56 毫秒就会产生一 个脉冲,如此周而复始。 图 5-7-4 采用 Counter3 及 4096 的计数值 图 5-7-5 是所产生的脉冲图(X 轴未正规化),由图中我们可以看 出其图形与理论上是有差异的,不仅在负向上产生了一个脉冲,实 际上在另一个方向也会有一些电压的变化,并且在变化的前后都会 产生震荡的现象,这是实务上必然的结果,电子的表现无法达到理 论上的完美,只能以尽量逼进来达到要求,当我们应用到其它的场 合时也要注意到此点。 第 219 页,共 386 页 图 5-7-5 由 COUT 所量到的脉冲图形 详细的项目程序代码请参考”EX\CH5\Pulse”,双击档案夹中的 Project1.vbp 项目即可进行以上的实验。 详细的程序代码如下: Option Explicit '*********************************************** '结束按钮被按下时的程序 '接着会关闭驱动程序,以释放资源 '最后使用 End 结束程序 '*********************************************** Private Sub Command1_Click() TMC12_DriverClose End End Sub Private Sub Command2_Click() Dim ChipNo As Integer Dim CounterNo As Integer If TMC12_ActiveBoard(0) <> TMC12_NoError Then MsgBox "激活卡片失败", vbCritical + vbOKOnly, "TMC12 DIO Control/Detect" Exit Sub End If '决定 8254 芯片号码及使用的计数器号码 ChipNo = Int(cmbCounter.ListIndex / 3) CounterNo = cmbCounter.ListIndex Mod 3 TMC12_Select8254 ChipNo '选择 8254 芯片 '设定模式 2(Pulse 输出) TMC12_CounterSet CounterNo, &H34 + CounterNo * 2 ^ 6, CLng(cmbFreq.List(cmbFreq.ListIndex)) End Sub '*********************************************** 第 220 页,共 386 页'窗体的 Form_Load 事件,系统激活时被执行 '先初始化驱动程序,若失败则结束系统 '*********************************************** Private Sub Form_Load() Dim wTotalBoard As Integer '记录卡片的数目 Dim Ret As Integer '传回值宣告 Dim i% '初始化卡片,并传回卡片数目 Ret = TMC12_DriverInit(wTotalBoard) If Ret <> TMC12_NoError Then '检查初始化是否成功 MsgBox "开启驱动程序失败!!!" & ErrStr(Ret), vbCritical + vbOKOnly, "TMC12 Card Error" End '结束系统 End If cmbCounter.Clear For i = 0 To 11 cmbCounter.AddItem "Counter->" & CInt(i + 1) Next i cmbCounter.ListIndex = 0 '显示第一个选项(计数器 0) For i = 0 To 15 cmbFreq.AddItem CStr(2 ^ i) Next i cmbFreq.ListIndex = 0 '显示第一个选项(除频为 1) End Sub Function ErrStr(ErrNo%) Select Case ErrNo Case 1 ErrStr = "开启驱动程序失败!" Case 2 ErrStr = "驱动程序未开启!" Case 3 ErrStr = "驱动程序版本错误!" Case 4 ErrStr = "安装中断时发生错误!" Case 5 ErrStr = "清除中断计数错误!!" Case 6 ErrStr = "取得中断计数错误!" Case 7 ErrStr = "缓存器错误!" Case 8 ErrStr = "移除中断错误!" Case 9 ErrStr = "搜寻卡片错误!" Case 10 ErrStr = "卡片号码超过!" Case 11 ErrStr = "重置错误!" End Select End Function 模块程序引用的是在 5-4-4 节所列的函数,请读者直接参考该 节的函数。 第 221 页,共 386 页5-7-2 方波的产生 方波的样式最常见的情形是一段时间内其准位为高电位,在下 一段相同的时间内,其准位为低电位,而且这个现象一直交互出现, 理论上的方波图形如图 5-7-6 所示(Duty=50%)。 图 5-7-6 理论上的方波(Duty=50) 由于其为计数过程中产生正负相反的 OUT 讯号出来,对照一下 5-2-1 节可以了解到,当使用 TMC12 上的计数器欲达到输出方波讯 号的功能时,必须使用到模式 3 的设定;模式 3 的设定会将计数器 的输出作成方波的型式,并且重复计数及输出。当使用的计数值是 2 的倍数时,所产生的就是一个对称式的方波,其高低准位的时间 长度是一样的,若给的是不是 2 的倍数的计数值,则输出的方波不 为对称。 画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立画 面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 F4 叫出属性窗口,更改其 Caption 属性为” TMC Square Wave Generator”,表示我们将进行方波的输出。 2、 安排二个 Combo 控件,按下 F4 叫出其属性窗口,Name 属性 分别改为 cmbCounter 及 cmbFreq,并改变其 Font 属性至适当的 大小。 3、 安排二个 Label 控件,按下 F4 叫出其属性窗口,Caption 属性 分别改为”选择计数器”及 ”除频数值”,用以标示二个 Combo 控 件。 第 222 页,共 386 页4、 安排二个按钮控件,其 Caption 属性分别输入「方波输出」及 「结束系统」,用来作脉冲的输出及结束程序之用。 5、 依上述步骤所作出的画面如图 5-7-7。 图 5-7-7 产生方波的画面设计 功能设计:程序的程序必须考虑模式的设定及计数值的指定,接下来 就依下列步骤作出相对应的功能: 1、 在窗体的 Form_Load 事件程序程序中键入以下程序代码 Dim wTotalBoard As Integer '记录卡片的数目 Dim Ret As Integer '传回值宣告 Dim i% '初始化卡片,并传回卡片数目 Ret = TMC12_DriverInit(wTotalBoard) If Ret <> TMC12_NoError Then '检查初始化是否成功 MsgBox "开启驱动程序失败!!!" & ErrStr(Ret), vbCritical + vbOKOnly, "TMC12 Card Error" End '结束系统 End If cmbCounter.Clear For i = 0 To 11 cmbCounter.AddItem "Counter->" & CInt(i + 1) Next i cmbCounter.ListIndex = 0 '显示第一个选项(计数器 0) For i = 0 To 9 cmbFreq.AddItem CStr(2 ^ i) Next i cmbFreq.ListIndex = 0 '显示第一个选项(除频为 1) 驱动程序的资源配置在这里进行,而且将 Counter 的号码及计 数值的显示部份也在这时填入 Combo 控件中。这二个 Combo 中的选项可用来让使用者选择所要输出的计数器及输入的计 数值。计数值的部份,我们使用了 2 的次方数给计数器。通常 2 的倍数是比较容易产生对称性结果的,因此笔者在使用计数 第 223 页,共 386 页值时也经常使用 2 的倍数作为计数值。 2、 双击「方波输出」的按钮控件,在其 Click 事件程序中写入以下 的程序代码 Dim ChipNo As Integer Dim CounterNo As Integer If TMC12_ActiveBoard(0) <> TMC12_NoError Then MsgBox "激活卡片失败", vbCritical + vbOKOnly, "TMC12 DIO Control/Detect" Exit Sub End If '决定 8254 芯片号码及使用的计数器号码 ChipNo = Int(cmbCounter.ListIndex / 3) CounterNo = cmbCounter.ListIndex Mod 3 TMC12_Select8254 ChipNo '选择 8254 芯片 '设定模式 3(方波输出) TMC12_CounterSet CounterNo, &H36 + CounterNo * 2 ^ 6, CLng(cmbFreq.List(cmbFreq.ListIndex)) 就模式 3 的计数功能来说,只要指定的动作完成,一旦工作脉 冲进入到计数器后,计数器就会开始工作,而这段程序代码主 要的就是作设定的工作。其它的说明如同上一小节,请读者参 阅前一小节,基本的作法就是先致能,再送出设定字组。 3、 双击”结束程序”按钮,在其 Click 事件中写入以下程序代码 TMC12_DriverClose End 先关闭驱动程序,再结束系统程序的执行。 决定计数芯片及所在的计数器可以透过计算的方式取得,这部 份已于上一小节说明过了。 至于控制字组的部份就必须检视每一个位,以取得正确的数 值;就此项目来说,我们分析的控制字组应该有如图 5-7-8 的内容。 图 5-7-8 控制字组的可能内容 由图 5-7-8,除了最高的二个位可以因为选择的计数器不同而有 所改变外,其它的位均不会改变,因此可能的组成数值以二进制的 第 224 页,共 386 页表示可能会有 00110110B、 01110110B、 10110110B 三种情形,而 以十六进制表示时,则会有&H36、 &H76、 &HB6;在程序中笔者 以 &H36 + CounterNo * 2 ^ 6 为之,当不同的计数器被选中时,自然 变换不同的控制字组数值,这和上一小节的原理是一样的。 使用模式 3 作输出时,计数器以间隔 2 的方式作计数,一旦计 数完成,COUT 输出的电位会高低变化,计数再加载初值,又是以 间隔 2 的方式向下计数,如此周而复始,于是产生了周期性的方波 出来。 图 5-7-9 是使用 Counter8,使用的计数值是 512,由于计数器 8 使用的是 80KHz 的脉冲作为工作脉冲,因此产生的频率是 160Hz 左右。 图 5-7-9 采用 Counter8 及 512 的计数值 图 5-7-10 是所产生的方波图形,由图中我们可以看出其图形与 理论上是有差异的,在方波的前后都会产生震荡的现象,这是实务 上必然的结果,电子的表现无法达到理论上的完美,只能以尽量逼 进来达到要求,当我们应用到其它的场合时也要注意到此点。 第 225 页,共 386 页 图 5-7-10 由 COUT 所量到的方波图形 而相对应的频率图形则如图 5-7-11 所示,其主要频率为 160Hz。 图 5-7-11 将讯号转为频率时的表现 详细的项目程序代码请参考”EX\CH5\SquareWave”,双击档案夹 中的 Project1.vbp 项目即可进行以上的实验。 详细的程序代码如下: Option Explicit '*********************************************** '结束按钮被按下时的程序 '接着会关闭驱动程序,以释放资源 '最后使用 End 结束程序 '*********************************************** Private Sub Command1_Click() TMC12_DriverClose End End Sub 第 226 页,共 386 页Private Sub Command2_Click() Dim ChipNo As Integer Dim CounterNo As Integer If TMC12_ActiveBoard(0) <> TMC12_NoError Then MsgBox "激活卡片失败", vbCritical + vbOKOnly, "TMC12 DIO Control/Detect" Exit Sub End If '决定 8254 芯片号码及使用的计数器号码 ChipNo = Int(cmbCounter.ListIndex / 3) CounterNo = cmbCounter.ListIndex Mod 3 TMC12_Select8254 ChipNo '选择 8254 芯片 '设定模式 3(方波输出) TMC12_CounterSet CounterNo, &H36 + CounterNo * 2 ^ 6, CLng(cmbFreq.List(cmbFreq.ListIndex)) End Sub '*********************************************** '窗体的 Form_Load 事件,系统激活时被执行 '先初始化驱动程序,若失败则结束系统 '*********************************************** Private Sub Form_Load() Dim wTotalBoard As Integer '记录卡片的数目 Dim Ret As Integer '传回值宣告 Dim i% '初始化卡片,并传回卡片数目 Ret = TMC12_DriverInit(wTotalBoard) If Ret <> TMC12_NoError Then '检查初始化是否成功 MsgBox "开启驱动程序失败!!!" & ErrStr(Ret), vbCritical + vbOKOnly, "TMC12 Card Error" End '结束系统 End If cmbCounter.Clear For i = 0 To 11 cmbCounter.AddItem "Counter->" & CInt(i + 1) Next i cmbCounter.ListIndex = 0 '显示第一个选项(计数器 0) For i = 0 To 9 cmbFreq.AddItem CStr(2 ^ i) Next i cmbFreq.ListIndex = 0 '显示第一个选项(除频为 1) End Sub Function ErrStr(ErrNo%) Select Case ErrNo Case 1 ErrStr = "开启驱动程序失败!" Case 2 ErrStr = "驱动程序未开启!" Case 3 ErrStr = "驱动程序版本错误!" Case 4 ErrStr = "安装中断时发生错误!" Case 5 ErrStr = "清除中断计数错误!!" Case 6 ErrStr = "取得中断计数错误!" Case 7 第 227 页,共 386 页 ErrStr = "缓存器错误!" Case 8 ErrStr = "移除中断错误!" Case 9 ErrStr = "搜寻卡片错误!" Case 10 ErrStr = "卡片号码超过!" Case 11 ErrStr = "重置错误!" End Select End Function 模块程序引用的是在 5-4-4 节所列的函数,请读者直接参考该 节的函数。 5-7-3 步进马达进阶控制 第四章 4-4-3 小节曾经对于步进马达到作过介绍,也说明了步 进马达的相关控制原理;本小节将从另一个方向来讨论步进马达, 尝试由专属的控制器来进行步进马达的控制。 除了可以使用 4-4-3 小节的方式控制步进马达外,工业用步进 马达的制造厂商通常也为其步进马达备有专属的驱动器,只要将讯 号送给驱动器,该驱动器即依需求控制步进马达的动作,图 5-7-12 是笔者将示范的 2 相步进马达驱动器外观;图 5-7-13 则是其控制接 点处的标示。 图 5-7-12 2 相步进马达驱动器 第 228 页,共 386 页 图 5-7-13 控制器接点部份的放大图 使用专属的步进马达驱动器时,其接线的方式如图 5-7-14 所 示,其中包括了步进马达在驱动器上的接线方式,以及 TMC12 的 接点接至驱动器上的标示,笔者将以此为例,说明在 Visual Basic 的程序上如何连接 TMC12 及步进马达驱动器。 图 5-7-14 驱动器、TMC12 与马达之接线图 由图 5-7-14,Pulse 处是讯号接入的地方,驱动器藉由此信道进 入的脉冲数(笔者将送入方波)来决定控制步进马达的旋转角度,而 且送入的方波的快慢决定了步进马达的转动速度。CW/CCW 意义是 正转/逆转,透过此引脚的电位高低,我们可以控制步进马达的转动 方向(实际是改变送给步进马达脉冲的顺序达到方向改变的功能, 如第四章讨论的步进马达原理所述)。MOTOR 接的是欲控制的步进 马达,接线上通常是依线材的颜色来区分;接入的电源是 110V, 而 FG 是接地线。 搭配上述的驱动器,笔者选用的步进马达外观如图 5-7-15 所 示,其为步进角 1.8 度的 2 相 步进马达;除了由 TMC12 上的计数 器送出控制步进马达转动的讯号外,也可以再加上转动方向的控制 (CW/CCW)。 第 229 页,共 386 页 图 5-7-15 2 相步进马达外观 画面设计: 我们要建立一个控制步进马达驱动器的项目,依以下的 建立步骤建立画面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 F4 叫出属性窗口,更改其 Caption 属性为”Step Motor Control”, 表示我们将进行步进马达的控制。 2、 安排一个文字框控件,其 Name 属性输入 txtSignalNo, Text 属 性先输入 1;此控件将用来输入所欲输出至步进马达控制器的 方波周期数。 3、 安排一个 CheckBox 控件,其 Caption 属性输入「正转」,这个 控件的 Value 属性将用来决定步进马达的转动方向是正转或逆 转;若使用者勾选,则为正转,若末勾选,则为逆转。 4、 安排一个 Combo 控件,其 Name 属性输入 cmbFreq,用来决定 输出给步进马达驱动器的方波频率。 5、 安排三个 Label 控件,其 Caption 分别输入「输出周期数」、「转 速」及「已送周期???」,它们分别用来标示文字框、Combo 控 件及显示传送给步进马达的方波数。 6、 依上述步骤所作出的画面如图 5-7-16。 第 230 页,共 386 页 图 5-7-16 步进马达控制的画面设计 功能设计:接下来就依下列步骤作出相对应的功能: 1、 在窗体的 Form_Load 事件程序程序中键入以下程序代码 Dim wTotalBoards As Integer Dim i As Integer If TMC12_DriverInit(wTotalBoards) <> TMC12_NoError Then MsgBox "驱动程序错误!", vbCritical + vbOKOnly, "错误" End If For i = 0 To 8 cmbFreq.AddItem CStr(2 ^ i) Next i cmbFreq.ListIndex = 0 '显示第一个选项(除频为 1) 最重要的就是先将驱动程序的资源加载,程序中并预先写入一 些计数器的计数值选择项目到 Combo 控件中。 2、 双击「输出」的按钮控件,在其 Click 事件程序中写入以下的程 序代码 Dim i% '激活卡片 If TMC12_ActiveBoard(0) <> TMC12_NoError Then MsgBox "TMC12 适配卡无法激活!", vbCritical + vbOKOnly, " 激活错误" Exit Sub End If For i = 1 To Int(txtSignalNo.Text) lblSend.Caption = "已送周期:" & CStr(i) & "个 !" OneSignal '传送一个方波到驱动器 Next i 此程序将会激活卡片,并且依输入的方波数传送给驱动器。传 送方波的程序是由 OneSignal 子程序处理,该子程序的内容如 下 Sub OneSignal() Dim wCount As Long 第 231 页,共 386 页 Dim PreCount As Long Dim mt As Single TMC12_Select8254 2 ' 选择第 2 颗 8254,逻辑上是 Counter7~9 ' Counter-1, mode=0, down count ffff TMC12_CounterSet 1, &H70, CLng(&HFFFF& / CLng(cmbFreq.List(cmbFreq.ListIndex))) '开始计数后,数值会变化 DoEvents '可以用 Cout 状态变化作判断 Do If ((TMC12_CounterRead(1, &HE4) And &H80) <> 0) Then Exit Do End If DoEvents Loop '此时计数完成,以下是时间延迟部份 ' Counter-2, mode=0, down count ffff TMC12_CounterSet 2, &HB0, CLng(&HFFFF& / CLng(cmbFreq.List(cmbFreq.ListIndex))) Do If ((TMC12_CounterRead(1, &HE8) And &H80) <> 0) Then Exit Do End If DoEvents Loop End Sub 笔者使用的是 TMC12 上的第 8 和第 9 组 Counter,所以使用了 第 2 颗的 8254,其它的详细说明,笔者稍后再述。 3、 双击 Combo 控件,在其 Click 事件程序中写入以下的程序代码 If Check1.Value = 1 Then TMC12_Do 1 '提至高电位 Else TMC12_Do 0 '降至低电位 End If 这段程序的目的在于控制驱动器上的 CW/CCW 接点电位,当 这个接点的电位不同时,会使得步进马达的转动方向发生变 化。 4、 双击”结束系统”按钮,在其 Click 事件中写入以下程序代码 '关闭驱动程序 TMC12_DriverClose End 先关闭驱动程序,再结束系统程序的执行。 在这个项目进行实验时,我们必须先接好线路。既然需传送可 控制个数及宽度的方波给驱动器,笔者决定使用模式 0 来产生输出 的讯号。由之前的讨论可知,模式 0 是在计数开始后,于 COUT 引 脚输出低电位,且一直维持在低电位,直到计数完成后才将 COUT 第 232 页,共 386 页引脚提到高电位,笔者将利用这个特性输出这个负向的方波,输出 的讯号采用的是 TMC12 上的 Counter8,而为造成一个完整的周期, 笔者特别又使用了 Counter9 作计数,同样是采用相同的除频值,一 样是使用模式 0,因 此 Counter9 计数完成所需的时间就和 Counter8 一样,这样就形成一个完整的周期了。完整的周期也只是送出一个 讯号而已,步进马达也只有前进或后退一个步进角,画面上我们允 许使用者输入欲传送的方波数,这时只要连续地呼叫 OneSignal 函 数,就可以持续地送方波给驱动器,也就可以随意控制其前进的角 度了,再加上正逆转的控制讯号,我们当然就可以随意控制步进马 达在任意的位置了。 此范例中使用的 TMC12_CounterRead 函数是用来读取计数器状 态的,另一方面,此函数也可以用来读取现在的计数值,但在其第 二个参数值里,程序中分别给了&HE4 及 &HE8,此字节是计数值或 状态的读回指令,格式如图 5-7-17。 图 5-7-17 读回指令字节解析 因此由图 5-7-17 可以解析出二种可能的组合数值,以二进制来 排列的话,一为 11100100B,另一则是 11101000B,将其表示为十 六进制时就是&HE4 和 &HE8 二个数值,而此指令的传回值格式则 如表 5-2-7 所示,笔者再列如下: D7 D6 D5 D4 D3 D2 D1 D0 Out Null Count RW1 RW0 M2 M1 M0 BCD 第 7 位就是我们所需要确定的状态,如果 COUT 引脚是高电位 的话,此位为 1,反之则为 0;再回想一下模式 0 的操作方式--当其 开始计数后,输出为低电位,当计数完成时,输出再度升为高电位。 程序就是透过这种方式确定每一次的计数动作是否完成,这样也可 以保证在 COUT 输出引脚的状态一定会维持一定的时间。 第 233 页,共 386 页接着是方向控制的引脚,在方向控制上需要一个讯号将 CW/CCW 的准位作变换,考虑 TMC12 适配卡的话,无疑地,我们 可以采用输出埠(CON3)上的接点来作,笔者采用的是 DO-0 的引脚, 也因此在 CheckBox 的程序片断中才会看到 TMC12_Do 1 的程序代 码,因为此程序就是将 DO-0 这支引脚提到高电位,将 1 改为 0 时, 就是将 DO-0 这支引脚降为低电位,如此就达到了控制步进马达方 向的目的。 实验结果如图 5-7-18 所示。 图 5-7-18 步进马达控制过程 图 5-7-18 是控制过程的图形,图中显示了我们将传送 30 个方 波给驱动器,而且方向是正转,当时已传送 12 个,还有 18 个方波 未传送,如果最后的结果显示出传送 30 个方波的话,就可以确定 驱动器也应该收到 30 个方波。30 个方波应该使得步进马达转几度 呢 ?30*1.8=54(度 )。当然了,如果我们希望它转一圈的话,就得输 入 200 个方波,以便转动 200*1.8=360(度 )。 详细的项目程序代码请参考”EX\CH5\StepMotor”,双击档案夹 中的 Project1.vbp 项目即可进行以上的实验。 详细的程序代码如下: Option Explicit '******************************************************** '方向控制的按钮 '利用第 1 支 DO 的引脚达到控制方向的目的 '******************************************************** Private Sub Check1_Click() If Check1.Value = 1 Then TMC12_Do 1 '提至高电位 Else TMC12_Do 0 '降至低电位 End If End Sub 第 234 页,共 386 页'******************************************************** '开始输出讯号的按钮 '此程序先激活卡片,接着再将讯号作输出 '******************************************************** Private Sub Command1_Click() Dim i% '激活卡片 If TMC12_ActiveBoard(0) <> TMC12_NoError Then MsgBox "TMC12 适配卡无法激活!", vbCritical + vbOKOnly, " 激活错误" Exit Sub End If '一边送讯号,一边显示已传送的周期数 For i = 1 To Int(txtSignalNo.Text) lblSend.Caption = "已送周期:" & CStr(i) & "个 !" OneSignal Next i End Sub '******************************************************** '结束程序的按钮 '关闭驱动程序后,使用 End 指令结束程序 '******************************************************** Private Sub Command2_Click() TMC12_DriverClose End End Sub '******************************************************** '窗体的加载事件(Form_load) '程序中将驱动程序初始化 '******************************************************** Private Sub Form_Load() Dim wTotalBoards As Integer Dim i As Integer If TMC12_DriverInit(wTotalBoards) <> TMC12_NoError Then MsgBox "驱动程序错误!", vbCritical + vbOKOnly, "错误" End If For i = 0 To 8 cmbFreq.AddItem CStr(2 ^ i) Next i cmbFreq.ListIndex = 0 '显示第一个选项(除频为 1) End Sub '******************************************************** '周期讯号输出子程序 '利用模式 0,造出周期讯号 '******************************************************** Sub OneSignal() Dim wCount As Long Dim PreCount As Long Dim mt As Single TMC12_Select8254 2 ' 选择第 2 颗 8254,逻辑 上是 Counter7~9 ' Counter-1, mode=0, down count ffff 第 235 页,共 386 页 TMC12_CounterSet 1, &H70, CLng(&HFFFF& / CLng(cmbFreq.List(cmbFreq.ListIndex))) '开始计数后,数值会变化 DoEvents '可以用 Cout 状态变化作判断 Do If ((TMC12_CounterRead(1, &HE4) And &H80) <> 0) Then Exit Do End If DoEvents Loop '此时计数完成,以下是时间延迟部份 ' Counter-2, mode=0, down count ffff TMC12_CounterSet 2, &HB0, CLng(&HFFFF& / CLng(cmbFreq.List(cmbFreq.ListIndex))) Do If ((TMC12_CounterRead(1, &HE8) And &H80) <> 0) Then Exit Do End If DoEvents Loop End Sub 模块程序引用的是在 5-4-4 节所列的函数,请读者直接参考该 节的函数。 第 236 页,共 386 页 5-8 CON1 计时计数实验—输入侦测 上一节讨论了输出控制的部份,也看了几个例子,这些输出的 例子里都是使用 COUT 引脚上的讯号达到讯号输出的目的。本节将 透过 TMC12 透过的计数器的功能,作输入的侦测或计算,而输入 的侦测方面就得使用到计数器上的二个输入引脚--CLK 引脚或是 Gate 引脚了,我们也来看看几个例子。 5-8-1 事件次数的计算(Event Counter) 所谓的事件就是一个突发事件,这个突发事件的发生在电气讯 号上可以是一个方波或是一个脉冲;通常机械上的事件发生所产生 的讯号不像电子的高频讯号,所以透过 TMC12 来看的话,几乎都 可以将它们看成是方波的讯号(由示波器看这些突然的事件讯号也 的确是如此)。 最常见到的事件产生装置是使用近接开关,如图 5-8-1 所示。 这类的近接开关可以在一般的电子材料行买到。 图 5-8-1 近接开关 图 5-8-1 中,平常 P 点未押按时,A、 C 二点是接通,而 B、 C 二点断路;当 P 点被押按时,A、 C 二点断路,而 B、 C 二点接通。 因此透过这样的机制,我们可以设计将 B、 C 二点或 A、 C 二点接 成一个回路,透过押按 P 点达到控制回路的导通与断开,进而造成 低电位和高电位状态,这样一高一低的状态形成后,就有如一个连 续的周期讯号,而且是一个方波讯号,这样的讯号就可以输入到 CLK 或 Gate 引脚。一旦有了讯号的输入,我们就可以再考虑 8254 的几种模式,看看那一种模式比较容易处理这样的情形。以下是二 种输入管道的讨论: 第 237 页,共 386 页1、 状态接至 CLK 引脚:状态会被当成是计数的基准。若 Gate 维 持在高电位,那么每一次的讯号进入后,计数器就可以下数一 次,一直到倒数到 0。 2、 状态接至 Gate 引脚:状态会被当成是计数的开关。状态是高电 位时,计数器的计数动作会持续进行;反之,当状态改变为低 电位时,计数器的计数动作就会停止;至于,开始计数到停止 计数的这段时间内,到底已经算了几次了,就依计数器所使用 的脉冲而定。 由上述的二种情形,若将状态接到 CLK 引脚,很显然的,我们 可以预期只要近接开关一次的动作,就可以导致计数器的计数值减 1;只要不计数到 0,它一定可以正常地显示出所经历的周期。反观 第二种情形,虽然也可以透过读取计数值的方式取得在一个周期产 生时的经历计数值,但是要将这个计数值回馈成真正的事件值却也 不方便,因为无法直接透过数值读取或计算就取得事件值。综合而 言,很显然的,我们必须采用的方式是将状态接到 CLK 引脚的方 式来作计数,我们只要不断去读取计数值,就可以知道有几个周期 的讯号进入到计数器了,当然事件值也就知道了。 实验准备进行,考虑计数器的使用时,笔者选择了使用第 1 组 计数器,在之前的设定中,笔者已经透过 Jumper 的设定,将第 1 组计数器的 CLOCK 来源设为 External,意即,第 1 组计数器将不 使用 TMC12 适配卡所提供的 CLK1 或 CLK2,我们必须另外接脉冲 的讯号给这组计数器使用。在这里已经很明显的,我们必须将事件 产生的状态线路接到第 1 组计数器的 CLK 引脚,当成是计数的基 准,此部份的接线可如图 5-8-2 所示,TMC12 的 EXTG(也就是 Gate 引脚)通常是高准位,因此可以不接,而 COUT 引脚没有使用,亦 不必接。 图 5-8-2 近接开关和计数器的接法 第 238 页,共 386 页接下来就要将以上的讨论实际作出一个事件计数的程序了。首 先是画面的设计部份。 画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立画 面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 F4 叫出属性窗口,更改其 Caption 属性为”16 Bits Event Counter”,表示我们将进行数字输入的侦测。 2、 安排二个 Label 控件,二者的 Caption 属性,一个写入「事件值」, 一个写入「???」;问号的 Label 控件将用来显示所侦测到的计 数值。 3、 安排一个定时器控件,Interval 属性设为 100,表示每 100 毫秒 执行一次内部的程序代码;Enabled 属性设为 False,暂时不予 激活,等到初始化卡片后再允许激活。 4、 安排二个按钮控件,其 Caption 属性分别输入「开始计数」及 「结束系统」,用来开始侦测现在的事件,以及结束程序之用。 5、 依上述步骤所作出的画面如图 5-8-3。 图 5-8-3 事件侦测的画面设计 功能设计:接下来就依下列步骤作出相对应的功能: 1、 在窗体的 Form_Load 事件程序程序中键入以下程序代码 Dim wTotalBoards As Integer If TMC12_DriverInit(wTotalBoards) <> TMC12_NoError Then MsgBox "驱动程序开启错误!", vbCritical + vbOKOnly, "驱动 程序错误" End If 首先是激活驱动程序,以便我们在程序中可以成功地控制适配 卡。 2、 在窗体的「开始计数」按钮的 Click 事件程序程序中键入以下 程序代码 第 239 页,共 386 页 '激活卡片 If TMC12_ActiveBoard(0) <> TMC12_NoError Then MsgBox "无法激活卡片!", vbCritical + vbOKOnly, "激活卡片 错误" Exit Sub End If Timer1.Enabled = True TMC12_Select8254 0 ' 选择第 0 个 8254 芯片 bReCount = True 如果可以成功激活适配卡,就选择第 0 颗 8254 芯片(因此我们 要使用 TMC12 上的第 1 组计数器,故选择此颗 8254),我们将 使用此芯片中的 Counter0 作计数。 3、 双击定时器控件,在其 Timer 事件程序中写入以下的程序代码 Dim wCount As Long '以下写入控制字组 ' 使用第 0 个 Counter,采用 Mode0,初值为 FFFF If bReCount Then '若重新计数,则写入新值 TMC12_CounterSet 0, &H30, &HFFFF bReCount = False End If wCount = TMC12_CounterRead(0, 0) wCount = 65535 - wCount Label1.Caption = CStr(wCount) 如果是重新计数的话,使用 TMC12_CounterSet 函数写入计数 值到芯片中,三个参数中,第 1 个指出计数器编号(相对于该颗 8254),第 2 个是控制字组,经排列应为&H30,而计数初始值 则给定&HFFFF,让计数最高可以算 65535。 计数值的读取使用 TMC12_CounterRead 函数,所传回的值是现 在计数器中的计数值,由于计数器是下数的方式,所以我们还 要使用 65535 去减去现在的计数值才是计数器已经下数的次 数,也才是真正的事件值。 4、 双击”结束程序”按钮,在其 Click 事件中写入以下程序代码 '关闭驱动程序 TMC12_DriverClose '结束程序 End 先关闭驱动程序,再结束系统程序的执行。 实验的进行可以依图 5-8-2 的作法,读者也可以依照实际的情 形接出相同的接线来作实验。实验结果如图 5-8-4 所示,图中显示 开始计数后已经算到 62 了,如果我们再按一下「开始计数」的按 钮时,计数的动作会重新再来一次,计数值自然也就重 0 开始了。 第 240 页,共 386 页为什么这里是 16Bit 呢 ?就如上述的,我们最高给的倒数计数值 是 &HFFFF(十六位),如果倒数完成后,又是开始由&HFFFF 往下计 数,因此只使用一颗计数器的话,最高的计数能力只能达到十六位 (6535);若是计数的长度比较长的话,可以使用串接的方式来达到; 所谓的串接,举例来说,我们要将第 1 组和第 2 组计数串接;当第 1 组计数器满了后,COUT 引脚发生变化,这个变化可以接到第 2 组计数器的 CLK,如果它的计数就只有一次而已,接下来就是第 1 组计数器回到初始计数值,又再一次开始计数;一旦计数完成, COUT 讯号又会送一次到第 2 组计数器,这时的计数器 2 的计数又 可以向下减 1,如此的串接方式,只要检视计数器 2 的累计量和现 行计数器 1 的读值,就可以完成计数器的串接工作了。 在使用二组计数器串接的情形下,数值的计算可以达到 32 位 的长度,计数值的读取可以如本范例一般进行,但在数值的组合上 必须使用移位的方式达到;例如,第一组计数器是 32 位计数器中 的低十六位,而第二组计数器是 32 位计数器中的高十六位,当分 别读取二组计数器的计数值后(假设二组数值分别是 T1、T2),第 二 组计数器中的计数值必须左移十六位,以便形成 32 位中的高十六 位,完整的计算值的算法应该是 Total=T2*2^16+T1 Total 就是完整的数值了,以 Visual Basic 的运算来说,笔者通常将 Total 宣告为 Double(储存空间是 8 个字节),以便容纳所有的长整 数数值。 图 5-8-4 事件值的侦测结果 详细的项目程序代码请参考”EX\CH5\ Counter16bit”,双击档案 夹中的 Project1.vbp 项目即可进行以上的实验。 详细的程序代码如下: Option Explicit 第 241 页,共 386 页Dim bReCount As Boolean '重新计数的旗标 '********************************************************** '开始计数按钮的事件 '首先激活卡片,接下来是选择芯片及计数器 '此范例采用 TMC12 上的第一个 Counter,故对应到第 0 颗 8254 的第 0 个 '计数器 '******************************************************** Private Sub Command1_Click() '激活卡片 If TMC12_ActiveBoard(0) <> TMC12_NoError Then MsgBox "无法激活卡片!", vbCritical + vbOKOnly, "激活卡片 错误" Exit Sub End If Timer1.Enabled = True TMC12_Select8254 0 ' 选择第 0 个 8254 芯片 bReCount = True End Sub '********************************************************* '结束程序按钮的事件 '关闭驱动程序,以释放资源 '使用 End 指令结束程序 '******************************************************* Private Sub Command2_Click() TMC12_DriverClose End End Sub '******************************************************** '窗体的加载事件(Form_Load) '在此对适配卡以 TMC12_DriverInit 作初始化的动作 '******************************************************** Private Sub Form_Load() Dim wTotalBoards As Integer If TMC12_DriverInit(wTotalBoards) <> TMC12_NoError Then MsgBox "驱动程序开启错误!", vbCritical + vbOKOnly, "驱动 程序错误" End If End Sub '**************************************************** '定时器 Timer 事件 '每隔一段时间即自动执行此内部的程序代码 '使用 TMC12_CounterRead 读取现在计数器内的读值 '再将读值反推为输入值后显示在 Label 控件上 '**************************************************** Private Sub Timer1_Timer() Dim wCount As Long '以下写入控制字组 ' 使用第 0 个 Counter,采用 Mode0,初值为 FFFF If bReCount Then '若重新计数,则写入新值 TMC12_CounterSet 0, &H30, &HFFFF bReCount = False End If 第 242 页,共 386 页 wCount = TMC12_CounterRead(0, 0) wCount = 65535 - wCount Label1.Caption = CStr(wCount) End Sub 模块程序引用的是在 5-4-4 节所列的函数,请读者直接参考该 节的函数。 5-8-2 频率计算 频率是周期的倒数,二者所表示的情形是一样的,但是二者应 用的场合不见得一样。我们在输出的讨论中也曾经看到了方波的产 生,每一秒钟可以产生的方波数就是频率,在部份应用的场合中也 需要侦测某些讯号产生的频率,这也可以使用 TMC12 上的 8254 芯 片达到侦测的目的。 为了实验的方便,笔者使用波形产生器(Function Generator)输 出方波讯号,并利用 TMC12 计算此方波的频率;当波形产生器上 的频率数值改变时,画面上显示的数值也可以随即改变。现在我们 再来探讨如何将这个讯号接入计数器的接脚: 1、 由于是输入的讯号,因此势必接入 CLK 引脚或 Gate 引脚; 若接入 CLK 引脚,则此讯号将被当成是计数的基准脉冲, 输入的脉冲数就是计数值的下数值。若是接到 Gate 引脚, 则计数动作的进行与否就决定于这个讯号是否处于高电位。 2、 在计数器上要得到频率的计数必须含有二项基本条件,一是 已知的时间间隔,二是在这段时间间隔内的计数值(欲被计 算的输入讯号方波数)必须知道。 3、 考虑上述的第一个条件,已知的时间间隔可以利用模式 0 来 达成,因为只要选用内部的脉冲,那么当计数器计数完毕时 所需的时间就是计数量除以脉冲频率,而且在 COUT 引脚会 在计数一开始由高电位转为低电位,而且在计数完成后,再 转为高电位,如此将产生一个负向的输出。这样达到了第一 个条件。 4、 由于第一个条件已经达成,而且它会在 COUT 输出产生负向 的讯号,我们可以利用这个负向的讯号接到另一个计数器的 Gate 引脚,由于 Gate 引脚是高准位动作,也就是当此引脚 是高电位时,计数动作进行;而低电位时,计数动作停止。 第 243 页,共 386 页如果要利用这个讯号的话,我们还必须把前一个计数器的 COUT 讯号作反相,使其高低准位变换,以符合 Gate 引脚 的规定。在此我们要设定的是 TMC12 上的第 6 组计数器 Gate 设定,调整 J18 的 Jumper 设定,使其 Gate 采用第 5 组计数 器的 COUT 反相讯号,这样就可以达到上述的规格了。这样 子可以造成第 5 组计数器的计数过程里,第六组计数器也因 Gate 的高电位而计数,而且第 5 组计数器计数完成后,第六 组计数器也停止计数。 5、 第 5 组计数器和第 6 组计数器已经同步了,接下来再次考虑 讯号的的接法,第 5 组计数器可以采用内部脉冲的作法(笔 者在之前的讨论已将其设为使用 CLK1),因此计数的时间已 然确立;第六组计数器的 CLK 输入讯号就可以接入所欲计 算的频率讯号,每输入一个频率讯号,计数器的值就会减 1, 直到第六组计数器的 Gate 电位降为低电位为止。 6、 上述的步骤完成后,整合第 5 组计数器的计数时间,以及第 6 组计数器的计数值,就可以得知频率=下数计数值/计数时 间。程序中会再予以讨论。 综合上述的几点,线路的接法就可推得为图 5-8-5。 图 5-8-5 频率量测接线图 在图 5-8-5 中,标准的时间间隔可由第 5 组计数器产生,而在 这段时间内所计数的数值则由第 6 组计数得到,当然频率值也可因 此而产生。接下来,我们再建立项目。 画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立画 面: 第 244 页,共 386 页1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 F4 叫出属性窗口,更改其 Caption 属性为” Frequency Measurement”,表示我们将进行数字输入的侦测。 2、 安排二个 Label 控件,二者的 Caption 属性,一个写入「输入频 率」,一个写入「???」;问号的 Label 控件将用来显示所侦测 到的频率值。 3、 安排一个定时器控件,Interval 属性设为 100,表示每 100 毫秒 执行一次内部的程序代码;Enabled 属性设为 False,暂时不予 激活,等到初始化卡片后再允许激活。 4、 安排二个按钮控件,其 Caption 属性分别输入「开始计算」及 「结束系统」,用来开始侦测现在的事件,以及结束程序之用。 5、 依上述步骤所作出的画面如图 5-8-6。 图 5-8-6 频率计算的画面设计 功能设计:接下来就依下列步骤作出相对应的功能: 1、 在窗体的 Form_Load 事件程序程序中键入以下程序代码 Dim wTotalBoards As Integer If TMC12_DriverInit(wTotalBoards) <> TMC12_NoError Then MsgBox "驱动程序错误!", vbCritical + vbOKOnly, "错误" End If 首先是激活驱动程序,以便我们在程序中可以成功地控制适配 卡。 2、 在窗体的「开始计算」按钮的 Click 事件程序程序中键入以下 程序代码 '激活卡片 If TMC12_ActiveBoard(0) <> TMC12_NoError Then MsgBox "TMC12 适配卡无法激活!", vbCritical + vbOKOnly, " 激活错误" Exit Sub End If '激活定时器 Timer1.Enabled = True 第 245 页,共 386 页利用 TMC12_ActiveBoard 函数激活适配卡,并且在成功激活卡片 后,致能定时器控件,使其依 Interval 属性中的设定值定时地 工作。 3、 双击定时器控件,在其 Timer 事件程序中写入以下的程序代码 Dim wCount As Long Dim PreCount As Long Dim mt As Single TMC12_Select8254 1 ' 选择第 1 颗 8254,逻辑 上是 Counter4~6 TMC12_CounterSet 1, &H70, &HFFFF ' Counter-1, mode=0, down count ffff TMC12_CounterSet 2, &HB0, &HFFFF ' Counter-2, mode=0, down count ffff '开始计数后,数值会变化 DoEvents '可以用 Cout 状态变化作判断 Do If ((TMC12_CounterRead(1, &HE4) And &H80) <> 0) Then Exit Do End If DoEvents Loop '此时可以读回计数值,并作计算 wCount = TMC12_CounterRead(2, &H80) ' COUT= 65536 * 0.000625 = 40.96ms wCount = (65535 - wCount) + 2'原厂建议加 2 mt = 40.96 / wCount Label1.Caption = Format(1000 / mt, "0.####") 程序首先使用 TMC12_Select8254 选择 8254 芯片,由于笔者将 使用第 5 组和第 6 组计数器,它们是属于第 1 颗 8254,故此函 数的参数给 1(8254 的编号由 0 开始)。 如果是重新计数的话,使用 TMC12_CounterSet 函数写入计数 值到芯片中,三个参数中,第 1 个指出计数器编号(相对于该颗 8254),第 2 个是控制字组,经排列应为&H70 及 &HB0,而计 数初始值则给定&HFFFF,让计数最高可以算 65535。 利用读回指令判断第 5 组的 COUT 引脚是否已自计数开始后升 至高电位,若已升至高电位,表示计数完毕,接下来就再检查 第 6 组计数器的计数值。 第 6 组计数器的计数值读取使用 TMC12_CounterRead 函数,所 传回的值是现在计数器中的计数值,由于计数器是下数的方 式,所以我们还要使用 65535 去减去现在的计数值才是计数器 已经下数的次数,也才是真正的事件值。 将所得的时间和次数作运算,即可得到真正的频率值。 4、 双击”结束程序”按钮,在其 Click 事件中写入以下程序代码 第 246 页,共 386 页 '关闭驱动程序 TMC12_DriverClose '结束程序 End 先关闭驱动程序,再结束系统程序的执行。 实验的进行可以依图 5-8-5 的作法,读者也可以依照实际的情 形接出相同的接线来作实验。实验结果如图 5-8-7 所示,图中显示 频率值是 40014.56Hz,由波形产生器所输出的设定频率是 40KHz, 因此所量测到的误差是(40014.56-40000)/40000*100=0.0364(%),误 差相当地小,可见我们所作出的频率侦测项目的确可用;如果由波 形产生器产生不同的频率值时,显示出来的频率值也会不一样。 图 5-8-7 频率值的计算结果 详细的项目程序代码请参考”EX\CH5\FreqMeasure”,双击档案 夹中的 Project1.vbp 项目即可进行以上的实验。 详细的程序代码如下: Option Explicit '********************************************************* '开始计算频率的按钮 '此程序先激活卡片,接着再激活定时器,由定时器接手作计算 '******************************************************** Private Sub Command1_Click() '激活卡片 If TMC12_ActiveBoard(0) <> TMC12_NoError Then MsgBox "TMC12 适配卡无法激活!", vbCritical + vbOKOnly, " 激活错误" Exit Sub End If '激活定时器 Timer1.Enabled = True End Sub '************************************************* '结束程序的按钮 第 247 页,共 386 页'关闭驱动程序后,使用 End 指令结束程序 '************************************************* Private Sub Command2_Click() TMC12_DriverClose End End Sub '************************************************** '窗体的加载事件(Form_load) '程序中将驱动程序初始化 '************************************************* Private Sub Form_Load() Dim wTotalBoards As Integer If TMC12_DriverInit(wTotalBoards) <> TMC12_NoError Then MsgBox "驱动程序错误!", vbCritical + vbOKOnly, "错误" End If End Sub '**************************************************** '定时器的 Timer 事件 '计算的工作在此循环中进行 '**************************************************** Private Sub Timer1_Timer() Dim wCount As Long Dim PreCount As Long Dim mt As Single TMC12_Select8254 1 ' 选择第 1 颗 8254,逻辑 上是 Counter4~6 TMC12_CounterSet 1, &H70, &HFFFF ' Counter-1, mode=0, down count ffff TMC12_CounterSet 2, &HB0, &HFFFF ' Counter-2, mode=0, down count ffff '开始计数后,数值会变化 DoEvents '可以用 Cout 状态变化作判断 '以下的二个 Do… Loop 未作时间判断,可能造成程序停掉 '这可以加入判断改进 Do If ((TMC12_CounterRead(1, &HE4) And &H80) <> 0) Then Exit Do End If DoEvents Loop '此时可以读回计数值,并作计算 wCount = TMC12_CounterRead(2, &H80) ' COUT= 65536 * 0.000625 = 40.96ms wCount = (65535 - wCount) + 2 mt = 40.96 / wCount Label1.Caption = Format(1000 / mt, "0.####") End Sub 模块程序引用的是在 5-4-4 节所列的函数,请读者直接参考该 节的函数。 第 248 页,共 386 页5-8-3 旋转对象的转速侦测 上一小节我们讨论到使用二个计数器的串接来达到频率计算 的目的,而项目中使用波形产生器所得到的结果也相当不错,这样 的例子也可以应用到实际的例子中,一个常见的例子就是转速的侦 测。许多的旋转机械中都需要作转速的侦测,也许转速会对应到某 些特殊的物理现象,透过转速的侦测,可以间接了解这些特殊的物 理现象,可以帮助研究人员作更深入的研究与探讨。 如果得知一部旋转机器的转速呢?首先得先找出这部旋转机器 的旋转位置何在。一部机械的转动一定是有一个转轴来带动,透过 转轴的带动而再传动到其它的零件,欲了解转速就必须找出转动的 位置,接着在转动的位置装上可以产生讯号的装置,图 5-8-8 就是 相当常见的一种装置—编码器(EnCorder)。 图 5-8-8 编码器(Samtak 公司) 编码器的装设可以许多的位置,通常是装设在转动轴的末端(这 样比较不影响整个的结构),将其编码器的轴心和转动轴连接在一 起,供给其工作电压后,一旦转动就会传出讯号,讯号为方波,而 且分为 A、 B、 Z 三相(一般最常见的), A、 B 二相的方波角度相差 90 度,而 Z 相周期则是二倍于另二相(部份设计是转一圈后产生一 个 Z 相讯号),如图 5-8-9 所示。 第 249 页,共 386 页 图 5-8-9 编码器所产生的讯号模式 三个讯号都可以被接到计数器中作计数,如果单独接其中的一 条就可以计算频率(此频率乘上 60 就是每分钟的转速--RPM)。另 外 需注意的是,编码器的规格中有一项是????P/R,这里的 P/R 表示每 一转所产生的脉冲数(Pulse per Revolution),图 5-8-8 是每转 2048 个脉冲产生,愈高的脉冲表示其分辨率愈高,我们量测到的数值也 就愈准,当利用 TMC12 上的计数器取得频率值后,还要再除上此 每分钟的脉冲数值,才能得到真正的转数。 了解编号的相关原理后,欲测量编码器所产生的频率就可以使 用 5-8-2 小节的方法,利用频率量测的项目,先取得频率值,再将 此频率值乘上 60 而得到每分钟的转速值,由于此值还必须考虑到 每分钟产生的脉冲数(如图 5-8-8 中的 2048P/R),所以此值还须除以 编码器规格中的 P/R 值,就可以得到编码器所接的转动设备的真正 转速了,实务上的讯号和计数器最简单接法如图 5-8-10 所示,此图 是将上一小节的实验予以延伸而作成;其它的需量测转速的场合也 可以使用类似的方法作量测。 图 5-8-10 编码器与计数器的实务接法 图 5-8-10 中,编码器所产生的 A、 B、 Z 三相的讯号只要接入 其中一个均可以作量测,但是必须注意的是 A 相和 B 相的相位差 90 度,而 Z 相的周期时间则为其它二相的二倍(或视其设计而定), 第 250 页,共 386 页讯号译码时也必须还原回来,否则还以为讯号不稳,怎会讯号数值 差二倍呢!各位读者可以拿此观念去套用在其它的转动件上试试看! 5-8-4 脉冲宽度的量测 读者是否注意到 5-8-2 节所用的技巧?这里面有一个很有意思的 地方,请参考图 5-8-5,第 5 组的 COUT 引脚所产生的讯号是一个 脉冲(可能宽度会改变,就看我们给它的计数初始值而定),而第六 组计数器因为在这一个脉冲时间内,一共产生被算了 N 个 计数值; 5-8-2 及 5-8-3 小节利用这二个数值而得到了频率,由于一个脉冲的 时间知道了,因此可以反推 N 个计数值用掉的时间。 同样的,如果现在的情形反过来,假设我们已经可以知道产生 N 个 计数值所需的时间,那么是不是也可以反过来得到该一个脉冲 所历经的时间呢?由于二个讯号有关系,因此可以透过其中一个讯 号的已知值而得到另一个讯号的数值;透过已知 N 个计数值所经历 的时间而反推一个脉冲的时间,就是计算该脉冲宽度(Pulse Width), 所以我们已经知道脉冲宽度量测(Pulse Width Measurment)的基本 作法了。 现在考虑一个计数器的接法,一个计数器有 CLK、Gate、COUT 几支引脚,由于讯号由外界输入,因此只会由 CLK 或 Gate 输入, 如果要产生一个可以参考的脉冲讯号,当然最好还是由 CLK 进入, 而且尽可能使用适配卡所提供的脉冲(例如第二组计数器,其使用 适配卡所提供的 1.6MHz 脉冲);至于被量测的讯号就可由 Gate 进 入了。 由 Gate 进入的讯号是一个脉冲,而脉冲的讯号就是一个高低电 位变换的讯号;计数器的计数动作会在 Gate 引脚高电位时进行, 而于 Gate 引脚为低电位停止,所以脉冲讯号进入 Gate 的形式就必 须是一个 High Pulse 准位的脉冲,也就是平常保持在低准位,而讯 号产生时升为高电位,接着再回到低电位。计数器就在高电位保持 的期间进行计数的行为,而程序可以在第二次低电位时间读取计数 值的变化,由于计数值的间隔时间已经由于 CLK 的脉冲而确定, 因此一个脉冲讯号的宽度也就可以得知。就以第二组计数器为例, 其讯号线路的接法就如图 5-8-11。 第 251 页,共 386 页 图 5-8-11 使用第二组计数器量测脉冲宽度的接法 在图 5-8-11 中还有一个重点就是如何得知讯号的准位已经发 生变化,在变化的时候,程序必须作相对应的动作,例如在低准位 的时候先将计数值写入计数器中,等到高准位时被触发而开始计 数,另外也必须侦测高准位后的低准位状态,以便读取计数值,并 计算出脉冲宽度,接下来,我们进行程序的设计。 画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立画 面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 F4 叫出属性窗口,更改其 Caption 属性为”Pulse Width Measurement”,表示我们将进行数字输入的侦测。 2、 安排一个 Combo 控件,属性名称定为 cmbCounter,它将用来让 使用者选择计数器号码。 3、 安排三个 Label 控件,其 Caption 属性,一个写入「选择计数器」, 一个写入「脉冲宽度」,一个写入「???」;问号的 Label 控件 将用来显示所侦测到的脉冲宽度。 4、 安排一个定时器控件,Interval 属性设为 100,表示每 100 毫秒 执行一次内部的程序代码;Enabled 属性设为 True。 5、 安排二个按钮控件,其 Caption 属性分别输入「开始量测」及 「结束系统」,用来开始侦测现在的事件,以及结束程序之用。 6、 依上述步骤所作出的画面如图 5-8-12。 第 252 页,共 386 页 图 5-8-12 脉冲宽度计算的画面设计 功能设计:接下来就依下列步骤作出相对应的功能: 1、 在窗体的 Form_Load 事件程序程序中键入以下程序代码 Dim wTotalBoard As Integer '记录卡片的数目 Dim Ret As Integer '传回值宣告 Dim i% '初始化卡片,并传回卡片数目 Ret = TMC12_DriverInit(wTotalBoard) If Ret <> TMC12_NoError Then '检查初始化是否成功 MsgBox "开启驱动程序失败!!!" & ErrStr(Ret), vbCritical + vbOKOnly, "TMC12 Card Error" End '结束系统 End If cmbCounter.Clear For i = 0 To 11 cmbCounter.AddItem "Counter->" & CInt(i + 1) Next i cmbCounter.ListIndex = 0 '显示第一个选项(计数器 0) 首先是激活驱动程序,以便我们在程序中可以成功地控制适配 卡,并且将可以选择的计数器填入 Combo 控件中,预先显示第 一个计数器为选择项。 2、 在窗体的「开始量测」按钮的 Click 事件程序程序中键入以下 程序代码 bStart = Not bStart '改变动作旗标 If bStart Then Command2.Caption = "停止量测" Else Command2.Caption = "开始量测" End If If TMC12_ActiveBoard(0) <> TMC12_NoError Then MsgBox "激活卡片失败", vbCritical + vbOKOnly, "TMC12 DIO Control/Detect" 第 253 页,共 386 页 Exit Sub End If 利用 TMC12_ActiveBoard 函数激活适配卡,程序中使用旗标 bStart 作为是否开始量测脉冲宽度的记录,其它的计算就放在定时器 中执行。程序也判断旗标的状态而改变按钮上的显示文字。 3、 双击定时器控件,在其 Timer 事件程序中写入以下的程序代码 Dim windata As Integer Dim ChipNo As Integer Dim CounterNo As Integer Dim wCount As Long Dim mSecond As Single If Not bStart Then Exit Sub '若不需量测,则跳出此循环 Do While True TMC12_Di windata ' 侦测 DI-0 准位 DoEvents '等待高电位 If (windata And 1) = 1 Then Exit Do End If Loop Do While True TMC12_Di windata ' 侦测 DI-0 准位 DoEvents '等待低电位 If (windata And 1) = 0 Then Exit Do End If Loop '决定 8254 芯片号码及使用的计数器号码 ChipNo = Int(cmbCounter.ListIndex / 3) CounterNo = cmbCounter.ListIndex Mod 3 TMC12_Select8254 ChipNo '选择 8254 芯片 '设定模式 0 TMC12_CounterSet CounterNo, &H30 + CounterNo * 2 ^ 6, &HFFFF '再等待一次高低准位的变化 Do While True TMC12_Di windata ' 侦测 DI-0 准位 DoEvents '等待高电位 If (windata And 1) = 1 Then Exit Do End If Loop Do While True TMC12_Di windata ' 侦测 DI-0 准位 DoEvents '等待低电位 If (windata And 1) = 0 Then Exit Do End If Loop 第 254 页,共 386 页 '将计数值读回 wCount = TMC12_CounterRead(CounterNo, 0) '算出实际的计数值 wCount = 65535 - wCount + 2 '反推出时间(以 Counter2 之 1.6MHz 为例) mSecond = wCount * 0.000625 '显示在屏幕上 lblPWM.Caption = mSecond & "ms" 程序一开始就侦测 DI-0 的状态,由图 5-8-11 的接法,讯号会 进入到 DI-0 的引脚,透过这个引脚讯号的侦测,我们可以知道 讯号的准位为何,而程序就在侦测这个状态的高电位及低电位 一个讯号周期出现时写入计数值,此时就等待再一个讯号的到 来,以及再一次的高低准位的侦测。 在第二次的高低准位侦测成功后,就可以得到该脉冲讯号的宽 度了,详细的流程如图 5-8-13。 图 5-8-13 讯号侦测及数值计算流程图 4、 双击”结束系统”按钮,在其 Click 事件中写入以下程序代码 '关闭驱动程序 TMC12_DriverClose '结束程序 End 先关闭驱动程序,再结束系统程序的执行。 实验的进行可以依图 5-8-11 的接法,并且给一个方波的讯号就 可以,讯号除了进入计数器作计数外,也会进到数字输入的通道 第 255 页,共 386 页(DI-0)。实验可能结果如图 5-8-14 所示,图中显示脉冲宽度是 5.003125ms,笔者由波形产生器所输出的设定频率是 100Hz。理论 上,100Hz 的方波,其脉冲宽度应该是 1/(100*2)=5(ms),因此所量 测到的误差是(5.003125-5)/5*100=0.0625(%),误差相当地小。不同 的方波频率所得的结果显示,其误差也是相当地小。 由于 100Hz 的方波,除了高电位所处的时间外,还有低电 位的时间也是和高电位的时间一样(假设 Duty=50%),因此脉冲宽 度才会是 1 秒钟除以(100*2),而不是 100。 图 5-8-14 频率值的计算结果 详细的项目程序代码请参考”EX\CH5\PulseWidth”,双击档案夹 中的 Project1.vbp 项目即可进行以上的实验。 详细的程序代码如下: Option Explicit Dim bStart As Boolean '开始计算的旗标 '*********************************************** '结束按钮被按下时的程序 '接着会关闭驱动程序,以释放资源 '最后使用 End 结束程序 '*********************************************** Private Sub Command1_Click() TMC12_DriverClose End End Sub Private Sub Command2_Click() bStart = Not bStart '改变动作旗标 If bStart Then Command2.Caption = "停止量测" Else Command2.Caption = "开始量测" End If If TMC12_ActiveBoard(0) <> TMC12_NoError Then 第 256 页,共 386 页 MsgBox "激活卡片失败", vbCritical + vbOKOnly, "TMC12 DIO Control/Detect" Exit Sub End If End Sub '*********************************************** '窗体的 Form_Load 事件,系统激活时被执行 '先初始化驱动程序,若失败则结束系统 '*********************************************** Private Sub Form_Load() Dim wTotalBoard As Integer '记录卡片的数目 Dim Ret As Integer '传回值宣告 Dim i% '初始化卡片,并传回卡片数目 Ret = TMC12_DriverInit(wTotalBoard) If Ret <> TMC12_NoError Then '检查初始化是否成功 MsgBox "开启驱动程序失败!!!" & ErrStr(Ret), vbCritical + vbOKOnly, "TMC12 Card Error" End '结束系统 End If cmbCounter.Clear For i = 0 To 11 cmbCounter.AddItem "Counter->" & CInt(i + 1) Next i cmbCounter.ListIndex = 0 '显示第一个选项(计数器 0) End Sub '*********************************************** '错误列表 '*********************************************** Function ErrStr(ErrNo%) Select Case ErrNo Case 1 ErrStr = "开启驱动程序失败!" Case 2 ErrStr = "驱动程序未开启!" Case 3 ErrStr = "驱动程序版本错误!" Case 4 ErrStr = "安装中断时发生错误!" Case 5 ErrStr = "清除中断计数错误!!" Case 6 ErrStr = "取得中断计数错误!" Case 7 ErrStr = "缓存器错误!" Case 8 ErrStr = "移除中断错误!" Case 9 ErrStr = "搜寻卡片错误!" Case 10 ErrStr = "卡片号码超过!" Case 11 ErrStr = "重置错误!" End Select End Function 第 257 页,共 386 页 '*********************************************** '定时器的 Timer 事件 '计算的工作由此程序进行 '透过 Digital Input 确认讯号准位 '*********************************************** Private Sub Timer1_Timer() Dim windata As Integer Dim ChipNo As Integer Dim CounterNo As Integer Dim wCount As Long Dim mSecond As Single If Not bStart Then Exit Sub '若不需量测,则跳出此循环 Do While True TMC12_Di windata ' 侦测 DI-0 准位 DoEvents '等待高电位 If (windata And 1) = 1 Then Exit Do End If Loop Do While True TMC12_Di windata ' 侦测 DI-0 准位 DoEvents '等待低电位 If (windata And 1) = 0 Then Exit Do End If Loop '决定 8254 芯片号码及使用的计数器号码 ChipNo = Int(cmbCounter.ListIndex / 3) CounterNo = cmbCounter.ListIndex Mod 3 TMC12_Select8254 ChipNo '选择 8254 芯片 '设定模式 0 TMC12_CounterSet CounterNo, &H30 + CounterNo * 2 ^ 6, &HFFFF '再等待一次高低准位的变化 Do While True TMC12_Di windata ' 侦测 DI-0 准位 DoEvents '等待高电位 If (windata And 1) = 1 Then Exit Do End If Loop Do While True TMC12_Di windata ' 侦测 DI-0 准位 DoEvents '等待低电位 If (windata And 1) = 0 Then Exit Do End If Loop '将计数值读回 wCount = TMC12_CounterRead(CounterNo, 0) '算出实际的计数值 第 258 页,共 386 页 wCount = 65535 - wCount + 2 '反推出时间(以 Counter2 之 1.6MHz 为例) mSecond = wCount * 0.000625 '显示在屏幕上 lblPWM.Caption = mSecond & "ms" End Sub 模块的程序和前几节都一样,请参考前几节。 第 259 页,共 386 页常问问题集(FAQ) Q:讯号输入了,可是计数动作失常? A: 请检查讯号的引脚是否正确,由于 TMC12 的引脚相当多,引 脚弄错是经常发生的。 Q: 我可以将编码器的 A、 B、 Z 三相都接吗? A:当然可以,不过要分到不同的通道上去。另外,如果都接的话,可以判 断的内容更多,只不过需要写的程序也比较多。都接的情形下要注意相位 的问题,你可以使用数字输入的引脚来帮助引脚电压的侦测。 Q: 讯号的准位是否有限制? A:是的。通常采用 20mA 以内的讯号,而且高低准位的电位要清楚;高电 位建议 2.1~5V,低电位建议在 0.7V 以下。 第 260 页,共 386 页本章习题 1、 请说明计数器的计数方式。 2、 是否可以直接使用缓存器的存取? 3、 如果想要一个 32 位的计数器,程序及接线方式应如何设计? 4、 若欲同时控制二部步进马达的驱动器,程序应如何撰写? 第 261 页,共 386 页第六章 数字输入的轮询及中断的使用 外界的数字状态我们可以透过适配卡上的数字输入引脚而测 得,通常也是由我们的程序直接下达询问的指令而取得输入状态数 值;如果希望一直取得这些状态的话,应如何处理呢?这是本章要 讨论的课题。 6-1 轮询(Polling) 计算机是相当被动的,如果我们要求计算机帮我们作事情,我 们必须下达指令,下一个指令,计算机就作一个动作,没下指令, 它就什么也不作;因此,如果我们希望它一直传回某一个状态回来, 那么就要一直下达传回的指令,一直不断要求传回状态的动作就称 为轮询。 6-1-1 输询的作法 1-定时器 之前讨论数字输入的作法中其实作已经展示了轮询的作法了, 里面主要是使用 Timer 控件达到不断要求适配卡传回数字输入的信 息,程序中的实作方式就类似于以下的作法 Private Sub Timer1_Timer() Dim i%, j%, DIStatus% '扫描前 8 个通道 DIStatus = PIODIO_InputByte(wBaseAddr + &HD0) For i = 0 To 7 …根据取得的 DIStatus 作判断处理 Next i …其它欲执行的指令 End Sub 使用上述的类型执行不断地取得数字输入时,当然还得先设定好定 时器的相关属性,例如 Enabled 属性及 Interval 属性二者就是最重 要的二个属性,一个是激活定时器的工作(Enabled),另一个则是设 定定时器中的程序代码多久必须执行一次(Interval)。 在 Enabled 属性被设为 True 的情形下,此种方式取决于定时器 中的 Interval 属性的设定,如果设定是 100,那么定时器内的程序 以每 100 毫秒被执行一次的速度一直作下去,直到 Enabled 属性被 设为 False,如图 6-1-1 所示。 第 262 页,共 386 页 图 6-1-1 定时器的执行过程 在定时器控件的每次程序代码执行完毕后,均会产生一个 Interval 属性所规定的等待时间,在这段等待时间内,CPU 会去执 行其它的作业,而将定时器的工作放在一旁,一直等到时间到了才 又回来取定时器内的程序代码至 CPU 执行;因此定时器内的程序 代码的执行间隔为 Interval 的设定值,如果再加上程序代码本身的 执行时间的话,定时器内的程序一开始执行到下一次定时器内的同 一段程序代码被执行时,总共是经过了 Interval 的设定值加上全部 程序代码的执行时间,千万不要认为定时器内的某一段程序一定在 Interval 的间隔就被执行一次哦!以上的论点是假设 Windows 操作系 统是处于非常理想的状况(通常会有差异)。 因此理想上,当我们设定 Interval 是 100 的话,定时器的内部 程序就是每 100 毫秒被执行一次,笔者以一个程序来检视一下这样 的推论,看看 Visual Basic 执行的结果是如何! 画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立画 面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 F4 叫出属性窗口,更改其 Caption 属性为”Timer Interval Test”, 表示我们将进行定时器的 Interval 属性测试。 2、 安排 1 个 Timer 控件,将其 Enabled 属性设为 False,使其于程 序激活时暂时不激活定时器功能;另外也将 Interval 属性设为 100,当定时器激活后以每 100 毫秒的速度执行其内部的程序代 码。 3、 安排 2 个按钮控件,在其 Caption 属性中分别输入”开始测试” 及 ”结束程序”,作为执行相对应功能的控制之用。 4、 安排 1 个 Text Box 控件,其 Font 属性设为适当的大小,这里将 用来显示执行的结果之用。 第 263 页,共 386 页5、 依上述步骤所作出的画面如图 6-1-2。 图 6-1-2 定时器 Interval 测试的画面设计 功能设计:接下来就依下列步骤作出相对应的功能: 1、 在「开始测试」按钮的 Click 事件程序程序中键入以下程序代 码 j = 0 Timer1.Enabled = True 程序的目的是激活定时器,使其开始执行定时器内部的程序代 码,并将记录值 j 设为 0。 2、 双击定时器,在其 Timer 事件程序程序中键入以下程序代码 Dim i& No(j) = GetTickCount '记录程序代码执行前计算机的 Tick 值 For i = 1 To 100000 i = i + 1 '执行一段程序代码 Next i Af(j) = GetTickCount '记录程序代码执行后计算机的 Tick 值 j = j + 1 If j >= 5 Then '执行 5 次后,将结果显示在文字框中 txtResult.Text = "" For i = 0 To 4 txtResult.Text = txtResult.Text + "开始 Tick(" & CStr(i) & ")=" & CStr(No(i)) & vbCrLf txtResult.Text = txtResult.Text + "结束 Tick(" & CStr(i) & ")=" & CStr(Af(i)) & vbCrLf Next i Timer1.Enabled = False '中止定时器的执行 End If 此段程序代码只让定时器控件执行 5 次,而且在执行的过程中, 将开始执行与结束执行时的系统 Tick 值记录下来,直到最后一 次才将所有 5 次的记录结果显示到文字框中,并且中止定时器 第 264 页,共 386 页的执行。计算机在操作系统开始运作后,每隔一毫秒就会将系 统中的 Tick 数值加 1,而 GettickCount 函式是 Windows 所提供 的 API,它被用来取得计算机系统现在的 Tick 值,所以透过二 次 GettickCount 的读取,我们就可以得知二个计算式的时间差 异了。 3、 双击「结束程序」的控钮控件,在其 Click 事件中写入以下程序 代码 End 按下此按钮后会结束程序的执行。 完成以上的设计后,我们可以开始进行测试。按下 F5 让项目 开始执行,在执行的画面上按下「开始测试」让定时器开始进行内 部程序的执行,其动作会连续执行 5 次,其结果可能如图 6-1-3 所 示。 图 6-1-3 定时器 Interval 属性的测试结果 由图中结果显示,5 次的执行结果不是一模一样,虽然我们设 定其 Interval 属性是 100 毫秒,意即前一次的结束到下一次的开始 应该是 100,由 图 6-1-3 的左图看出是 101,而于右图则看出是 110, 显然还是有差异。程序中以 For Loop 计算了 100000 次的加法,除 了第一次的执行过程较慢外,其它的几次均是瞬间完成(小于 1ms, 笔者使用的是 Intel Celeron 900MHz CPU)。读者也可以多加测试程 序,您将会发现,每一次的测试结果都不是那么的精确,所以如果 读者相当在意时间所造成的影响的话,使用 Timer 的 Interval 属性 也就需要加以考虑这些细微的差异。以免实际结果和理论上造成的 差距而造成其它的疑虑。 第 265 页,共 386 页详细的项目程序代码请参考”EX\CH6\TimerTest”,双击档案夹 中的 Project1.vbp 项目即可进行以上的实验。 详细的程序代码如下: Dim j% '宣告记录 Tick 的变量 Dim Af(0 To 4) As Long, No(0 To 4) As Long '********************************************** '开始测试的按钮控件之 Click 事件程序 '将计数值清为 0 , 并且激活定时器 '********************************************** Private Sub Command1_Click() j = 0 Timer1.Enabled = True End Sub '********************************************** '结束程序的按钮控件之 Click 事件程序 '结束程序的执行 '********************************************** Private Sub Command2_Click() End End Sub '********************************************** 'Timer 控件之 Timer 事件 '激活后即激活内部的程序予以执行 '并在执行一段时间后,将结果显示在文字框控件内 '********************************************** Private Sub Timer1_Timer() Dim i& No(j) = GetTickCount '记录程序代码执行前计算机的 Tick 值 For i = 1 To 100000 i = i + 1 '执行一段程序代码 Next i Af(j) = GetTickCount '记录程序代码执行后计算机的 Tick 值 j = j + 1 If j >= 5 Then '执行 5 次后,将结果显示在文字框中 txtResult.Text = "" For i = 0 To 4 txtResult.Text = txtResult.Text + "开始 Tick(" & CStr(i) & ")=" & CStr(No(i)) & vbCrLf txtResult.Text = txtResult.Text + "结束 Tick(" & CStr(i) & ")=" & CStr(Af(i)) & vbCrLf Next i Timer1.Enabled = False '中止定时器的执行 End If End Sub 模块中的程序则只有单独的一行 API 宣告如下 Public Declare Function GetTickCount Lib "kernel32" () As Long 第 266 页,共 386 页 另外一个值得我们注意的问题是,此 Interval 的时间既然必须 给定,是否可以给个最小值(不能是 0,此值将使得定时器控件不工 作 ),以便让定时器在最小的时间间隔就执行工作?应该可以。根据 属性的定义,此值是以毫秒(千分之一秒)为单位,因此最小值是 1 毫秒;不过,这个最小的值可能无法达到;我们还是以本节的范例 来作实验,请读者将上述项目的定时器 Interval 属性设为 1,储存 后按下 F5 将其执行,其可能结果如图 6-1-4 所示,结果显示其数值 会在 50 几个毫秒左右,无法达到 1 毫秒的精度,这也给我们在设 计系统程序时一个重要的参考,理论上的值总是和实务上的结果有 一些差距。 图 6-1-4 当 Interval 设为 1 时的执行结果 6-1-2 输询的作法 2-Do Loop 另一种轮询的方式是使用 Do…Loop 的组合,让程序的执行一 直在循环的内部,如以下的程序片断所示 Private Sub Command1_Click() Dim i%, j%, DIStatus% Do '扫描前 8 个通道 DIStatus = PIODIO_InputByte(wBaseAddr + &HD0) For i = 0 To 7 …根据取得的 DIStatus 作判断处理 Next i …其它欲执行的指令 Loop End Sub 上述的程序例子里,当按钮控件 Command1 被按下后,内部程序即 被执行,而且其执行会一直在 Do…Loop 循环内执行,如果使用这 第 267 页,共 386 页种方式,通常也会在程序的内部加上检查的机制,免得程序停不下 来。我们建立一个简单的测试项目看看其执行的结果。 画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立画 面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 F4 叫出属性窗口,更改其 Caption 属性为”Do Loop Test”,表 示 我们将进行定时器的 Do Loop 测试。 2、 安排 2 个按钮控件,在其 Caption 属性中分别输入”开始测试” 及 ”结束程序”,作为执行相对应功能的控制之用。 3、 安排 1 个 Text Box 控件 Name 属性设为 txtResult,其 Font 属性 设为适当的大小,这里将用来显示执行的结果之用。 4、 依上述步骤所作出的画面如图 6-1-5。 图 6-1-5 Do Loop 范例设计画面 功能设计:接下来就依下列步骤作出相对应的功能: 1、 双击”开始测试”按钮,在其 Click 事件程序程序中键入以下程序 代码 Dim i& Do For i = 0 To 100000 i = i + 1 Next i CalNo = CalNo + 1 '以下将执行的次数显示在文字框中 txtResult.Text = "已计算" & CStr(CalNo) & "次 !" Loop 此段程序代码由 For Next 叙述执行 100000 次加法运算,接着 将 CalNo 变量加 1,然后显示 CalNo 的结果在文字框控件中。 2、 双击「结束程序」的控钮控件,在其 Click 事件中写入以下程序 第 268 页,共 386 页代码 End 按下此按钮后会结束程序的执行。 接着在 Visual Basic 的执行环境中按下 F5 执行此范例,图 6-1-6 是执行的结果: 图 6-1-6 范例的执行结果 图 6-1-6 是笔者已经按下”开始测试”的按钮所产生的结果,这 个结果很奇怪,似乎不符合程序的设计预设结果,其应讨论的有以 下二点奇怪的地方: 1、 不管我们按下画面的什么按钮,似乎没有什么反应。 2、 结果显示的文字框中没有显示出执行的次数显示。 其实,范例中的写法在很早以前就出现,而在窗口应用程序中 则比较少出现;如果我们采用这种写法,必须考虑一些额外的程序 处理,否则就会出现上述的二种情形;当出形这种现象时,给我们 的第一个感觉就是—程序当掉了,读者可以在 Visual Basic 的执行 环境下同时按下 Ctrl+Break 来中止程序的执行,此时会产生程序断 点,请读者直接按下工具列上的停止按钮就可以了。 原始项目存放在光盘片中目录”EX\CH6\DoLoopTest1”里,执行 里面的 Project1.vbp 就可以了。 接下来,我们将以上”开始测试”按钮中的程序稍微作一下修改, 如下所列 Dim i& Do 第 269 页,共 386 页 For i = 0 To 100000 i = i + 1 Next i CalNo = CalNo + 1 '以下将执行的次数显示在文字框中 txtResult.Text = "已计算" & CStr(CalNo) & "次 !" DoEvents '暂时释放控制权,查看一下信息队列 Loop 上述的程序中与前一个程序不同的地方在于多了一行 DoEvents 的叙述,将程序执行起来,这次不会出现类似当机的情形 了,而按下”开始测试”的按钮后,执行的结果如图 6-1-7 所示。 图 6-1-7 加入 DoEvents 后的执行结果 之前所说的二种情形都没有出现,不仅鼠标的押按动作正常, 而且结果显示的文字框也正常显示计算的信息。 为什么一个 DoEvents 指令的差会如此不同呢?原因在于计算机 的执行过程相当快速,在 Do Loop 的循环中如果没有让 CPU 去处 理其它的事务时,所有的 CPU 执行时间就都被该段程序代码给占 据了,不仅没有时间去检测鼠标的压按动作,而且也没有时间执行 画面的显示更新指令,所以给我们的觉得是程序当掉了;透过 DoEvents 指令可以强迫暂时中断一下该段程序代码的执行,而让 CPU 去看看指令暂存区中是否有其它的指令需要服务,鼠标的压按 动作和画面的显示更新指令就可以得到 CPU 的处理时间而被执行, 因而解决了之前的问题。 加入 DoEvents 后的项目笔者放在光盘片中目 录 ”EX\CH6\DoLoopTest2”里,请读者执行后,比较其结果。 和 Do Loop 一样作法的叙述还有 While 叙述,这些 Visual Basic 提供的方式都可以达到同样的功能,读者只要了解其写法,都可以 应用到自己的应用程序中而得到连续执行的目的。前面也已经说明 第 270 页,共 386 页过,不断执行的目的在于使得程序片断一直获得执行权而得到执 行,如果我们将外界状态检测的程序代码放在这些不断执行的循环 中,是否就可以达到不断检测的目了呢? 第 271 页,共 386 页 6-2 中断(Interrupt) 计算机运作的过程中,如果需得知某一状况通常是送要求指令 给硬设备,并等待其数值传回;如果希望在程序正常运作下不分配 额外的检测时间给上述检测程序的话,就需要求由硬件本身透过机 制来通知程序相关的状态,这时中断就发生了。 6-2-1 适配卡里的中断 在第四章讨论到数字输出入适配卡时,曾经就卡片内部的缓存 器作过中断缓存器的介绍,但并无一个专门的例子作介绍,本节将 就中断的部份作详细的讨论。 首先在硬件的中断触发上,可以用图 6-2-1 予以表示,图中假 设可以引发中断的外部讯号有 8 个,每一片适配卡都有一个中断的 通道可以使用,当外部的中断产生时,透过适配卡上的线路,最后 就是到达计算机上的实际中断硬件,借着中断线路而通知操作系统 此中断事件的发生。 图 6-2-1 硬件上的中断过程 图 6-2-1 也可以看出来,由于每一片适配卡一般所使用的中断 线路只有一个,当欲监视的中断来源大于一个以上时,必须将这些 可能的中断讯号作 OR 运算,只要一个讯号成立了,再触发计算机 中的中断机制;特别注意到图中的各中断讯号与实际计算机里面的 中断线路之间的关系,实际的中断触发是低电平触发,也就是当电 压电平 Low 时,才会触发计算机硬件中的中断讯号;如果我们的讯 号一般是处于低电平,而是希望在高电平时触发中断的话,就必须 使用反相(Not)电路,这也是图 6-2-1 中所展示的意义,因此在适配 第 272 页,共 386 页卡中是以高电平为中断触发的电平;相反的,如果我们希望一般是 处于高电位,而于低电位状态发生后,引发中断处理程序,则不需 要 Not 程序,直接将各个讯号源 OR 起来就可以了。 前面几章所介绍的输出入适配卡,均可以使用相同的中断机 制,当中断进入时,可以引发中断子程序的执行。 Visual Basic 的程序和中断执行之间的关系则如图 6-2-2 所示, 中断子程序被执行时会暂停 Visual Basic 程序的正常流程,待中断 子程序执行完后,再接下去作其它的正常流程部份。 图 6-2-2 Visual Basic 程序和中断流程 程序刚执行的时候需要设定中断子程序,而设定中断子程序之 后也必须激活中断检测的功能,此功能除了硬件上需要配合外,厂 商所提供的 DLL 也必须具有相对应的功能,使得 Visual Basic 的程 序不需要另外写程序去检测中断之发生与否。 由图 6-2-2 也可以看出,Visual Basic 程序一旦激活中断的功能, 并且给定中断的子程序后,只要欲检测的数字输入讯号的电平发生 了变化,就会使得计算机中的中断讯号产生,这将使得 CPU 得知 这个变化,CPU 会先暂停原来处理的工作,并转而处理该中断所代 表的子程序区段(也就是 Visual Basic 程序预先设定好的中断子程 序 );当该中断子程序处理完成后,再回过头来到被暂停的程序部 份,接下去作未完的程序动作。 在程序的实作上,笔者建议先将各个欲处理的程序代码分别撰 写妥当,将这些子程序先规画好其对应的中断情形,接着使用函式 指定中断所对应到的子程序,并激活中断检测功能;接下来,就等 着中断的产生了! 6-2-2 8 通道中断卡片-PISO-725 第 273 页,共 386 页了解中断的硬件部份后,笔者在此要介绍可以同时引发 8 个通 道中断的适配卡 PISO-725。 图 6-2-3 PISO-725 适配卡(8 通道输出/8 通道输入卡) PISO-725 和一般的数字输出入适配卡差不多,但其采用的输出 入接点接口不一样,针对输出和输入接点简单叙述如下: 数字输入: • 8 通道数字输入。 • 所有数字输入通道均可产生中断。 • 透过跳针的设定可选择输入型式为隔离或非隔离式。 • 隔离式输入使用组件 PC-357。 • 隔离式输入之高电位范围:3.5 ~ 30V。 • 隔离式输入之低电位范围:0 ~ 1V。 • 隔离式输入之阻抗:1.2K, 1W。 • 隔离式输入之隔离电压:3750V。 • 隔离式输入之反应时间:20uS。 • 非隔离式输入之电位范围:TTL 兼容范围。 数字输出: • 8 通道机电式继电器输出。 • 二种型类型的 C 型继电器。 第 274 页,共 386 页• 拥有 COM、 NC、 NO 接点的 C 型继电器可供使用者外接设备。 • 拥有 COM、 NO 接点的 C 型继电器提供回读指令供程序检查继 电器状态。 • 卡片上具继电器状态指示发光二极管。 • 继电器功率:AC: 0.3A/120VAC、 DC: 1A/30VDC。 • 继电器闭合所需时间:5ms。 • 继电器断开所需时间:10ms。 • 寿命:100,000 次 (于 30V/1A 条件下)。 当我们准备使用这片卡片作相关的应用时,必须依据本身的需 求及其规格作彼此的检查,才能使得项目的执行获得较好的结果, 一旦应用的场合需要时,也需经常地在实际的应用与规格间来回检 查。 使用这片适配卡时,一样要使用厂商所附的 DLL 才行,而使用 的程序均和第四章所述的一样,不同的是,第四章讨论的是 TTL 电平的数字输出适配卡,而在这里要讨论的是带有继电器及同时具 有 8 通道中断检测的适配卡。 6-2-3 8 通道继电器输出控制 本小节将说明如何使用程控 PISO-725 上的 8 颗继电器。首先, 由第四章的基础我们可以知道,在数字输出入适配卡上最主要是透 过缓存器的读写来达到控制或取得状态的目的,当然使用 PISO-725 也不例外,本卡片的相关缓存器的资料如表 6-2-1 所示。 表 6-2-1 PISO-725 缓存器定义 2018香港马会开奖现场 读取时 写入时 wBase+0 初始化控制缓存器 同左 wBase+2 辅助控制缓存器 同左 wBase+3 辅助资料缓存器 同左 wBase+5 中断屏蔽控制缓存器 同左 wBase+7 辅助引脚状态缓存器 同左 wBase+&H2A 中断极性控制缓存器 同左 wBase+&HC0 读回 D0~ D7 的状态(反相) 输出 D0~ D7 wBase+&HC4 读取 DI 0~DI7 不可用 PISO-725 采用的是 37 针 D 型接头,表 6-2-2 是其接脚定义, 当我们外接讯号时,必须清楚这些引脚所对应的 IO 接点,才不致 出错。 第 275 页,共 386 页表 6-2-2 PISO-725 引脚定义 引脚编号 定义 引脚编号 定义 1 NO0 20 NO3 2 COM0 21 COM3 3 NC0 22 NC3 4 NO1 23 NO4 5 COM1 24 COM4 6 NC2 25 NO5 7 NO2 26 COM5 8 COM2 27 NO6 9 NC7 28 COM6 10 NO7 29 GND 11 COM7 30 DIB0 12 DIA0 31 DIB1 13 DIA1 32 DIB2 14 DIA2 33 DIB3 15 DIA3 34 DIB4 16 DIA4 35 DIB5 17 DIA5 36 DIB6 18 DIA6 37 DIB7 19 DIA7 表 6-2-2 中的 NO?指的是第? 个继电器的常开接点(Normal Open),而 NC?则是指第?个继电器的常闭接点(Normal Close),COM? 指的是第?个继电器的共接点;平常 COM?和 NC?是接通的,而继电 器导通后,COM?就和 NO?导通。如同第三章的说明,拥有 COM 和 NO 二个接点的继电器型式称为 Form A;而拥有 COM、 NO 及 NC 三个接点的继电器则称之为 Form C。 表 6-2-2 中的 DIA?指的是第?个数字输入通道的 A 接点,而 DIB? 则是指第?个数字输入通道的 B 接点,这二个接点就是讯号的正负 点接入处。 各位读者可以查看一下表 6-2-1 中,继电器的控制属于数字输 出的部份,所以我们需要将资料送往的2018香港马会开奖现场是(基2018香港马会开奖现场+&HC0),所 以我们可以将欲控制的状态值送到这个2018香港马会开奖现场上,那么继电器的反应 就会是依我们的值而作动作。以下我们就来建立一个控制 PISO-725 上的继电器的项目。 第 276 页,共 386 页画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立画 面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 F4 叫出属性窗口,更改其 Caption 属性为”PISO-725 Relay Control”,表示我们将进行该适配卡的继电器测试。 2、 安排 2 个 Image 控件,其 Picture 属性分别加载 Redlamp.jpg 和 Graylamp.jpg, Visible 属性均设为 False,使其于项目激活后不 显示,我们将利用这二个控件在必要时为其它的灯号改变颜色。 3、 安排 8 个 Image 控件,Name 名称定为 imgDO,先将其 Picture 属性加载 Graylamp.jpg,这些灯号将作为继电器开关的指示之 用,将其 Enabled 属性设为 False,以避免未开启卡片就执行控 制动作。 4、 安排 8 个 Label 控件,将其 Caption 属性设 为 ”Relay-0”~”Relay-7”,作为标示继电器输出 Image 之用。 5、 安排 2 个按钮控件,其 Caption 属性分别输入”激活卡片”及 ”结 束程序”,作为执行相对应功能。 6、 依上述步骤所作出的画面如图 6-2-4。 图 6-2-4 继电器控制的画面设计 功能设计:接下来就依下列步骤作出相对应的功能: 1、 在「激活卡片」按钮的 Click 事件程序程序中键入以下程序代 码 Dim wRetVal As Integer Dim InVal1 As Integer Dim InVal2 As Integer Dim InVal3 As Integer Dim i As Long '适配卡的初始化 第 277 页,共 386 页 wInitialCode = PIODIO_DriverInit() If wInitialCode <> PIODIO_NoError Then MsgBox "驱动程序初始化失败!", , "PIODIO Card Error" Exit Sub End If '搜寻计算机中是否存在 PIO_725 适配卡 (若使用在其它地方,此程 序也可不作) If PIODIO_SearchCard(1, PIO_725) <> PIODIO_NoError Then MsgBox "PIO_725 适配卡不存在!!", , "PIODIO Card Error" Exit Sub End If '取得卡片的组态信息(由于只有一片,故第一个参数给 0,代表第一 片 ) wRetVal = PIODIO_GetConfigAddressSpace(0, wBaseAddr, wIrq, wSubVendor, _ wSubDevice, wSubAux, wSlotBus, wSlotDevice) If wRetVal <> PIODIO_NoError Then MsgBox "组态信息错误 !!" Exit Sub End If DOValue = 0 PIODIO_OutputByte wBaseAddr, 1 '激活所有的 DIO 功能 '激活 Image,允许作实验 For i = 0 To 7 imgDO(i).Enabled = True Next i 程序的目的是初始化适配卡,并且取得适配卡的2018香港马会开奖现场;另外也 激活适配卡的 IO 功能以及可供控制用的 Image 控件。 2、 双击 Image 控件,在其 Click 事件程序程序中键入以下程序代码 fDO(Index) = Not fDO(Index) '转态 '判断状态,并将记录的数值作运算 If fDO(Index) Then '若为激活,则加上数值 DOValue = DOValue + 2 ^ Index '将灯号换为红色 imgDO(Index).Picture = imgRed.Picture Else '若为关闭,则减掉数值 DOValue = DOValue - 2 ^ Index '将灯号换为灰色 imgDO(Index).Picture = imgGray.Picture End If '将数值输出到低八位的2018香港马会开奖现场 PIODIO_OutputByte wBaseAddr + &HC0, DOValue ' Relay Output 此段程序分别针对每个 Image 控件作输出。透过 Index 的指示 可以指出使用者是点选中那一个 Image 控件,而 DOValue 的目 的在于记录输出的数值,如果该通道要输出的话,就将通道所 代表的数值加在 DOValue 中,反之,若该通道不作输出的话, 就从 DOValue 减去该数值。 通道数值的计算可由一个字节来求得,该字节的最低位代表通 道 0,而最高位则代表通道 7,只要是该通道要输出,就将该 第 278 页,共 386 页通道的位设为 1,否则就设为 0,最后将这些数值组合起来, 就是必须送到缓存器的数值了。例如通道 0、通道 2、通道 5 输出高电位,而其它则输出低电位,写成二进制的表示方法时 就是 00100101,将其转换为十进制的话,就是依次将 2 的次方 数乘上该位的数值(0 或 1)即可,所以由高位往低位写的话,数 值是 0*2^7+0*2^6+1*2^5+0*2^4+0*2^3+1*2^2+0*2^1+1*2^0 =32+4+1 =37(十进制) 当我们把数值 37 写到(基2018香港马会开奖现场+&HC0)的2018香港马会开奖现场时,就会使得通道 0、通道 2 和通道 5 输出高电位,其它的通道则输出低电位。 3、 双击 「结束程序」的控钮控件,在其 Click 事件中写入以下程序 代码 '关闭驱动程序 PIODIO_DriverClose '结束程序 End 按下此按钮后会关闭驱动程序,并结束程序的执行。 完成以上的设计后,我们可以开始进行测试。按下 F5 让项目 开始执行,首先是按下画面上的「激活卡片」,接着就可以按下其 它的 Image 控件,各位读者将会发现 PISO-725 上的继电器会随着 操作而响起切换声,我们也因此知道控制动作是否正常地被执行; 另外,如果可能的话,最好再接上外部的设备,以实际的联机方式 取得实验的结果,这样是最真实的。 实验的结果可能如图 6-2-5 所示;图中显示了继电器编号 0、1、 2、 6、 7 是开启的,而继电器编号 3、 4、 5 是关闭的。如果该继电 器是属于 Form A 型式的话,开启继电器将使得 COM 和 NO 二点接 通;而关闭将使得这二个点断开。如果该继电器是属于 Form C 型 式的话,开启继电器将使得 COM 和 NO 二点接通,COM 和 NC 二 点断开;而关闭将使得 COM 和 NO 二点断开,COM 和 NC 二点接 通。 第 279 页,共 386 页 图 6-2-5 继电器控制实验结果 详细的项目程序代码请参考”EX\CH6\RelayControl”,双击档案 夹中的 Project1.vbp 项目即可进行以上的实验。 详细的程序代码如下: Option Explicit '基2018香港马会开奖现场 Dim wBaseAddr As Long '中断号码 Dim wIrq As Integer 'PCI 芯片信息 Dim wSubVendor As Integer, wSubDevice As Integer, wSubAux As Integer 'PCI 插槽信息 Dim wSlotBus As Integer, wSlotDevice As Integer Dim wInitialCode As Integer Dim fDO(0 To 7) As Boolean '数字输出的旗标 Dim DOValue As Long '数字输出数值 '****************************************************** '以下是结束程序的按钮所引发的动作 '首先关闭驱程序,接着再以 End 结束程序 '****************************************************** Private Sub cmdEnd_Click() '关闭驱动程序 PIODIO_DriverClose '结束程序 End End Sub '****************************************************** '以下是卡片初始化的动作 '任何对于卡片的操作之前,必须先执行初始化的动作 '****************************************************** Private Sub CmdInitial_Click() Dim wRetVal As Integer Dim InVal1 As Integer Dim InVal2 As Integer Dim InVal3 As Integer 第 280 页,共 386 页 Dim i As Long '适配卡的初始化 wInitialCode = PIODIO_DriverInit() If wInitialCode <> PIODIO_NoError Then MsgBox "驱动程序初始化失败!", , "PIODIO Card Error" Exit Sub End If '搜寻计算机中是否存在 PIO_725 适配卡 (若使用在其它地方,此程 序也可不作) If PIODIO_SearchCard(1, PIO_725) <> PIODIO_NoError Then MsgBox "PIO_725 适配卡不存在!!", , "PIODIO Card Error" Exit Sub End If '取得卡片的组态信息(由于只有一片,故第一个参数给 0,代表第一 片 ) wRetVal = PIODIO_GetConfigAddressSpace(0, wBaseAddr, wIrq, wSubVendor, _ wSubDevice, wSubAux, wSlotBus, wSlotDevice) If wRetVal <> PIODIO_NoError Then MsgBox "组态信息错误 !!" Exit Sub End If DOValue = 0 PIODIO_OutputByte wBaseAddr, 1 '激活所有的 DIO 功能 '激活 Image,允许作实验 For i = 0 To 7 imgDO(i).Enabled = True Next i End Sub '****************************************************** '当表示灯号的 Image 控件被按到时,就执行以下的程序 '每个 Image 对应到一个 Bit,状态用 fDO 记录 '数值的部份使用 DOValue 作记录 '****************************************************** Private Sub imgDO_Click(Index As Integer) fDO(Index) = Not fDO(Index) '转态 '判断状态,并将记录的数值作运算 If fDO(Index) Then '若为激活,则加上数值 DOValue = DOValue + 2 ^ Index '将灯号换为红色 imgDO(Index).Picture = imgRed.Picture Else '若为关闭,则减掉数值 DOValue = DOValue - 2 ^ Index '将灯号换为灰色 imgDO(Index).Picture = imgGray.Picture End If '将数值输出到低八位的2018香港马会开奖现场 PIODIO_OutputByte wBaseAddr + &HC0, DOValue ' Relay Output End Sub PDIODIO.Bas 模块部份是和第四章所使用的模块相当类似,不 同的地方是加上了中断相关的函式,笔者还是将其全部列出,请读 注意中断的宣告部份,另外,DLL 的名称也改为 PIODIO3.DLL, 第 281 页,共 386 页这是厂商在笔者撰写时所出新版 DLL,提供的功能更完备,为了与 旧版区别,故加上编号。笔者进行撰稿时已进行测试,且功能完整, 应该可以在本书付梓时在书后的光盘片中找到此 DLL 最新的版本, 实际的 DLL 名称以泓格科技网站公布之最新版名称为准。 '以下是错误宣告 Global Const PIODIO_NoError = 0 Global Const PIODIO_DriverOpenError = 1 Global Const PIODIO_DriverNoOpen = 2 Global Const PIODIO_GetDriverVersionError = 3 Global Const PIODIO_InstallIrqError = 4 Global Const PIODIO_ClearIntCountError = 5 Global Const PIODIO_GetIntCountError = 6 Global Const PIODIO_RegisterApcError = 7 Global Const PIODIO_RemoveIrqError = 8 Global Const PIODIO_FindBoardError = 9 Global Const PIODIO_ExceedBoardNumber = 10 Global Const PIODIO_ResetError = 11 Global Const PIODIO_IrqMaskError = 12 Global Const PIODIO_ActiveModeError = 13 Global Const PIODIO_GetActiveFlagError = 14 Global Const PIODIO_ActiveFlagEndOfQueue = 15 Global Const PIOD144_P2C0 = 0 ' pin29 of CN1(37 pin D-type, pin1 to pin37) Global Const PIOD144_P2C1 = 1 ' pin28 of CN1(37 pin D-type, pin1 to pin37) Global Const PIOD144_P2C2 = 2 ' pin27 of CN1(37 pin D-type, pin1 to pin37) Global Const PIOD144_P2C3 = 3 ' pin26 of CN1(37 pin D-type, pin1 to pin37) ' Interrupt Channel for PIO-D48 Global Const PIOD48_INTCH0 = 1 ' INT_CHAN_0 Global Const PIOD48_INTCH1 = 2 ' INT_CHAN_1 Global Const PIOD48_INTCH2 = 4 ' INT_CHAN_2 Global Const PIOD48_INTCH3 = 8 ' INT_CHAN_3 ' Interrupt ActiveMode for PIOD48_XXX functions Global Const PIOD48_ActiveLow = 1 ' Active When Low Global Const PIOD48_ActiveHigh = 2 ' Active When High ' to trigger a interrupt when high -> low Global Const PIODIO_ActiveLow = 0 ' to trigger a interrupt when low -> high Global Const PIODIO_ActiveHigh = 1 '定义卡片常数,用来寻找卡片之用 Global Const PIO_D144 = &H800100 ' 144 * D/I/O Global Const PIO_D96 = &H800110 ' 96 * D/I/O Global Const PIO_D64 = &H800120 ' 64 * D/I/O Global Const PIO_D56 = &H800140 ' D24 + 16I + 16O 第 282 页,共 386 页Global Const PIO_D48 = &H800130 ' 48 * D/I/O Global Const PIO_D24 = &H800140 ' 24 * D/I/O Global Const PIO_725 = &H800CFF ' 725 '测试函式 Declare Function PIODIO_ShortSub Lib "PIODIO3.dll" (ByVal a As Integer, ByVal b As Integer) As Integer Declare Function PIODIO_FloatSub Lib "PIODIO3.dll" (ByVal a As Single, ByVal b As Single) As Single Declare Function PIODIO_GetDllVersion Lib "PIODIO3.dll" () As Integer '驱动程序部份 Declare Function PIODIO_DriverInit Lib "PIODIO3.dll" () As Integer Declare Sub PIODIO_DriverClose Lib "PIODIO3.dll" () Declare Function PIODIO_SearchCard Lib "PIODIO3.dll" (wBoards As Integer, ByVal dwPIOPISOCardID As Long) As Integer Declare Function PIODIO_GetDriverVersion Lib "PIODIO3.dll" (wDriverVersion As Integer) As Integer Declare Function PIODIO_GetConfigAddressSpace Lib "PIODIO3.dll" ( _ ByVal wBoardNo As Integer, wAddrBase As Long, wIrqNo As Integer, _ wSubVendor As Integer, wSubDevice As Integer, wSubAux As Integer, _ wSlotBus As Integer, wSlotDevice As Integer) As Integer Declare Function PIODIO_ActiveBoard Lib "PIODIO3.dll" (ByVal wBoardNo As Integer) As Integer Declare Function PIODIO_WhichBoardActive Lib "PIODIO3.dll" () As Integer 'DIO 的部份 Declare Sub PIODIO_OutputByte Lib "PIODIO3.dll" (ByVal address As Long, ByVal dataout As Integer) Declare Function PIODIO_InputByte Lib "PIODIO3.dll" (ByVal address As Long) As Integer '中断宣告部份 Declare Function PIODIO_InstallCallBackFunc Lib "PIODIO3.dll" (ByVal wChannel As Integer, ByVal wInitialState As Integer, ByVal user_func As Long) As Integer Declare Function PIODIO_RemoveAllCallBackFunc Lib "PIODIO3.dll" () As Integer Declare Function PIODIO_EnableInt Lib "PIODIO3.dll" () As Integer Declare Function PIODIO_DisableInt Lib "PIODIO3.dll" () As Integer Declare Function PIODIO_DebugWrite Lib "PIODIO3.dll" (lBuf As Long) As Integer Declare Function PIODIO_DebugRead Lib "PIODIO3.dll" (lBuf As Long) As Integer 6-2-4 8 通道中断讯号检测 第 283 页,共 386 页PISO-725 适配卡上提供有 8 个通道的数字输入,这些数值输入 通道的讯号可分为二种,一是非隔离式输入讯号,一是隔离式输入 讯号,数字输入的电路如图 6-2-6 所示。 JB? 1 3 2 4 U2A PC357 2 3 41 R1 670 + - LM339 7 6 1 3 12 DIA? 4.7k VCC DIB? 4.7k 2k VCC JA? 1 3 2 4 DI? 图 6-2-6 PISO-725 数字输入电路图 每一个数字输入的部份有二个接点,分别标示为 DIA?及 DIAB?,经过电路后进到计算机中,在图上标示为 DI?,其中的?可 由 0~7,表示通道号码。 至于采用隔离或非隔离输入是在 JA?及 JB?二个 Jumper 上作选 择;如果采用的是非隔离式输入的话,JA?和 JB?均需插在 3-4 的位 置;而若采用隔离式输入的话,JA?和 JB?就换插在 1-2 的位置。图 6-2-7 是 PISO-725 上 JA?及 JB?的位置分布图。应特别注意的是, 一组数字输入使用了 JA 及 JB 二组跳针,一旦决了采用的输入方式 后,必须同时改变 JA?及 JB?二个跳针。 图 6-2-7 Jumper 分布图 适配卡上的跳针编号和电路图是一样的,所以由图 6-2-7 可知 该片适配卡采用的是隔离式的数字输入;图中共有 8 组跳针,JA? 和 JB?合为一组。 第 284 页,共 386 页当采用隔离式的输入时,图 6-2-6 中的线路图就可以等效于图 6-2-8。 DIB? PC357 2 3 41 VCC 4.7k DI?670 4.7k + - LM339 7 6 1 3 12 DIA? VCC 2k 图 6-2-8 隔离式输入的等效电路图 当采用非隔离式的输入时,图 6-2-6 的线路图就可以等效于图 6-2-9。 DIB? DIA? DI? 图 6-2-9 非隔离式输入的等效电路 什么时候采用非隔离式的输入呢?由此 6-2-2 节所说明之适配卡 上的规格可知,其非隔离式的输入兼容于 TTL 电平,因此,当 DIA? 讯号电平大于 DIB?电平 2.1V 以上时,即为高电位;反之,当 DIA? 讯号电平仅大于 DIB?电平 0.7V 以下时,即为低电位;同时,DIA? 上的电流也必须小于 20 毫安。当我们使用的是测试讯号时(例如使 用波形产生器的讯号),就适合使用非隔离式的输入方式。 另一方面,隔离式的输入方式里,高电位的定义是 3.5V~30V(DIA?和 DIB?比较而来),当我们需要的检测的电压较大 时 (例如在工业应用场合里的传感器讯号,它们很多是 12 V 或 24V 的直流讯号),不能直接进入到适配卡中,采用隔离式的输入可以 保护适配卡免于损坏;而高低讯号的检测就透过图 6-2-6 中的 PC357 传递光讯号,即可避免适配卡因过大的电压而打坏。 中断的来源是使用数字输入通道,所以在第四章所讨论的数字 输入的相关项目的作法也可以应用到 PISO-725 上的数字输入通道 上,在本节中就跳过数字输入的作法,直接将检测数字输入的项目 改由中断的方式来进行,看看利用定时器和中断的作法有什么不 同。 第 285 页,共 386 页接着笔者就软件部份讨论有关中断的作法。在本章前面已经说 明过中断的流程,就软件上来说,中断产生后,必须要有一个地方 可以让中断获得处理,以 Visual Basic 的方式来说,势必要有一段 子程序可以在中断发生时执行,而当子程序执行完毕后,再回到中 断发生之前的状态,继续之前的程序行程;这样的作法就是照着图 6-2-2 中的流程来规划。 当我们欲激活中断时有几个步骤必须要作,首先是使用厂商提 供的函式,在此是 PIODIO_InstallCallBackFunc 函式,此函式的宣告 如下 Declare Function PIODIO_InstallCallBackFunc Lib "PIODIO3.dll" (ByVal wChannel As Integer, ByVal wInitialState As Integer, ByVal user_func As Long) As Integer 此函式的参数有 3 个: wChannel 指的是所欲激活中断的通道号码,可用数值为 0~7。 wInitialState 指的是该通道一开始的状态,可用数值是 0 或 1,所 谓一开始的状态即是中断所欲检测的状态改变之意,如果我们希望 中断检测的是由低电位转为高电位的状态,那么该通道的初始状态 就是低电位(以数值 0 表示),一旦电位升至高电位后就会引发中断 的执行;反过来说,如果该通道平常是维持在高电位,而我们希望 在其降为低电位时产生中断,那么就要设定开始状态为高电位(以 数值 1 表示),一旦该通道电位降至低电位时,就会引发中断。 user_func 指的是使用者自行建立的中断子程序2018香港马会开奖现场;当中断产生 时,CPU 必须去执行一段由我们自行建立的子程序,通常我们是先 建立好子程序后,再将此子程序的2018香港马会开奖现场指定给 DLL,而2018香港马会开奖现场的传送 则是使用 Visual Basic 内建的 AddressOf 运算子,此运算子将会取 得函式的2018香港马会开奖现场。 设定完上述的函式,就可使用 PIODIO_EnableInt 将中断功能予 以激活;此指令一经下达,适配卡就会开始检测中断讯号是否被激 活,而由于此适配卡是 8 个数字输入通道都可激活中断,因此中断 产生后,我们自然可以将不同的中断来源导向到不同的子程序,以 完成不同的工作。 有了 PISO-725 相关数字输入及中断的观念后,以下我们将建 立一个同时检测 8 个数字输入状况的中断范例。 画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立画 面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小,按下 第 286 页,共 386 页F4 叫出属性窗口,更改其 Caption 属性为”PISO-725 Interrupt Dectect”,表示我们将进行该适配卡的中断检测。 2、 安排 8 个 Shape 控件,并选择圆形外观,将其 BackColor 属性 设为白色,且设定其 BackStyle 为不透明,如此一来,当我们改 变此控件的背景颜色时,就如同改变灯号一般。 3、 安排 8 个 Label 控件,将其 Caption 属性设为”DI-0”~”DI-7”,作 为标示数字输入灯号之用。 4、 安排 8 个 Label 控件,将其 Caption 属性输入 0000,我们将用 此控件显示出其已经被中断的次数。 5、 安排 2 个按钮控件,其 Caption 属性分别输入”激活卡片”及 ”结 束程序”,作为执行相对应功能。 6、 依上述步骤所作出的画面如图 6-2-10。 图 6-2-10 中断讯号检测的画面设计 功能设计:接下来就依下列步骤作出相对应的功能: 1、 在「激活卡片」按钮的 Click 事件程序程序中键入以下程序代 码 Dim wRetVal As Integer Dim InVal1 As Integer Dim InVal2 As Integer Dim InVal3 As Integer Dim i As Long '适配卡的初始化 wInitialCode = PIODIO_DriverInit() If wInitialCode <> PIODIO_NoError Then MsgBox "驱动程序初始化失败!", , "PIODIO Card Error" Exit Sub End If '搜寻计算机中是否存在 PIO_D56 适配卡 (若使用在其它地方,此程 序也可不作) If PIODIO_SearchCard(1, PIO_725) <> PIODIO_NoError Then 第 287 页,共 386 页 MsgBox "PIO_725 适配卡不存在!!", , "PIODIO Card Error" Exit Sub End If '取得卡片的组态信息(由于只有一片,故第一个参数给 0,代表第一 片 ) wRetVal = PIODIO_GetConfigAddressSpace(0, wBaseAddr, wIrq, wSubVendor, _ wSubDevice, wSubAux, wSlotBus, wSlotDevice) If wRetVal <> PIODIO_NoError Then MsgBox "组态信息错误 !!" Exit Sub End If PIODIO_OutputByte wBaseAddr, 1 '激活所有的 DIO 功能 '设定中断服务子程序 PIODIO_InstallCallBackFunc 0, 0, AddressOf User_Callback_0 PIODIO_InstallCallBackFunc 1, 0, AddressOf User_Callback_1 PIODIO_InstallCallBackFunc 2, 0, AddressOf User_Callback_2 PIODIO_InstallCallBackFunc 3, 0, AddressOf User_Callback_3 PIODIO_InstallCallBackFunc 4, 1, AddressOf User_Callback_4 PIODIO_InstallCallBackFunc 5, 1, AddressOf User_Callback_5 PIODIO_InstallCallBackFunc 6, 1, AddressOf User_Callback_6 PIODIO_InstallCallBackFunc 7, 1, AddressOf User_Callback_7 '激活中断的检测 PIODIO_EnableInt '将变量初始化为 0 For i = 0 To 7 IntCount(i) = 0 Next i 程序的目的是初始化适配卡,并且取得适配卡的2018香港马会开奖现场;另外就 是 PIODIO_InstallCallBackFunc 函式的呼叫,经由以上的说明,我 们必须设定中断所使用的几个参数,这些参数包括了通道号 码、初始状态及中断子程序的2018香港马会开奖现场。程序中接着呼叫 PIODIO_EnableInt 将中断功能激活。程序中也将记录的变量预 先清除为 0。 8 个中断输入的回呼函式部份均被建立,笔者将所有的中断子 程序均建立,我们将检视中断的作法是如何被实现的。 2、 接着我们要建立中断子程序所需的程序代码,这个部份的程序 代码必须建在模块层级,因此请另外新增一个模块,并建立在 「激活卡片」按钮中所指定的子程序。由于笔者设计的每个子 程序均相同,但使用数组记录其中的变化,每一个子程序的内 容如下 '计算已触发了几次 IntCount(0) = IntCount(0) + 1 '将次数显示到画面上 Form1.lblINTNo(0).Caption = CStr(Val(Form1.lblINTNo(0).Caption) + 1) '改变颜色为红色 Form1.spDI(0).FillColor = RGB(255, 0, 0) StatusDetect 0, 100 '检测状态的改变 第 288 页,共 386 页 '改变颜色为白色 Form1.spDI(0).FillColor = RGB(255, 255, 255) 这段程序代码是以第 0 个通道的中断子程序为例;它将中断计 数值加 1,并且将此值显示在画面上的 Label 控件中,另外也 利用主画面上的 Shape 控件颜色的改变达到通知的目的。其它 的各通道中断子程序也是一样的,只是其中的数组索引不同而 已。 程序中也呼叫了子程序 StatusDetect,此函式如下 Sub StatusDetect(ChNo As Integer, DT As Long) Dim TT As Long, DIStatus As Integer, OrgStatus As Integer TT = GetTickCount '先取得通道 ChNo 现在的输入状态 OrgStatus = PIODIO_InputByte(wBaseAddr + &HC4) And 2 ^ ChNo Do '再次检查通道 ChNo 的状态,看看是否已改变 DIStatus = PIODIO_InputByte(wBaseAddr + &HC4) And 2 ^ ChNo '若状态已改变,就跳出子程序 If DIStatus <> OrgStatus Then Exit Sub ' If GetTickCount - TT <= 0 Then TT = GetTickCount Loop Until GetTickCount - TT >= DT '或是等到时间终了跳出循环 End Sub 这段函式的输入参数有二,一是通道号码,另一个则是等待的 毫秒数;其目的是检测数字输入状态的改变,如果在等待的毫 秒数到达之前,数字输入的状态已经改变的话,就跳出程序, 否则就等待时间结束。 3、 双击 「结束程序」的控钮控件,在其 Click 事件中写入以下程序 代码 '取消中断 PIODIO_DisableInt '关闭驱动程序 PIODIO_DriverClose End 按下此按钮后会将所有的中断功能关闭,同时关闭驱动程序,并 结束程序的执行。 完成以上的设计后,我们可以开始进行测试。笔者的作法是利 用实验设备连接 PISO-725 上的 8 个数字输入通道,以开关控制数 字输入的状态为高电位或低电位。接着按下 F5 让项目开始执行, 首先是按下画面上的「激活卡片」,然后改变开关状态,数字输入 的状态就会发生改变,透过这个改变可以改变画面上的现象,读者 可以观察这些改变。 第 289 页,共 386 页为了实验的方便,我们可以将 PISO-725 的 D 型接座外接到延 伸板上,在接线上比较方便,图 6-2-11 是笔者使用的延伸板,其与 适配卡上的接点正好是一对一地作延伸。 图 6-2-11 DN-37 延伸板 透过 DN-37 将 8 个数字输入通道的正负讯号引脚均连接妥当 后,即可进行开关的切换实验;我们必须注意到的是,在程序设计 中,笔者将 8 个输入通道中的前四个的初始状态设为低电平,而将 后四个数字输入通道的初始状态设为高电平,所以当外界的讯号准 备妥当时,也要依此设计先调整其初始状态,如果初始状态未先调 整至预设的状态,一激活卡片后就会一直引发中断(避免通道为浮 接状态,此举亦引起当机)。图 6-2-12 是笔者实验的结果。 图 6-2-12 中断讯号检测实验结果 图 6-2-12 是 8 个中断输入过程中所引发的结果,红色的部份是 正好被引发而时间尚未结束或中断通道的状态尚未改变;白色则显 示出中断的情形已结束,或是时间已超过设定值。 从整个程序看来并没有包含中断号码的设定,既然从硬件的角 来看必须有计算机上的中断来触发后续的软件动作,为何在程序没 第 290 页,共 386 页有硬件上的中断相关信息呢?这是因为在安装适配卡的过程中,此 适配卡所需要的中断就已经安装妥当了,只要适配卡上的硬件设计 正确的话,讯号由外界引发,透过适配卡上的传递就会引发主机板 上的中断讯号,CPU 也会取得这个中断的信息,并进而转通知 Visual Basic 的程序,准备处理这个中断信息。这个中断信息一被检测后, 后续就由卡片的驱动程序和中断处理有关的程序代码接收处理,这 些处理就包括了呼叫我们先前建立的各个中断处理子程序。 不要在回呼函式中设定断点作检测,这种作法会造成 Visual Basic 的崩溃;欲检测回呼函式可以在其中写入 Debug,将信息输 出到实时运算窗口中。 中断之处理在本例中看到了实际的外观,实际上在窗口操作系 统里使用中断来处理的场合并不多见;在上述的例子中我们看到只 要电平改变就会不断地引发中断子程序的执行,当然我们可以利用 旗标的改变达到控制引发状态的目的,而厂商则是将这部份的未来 规划为只在上升缘或是下降缘触发一次,当状态维持固定时,不会 一直触发中断子程序,这种作法相当符合一般的使用原则,这个部 份的最新DLL 请至www.icpdas.com 下载, 或是连络 service@icpdas.com,而中断的引发方式则与本例类似,但又更精简, 只需建立一主要的中断子程序,便可于该子程序依参数数值的判 断,得知是那一个通道引起中断,转而再次呼叫我们在本例子中所 建立的其它子程序。使用初始化函式及取得卡片的方式和TMC12 是一样的,接着就是建立回呼函式,例如下列程序就是其建立的片 断 ' 0 --> disable ' 1 --> init_LOW, active_HIGH ' 2 --> init_HIGH, active_LOW init(0) = 2 'init_HIGH, active_low init(1) = 1 ' init_LOW, active_HIGH init(2) = 2 'init_HIGH, active_low init(3) = 1' init_LOW, active_HIGH init(4) = 2 'init_HIGH, active_low init(5) = 1' init_LOW, active_HIGH init(6) = 2 'init_HIGH, active_low init(7) = 1' init_LOW, active_HIGH dwIntN = 1 i = PISO725_InstallCallBackFunc(board, init(0), dwIntN, AddressOf callback_main) j = PISO725_EnableInt(board) 主要的回呼函式中可以再次呼叫其它的函式,或是直接在其内 部作运算,如以下的程序片断即为上述程序中的回呼函式 Public Sub callback_main() 第 291 页,共 386 页 Dim i As Long i = dwIntN And 1 If i = 1 Then 'channel_0 is active int_count_0 = int_count_0 + 1 End If i = dwIntN And 2 If i = 2 Then 'channel_1 is active int_count_1 = int_count_1 + 2 End If i = dwIntN And 4 If i = 4 Then 'channel_2 is active int_count_2 = int_count_2 + 4 End If i = dwIntN And 8 If i = 8 Then 'channel_3 is active int_count_3 = int_count_3 + 8 End If i = dwIntN And &H10 If i = &H10 Then 'channel_4 is active int_count_4 = int_count_4 + 10 End If i = dwIntN And &H20 If i = &H20 Then 'channel_5 is active int_count_5 = int_count_5 + 100 End If i = dwIntN And &H40 If i = &H40 Then 'channel_6 is active int_count_6 = int_count_6 + 1000 End If i = dwIntN And &H80 If i = &H80 Then 'channel_7 is active int_count_7 = int_count_7 + 10000 End If End Sub 在上述的程序中,透过 dwIntN 参数的数值比对,我们可以得 知是那一个通道的中断被引发,自然就可以在程序中写出相对应的 程序代码。 本小节详细的项目程序代码请参考”EX\CH6\INTCheck”,双击档 案夹中的 Project1.vbp 项目即可进行以上的实验。 详细的程序代码如下: Option Explicit '**************************************************** '以下是结束程序所作的部份 '将中断及驱动程序关闭,然后结束程序 '**************************************************** Private Sub cmdEnd_Click() '取消中断 PIODIO_DisableInt '关闭驱动程序 第 292 页,共 386 页 PIODIO_DriverClose End End Sub '**************************************************** '以下是卡片初始化的动作 '任何对于卡片的操作之前,必须先执行初始化的动作 '**************************************************** Private Sub CmdInitial_Click() Dim wRetVal As Integer Dim InVal1 As Integer Dim InVal2 As Integer Dim InVal3 As Integer Dim i As Long '适配卡的初始化 wInitialCode = PIODIO_DriverInit() If wInitialCode <> PIODIO_NoError Then MsgBox "驱动程序初始化失败!", , "PIODIO Card Error" Exit Sub End If '搜寻计算机中是否存在 725 适配卡 If PIODIO_SearchCard(1, PIO_725) <> PIODIO_NoError Then MsgBox "PIO_725 适配卡不存在!!", , "PIODIO Card Error" Exit Sub End If '取得卡片的组态信息(由于只有一片,故第一个参数给 0,代表第一 片 ) wRetVal = PIODIO_GetConfigAddressSpace(0, wBaseAddr, wIrq, wSubVendor, _ wSubDevice, wSubAux, wSlotBus, wSlotDevice) If wRetVal <> PIODIO_NoError Then MsgBox "组态信息错误 !!" Exit Sub End If CmdInitial.Enabled = False PIODIO_OutputByte wBaseAddr, 1 '激活所有的 DIO 功能 '设定中断服务子程序,每一个中断输入都设定一个处理的子程序 'AddressOf 用来取得模块中子程序的2018香港马会开奖现场 PIODIO_InstallCallBackFunc 0, 0, AddressOf User_Callback_0 PIODIO_InstallCallBackFunc 1, 0, AddressOf User_Callback_1 PIODIO_InstallCallBackFunc 2, 0, AddressOf User_Callback_2 PIODIO_InstallCallBackFunc 3, 0, AddressOf User_Callback_3 PIODIO_InstallCallBackFunc 4, 1, AddressOf User_Callback_4 PIODIO_InstallCallBackFunc 5, 1, AddressOf User_Callback_5 PIODIO_InstallCallBackFunc 6, 1, AddressOf User_Callback_6 PIODIO_InstallCallBackFunc 7, 1, AddressOf User_Callback_7 '激活中断的检测 PIODIO_EnableInt '将变量初始化为 0 For i = 0 To 7 IntCount(i) = 0 Next i End Sub 第 293 页,共 386 页中断子程序是建在另外的模块中,以下是该模块的内容 Public Declare Function GetTickCount Lib "kernel32" () As Long '以下定义卡片要用的变量 Public wBaseAddr As Long Public wIrq As Integer Public wSubVendor As Integer, wSubDevice As Integer, wSubAux As Integer Public wSlotBus As Integer, wSlotDevice As Integer Public wTotalBoards, wInitialCode As Integer '以下定义各中断子程序所使用的变量 Public IntCount(0 To 7) As Integer Public bInt(0 To 7) As Boolean '**************************************************** '以下延迟及状态检测的子程序,单位是毫秒 'Loop 循环中的检查中包含了状态的确认 '当状态已改变即跳出循环,否则在一段时间后也会自动跳出 'ChNo 是通道号码,DT 是检查的时间 '**************************************************** Sub StatusDetect(ChNo As Integer, DT As Long) Dim TT As Long, DIStatus As Integer, OrgStatus As Integer TT = GetTickCount '先取得通道 ChNo 现在的输入状态 OrgStatus = PIODIO_InputByte(wBaseAddr + &HC4) And 2 ^ ChNo Do '再次检查通道 ChNo 的状态,看看是否已改变 DIStatus = PIODIO_InputByte(wBaseAddr + &HC4) And 2 ^ ChNo '若状态已改变,就跳出子程序 If DIStatus <> OrgStatus Then Exit Sub ' If GetTickCount - TT <= 0 Then TT = GetTickCount Loop Until GetTickCount - TT >= DT '或是等到时间终了跳出循环 End Sub '*************************************************** '通道 0 产生中断时的服务子程序 '*************************************************** Public Sub User_Callback_0() '计算已触发了几次 IntCount(0) = IntCount(0) + 1 '将次数显示到画面上 Form1.lblINTNo(0).Caption = CStr(Val(Form1.lblINTNo(0).Caption) + 1) '改变颜色为红色 Form1.spDI(0).FillColor = RGB(255, 0, 0) StatusDetect 0, 100 '检测状态的改变 '改变颜色为白色 Form1.spDI(0).FillColor = RGB(255, 255, 255) End Sub '*************************************************** '通道 1 产生中断时的服务子程序 第 294 页,共 386 页'*************************************************** Public Sub User_Callback_1() '计算已触发了几次 IntCount(1) = IntCount(1) + 1 '将次数显示到画面上 Form1.lblINTNo(1).Caption = CStr(Val(Form1.lblINTNo(1).Caption) + 1) '改变颜色为红色 Form1.spDI(1).FillColor = RGB(255, 0, 0) StatusDetect 1, 100 '检测状态的改变 '改变颜色为白色 Form1.spDI(1).FillColor = RGB(255, 255, 255) End Sub '*************************************************** '通道 2 产生中断时的服务子程序 '*************************************************** Public Sub User_Callback_2() '计算已触发了几次 IntCount(2) = IntCount(2) + 1 '将次数显示到画面上 Form1.lblINTNo(2).Caption = CStr(Val(Form1.lblINTNo(2).Caption) + 1) '改变颜色为红色 Form1.spDI(2).FillColor = RGB(255, 0, 0) StatusDetect 2, 100 '检测状态的改变 '改变颜色为白色 Form1.spDI(2).FillColor = RGB(255, 255, 255) End Sub '*************************************************** '通道 3 产生中断时的服务子程序 '*************************************************** Public Sub User_Callback_3() '计算已触发了几次 IntCount(3) = IntCount(3) + 1 '将次数显示到画面上 Form1.lblINTNo(3).Caption = CStr(Val(Form1.lblINTNo(3).Caption) + 1) '改变颜色为红色 Form1.spDI(3).FillColor = RGB(255, 0, 0) StatusDetect 3, 100 '检测状态的改变 '改变颜色为白色 Form1.spDI(3).FillColor = RGB(255, 255, 255) End Sub '*************************************************** '通道 4 产生中断时的服务子程序 '*************************************************** Public Sub User_Callback_4() '计算已触发了几次 IntCount(4) = IntCount(4) + 1 '将次数显示到画面上 Form1.lblINTNo(4).Caption = CStr(Val(Form1.lblINTNo(4).Caption) + 1) '改变颜色为红色 Form1.spDI(4).FillColor = RGB(255, 0, 0) 第 295 页,共 386 页 StatusDetect 4, 100 '检测状态的改变 '改变颜色为白色 Form1.spDI(4).FillColor = RGB(255, 255, 255) End Sub '*************************************************** '通道 5 产生中断时的服务子程序 '*************************************************** Public Sub User_Callback_5() '计算已触发了几次 IntCount(5) = IntCount(5) + 1 '将次数显示到画面上 Form1.lblINTNo(5).Caption = CStr(Val(Form1.lblINTNo(5).Caption) + 1) '改变颜色为红色 Form1.spDI(5).FillColor = RGB(255, 0, 0) StatusDetect 5, 100 '检测状态的改变 '改变颜色为白色 Form1.spDI(5).FillColor = RGB(255, 255, 255) End Sub '*************************************************** '通道 6 产生中断时的服务子程序 '*************************************************** Public Sub User_Callback_6() '计算已触发了几次 IntCount(6) = IntCount(6) + 1 '将次数显示到画面上 Form1.lblINTNo(6).Caption = CStr(Val(Form1.lblINTNo(6).Caption) + 1) '改变颜色为红色 Form1.spDI(6).FillColor = RGB(255, 0, 0) StatusDetect 6, 100 '检测状态的改变 '改变颜色为白色 Form1.spDI(6).FillColor = RGB(255, 255, 255) End Sub '*************************************************** '通道 7 产生中断时的服务子程序 '*************************************************** Public Sub User_Callback_7() '计算已触发了几次 IntCount(7) = IntCount(7) + 1 '将次数显示到画面上 Form1.lblINTNo(7).Caption = CStr(Val(Form1.lblINTNo(7).Caption) + 1) '改变颜色为红色 Form1.spDI(7).FillColor = RGB(255, 0, 0) StatusDetect 7, 100 '检测状态的改变 '改变颜色为白色 Form1.spDI(7).FillColor = RGB(255, 255, 255) End Sub PIODIO.BAS 模块的内容和上一小节一样,请读者参考上一小 节的说明。 第 296 页,共 386 页 第 297 页,共 386 页 常问问题集(FAQ) Q:程序执行时,一直无法顺利初始化? A: 将适配卡换一个 PCI 卡槽看看,有的时候会有这个问题。 Q:725 的继电器接点可以直接接外部设备吗? A:当然可以。不过,还是要再一次查看继电器的规格和你所使用的设备是 否匹配,特别是在功率上的限制。 Q: 中断讯号是隔离好?还是不隔离好? A:如果可以采用隔离式的输入方式的话是比较好,安全一点嘛!如果只是简 单的 TTL 电平讯号的话,使用非隔离式也可以,方便一点。 Q: 中断讯号可以浮接吗? A:尽量避免浮接的情形,浮接对中断讯号来说有时会造成不确定电平,对 于程序的判读会造成影响。 第 298 页,共 386 页本章习题 1、 请说明中断程序。 2、 若激活 8 个中断,但中断子程序却少了一个,会有什么情形? 3、 设计一个 PISO-725 适配卡数字输入的项目,但使用定时器。 4、 设计一项目,PISO-725 上每个数字输入的讯号变化时,转输出 到其上的继电器通道? 第 299 页,共 386 页第七章 监控系统设计 前面的章节我们分别讨论了适配卡的基本原理和各别通道的实 验,本章中我们将结合相关卡片理论,设计一个监控系统。 7-1 被监控系统 我们已经介绍了相关的数字输出入原理,其中也包括了计时计数 的原理,活用这些类型的通道和讯号便可有效设计出数字讯号的监 控系统。我们将由各子画面设计到监控程序设计逐步完成此一监控 系统项目。透过需求的一步步解析,最后就可以定出所需要的适配 卡类型及输出入讯号的通道数。 7-1-1 系统架构 本章所欲完成的监控系统为一制造过程中的混料子系统(笔者虚 构的系统),一个配方的完成由 5 种不同的配方予以混合,并经特定 的混合比例和搅拌时间后完成。我们将全部制造过程中的一部份抽 出说明。 子系统布置及组件说明如图 7-1-1 所示。 图 7-1-1 系统各组件说明 系统每个次部份说明如下: 1、 5 种配方由图上方的 5 个桶型容器提供,每一个桶型容器上 第 300 页,共 386 页下方各有一个控制配方进出的开关;桶型容器上亦有检测桶 型容器配方量的上下限检测器。 2、 5 种配合进入大的桶型容器后便可进行搅拌,此大的桶型容 器的搅拌器由一马达负责带动,可由开关控制马达的启闭, 马达并提供一编码器之讯号输出,以便检测搅拌器之转速。 3、 大的桶型容器亦有配方量上下限检测器。 4、 大的桶型容器搅拌完成后,由供料开关的开启使得配方流向 下一个制程。 在这些要求中,首先由系统的角度分离出输出入的方向。必须被 系统量测到的参数就是输入,这些参数通常用来显示出某些数值或 状态;而须要被系统控制的参数即为输出。因此上述的子系统可以 归纳出: 1、 配方量的过低或过高,由上下限检测器提供讯号给我们的程 序,故为输入,数量为 12 个 (5 种配方槽及 1 个混料大槽, 每槽 2 个,故 12 个 )。 2、 5 个配方槽上下方均有控制配方进口及配方出口的控制开关 各一个,故为输出,数量为 10 个。 3、 搅拌马达须被控制开闭,故为输出,数量为 1 个。 4、 搅拌完成后开启供料开关送往下一个制程,故为输出,数 量 为 1 个。 5、 搅拌马达的转速需检测,故为输入,数量为 1 个。 透过输出入通道的分析,我们可以了解到此系统需要的数字输出 有 12 个,数字输入有 12 个,另外还有一个计数器的讯号(也就 是转速的讯号)。 7-1-2 监控要求及卡片选择 系统中的总配方由 5 个子配方混合而成,每一种配方的加入希 望可以达到自动化的要求,混料过程的转速也需要时常注意到,配 方的混料时间到达后即开启供料开关让配方进到下一个制程。以下 再列出系统监控应作到的几点要求: 1、 子配方的供料由计算机自动控制。 第 301 页,共 386 页2、 子配方的供料必须大过低料位,而于高料位时停止。 3、 当高料位被检测到时即关闭子配方之进料开关,并且开启出 料开关。 4、 依序完成 5 个子配方的进出料控制动作。 5、 完成子配方的进料工作后,激活搅拌马达。 6、 不断检测总配方的高低料位。 7、 将搅拌马达的转速显示到画面上。 8、 搅拌的时间达到要求后,开启供料开关让混合完成的配方进 入下一个制程。 9、 每一个过程最好以声音表现出来。 通常在写出以上的各项时,需求者一般就是将其了解的流程以 口叙的方式描述出来,我们将其流程以程序的方式表现出来。 透过以上二小节的说明,我们可以了解这样的系统所需要的是 是一个数字控制的系统,而在之前几章的说明中,数字输出入的卡 片均可以达到这些需求,如果无法以单独一片适配卡完成,也可以 组合多片适配卡来完成一个系统。由于这样的混料系统中除了开关 的控制和状态的检测外,还要一个转速的计算;如果想要以单独的 一片适配卡来完成的话,应该是选择我们在第 5 章所介绍的 TMC-12 计时计数卡—这片适配卡除了拥有计时计数功能之外,还有 16 个 通道的数字输出和 16 个通道的数字输入,这些规格正好符合这个 系统项目的要求。 选择所使用的适配卡时,不要将所有的通道都用完,至少要留 下几个空的通道供以后扩充或维修时使用,免得日后的通道数增加 或是某一个通道损坏必须更换到另一个通道时发生困扰。 第 302 页,共 386 页7-2 监控系统的画面设计 监控系统程序的设计一般的程序是由画面设计开始,当画面设计 完成后,再接下去作程序的撰写,笔者也强烈建议如此的程序。针 对上一节的系统描述,本节将对应出实验的模块及相对应的接点, 并说明设计细节。 7-2-1 系统对照图 首先考虑系统架构图及相关的要求,我们可以将原来的系统图 放置在监控画面上,目的在于让使用者可以明了所监控的系统构 造,一方面也容易明白所监控参数的所在位置。 由原来的图形已经很清楚系统设计所需要的输出入参数,为了 让使用者很清楚地了解到实际的系统配置,我们的设计就将需要的 各种显示及控制按钮放置在设备的旁边;首先利用 Visual Basic 设 计将系统的布置架构放在窗体中,如图 7-2-1 所示。 图 7-2-1 系统布置 图 7-2-1 的主要目的在于让系统很容易呈现在使用者面前,当 使用者看到此画面时,让相当于站在原来的系统面前,看着系统正 在运作一般,欲制造此种画面有几个步骤: 1、 先将系统的布置图绘出,我们称为系统原理图,读者可使用 第 303 页,共 386 页PowerPoint、 CorelDraw 或其它的绘图软件制作,或是直接 使用数字相机照下系统图片,这部份不在本书讨论范围,请 读者参考其它的绘图相关书藉。笔者使用的是 CorelDraw 先 画出其架构图,接着再转存为图片格式(*.bmp 或 *.tif),笔 者转存的画面是 640*480 的大小。(Visual Basic 上以 bmp 格 式较佳) 2、 接着在窗体上可以使用图片框或 Image 控件将这个图形加 载;将此图片框或 Image 控件的外观大小拉成和窗体一样的 大小即可。 3、 另一种方式则是直接将窗体的 Picture 属性设为该图片,如 此一来,图片将成为该窗体的底图。 笔者在此采用的是最后一种方法,也就是将窗体的底图设为该 系统架构图;另外读者也可以看到在该图的其它地方还留有一些空 白之处,这些地方是笔者预留未来摆放其它控件之用的。 7-2-2 驱动控件及显示控件 由上一节的方式制造了拥有系统架构图的窗体,接着我们再考 虑有关驱动控制以及状态显示的部份。 笔者预计使用灯号来指示出料位高低限的状态,如果进料量低 于低限或高于高限,就会在画面上显示出红色的灯号,正常情形就 显示出绿色的灯号。 其它的驱动控制部份(包括泄料开关、进料开关、马达开关)则 使用按钮;若采用程序自动检验及控制相关程序的话,当然开关的 部份也会由程序自动激活,为了区别某一个开关是否被按下,我们 必须设计将被按下的开关颜色作变更。驱动和显示的部份就作出如 图 7-2-2 的样子。 第 304 页,共 386 页 图 7-2-2 加上驱动和显示部份的画面 如何利用 Visual Basic 作出图 7-2-2 的画面供使用者参考呢?我 们以下就依各步骤予以说明: 1、 以上一小节所建立的窗体为基础,我们在该窗体中接下去 作以下的各种配置。 2、 安排一个 Label 控件,置于画面的下方,其 Caption 属性输 入 ”配方混料制程监控系统”,改变适当字型,并调整字距。 3、 安排 10 个按钮控件,它们分别置放在 5 个子配方槽旁边, 用来控制每个子配方槽的进料开关及出料开关。 4、 安排一个按钮控件,放在马达旁边,用来控制马达的激活 及关闭。 5、 安排一个按钮控件,放在混合完成的控制阀处,用来控制 将完成的混合配方送往后段制程。 6、 上述的几个按钮控件之 Picture 属性及 DownPicture 属性均 作了变更,笔者使用的是不同的渐层颜色,在一般状态呈 现绿色,而在被使用按下后就改呈现红色;这样的颜色改 变可以提醒使用者正在执行的动作为何。 7、 为了可以显示状态,先在画面上安排二个 Image 控件,其 Picture 属性加载 Green.jpg 和 Red.jpg 二张图,用来代表一 第 305 页,共 386 页般状态的绿色灯和状态改变的红色灯。由于这二个控件将 被我们作为其它灯号改变的供应控件,故将它们的 Visible 属性设为 Flase,使其于执行时期不显示。 8、 5 个子配方槽和混合槽都有高低料位的检测器,这个部份 就安排 12 个 Image 控件来表示;这些控件中的 Picture 属 性先加载表示一般状态的绿色灯;当需要改变状态时,再 将其 Picture 属性改为上项所安排的 Image 控件中的 Red 部份即可。 9、 安排一个 Label 控件于马达旁边,此控件将用来表示转速。 10、 至此算是完成第二阶段的画面设计了。 7-2-3 完成的画面 为了再加强使用者的了解,另外也要考虑到控制的需求,笔者 再增加几个控件的设计,完成此系统的画面设计如图 7-2-3。 图 7-2-3 系统画面 将以上的画面以 Visual Basic 的方式作设计时,其步骤如下: 第 306 页,共 386 页1、 依据上一小节的画面,我们接下去作其它的控件的摆放。 2、 安排 5 个 Label 控件,其 Caption 属性分别输入”配方 A”、 ”配 方 B”、”配方 C”、”配方 D”、”配方 E”,将它们分别置入 5 个子 配方槽的中间位置,这样可以帮助辨认该槽的编号。 3、 另外也安排一个 Label 控件,其 Caption 属性输入”混合槽”,置 入大槽中间作为标示之用。 4、 分别在每一个配方槽的高低料位显示位置安排 Label 控件,其 Caption 属性输入”LOW”及 ”HIGH”,用以标示高低料位显示及 其灯号。 5、 若是自动搅拌的话,还需要一个自动搅拌的时间设定,所以在 画面上安排一个文字框控件,可以让使用者输入所需要的搅拌 时间。 6、 另外安排二个按钮控件,其 Caption 属性分别输入”开始监控” 及 ”结束系统”,作为激活监控及结束系统之用。 7、 笔者也于画面上安排一个定时器控件,此控件用来执行实际的 自动控制程序之用。在 Visual Basic 中经常使用定时器来达到自 动执行程序代码的目的,因此将它放在我们的系统画面上也是 理所当然了。 至此算是完成了主画面的设计工作,这些画面上的显示可以使 用 Visual Basic 本有的画面控件就可以完成,只要了解了 Visual Basic 具有那些控件可以使用,完成这些画面就不是问题。 完成整个主控画面的制作后,其它的就要靠程序予以完成,接 下来继续讨论相关的程序作法。 第 307 页,共 386 页7-3 监控系统程序设计 上一节我们完成了系统的主画面,但尚未加入程序,接下来的部 份将继续地加入监控的程序。 7-3-1 程序设计步骤分析 先不要急着写程序,这点很重要,系统虽然大小有别,不过通 常不会有那种非常简单,而不需要思考再三的项目交到我们的手 中,所以不要期望有那种只要随便 Coding 几下就可以完成项目的机 会,还是要先思考一下如何下手比较安全,毕竟多花时间在事先的 准备及思考上,就可以少花时间在事后的修改及除错(就算是自己写 的程序,修改、除错也是相当累人的一件工作)。 先考虑监控程序的执行过程,监控程序的主要流程可以分成: 1、 子配方的料量控制。其中包括了: (1). 配方 A~配方 E 的进料控制,使配方先进入子槽。 (2). 各子配方低量检测及显示。 (3). 各子配方高量检测及显示。 (4). 配方 A~配方 E 的出料控制,使其配方进入混合槽。 2、 混合槽的料量检测及控制。其中包括了: (1). 配方的低量检测及显示。 (2). 配方的高量检测及显示。 (3). 搅拌马达的开启及关闭。 (4). 供料开关的开启及关闭。 透过上述的分析,我们可以将监控的流程分成二个部份进行, 先进行各子配方的进出料部份,再进行混合槽的搅拌及出料。 由于将透过计时计数卡作相关的控制,我们必须将各个 IO 点 和实际所需的控制点作映像,以了解到实际的输出入控制上和所使 用的接口之间的对映关系,透过这一层对映关系,我们才能将对实 际设备的操作转而对适配卡的操作,而适配卡的操作结果将会显示 到实际的设备中。 第 308 页,共 386 页先考虑数字输入,以上的分析中可以了解到各子配方的高低料 的检测需要数字输入通道(共有 10 个 ),混合槽的高低料位检测也是 数字输入通道(共有 2 个 )。全部加起来有 12 个。这部份可由数字输 入通道提供。 数字输出的部份是输出控制,各子配方的进出料开关控制是数 字输出(共有 10 个 );马达开关控制需要 1 个,供料开关需要 1 个。 数字输出的部份加起来也要 12 个。这个部份可由数字输出通道提 供。 马达转速的检测需要一个计时计数通道。这个可由计时计数通 道提供。大概的流程图表示如图 7-3-1 所示。 图 7-3-1 监控流程图 再以图 7-3-1 的各个部份分别考量后,可以再细画流程图如图 7-3-2。 图 7-3-2 更详细的流程图 流程图表示出一个完整的流程,即从子配方的料位检测开始到 最后的混合槽完成混合后供料给下一阶段的制程为止;一个流程完 成后,再开始一个新的流程,周而复始。至于 IO 输出入点的映像 部份,笔者的规画面如表 7-3-1~表 7-3-4。 第 309 页,共 386 页表 7-3-1 子配方料位输入映像 位置 IO 型态 IO 编号 配方 A 高料位 输入 DI0 配方 B 高料位 输入 DI1 配方 C 高料位 输入 DI2 配方 D 高料位 输入 DI3 配方 E 高料位 输入 DI4 配方 A 低料位 输入 DI6 配方 B 低料位 输入 DI7 配方 C 低料位 输入 DI8 配方 D 低料位 输入 DI9 配方 E 低料位 输入 DI10 表 7-3-2 子配方进出料控制映像 位置 IO 型态 IO 编号 配方 A 进料 输出 DO0 配方 B 进料 输出 DO1 配方 C 进料 输出 DO2 配方 D 进料 输出 DO3 配方 E 进料 输出 DO4 配方 A 出料 输出 DO5 配方 B 出料 输出 DO6 配方 C 出料 输出 DO7 配方 D 出料 输出 DO8 配方 E 出料 输出 DO9 表 7-3-3 混合槽料位输入映像 位置 IO 型态 IO 编号 混合槽高料位 输入 DI5 混合槽低料位 输入 DI11 表 7-3-4 其它控制映像 位置 IO 型态 IO 编号 马达开关 输出 DO10 供料开关 输出 DO11 马达转速 输入 Counter6 第 310 页,共 386 页表 7-3-1~表 7-3-3 分别由输出入的观点建立实际设备和输出入 适配卡之间的对映关系;而表 7-3-4 则除了二个输出的控制点外, 还有一个马达转速的检测使用的是计时计数器 0 作为计数的通道。 7-3-2 子配方槽的监控部份 程序的部份先进行子配方的监控部份。由流程图可以知道我们 必须先就子配方的料位检测开始,由表 7-3-1,各子配方料位检测 所使用的是数字输入通道,回顾第五章关于 TMC-12 中数字输入的 说明,数字输入通道使用的函式是 TMC12_Di,其函式原型的宣告 为 Declare Function TMC12_Di Lib "TMC12.dll" (wDiData As Integer) As Integer 此函式将传回数字输入通道的状态,透过数值的检查可以得知每一 个子配方的高低料准位的情形。 另外子配方的进出料控制则是使用数字输出,其所使用的函式 是 TMC12_Do,其函式原型的宣告为 Declare Sub TMC12_Do Lib "TMC12.dll" (ByVal wOutData As Integer) 将每个通道的代表数值输入后,即可控制该通道的输出状态。由于 TMC12 拥有 16 个数字输出通道,函式中的参数 wOutData 为整数 型态,其为 16 位数值,这 16 个位就对应到 TMC12 的 16 个数字输 出通道。数字输出通道若设为 1,则该通道 High 起来;若设为 0, 则该通道 Low 下去,将这些通道代表的 0 和 1 一一地组合起来,就 形成了所需的参数数值。 图 7-3-3 是针对和子配方槽相关的控制与检测所绘成的流程, 这个部份和 7-3-2 的左边部份一样的,笔者特别将其独立为一个子 程序,方便系统的呼叫。 第 311 页,共 386 页 图 7-3-3 子配方槽流程图 再将上述的流程图发展成子程序列于下: Function FormulaControl() As Boolean Dim i%, InputStatus As Integer '子配方出料开关关闭 DOValue = DOValue And (Not 2 ^ FA_Out_Ch) '配方 A 出料开关数值 DOValue = DOValue And (Not 2 ^ FB_Out_Ch) '配方 B 出料开关数值 DOValue = DOValue And (Not 2 ^ FC_Out_Ch) '配方 C 出料开关数值 DOValue = DOValue And (Not 2 ^ FD_Out_Ch) '配方 D 出料开关数值 DOValue = DOValue And (Not 2 ^ FE_Out_Ch) '配方 E 出料开关数值 TMC12_Do CInt("&H" & Hex(DOValue)) '关闭所有出料开关 Delay 1000 '延迟一段时间 '将输出开关上的 Command 上的 Caption 作改变,以显示状态 For i = 0 To 4 cmdOutput(i).Caption = "CLOSE" Next i '5种配方的进料开关打开 DOValue = DOValue Or 2 ^ FA_In_Ch '配方 A 的进料控制开关数值 DOValue = DOValue Or 2 ^ FB_In_Ch '配方 B 的进料控制开关数值 DOValue = DOValue Or 2 ^ FC_In_Ch '配方 C 的进料控制开关数值 DOValue = DOValue Or 2 ^ FD_In_Ch '配方 D 的进料控制开关数值 DOValue = DOValue Or 2 ^ FE_In_Ch '配方 E 的进料控制开关数值 '实际的数字输出 TMC12_Do CInt("&H" & Hex(DOValue)) Delay 1000 '延迟一段时间 '将开关上的 Command 上的 Caption 作改变,以显示状态 For i = 0 To 4 cmdInput(i).Caption = "OPEN" Next i '监测高料准位是否达到 Do wRtn = TMC12_Di(InputStatus) '取得数字输入的状态 wRtn = CheckLOStatus(InputStatus) '检查低料位状态 wRtn = CheckHIStatus(InputStatus) '检查高料位状态 第 312 页,共 386 页 '若高料准位已达,该通道的输出要关闭 If fHiState(0) Then DOValue = DOValue And (Not 2 ^ FA_In_Ch) cmdInput(0).Caption = "CLOSE" End If If fHiState(1) Then DOValue = DOValue And (Not 2 ^ FB_In_Ch) cmdInput(1).Caption = "CLOSE" End If If fHiState(2) Then DOValue = DOValue And (Not 2 ^ FC_In_Ch) cmdInput(2).Caption = "CLOSE" End If If fHiState(3) Then DOValue = DOValue And (Not 2 ^ FD_In_Ch) cmdInput(3).Caption = "CLOSE" End If If fHiState(4) Then DOValue = DOValue And (Not 2 ^ FE_In_Ch) cmdInput(4).Caption = "CLOSE" End If '再作一次实际的数字输出,以关闭已达高料位的配方槽 TMC12_Do CInt("&H" & Hex(DOValue)) '检查是否所有的子配方都已达到高料位 bAllHI = True For i = 0 To 4 If Not fHiState(i) Then bAllHI = False '若还有未达高料位的,就设为 False End If Next i DoEvents Loop Until bAllHI FormulaControl = bAllHI End Function 程序中引用了常数定义,例如 FA_In_Ch 表示子配方 A 的进料 开关通道号码,其余的常数定义也是类似的,其目的在于方便记忆 及修改,只要在定义的模块内修改,程序中他引用此定义的地方就 可以在不须修改程序的情形下照样使用。 画面上的子配方槽有相对应的按钮可以用来各别控制进出料 开关,当程序进行的过程中,藉由改变这些按钮的 Caption 属性, 可以达到告知的目的,故在程序执行时,这些按钮上会出 现 ”CLOSE”或 ”OPEN”的文字,其目的即在显示目前的状态,其它 的各子程序部份也有这样的设计。 在上段的程序中有二个检查高低料位状态的子程序 (CheckLOStatus 及 CheckHIStatus),透过检查数字输入数值的状态就 可以得知各配方高低料位的状态,其子程序的内容如下: Function CheckLOStatus(InputStatus As Integer) As Integer fLoState(0) = IIf((InputStatus And 2 ^ FA_LO_Ch) <> 0, True, False) '比对配方 A 低料状态 第 313 页,共 386 页 fLoState(1) = IIf((InputStatus And 2 ^ FB_LO_Ch) <> 0, True, False) '比对配方 B 低料状态 fLoState(2) = IIf((InputStatus And 2 ^ FC_LO_Ch) <> 0, True, False) '比对配方 C 低料状态 fLoState(3) = IIf((InputStatus And 2 ^ FD_LO_Ch) <> 0, True, False) '比对配方 D 低料状态 fLoState(4) = IIf((InputStatus And 2 ^ FE_LO_Ch) <> 0, True, False) '比对配方 E 低料状态 '若低料准位已达,该通道的红色灯号要亮起 If fLoState(0) Then imgLo(0).Picture = imgRed.Picture '红灯亮起 Else imgLo(0).Picture = imgGreen.Picture '绿灯亮起 End If If fLoState(1) Then imgLo(1).Picture = imgRed.Picture '红灯亮起 Else imgLo(1).Picture = imgGreen.Picture '绿灯亮起 End If If fLoState(2) Then imgLo(2).Picture = imgRed.Picture '红灯亮起 Else imgLo(2).Picture = imgGreen.Picture '绿灯亮起 End If If fLoState(3) Then imgLo(3).Picture = imgRed.Picture '红灯亮起 Else imgLo(3).Picture = imgGreen.Picture '绿灯亮起 End If If fLoState(4) Then imgLo(4).Picture = imgRed.Picture '红灯亮起 Else imgLo(4).Picture = imgGreen.Picture '绿灯亮起 End If End Function Function CheckHIStatus(InputStatus As Integer) As Integer fHiState(0) = IIf((InputStatus And 2 ^ FA_HI_Ch) <> 0, True, False) '比对配方 A 高料状态 fHiState(1) = IIf((InputStatus And 2 ^ FB_HI_Ch) <> 0, True, False) '比对配方 B 高料状态 fHiState(2) = IIf((InputStatus And 2 ^ FC_HI_Ch) <> 0, True, False) '比对配方 C 高料状态 fHiState(3) = IIf((InputStatus And 2 ^ FD_HI_Ch) <> 0, True, False) '比对配方 D 高料状态 fHiState(4) = IIf((InputStatus And 2 ^ FE_HI_Ch) <> 0, True, False) '比对配方 E 高料状态 '依其状态改变灯号 If fHiState(0) Then imgHI(0).Picture = imgRed.Picture '红灯亮起 Else imgHI(0).Picture = imgGreen.Picture '绿灯亮起 End If If fHiState(1) Then imgHI(1).Picture = imgRed.Picture '红灯亮起 Else imgHI(1).Picture = imgGreen.Picture '绿灯亮起 第 314 页,共 386 页 End If If fHiState(2) Then imgHI(2).Picture = imgRed.Picture '红灯亮起 Else imgHI(2).Picture = imgGreen.Picture '绿灯亮起 End If If fHiState(3) Then imgHI(3).Picture = imgRed.Picture '红灯亮起 Else imgHI(3).Picture = imgGreen.Picture '绿灯亮起 End If If fHiState(4) Then imgHI(4).Picture = imgRed.Picture '红灯亮起 Else imgHI(4).Picture = imgGreen.Picture '绿灯亮起 End If End Function 除了检查高低料位的状态外,也会将其所属的灯号作相对应的 变更,使观看者由画面就可以知道各子配方的料位状态。 程序使用常数可以使得程序的阅读变得容易,常数定义通常在 模块内,以下是本章所使用的常数定义部份 '************************************ '以下是各 IO 映像的常数定义 '************************************ '各子配方的高料位通道 Public Const FA_HI_Ch = 0 Public Const FB_HI_Ch = 1 Public Const FC_HI_Ch = 2 Public Const FD_HI_Ch = 3 Public Const FE_HI_Ch = 4 '各子配方的低料位通道 Public Const FA_LO_Ch = 6 Public Const FB_LO_Ch = 7 Public Const FC_LO_Ch = 8 Public Const FD_LO_Ch = 9 Public Const FE_LO_Ch = 10 '混合槽高低料位通道 Public Const Mix_HI_Ch = 5 Public Const Mix_LO_Ch = 11 '各子配方的进料控制通道 Public Const FA_In_Ch = 0 Public Const FB_In_Ch = 1 Public Const FC_In_Ch = 2 Public Const FD_In_Ch = 3 Public Const FE_In_Ch = 4 '各子配方的出料控制通道 Public Const FA_Out_Ch = 5 Public Const FB_Out_Ch = 6 Public Const FC_Out_Ch = 7 Public Const FD_Out_Ch = 8 Public Const FE_Out_Ch = 9 '其它控制 Public Const Motor_Ch = 10 第 315 页,共 386 页Public Const Output_Ch = 11 Public Const Motor_Speed_Ch = 6 '第 6 个 CLK 输入通道 Public Const BaseSec = 1.15740695036948E-05 '1 秒钟以数值表示 系统所使用的通道全部都定义在以上的内容中,如果需要在设 计的过程中改变所使用的通道号码的话,直接在模块里修改以上的 内容即可;如果项目里面有很多地方都会使用到相同的变量时,使 用常数定义的方式只要在定义处作修改,项目其它地方就会一起改 变;如此一来,当我们需要修改某一个通道的对应值时,就不必在 项目中的所有地方都作变更,而只要改一个地方就好了。试想, 如 果那一天需要变更数字输出通道的号码,由原来的 DO1 变更为 DO14,我们只需要改变定义中的号码就可以了,无需所有的地方都 改过,是不是比较方便呢?! 控制方式: 系统中的开关是透过 TMC12 的数字输出功能达到,在使用 TMC12_Do 函式将实际的输出数值作输出之前必须考量到如何写 出所对应的通道之数值,以下是程序的片断 DOValue = DOValue Or 2 ^ FA_In_Ch '配方 A 的进料控制开关数值 …… DOValue = DOValue And (Not 2 ^ FE_Out_Ch) '配方 E 出料开关数值 …… TMC12_Do CInt("&H" & Hex(DOValue)) '关闭所有出料开关 Delay 1000 '延迟一段时间 上段程序包含有将配方 A 的进料通道(FA_In_Ch)High 起来的数值 计算、配方 E 出料通道(FE_Out_Ch)Low 下去、实际的 Output 函式 及延迟函式,当我们要实际控制时,这几个程序必须要了解。 首先是某一个通道 High 起来时的数值写法。当我们要让某一 个通道 High 起来,必须将该通道的数值写为 1;当欲使某一通道 Low 下去,则写为 0。将某一个通道 High 起来还要考虑到不能影响 到原来的其它数值,这时我们要使用 Or 运算写出实际的控制输出 的数值,如图 7-3-4 所示,图中显示出 Ch Set 的 Bit1 的部份(其数 值为 1)是我们要改变的通道,再使用 Or 运算后,求出的 DoValue 数值不会影响其它的通道,并且也达到改变某一通道状态的目的。 图 7-3-4 Or 运算可以不影响其它的通道 第 316 页,共 386 页另外就是希望将某一个通道的状态 Low 下来时应作的运算部 份;再依图 7-3-4 的情形来看,Bit1 已经是 High 起来,如果我们只 想针对此位作 Low 下去的动作的话,在程序上的作法是先作 Not 运算,再作 And 运算,如图 7-3-5 所示;图中先将位 1 的部份写出, 再使用 Not 运算将其反相,接着使用 And 运算后就可以得到位 1 被 Low 下去的结果。 图 7-3-5 将某一位 Low 下来的方法 当欲送出控制的数值时,程序中并不是直接送出该通道的 1 或 0 的数值,而是运算后取得 DOValue 数值,才使用 TMC12_Do 函式 送出;这样作的原因是因 TMC12 的数字输出通道上可能还有其它 的通道正进行输出动作(通常是如此),我们的动作不可以干扰其它 通道的动作,因此必须使用上述的运算方式改变我们有兴趣的通 道,而保留其它通道的状态。 一旦送出控制的数值后,程序中还延迟了一小段时间,因为程 序执行的速度很快,程序执行到了,最后输出的机械动作可能还没 反应过来;所以设计上会有一小段时间的延迟,以便让机械有时间 反应。程序执行完毕后,不见得最后的控制结果也是 OK 了 —因为 机械动作会比较慢反应出来。 在 Or 运算中,0 或 1 与 1 作 Or 的话,其结果值一定是 1; 而在 And 运算中,0 或 1 与 0 作 And 的话,其结果值一定是 0。以 上程序中的 DOValue 运算就是利用这样的原理。 程序中针对欲输出的数值并不是直接以 Long 的数值型态作输 出,而是将其转换为十六进制的格式后,再转换为 Int 的型态后输 出;其原因在于 TMC12_Do 函式中的参数是整数型态,但是数字输 出的通道有十六个,如果用整数来计算的话,在最高位的计算将会 导致溢位的错误。C 语言中的整数的宣告可将其宣告为无号数型态, 但 Visual Basic 中的整数没有无号数型态,为了避开这种困扰,只 好使用较高范围的型态变量记录数值,所以程序中使用 Long 型态 记录;数值的计算结果出来后,欲使用 TMC12_Do 函式作输出时, 还要将其转换回来,因此使用 Hex 函式将其转换为十六进制,再使 第 317 页,共 386 页用 Cint 函式限制其范围。这样的作法一方面可以使得程序中的计算 不会溢位,一方面在实际输出时,再转换回原来的宣告格式即可。 状态比较: 透过 TMC12_Di 取得数字输入的状态后,为了对应所欲了解的 输入情形,使用了以下的程序片断 fHiState(0) = IIf((InputStatus And 2 ^ FA_HI_Ch) <> 0, True, False) IIF 函式是 Visual Basic 所提供的函式,此函式有三个参数,第一个 参数为表达式,当此表达式的结果为真时,将第二个参数传回,而 当表达式为假时,则将第三个参数传回。因此在上述的式子里,如 果 InputStatus 和 FA_HI_Ch 通道的数值作 And 运算,若为真,则 fHiState(0)为 True,否则 fHiState(0)为 False。 此部份的函式执行完毕后,接下来是进行混合槽的程序撰写 了。 7-3-3 混合槽的监控部份 各子配方槽的控制完成后,各配方会开始流入混合槽,混合槽 中的相关监控和子配方槽的部份是差不多的,其流程如图 7-3-6。 此部份主要是检测混合槽是否已达高料位。 图 7-3-6 混合槽流程图 再将上述的流程图发展成一个子程序列于下: Function MainTank(WaitTime As Integer) As Boolean Dim wRtn As Integer, InputStatus As Integer Dim i As Integer Dim DT As Long '若已全部达到高料位,准备进入混合槽 '先关闭供料开关 第 318 页,共 386 页 DOValue = DOValue And 2 ^ Output_Ch TMC12_Do CInt("&H" & Hex(DOValue)) cmdNextStep.Caption = "CLOSE" '标示为关闭 '监测混合槽高料准位是否达到 wRtn = TMC12_Di(InputStatus) '取得数字输入的状态 wRtn = CheckMainHILOStatus(InputStatus) '检查混合槽高低料位 '若已高料位,则跳出,不需再作 If fMainHiState Then MainTank = True Exit Function End If '开启进料开关,使其全部进入混合槽 DOValue = DOValue Or 2 ^ FA_Out_Ch DOValue = DOValue Or 2 ^ FB_Out_Ch DOValue = DOValue Or 2 ^ FC_Out_Ch DOValue = DOValue Or 2 ^ FD_Out_Ch DOValue = DOValue Or 2 ^ FE_Out_Ch TMC12_Do CInt("&H" & Hex(DOValue)) '实际的输出 '将输出开关上的 Command 上的 Caption 作改变,以显示状态 For i = 0 To 4 cmdOutput(i).Caption = "OPEN" Next i DoEvents '监测混合槽高料准位是否达到 DT = GetTickCount MainTank = False '先假设动作失败 Do wRtn = TMC12_Di(InputStatus) '取得数字输入的状态 '判断料位状态,并熄灭料位灯号 wRtn = CheckHIStatus(InputStatus) wRtn = CheckLOStatus(InputStatus) wRtn = CheckMainHILOStatus(InputStatus) DoEvents '若已高料位,则跳出 If fMainHiState Then CloseFormulaIn '关闭子配方槽的出料开关 MainTank = True Exit Do '表示动作完成 End If If GetTickCount - DT <= 0 Then DT = GetTickCount Loop Until (GetTickCount - DT) / 1000 >= WaitTime End Function 此段程序依然使用 TMC12_Do 及 TMC12_Di 二组函式,藉以控 制输出,以及取得数字输入的情形;为了取得数字输入的状态,程 序中使用一段 Do… Loop 循环不断地作检测,直到检测到混合槽的 高料准位讯号已经出现为止,此时也表示此子程序可以结束。 程序中的料位检测子程序除了先前提到的 CheckHIStatus 和 CheckLOStatus 之外,另外还有一个检测混合槽料位状态的 CheckMainHILOStatus 函式,此函式也是计算数字输入之数值,藉 以了解料位状态,其内容如下 Function CheckMainHILOStatus(InputStatus As Integer) As Integer 第 319 页,共 386 页 fMainHiState = IIf((InputStatus And 2 ^ Mix_HI_Ch) <> 0, True, False) '比对混合槽高料状态 fMainLoState = IIf((InputStatus And 2 ^ Mix_LO_Ch) <> 0, True, False) '比对混合槽高料状态 '若低料准位已达,该通道的红色灯号要亮起 If fMainHiState Then imgHI(5).Picture = imgRed.Picture '红灯亮起 Else imgHI(5).Picture = imgGreen.Picture '绿灯亮起 End If If fMainLoState Then imgLo(5).Picture = imgRed.Picture '红灯亮起 Else imgLo(5).Picture = imgGreen.Picture '绿灯亮起 End If End Function 此段程序中检测到料位的状态后,也会同时改变代表的旗标 (fMainHiState、fMainLoState)及画面上的灯号,观看者可藉以了解料 位的状态。 7-3-4 搅拌及供料流程控制 在子配方进到混合槽后,接下来的动作是开始搅拌及搅拌完成 后的供料动作,这部份的流程图如图 7-3-7 所示,此部份的流程图 实际上就是图 7-3-2 的右边部份的流程图。 图 7-3-7 搅拌及供料流程图 流程图可以看出所处理的是在混合槽料位的监视、马达搅拌及 最后的供料过程。以上的流程图所发展的子程序列于下: Function FormulaOutput() As Boolean Dim wCount As Long 第 320 页,共 386 页 Dim StartTime As Double, EndTime As Double Dim mt As Single Dim wRtn As Integer, InputStatus As Integer '开启马达的控制通道 DOValue = DOValue Or 2 ^ Motor_Ch '实际的马达控制 TMC12_Do CInt("&H" & Hex(DOValue)) cmdMotor.Caption = "Running..." Delay 1000 '开始计算搅拌时间是否到达 StartTime = CDbl(Now) MixTime = CLng(Val(txtMixTime.Text) * 60) '混合的秒数 EndTime = StartTime + BaseSec * MixTime '应结束的时间 Do '设定马达转速显示的撷取通道 '采用第五章的相关技术即可达到(频率量测) TMC12_Select8254 Int((Motor_Speed_Ch - 1) / 3) ' 选择第 1 颗 8254,逻辑上是 Counter4~6 TMC12_CounterSet 1, &H70, &HFFFF ' Counter-1, mode=0, down count ffff TMC12_CounterSet 2, &HB0, &HFFFF ' Counter-2, mode=0, down count ffff '开始计数后,数值会变化 DoEvents Do If ((TMC12_CounterRead(1, &HE4) And &H80) <> 0) Then Exit Do End If DoEvents Loop '此时可以读回计数值,并作计算 wCount = TMC12_CounterRead(2, &H80) wCount = (65535 - wCount) + 2 mt = 40.96 / wCount '单位计数值 '以每转 2000Pulse 的编码器连接马达,故除以 2000 MotorSpeed = CLng(1000 / mt * 60 / 2000) '将频率转换为转速 lblRPM.Caption = Format(MotorSpeed, "0") & "rpm" DoEvents '这里可以再作其它的事情 Loop Until CDbl(Now) >= EndTime cmdMotor.Caption = "STOP" '时间到达后关闭关马达,开启供料开关 DOValue = DOValue And (Not 2 ^ Motor_Ch) TMC12_Do DOValue Delay 1000 DOValue = DOValue Or (2 ^ Output_Ch) cmdNextStep.Caption = "OPEN" TMC12_Do DOValue '开始检测混合槽的低位检测 Do wRtn = TMC12_Di(InputStatus) '取得数字输入的状态 wRtn = CheckMainHILOStatus(InputStatus) '检查混合槽高低料 位 DoEvents '若已低过低料位,则跳出 If Not fMainLoState Then Exit Do Loop 第 321 页,共 386 页 '关闭供料开关 DOValue = DOValue And (Not 2 ^ Output_Ch) TMC12_Do DOValue cmdNextStep.Caption = "CLOSE" '至此完成一个循环的控制 FormulaOutput = True End Function 此段程序除了利用 TMC12_Do 及 TMC12_Di 之外,还有转速的 检测部份,在第五章讨论到计时计数卡的时候也曾针对频率的计算 作过项目讨论,频率和转速其实是一样的东西,频率(每秒钟的次数) 等于转速(每分钟的转数)乘上 60,所以我们可以利用和求取频率一 样的方式计算出转速。由于转速的计算通常会采用编码器接在转动 轴上,透过编码器的方波输出而得到转数,一般的编码会有每转 2000 个方波产生(还有其它不同输出数),系统假设采用此种编码器, 故求出的数值需再除以 2000,以还原出真正的转速。 时间的等待: 日期及时间有其特殊的表示方法,其型态是 Date,在 Visual Basic 中可以直接宣告一个 Date 型态的变量;在程序的执行过程中, 如果要计算时间的差距也有其函式可以使用,例如 DateDiff 函式。 我们的程序中将原来的Date型态变量转换为以Double 表示的数值, 这样可以方便我们将数值拿来作其它的运算。将一个 Date 型态的 变量以 Double 表示的方法就是使用 CDbl 函式即可。 主画面上有一个文字框控件可以让我们输入所需要的搅拌时 间,它是以分钟为单位;在上述的程序中,一开始计算时间前,使 用了 StartTime = CDbl(Now) Now 函式会传回现在的系统时间值,再加上 CDbl 函式的话,会把 传回的时间值以双精度的变量作记录,此值将当成开始搅拌的时间 起点,并且计算出所需等待的秒数,再加上起始的时间,就是最后 搅拌应结束的时间(如下列程序) MixTime = CLng(Val(txtMixTime.Text) * 60) '混合的秒数 EndTime = StartTime + BaseSec * MixTime '应结束的时间 BaseSec 是双精度的数值描述秒数,其数值如模块中的定义一般, 我们可以在 Visual Basic 的实时运算窗口中找出每隔一秒钟所对应 的双精度值,即可找出 BaseSec 的数值,如图 7-3-8 所示,太小的 部份会不一样,但已精确到小数点下 10 位。 第 322 页,共 386 页 图 7-3-8 1 秒钟的 Double 值 接下来的 Do… Loop 程序只有等待时间的到来,循环中不断地 检查现在的时间是否已达到该结束的时间,若已达时间就结束此循 环;在此循环中由于没有其它的动作在进行,通常我们也可以安排 一些显示讯息的程序代码,让观看者知道现在已执行到那一个步骤 了。 Do DoEvents …转速计算及等待… Loop Until CDbl(Now) >= EndTime 搅拌的时间到了以后,接下来的程序用来关闭搅拌马达,并且 开启供料的开关,让混合完成的配方进到下一个制程;这段程序里 也有和 TMC12_Do 相关的部份,而且也利用 Do… Loop 循环检查在 混合槽内的配方量是否已达低料量的位置,若是已达此位置表示这 些配方已进到下一段的制程中,此时也就可以关闭供料开关。 程序执行至此,一个环循就此完成,若是要接下去再作的话, 就是再把之前的步骤再作一次,让程序再次执行即可。 7-3-5 整体讨论 主要的各项工作均已发展得差不多了,本节还要将这些分散的 部份作组合,使其成为一个完整的项目程序。 系统初始化 项目一开始通常是以窗体的 Form_Load 事件开始执行(也可以 使用 Main 函式开始执行),因此在其事件程序中可写入以下的程序 代码 第 323 页,共 386 页 Dim i% Dim wTotalBoards As Integer '设定各按钮及显示灯的说明文字 For i = 0 To 4 cmdInput(i).ToolTipText = "配方" & Chr(65 + i) & "进料控制开关" cmdOutput(i).ToolTipText = "配方" & Chr(65 + i) & "出料控制开关 " imgHI(i).ToolTipText = "配方" & Chr(65 + i) & "之高位指示灯" imgLo(i).ToolTipText = "配方" & Chr(65 + i) & "之低位指示灯" Next i '其它控件说明文字 cmdNextStep.ToolTipText = "完成配方输出控制开关" imgHI(5).ToolTipText = "完成配方之高位指示灯" imgLo(5).ToolTipText = "完成配方之低位指示灯" cmdMotor.ToolTipText = "马达控制开关" lblRPM.ToolTipText = "转速指示器" '加载驱动程序 If TMC12_DriverInit(wTotalBoards) <> TMC12_NoError Then MsgBox "驱动程序错误!", vbCritical + vbOKOnly, "错误" End If '激活卡片 If TMC12_ActiveBoard(0) <> TMC12_NoError Then MsgBox "TMC12 激活失败", vbCritical + vbOKOnly, "Formula Mix Control" Exit Sub End If '所有的输出均关闭 TMC12_Do 0 项目画面上有许多的控件,这些控件在原始设计者看来也许一 目了然,但是其它的操作工程师可能就不是很清楚了,这时,在画 面上如果可以出现简要的说明,将可有效地帮助工程师了解每个控 件的作用及其所代表的意义。上段在窗体 Load 事件所写的程序就 是每个控件的 ToolTipText 属性的设定,此属性会使得该控件在鼠 标在控制上停留 0.5 秒后,自动出现一个小小的说明文字框,用以 帮助操作工程师了解现在鼠标所指的控件用途。 接着在程序的后段即是加载驱动程序及激活卡片,卡片激活成 功后即可进行其它的后续工作。 按钮的个别控制 各个按钮用来控制诸如进料、出料、马达等等动作的进行,但 项目的设计也必须考虑到各个按钮的个别控制问题。很有可能在未 执行自动监控时,工程师想个别控制其中的某个或某些个子配方槽 的开关,使其可以进料、出料;亦或是某些突然的情形发生,使得 整个系统停摆,这时也必须有个方法可以让工程师以手动的方式先 行排除掉系统的问题;而由于整个配方调配的过程是自动化处理, 如果突然停止的话,也会有一些配方还留在槽内,以正常的程序来 第 324 页,共 386 页说,这些残余的配方也必须将其清除,以利后续重新开始的自动化 处理程序。 首先来看看各子配方槽的进料控制部份,在代表其控制的按钮 上双击,即可进入程序代码编辑窗口(如图 7-3-9),预设的事件程序 是 Click 事件。 图 7-3-9 进料控制钮双击 不过,笔者在此不使用此程序,而是要编辑另外一个事件 —MouseDown,读者可在程序代码编辑窗口中点选右上方的事件选 择下拉表中取得 MouseDown 的事件,整个的事件撰写如下 Private Sub cmdInput_MouseDown(Index As Integer, Button As Integer, Shift As Integer, X As Single, Y As Single) If Button = vbLeftButton Then '表示开启进料开关 Select Case Index Case 0 '配方 A DOValue = DOValue Or 2 ^ FA_In_Ch '使用 or 避开叠加 Case 1 '配方 B DOValue = DOValue Or 2 ^ FB_In_Ch '使用 or 避开叠加 Case 2 '配方 C DOValue = DOValue Or 2 ^ FC_In_Ch '使用 or 避开叠加 Case 3 '配方 D DOValue = DOValue Or 2 ^ FD_In_Ch '使用 or 避开叠加 Case 4 '配方 E DOValue = DOValue Or 2 ^ FE_In_Ch '使用 or 避开叠加 End Select '实际的控制动作,并将旗标激活 TMC12_Do CInt("&H" & Hex(DOValue)) fFormulaIn(Index) = True End Sub 为了将各子配方槽进料开关打开,故使用和之前一样的方式,用 Or 运算求出实际的数字输出控制的数值,再将此数值以 TMC12_Do 函式送出。 另外就是 MouseUp 的事件程序部份了,此部和 MouseDown 是 一组的,通常在 MouseDown 所作的程序会在 MouseUp 的时候将其 还原,程序如下 第 325 页,共 386 页Private Sub cmdInput_MouseUp(Index As Integer, Button As Integer, Shift As Integer, X As Single, Y As Single) '表示关闭进料开关 If Button = vbLeftButton Then Select Case Index Case 0 '配方 A DOValue = DOValue And (Not 2 ^ FA_In_Ch) '先 Not,再 And Case 1 '配方 B DOValue = DOValue And (Not 2 ^ FB_In_Ch) '先 Not,再 And Case 2 '配方 C DOValue = DOValue And (Not 2 ^ FC_In_Ch) '先 Not,再 And Case 3 '配方 D DOValue = DOValue And (Not 2 ^ FD_In_Ch) '先 Not,再 And Case 4 '配方 E DOValue = DOValue And (Not 2 ^ FE_In_Ch) '先 Not,再 And End Select '实际的控制动作,并将旗标激活 TMC12_Do CInt("&H" & Hex(DOValue)) fFormulaIn(Index) = False End If End Sub 在上段的程序中使用的也是之前说明过的方式,将某一个通道 的数值先作 Not 运算,再作 And 运算,将该通道所代表的位设为 0, 使其输出 Low 下来,最后也是利用 TMC12_Do 函式将数值输出到 适配卡上。 为什么使用的是 MouseDown 和 MouseUp 而不是 Click 事件呢? 平常不都是处理 Click 事件吗? 的确,在一般的情形下都是使用 Click 事件程序,但此 Click 程序是在压下后开始执行其内部的程序 代码,放开按钮后,其内部的程序代码还是继续在执行,如果原来 在其内部有输出工作的话,即使放开按钮,其输出的工作仍会继续 执行,那停止数字输出的工作应该在那里执行呢? 显然单单使用 Click 是不够的! 再考虑其它的情形,如果今天控制的是马达的运动,当按钮按 下后会使得马达带动一个设备的移动,如果今天设计的方式是按一 下按钮,马达就开始带动设备的移动,而再按一下就停止带动设备 移动的话,操作员在按下按钮后,临时被叫开了,而忘了再次按下 按钮,那不是造成设备的一直移动吗?!读者想想看,一直移动的设 备会有如何的下场?笔者想,应该是撞机吧! 而 MouseDown 和 MouseUp 触发的情形是如何呢?当鼠标在按钮 按下时,即会触发 MouseDown 事件,所以系统将会执行其内的程 序代码;而当鼠标放开时,该按钮即会浮起,此时会触发 MouseUp 事件,此事件内的程序代码就会被执行。将这二个事件结合起来, 当按下按钮时作数字输出的动作,而放开鼠标时,取消数字输出的 第 326 页,共 386 页动作,即可成功地控制某一个配方的出料动作,也不会发生按下去 后忘了将其数字输出取消的情形。最后就是使得按下鼠标时激活开 关,而放开鼠标时关闭开关。 程序也可以看到笔者特别针对鼠标的操作中只对 vbLeftButton 作处理,程序处理的前提在于判断到使用者所按下的是鼠标的左 键,否则程序是不会执行的!这个判断可以避开使用者按到其它的鼠 标键所造成的误动作。 同样的理由,我们也可以针对各子配方的出料控制作个别的控 制,而且也是使用 MouseDown 和 MouseUp 二个事件程序的组合, 在设计环境下,点选画面上的出料按钮,即可进入程序代码编辑窗 口,如图 7-3-10。 图 7-3-10 双击出料按钮可以进入程序代码编辑窗口 其 MouseDown 程序的内容如下: Private Sub cmdOutput_MouseDown(Index As Integer, Button As Integer, Shift As Integer, X As Single, Y As Single) If Button = vbLeftButton Then '表示开启出料开关 Select Case Index Case 0 '配方 A DOValue = DOValue Or 2 ^ FA_Out_Ch '使用 or 避开叠加 Case 1 '配方 B DOValue = DOValue Or 2 ^ FB_Out_Ch '使用 or 避开叠加 Case 2 '配方 C DOValue = DOValue Or 2 ^ FC_Out_Ch '使用 or 避开叠加 Case 3 '配方 D DOValue = DOValue Or 2 ^ FD_Out_Ch '使用 or 避开叠加 Case 4 '配方 E DOValue = DOValue Or 2 ^ FE_Out_Ch '使用 or 避开叠加 End Select TMC12_Do CInt("&H" & Hex(DOValue)) fFormulaOut(Index) = True End If End Sub 而 MouseUp 的内容则如下: 第 327 页,共 386 页Private Sub cmdOutput_MouseUp(Index As Integer, Button As Integer, Shift As Integer, X As Single, Y As Single) If Button = vbLeftButton Then '表示关闭 出料开关 Select Case Index Case 0 '配方 A DOValue = DOValue And (Not 2 ^ FA_Out_Ch) '先 Not,再 And Case 1 '配方 B DOValue = DOValue And (Not 2 ^ FB_Out_Ch) '先 Not,再 And Case 2 '配方 C DOValue = DOValue And (Not 2 ^ FC_Out_Ch) '先 Not,再 And Case 3 '配方 D DOValue = DOValue And (Not 2 ^ FD_Out_Ch) '先 Not,再 And Case 4 '配方 E DOValue = DOValue And (Not 2 ^ FE_Out_Ch) '先 Not,再 And End Select TMC12_Do CInt("&H" & Hex(DOValue)) fFormulaOut(Index) = False End If End Sub 除了各子配方槽进出料开关控制外,其它的部份也要考虑到, 这里包括了马达开关的各别控制,其程序代码如下 Private Sub cmdMotor_Click() '改变马达的开关状态 fMotorOn = Not fMotorOn '开启马达的控制通道 If fMotorOn Then '若开启马达,则… DOValue = DOValue Or 2 ^ Motor_Ch Else '若关闭马达,则… DOValue = DOValue And (Not 2 ^ Motor_Ch) End If '实际的马达控制 TMC12_Do CInt("&H" & Hex(DOValue)) End Sub 此段程序一开始使用旗标记录马达开关的状态,随后以此状态 来确定是否开启马达,或是关闭马达;开启与关闭数值的程序技巧 和前面的讨论一样。 供料开关独立控制的程序代码则如下 Private Sub cmdNextStep_Click() '改变开关状态 fNextStep = Not fNextStep '开启马达的控制通道 If fNextStep Then '若开启马达,则… DOValue = DOValue Or 2 ^ Output_Ch Else '若关闭马达,则… DOValue = DOValue And (Not 2 ^ Output_Ch) End If '实际的马达控制 TMC12_Do CInt("&H" & Hex(DOValue)) End Sub 第 328 页,共 386 页此段程序代码和马达开关的控制类似,都是以旗标达到改变状 态,再透过旗标状态的检查决定应如何对供料开关作控制。 程序的组合 通常在 Visual Basic 程序中需要使用到不间断,或是固定时间 执行周期动作的情形下,使用 Timer 控件是相当普遍的作法,而此 控件也的确提供了不少的便利性。在我们这个项目中也将使用这个 控件,使其不断地执行自动化的监控。 我们已经在之前的各小节中发展了各函式,现在必须将这些程 序组合起来,我们在定时器的 Timer 事件中写入以下的程序代码 Private Sub Timer1_Timer() If Not fMonitor Then Exit Sub '若未激活监控,则跳出循环 '以下是自动监控的流程 FormulaControl '子配方的控制 If Not MainTank(8000) Then '表示过程有问题,应予以警告 MsgBox "混合槽经过了设定的时间仍未达到正常准位,请检查!" fMonitor = False '暂停自动监控流程 Exit Sub End If FormulaOutput '搅拌及供料的流程 End Sub 程序非常地短,均使用子程序,而且也都是我们在前几小节发展的 子程序,当定时器被激活后这些子程序即会被执行,而之前讨论的 各结果也就一一地展现出来,上述的这段程序代码执行一次就是完 整的进料、出料、搅拌及供料等一连串的过程。 定时器的激活可以使用一个按钮来控制,因此在画面上可设计 一个按钮作为切换定时器激活及关闭的工具,也就是主画面上的 「开始监控」这个按钮控件;在该按钮之 Click 事件中可输入以下 的程序代码 fMonitor = Not fMonitor If fMonitor Then Command1.Caption = "停止监控" Else Command1.Caption = "开始监控" End If 该程序片段只有数行,透过旗标的属性变化,便可控制定时器内的 程序代码是否继续执行。由于定时器一旦执行,会将所有的过程执 行一次,如果我们在定时器内部的程序代码执行的过程中停止监控 的动作(按下按钮),虽然画面上已出现了「开始监控」的按钮文字 第 329 页,共 386 页了,但是还是要等待定时器内的程序完全执行完毕后才会真正地停 止。 程序的执行 开始程序的执行之前必须先将各实验的线路接妥,我们将使用 到的是 TMC12 的计时计数卡,其上的配备是一个 37 脚位的接头及 2 个 20Pin 的接头,它们被分别用来和转速、数字输出、数字输入 讯号作连接。笔者使用的是具隔离效果的 DB-16P(16 通道隔离式数 字输入板)、 DB-16R(16 通道继电器输出板)及用来连接编码器输出 讯号的 37Pin 端子台。在系统直接应用到真实的对象前,最好先行 测试一下,看看是否可以如预期般地运作,免得大费周章地架设了 设备而程序却不听使唤,笔者所使用的整个测试卡片环境如图 7-3-11 所示。 图 7-3-11 监控系统所使用的外接延伸板 在图 7-3-11 中所使用的设备中,有关的输入讯号的检测上,笔 者使用的是二片另外作的摇头开关板,藉以控制数字输入板上的状 态,如此可以仿真出各子配方槽及混合槽中的料位高低的讯号。数 字输出所使用的继电器板则无需连接其它的配备,只要板子上的灯 号亮起就表示我们的控制动作是正常的。 至于转速讯号的来源,笔者使用讯号产生器(Function Generator),产生的波形就仿真编码器所产生的方波;整个连接起 来就完成了所需的实验配备,这样的配备如果可以实验成功的话, 往后实际接上外界的设备大致上也会成功,如果需要修正,也只需 要一小部份作即可。 第 330 页,共 386 页硬件线路准备妥当且作过检查,而相关程序细节也已作过讨 论,当程序执行后,按下『开始监控』的按钮,程序即开始作进料 控制及显示,如图 7-3-12。 图 7-3-12 每个子配方开始进料的动作 图中所显示的情形是各子配方开始作进料的动作,配方由上方 的进料控制开关进入,当配方进入后,各子配方的料位就会发生变 化,由图中可知每个子配方的低料位已经就绪了,亦即此时的配方 量已经达到了低料位的状态,而高料位的状态则只有配方 A 和配方 B 达到,其余的尚在加入中。仔细观图 7-3-12 中各子配方上方的进 料控制按钮上的文字,我们发现在完成进料的配方 A 及配方 B 上的 按钮文字已经转换为”CLOSE”字眼,而其余尚在加入中的子配方上 的按钮则是”OPEN”字眼。等到全部的高料位状态都被检测到后, 就完成了第一个步骤的监控流程。 随着情形的不同,各子配方的进料开关也会随着高料位状态的 出现而关闭,当所有的子配方均已达到高料位的状态后,所有的子 配方开始进入混合槽,子配方槽下方的出料控制按钮上的文字会改 变为”OPEN”,表示各子配方进入混合槽,当所有的配方进入后, 混合槽的高低料位检测会就依次出现,等到高料位出现后,表示所 有的子配方已经进入到混合槽,混合槽也即将开始作搅拌的动作, 如图 7-3-13 所示。 第 331 页,共 386 页 图 7-3-13 完成子配方的进料,混合槽开始工作 由图 7-3-13,所有的开关除了马达是”Running…”之外,其余都 是 ”CLOSE”的状态,而在马达旁边的转速显示则显示现在的马达转 速是 1186rpm。马达的所有运转时间是在画面上所示的”搅拌时间 (分 )”,一旦马达搅拌达到时间的要求后,就会停止搅拌,并开始其 左边的供料开关,使搅拌过的配方进到下一个制程。 当供料动作进行中,混合槽中的料位显示会改变,高料位的指 示会消失,随后低料的指示也会不见,当低料位的显示变成绿色后, 表示混合槽的配方已完全地进入到下一个制程了,因此关闭供料开 关,至此完成一个循环的动作。 所有的动作完成一次后,定时器内的程序又重新开始运作,依 然照上述的流程走过,不断地重复进料→ 检测料位状态→ 出料→ 检 测混合槽料位状态→ 马达激活→ 计算时间→ 供料。 以上的程序代码,您可以参考光盘中『EX』\『 CH7』\『 Step4』 档案夹中的项目,双击 Project1.vbp 项目档,即可进行以上的测试。 7-3-6 声音的加入 上一小节已经就程序的部份作了撰写,而且也以实验的方式证 明程序的实用性,除了功能上的实现外,本小节还要为这个项目加 上声音及额外的流程式显示说明,让观看者更容易了解到整个监控 程序的运作。首先笔者简要说明多媒体控件。 第 332 页,共 386 页多媒体控件(MultiMedia Control) 多媒体控件并不是 Visual Basic 预设的控件,所以它不会在项 目开启时出现在工具箱中,欲将其加入工具箱的方法如下: 1、 在 Visual Basic 的设计环境下选择菜单的『项目』\『设定使 用组件』,待出现选择画面后,选择『Microsoft Multimedia Control』,按下确定,工具箱的画面上即会多一个多媒体图 标。如图 7-3-14 所示。 图 7-3-14 多媒体控件加入工具箱的过程 2、 点选工具箱中的多媒体控件图标,在主画面上的任一处拉出 适当外观,其外观如一般的媒体播放上的控制盘面,执行后 可以透过此盘面直接控制多媒体设备的工作,方便又很好辨 认。如图 7-3-15 所示。 图 7-3-15 在画面上布置多媒体控件 第 333 页,共 386 页3、 此控件的使用方式有二种,一是直接在画面上操作该控件的 各个按钮,一是使用程序下达动作指令的方法作控制。以监 控系统的角度看来,当然是使用第二种方法来得好,在监测 有问题时,直接就由系统发出声音。 4、 此控件的控制程序依然是对象、属性、事件、方法四个步骤。 在属性的使用上,最常使用的属性说明如下: (1). DeviceType:设备型态。用于指定控件所控制的设备, 输入的型态是字符串,常使用的型态字符串 有 ”WaveAudio”(播放声音档)、 ”Sequencer”(播放 MIDI 音乐)、 ”CDAudio”(播放 CD)。 (2). FileName:播放檔名。用来指定所要播放的多媒体文件 名称。 (3). Co mmand:执行指令字符串。控件上的所有控制按钮都 可以改由此属性下达指令字符串而达到相同的功能。指 令字符串包括 了 ”Open”、 ”Close”、 ”Stop”、 ”Pause”、 ”Back”、 ”Step” 、”Prev”、”Next”、”Seek”、”Record”、”Eject”、”Save”, 看这些字符串大约可以了解其功能,当指令字符串下达 后,控件即马上作出控制的动作。监控系统即采用此方 法。 (4). UpdateInterval:指定改变播放显示状态的间隔时间(单位 为毫秒),当设为 0 时,表示不使用此项功能。不为 0 时 会引发 StatusUpdate 事件。 (5). Notify:通知指令。设成 True 时,若指令执行完成将会 引发 Notify 事件,藉以达而通知的目的。通常利用此一 指令将播放完毕后的档案归位。 监控系统会使用到的事件说明如下: (1). Done:当 Notify 属性设为 True 时,一个指令完成后即 会引发此事件。通常我们在播放声音的同时,也会设定 Notify 为 True,而在 Done 事件中输入停止及倒带的指 令,当播放结束后,Done 事件会被执行,而停止及倒 带指令就会被执行。 5、 了解上述的各项要点后,多媒体控件的控制流程再表示如图 7-3-16 所示。 第 334 页,共 386 页下達停止指令 下達關閉指令 指定設備型態、 檔案名稱 下達開啟指令 高值警戒發生? 下達播放指令 播放完畢? 引發Done事件 開始 下達停止及倒帶 的指令 過程結束 是 是 图 7-3-16 多媒体控件程序流程 了解声音的控制方法后,我们打算将声音的功能加入到系统 中,笔者打算使用三种声音,第一个是水声(Water.wav),用来显示 各子配方的进料状态;第二个声音是泡泡声(Bubble.wav),用来显 示混合槽的马达激活后的搅拌过程;第三个声音是完成后的一个声 音 (Cannon.wav),用来指示供料动作的完成,此声音也表示一个完 整回路的完成,三个声音均是 Wav 文件,均可在光盘中『 EX』\『 CH7』 \『 Step5』里找到。 多媒体控件首先必须作初始设定,设定的部份写在 Form_Load 事件程序中,主要是设定所使用媒体类别(在这里使用的是 Wav 格 式 ),增加的程序写在该段程序的最末端,如下 …原来的程序 '多媒体控件的初始化 MMC1.UpdateInterval = 0 MMC1.Command = "Stop" MMC1.Command = "Close" MMC1.Wait = True MMC1.DeviceType = "WaveAudio" 有了上述的设定后,需要加入声音的三个地方分别描述如下: 子配方的进料通知: 由于子配方的程序是写在 FormulaControl 子程序中,因此声音 的片断程序也必须加在其中,程序代码如下 Function FormulaControl() As Boolean Dim i%, InputStatus As Integer txtMsg.Text = "开始进行子配方槽的加料动作!" '子配方出料开关关闭 第 335 页,共 386 页 DOValue = DOValue And (Not 2 ^ FA_Out_Ch) '配方 A 出料开关数值 DOValue = DOValue And (Not 2 ^ FB_Out_Ch) '配方 B 出料开关数值 DOValue = DOValue And (Not 2 ^ FC_Out_Ch) '配方 C 出料开关数值 DOValue = DOValue And (Not 2 ^ FD_Out_Ch) '配方 D 出料开关数值 DOValue = DOValue And (Not 2 ^ FE_Out_Ch) '配方 E 出料开关数值 TMC12_Do CInt("&H" & Hex(DOValue)) '关闭所有出料开关 Delay 1000 '延迟一段时间 '将输出开关上的 Command 上的 Caption 作改变,以显示状态 For i = 0 To 4 cmdOutput(i).Caption = "CLOSE" Next i '5种配方的进料开关打开 DOValue = DOValue Or 2 ^ FA_In_Ch '配方 A 的进料控制开关数值 DOValue = DOValue Or 2 ^ FB_In_Ch '配方 B 的进料控制开关数值 DOValue = DOValue Or 2 ^ FC_In_Ch '配方 C 的进料控制开关数值 DOValue = DOValue Or 2 ^ FD_In_Ch '配方 D 的进料控制开关数值 DOValue = DOValue Or 2 ^ FE_In_Ch '配方 E 的进料控制开关数值 '实际的数字输出 TMC12_Do CInt("&H" & Hex(DOValue)) '将开关上的 Command 上的 Caption 作改变,以显示状态 For i = 0 To 4 cmdInput(i).Caption = "OPEN" Next i Delay 1000 '延迟一段时间 '设定声音档案 MMC1.Command = "Close" MMC1.FileName = App.Path & "\water.wav" MMC1.Command = "Open" MciOK = True '监测高料准位是否达到 Do txtMsg.Text = "子配方槽料位检测中…" If MciOK Then MciOK = False MMC1.Command = "Play" '播放声音 End If wRtn = TMC12_Di(InputStatus) '取得数字输入的状态 wRtn = CheckLOStatus(InputStatus) '检查低料位状态 wRtn = CheckHIStatus(InputStatus) '检查高料位状态 '若高料准位已达,该通道的输出要关闭 If fHiState(0) Then DOValue = DOValue And (Not 2 ^ FA_In_Ch) cmdInput(0).Caption = "CLOSE" End If If fHiState(1) Then DOValue = DOValue And (Not 2 ^ FB_In_Ch) cmdInput(1).Caption = "CLOSE" End If If fHiState(2) Then DOValue = DOValue And (Not 2 ^ FC_In_Ch) cmdInput(2).Caption = "CLOSE" End If If fHiState(3) Then DOValue = DOValue And (Not 2 ^ FD_In_Ch) cmdInput(3).Caption = "CLOSE" End If If fHiState(4) Then 第 336 页,共 386 页 DOValue = DOValue And (Not 2 ^ FE_In_Ch) cmdInput(4).Caption = "CLOSE" End If '再作一次实际的数字输出,以关闭已达高料位的配方槽 TMC12_Do CInt("&H" & Hex(DOValue)) '检查是否所有的子配方都已达到高料位 bAllHI = True For i = 0 To 4 If Not fHiState(i) Then bAllHI = False '若还有未达高料位的,就设为 False End If Next i DoEvents Loop Until bAllHI txtMsg.Text = "完成子配方槽的加料动作!" FormulaControl = bAllHI End Function 粗体字的部份是另外加上去的,目的在于播放声音档案;当料 位的检测尚未完成前,此声音会一直播放,透过声音也可以使观看 者了解到现在还在进行子配方的进料动作。 声音的播放使用的是媒体控件的 Command 属性,此属性给 定 ”Play”字符串后,就会依所设定的文件名称作播放。 在多媒体控件上双击,进入程序代码编辑窗口,笔者在控件的 Done 事件上输入了以下的程序代码 Private Sub MMC1_Done(NotifyCode As Integer) If NotifyCode = 1 Then MMC1.Command = "Stop" MMC1.Command = "Prev" MciOK = True End If End Sub 此段程序代码会在声音档播放完毕后执行,笔者的目的是在作 回复声音档案指针位置的动作,其事件参数 NotifyCode 为 1 时表示 声音已播放完毕,程序中就将 Command 属性改为 Stop 及 Prev,回 复档案的启始位置,并将 MciOK 旗标设为 True,以便让程序的其 它知道声音已播放完毕,可以进行下一次的声音播放指令。例如上 述子配方进料的程序中也可以看到检查 MciOK 旗标的程序,透过 旗标的检查确定已经播放完毕,并且再次下达播放声音的指令 (Play)。 搅拌进行中及供料的通知: 搅拌进行中的程序是写在 FormulaOutput 子程序中,因此二段 声音的片断程序也必须加在其中,程序代码如下 Function FormulaOutput() As Boolean 第 337 页,共 386 页 Dim wCount As Long Dim StartTime As Double, EndTime As Double Dim mt As Single Dim wRtn As Integer, InputStatus As Integer txtMsg.Text = "混合槽搅拌开始…" '开启马达的控制通道 DOValue = DOValue Or 2 ^ Motor_Ch '实际的马达控制 TMC12_Do CInt("&H" & Hex(DOValue)) cmdMotor.Caption = "Running..." Delay 1000 '开始计算搅拌时间是否到达 StartTime = CDbl(Now) MixTime = CLng(Val(txtMixTime.Text) * 60) '混合的秒数 EndTime = StartTime + BaseSec * MixTime '应结束的时间 '变更播放的声音 MMC1.Command = "Close" MMC1.FileName = App.Path & "\Bubble.wav" MMC1.Command = "Open" MciOK = True Do txtMsg.Text = " 混合槽搅拌中…" & "(" & Format((EndTime - Now) / BaseSec, "0") & "秒 )" If MciOK Then MciOK = False MMC1.Command = "Play" '播放声音 End If '设定马达转速显示的撷取通道 '采用第五章的相关技术即可达到(频率量测) TMC12_Select8254 Int((Motor_Speed_Ch - 1) / 3) ' 选择第 1 颗 8254,逻辑上是 Counter4~6 TMC12_CounterSet 1, &H70, &HFFFF ' Counter-1, mode=0, down count ffff TMC12_CounterSet 2, &HB0, &HFFFF ' Counter-2, mode=0, down count ffff '开始计数后,数值会变化 DoEvents Do If ((TMC12_CounterRead(1, &HE4) And &H80) <> 0) Then Exit Do End If DoEvents Loop '此时可以读回计数值,并作计算 wCount = TMC12_CounterRead(2, &H80) wCount = (65535 - wCount) + 2 mt = 40.96 / wCount '单位计数值 '以每转 2000Pulse 的编码器连接马达,故除以 2000 MotorSpeed = CLng(1000 / mt * 60 / 2000) '将频率转 换为转速 lblRPM.Caption = Format(MotorSpeed, "0") & "rpm" DoEvents '这里可以再作其它的事情 Loop Until CDbl(Now) >= EndTime txtMsg.Text = "搅拌完成,进入下一制程中…" cmdMotor.Caption = "STOP" 第 338 页,共 386 页 '时间到达后关闭关马达,开启供料开关 DOValue = DOValue And (Not 2 ^ Motor_Ch) TMC12_Do DOValue Delay 1000 DOValue = DOValue Or (2 ^ Output_Ch) cmdNextStep.Caption = "OPEN" TMC12_Do DOValue fMixLo = False '开始检测混合槽的低位检测 Do wRtn = TMC12_Di(InputStatus) '取得数字输入的状态 wRtn = CheckMainHILOStatus(InputStatus) '检查混合槽高低料 位 DoEvents '若已低料位,则跳出 If Not fmainlostatus Then Exit Do Loop '关闭供料开关 DOValue = DOValue And (Not 2 ^ Output_Ch) TMC12_Do DOValue cmdNextStep.Caption = "CLOSE" '变更播放的声音 MMC1.Command = "Close" MMC1.FileName = App.Path & "\Cannon.wav" MMC1.Command = "Open" MMC1.Command = "Play" Delay 3000 '延迟一下 '至此完成一个循环的控制 FormulaOutput = True End Function 粗体字的部份是另外加上去的,前一个是搅拌进行中所使用的 声音,透过这个声音的出现,可以让观看者了解到现在正进行搅拌 的动作;而最后的供料动作完成后,则是出现另外一个声音,用以 通知此动作的完成。同样的,声音的播放使用的是媒体控件的 Command 属性,此属性给定”Play”字符串后,就会依所设定的文件 名称作播放,但必须注意的是,当我们改变文件名称时,最好使 用 ”Close”指令作关闭的动作,接着才作另外声音的指定及播放,免 得只能播放一次。 另外是增加说明文字的部份,由于画面上的左方还有一些空白 的地方,笔者认为可以再增加一些文字叙述的部份,以增加观看者 的了解;笔者在这个空白的地方加上了一个文字框控件,其底色使 用淡黄色,而前景颜色则是使用了蓝色,而且字体也加大了一些, Name 属性给定为 txtMsg,透过其 Text 属性的变更就可以在画面上 显示出相关的讯息,整个画面如图 7-3-17 所示。 第 339 页,共 386 页 图 7-3-17 加了多媒体及文字框后的画面 笔者在程序的若干地方均加上了 txtMsg.Text=”XXX”之类的程 序代码,用以说明相关的状态,读者可在程序中看出其用法。 整个程序执行后的情形除了多出声音和显示的文字之外,其余 的和上一小节一样,整体程序运作尚可,亦无重大问题产生,图 7-3-18 是执行的结果,左方的文字框会显示现在进行的动作。 图 7-3-18 混合槽进料中的情形 图 7-3-19 是另外一个阶段的执行情形,其显示出搅拌动作的进 行,此时的声音也会播放出来。 第 340 页,共 386 页 图 7-3-19 混合槽搅拌中的情形 加上声音及文字框的流程说明,可以增加观看者对于系统运作 的了解,实际的程序验证及操作,还是得由读者亲自上场,方能体 会。 以上的程序代码,您可以参考光盘中『EX\CH7\Step5』档案夹 中的项目,双击 Project1.vbp 项目档,即可进行以上的测试。其它 的档案夹由光盘片中『EX\CH7\Step1』~『 EX\CH7\Step4』是本专题 建构中的各个步骤,提供给各位读者作参考。 完整的程序代码列表如下: '******************************************************* '子配方的进料开关压下时产生的事件程序 '用于各别控制子配方的开关,压下时产生开启的动作 '****************************************************** Private Sub cmdInput_MouseDown(Index As Integer, Button As Integer, Shift As Integer, X As Single, Y As Single) If Button = vbLeftButton Then '表示开启进料开关 Select Case Index Case 0 '配方 A DOValue = DOValue Or 2 ^ FA_In_Ch '使用 or 避开叠加 Case 1 '配方 B DOValue = DOValue Or 2 ^ FB_In_Ch '使用 or 避开叠加 Case 2 '配方 C DOValue = DOValue Or 2 ^ FC_In_Ch '使用 or 避开叠加 Case 3 '配方 D DOValue = DOValue Or 2 ^ FD_In_Ch '使用 or 避开叠加 Case 4 '配方 E DOValue = DOValue Or 2 ^ FE_In_Ch '使用 or 避开叠加 End Select '实际的控制动作,并将旗标激活 第 341 页,共 386 页 TMC12_Do CInt("&H" & Hex(DOValue)) fFormulaIn(Index) = True End If End Sub '***************************************************** '子配方的进料开关放开时产生的事件程序 '用于各别控制子配方的开关,按钮放开后关闭 '***************************************************** Private Sub cmdInput_MouseUp(Index As Integer, Button As Integer, Shift As Integer, X As Single, Y As Single) '表示关闭进料开关 If Button = vbLeftButton Then Select Case Index Case 0 '配方 A DOValue = DOValue And (Not 2 ^ FA_In_Ch) '先 Not,再 And Case 1 '配方 B DOValue = DOValue And (Not 2 ^ FB_In_Ch) '先 Not,再 And Case 2 '配方 C DOValue = DOValue And (Not 2 ^ FC_In_Ch) '先 Not,再 And Case 3 '配方 D DOValue = DOValue And (Not 2 ^ FD_In_Ch) '先 Not,再 And Case 4 '配方 E DOValue = DOValue And (Not 2 ^ FE_In_Ch) '先 Not,再 And End Select '实际的控制动作,并将旗标激活 TMC12_Do CInt("&H" & Hex(DOValue)) fFormulaIn(Index) = False End If End Sub '**************************************************** '马达的开关 '**************************************************** Private Sub cmdMotor_Click() '改变马达的开关状态 fMotorOn = Not fMotorOn '开启马达的控制通道 If fMotorOn Then '若开启马达,则… DOValue = DOValue Or 2 ^ Motor_Ch Else '若关闭马达,则… DOValue = DOValue And (Not 2 ^ Motor_Ch) End If '实际的马达控制 TMC12_Do CInt("&H" & Hex(DOValue)) End Sub '***************************************************** '供料开关,以进行下一个制程 '***************************************************** Private Sub cmdNextStep_Click() '改变开关状态 第 342 页,共 386 页 fNextStep = Not fNextStep '开启马达的控制通道 If fNextStep Then '若开启马达,则… DOValue = DOValue Or 2 ^ Output_Ch Else '若关闭马达,则… DOValue = DOValue And (Not 2 ^ Output_Ch) End If '实际的马达控制 TMC12_Do CInt("&H" & Hex(DOValue)) End Sub '*********************************************** '子配方的出料开关压下时产生的事件程序 '用于各别控制子配方的开关,压下时产生开启的动作 '********************************************** Private Sub cmdOutput_MouseDown(Index As Integer, Button As Integer, Shift As Integer, X As Single, Y As Single) If Button = vbLeftButton Then '表示开启出料开关 Select Case Index Case 0 '配方 A DOValue = DOValue Or 2 ^ FA_Out_Ch '使用 or 避开叠加 Case 1 '配方 B DOValue = DOValue Or 2 ^ FB_Out_Ch '使用 or 避开叠加 Case 2 '配方 C DOValue = DOValue Or 2 ^ FC_Out_Ch '使用 or 避开叠加 Case 3 '配方 D DOValue = DOValue Or 2 ^ FD_Out_Ch '使用 or 避开叠加 Case 4 '配方 E DOValue = DOValue Or 2 ^ FE_Out_Ch '使用 or 避开叠加 End Select TMC12_Do CInt("&H" & Hex(DOValue)) fFormulaOut(Index) = True End If End Sub '************************************************* '子配方的出料开关放开时产生的事件程序 '用于各别控制子配方的开关,按钮放开后关闭 '************************************************ Private Sub cmdOutput_MouseUp(Index As Integer, Button As Integer, Shift As Integer, X As Single, Y As Single) If Button = vbLeftButton Then '表示关闭 出料开关 Select Case Index Case 0 '配方 A DOValue = DOValue And (Not 2 ^ FA_Out_Ch) '先 Not,再 And Case 1 '配方 B DOValue = DOValue And (Not 2 ^ FB_Out_Ch) '先 Not,再 And Case 2 '配方 C DOValue = DOValue And (Not 2 ^ FC_Out_Ch) '先 Not,再 And Case 3 '配方 D DOValue = DOValue And (Not 2 ^ FD_Out_Ch) '先 Not,再 And 第 343 页,共 386 页 Case 4 '配方 E DOValue = DOValue And (Not 2 ^ FE_Out_Ch) '先 Not,再 And End Select TMC12_Do CInt("&H" & Hex(DOValue)) fFormulaOut(Index) = False End If End Sub '************************************************** '开始监测的 Click 事件程序 '此按钮用于是否激活自动监测,以旗标表示 '************************************************* Private Sub Command1_Click() fMonitor = Not fMonitor If fMonitor Then Command1.Caption = "停止监控" Else Command1.Caption = "开始监控" End If End Sub '*********************************************** '结束程序按钮的 Click 事件程序 '关闭驱动程序后结束程序的执行 '*********************************************** Private Sub Command2_Click() TMC12_Do 0 '关闭所有的输出 TMC12_DriverClose '关闭驱动程序 End End Sub '********************************************** '窗体的 Load 事件程序 '将控件的说明文字写入,并激活适配卡 '********************************************** Private Sub Form_Load() Dim i% Dim wTotalBoards As Integer '设定各按钮及显示灯的说明文字 For i = 0 To 4 cmdInput(i).ToolTipText = "配方" & Chr(65 + i) & "进料控制开关" cmdOutput(i).ToolTipText = "配方" & Chr(65 + i) & "出料控制开关 " imgHI(i).ToolTipText = "配方" & Chr(65 + i) & "之高位指示灯" imgLo(i).ToolTipText = "配方" & Chr(65 + i) & "之低位指示灯" Next i '其它控件说明文字 cmdNextStep.ToolTipText = "完成配方输出控制开关" imgHI(5).ToolTipText = "完成配方之高位指示灯" imgLo(5).ToolTipText = "完成配方之低位指示灯" cmdMotor.ToolTipText = "马达控制开关" lblRPM.ToolTipText = "转速指示器" '加载驱动程序 If TMC12_DriverInit(wTotalBoards) <> TMC12_NoError Then MsgBox "驱动程序错误!", vbCritical + vbOKOnly, "错误" 第 344 页,共 386 页 End If '激活卡片 If TMC12_ActiveBoard(0) <> TMC12_NoError Then MsgBox "TMC12 激活失败", vbCritical + vbOKOnly, "Formula Mix Control" Exit Sub End If '所有的输出均关闭 TMC12_Do 0 '多媒体控件的初始化 MMC1.UpdateInterval = 0 MMC1.Command = "Stop" MMC1.Command = "Close" MMC1.Wait = True MMC1.DeviceType = "WaveAudio" End Sub Private Sub MMC1_Done(NotifyCode As Integer) If NotifyCode = 1 Then MMC1.Command = "Stop" MMC1.Command = "Prev" MciOK = True End If End Sub '************************************************ '定时器控件的 Timer 事件程序 '主要的自动化过程控制在此实现 '************************************************* Private Sub Timer1_Timer() If Not fMonitor Then Exit Sub '若未激活监控,则跳出循环 '以下是自动监控的流程 FormulaControl '子配方的控制 If Not MainTank(800) Then '子配方必须于 800 秒内完全进入主槽 '播放警告声音 MMC1.Command = "Close" MMC1.FileName = App.Path & "\Alert.wav" MMC1.Command = "Open" MMC1.Command = "Play" '表示过程有问题,应予以警告 txtMsg.Text = "混合槽失败!" MsgBox "混合槽经过了设定的时间仍未达到正常准位,请检查!" fMonitor = False '暂停自动监控流程 Command1.Caption = "开始监控" Exit Sub End If FormulaOutput '搅拌及供料的流程 End Sub '*************************************************** '子配方的相关检测及控制 '************************************************** Function FormulaControl() As Boolean Dim i%, InputStatus As Integer txtMsg.Text = "开始进行子配方槽的加料动作!" '子配方出料开关关闭 第 345 页,共 386 页 DOValue = DOValue And (Not 2 ^ FA_Out_Ch) '配方 A 出料开关数值 DOValue = DOValue And (Not 2 ^ FB_Out_Ch) '配方 B 出料开关数值 DOValue = DOValue And (Not 2 ^ FC_Out_Ch) '配方 C 出料开关数值 DOValue = DOValue And (Not 2 ^ FD_Out_Ch) '配方 D 出料开关数值 DOValue = DOValue And (Not 2 ^ FE_Out_Ch) '配方 E 出料开关数值 TMC12_Do CInt("&H" & Hex(DOValue)) '关闭所有出料开关 Delay 1000 '延迟一段时间 '将输出开关上的 Command 上的 Caption 作改变,以显示状态 For i = 0 To 4 cmdOutput(i).Caption = "CLOSE" Next i '5种配方的进料开关打开 DOValue = DOValue Or 2 ^ FA_In_Ch '配方 A 的进料控制开关数值 DOValue = DOValue Or 2 ^ FB_In_Ch '配方 B 的进料控制开关数值 DOValue = DOValue Or 2 ^ FC_In_Ch '配方 C 的进料控制开关数值 DOValue = DOValue Or 2 ^ FD_In_Ch '配方 D 的进料控制开关数值 DOValue = DOValue Or 2 ^ FE_In_Ch '配方 E 的进料控制开关数值 '实际的数字输出 TMC12_Do CInt("&H" & Hex(DOValue)) '将开关上的 Command 上的 Caption 作改变,以显示状态 For i = 0 To 4 cmdInput(i).Caption = "OPEN" Next i Delay 1000 '延迟一段时间 '设定声音档案 MMC1.Command = "Close" MMC1.FileName = App.Path & "\water.wav" MMC1.Command = "Open" MciOK = True '监测高料准位是否达到 Do txtMsg.Text = "子配方槽料位检测中…" If MciOK Then MciOK = False MMC1.Command = "Play" '播放声音 End If wRtn = TMC12_Di(InputStatus) '取得数字输入的状态 wRtn = CheckLOStatus(InputStatus) '检查低料位状态 wRtn = CheckHIStatus(InputStatus) '检查高料位状态 '若高料准位已达,该通道的输出要关闭 If fHiState(0) Then DOValue = DOValue And (Not 2 ^ FA_In_Ch) cmdInput(0).Caption = "CLOSE" End If If fHiState(1) Then DOValue = DOValue And (Not 2 ^ FB_In_Ch) cmdInput(1).Caption = "CLOSE" End If If fHiState(2) Then DOValue = DOValue And (Not 2 ^ FC_In_Ch) cmdInput(2).Caption = "CLOSE" End If If fHiState(3) Then DOValue = DOValue And (Not 2 ^ FD_In_Ch) cmdInput(3).Caption = "CLOSE" End If If fHiState(4) Then 第 346 页,共 386 页 DOValue = DOValue And (Not 2 ^ FE_In_Ch) cmdInput(4).Caption = "CLOSE" End If '再作一次实际的数字输出,以关闭已达高料位的配方槽 TMC12_Do CInt("&H" & Hex(DOValue)) '检查是否所有的子配方都已达到高料位 bAllHI = True For i = 0 To 4 If Not fHiState(i) Then bAllHI = False '若还有未达高料位的,就设为 False End If Next i DoEvents Loop Until bAllHI txtMsg.Text = "完成子配方槽的加料动作!" FormulaControl = bAllHI End Function '********************************************** '混合槽相关检测及控制 'WaitTime 是等待的时间(秒 ),所有的进料动作必须在此时间内完成 '********************************************** Function MainTank(WaitTime As Integer) As Boolean Dim wRtn As Integer, InputStatus As Integer Dim i As Integer Dim DT As Long '若已全部达到高料位,准备进入混合槽 '先关闭供料开关 txtMsg.Text = "混合槽的动作开始!" DOValue = DOValue And 2 ^ Output_Ch TMC12_Do CInt("&H" & Hex(DOValue)) cmdNextStep.Caption = "CLOSE" '标示为关闭 '监测混合槽高料准位是否达到 wRtn = TMC12_Di(InputStatus) '取得数字输入的状态 wRtn = CheckMainHILOStatus(InputStatus) '检查混合槽高低料位 '若已高料位,则跳出,不需再作 If fMainHiState Then MainTank = True Exit Function End If txtMsg.Text = "混合槽开始进料……" '开启进料开关,使其全部进入混合槽 DOValue = DOValue Or 2 ^ FA_Out_Ch DOValue = DOValue Or 2 ^ FB_Out_Ch DOValue = DOValue Or 2 ^ FC_Out_Ch DOValue = DOValue Or 2 ^ FD_Out_Ch DOValue = DOValue Or 2 ^ FE_Out_Ch TMC12_Do CInt("&H" & Hex(DOValue)) '实际的输出 '将输出开关上的 Command 上的 Caption 作改变,以显示状态 For i = 0 To 4 cmdOutput(i).Caption = "OPEN" Next i DoEvents '监测混合槽高料准位是否达到 DT = GetTickCount MainTank = False '先假设动作失败 Do 第 347 页,共 386 页 txtMsg.Text = "混合槽进料中…" wRtn = TMC12_Di(InputStatus) '取得数字输入的状态 '判断料位状态,并熄灭料位灯号 wRtn = CheckHIStatus(InputStatus) wRtn = CheckLOStatus(InputStatus) wRtn = CheckMainHILOStatus(InputStatus) DoEvents '若已高料位,则跳出 If fMainHiState Then CloseFormulaIn '关闭子配方槽的出料开关 MainTank = True Exit Do '表示动作完成 End If If GetTickCount - DT <= 0 Then DT = GetTickCount Loop Until (GetTickCount - DT) / 1000 >= WaitTime End Function '************************************************** '供料动作的进行 '************************************************* Function FormulaOutput() As Boolean Dim wCount As Long Dim StartTime As Double, EndTime As Double Dim mt As Single Dim wRtn As Integer, InputStatus As Integer txtMsg.Text = "混合槽搅拌开始…" '开启马达的控制通道 DOValue = DOValue Or 2 ^ Motor_Ch '实际的马达控制 TMC12_Do CInt("&H" & Hex(DOValue)) cmdMotor.Caption = "Running..." Delay 1000 '开始计算搅拌时间是否到达 StartTime = CDbl(Now) MixTime = CLng(Val(txtMixTime.Text) * 60) '混合的秒数 EndTime = StartTime + BaseSec * MixTime '应结束的时间 '变更播放的声音 MMC1.Command = "Close" MMC1.FileName = App.Path & "\Bubble.wav" MMC1.Command = "Open" MciOK = True Do txtMsg.Text = " 混合槽搅拌中…" & "(" & Format((EndTime - Now) / BaseSec, "0") & "秒 )" If MciOK Then MciOK = False MMC1.Command = "Play" '播放声音 End If '设定马达转速显示的撷取通道 '采用第五章的相关技术即可达到(频率量测) TMC12_Select8254 Int((Motor_Speed_Ch - 1) / 3) ' 选择第 1 颗 8254,逻辑上是 Counter4~6 TMC12_CounterSet 1, &H70, &HFFFF ' Counter-1, mode=0, down count ffff TMC12_CounterSet 2, &HB0, &HFFFF ' Counter-2, mode=0, down count ffff '开始计数后,数值会变化 DoEvents 第 348 页,共 386 页 Do If ((TMC12_CounterRead(1, &HE4) And &H80) <> 0) Then Exit Do End If DoEvents Loop '此时可以读回计数值,并作计算 wCount = TMC12_CounterRead(2, &H80) wCount = (65535 - wCount) + 2 mt = 40.96 / wCount '单位计数值 '以每转 2000Pulse 的编码器连接马达,故除以 2000 MotorSpeed = CLng(1000 / mt * 60 / 2000) '将频率转 换为转速 lblRPM.Caption = Format(MotorSpeed, "0") & "rpm" DoEvents '这里可以再作其它的事情 Loop Until CDbl(Now) >= EndTime txtMsg.Text = "搅拌完成,进入下一制程中…" cmdMotor.Caption = "STOP" '时间到达后关闭关马达,开启供料开关 DOValue = DOValue And (Not 2 ^ Motor_Ch) TMC12_Do DOValue Delay 1000 DOValue = DOValue Or (2 ^ Output_Ch) cmdNextStep.Caption = "OPEN" TMC12_Do DOValue fMixLo = False '开始检测混合槽的低位检测 Do wRtn = TMC12_Di(InputStatus) '取得数字输入的状态 wRtn = CheckMainHILOStatus(InputStatus) '检查混合槽高低料 位 DoEvents '若已低料位,则跳出 If Not fmainlostatus Then Exit Do Loop '关闭供料开关 DOValue = DOValue And (Not 2 ^ Output_Ch) TMC12_Do DOValue cmdNextStep.Caption = "CLOSE" '变更播放的声音 MMC1.Command = "Close" MMC1.FileName = App.Path & "\Cannon.wav" MMC1.Command = "Open" '等待上一次的声音播放完毕 MMC1.Command = "Play" Delay 3000 '延迟一下 '至此完成一个循环的控制 FormulaOutput = True End Function '****************************************************** '子配方的高料位检测子程序 '***************************************************** Function CheckHIStatus(InputStatus As Integer) As Integer fHiState(0) = IIf((InputStatus And 2 ^ FA_HI_Ch) <> 0, True, False) '比对配方 A 高料状态 第 349 页,共 386 页 fHiState(1) = IIf((InputStatus And 2 ^ FB_HI_Ch) <> 0, True, False) '比对配方 B 高料状态 fHiState(2) = IIf((InputStatus And 2 ^ FC_HI_Ch) <> 0, True, False) '比对配方 C 高料状态 fHiState(3) = IIf((InputStatus And 2 ^ FD_HI_Ch) <> 0, True, False) '比对配方 D 高料状态 fHiState(4) = IIf((InputStatus And 2 ^ FE_HI_Ch) <> 0, True, False) '比对配方 E 高料状态 '依其状态改变灯号 If fHiState(0) Then imgHI(0).Picture = imgRed.Picture '红灯亮起 Else imgHI(0).Picture = imgGreen.Picture '绿灯亮起 End If If fHiState(1) Then imgHI(1).Picture = imgRed.Picture '红灯亮起 Else imgHI(1).Picture = imgGreen.Picture '绿灯亮起 End If If fHiState(2) Then imgHI(2).Picture = imgRed.Picture '红灯亮起 Else imgHI(2).Picture = imgGreen.Picture '绿灯亮起 End If If fHiState(3) Then imgHI(3).Picture = imgRed.Picture '红灯亮起 Else imgHI(3).Picture = imgGreen.Picture '绿灯亮起 End If If fHiState(4) Then imgHI(4).Picture = imgRed.Picture '红灯亮起 Else imgHI(4).Picture = imgGreen.Picture '绿灯亮起 End If End Function '************************************************* '关闭各子配方的进料开关(MainTank) '************************************************** Function CloseFormulaIn() Dim i As Integer DOValue = DOValue And (Not 2 ^ FA_Out_Ch) DOValue = DOValue And (Not 2 ^ FB_Out_Ch) DOValue = DOValue And (Not 2 ^ FC_Out_Ch) DOValue = DOValue And (Not 2 ^ FD_Out_Ch) DOValue = DOValue And (Not 2 ^ FE_Out_Ch) TMC12_Do CInt("&H" & Hex(DOValue)) '实际的输出 For i = 0 To 4 cmdOutput(i).Caption = "CLOSE" Next i End Function '********************************************** '子配方的低料位检测子程序 '*********************************************** Function CheckLOStatus(InputStatus As Integer) As Integer 第 350 页,共 386 页 fLoState(0) = IIf((InputStatus And 2 ^ FA_LO_Ch) <> 0, True, False) '比对配方 A 低料状态 fLoState(1) = IIf((InputStatus And 2 ^ FB_LO_Ch) <> 0, True, False) '比对配方 B 低料状态 fLoState(2) = IIf((InputStatus And 2 ^ FC_LO_Ch) <> 0, True, False) '比对配方 C 低料状态 fLoState(3) = IIf((InputStatus And 2 ^ FD_LO_Ch) <> 0, True, False) '比对配方 D 低料状态 fLoState(4) = IIf((InputStatus And 2 ^ FE_LO_Ch) <> 0, True, False) '比对配方 E 低料状态 '若低料准位已达,该通道的红色灯号要亮起 If fLoState(0) Then imgLo(0).Picture = imgRed.Picture '红灯亮起 Else imgLo(0).Picture = imgGreen.Picture '绿灯亮起 End If If fLoState(1) Then imgLo(1).Picture = imgRed.Picture '红灯亮起 Else imgLo(1).Picture = imgGreen.Picture '绿灯亮起 End If If fLoState(2) Then imgLo(2).Picture = imgRed.Picture '红灯亮起 Else imgLo(2).Picture = imgGreen.Picture '绿灯亮起 End If If fLoState(3) Then imgLo(3).Picture = imgRed.Picture '红灯亮起 Else imgLo(3).Picture = imgGreen.Picture '绿灯亮起 End If If fLoState(4) Then imgLo(4).Picture = imgRed.Picture '红灯亮起 Else imgLo(4).Picture = imgGreen.Picture '绿灯亮起 End If End Function '*********************************************** '混合槽的高低料位检测子程序 '********************************************* Function CheckMainHILOStatus(InputStatus As Integer) As Integer fMainHiState = IIf((InputStatus And 2 ^ Mix_HI_Ch) <> 0, True, False) '比对混合槽高料状态 fMainLoState = IIf((InputStatus And 2 ^ Mix_LO_Ch) <> 0, True, False) '比对混合槽高料状态 '若低料准位已达,该通道的红色灯号要亮起 If fMainHiState Then imgHI(5).Picture = imgRed.Picture '红灯亮起 Else imgHI(5).Picture = imgGreen.Picture '绿灯亮起 End If If fMainLoState Then imgLo(5).Picture = imgRed.Picture '红灯亮起 Else imgLo(5).Picture = imgGreen.Picture '绿灯亮起 End If 第 351 页,共 386 页 End Function Tmc12.bas 模块和第五章所讨论的一样,在此就不列出了,请 读者参考该章中的程序代码。 Module.bas 模块中的程序代码则如下: Public Declare Function GetTickCount Lib "kernel32" () As Long Public fMonitor As Boolean '是否开始监控的旗标 Public fHiState(0 To 5) As Boolean '高值状态灯号 Public fLoState(0 To 5) As Boolean '低值状态灯号 Public fMainHiState As Boolean '混合槽高值状态灯号 Public fMainLoState As Boolean '混合槽低值状态灯号 Public fFormulaIn(0 To 4) As Boolean '各配方进料开关状态 Public fFormulaOut(0 To 4) As Boolean '各配方出料开关状态 Public fMotorOn As Boolean '马达开关状态 Public fNextStep As Boolean '完成配方进入下一步骤的开关 Public MotorSpeed As Long '马达转速 Public MixTime As Long '搅拌时间设定(秒 ) Public DOValue As Long '数字输出的数值 Public MciOK As Boolean '记录 MCI 是否已播放完毕 '************************************ '以下是各 IO 映像的常数定义 '************************************ '各子配方的高料位通道 Public Const FA_HI_Ch = 0 Public Const FB_HI_Ch = 1 Public Const FC_HI_Ch = 2 Public Const FD_HI_Ch = 3 Public Const FE_HI_Ch = 4 '各子配方的低料位通道 Public Const FA_LO_Ch = 6 Public Const FB_LO_Ch = 7 Public Const FC_LO_Ch = 8 Public Const FD_LO_Ch = 9 Public Const FE_LO_Ch = 10 '混合槽高低料位通道 Public Const Mix_HI_Ch = 5 Public Const Mix_LO_Ch = 11 '各子配方的进料控制通道 Public Const FA_In_Ch = 0 Public Const FB_In_Ch = 1 Public Const FC_In_Ch = 2 Public Const FD_In_Ch = 3 Public Const FE_In_Ch = 4 '各子配方的出料控制通道 Public Const FA_Out_Ch = 5 Public Const FB_Out_Ch = 6 Public Const FC_Out_Ch = 7 Public Const FD_Out_Ch = 8 Public Const FE_Out_Ch = 9 '其它控制 Public Const Motor_Ch = 10 第 352 页,共 386 页Public Const Output_Ch = 11 Public Const Motor_Speed_Ch = 6 '第 6 个 CLK 输入通道 Public Const BaseSec = 1.15740695036948E-05 '1 秒钟以数值表示 '******************************************************* '延迟函式,单位是毫秒 '******************************************************* Sub Delay(DT As Long) Dim TT As Long TT = GetTickCount Do DoEvents If GetTickCount - TT < 0 Then TT = GetTickCount Loop Until GetTickCount - TT >= DT End Sub 第 353 页,共 386 页本章习题 1、 若某一个通道损坏,如何改变到其它尚未使用的通道? 2、 请在子配方出料到混合槽时加入声音。 3、 试着更改不同阶段的声音,并试着加入人声。 4、 为系统程序加上时间的显示。 第 354 页,共 386 页第八章 其它 DIO 适配卡及 ISA 接口 前面的几章说明了几种常用的数字输出入适配卡,这些适配卡 其基本原理已如各章所述,在实际应用时,只要掌握住这些基本的 原理,其它型式的数字适配卡其实也就不难入手了。 本章将介绍一些其它型式的适配卡,主要还是以 PCI 接口为主; 除此之外,ISA 接口在工业场合中还是经常使用到,本章也将略为 说明 ISA 接口和 PCI 接口的差异,以及它们在控制上的不同。最后 则是简略地介绍步进马达控制卡。 8-1 进阶的适配卡 除了之前所说明的适配卡外,就数字输出入来说,另外还有一些 进阶的适配卡可供使用,有些适配卡的使用范围比较集中在某一个 特殊的场合,某些适配卡则是提供了更多的通道数,控制程序上是 差不多,在本章主要说明的是这些卡片的相关规格,至于实际的程 序设计,由于和先前所提到的适配卡是类似的,在此就不赘述。 8-1-1 多通道隔离式输出入卡(PISO-P32C32) 如果需要的通道数较多,而且必须有隔离功能的话,就可以选用 此片拥有 32 个输出和 32 个输入通道的数字适配卡,其外观如图 8-1-1 所示,卡片上的分布则如图 8-1-2 所示。 图 8-1-1 PISO-P32C32 多通道隔离适配卡 第 355 页,共 386 页 图 8-1-2 PISO-P32C32 组件分布图 此适配卡的输出入均为光隔离式,可以有效地将计算机端和外界 的讯号隔离,达到保护的目的;其数字输出入通道的规格如表 8-1-1。 表 8-1-1 PISO-P32C32 数字输出入通道规格 数字输入 数字输出 Input voltage: 3.5V to 30V Input impedance: 1.2K Isolation voltage: Using internal power: 3000V Using external power: 3750V Response time: 1K Hz max. Isolation voltage: 3750V Open collector output: 125 mA/30V per channel Response time: 10K Hz typical Power Consumption: +5V @ 600mA (typical) 此适配卡的通道共有 64 个,输出与输入各半;在使用大量数字 通道的场合中,此型适配卡是相当适合的。本适配卡的接点被区分 为四组,每组均为隔离式设计,可以有效地保护适配卡。 在此适配卡有二组接点的部份,一个在适配卡侧边(图 8-1-2 的 CON1),一个则在适配卡上(图 8-1-2 的 CON2), CON2 可以透过延 伸的线材将其延伸至 Slot 文件板的位置,形成和 CON1 一样的 37Pin 接座,二组接座都需使用 37Pin 的外接端子台(如图 8-1-3)作连接。 图 8-1-3 37Pin 接座 第 356 页,共 386 页使用二个 37Pin 的接座就可以连接二个输出入的接点,这些接 点被区分为四组,称之为 Bank1、Bank2、Bank3、Bank4,图 8-1-4 也可以看出二组 37Pin 接座上的引脚分布,图左是原有板子上的 37Pin 接点,而图右则是延伸自适配卡后之 40Pin 接座;每一组的 外接式电源输入也是分开的,四组独立的通道使得使用上更加地安 全。 图 8-1-4 输出入引脚定义 数字输入的接线法: 使用数字输入时的接线方法每种卡片均类似,也可以由各卡片 的手册看到建议的接线方式,如图 8-1-5 就是使用外接式电源时的 一种输入讯号的接法。 图 8-1-5 P32C32 的数字输入接法(采用外部电源) 第 357 页,共 386 页图 8-1-5 所显示的是使用一个开关作为数字输入的来源,首先 将适配卡上的 Jumper 跳至外部电源端,当标示为 SWITCH1 的开关 被押下后,外部电源开始流入,经由标示为(1)的线路流入发光二极 管,此发光二极管导致其左边的光敏晶体管被触发,而使得 DI0 引 脚发生变化,电流再由标示为(3)的线路,流经 SWITCH1,经由标 示为(4)的线路回到负接点,如此完成一个循环;当此电路持续存在 时,DI0 引脚的状态也将保持住,故由计算机端下达的数字输入指 令时,可以读到此引脚的状态。其它的数字输入引脚的接线方式也 可以如此。 数字输出的接线法: 使用数字输出控制时的接线方法彼此之间亦类似,也可以由各 卡片的手册看到建议的接线方式,如图 8-1-6 就是使用外接式电源 时的一种控制输出的接法。 图 8-1-6 P32C32 的数字输出接法(采用外部电源) 图 8-1-6 采用的是集电极开路方式的输出方式,在标示为(1)的 光耦合器中,如果利用 Visual Basic 程序将左方的 Dout 讯号拉至低 电位,则光耦合器中的发光二极管将触发其右方的光敏晶体管;同 时,在标示为(2)处的晶体管之基极也因而被触发,导致电流经标示 为 (3)的线路以标示为(4)的路径流至 GND 接脚,由于完整的电路形 成,外部装置(External Device)也因而被激活。 程序的建立方法: 了解硬件部份的相关细节后,接下来是程序的设计。由前面章 节的说明,欲取得数字输入的状态,或是控制数字输出,必须先取 得代表这些输出入的缓存器2018香港马会开奖现场(请参阅第四章),P32C32 的缓存器 2018香港马会开奖现场如表 8-1-2 所示。 表 8-1-2 PSIO-P32C32 的缓存器定义 2018香港马会开奖现场 读取时的意义 写入时的意义 Wbase+0 RESET\ 控制缓存器 同左 Wbase+2 辅助控制缓存器 同左 第 358 页,共 386 页Wbase+3 辅助资料缓存器 同左 Wbase+5 中断屏蔽控制缓存器 同左 Wbase+7 辅助引脚状态缓存器 同左 Wbase+&H2a 中断极性控制缓存器 同左 Wbase+&Hc0 读取 DI_0 ~ DI_7 状态 控制 D O_0 ~DO_7 状态 Wbase+&Hc4 读取 DI_8 ~ DI_15 状态 控制 D O_8 ~DO_15 状态 Wbase+&Hc8 读取 DI_16 ~ DI_23 状态 控制 D O_16 ~DO_23 状态 Wbase+&Hcc 读取 DI_24 ~ DI_31 状态 控制 D O_24 ~DO_31 状态 有了相关的2018香港马会开奖现场,就是使用厂商所提供的函式取得基2018香港马会开奖现场,表 8-1-2 中的 Wbase 就是使用此适配卡的驱动程序中的函式所取得的 基2018香港马会开奖现场;再利用 OutputBye 及 InputByte 二个函式控制数字输出状 态或取得数字输入值,以下是 PISO-P32C32 及下二小节将介绍的 PISO-P64、 PISO-C64 所使用的函式。 Declare Function PISODIO_DriverInit Lib "PISODIO.dll" () As Integer Declare Sub PISODIO_DriverClose Lib "PISODIO.dll" () Declare Function PISODIO_SearchCard Lib "PISODIO.dll" ( wBoards As Integer, ByVal dwPIOPISOCardID As Long) As Integer Declare Function PISODIO_GetDriverVersion Lib "PISODIO.dll" (wDriverVersion As Integer) As Integer Declare Function PISODIO_GetConfigAddressSpace Lib "PISODIO.dll" ( _ ByVal wBoardNo As Integer, wAddrBase As Long, wIrqNo As Integer, _ wSubVendor As Integer, wSubDevice As Integer, wSubAux As Integer, _ wSlotBus As Integer, wSlotDevice As Integer) As Integer Declare Function PISODIO_ActiveBoard Lib "PISODIO.dll" ( ByVal wBoardNo As Integer) As Integer Declare Function PISODIO_WhichBoardActive Lib "PISODIO.dll" () As Integer ' DIO functions Declare Sub PISODIO_OutputByte Lib "PISODIO.dll" ( _ ByVal address As Long, ByVal dataout As Integer) Declare Sub PISODIO_OutputWord Lib "PISODIO.dll" ( _ ByVal address As Long, ByVal dataout As Long) Declare Function PISODIO_InputByte Lib "PISODIO.dll" ( _ ByVal address As Long) As Integer Declare Function PISODIO_InputWord Lib "PISODIO.dll" ( _ ByVal address As Long) As Long 粗体部份是会经常使用的函式,由这些函式我们可以发现和第 四章所使用的函式是类似,再者其输出入观念也相仿,所以在第四 章建构程序的方式也就可以完全地应用到这个地方。 PISODIO_OutputByte 和 PISODIO_OutputWord 的差异在于前者使用 8 位的数值控制通道状态,因此一次控制 8 个数字输出通道状态;后 第 359 页,共 386 页者使用 16 位的数值控制通道状态,故一次控制 16 个数字输出通道 的状态。PISODIO_InputByte 和 PISODIO_InputWord 均使用在取得数 字输入的讯息,但是二者的差别也是在于同时取 8 个通道或 16 个 通道的差别。 利用这些函式就可以将控制的数值送到相对应的缓存器2018香港马会开奖现场 上,送出的方式及其原则就如第四章的说明。 8-1-2 64 通道数字输入适配卡 如果需要的数字输入通道数较多,而且必须有隔离功能的话,就 可以选用此片适配卡(PISO-P64),其外观如图 8-1-7 所示,卡片上的 分布则如图 8-1-8 所示。 图 8-1-7 PISO-P64 光隔离式数字输入卡 图 8-1-8 PISO-P64 组件分布图 第 360 页,共 386 页此适配卡的输入采用光隔离式,可以有效地将计算机端和外界的 讯号隔离,达到保护的目的;其数字输入通道的规格如表 8-1-3。 表 8-1-3 PISO-P64 数字输入通道规格 数字输入 Input voltage: 3.5V to 30V Input impedance: 1.2K/1W Isolation voltage: Using internal power: 3000V Using external power: 3750V Response time: 1K Hz max. Power Consumption: +5V @ 400mA (typical) 使用二个 37Pin 的接座就可以连接成功连接二个输入的接点, 和 P32C32 一样,这些接点也被区分为四组,称之为 Bank1、Bank2、 Bank3、Bank4,图 8-1-9 也可以看出二组 37Pin 接座上的引脚分布, 图左是原有板子上的 37Pin 接点,而图右则是延伸自适配卡后之 40Pin 接座;每一组的外接式电源输入也是分开的,四组独立的通 道使得使用上更加地安全。 图 8-1-9 PISO-P64 输入引脚定义 数字输入的接线法: 除了接点数较多之外,此型适配卡和 P32C32 之数字输入完全 相同,请参考图 8-1-5 及其说明,便可了解此型适配卡之数字输入 的接法。 程序的建立方法: 第 361 页,共 386 页我们同样得先取得代表这些输入的缓存器2018香港马会开奖现场( 请参阅第四 章 ), P64 的缓存器2018香港马会开奖现场如表 8-1-4 所示。 表 8-1-4 PSIO-P64 的缓存器定义 2018香港马会开奖现场 读取时的意义 写入时的意义 wBase+0 RESET\ 控制缓存器 同左 wBase+2 辅助控制缓存器 同左 wBase+3 辅助资料缓存器 同左 WBase+5 中断屏蔽控制缓存器 同左 WBase+7 辅助引脚状态缓存器 同左 WBase+&H2a 中断极性控制缓存器 同左 WBase+&Hc0 读取 DI_0 ~ DI_7 状态 保留 WBase+&Hc4 读取 DI_8 ~ DI_15 状态 保留 WBase+&Hc8 读取 DI_16 ~ DI_23 状态 保留 WBase+&Hcc 读取 DI_24 ~ DI_31 状态 保留 WBase+&Hd0 读取 DI_32 ~ DI_39 状态 保留 WBase+&Hd4 读取 DI_40 ~ DI_47 状态 保留 WBase+&Hd8 读取 DI_48 ~ DI_55 状态 保留 WBase+&Hdc 读取 DI_56 ~ DI_63 状态 保留 有了相关的2018香港马会开奖现场,就是使用厂商所提供的函式取得基2018香港马会开奖现场,表 8-1-4 中的 WBase 就是使用此适配卡的驱动程序中的函式所取得的 基2018香港马会开奖现场;再利用 PISODIO_InputByte 和 PISODIO_InputWord 函式取得 数字输入值,函式如上一小节所述。 8-1-3 64 通道数字输出适配卡 相对于多通道的数字输入卡,也有另一种单作数字输出的适配卡 (PISO-C64),其外观如图 8-1-10 所示,卡片上的分布则如图 8-1-11 所示。 第 362 页,共 386 页 图 8-1-10 PISO-C64 光隔离式数字输入卡 图 8-1-11 PISO-C64 组件分布图 此适配卡的输入采用光隔离式,可以有效地将计算机端和外界的 讯号隔离,达到保护的目的;其数字输出通道的规格如表 8-1-5。 表 8-1-5 PISO-C64 数字输出通道规格 数字输入 Isolation voltage: 3750V Open collector output: 125 mA/30V per channel Response time: 10K Hz typical Power Consumption: +5V @ 800mA (typical) 使用二个 37Pin 的接座就可以连接成功连接二个输入的接点, 和 P32C32 一样,这些接点也被区分为四组,称之为 Bank1、Bank2、 Bank3、Bank4,图 8-1-12 也可以看出二组 37Pin 接座上的引脚分布, 图左是原有板子上的 37Pin 接点,而图右则是延伸自适配卡后之 第 363 页,共 386 页40Pin 接座;每一组的外接式电源输入也是分开的,四组独立的通 道使得使用上更加地安全。 图 8-1-12 PISO-C64 输出引脚定义 数字输出的接线法: 除了接点数较多之外,此型适配卡和 P32C32 之数字输出完全 相同,请参考图 8-1-6 及其说明,便可了解此型适配卡之数字输出 的接法。 程序的建立方法: 我们同样得先取得代表这些输入的缓存器2018香港马会开奖现场( 请参阅第四 章 ), C64 的缓存器2018香港马会开奖现场如表 8-1-6 所示。 表 8-1-6 PSIO-C64 的缓存器定义 2018香港马会开奖现场 读取时的意义 写入时的意义 wBase+0 RESET\ 控制缓存器 同左 wBase+2 辅助控制缓存器 同左 wBase+3 辅助资料缓存器 同左 WBase+5 中断屏蔽控制缓存器 同左 WBase+7 辅助引脚状态缓存器 同左 WBase+&H2a 中断极性控制缓存器 同左 WBase+&Hc0 保留 控制 DO_0 ~DO_7 状态 WBase+&Hc4 保留 控制 DO_8 ~DO_15 状态 WBase+&Hc8 保留 控制 DO_16 ~DO_23 状态 WBase+&Hcc 保留 控制 DO_24 ~DO_31 状态 WBase+&Hd0 保留 控制 DO_31 ~DO_39 状态 第 364 页,共 386 页WBase+&Hd4 保留 控制 DO_40 ~DO_47 状态 WBase+&Hd8 保留 控制 DO_48 ~DO_55 状态 WBase+&Hdc 保留 控制 DO_56 ~DO_63 状态 有了相关的2018香港马会开奖现场,就是使用厂商所提供的函式取得基2018香港马会开奖现场,表 8-1-6 中的 WBase 就是使用此适配卡的驱动程序中的函式所取得的 基2018香港马会开奖现场;再利用 PISODIO_OutputByte 和 PISODIO_OutputWord 函式 控制各通道的数字输出状态,函式如 8-1-1 小节所述。 到此为止的三小节均是 64 个通道,但区分为数字输出和输入均 具备、均为输入或均为输出等三种不同的适配卡;三种适配卡均为 隔离式的设计,适合用在保护卡片和计算机系统的场合中,读者可 依需求选择适当型式的适配卡。程序的部份,除了函式可能略有不 同 外,设计的理念和原则完全和第四章是一样的,读者可以参阅 第四章的各范例的说明应可了解其应用方式。 8-1-4 非隔离型数字适配卡 相较于前述的隔离式适配卡,非隔离式的数字适配卡在使用上就 必须考虑到安全的问题,图 8-1-13 是编号为 PIO-D144 的数字适配 卡。此卡片提供了共 144 个数字通道,这些通道被分成 6 个群组(由 图中也可以看出它有 6 处接点位置)。 图 8-1-13 144 通道数字 IO 适配卡 图 8-1-14 是卡片各接点的标示,由图上可看出每个群组有 3 个埠, 称为 A 埠、B 埠及 C 埠,在图上的标是 PA、 PB、 PC,这样的分法 是由 8255 芯片而来,D144 的每一个埠就是使用相同的定义。 第 365 页,共 386 页 图 8-1-14 PIO-D144 的各组编号 本书曾在第四章讨论到数字输出入适配卡,PIO-D144 的通道数 较多,输出入的原理和控制的程序也一样,程控方法均和第四章的 方法一样,因此可以使用第四章一样的方式对此卡片作控制-只要了 解各缓存器的定义和对应的组别即可。由于卡片上有 6 个群组,每 个群组有 3 个埠,因此共有 18 个埠可作 IO,将其依序编号的话, 由埠 0~埠 17,图 8-1-14 中的 6 个接头(CN1~CN6),由 CN1 的埠 0 开始编号,直到 CN6 的埠 17,共 18 个,缓存器也将以此作为辨别, 如表 8-1-7 所示,而表 8-1-8 则是其缓存器的定义。 表 8-1-7 埠号的定义 接头编号 PA0~ PA7 PB0~PB7 PC0~PC7 CN1 Port0 Port1 Port2 CN2 Port3 Port4 Port5 CN3 Port6 Port7 Port8 CN4 Port9 Port10 Port11 CN5 Port12 Port13 Port14 CN6 Port15 Port16 Port17 表 8-1-8 是此卡片的缓存器定义 2018香港马会开奖现场 读取时的意义 写入时的意义 wBase+0 RESET\ 控制缓存器 同左 wBase+2 辅助控制缓存器 同左 wBase+3 辅助资料缓存器 同左 WBase+5 中断屏蔽控制缓存器 同左 WBase+7 辅助引脚状态缓存器 同左 WBase+&H2a 中断极性控制缓存器 同左 WBase+&Hc0 读取数字输入资料 控制数字输出状态 WBase+&Hc4 保留 选择要控制的端口号码 第 366 页,共 386 页WBase+&Hc8 保留 Port0~Port5 的方向控制 WBase+&Hcc 保留 Port6~Port11 的方向控制 WBase+&Hd0 保留 Port12~Port17 的方向控制 由表 8-1-8 可知,每一次使用2018香港马会开奖现场 WBase+&Hc0 可以读取或是控 制一个埠,而到底读取或控制的端口号码就要视 WBase+&Hc4 2018香港马会开奖现场 所送出的数值,例如以 OutputByte 指令送出 3 到 WBase+&Hc4 的地 址,表示我们将控制端口 3 的 8 个位,接着使用 OutputByte 指令送 数值至 WBase+&Hc0 就表示控制埠 3 的通道状态,而若使用 InputByte 指令读取 WBase+&Hc0 的话,就表示读取端口 3 的数字输 入状态。不过需特别注意的是输出入方向的指令,如果我们要作输 出控制的话,必须先行指令该端口为输出;反之,若要将某一个端 口当成数字输入使用的话,也要将该埠设定为输入才行。至于各端 口的数字输出入方向的设定,必须将设定数值写到对应的2018香港马会开奖现场去, 表 8-1-9 是各埠所对应的输出入设定。 表 8-1-9 各埠的输出入方向设定 (Write): wBase+&Hc8 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 0 0 CN2_PC CN2_PB CN2_PA CN1_PC CN1_PB CN1_PA (Write): wBase+&Hcc Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 0 0 CN4_PC CN4_PB CN4_PA CN3_PC CN3_PB CN3_PA (Write): wBase+&Hd0 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 0 0 CN6_PC CN6_PB CN6_PA CN5_PC CN5_PB CN5_PA 由表 8-1-9,如果该位为 0,表示作为数字输出;若为 1,表示作 为数字输入,只要设计好每一个端口的位值,就可得知使用 WBase+&Hc0 2018香港马会开奖现场时应该用 OutputByte 或是 InputByte 函式了。 第 367 页,共 386 页8-2 ISA 适配卡 在 PCI 尚未成为主流之前,适配卡的型式以 ISA 接口为主,使 用此种型适配卡需要对于计算机系统的硬件架构稍微了解才能适 切地使用各式的适配卡;其使用的时间相当地久远,也很稳定,目 前为止尚有为数不少的适配卡还是设计在 ISA 的接口中使用,厂商 通常还是供应有 ISA 接口的各种控制卡。 8-2-1 ISA 接口 现在的工业级背板中还是有很多是含有 ISA 接口槽的设计,图 8-2-1 是其中的一种。 图 8-2-1 工业级背板中的 ISA 插槽 图 8-2-1 中含有 PCI 插槽,也含有 ISA 插槽,甚至也可以发现 有 PCI 和 ISA 合在一起的设计;不管如何,只要是含有 ISA 插槽, 就算其后还连接有 PCI 插槽,我们也可以将 ISA 适配卡插入其中, 一般的 ISA 适配卡插入背板处之金手指和 PCI 接口不同,例如图 8-2-2 是编号 ISO-P32C32 的数字输出入适配卡,此适配卡和 PISO-P32C32 在通道数和功能是完全一样的,但是采用 ISA 接口。 第 368 页,共 386 页 图 8-2-2 ISA 接口之数字输出入适配卡 以规划的方式来,使用 ISA 适配卡第一步就必须要清楚所使用 的2018香港马会开奖现场区域,就如第一章所说明的,所有的适配卡都必须在计算机 系统中占有一定的2018香港马会开奖现场区段,以便程控的指令数值可以传送到该地 址,而适配卡可依数值的规定而运作。之前我们采用 PCI 适配卡时, 对于2018香港马会开奖现场的指定是利用函式的呼叫而取得,并不需要我们特别去规 划适配卡所需的2018香港马会开奖现场;但 ISA 适配卡就不一样了,ISA 适配卡一般 会在其卡片上留有一组指拨开关(图 8-2-2 中间即有一个),此指拨 开关用于设定该适配卡的启始2018香港马会开奖现场,决定了启始2018香港马会开奖现场后,就决定了 我们之前所讨论的程序设计中的 WBase,其它的控制方法完全和 PCI 适配卡一样。 如何决定2018香港马会开奖现场呢?首先必须查看系统中还有那一些2018香港马会开奖现场没有被 使用。由于现在的计算机系统所使用硬设备相当多,也会占用到很 多的2018香港马会开奖现场空间,在操作系统中的控制台可以让我们查看现在系统已 经被占用的2018香港马会开奖现场空间,只要避开这些2018香港马会开奖现场的设定就是合法的设定, 查看的方式是点选『控制面板』\『系统』\『装置管理员』\『计算 机』中的内容,接着选择 I/O 显示项目,就可以看到图 8-2-3 的画 面。 第 369 页,共 386 页 图 8-2-3 计算机中的2018香港马会开奖现场使用情形 接着就要调整 ISA 适配卡上的指拨开关到没有其它设备使用的 2018香港马会开奖现场区域,而 ISA 适配卡会提供该指拨开关的调整方式和其对应的 2018香港马会开奖现场,图 8-2-4 是指拨开关的定义,而表 8-2-1 就是 ISO-P32C32 所 提供的2018香港马会开奖现场设定对照表。 图 8-2-4 指拨开关定义 表 8-2-1 2018香港马会开奖现场对照表 Address SW1_1 SW1_2 SW1_3 SW1_4 SW1_5 SW1_6 A8 A7 A6 A5 A4 A3 *&H200 ON ON ON ON ON ON &H208 ON ON ON ON ON OFF &H210 ON ON ON ON OFF ON &H218 ON ON ON ON OFF OFF &H220 ON ON ON OFF ON ON &H228 ON ON ON OFF ON OFF : : : : : : : &H2C0 ON OFF OFF ON ON ON &H2C8 ON OFF OFF ON ON OFF : : : : : : : &H300 OFF ON ON ON ON ON &H308 OFF ON ON ON ON OFF 第 370 页,共 386 页: : : : : : : 适配卡会有一个默认值(例如表 8-2-1 中 &H200 上有一个星号), 此默认值是出厂设定,不一定符合我们所使用的计算机系统环境; 例如 ISO-P32C32 之默认值是&H200(十六进制的 200),但是如果计 算机中装有声卡的话,声卡的预设2018香港马会开奖现场就是&H200,如此一来,我 们的数字输出入适配卡就不能设为&H200,否则二片适配卡就会 『相冲』,轻者其中一片适配卡或是二片适配卡无法动作,重者连 计算机都无法开机。因此,使用 ISA 适配卡就必须针对2018香港马会开奖现场的设定 特别地注意和小心。 接着是中断的设定。计算机系统中共有十六条中断线路可以使 用,硬件可以在必要的时候透过中断线路通知 CPU,以得到相关的 服务;计算机系统里的中断分配也可以由计算机系统内容的信息中 看到,如图 8-2-5 所示。 图 8-2-5 系统中断的设定内容 现在的计算机中使用了很多的 PCI 适配卡,PCI 适配卡会自行 设定所需要的系统资源(2018香港马会开奖现场、中断、直接内存存取),由讯息中可 以知道一个计算机系统里的中断几乎都被使用得差不多了,自行找 出空白的中断2018香港马会开奖现场其实是不容易的。使用 ISA 适配卡又不得不自行 指定中断号码,但若找不到空白的中断号码时应如何处理呢?此种 情形的作法是先找到中断分配表中那一个中断是由 PCI 适配卡所使 用,由于 PCI 适配卡可以多个适配卡共享一个中断,故最好找一个 中断号码是只有一个 PCI 适配卡使用的,接着在计算机内容中选择 『保留资源』卷标页,再按下新增,系统就会让我们新增一个保留 的中断,选择我们希望使用的中断号码,并且按下确定;在重新开 机后,其它的 PCI 适配卡会重新编排所使用的中断,而我们所指定 的中断不会被占用,如图 8-2-5 中的中断号码 09,标示着『系统保 第 371 页,共 386 页留』就是这样作出来的。完成保留的程序后,我们就可以将 ISA 适 配卡的中断指定为我们所保留的中断了。 以 ISO-P32C32 为例,其中断的指定如图 8-2-6 所示,是透过跳 针来指达成,图中的 N.C.指的是不使用中断,其余的号码则是中断 号码。 图 8-2-6 中断 Jumper 设定 在之前的章节也讨论过中断的用法(第六章),用中断的目的通 常是在侦测外界的讯号,当依讯号状态发生变化时进行下一个步骤 的处理动作。ISO-P32C32 的中断可以设定二个,分别给二个数字 输入的通道使用,所以在图 8-2-6 中的插针有二个可以作设定。 8-2-2 步进马达控制卡及 X-Y 滑台 本书在第五章曾经利用计时计数卡控制过步进马达,由于步进 马达使用专用的控制器作连接时,必须输入脉冲使其作步进转动。 除了使用计时计数卡透过程序的控制产生脉冲,进而控制步进马达 之外,也有专门为步进马达所作的控制卡,而步进马达由于特性良 好,进行直线运动时具有相当好的控制性,使其衍生出线性滑台, 滑台有一个自由度的滑台,也有二个自由度的滑台(X-Y Table),有 时我们也称一个自由度和二个自由度为单轴和双轴。 单轴的定位滑台在定位场合中经常使用,其外观如图 8-2-7 所 示。内部的组成除了使用步进马达之外,还有导螺杆;步进马达的 步进角透过导螺杆的转换而成为线性运动。 第 372 页,共 386 页图 8-2-7 单轴定位滑台 此定位滑台除了利用步进马达作正转及逆转使得滑台前进或 后退外,尚有部份的讯号是当作辅助之用;例如图 8-2-7 中的 LS11~LS14 四个点是所谓的极限点(Limit),当滑台运动时,不可以 让滑台超过其所限制的最大前进位置及最大的后退位置,因此需要 透过限制点传回其是否已达极限位置的讯号。 步进马达可以依控制器所给定的脉冲数而作一定角度的运转, 滑台从而作出定位;既然要定位,一定要有一个参考的位置当成原 点,前进及后退就以这个原点当作参考点,图 8-2-7 中的 ORG1 就 是原点,利用这原点的讯号,我们可以得知输出的方波数和位置的 关系。 一般的步进马达的控制器控制马达转动方向的方式有二种,一 种是以脉冲数及方向引脚接点的高低电位来作控制(这是我们在第 五章所使用的),先控制方向接点的电位,以选定要马达转动的方向, 接着再以方波送入方波接点,马达就以选定的方向作转动;另一种 是以二个分开的接点作脉冲输入的控制,一个接点作正向转动,而 另一个接点则作逆向转动;若输入方波给正向通道,则马达作正向 转动;若输入方波给逆向通道,则马达作逆向转动(第五章所述的步 进马达控制器也可以作此种方式的控制)。通常步进马达控制卡都支 持这二种控制方法,图 8-2-8 说明了这二种不同的模式使用时的高 低电位变化。 图 8-2-8 二种控制方式的不同 在图 8-2-8 的上图里,CW 引脚(Clockwise,顺时钟)送入脉冲时, CCW 引脚(Counter Clockwise,逆时钟)是处于低电位,而当 CCW 引脚送入脉冲时,则 CW 引脚处于低电位;下图中,同样的脉冲都 是由 Pulse 引脚送入,但是透过 Direction 引脚(控制器上通常标示 为 DIR)的电位不同,可以控制马达的转动方向。 在滑台的导螺杆长度范围内,极限位置和原点位置都可以由使 用者重新定义,在滑台的内部的极限位置和原点位置使用的是光遮 断器(Photo-Interrupt),而且这些位置都可以移动。光遮断器所产生 第 373 页,共 386 页的作用就类似我们在第三章所提到的光耦合器,它们都可以提供一 个开或关的讯号,适配卡上的数字输入接点可以透过这些讯号得到 状态。 接下来考虑到适配卡的选择。步进马达的控制必须使用到脉冲 输出,透过脉冲数达到控制转动的步进数,除此之外,步进马达的 正逆转在其控制器上是单独的一个接点,此接点的高电位或低电位 决定了此步进马达的转动方向,连带地决定了滑台的前进与后退。 再者是各极限位置的侦测,所谓的极限位置的侦测也就是数字输入 状态的侦测,只要取得各极限点的电位状态,就可以知道滑台是否 已抵达该位置。 图 8-2-9 是编号 Step-200 的 ISA 接口之二轴步进马达控制卡, 此控制卡就拥有上述的特点。 图 8-2-9 步进马达控制卡(ISA 接口) 由于此步进马达控制卡是 ISA 接口,所以使用时必须依 ISA 适 配卡使用的步骤先行设定2018香港马会开奖现场、中断,才能够正常使用控制卡,图 8-2-9 中的左下角可以看出其2018香港马会开奖现场设定的拨码开关。 图 8-2-10 是使用步进马达控制卡时的接头引脚定义。这些定义 和步进马达有关,之前所提供的各个极限开关的接点位置也在其 上,而其编号可以看出此控制可以控制二轴的步进马达,如果我们 以步进马达设计 X-Y 运动平台的话,就可以使用此型的卡片发展其 相关的位置控制程序。 第 374 页,共 386 页 图 8-2-10 步进马达控制卡的接头引脚定义 图 8-2-10 的各引脚定义如表 8-2-2 的说明;卡片所使用的地线 和极限位置传感器所使用的地线最好分开。 表 8-2-2 各控制引脚说明 接脚名称 轴别 意义 CW_PULSE1 X 可作 CW 或 PULSE 控制引脚 CCW_DIR1 X 可作 CCW 或 DIR 控引脚 HOLD1 X 伺服 ON/OFF 或保持 ON/OFF 讯号 CW_PULSE2 Y 可作 CW 或 PULSE 控制引脚 CCW_DIR2 Y 可作 CCW 或 DIR 控引脚 HOLD2 Y 伺服 ON/OFF 或保持 ON/OFF 讯号 /ORG1, /LS11, /LS12 /LS13, /LS14 X 极限位置接入点 /ORG2, /LS21, /LS22 /LS23, /LS24 Y 极限位置接入点 /EMG 急停讯号接入点(可外接急停开关) +5V pin 1 pin 14 内部供给的 5V 电源 GND pin 5 pin 18 地线 EXT_VCC pin 6 外部的 24V 电源(用于极限位置传感器) 第 375 页,共 386 页pin 19 EXT_GND pin 13 pin 25 外部接地点(用于极限位置传感器) 此片运动控制卡采用 DDA 技术作为脉冲输出的依据,表 8-2-3 是其 DDA 数值表。 表 8-2-3 DDA 及脉冲值对照表 DDA cycle DDA period Max. pulse rate(n=2047) Min. pulse rate (n=1) 1 8.192ms 249877pps 122pps 2 12.288ms 166585pps 81pps 3 16.384ms . . . . . . N (N+1)*4.096m s 2047/(DDA period) 1/(DDA period) . . . . 254 1040.384ms 1967pps 0.96pps 由表 8-2-3,不同的 DDA 设定,可以产生的最大脉冲数和最小 脉冲数各不相同,选择的过程通常是稍微小一些的 DDA 值较佳, 较大的 DDA 值会造成较的加减速振动现象;透过不同的 Pulse Rate 的数值指定即可定出最高的输出脉冲数(High Speed)和最低的输出 脉冲数(Low Speed)。 加减速的形成是在高低速切换时的斜率值,由于高低速的脉冲 数不同,如果骤然就在二个速度之间切换将使得滑台不平稳,因此 需要在这个速度间以一定的加减速值作一个切换,以使得转换过程 较为顺畅。 透过运动控制卡,我们将可以顺利及方便地控制线性滑台,图 8-2-11 是笔者用来控制的一种单轴滑台,此图已将上盖拿开,故可 看清楚内部的构造,线性滑台通常的内部作法也就是如此。 第 376 页,共 386 页 图 8-2-11 线性滑台的内部构造 程序的写法: 步进马达控制卡其实也是数字输出入卡的延伸,但其在底层对 于步进马达作了相当大的配合,目的即在顺利地控制马达并侦测各 个极限位置的讯号,基于此点,其函式也就因此作了比较高阶的包 装,低层的 IO 函式已被包装,而以较易了解的高阶函式库出现, 以下是 Step-200 的函式宣告 Declare Sub MSTEP2_INITIAL Lib "Step32.Dll" () Declare Sub MSTEP2_END Lib "Step32.Dll" () '以下是设定函式群 Declare Function MSTEP2_REGISTRATION Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal address As Long) As Byte Declare Function MSTEP2_LIMIT_X Lib "Step32.Dll" (ByVal cardNo As Byte) As Byte Declare Function MSTEP2_LIMIT_Y Lib "Step32.Dll" (ByVal cardNo As Byte) As Byte Declare Function MSTEP2_STATUS Lib "Step32.Dll" (ByVal cardNo As Byte) As Byte '极限状态的读取 Declare Sub MSTEP2_WAIT_X Lib "Step32.Dll" (ByVal cardNo As Byte) Declare Function MSTEP2_IS_X_STOP Lib "Step32.Dll" (ByVal cardNo As Byte) As Byte Declare Sub MSTEP2_WAIT_Y Lib "Step32.Dll" (ByVal cardNo As Byte) Declare Function MSTEP2_IS_Y_STOP Lib "Step32.Dll" (ByVal cardNo As Byte) As Byte '设定控制卡脉冲输出的相关设定 Declare Sub MSTEP2_SET_VAR Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal set_DDA_cycle As Byte, ByVal set_Acc_Dec As Byte, _ ByVal set_Low_Speed As Long, ByVal set_High_Speed As Long) Declare Sub MSTEP2_SET_DEFDIR Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal defdirX As Byte, ByVal defdirY As Byte) Declare Sub MSTEP2_SET_MODE Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal modeX As Byte, ByVal modeY As Byte) Declare Sub MSTEP2_SET_SERVO_ON Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal sonX As Byte, ByVal sonY As Byte) Declare Sub MSTEP2_SET_NC Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal sw As Byte) '各轴的紧急停止命令 Declare Sub MSTEP2_STOP_X Lib "Step32.Dll" (ByVal cardNo As Byte) Declare Sub MSTEP2_STOP_Y Lib "Step32.Dll" (ByVal cardNo As Byte) 第 377 页,共 386 页Declare Sub MSTEP2_RESET_SYSTEM Lib "Step32.Dll" (ByVal cardNo As Byte) Declare Sub MSTEP2_STOP_ALL Lib "Step32.Dll" (ByVal cardNo As Byte) Declare Sub MSTEP2_EMG_STOP Lib "Step32.Dll" (ByVal cardNo As Byte) '移动命令,高速、低速二种,有移至原点或是移动特定脉冲数 Declare Sub MSTEP2_LSP_ORG Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal dir As Byte, ByVal axis As Byte) Declare Sub MSTEP2_HSP_ORG Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal dir As Byte, ByVal axis As Byte) Declare Sub MSTEP2_HSD_ORG Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal dir As Byte, ByVal axis As Byte) Declare Sub MSTEP2_LSP_PULSE_MOVE Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal axis As Byte, ByVal pulseN As Long) Declare Sub MSTEP2_HSP_PULSE_MOVE Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal axis As Byte, ByVal pulseN As Long) Declare Sub MSTEP2_LSP_MOVE Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal dir As Byte, ByVal axis As Byte) Declare Sub MSTEP2_HSP_MOVE Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal dir As Byte, ByVal axis As Byte) Declare Sub MSTEP2_CSP_MOVE Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal dir As Byte, ByVal axis As Byte, ByVal speedN As Long) Declare Sub MSTEP2_SLOW_DOWN Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal axis As Byte) Declare Sub MSTEP2_SLOW_STOP Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal axis As Byte) Declare Sub MSTEP2_INTP_PULSE Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal Xpulse As Long, ByVal Ypulse As Long) Declare Sub MSTEP2_INTP_LINE Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal Xpulse As Long, ByVal Ypulse As Long) '长距离移动指令 Declare Sub MSTEP2_INTP_LONG_LINE Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal x As Long, _ ByVal y As Long, ByVal speed As Long) Declare Sub MSTEP2_CIRCLE Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal x As Long, ByVal y As Long, _ ByVal dir As Byte, ByVal speed As Long) Declare Sub MSTEP2_ARC Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal x As Long, ByVal y As Long, _ ByVal R As Long, ByVal dir As Byte, ByVal speed As Long) '//---------------------------------------------------- Declare Sub MSTEP2_INTP_LINE02 Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal x As Long, _ ByVal y As Long, ByVal speed As Long, ByVal acc_mode As Byte) Declare Sub MSTEP2_INTP_CIRCLE02 Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal x As Long, ByVal y As Long, _ ByVal dir As Byte, ByVal speed As Long, ByVal acc_mode As Byte) Declare Sub MSTEP2_INTP_ARC02 Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal x As Long, ByVal y As Long, _ ByVal R As Long, ByVal dir As Byte, ByVal speed As Long, ByVal acc_mode As Byte) Declare Function MSTEP2_INTP_STOP Lib "Step32.Dll" () As Byte '//---------------------------------------------------- Declare Sub MSTEP2_GET_CARD Lib "Step32.Dll" (ByVal cardNo As Byte) 第 378 页,共 386 页这些指令包括驱动程序初始化、参数设定及移动指令等等,使 用这些指令的顺序如下: 1、 程序开始执行前,使用 MSTEP2_INITIAL()加载驱动程序。 2、 使用 MSTEP2_REGISTRATION 设定卡片号码及2018香港马会开奖现场。 3、 使用 MSTEP2_RESET_SYSTEM 重置系统。 4、 使用 MSTEP2_SET_VAR 设定卡片输出所需的参数,包括 了 DDA 值 (可设定最高及最低的脉冲数,实际的脉冲输出 控制必须在此范围内,脉冲输出的控制卡里经常使用此 技术芯片)、加减速值及高速/低速值。 5、 使用 MSTEP2_SET_DEFDIR 指定方向。 6、 使用 MSTEP2_SET_MODE 指定输出的模式(CW_CCW 模 式或 Pulse_DIR 模式)。 7、 开始其它的控制,如送出脉冲。 程序结束前需要呼叫二个函式,呼叫 MSTEP2_RESET_SYSTEM 以重置系统,呼叫 MSTEP2_END 以释放驱动程序。 以下笔者就依前述的步骤建立一个双轴线性滑台的控制项目, 让我们来看看如何以这片运动控制卡建立控制的程序。 画面设计: 我们要建立一个一般的项目,依以下的建立步骤建立画 面: 1、 开启一个新的 Visual Basic 项目,将窗体拉至适当的大小, 按下 F4 叫出属性窗口,更改其 Caption 属性为”X-Y Table Control”,表示我们将进行双轴的运动控制。 2、 安排 1 个 Frame 控件,将外观拉至适当大小,其 Caption 属 性输入”X 轴控制”。 3、 在上述的 Frame 控件内放入二个文字框控件,其 Name 属性 分别输入”txtXHi”及 ”txtXLo”,而 Text 属性则输入 0,当成 是默认值。此二个控件将用来输入高速转动和低速转动的脉 波数;若为正值表示正转,而负值则表示逆转。 4、 同样在该 Frame 控件内安排二个 Label 控件,Caption 属性 分别输入”转动数(HI)”及 ”转动数(LO)”,用来标示上述的二 个文字框控件。 5、 在同样的 Frame 控件内安排二个命令按钮控件,其 Caption 输入”GO”,当文字框内输入数值后,可以按下此按钮用来 第 379 页,共 386 页驱动 X 轴转动相对应的脉冲数(分别对应到高速及低速)。 6、 另外安排一个按钮控件,其 Caption 属性输入”停止运动”, 用来停止滑台的运动。 7、 重复步骤 2 至步骤 6,同样建立一个一模一样的区块,但是 相关的属性及命令则改为 Y 轴。这组 Frame 控件的数值及 按钮则是用来控制 Y 轴。 8、 在画面安排三个按钮控件,其 Caption 属性分别输入”系统 重置”、”全部停止”及 ”结束系统”,用来执行相对应的功能。 9、 依上述步骤所作出的画面如图 8-2-12。 图 8-2-12 双轴控制的画面设计 功能设计:接下来就依下列步骤作出相对应的功能: 1、 在 Form_Load 事件程序程序中键入以下程序代码 '相关的数值指定 CardNo = 1 CardAddress = &H300 '2018香港马会开奖现场(依适配卡的 Switch 设定) DDAValue = 5 'DDA 芯片基底时间号码设定 Acc_Dcc = 1 '加减速设定 Low_Speed = 10 '低速设定 High_Speed = 150 '高速设定 XDir = NORMAL_DIR '正常转动方向 YDir = NORMAL_DIR '正常转动方向 XMode = PULSE_DIR '讯号模式(脉冲-方向) YMode = PULSE_DIR '讯号模式(脉冲-方向) '以下作控制卡的初始化 MSTEP2_INITIAL MSTEP2_REGISTRATION CardNo, CardAddress MSTEP2_RESET_SYSTEM CardNo MSTEP2_SET_VAR CardNo, DDAValue, Acc_Dcc, Low_Speed, High_Speed MSTEP2_SET_DEFDIR CardNo, XDir, YDir MSTEP2_SET_MODE CardNo, XMode, YMode 窗体一激活,程序就将必要的参数先作设定;这些设定包括了 方向、高低脉冲速度、加速度/减速度值、使用控制模式…等等。 第 380 页,共 386 页部份的常数定义则是放在模块里。由于笔者的计算机中只有一 片运动控制卡,故其 CardNo 设为 1;2018香港马会开奖现场的设定则依适配卡上 的 Switch 而定,笔者的设定是&H300。当读者要作设定时,必 须检视一下系统内容,以确定所使用的2018香港马会开奖现场值是合法的。 这些设定中比较须注意的是 DDA 的相关设定,讨论如下: 由于 DDA 的值设定为 5 ,因此其使用 DDA 周期是 (5+1)*4.096ms=24.576ms。 加减速设为 1 ,故其在高低速间切换时的斜率为 1/(24.576)=2655.5(pps/s^2)。 低速值为 10,故其 PPS 值为 LowSpeed=10/24.576=407(pps)。 因此在低速运动的过程中是以每秒钟 407 个脉冲的速度送给步 进马达驱动器。 高速值为 150,故其 PPS 值为 HighSpeed=150/24.576=6107(pps)。 因此在高速运动的过程中是以每秒钟 6107 个脉冲的速度送给 步进马达驱动器。 2、 双击”X 轴控制”的 Frame 控件内的上方标示为”GO”的按钮控 件,在此控件的 Click 事件中输入 MSTEP2_HSP_PULSE_MOVE CardNo, X_axis, CInt(txtXHi.Text) 此段程序代码将使用者设定的高速转动脉冲数值( 此例中为 6107pps)送至以函式 MSTEP2_HSP_PULSE_MOVE 送到运动控 制卡,运动控制卡即会以加速度、最高脉冲数及减速度的顺序 作使用者输入的脉冲数输出工作。 3、 双击”X 轴控制”的 Frame 控件内的下方标示为”GO”的按钮控 件,在此控件的 Click 事件中输入 MSTEP2_LSP_PULSE_MOVE CardNo, X_axis, CInt(txtXLo.Text) 此段程序代码将使用者设定的低速转动脉冲数值送至以函式 MSTEP2_LSP_PULSE_MOVE 送到运动控制卡,输出的脉冲速 度则是以设定的低速(此例是 407pps)为之。 4、 双击”X 轴控制”的 Frame 控件内的下方标示为”停止运动”的按 钮控件,在此控件的 Click 事件中输入 MSTEP2_STOP_X CardNo 此段程序代码将不管 X 轴脉冲已输出的数量,马上停止 X 轴的 运动状态。 5、 重复步骤 2 至步骤 4,对画面右边的”Y 轴控制”之 Frame 控件写 入程序,但将原来的 X 轴控制转而对 Y 轴的控制,这个部份的 程序代码则列如下 第 381 页,共 386 页 Private Sub cmdStopY_Click() '停止 Y 轴的运动 MSTEP2_STOP_Y CardNo End Sub Private Sub cmdYHi_Click() '要求 Y 轴依脉冲数转动(高速) MSTEP2_HSP_PULSE_MOVE CardNo, Y_axis, CInt(txtYHi.Text) End Sub Private Sub cmdYLo_Click() '要求 Y 轴依脉冲数转动(低速) MSTEP2_LSP_PULSE_MOVE CardNo, Y_axis, CInt(txtYLo.Text) End Sub 除了轴名称不同外,其余的部份均和 X 轴相同。 6、 双击 「系统重置」的控钮控件,在其 Click 事件中写入以下程序 代码 '以下作控制卡的重置 MSTEP2_RESET_SYSTEM CardNo MSTEP2_SET_VAR CardNo, DDAValue, Acc_Dcc, Low_Speed, High_Speed MSTEP2_SET_DEFDIR CardNo, XDir, YDir MSTEP2_SET_MODE CardNo, XMode, YMode 按下此按钮后会将卡片作重置的处理,并将参数回复初始值。 7、 双击 「全部停止」的控钮控件,在其 Click 事件中写入以下程序 代码 MSTEP2_STOP_ALL CardNo 按下此按钮后会将停止 X、 Y 轴的运动状态。 8、 双击 「结束系统」的控钮控件,在其 Click 事件中写入以下程序 代码 '重置系统 MSTEP2_RESET_SYSTEM CardNo '释放驱动程序资源 MSTEP2_END End '结束程序 在重置系统及释放程序资源后,以 End 指令结束程序的执行。 完成以上的设计后,我们可以开始进行测试。请将 X-Y 滑台和 运动控制卡所延伸出来的 25Pin 接座连接妥当即可开始作测试;需 要注意的是,并不是每组滑台都具有 Step200 运动控制卡上所示的 前后各二组极限开关,有些滑台只有一组开关。 第 382 页,共 386 页根据笔者以图 8-2-11 的滑台作测试发现其效果相当不错,可以 正确地送出控制脉冲,二组输出均正常动作。 本项目作出的是双轴的运动,除此之外,根据函式的内容可以 看出,我们可以作的尚有较为复杂的轨迹运动,这部份的函式是 Declare Sub MSTEP2_INTP_PULSE Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal Xpulse As Long, ByVal Ypulse As Long) Declare Sub MSTEP2_INTP_LINE Lib "Step32.Dll" (ByVal cardNo As Byte, ByVal Xpulse As Long, ByVal Ypulse As Long) 这个部份就留给读者们自行思考和实作了。 详细的项目程序代码请参考”EX\CH8\Motion”,双击档案夹中的 Project1.vbp 项目即可进行以上的实验。 详细的程序代码如下: '************************************** '结束系统的 Click 事件 '事件中处理重置指令、释收资源 '最后以 End 指令结束程序 '************************************** Private Sub cmdEND_Click() '重置系统 MSTEP2_RESET_SYSTEM CardNo '释放驱动程序资源 MSTEP2_END End '结束程序 End Sub '************************************** '系统重置指令 '主要的程序代码和 Form_Load 事件一样 '************************************** Private Sub cmdReset_Click() '以下作控制卡的重置 MSTEP2_RESET_SYSTEM CardNo MSTEP2_SET_VAR CardNo, DDAValue, Acc_Dcc, Low_Speed, High_Speed MSTEP2_SET_DEFDIR CardNo, XDir, YDir MSTEP2_SET_MODE CardNo, XMode, YMode End Sub '************************************** 'StopALL 的 Click 事件 '将所有的轴运动停止 '************************************** Private Sub cmdStopALL_Click() '停止 X-Y 轴的运动 MSTEP2_STOP_ALL CardNo End Sub 第 383 页,共 386 页'************************************** 'StopX 的 Click 事件 '将 X 轴运动停止 '************************************** Private Sub cmdStopX_Click() '停止 X 轴的运动 MSTEP2_STOP_X CardNo End Sub '************************************** 'StopY 的 Click 事件 '将 Y 轴运动停止 '************************************** Private Sub cmdStopY_Click() '停止 Y 轴的运动 MSTEP2_STOP_Y CardNo End Sub '************************************** 'X 轴高速运动的 Click 事件 '控制 X 轴转动一定步数 '************************************** Private Sub cmdXHi_Click() '要求 X 轴依脉冲数转动(高速) MSTEP2_HSP_PULSE_MOVE CardNo, X_axis, CInt(txtXHi.Text) End Sub '************************************** 'X 轴低速运动的 Click 事件 '控制 X 轴转动一定步数 '************************************** Private Sub cmdXLo_Click() '要求 X 轴依脉冲数转动(低速) MSTEP2_LSP_PULSE_MOVE CardNo, X_axis, CInt(txtXLo.Text) End Sub '************************************** 'Y 轴高速运动的 Click 事件 '控制 Y 轴转动一定步数 '************************************** Private Sub cmdYHi_Click() '要求 Y 轴依脉冲数转动(高速) MSTEP2_HSP_PULSE_MOVE CardNo, Y_axis, CInt(txtYHi.Text) End Sub '************************************** 'Y 轴低速运动的 Click 事件 '控制 Y 轴转动一定步数 '************************************** Private Sub cmdYLo_Click() '要求 Y 轴依脉冲数转动(低速) MSTEP2_LSP_PULSE_MOVE CardNo, Y_axis, CInt(txtYLo.Text) End Sub '************************************** '窗体的 Load 程序 '在此程序中设定卡片2018香港马会开奖现场、卡片号码 '转动方向、高低速设定等和运动控制卡相关的设定 第 384 页,共 386 页'************************************** Private Sub Form_Load() '相关的数值指定 CardNo = 1 CardAddress = &H300 '2018香港马会开奖现场(依适配卡的 Switch 设定) DDAValue = 5 'DDA 芯片基底时间号码设定 Acc_Dcc = 1 '加减速设定 Low_Speed = 10 '低速设定 High_Speed = 150 '高速设定 XDir = NORMAL_DIR '正常转动方向 YDir = NORMAL_DIR '正常转动方向 XMode = PULSE_DIR '讯号模式(脉冲-方向) YMode = PULSE_DIR '讯号模式(脉冲-方向) '以下作控制卡的初始化 MSTEP2_INITIAL MSTEP2_REGISTRATION CardNo, CardAddress MSTEP2_RESET_SYSTEM CardNo MSTEP2_SET_VAR CardNo, DDAValue, Acc_Dcc, Low_Speed, High_Speed MSTEP2_SET_DEFDIR CardNo, XDir, YDir MSTEP2_SET_MODE CardNo, XMode, YMode End Sub 模块 Module1.bas 的内容是公用变量的宣告,其内容如下 '************************ '共享变量宣告 '************************ Public CardAddress As Long '卡片2018香港马会开奖现场 Public CardNo As Byte '卡片编号设定 Public DDAValue As Byte 'DDA 数值 Public Acc_Dcc As Byte '加减速设定 Public Low_Speed As Integer '低速值设定 Public High_Speed As Integer '高速值设定 Public XMode As Byte 'X 轴讯号模式设定 Public YMode As Byte 'Y 轴讯号模式设定 Public XDir As Byte, YDir As Byte 'X,Y 轴方向设定 至于 Step32.bas 就如本小节上述,请参考本小节上述的说明。 第 385 页,共 386 页本章习题 1、 请说明使用 ISA 适配卡的步骤。 2、 一系统需要的数字输出 50 个通道,并需隔离,如何选择输 出适配卡? 3、 请指出步进马达转线性滑台控制的几个特点。 4、 若需要使用单轴步进马达控制卡,其程序的架构应如何设 计 ?硬件上的接线又应如何?(以第五章所述之 Vexta 控制器 为例) 5、 依 8-2-2 的程序,将位置侦测(原点和左右极限)状态显示有 画面上,程序应如何撰写? 6、 欲使得 X-Y 滑台作出弦波运动的话,程序应如何撰写? 第 386 页,共 386 页
还剩386页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 8 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

1147768077

贡献于2013-12-17

下载需要 8 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf