Hardware Access/ko

From Lazarus wiki
Jump to navigationJump to search

Deutsch (de) English (en) español (es) français (fr) magyar (hu) 日本語 (ja) 한국어 (ko) polski (pl) português (pt) русский (ru) slovenčina (sk) 中文(中国大陆)‎ (zh_CN)

개관

이 페이지는 라자루스상에서 하드웨어 장치(디바이스)에 접근하기위한 뉴토리얼의 시작입니다. 이 장치들은 ISA, PCI, USB, parallel port, serial port 등을 포함하지만 이것들에 제한된것은 아닙니다. 완전한 멀티 플랫폼상에서 하드웨어에 접근하는 것은 프리파스칼 라이브러리나 LCL에의해 구현되지는 않았으므로 이 튜토리얼은 다른 플랫폼상에서의 기초적인 하드웨어 접근 방법을 다룰 것 입니다. 조건적인 컴파일에 의해 다른 플랫폼에서 컴파일된 코드는 다음과 같을 것이다: <delphi>

uses
 Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ExtCtrls,
{$IFDEF WIN32}
  Windows;
{$ENDIF}
{$IFDEF Unix}
  ports;
{$ENDIF}

</delphi>

이 시점에서 Mac OX/x86이 HW 접근을 허용하는지는 아직 확실치는 않다. 이 경우에서는 내가 가정하듯이 위의 방법이 허용되지 않을 수 있지만 곧 io.dll같은 드라이버가 나타날 것이다.

패러랠과 시리얼 의 비교

ISA카드, PCI 카드 및 패러랠 포트는 parallel 프로토콜에 의해 컴퓨터와 통신을 한다. 시리얼 포트와 USB장치는 serial 프로토콜로 통신한다. 프로세서와 프로그래밍 언어는 모두 데이터에 패러랠한 접근을 통해 작업을 수행하기 때문에 이런 종류의 프로토콜에 대한 접근은 소프트웨어 측면에서 보면 구현하기가 더 쉽다.예를 들어, Integer 변수에 접근할 때, 단일 명령으로 그 값에 접근할 수가 있다. 그러나 시리얼 포트 프로토콜에서는 동시에 한개의 비트만을 알 수 밖에 없으며 데이터를 이해하기 위해서는 그 비트 조각들을 붙여 놓을 필요가 있다.

시리얼 툥신은 직접적으로 구현하기가 어렵지만, 미리 만들어 놓은(pre-made) 컴포넌트를 사용한다면 매우 쉽게 구현할 수가 있다.이는 하드웨어 측면에서도 역시 어려운건 마찬가지이기 때문에 많은 장치들은 그것을 구현하기 위해 특별한 통합 써킷이나 마이크로 컨트롤러를 사용한다.

이제 다음에 하드웨어 접근 프로토콜에 대한 간략한 비교가 있다:

속도 하드웨어 구현의 난이도
시리얼 포트 매우 느림 (< E5 bit/s) 중간
패러랠 포트 느림 (~ E6 bit/s) 쉬움
ISA 카드 중간(~ E7 bit/s) 중간
USB 중간(~ E7 bit/s) 어려움
PCI 카드 매우 빠름 (> E9 bit/s) 매우 어려움

패러랠 통신

윈도우에서 inpout32.dll의 이용

윈도우즈는 9x시리즈와 NT시리즈에서 하드웨어 접근에 있어서 다른 방법을 취하고 있다. 9x 시리즈(95, 98, Me)에서 프로그램은 DOS상에서 했듯이 하드웨어에 직접 접근 할 수가 있었다. 그러나 NT 시리즈(Windows NT and XP)에서는 이런 접근방법을 허용하고 있지 않다. 이 구조에서 하드웨어 포트와의 모든 통신은 디바이스 드라이버를 통해 이루어진다. 이것은 보안 메커니즘이지만, 작은 프로젝트에서도 드라이버 개발에 시간과 비용 등 많은 댓가를 추가하게 된다.

행복하게도 이 문제를 해결할 수 있는 라이브러리가 있다. 만약 Windows NT가 탐지 된다면, HWInterface.sys 커널 디바이스 드라이버의 압축을 해제하고 설치를 한다. 만약 Windows 9x가 탐지된다면 하드웨어 접근은 단순히 어셈블러 opcode를 이용한다.

