65.9K
CodeProject 正在变化。 阅读更多。
Home

机器控制器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (33投票s)

2013年10月30日

CPOL

6分钟阅读

viewsIcon

73395

downloadIcon

2590

使用串行端口或并行端口控制机器

引言

系统要求

  • 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 V2CK1601quasar

自己构建继电器盒时,请记住您不能消耗太多电流,因为这会烧坏您的并行端口或整个主板。因此,请自行承担风险……!

有用链接

© . All rights reserved.