Драйвер для работы с LPT портом (интегрированным или в виде платы PCI)
Лет 10-15 назад параллельный порт был довольно востребованным интерфейсом для связи между устройствами. Сегодня производители различных устройств отдают предпочтение последовательным интерфейсам. Тем не менее, и сегодня LPT порт всё ещё можно встретить. А некоторые разработчики ещё пишут под него программы (или поддерживают написанные во времена расцвета параллельных интерфейсов). Но в компьютерах сейчас LPT порт – достаточно большая редкость. Существуют, конечно, платы расширения для компьютера, которые реализуют интерфейс LPT. Они в основном представляют собой платы на шине PCI. К сожалению, далеко не весь софт, который был написан для интегрированных в материнскую плату LPT портов, будет работать с LPT-PCI-платами.
Предлагается решение этой проблемы: программный класс, написанный под .NET, и динамическую библиотеку, которая работает с LPT. Данная реализация проверена на 32- и 64-разрядных Windows XP, Windows 7, 8 и 10 как со встроенными LPT портами, так и реализованными в виде плат расширения на шине PCI или PCI-Express. Скачать драйвер для LPT порта можно по ссылке внизу после статьи.
1Установка драйвера для работы с LPT портом
В приложенном архиве находятся две папки – для 32-разрядной и для 64-разрядной версий Windows. В одной из папок лежит файл InstallDriver.exe. Сначала запустите этот файл, он установит динамические библиотеки в систему. После этого компьютер следует перезагрузить.
Чтобы использовать предлагаемый драйвер, файлы inpout32.dll и inpoutx64.dll должны располагаться в одной директории с исполняемым файлом вашей программы.
2Программный класс использования библиотек работы с LPT портом
Мной был написан класс для .NET, который использует динамические библиотеки inpout32.dll и inpoutx64.dll и позволяет считывать из параллельного порта и записывать в его регистры данные.
Упомянутые библиотеки написаны не мной. Я предлагаю удобный класс-оболочку, упрощающий работу с данными библиотеками. Кроме того, автор библиотек в файле readme.txt сообщает, что его драйвер не поддерживает PCI устройства. Мне удалось его запустить для работы как с интегрированным параллельным портом, так и LPT портом, реализованном в виде платы расширения на шине PCI-Express. Причём порт отлично работает и на современной Windows10 x64, и на более старых системах.
Вот код класса LPT на языке VB.NET:
Код класса LPT на языке VB.NET (разворачивается)
Imports System.Runtime.InteropServices ''' <summary> ''' Работа с LPT портом. При создании определяет наличие драйвера и выбирает подходящую библиотеку. ''' </summary> Public NotInheritable Class LPT #Region "CONST" Public Const DLL32 As String = "c:\temp\inpout32.dll" Public Const DLL64 As String = "c:\temp\inpoutx64.dll" Public Const DLL32DRV As String = "c:\temp\inpoutx32drv.dll" ''' <summary> ''' Регистры параллельного порта. ''' </summary> Public Enum Register As Integer ''' <summary> ''' Регистр данных SPP/EPP. ''' </summary> DATA = 0 ''' <summary> ''' Регистр состояния SPP/EPP. ''' </summary> STATUS = DATA + 1 ''' <summary> ''' Регистр управления SPP/EPP. ''' </summary> CONTROL = DATA + 2 ''' <summary> ''' Регистр адреса EPP. Чтение или запись в него генерирует свзяанный цикл чтения или записи адреса EPP. ''' </summary> EPP_ADDRESS = DATA + 3 ''' <summary> ''' Регистр данных EPP. Чтение (запись) генерирует связанный цикл чтения (записи) данных EPP. ''' </summary> EPP_DATA = DATA + 4 ''' <summary> ''' Регистр активации режима EPP. ''' </summary> ''' <remarks> ''' Если только смешанный "ECP+EPP" или "Extended" - скорее всего потребуется еще выполнить запись в порт ECR байта 0x80 ''' (ECR - это регистр ECP-режима, ECR=BASE_ADDR+0x402, BASE_ADDR - базовый адрес LPT-порта ''' </remarks> ECR = DATA + &H402 End Enum #End Region '/CONST #Region "DllImport" <DllImport(DLL32, CharSet:=CharSet.Auto, EntryPoint:="IsInpOutDriverOpen")> Private Shared Function IsInpOutDriverOpen() As Byte End Function <DllImport(DLL64, CharSet:=CharSet.Auto, EntryPoint:="IsInpOutDriverOpen")> Private Shared Function IsInpOutDriverOpen_x64() As UInt64 End Function <DllImport(DLL32, CharSet:=CharSet.Auto, EntryPoint:="Inp32")> Private Shared Function Inp32(PortAddress As UInt32) As Byte End Function <DllImport(DLL32, CharSet:=CharSet.Auto, EntryPoint:="Out32")> Private Shared Sub Out32(portAddress As UInt32, data As UInt32) End Sub <DllImport(DLL64, CharSet:=CharSet.Auto, EntryPoint:="Inp32")> Private Shared Function Inp64(PortAddress As UInt64) As Byte End Function <DllImport(DLL64, CharSet:=CharSet.Auto, EntryPoint:="Out32")> Private Shared Sub Out64(PortAddress As UInt64, Data As UInt64) End Sub #End Region '/DllImport #Region "CTOR" ''' <summary> ''' Проверяет возможность использования x86 или x64 драйвера. В случае невозможности выбрасывает исключение "драйвер не установлен". ''' </summary> Shared Sub New() Dim x86 As Boolean = CheckIsDriverX86() Dim x64 As Boolean = CheckIsDriverX64() If x64 Then _IsX64DriverUsed = True ElseIf x86 Then _IsX64DriverUsed = False Else Throw New SystemException("Драйвер для LPT порта не установлен.") End If End Sub ''' <summary> ''' Инициализация порта - выставление XXXX0100'b в регистре контроля. ''' </summary> Private Shared Sub InitLpt(port As ULong) Write(port, Register.ECR, &H80) Write(port, Register.CONTROL, &H4) End Sub #End Region '/CTOR #Region "PROPS" ''' <summary> ''' Используется ли 64-разрядная версия драйвера. ''' Если драйвер не установлен, возвращается NULL. ''' </summary> Public Shared ReadOnly Property IsX64DriverUsed As Boolean? Get Return _IsX64DriverUsed End Get End Property Private Shared ReadOnly _IsX64DriverUsed As Boolean? = Nothing #End Region '/PROPS #Region "METHODS" ''' <summary> ''' Определяет, является ли драйвер 32-разрядным. ''' </summary> Private Shared Function CheckIsDriverX86() As Boolean Try Dim res As Byte = IsInpOutDriverOpen() Return True Catch ex As Exception Return False End Try End Function ''' <summary> ''' Определяет, является ли драйвер 64-разрядным. ''' </summary> Private Shared Function CheckIsDriverX64() As Boolean Try Dim nResult As ULong = IsInpOutDriverOpen_x64() Return (nResult <> 0) Catch ex As Exception Return False End Try End Function #End Region '/METHODS #Region "Read / Write LPT Port" ''' <summary> ''' Читает из заданного регистра LPT порта 1 байт данных. ''' </summary> ''' <param name="port">Номер порта.</param> ''' <param name="reg">Регистр порта.</param> Public Shared Function Read(port As ULong, Optional reg As Register = Register.DATA) As Byte Dim data As Byte = 0 If IsX64DriverUsed Then data = Inp64(CULng(port + reg)) Else data = Inp32(CUInt(port + reg)) End If Return data End Function ''' <summary> ''' Записывает в заданный регистр LPT порта число. ''' </summary> ''' <param name="port">Адрес порта.</param> ''' <param name="reg">Регистр порта.</param> ''' <param name="data">Число для записи. В 32-разрядных системах аргумент должен быть не более, чем максимальное значение для типа UInt32.</param> Public Shared Sub Write(port As ULong, reg As Register, data As ULong) If IsX64DriverUsed Then Out64(port + CULng(reg), data) Else If (data <= UInteger.MaxValue) Then Out32(CUInt(port + reg), CUInt(data)) Else Throw New ArgumentException("В 32-разрядных системах аргумент должен быть 32-разрядным (тип UInt32).", "data") End If End If End Sub #End Region '/Read / Write LPT Port ''' <summary> ''' Тестирует наличие LPT порта по заданному адресу. ''' </summary> ''' <param name="lptAddress">Адрес параллельного порта.</param> Public Shared Function CheckPortPresent(lptAddress As ULong) As Boolean Dim portPresent As Boolean = False Try InitLpt(lptAddress) Dim data As ULong = Read(lptAddress, Register.DATA) 'сохраняем текущее значение регистра данных 'Проверим: что записали, то и прочитали? Write(lptAddress, Register.DATA, &H0) portPresent = portPresent And (&H0 = Read(lptAddress, Register.DATA)) Write(lptAddress, Register.DATA, &H55) portPresent = portPresent And (&H55 = Read(lptAddress, Register.DATA)) Write(lptAddress, Register.DATA, &HAA) portPresent = portPresent And (&HAA = Read(lptAddress, Register.DATA)) Write(lptAddress, Register.DATA, data) 'восстанавливаем прежнее значение регистра данных 'Проверим наличие регистров управления и данных, если порт не обнаружен (в случае однонаправленного порта) If (Not portPresent) Then data = Read(lptAddress, Register.CONTROL) portPresent = ((data <> 0) AndAlso (data <> &HFF)) 'Не пустое значение? => Порт присутствует. If (Not portPresent) Then data = Read(lptAddress, Register.STATUS) portPresent = ((data <> 0) AndAlso (data <> &HFF)) End If End If Catch ex As Exception Return False End Try Return portPresent End Function End Class '/LPT
Код класса LPT на языке C#:
Код класса LPT на языке C# (разворачивается)
using System; using System.Diagnostics; using System.Runtime.InteropServices; namespace LPTIO { /// Класс для работы LPT портом. При создании определяет наличие драйвера и его разрядность. public sealed class LPT { private const string DLL32 = "inpout32.dll"; private const string DLL64 = "inpoutx64.dll"; private static bool _IsX64DriverUsed; /// Используется ли 64-разрядная версия драйвера. public static bool IsX64DriverUsed { get { return LPT._IsX64DriverUsed; } } static LPT() { if (LPT.getIsDriverX86()) { LPT._IsX64DriverUsed = false; } else { if (!LPT.getIsDriverX64()) throw new SystemException("Драйвер для LPT порта не установлен."); LPT._IsX64DriverUsed = true; } } [DebuggerNonUserCode] public LPT() { } [DllImport("inpout32.dll", CharSet = CharSet.Auto)] private static extern byte IsInpOutDriverOpen(); [DllImport("inpoutx64.dll", EntryPoint = "IsInpOutDriverOpen", CharSet = CharSet.Auto)] private static extern ulong IsInpOutDriverOpen_x64(); [DllImport("inpout32.dll", CharSet = CharSet.Auto)] private static extern byte Inp32(uint PortAddress); [DllImport("inpout32.dll", CharSet = CharSet.Auto)] private static extern void Out32(uint portAddress, uint data); [DllImport("inpoutx64.dll", EntryPoint = "Inp32", CharSet = CharSet.Auto)] private static extern byte Inp64(ulong PortAddress); [DllImport("inpoutx64.dll", EntryPoint = "Out32", CharSet = CharSet.Auto)] private static extern void Out64(ulong PortAddress, ulong Data); /// Определяет, является ли драйвер для 32-разрядной версии Windows. private static bool getIsDriverX86() { bool flag; try { LPT.IsInpOutDriverOpen(); flag = true; } catch (Exception ex) { ProjectData.SetProjectError(ex); flag = false; ProjectData.ClearProjectError(); } return flag; } /// Определяет, является ли драйвер для 64-разрядной версии Windows. private static bool getIsDriverX64() { bool flag; try { flag = Decimal.Compare(new Decimal(LPT.IsInpOutDriverOpen_x64()), Decimal.Zero) != 0; } catch (Exception ex) { ProjectData.SetProjectError(ex); flag = false; ProjectData.ClearProjectError(); } return flag; } /// <summary>Читает из LPT порта 1 байт данных.</summary> /// <param name="port">Номер порта.</param> /// <param name="reg">Регистр порта.</param> public static byte Read(ulong port, LPT.Register reg = LPT.Register.DATA) { return !LPT.IsX64DriverUsed ? LPT.Inp32(Convert.ToUInt32(Decimal.Add(new Decimal(port), new Decimal((int) reg)))) : LPT.Inp64(Convert.ToUInt64(Decimal.Add(new Decimal(port), new Decimal((int) reg)))); } /// <summary>Записывает в LPT порт число.</summary> /// <param name="port">Адрес порта.</param> /// <param name="reg">Регистр порта.</param> /// <param name="data">Число для записи. В 32-разрядных системах аргумент должен быть не более, чем максимальное значение для типа UInt32.</param> public static void Write(ulong port, LPT.Register reg, ulong data) { if (LPT.IsX64DriverUsed) { LPT.Out64(checked (port + (ulong) (uint) reg), data); } else { if (data > (ulong) uint.MaxValue) throw new ArgumentException("В 32-разрядных системах аргумент должен быть 32-разрядным (тип UInt32).", "data"); LPT.Out32(Convert.ToUInt32(Decimal.Add(new Decimal(port), new Decimal((int) reg))), checked ((uint) data)); } } /// <summary>Регистры порта LPT.</summary> public enum Register { DATA, STATUS, CONTROL, } } }
3Применение класса .NET для работы с LPT портом
Если мы посмотрим на список экспортируемых функций библиотеки inpout32.dll с помощью замечательного инструмента DLL Export Viewer от NirSoft, то увидим следующую картину:
Это список функций, которые мы можем использовать. Фактически все они используются в классе LPT, но реализация скрыта, и из публичных методов пользователю доступны только два метода и одно свойство (они рассматриваются чуть далее).
При инстанцировании класс сам определит, библиотеку какой разрядности ему использовать – inpout32.dll или inpoutx64.dll. Поэтому от пользователя не требуется никаких действий по инициализации или определения разрядности используемой dll. Вы можете сразу записывать или читать из LPT порта. Одно «Но»: если драйвер не установлен, обращение к любому из методов динамической библиотеки вызовет исключение, поэтому рекомендую использовать блоки Try…Catch для перехвата и обработки исключений.
Несколько примеров использования класса LPT.
Для определения, используется ли 64-разрядная версия драйвера (inpoutx64.dll при True) или 32-разрядная (inpout32.dll при False) (на самом деле, это знать не обязательно, класс использует именно ту библиотеку, которая нужна, но вдруг вам для чего-то понадобится это узнать из своей программы):
bool is64bitDriver = LPT.IsX64DriverUsed;
Для записи числа "123" в регистр контроля LPT порта вызовите из своего класса:
LPT.Write(currentPort, LPT.Register.CONTROL, 123);
Для чтения одного байта из регистра данных LPT порта и чтения регистра статуса:
byte b = LPT.Read(currentPort, LPT.Register.DATA); byte s = LPT.Read(currentPort, LPT.Register.STATUS);
Здесь currentPort – адрес LPT порта. Причём, если у вас интегрированный LPT порт, то его адрес будет, скорее всего, 378h. А если у вас LPT порт на плате расширения, то адрес будет другой, например, D100h или C100h.
Чтобы узнать адрес LPT порта, зайдите в диспетчер устройств Windows, найдите раздел Порты COM и LPT, выберите используемый параллельный порт, и в окне свойств (щёлкнув по нему правой кнопкой мыши) посмотрите, какие ресурсы использует выбранный порт (необходимо брать первое значение из диапазона).
Например, в данном случае необходимо использовать номер порта C100.
Download attachments:
- Скачать драйвер для LPT порта (12642 Downloads)
Поблагодарить автора:
Поделиться
10 comments
-
Роман Понедельник, 21 Январь 2019 20:24 Ссылка на комментарий
Вы не подскажете,что дальше делать? Я скачал visual C , скопировал ваш код для С# и вставил. но при отладке выдает ошибки. Например такое: Ошибка 1 Элемент "ProjectData" не существует в текущем контексте. C:\Users\Роман\AppData\Local\Temporary Projects\ConsoleApplication1\Program.cs 71 17 ConsoleApplication1
Я ни бум-бум. Но мне нужно изменить аддресацию для Pci-LPT платы St-lab i400 Parallel Port. Там значения нестандартные прописались D090-D097. Что делать? Хотелось бы иметь возможность активировать этот код -
aave1 Вторник, 22 Январь 2019 19:06 Ссылка на комментарий
Роман, а что значит скачал "Visual C"? Дело в том, что нужна подходящая среда разработки (IDE), например, Visual Studio или Visual Studio Code. Какую вы используете? Конкретно по ошибке "Элемент "ProjectData" не существует в текущем контексте" я вам не подскажу. Скорее всего дело в настройках проекта, это уж смотрите сами. Попросите кого-то более опытного помочь настроить IDE. Код рабочий, 100%. Мне по работе приходится иметь дело со старым оборудованием, и я успешно использую его. Только я использую VB.NET, а код на C# получил путём декомпиляции готовой сборки. Попробуйте использовать код на VB.NET. Он к тому же проще для понимания новичкам.
-
Роман Четверг, 24 Январь 2019 18:51 Ссылка на комментарий
Привет! Я понял, что я не то что-то делаю. Я попробую то что вы советуете - среду vb.net. Мне только главное знать,поможет ли это мне запустить старый лпт сканер 98 года: canoscan fb320p. Так как сканер отказывает работать с нестандартными адресами порта. Пока что единственный способ каким мне удалось решить эту проблему, это установка vmware player - добавление виртуального lpt, которому автоматом присваиваются правильные адреса и сканер тут же реагирует и начинает работать. Только есть в этом одна проблема: очень медленно! Минут 20-30 занимает сканирование 300 точек. Хотелось бы как-то решить эту проблему без vmware, но вот думаю, поможет ли этот код, так как я в описании функций не вижу там инструкций для смены аддресации....
-
aave1 Пятница, 25 Январь 2019 17:36 Ссылка на комментарий
Мне кажется, что для решения вашей задачи со сканером это решение не подходит, и запустить старый сканер не получится. Потому что придётся самому переписывать софт для сканирования. Ваша задача должна решаться как-то на уровне драйвера порта или той программы, которая используется для сканирования. Может быть, есть обновлённые драйверы для сканера с возможностью указывать адрес порта. А может проще найти старый комп, где на материнке присутствует LPT. Кстати, Asus до сих пор выпускает материнские платы с LPT под более-менее современные процессоры, например, модель H81M-C.
-
Василий Суббота, 27 Апрель 2019 05:20 Ссылка на комментарий
Спасибо тебе, добрый человек. Установка этого драйвера и замена inpout32.dll в директории исполняемой программы, помогли запустить софт, который ранее работал только на Win XP из под Win 10x64
-
Анатолий Вторник, 10 Сентябрь 2019 08:28 Ссылка на комментарий
В программировании я не разбираюсь. Есть такая проблема: для прошивки контроллеров AVR раньше использовал программатор AVReal. Он быстро и качественно работает. Покупая новый компьютер я специально искал материнку с LPT портом. Однако в Win10-64бит старые драйвера ввода-вывода не работают и они должны иметь цифровую подпись. Соответственно получаю сообщение об ошибке при использовании программатора. У меня вопрос: можно ли с Вашим драйвером LPT порта решить эту проблему? Какие шаги по установке и использованию я должен выполнить? Если не сложно, скиньте ответ на мою почту
-
aave1 Вторник, 10 Сентябрь 2019 18:03 Ссылка на комментарий
Анатолий, попробуйте заменить файлы inpout32.dll и inpoutx64.dll в директории вашей программы (если они там есть) на файлы с теми же названиями из приложенного к статье архива. На всякий случай, конечно, сделайте резервные копии оригинальных файлов. Если не поможет, попробуйте отключить обязательную проверку цифровой подписи и установите ваши драйвера как обычно. Как отключить проверку цифровой подписи я описывал тут: https://soltau.ru/index.php/arduino/item/356-how-install-drv-usbasp, начиная с пункта 2.
-
Paul Воскреснье, 15 Октябрь 2023 06:54 Ссылка на комментарий
Уважаемый автор! Спасибо за Ваш труд, действительно интересно. Но у меня выходит по-другому:
WIndows такую штуковину, как у Вас на фото, определяет почему-то как "Последовательный порт" или как "Неизвестное устройство PCI". Я выделяю это устройство и в установках драйвера прописываю "Стандартный параллельный порт" или "Стандартный Порт принтера ECP", Windows брыкается, конечно, но надо подтвердить, что взять именно этот драйвер.
Вуаля!
*Мой п/ящик украл mail.ru, новый заводить некогда)), ящик указал не тот. -
aave1 Воскреснье, 15 Октябрь 2023 17:19 Ссылка на комментарий
Paul, спасибо за альтернативное решение)