ВВЕДЕНИЕ. Где-то год назад, передо мной встала задача использовать LPT порт компьютера для подключения к одному устройству через интерфейс JTAG, с целью считывания дампа flash-памяти. Эту задачу я решил взвалить на хрупкие плечи BlitzMax-а. И только я начал разработку, как у меня сменился рабочий комп. На новом уже и небыло параллельного порта, так что я даже не смог проверить свое творение))). Но все-таки хочу рассказать обо всем этом. Времени правда прошло предостаточно, где-то я что-то и подзабыл, ну да ладно буду вспоминать по ходу дела. ПАРАЛЛЕЛЬНЫЙ ПОРТ (LPT). Странно, но в новых материнских платах, стали избавляться от такого большого довеска как паралельный порт. Хотя это и не сложно понять, ведь этот интерфейс уже довольно таки старый и теперь уже нигде почти не используется, помимо этого еще и занимает предостаточно места. На смену ему пришел USB - универсальная шина, на которую можно цеплять всё: от мышки до подогревалки кофе или точилки для карандашей или другого ЮСБ-барахла. Тем не менее для программистов работающих с перефирийными устройствами, этот паралельный порт подходил как нельзя кстати. Он содержит 25 пинов. В этом его недостаток(занимает много места), но в этом его и приемущество. В отличие от последовательных шин, этот порт может передавать данные паралельно, что и так понятно из названия))) Первоначально он использовался для подключения принтеров к компьютеру, если помните они так орали при печати, как кошки в мае не кричат. И в связи с тем что он часто использовался для подключения принтеров у него были некоторые специфичные сигналы и названия. Состоит порт из 3 логических портов(еще я буду называть их регистрами) 1)порт данных (DATA) 2)порт состояния (STATUS) 3)порт контроля (CONTROL) Изначально порт данных был только для вывода, но теперь он может быть использован и для входа данных. Насколько я знаю, устанавливается режим в БИОС-е. (поправьте если не так) Как они распределены на контактере, можно увидеть из рисунка: Как видите дата-порт имеет 8 пинов (контакты 2-9). Чаще всего работают с ним, так как к нему легко обращаться с байтом данных(8 бит) и он двунаправленный, остальные порты в этом плане не полноценны. Регистр STATUS(контакты 10-13 и 15) служит только для чтения данных из-вне. Следующий регист CONTROL(контакты 1, 14, 16-17) он еще меньше - всего 4 контта(пина) умеет работать только на вывод данных. Итого мы видим 1 полный двунаправленный порт и 2 однонаправленных, да еще и не полных. Некоторые пины имеют свои технические названия, например 16 - INITIALIZE - и служит для инициализации принтера. Конечно, для инициализации достаточно и одной направленности, потому эти сигналы и остались с давних времен однонаправленными, так сказать для совместимости. Остальные контакты земляные GND. Тем не менее, программисту есть где развернуться ))). Программирование. На каждом компьютере может быть установлено до 4 LPT-портов. И чтобы как-то с ними разбираться у них есть адрессация. Так, например, первый LPT-порт имеет адрес 0x378 в 16-тиричной системе счисления, второй 0x278. Эти адреса называются базовыми для порта(BASE_ADDRESS) и указывают на регистр DATA. Чтоб узнать адрес регистра STATUS порта надо просто прибавить 1 к базовому адресу, а чтоб получить адрес регистра CONTROL надо прибавить 2 к базовому адресу. Вот пример: BASE_ADDRESS = 0х378 // LPT1 DATA_REG_ADDRESS = BASE_ADDRESS // 0x378 STATUS_REG_ADDRESS = BASE_ADDRES + 1 // 0x379 CONTROL_REG_ADDRESS = BASE_ADDRESS + 2 // 0x37A Да, раньше в ДОС-е и Win`95(8) можно было обращаться к порту простыми функциями записи в порт. Но с NT системами такое уже не проходит (надо работать через WinApi) а через WinApi мне работать как-то не хотелось))) . И о счастье, бродя по интернету в поисках решения этой проблемы малой кровью, я зашел на сайт http://www.pcports.ru на котором рассматривалась библиотека inpout32.dll и чем она хороша, что позволяет работать с портом независимо в какой системе ты работаешь в 95(8) или в NT/2000. Вот схема того как она функционирует, с сайта разработчика этой библиотеки.
как вы сами видите, она сначала проверяет какая система установлена у пользователя, а потом применяет необходимые функции записи/чтения из порта. Хорошо, что такая библиотека нашлась. Правда есть и еще много таких же либ, для работы с параллельным портом, но эта имеет малый размер, работает на нескольких системах и имеет в себе все 2 необходимых нам функции. Для чтения(Inp32()) и записи(Out32()) в порт. Вот их прототипы: Out32(port, data); // Out32(адрес_порта, данные) data = Inp32(port); // данные = Inp32(адрес_порта) Конечно помимо этих функций она содержит еще ряд, таких как открытие и закрытие драйвера, проверка системы и др. Но они более низкоуровневые и нами использоваться не будут. Если вы хотите их изучить подробнее или посмотреть исодный код, то посетите эту страничку http://logix4u.net/Legacy_Ports/Parallel_Port/How_Inpout32.dll_works_.html Кстати, на их сайте так же сть форум, в котором комьюнити поможет решить вашу проблему с программированием портов. Нам же надо двигаться дальше. BlitzMax + dll. Просто великолепно, нужная библиотека у нас есть. Осталось научить БМ понимать и использовать ее функции в наших целях. Для того чтобы воспользоваться библиотекой, все равно придется использовать WInApi функцию. Называется она LoadLibraryA , которая принимает имя библиотеки в кодировке ANSI, для unicode есть функция LoadLibraryW. И возвращает она хэндл библиотеки. Его надо проверить на нуль, чтоб убедиться что либа успешно загружена. SuperStrict
Global DllHandle:Int = LoadLibraryA("inpout32.dll")
If (Not DllHandle) RuntimeError(" Where is the file ~qinpout32.dll~q ? ") End If
Кстати, мы можем не только узнать что наша dll-ка не загружена, но так же узнать более подробное сообщение об ошибке. ВинАпи предоставляет такую возможность функцией GetLastError(), которая возвращает значение последнего неудачного Windows API вызова, а уже по номеру можно найти ее текстовое описание в msdn. Или сформировать это описание другими способами. Но это уже в другую степь. Теперь из этой либы нам надо заимствовать парочку функций, для которых мы это всё и затевали. Делается это так:
Global Inp32:Int( port:Int )"Win32" = GetProcAddress(DllHandle,"Inp32") Global Out32( port:Int, value:Int )"Win32" = GetProcAddress(DllHandle,"Out32") GetProcAddress - так же ВинАпи функция. В случае успешного завеpшения возвращает точку входа в функцию, в пpотивном случае возвращает 0. Принимает хэнд библиотеки и название функции из этой библиотеки. Вот после этого можно использвать эти функции, как нам угодно в нашей программе на БМ, все остальное сделает dll-ка. ))) Программа. SuperStrict
Global DllHandle:Int = LoadLibraryA("inpout32.dll")
If (Not DllHandle) RuntimeError(" Where is the file ~qinpout32.dll~q ? ") End If
Global Inp32:Int(port:Int) "Win32" = GetProcAddress(DllHandle, "Inp32") Global Out32(port:Int, value:Int) "Win32" = GetProcAddress(DllHandle, "Out32")
Const LPT1:Short = $378
Const LPT_DATA:Int = 0 Const LPT_STATUS:Int = 1 Const LPT_CONTROL:Int = 2
Type _Flag Field value:Byte Method Set(bit:Int) value :| (1 Shl bit) End Method
Method Get:Int(bit:Int) Return (value & (1 Shl bit)) Shr bit End Method
Method Clear(bit:Int) value :& ~(1 Shl bit) End Method Method Toggle(bit:Int) value :~ (1 Shl bit) End Method
Method Reset() value = 0 End Method End Type
Type _Port
Field Address:Short Field idreg:Int Field in:_Flag = New _Flag Field out:_Flag = New _Flag Method New() Address = LPT1 idreg = LPT_DATA in.Reset() out.Reset() End Method
Method Delete() in = Null out = Null End Method Function Create:_Port(_address:Short, _idreg:Int) Local Port:_Port = New _Port Port.Address = _address + _idreg Port.idreg = _idreg Return Port End Function Method Write(value:Byte) out.value = value Out32(Address, out.value) End Method Method Read:Byte() Return Inp32(Address) End Method
Method Set(pin:Int) out.value = Read() Select(idreg) Case LPT_STATUS WriteStdout("Impossible! ") Case LPT_CONTROL Select pin Case 4, 5, 6, 7 WriteStdout("CONTROL registrer used 0-3 bits. "); Return End Select End Select out.Set(pin) Write(out.value)
End Method
Method Clr(pin:Int) out.value = Read() Select(idreg) Case LPT_STATUS WriteStdout("Impossible! ") Case LPT_CONTROL Select pin Case 4, 5, 6, 7 WriteStdout("CONTROL registrer used 0-3 bits. "); Return End Select End Select out.Clear(pin) Write(out.value)
End Method
Method Get:Int(pin:Int) in.value = Read() Select( idreg )
Case LPT_STATUS
Select pin Case 0, 1, 2 WriteStdout("STATUS registrer used 3-7 bits. ") End Select
Case LPT_CONTROL Select pin Case 4, 5, 6, 7 WriteStdout("CONTROL registrer used 0-3 bits. ") End Select End Select
Return in.Get(pin) End Method Method Clear() in.Reset() out.Reset() End Method
End Type
Local i:Int Local CtrlPort:_Port = _Port.Create(LPT1, LPT_CONTROL) Local DataPort:_Port = _Port.Create(LPT1, LPT_DATA) Local StatPort:_Port = _Port.Create(LPT1, LPT_STATUS)
'CtrlPort.Set(0) 'CtrlPort.Set(1) 'CtrlPort.Set(2) 'CtrlPort.Set(3)
Print
For i = 0 Until 8 Print "DATA[" + i + "] = " + DataPort.Get( i ) Next
Print
For i = 0 Until 8 Print "STATUS[" + i + "] = " + StatPort.Get( i ) Next
Print
For i = 0 Until 8 Print "CONTROL[" + i + "] = " + CtrlPort.Get( i ) Next
While Not KeyHit(KEY_ESCAPE) Wend
End
Как я и говорил, программу протестировать мне не удалось. Даже посмотреть на каком-нибудь снифере LPT-порта. Если вдруг, что-то не работает, то сообщите мне об этом. А если работает- то я тоже хочу об этом знать ))) К слову, когда писалась эта статья, я посетил сайт разработчика inpout32.dll там уже анонсирована следующая библиотека (hwinterface32), которая имеет более обширный функционал. Теперь помимо записи в порт данных разного размера, там есть функции для записи/считывания памяти. Если кому интересно, то сейчас идет ее бета-тестирование, можете помочь отловить пару блох. http://logix4u.net/Legacy_Ports/Parallel_Port/How_to_read_Parallel_Serial_Port_address_from_BIOS.html Спасибо, за внимание. Удачи Вам в разработке ;)
|