Difference between revisions of "Hardware Access/zh CN"

From Free Pascal wiki
Jump to navigationJump to search
(对英文文档的翻译)
m
Line 1: Line 1:
{{硬件访问}}
+
{{Hardware Access}}
 +
 
 
__TOC__
 
__TOC__
 
==概览==
 
==概览==

Revision as of 11:31, 2 January 2010

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)

概览

本页是关于在Lazarus上访问硬件设备的教程的开始。这些设备包括,但不限于:ISA,PCI,USB,并行端口,串行端口。

RTL或LCL没有实现统一的多平台硬件设备的访问。因此本教程将基本覆盖不同平台上的硬件访问方法。在不同环境下可以使用条件编译来编译代码,像这样:

<delphi>

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

</delphi>

此时还不知道Mac OS X/x86是否会允许HW访问。它可能不允许,虽然那种情形下我假设像io.dll的驱动器将会及时出现。

并行和串行的比较

ISA卡,PCI卡和并行端口使用“并行”协议与计算机进行通信。串行端口和USB设备使用“串行”协议。因为处理器和编程语言都通过并行方式处理数据,在软件端比较容易实现对这类协议的访问。例如,访问一个整型变量时,可以仅用一个命令就能访问它的值。然而,使用串行协议,一次仅能知道一位(bit),需要将所有片断(piece)放在一起才能理解数据。

串行通信比较难于直接实现,如果使用预制(pre-made)组件,可能会稍微容易些。在硬件端也比较困难,因此许多设备使用专门的集成电路或甚至微控制器来实现。

现在来做一个硬件访问协议的简要比较:

速度 硬件实现难度
串行端口 非常慢 (< E5 bit/s) 中等
并行端口 慢 (~ E6 bit/s) 容易
ISA卡 中等 (~ E7 bit/s) 中等
USB 中等 (~ E7 bit/s) 困难
PCI卡 非常快 (> E9 bit/s) 非常难

并行通信

为Windows使用inpout32.dll

在9x系列和NT系列,Windows有不同的方法来访问硬件设备。在9x系列(95,98,Me),程序可以直接访问硬件,正如在DOS上一样。然而,NT系列(Windows NT和XP)不允许这种方式。在该架构上,所有与硬件端口的通信必须通过一个设备驱动器。这是一种安全机制,但是为一个小项目开发一个驱动器可能会花费太多的时间和金钱。

幸运地是有一个库解决了这个问题。如果检测到Windows NT,它解压HWInterface.sys内核设备驱动器并安装。如果检测到Windows 9x,它简单地使用汇编操作码访问硬件。

但是如何使用这个库呢?非常简单!它仅有2个函数:Inp32和Out32,它们的使用是比较直观的。

我们将动态加载该库,因此让我们首先定义这2个函数:

<delphi>

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

</delphi>

  • Address代表期望访问的端口地址
  • Out32发送数据到指定地址的端口
  • Inp32从指定地址的端口返回一字节

现在可以加载该库了。这可能在一个类似程序主form的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上使用Lazarus 0.9.10,在端口$0220的一个自定义ISA卡上测试过。当然为了让该代码运行,你需要有一个有使用条款的Windows。为了部署,你仅需要把“inpout32.dll”包含在应用的相同目录下。

这是该库的主页: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里找到关于如何告诉Windows不要将你设备的地址分配给即插即用设备的指示:

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

在Linux上使用ioperm访问端口

在Linux上访问硬件的最好方法通过设备驱动器,但是考虑到创建一个驱动器任务的复杂性,有时候一个快速的方法是非常有用的。

为了使用Linux下的“ports”单元,程序必须以root身份运行,并且必须调用IOPerm设置端口访问的合适权限。你可以在这里找到关于“ports”单元的文档。

要做的第一件事是链接(g)libc和调用IOPerm。一个链接整个(g)libc的单元存在于free pascal,但是当应用直接使用时该单元出现了问题,并且静态链接整个(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>

该代码在Mandriva Linux 2005和Damn Small Linux 1.5上使用Lazarus 0.9.10,在端口$0220的一个自定义ISA卡上测试过。

通用UNIX硬件访问

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


注意 FPC在unit x86里为ioperm提供了一个叫做“fpioperm”的抽象,也定义了fpIOPL和输出/输入函数。这些函数当前是为Linux/x86和FreeBSD/x86实现的。

不建议链接到的libc,除非绝对必要的,因为可能的部署和移植功能。 像上面那样(通过为别处有效的函数声明特设导入)手工链接libc也是不建议的(例如,如果标准C库不叫作libc,那么上面的libc导入行将不必要(unnecessarily)地失败,比如BeOS或非标准C符号扩展(symbol mangling)平台上的libroot)。

注意 2 不建议在除了Kylix兼容性的任何环境下使用unit libc。参见libc unit

串行通信

使用Synaser库开发一个串行通信软件是非常容易的。当与Synaser文档一起使用时,例子应该很好理解。最主要的部分是TBlockSerial.Config设置速度(位/秒),数据位,奇偶校验位,停止位和握手协议,如果有的话。下面代码在一个连接到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>

下面的代码例子是上面例子的一个替代版本。上面的例子看起来在主要概念上有严重错误,准确地是“while true do...”部分。在测试系统(Asus A6T Laptop with Digitus USB to RS232 Adapter, Ubuntu 8.04.1)上,该部分导致了下面错误:每个会话只能成功运行一次应用,当再次起动应用时,应用不能连接到串行端口。因此,每次用户试图重启应用时,需要重启(计算机),这是一个非常恼人的bug。

原因不难理解:应用处于while true do循环,更准确地说是无限循环。没有退出条件,因此关闭应用的唯一办法是关闭终端或按CTRL-C。但是如果通过种办法退出应用,释放串行端口的重要部分“ser.free”从来不被调用。德语Lazarus论坛的下面贴子里描述了这个问题http://www.lazarusforum.de/viewtopic.php?f=10&t=2082

为了让每个用户明白,而不是按CTRL-C,在主要应用周围有一些代码。也许有人担心,为什么/dev/ttyUSB0被用作com端口:这是由于在测试系统上使用的USB串行适配器。如果有内置串行端口,请使用“Com0”——像上面例子代码中声明。

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

另外,外部链接节有UNIX和Windows串行端口教程。


值得一提的是linux下TBlockSerial.LinuxLock参数的功能。当设置默认为True时,连接将试图在/var/lock下创建一个锁文件(例如,“LCK..ttyUSB0”),如果所请求的端口存在一个锁就会失败。如果没有调用释放,锁文件将一直存在。设置LinuxLock为False将使Synaser忽略端口锁定。

替代Synaser:

基于Synaser的可视组件5dpo

另外一个非常简单的fpc单元现在是freepascal(至少版本2.2.2)的部分了,只要将Serial放到你的Uses清单里,然而除了Serial.pp源文件外还没有其它文档。

USB

libusb

一个跨Linux,BSDs和Mac OS X平台的可能性是libusb

标题在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

如果你使用FTDI的芯片,你可以使用他们芯片dll接口的pascal头。

外部链接

通信协议速度比较:

  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

串行通信链接:

  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数字示波器——一个硬件访问例子的全部源代码包含在:

[2]


Networking