그렇지만 이 라이브러리를 어떻게 사용할까요? 간단합니다! 이것은 Inp32 와 Out32 등의 단지 두개의 함수만이 있으므로 사용법은 매우 직관적이다

라이브러리를 동적으로 로드하므로, 먼저 두개의 함수를 정의해 보자:

<delphi>

type
  TInp32 = function(Address: SmallInt): SmallInt; stdcall;
  TOut32 = procedure(Address: SmallInt; Data: SmallInt); stdcall;

</delphi>

  • Address는 접근하려는 포트의 어드레스를 표시한다
  • Out32는 지정한 Address의 포트로 Data를 전송한다
  • Inp32는 지정한 포트로부터 한개의 byte를 되돌려 준다

이제 라이브러리를 로드한다. 이것은 작성한 프로그램의 메인폼의 OnCreate 메소드같은 곳에서 구현할 수가 있다:

<delphi>

type
  TMyForm = class(TForm)
  .........
  private
    { private declarations }
    Inpout32: THandle;
    Inp32: TInp32;
    Out32: TOut32;
  .........
implementation
  .........
procedure TMyForm.FormCreate(Sender: TObject);
begin
{$IFDEF WIN32}
  Inpout32 := LoadLibrary('inpout32.dll');
  if (Inpout32 <> 0) then
  begin
    // needs overtyping, plain Delphi's @Inp32 = GetProc... leads to compile errors
    Inp32 := TInp32(GetProcAddress(Inpout32, 'Inp32'));
    if (@Inp32 = nil) then Caption := 'Error';
    Out32 := TOut32(GetProcAddress(Inpout32, 'Out32'));
    if (@Out32 = nil) then Caption := 'Error';
  end
  else Caption := 'Error';
{$ENDIF}
end;

</delphi>

OnCreate 에서 라이브러리를 로드했으면 OnDestroy에서 언로드하는 것을 잊으면 안된다:

<delphi>

procedure TMyForm.FormDestroy(Sender: TObject);
begin
{$IFDEF WIN32}
  FreeLibrary(Inpout32);
{$ENDIF}
end;

</delphi>

다음에 Inp32 함수를 이용하는 방법에 관한 단순한 예제가 있다:

<delphi>

{$IFDEF WIN32}
  myLabel.Caption := IntToStr(Inp32($0220));
{$ENDIF}

</delphi>

이 코드는 Windows XP상의 포트 $0220에 있는 커스텀 ISA카드에서 Lazarus 0.9.10으로 터스트하였다. 물론 이코드를 실행하기 위해서는 uses 절에 Windows를 넣어야 한다. 개발을 위해 할 일은 작성하는 응용프로그램과 같은 디렉토리에 있는"inpout32.dll"를 인클루드(include)하기만 하면 된다.

이 라이브러리에 관한 홈페이지는 다음과 같다: www.logix4u.net/inpout32.htm *토론을 보세요*

Windows 9x에서 어셈블러 사용

Windows 9x에서 역시 어셈블러 코드를 사용할 수 있다. $CC를 $320 포트에 쓰고싶다고 가정해보자. 다음 코드가 그렇게 해줄 것이다:

<delphi>

{$ASMMODE ATT}
...
   asm
       movl $0x320, %edx
       movb $0xCC, %al
       outb %al, %dx
   end ['EAX','EDX'];

</delphi>

Windows상에서 문제 해결

Windows상에서 플럭앤플레이를 지원하지 않는 패러랠 하드웨어를 사용할 때 문제를 일으키는 가능한 한가지는 당신의 하드웨어에 의해 사용되고 있는 포트를 다른 하드웨어에 할당할 때 일어난다. Windows가 당신의 디바이스 주소에 플럭앤플레이 디바이스를 할당하지 못하도록 하는 방법을 아래 URL에서 그에 대해 설명된 것을 찾을 수 있을 것이다:

http://support.microsoft.com/kb/135168

리눅스상에서 포트 접근을 위해서는 ioperm을 사용

