Hardware Access/sk

From Lazarus wiki
Revision as of 10:05, 7 November 2005 by Almindor (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
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)


This page is the start of a tutorial about accessing hardware devices on Lazarus. These devices include, but are not limited to: ISA, PCI, USB, parallel port, serial port.

Access to hardware devices on a completely multi-platform way is not implemented by Free Pascal Runtime Library or by the LCL, so this tutorial will basically cover hardware access methods on different platforms. The code can be compiled on different environments using conditional compiles, like this:

 Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ExtCtrls,
{$IFDEF Linux}

Help is appreciated, specially about MacOS.

Using inpout32.dll for Windows

Windows has different ways to access hardware devices on the 9x series and on the NT series, and this can be really problematic. The 9x series (95, 98, Me) allow the programs to access ports directly, just like they did on DOS. Windows NT, however, completely prevents this, and only allows access to the ports throught device drivers. This is a secutiry mechanism, but can be anoying as a device driver may be too complex for a small project that need to access a parallel port.

Happily there is a library that carryes a device driver inside it. If Windows NT is detected, it decompresses the device and install the Hwinterface.sys kernel device driver. If Windows 9x is detected it simply uses assembler opcodes to access the hardware.

But how to use the library? Simple! It only has two functions, Inp32 and Out32, and their use is quite intuitive.

We will be loading the library dynamically, so let's define both functions first:

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

  • Address represents the address of the port you desire to access. Out32 sends Data to the port you specify by Address. Inp32 returns a byte from the port you specify by Address.

Now we can load the library. This can be implemented in a place like the OnCreate method of your program's main form:

  TMyForm = class(TForm)
    { private declarations }
    Inpout32: THandle;
    Inp32: TInp32;
    Out32: TOut32;
procedure TMyForm.FormCreate(Sender: TObject);
  Inpout32 := LoadLibrary('inpout32.dll');
  if (Inpout32 <> 0) then
    @Inp32 := GetProcAddress(Inpout32, 'Inp32');
    if (@Inp32 = nil) then Caption := 'Error';
    @Out32 := GetProcAddress(Inpout32, 'Out32');
    if (@Out32 = nil) then Caption := 'Error';
  else Caption := 'Error';

If you load the library on OnCreate just don't forget to unload it in OnDestroy:

procedure TMyForm.FormDestroy(Sender: TObject);

Here is a simple example of how to use Inp32 function:

  myLabel.Caption := IntToStr(Inp32($0220));

This code was tested with a custom ISA card on port $0220, using Lazarus 0.9.10 on Windows XP. Of course you will need to have Windows on your uses clause in order for this code to run. For deployment you only need to include "inpout32.dll" in the same directory of our application.

This is the homepage for the library: www.logix4u.net/inpout32.htm

Using ioperm to access ports on Linux

The best way to access the hardware on Linux is throught device drivers, but, due to the complexity of the task of creating a driver, sometimes a quick method is very useful.

In order to use the "ports" unit under linux your program must be run as root, and IOPerm must be called to set appropriate permissions on the port access. You can find documentation about the "ports" unit here.

The first thing to do is link to glibc and call IOPerm. A unit that links to the entire glibc exists on free pascal, but this unit gives problems when used directly by application and linking statically to the entire glibc library is not a very good idea for a multi-platform application. The use of platform-specific functions should be kept to a minimum.

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

  • "from" represents the first port to be accessed.
  • "num" is the number of ports after the first to be accessed, so ioperm($220, 8, 1) will give access for the program for all ports between and including $220 and $227.

After linking to OIPerm you can port[<Address>] to access the ports.

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

This code was tested with a custom ISA card on port $0220, using Lazarus 0.9.10 on Mandriva Linux 2005 and Damn Small Linux 1.5