机器控制器
使用串行端口或并行端口控制机器
引言
系统要求
- Windows XP, 7, Vista, 8, 8.1, 10, 11
- .NET Framework v4
关于此解决方案
- 此解决方案包含两个 Windows 应用程序,一个用于串行端口,另一个用于并行端口
- VB 用于开发一个非常简单的 RS232 控制器应用程序,该应用程序可以轻松使用串行端口。
- 它支持将您的命令存储在文本文件中以供将来使用
- 它支持将文件发送到串行端口
- 为并行端口控制器创建了一个单独的应用程序
- 此解决方案还包含一个最近使用的文件菜单栏库,该库在 RS232 控制器中使用,以跟踪最后使用的文件,并且该库可用于任何 Windows 应用程序。
使用此应用程序
- 下载并安装应用程序
- 使用合适的电缆将设备连接到计算机
- 打开您的机器
- 运行应用程序并使用它来发送和接收机器数据
- 存储您的命令或机器输出以供将来使用
使用机器控制器的原因
如果您需要扩展机器的使用范围,您可能需要通过机器控制器来控制它。这可以分为以下几类:基于时间的控制、基于事件的控制和通信控制。
基于时间的控制
基于时间的操作包括基于经过一定时间的操作,或在特定时间执行的操作。
基于事件的控制
如果您希望机器在特定条件下执行某些操作,或者在发生其他事件时执行操作。
通信控制
它将有助于向机器发送命令或数据以更改其启动或运行状态,或从机器接收数据以供其他应用程序处理。
什么是串行端口
串行端口是一种串行通信物理接口,信息通过该接口一次传输一个位(与并行端口相反)。在个人计算机的大部分历史中,数据通过串行端口传输到调制解调器、终端和各种外围设备。
什么是 RS-232
Recommended Standard-232 是计算机和外围设备之间串行传输的标准。使用 25 针 DB-25 或 9 针 DB-9 连接器,其正常的 50 英尺电缆限制可以通过高质量电缆延长至数百英尺。许多设备和机器都有内置的 RS232 端口。利用此端口非常简单。所有编程语言都支持此端口。
关于并行端口
并行端口、打印机端口或 LPT 端口是与外部设备通信的另一种简便方法。我提供了一个可用于控制并行端口连接设备的应用程序。Windows NT 版本不允许直接访问端口,因此我们需要一个驱动程序才能直接与端口通信。因此,我们正在使用一个免费驱动程序和库。该库支持 Windows 的 32 位和 64 位版本,适用于 x86 和 x64 CPU。
与并行端口通信
并行端口通常是一个 25 针母口,通常用于将打印机连接到计算机。它可用于控制继电器盒。许多公司提供用于并行端口的继电器盒,可用作 PLC 来控制机器。并行端口包含三种类型的寄存器:数据、状态和控制寄存器。状态寄存器是只读寄存器,用于获取端口状态。
关注点
关于最近使用的文件库
- 我们首先构建
MostRecentlyFilesItem
类,它继承自ToolStripMenuItem
- 我们添加了一个
FileName
属性 - 一旦设置了
FileName
属性,该菜单项的文本将更改为文件编号和文件名
//C# Code
public sealed override string Text
{
get
{
try
{
if (this.Enabled)
{
var number = this.Index + 1;
var sEntryName = ShortenPathname(this.FileName);
if (number < 10)
{
return "&" + number + " " + sEntryName;
}
else if (number == 10)
{
return "1&0" + " " + sEntryName;
}
else
{
return number + " " + sEntryName;
}
}
else
{
return DefaultText;
}
}
catch (Exception ex)
{
if (_ErrMsg.ErrMsg(ex))
{
System.Diagnostics.Debugger.Break();
}
return null;
}
}
set
{
//
}
}
'VB Code
Public NotOverridable Overrides Property Text As String
Get
Try
If Me.Enabled Then
Dim number = Me.Index + 1
Dim sEntryName = ShortenPathname(Me.FileName)
If number < 10 Then
Return "&" & number & " " & sEntryName
ElseIf number = 10 Then
Return "1&0" & " " & sEntryName
Else
Return number & " " & sEntryName
End If
Else
Return DefaultText
End If
Catch ex As Exception
If ErrMsg(ex) Then Diagnostics.Debugger.Break()
Return Nothing
End Try
End Get
Set(value As String)
'
End Set
End Property
然后我们构建主菜单项管理器,它将控制菜单项
//C# Code
public class MostRecentlyFiles : MostRecentlyFilesItem, IList<string>
'Vb Code
Public Class MostRecentlyFiles
Inherits MostRecentlyFilesItem
Implements IList(Of String)
- 前面的类被继承,以作为文件菜单项之一,并使更密集地控制它
- 它
Implements
IList(Of String)
以作为文件列表,只需向其添加文件,它就会在菜单项中正确显示 - 要设置所有项,您可以调用
IList.AddRange
方法 - 添加了
Files
属性来获取或设置菜单中文件的逗号分隔列表 - 如果文件不存在,将被忽略。
//C# code
public void Add(string sFileName)
{
try
{
if (string.IsNullOrEmpty(sFileName) || !System.IO.File.Exists(sFileName))
{
return;
}
this.Enabled = true;
if (Count > 0)
{
int Index = _EntriesFiles.IndexOf(sFileName);
if (Index >= 0)
{
if (Index == 0)
{
return;
}
this._EntriesFiles.Remove(sFileName);
this._EntriesFiles.Insert(0, sFileName);
for (int i = 0; i < this._EntriesFiles.Count; i++)
{
this._EntriesItems[i].FileName = this._EntriesFiles[i];
}
return;
}
}
while (!(this.Count <= _EntriesMaxCount))
{
this.RemoveAt(this.Count - 1);
}
MostRecentlyFilesItem menuItem = null;
if (this.Count == 0 && !HasDropDownItems)
{
this.Initialize(this, sFileName, OnClick);
menuItem = this;
}
else
{
menuItem = new MostRecentlyFilesItem(this, sFileName, OnClick);
MenuItems.Insert(StartIndex, menuItem);
}
this._EntriesFiles.Insert(0, sFileName);
this._EntriesItems.Insert(0, menuItem);
}
catch (Exception ex)
{
if (_ErrMsg.ErrMsg(ex))
{
System.Diagnostics.Debugger.Break();
}
}
}
'VB code
Public Sub Add(ByVal sFileName As String) Implements IList(Of String).Add
Try
If String.IsNullOrEmpty(sFileName) OrElse Not IO.File.Exists(sFileName) Then
Exit Sub
End If
Me.Enabled = True
If Count > 0 Then
Dim Index As Integer = _EntriesFiles.IndexOf(sFileName)
If Index >= 0 Then
If Index = 0 Then Exit Sub
Me._EntriesFiles.Remove(sFileName)
Me._EntriesFiles.Insert(0, sFileName)
For i As Integer = 0 To Me._EntriesFiles.Count - 1
Me._EntriesItems(i).FileName = Me._EntriesFiles(i)
Next
Exit Sub
End If
End If
Do Until Me.Count <= _EntriesMaxCount
Me.RemoveAt(Me.Count - 1)
Loop
Dim menuItem As MostRecentlyFilesItem
If Me.Count = 0 AndAlso Not HasDropDownItems Then
Me.Initialize(Me, sFileName, AddressOf OnClick)
menuItem = Me
Else
menuItem = New MostRecentlyFilesItem(Me, sFileName, AddressOf OnClick)
MenuItems.Insert(StartIndex, menuItem)
End If
Me._EntriesFiles.Insert(0, sFileName)
Me._EntriesItems.Insert(0, menuItem)
Catch ex As Exception
If ErrMsg(ex) Then Diagnostics.Debugger.Break()
End Try
End Sub
Public Property Files As String
Get
'NEH
If Count = 0 Then
Return ""
Else
Return Join(_EntriesFiles.ToArray, ",")
End If
End Get
Set(value As String)
Try
Me.Clear()
If String.IsNullOrEmpty(value) Then Exit Property
Dim vItems = Split(value, ",")
If vItems Is Nothing OrElse vItems.Length = 0 Then Exit Property
AddRange(vItems)
Catch ex As Exception
If ErrMsg(ex) Then Diagnostics.Debugger.Break()
End Try
End Set
End Property
关于 RS232Controller
- 内置的 SerialPort 控件已扩展,使读写数据更加容易
- 监控
DataReceived
,当找到任何新数据时,将触发DataReaded
事件
//C# code
public class SerialPortEx : SerialPort
{
public void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
//Me.BytesToRead Not Me.ReadBufferSize
byte[] BytesReaded = new byte[this.BytesToRead];
int BytesReadedCount = this.Read(BytesReaded, 0, this.BytesToRead);
if (BytesReadedCount == 0)
{
return;
}
string sData = this.Encoding.GetString(BytesReaded, 0, BytesReadedCount);
OnDataReaded(sData, false);
}
catch (Exception ex)
{
if (_ErrMsg.ErrMsg(ex))
{
System.Diagnostics.Debugger.Break();
}
}
}
public event EventHandler<LinkClickedEventArgs> DataReaded;
'VB code
Public Class SerialPortEx
Inherits SerialPort
Public Sub OnDataReceived(sender As Object, e As SerialDataReceivedEventArgs) _
Handles Me.DataReceived
Try
'Me.BytesToRead Not Me.ReadBufferSize
Dim BytesReaded(Me.BytesToRead - 1) As Byte
Dim BytesReadedCount As Integer = Me.Read(BytesReaded, 0, Me.BytesToRead)
If BytesReadedCount = 0 Then
Exit Sub
End If
Dim sData As String = Me.Encoding.GetString(BytesReaded, 0, BytesReadedCount)
OnDataReaded(sData, False)
Catch ex As Exception
If ErrMsg(ex) Then Diagnostics.Debugger.Break()
End Try
End Sub
Public Event DataReaded As EventHandler(Of LinkClickedEventArgs)
关于 LPTController
- 支持多个驱动程序来读取和写入数据。
- 应用程序将检查可用的驱动程序以供使用。
- 如果找不到驱动程序或找不到并行端口,将使用虚拟驱动程序。
PortIO
类是读写端口的主要类,可以按如下方式使用
//C# code
PortIO vProtIO = new PortIO();
//Read
var Value = vProtIO.PortData;
//or
var DataRegister0 = vProtIO.DataRegister(PortIO.BitRegister.Bit0);
//Write
vProtIO.PortData = Value;
//or
vProtIO.SetDataRegister(PortIO.BitRegister.Bit0, DataRegister0);
'VB code
Dim vProtIO As New PortIO
'Read
Dim Value = vProtIO.PortData
'or
Dim DataRegister0 = vProtIO.DataRegister(PortIO.BitRegister.Bit0)
'Write
vProtIO.PortData = Value
'or
vProtIO.SetDataRegister(PortIO.BitRegister.Bit0, DataRegister0)
PortIO 类成员
我使用一个类来访问不同的库,这个类有以下成员
属性
Port
:设置或获取要读写的端口PortAddress
:获取所选端口的地址PortData
:以一个字节值获取或设置所有数据寄存器的值PortControl
:以一个字节值获取或设置所有控制寄存器的值PortStatus
:以一个字节值获取所有状态寄存器的值IsTestSigning
:检查驱动程序的测试签名是开启还是关闭
函数和方法
PortsCount
:获取 PC 中可用并行端口的数量Inp
:从特定端口地址获取所有寄存器值(一个字节值)Out
:为特定端口地址设置所有寄存器值(一个字节值)InpPhys
:从物理内存获取值(winring0 不支持)DataRegister
:获取特定数据寄存器的布尔值SetDataRegister
:设置特定数据寄存器的布尔值StatusRegister
:获取特定状态寄存器的布尔值ControlRegister
:获取特定控制寄存器的布尔值SetControlRegister
:设置特定控制寄存器的布尔值IsAdmin
:检查应用程序是否以管理员身份运行RunAsAdmin
:以管理员身份运行指定的应用程序RunAsAdminRestart
:以管理员身份重新启动当前应用程序
事件
GotError
:在处理库时引发错误时发生
寻址并行端口和寄存器
端口寻址在端口编程中起着重要作用,因为它是程序连接到外部电路或设备的门户。因此,我想解释所有可用的端口地址。
在普通 PC 中,并行端口地址可能因 BIOS 设置和其他问题而异。最常见的地址是 &h378
,但是您可以通过检查存储在 &H408
中的数据来从 BIOS 获取所有并行端口地址,并且以下函数将执行此操作:此函数是内置的 PortIO
类
//C# code
private void GetParallelPortAddress()
{
try
{
_PortsAddresses = new List<UInt16>();
for (int nPortNo = 0; nPortNo <= 9; nPortNo++)
{
var pPortAddressLocation = new IntPtr(0x408 + 2 * nPortNo);
var nPortAddress = _PortIO.InpPhys(pPortAddressLocation).ToInt32();
if (nPortAddress > 0 && nPortAddress < 0xFFF)
{
_PortsAddresses.Add(Convert.ToUInt16(nPortAddress));
}
else
{
break;
}
}
}
catch (Exception ex)
{
_ErrMsg.ErrMsg(ex);
}
}
'VB code
Private Sub GetParallelPortAddress()
Try
_PortsAddresses = New List(Of UInt16)
For nPortNo As Integer = 0 To 9
Dim pPortAddressLocation = New IntPtr(&H408 + 2 * nPortNo)
Dim nPortAddress = _PortIO.InpPhys(pPortAddressLocation).ToInt32
If nPortAddress > 0 AndAlso nPortAddress < &HFFF Then
_PortsAddresses.Add(CType(nPortAddress, UInt16))
Else
Exit For
End If
Next
Catch ex As Exception
ErrMsg(ex)
End Try
End Sub
使用 LPT 控制器所需的设备
要使用此应用程序,您应该购买一个并行端口继电器盒,例如 KIT 74 V2、CK1601、quasar。
自己构建继电器盒时,请记住您不能消耗太多电流,因为这会烧坏您的并行端口或整个主板。因此,请自行承担风险……!