Difference between revisions of "Hardware Access"

From Free Pascal wiki
Jump to navigationJump to search
(→‎Overview: s/linux/unix, Mac OS X line)
(The example is intentionally Linux specific, I added another section for UNIX in general. Write to that.)
Line 17: Line 17:
 
</code>
 
</code>
  
It is not known yet at this time if Mac OS X/x86 will allow HW access. It can disallow it, though I assume in that case, in time, drivers like io.dll will appear.
+
It is not known yet, at this time, if Mac OS X/x86 will allow HW access. It can disallow it, though I assume in that case, in time, drivers like io.dll will appear.
  
 
==Parallel and Serial Comparison==
 
==Parallel and Serial Comparison==
Line 135: Line 135:
 
This is the homepage for the library: [http://www.logix4u.net/inpout32.htm www.logix4u.net/inpout32.htm]
 
This is the homepage for the library: [http://www.logix4u.net/inpout32.htm www.logix4u.net/inpout32.htm]
  
===Using ioperm to access ports on Unix===
+
===Using ioperm to access ports on Linux===
  
The best way to access the hardware on Unix is throught device drivers, but, due to the complexity of the task of creating a driver, sometimes a quick method is very useful.
+
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 "[[doc:rtl/ports|ports]]" unit under Unix 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 "[[doc:rtl/ports|ports]]" unit [http://www.freepascal.org/docs-html/rtl/ports/index.html here].
+
In order to use the "[[doc:rtl/ports|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 "[[doc:rtl/ports|ports]]" unit [http://www.freepascal.org/docs-html/rtl/ports/index.html here].
  
The first thing to (not, see notes) do is link to (g)libc and call IOPerm. A unit that links to the entire (g)libc exists on free pascal, but this unit gives problems when used directly by application and linking statically to the entire (g)libc library is not a very good idea for a multi-platform application. The use of platform-specific functions should be kept to a minimum.
+
The first thing to do is link to (g)libc and call IOPerm. A unit that links to the entire (g)libc exists on free pascal, but this unit gives problems when used directly by application and linking statically to the entire (g)libc library is not a very good idea for a multi-platform application. The use of platform-specific functions should be kept to a minimum.
  
 
<code>
 
<code>
{$IFDEF Unix}
+
  {$IFDEF Linux}
Uses Clib;  // retrieve libc library name.
+
  function ioperm(from: Cardinal; num: Cardinal; turn_on: Integer): Integer; cdecl; external 'libc';
{$ENDIF}
 
 
 
  {$IFDEF Unix}
 
  function ioperm(from: Cardinal; num: Cardinal; turn_on: Integer): Integer; cdecl; external clib;
 
 
  {$ENDIF}
 
  {$ENDIF}
 
</code>
 
</code>
Line 159: Line 155:
  
 
<code>
 
<code>
  {$IFDEF Unix}
+
  {$IFDEF Linux}
 
   i := ioperm($220, 8, 1);
 
   i := ioperm($220, 8, 1);
 
   port[$220] := $00;
 
   port[$220] := $00;
Line 169: Line 165:
  
 
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
 
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
 +
 +
===Hardware Access on UNIXes===
 +
 +
<pre>
 +
{$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}
 +
</pre>
 +
  
 
'''Note''' that FPC provides an abstraction for ioperm called "fpioperm" in [[http://www.freepascal.org/docs-html/rtl/x86/index.html unit x86]], and also defines out and inport functions. These functions are currently implemented for Linux/x86 and FreeBSD/x86.
 
'''Note''' that FPC provides an abstraction for ioperm called "fpioperm" in [[http://www.freepascal.org/docs-html/rtl/x86/index.html unit x86]], and also defines out and inport functions. These functions are currently implemented for Linux/x86 and FreeBSD/x86.

Revision as of 14:25, 19 January 2006

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)

Overview

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:

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

It is not known yet, at this time, if Mac OS X/x86 will allow HW access. It can disallow it, though I assume in that case, in time, drivers like io.dll will appear.

Parallel and Serial Comparison

ISA Cards, PCI Cards and the Parallel Port communicate with the computer using a parallel protocol. The Serial Port and USB devices work with a serial protocol. Because the processor and thus programming languages all work on a parallel approach to data, access to this kinds of protocols are easier to be implemented on the software side. When you acess an Integer variable, for example, you can access it's value with a single command. With a serial protocol, however, you can only know one bit at a time, and you need to glue the pieces together latter to understand the data.

Serial communication is difficult to be implemented directly, but it can be pretty easy if you use a pre-made component. It is also harder on the hardware side, so many devices use specialised Integrated Circuits or even Microcontrolers to implement it.

Now a brief comparison of hardware access protocols will be given:

Speed Hardware implementation difficulty
Serial Port Very slow (< E5 bit/s) Medium
Parallel Port Slow (~ E6 bit/s) Easy
ISA Card Medium (~ E7 bit/s) Medium
USB Medium (~ E7 bit/s) Hard
PCI Card Very Fast (> E9 bit/s) Very Hard

Parallel Communication

Using inpout32.dll for Windows

Windows has different ways to access hardware devices on the 9x series and on the NT series. On the 9x series (95, 98, Me) programs can access the hardware directly, just like they did on DOS. The NT series (Windows NT and XP), however, don't allow this approach. On this architecture, all communication with hardware ports must be throught a device driver. This is a security mechanism, but developing a driver can cost too much in terms of time and money for small projects.

Happily there is a library that solves this problem. If Windows NT is detected, it decompresses HWInterface.sys kernel device driver and installs it. If Windows 9x is detected, it simply uses assembler opcodes to access the hardware.

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

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

type
  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:

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
    @Inp32 := GetProcAddress(Inpout32, 'Inp32');
    if (@Inp32 = nil) then Caption := 'Error';
    @Out32 := GetProcAddress(Inpout32, 'Out32');
    if (@Out32 = nil) then Caption := 'Error';
  end
  else Caption := 'Error';
{$ENDIF}
end;

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

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

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

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

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 (g)libc and call IOPerm. A unit that links to the entire (g)libc exists on free pascal, but this unit gives problems when used directly by application and linking statically to the entire (g)libc 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';
{$ENDIF}

  • "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 IOPerm 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);
{$ENDIF}

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

Hardware Access on UNIXes

{$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}


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

The External Links section has UNIX and Windows serial port tutorials.

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/