리눅스에서 하드웨어에 접근하는 가장 좋은 방법은 디바이스 드라이버를 통해 하는 것이지만 드라이버를 만드는 작업이 너무 복잡함으로 인해 가끔은 빠른 방법이 매우 유용하다.

리눅스에서 "ports" unit를 이용하기 위해서는 프로그램은 root로 실행해야만 하며 포트접근을 위한 적절한 퍼미션 설정을 위해 IOPerm을 콜해야 한다. "ports" 유닛에 관한 문서는 다음에서 찾을 수 있다. 이곳.

첫번째 할 일은 (g)libc를 링크하고 IOPerm을 콜하는 것이다. 유닛과 연결된 완전한 (g)libc가 프리파스칼에 존재하지만 이 유닛은 어플리케이션이 직접 사용할 때 문제를 야기하고, 또한 이것을 완전한 (g)libc 라이브러리에 정적으로 연결하는 것은 호환되지 않은 방법으로 버전이 변화하기 때문에 좋은 생각은 아니다. 그러나 ioperm같은 함수는 변하지 않을 것 같다.

<delphi>

{$IFDEF Linux}
function ioperm(from: Cardinal; num: Cardinal; turn_on: Integer): Integer; cdecl; external 'libc';
{$ENDIF}

</delphi>

  • "from" 은 접근할 수 있는 첫번째 포트를 표시한다.
  • "num" 은 첫번째 접근한 다음의 포트 수이므로 ioperm(&220, 8, 1)은 프로그램에게 $220과 $227를 포함한 그 사이의 모든 포트를 접근하게 할 것이다.

IOPerm에 링크 한 후 포트에 접근하기 위해 port[<Address>]를 사용할 수 있다.

<delphi>

{$IFDEF Linux}
  i := ioperm($220, 8, 1);
  port[$220] := $00;
  myLabel.Caption := 'ioperm: ' + IntToStr(i);
  i := Integer(port[$220]);
  myOtherLabel.Caption := 'response: ' + IntToStr(i);
{$ENDIF}

</delphi>

이 코드는 커스텀 ISA 카드의 포트 $0220에서 테스트 하였으며, Mandriva Linux 2005 와 Damn Small Linux 1.5상에서 Lazarus 0.9.10을 사용하였다.

General UNIX Hardware Access

<delphi> {$IFDEF Unix} Uses Clib; // retrieve libc library name. {$ENDIF}

{$IFDEF Unix} function ioperm(from: Cardinal; num: Cardinal; turn_on: Integer): Integer; cdecl; external clib; {$ENDIF} </delphi>


Note that FPC provides an abstraction for ioperm called "fpioperm" in unit x86, and also defines out and inport functions. These functions are currently implemented for Linux/x86 and FreeBSD/x86.

It is not recommended to link to libc unless absolutely necessary due to possible deployment and portability functions. Also manual linking to libc (by declaring ad hoc libc imports for functions that are available elsewhere) like done above is not recommended (e.g. the above libc import line will unnecessarily fail if the standard C lib is not called libc, like e.g. libroot on BeOS, or on platforms with a non standard C symbol mangling).

Note 2 Using _unit_ libc is not recommended under any circumstances other than Kylix compability. This because the unit is relatively unportable (due to excessive exposure of structures and other private symbols) and must only be modified as little as possible out of Kylix compability issues.

Serial Communication

It is very easy to build a serial communication software using the Synaser library. The example when used together with the Synaser documentation should be trivial to understand. The most important part is TBlockSerial.Config to configure the speed (in bits per second), data bits, parity bits, stop bits and handshake protocol, if any. The following code was tested with a serial mouse connected to COM 1.

<delphi> program comm;

{$apptype console}

uses

 Classes, SysUtils, Synaser;

var

 ser: TBlockSerial;

begin

 ser:=TBlockSerial.Create;
 try
   ser.Connect('COM1');
   ser.config(1200, 7, 'N', SB1, False, False);
   while True do
     Write(IntToHex(ser.RecvByte(10000), 2), ' ');
 finally
   ser.free;
 end;

end. </delphi>

