Hardware Access/pt

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)

Visão Geral

Está página é o começo de um tutorial sobre acesso a dispositivos de hardware por programas desenvolvidos no Lazarus. Estes dispositivos incluem, porem não estão limitados a: Placas ISA e PCI, as portas paralela e serial e USB.

O acesso ao hardware de uma maneira completamente multi-plataforma não é implementada pela biblioteca RunTime do Free Pascal ou pela LCL, assim este tutorial cobre metodos diferentes para diferentes plataformas. O código pode ser compilado em ambientes diferentes utilizando a compilação condicional. Abaixo segue um exemplo disto:

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

Quem desejar pode contribuir para este documento.

Comparação entre Paralelo e Serial

Placas ISA, Placas PCI e a Porta Paralela comunicam com o computador utilizando um protocolo de comunição parallela. A Porta Serail e dispositivos USB trabalham com um protocolo serial. Como o processando e, por extensão, as linguagens de programação trabalham com uma abordagem paralela aos dados, esse tipode acesso é mais fácil de ser implementado no software. Ao acessar uma variável do tipo inteiro, por exemplo, você pode acessar seu conteúdo inteiro com um único comando. Esta é a abordagem paralela. No protocolo serial apenas bit é enviado de cada vez e é preciso junta-los para depois poder compreender os dados.

Comunicação serial é difícil de ser implementada diretamente, mas pode ser muito fácil se utilizarmos um componente pronto. Ela também é mais difícil no lado do hardware, logo muitos dispositivos utilizam Circuitos Integrados especialmente desenvolvidos ou até microcontroladores para implementar o protocolo serial.

Agora uma breve comparação dos protocolos de comunicação:

Velocidade Dificuldade de implementação do hardware
Porta Serial Muito lenta (< E5 bit/s) Média
Porta Paralela Lenta (~ E6 bit/s) Fácil
Placa ISA Média (~ E7 bit/s) Média
USB Média (~ E7 bit/s) Difícil
Placa PCI Muito Rápida (> E9 bit/s) Muito difícil

Comunicação Paralela

Utilizando a inpout32.dll para Windows

No Windows há diferentes metodos para acessar dispositivos de hardware na série 9x e na série NT, o que pode gerar muitos problemas. A série 9x (95, 98, Me) permite que os programas acessem as portas diretamente, da mesma maneira que éra feita no DOS. A série NT, porém, não permite isto e todos acesso as portas deve ser feito através de Device Drivers. Isto é mecanismo de segurança, mas pode complicar o desenvolvimento de aplicativos pequenos que apenas precisam acessar uma determinada porta.

Felizmente existe uma biblioteca que carrega dentro de si um driver. Quando ela é chamada para acessar uma determinada porta ela detecta se o Windows em operação é da série NT e, em caso afirmativo, descomprime o driver Hwinterface.sys e o instala no kernel. Se a série 9x for detectada ele simplesmente utiliza instruções de assembler para acessar o hardware.

Mas como utilizar a biblioteca? Simples! Ela possuí apenas duas funções, Inp32 e Out32, e seu uso é bastante intuitivo.

Nós estaremos carregando a biblioteca dinamicamente, então é bom definir as funções antes:

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

  • Address representa o endereço da porta a ser acessada. Out32 envida Dados para a porta. Inp32 returna um byte da porta no Address.

Agora podemos carregar a biblioteca. Isto pode ser implementado num lugar como o evento OnCreate do seu formulário principal:

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;

Se você carregar a biblioteca no evento OnCreate não esqueça de descarrega-la no evento OnDestroy:

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

Aqui está um exemplo de uso da função Inp32:

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

Este código foi testado com uma placa ISA própria na porta$0220, utilizando o Lazarus 0.9.10 no Windows XP. Obviamente é necessário possuir a unidade Windows na clausula "uses" para que este código possa funcionar. Para distribuir um aplicativo que utilize a biblioteca você precisa apenas incluir a "inpout32.dll" no mesmo diretório do executável.

Está é a página da biblioteca: www.logix4u.net/inpout32.htm

Utilizando ioperm para acessar portas no Linux

A melhor maneira de acessar dispositivos de hardware no Linux é através de drivers, mas, devido a complexidade envolvida na criação de um, as vezes um método mais simples é útil.

Para utilizar a unidade "ports" no Linux, seu programa deve ser executado como superusuário (i.e. root) e utilizar a função IOPerm para definir as permissões de acesso as portas. Você pode encontrar documentação sobre a unidade "ports" aqui.

A primeira coisa a ser feita é estabelecer um vínculo estático com o glibc e chamar a função IOPerm. Uma unidade que estabelece vínculos para todo o glibc existe no Free Pascal, mas ela causa problemas quando utilizada diretamente pelo aplicativo, além do que não é adequado para um programa multi-plataforma chamar funções demais do glibc, assim iremos criar o mínimo indispensável de vínculos dependendes de plataforma declarando apenas a função do glibc a ser utilizada.

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

  • "from" representa a primeira porta a ser acessada.
  • "num" é o número de portas após a primeira a serem acessadas. Assim ioperm($220, 8, 1) fornecerá ao programa acesso a todos portas entre e incluindo $220 e $227.

Após chamar a função OIPerm você pode utilizar "port[<Endereço>]" para acessar as portas.

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

Este código for testado com uma placa ISA própria na porta $0220, utilizando o Lazarus 0.9.10 no Mandriva Linux 2005 e no Damn Small Linux 1.5.

Comunicação Serial

Links Externos

Comparação de velocidade de Protocolos de Comunicação

  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

Outros links:

  1. http://paginas.terra.com.br/informatica/klhugo - Kleber Hugo's website