The following code-example is an alternative version of the example above. The example above seems to have a critically fault in its main concept, to be exactly, it is the part with "while true do...". On the Test - System (Asus A6T Laptop with Digitus USB to RS232 Adapter, Ubuntu 8.04.1), this part caused the following error: The application ran only one time successfully per session, when the application was started again, the application was unable to connect to the serial port. Thus, a reboot was necessary everytime the user tried to relaunch the application, which is/was a really annoying bug.

The reason is not difficult to understand: The application is in the while true do - loop, which is, to be more precisely, an endless loop. There is no abort-condition, so the only way to close the application is to close the terminal or to press CTRL-C. But if you quit the application this way, the important part with "ser.free", which frees the serial port, will never be called. This problem is described in the following thread in the German Lazarus-Forum http://www.lazarusforum.de/viewtopic.php?f=10&t=2082

There is a bit code around the main application to make every user clear, not to press CTRL-C. If anyone is worrying, why /dev/ttyUSB0 is used for com-port: this is due to the USB to Serial Adapter (from Digitus) on the test-system. If you have an built-in serial port, please use the 'Com0' - declaration like in the code - example above.

<delphi>

program serialtest;

{$mode objfpc}{$H+}

uses

 {$IFDEF UNIX}{$IFDEF UseCThreads}
 cthreads,
 {$ENDIF}{$ENDIF}
 Classes,SysUtils,Synaser,Crt
 { you can add units after this };
 var l:boolean;
 function check_affirmation():boolean;
 var k:string;
 begin
      Writeln('To quit the application please do NOT use CTRL-C! Instead, please press any key to quit the application! '+
      'Please confirm this notification before the application continues! '+
      '[0]=Quit, [1]=Confirm, please continue! ');
      Writeln('Your decision: ');
      Read(k);
      if StrtoInt(k) = 1 then
      begin
           check_affirmation:=true;
           Writeln('OK, application continues ...');
      end
      else
      begin
           check_affirmation:=false;
           Writeln('Abort');
      end
 end;
 procedure RS232_connect;
 var
    ser: TBlockSerial;
 begin
      ser:=TBlockSerial.Create;
      try
         ser.Connect('/dev/ttyUSB0'); //ComPort
         Sleep(1000);
         ser.config(1200, 7, 'N', SB1, False, False);
         Write('Device: ' + ser.Device + '   Status: ' + ser.LastErrorDesc +' '+
         Inttostr(ser.LastError));
         Sleep(1000);
         repeat
               Write(IntToHex(ser.RecvByte(10000), 2), ' ');
         until keypressed; //Important!!!
      finally
             Writeln('Serial Port will be freed...');
             ser.free;
             Writeln('Serial Port was freed successfully!');
      end;
 end;
 begin
    l:=check_affirmation();
    if l=true then
    RS232_connect()
    else
    Writeln('Program quit! ');
 end.

</delphi>

Also, the External Links section has UNIX and Windows serial port tutorials.

USB

libusb

A cross platform possibility for Linux, BSDs and Mac OS X is libusb.

Headers are listed in http://www.freepascal.org/contrib/db.php3?category=Miscellaneous:

name author version date link remarks
libusb.pp Uwe Zimmermann 0.1.12 2006-06-29 http://www.sciencetronics.com/download/fpc_libusb.tgz
libusb.pas Johann Glaser 2005-01-14 http://www.johann-glaser.at/projects/libusb.pas
fpcusb Joe Jared 0.11-14 2006-02-02 http://relays.osirusoft.com/fpcusb.tgz download link broken

FTDI

If you use one of the chips from FTDI, you can use their pascal headers for their dll interface to the chips.

External Links

Communication Protocols speed comparison:

  1. http://en.wikipedia.org/wiki/Serial_port#Speed
  2. http://www.lvr.com/jansfaq.htm - Jan Axelson's Parallel Port FAQ
  3. http://en.wikipedia.org/wiki/USB#Transfer_Speed
  4. http://en.wikipedia.org/wiki/PCI#Conventional_PCI_bus_specifications

Serial Communication Links:

  1. On UNIX: [1]
  2. On Windows: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnfiles/html/msdn_serial.asp
  3. Synaser component: http://synapse.ararat.cz/
  4. Comport Delphi package: http://sourceforge.net/projects/comport/

ISA Digital Oscilloscope - A example of hardware access with full source included:

[2]


Networking