BT2kLOGO
Boeken van eigen hand
Leesproef
- -

B. Kainka / H.-J. Berndt
PC-POORTEN ONDER WINDOWS
meten, regelen en besturen via de standaardpoorten
Uitgeverij Segment B.V. Postbus 75, 6190 AB Beel (L) , 1999
ISBN 90 5381 114 1 NUGI 852/853
Elektuur.nl 

lees de inhoudsopgave


1 Inleiding

Wanneer we met een computer reële processen willen meten of regelen, hebben we hoe dan ook een geschikte verbinding tussen de computer en de buitenwereld nodig. De software moet in staat zijn informatie van buiten in te lezen en stuursignalen naar externe apparatuur te zenden. De door de computer te verwerken informatie kan in binaire vorm, dus in ja/nee-vorm, of als analoge (continu variabele) grootheid ter beschikking staan. Het contact met de buitenwereld vindt plaats via interfaces. Bij PC's die zich aan de de facto industriestandaard houden treffen we meestal onderstaande typen interfaces aan:

• Uitbreidingskaarten worden in vrije slots van de computer geprikt en maken een hoge verwerkingssnelheid mogelijk, maar zijn vaak erg duur.

• Standaard-interfaces vormen de verbinding tussen de PC en externe interface-schakelingen. Heel gebruikelijk zijn bijvoorbeeld interfaces die op de seriële RS-232-poort worden aangesloten en die tegen betrekkelijk geringe kosten gebouwd kunnen worden.

• Intelligente interfaces bezitten een eigen processor en werken grotendeels zelfstandig, zonder continue data-uitwisseling met de PC. De gebruiker moet zich wel in de programmering van deze systemen verdiepen.

• De standaard aanwezige PC-poorten: de seriële poort, de printerpoort en vaak ook de joystick-poort (gameport) kunnen zonder meer als interface worden gebruikt. In veel gevallen is hiervoor geen extra hardware vereist.

De standaard aanwezige poorten zijn welhaast ideaal voor eenvoudige experimenten. Op de seriële poort kunnen we bijvoorbeeld zonder meer schakelaars of LED's aansluiten, wat diverse interessante mogelijkheden biedt. Bij de printerpoort hebben we een flink aantal rechtstreeks adresseerbare in- en uitgangen ter beschikking, waarmee een hoge overdrachtssnelheid gerealiseerd kan worden.

Met behulp van simpele uitbreidingsschakelingen kunnen we de beperkingen van deze poorten gemakkelijk omzeilen. Zo kunnen we met een handjevol onderdelen spanningen meten, wanneer we er althans geen bezwaar tegen hebben dat de programmatuur iets ingewikkelder wordt en de snelheid wat lager. Ook projecten met veel in- en uitvoerlijnen kunnen met betrekkelijk eenvoudige hardware worden gerealiseerd.

In dit boek zullen we voornamelijk projecten bespreken waarvoor weinig extra hardware vereist is, met als logisch gevolg dat de software wat rekenintensiever uitvalt. Het is dan ook wenselijk dat de lezer enige ervaring met hogere programmeertalen heeft en de grondbeginselen van de elektronica beheerst. De schakelingen in dit boek zijn over het algemeen zo eenvoudig dat ze gemakkelijk op een stukje gaatjesprint kunnen worden opgebouwd. Alle projecten in dit boek vormen een afgerond geheel en kunnen zo nagebouwd worden. Daarnaast vormen ze de basis voor verdere ontwikkelingen, waarvoor echter, gezien de grotere complexiteit, geschikte meetapparatuur plus ervaring in het gebruik daarvan vereist zijn. De geïnteresseerde lezer moet dan bijvoorbeeld over een oscilloscoop beschikken.

Voor dit boek hebben we, onder Windows95/98, gebruik gemaakt van de programmeertalen Visual Basic 5 en Delphi 3/4. Alle programmeervoorbeelden staan op de CD bij dit boek.



1.1 PORT.DLL

Onder Windows vormt de toegang tot de poorten van de PC het grote probleem. Daarom maken we hier gebruik van een universeel bruikbare DLL (Dynamic Link Library, een bibliotheek met Windows-functies), die zelf in Delphi 3 is geschreven. Een DLL vervult de functie van een "taaluitbreiding" voor verschillende programmeertalen. De speciaal voor dit boek ontwikkelde PORT.DLL staat op de bijgaande CD. De DLL moet naar de Windows-systeemdirectory worden gekopieerd zodat elk programma er gebruik van kan maken. Het is echter ook mogelijk de bibliotheek in de directory te plaatsen waar ook het betreffende EXE-programma staat.

PORT.DLL vervult onderstaande taken:

• openen van interfaces
• seriële data-overdracht
• toegang tot poortlijnen
• algemeen: in- en uitvoer van data via poorten
• nauwkeurige milliseconde-pulsgever
• pulsgever in microseconde-raster
• toegang tot de geluidskaart
• invoer via joystick

De DLL kan onder verschillende programmeersystemen worden gebruikt. Naast de voor dit boek gebruikte programmeertalen kunnen de DLL-functies bijvoorbeeld ook vanuit C-programma's worden aangeroepen. Een eenmaal ontwikkeld programma kan daarom gemakkelijk voor een ander programmeersysteem worden aangepast. Bovendien kan de DLL in Word- en Excel-macro's worden aangeroepen (zie paragraaf 1.2 en hoofdstuk 14; meer tips treft u onder andere in [5] aan).

De ontwikkeling van een algemene DLL voor de toegang tot willekeurige hardware staat in zekere zin haaks op de Windows-filosofie om alle toegang tot de hardware via drivers te laten verlopen. Een driver hoort altijd bij een specifiek apparaat. Voor kleine hardware-experimenten is dan eigenlijk geen plaats meer. De "correcte" ontwikkeling van een driver is echter een ingewikkeld proces dat eigenlijk uitsluitend aan grote bedrijven is voorbehouden.

Onder DOS waren er in elke programmeertaal poort-instructies voorhanden (GW-Basic: INP en OUT, Turbo-Pascal: PORT[..]), via welke we rechtstreeks toegang hadden tot alle hardware-componenten van het computersysteem. Onder Windows 3.1 werd dat al minder maar bleven de beperkingen nog enigszins binnen de perken, zodat ze betrekkelijk eenvoudig omzeild konden worden. Delphi 1 kende zelfs nog de "antieke" PORT-instructie.

Onder Windows 95 is het leven een stuk moeilijker geworden. In Visual Basic 5 bestaat er geen rechtstreekse mogelijkheid meer om toegang tot poorten in het algemeen te krijgen. Vanaf Delphi 3 is directe toegang tot poortadressen nog slechts mogelijk met behulp van een machinetaalroutine. Hiervan wordt in de DLL gebruik gemaakt om vanuit Visual Basic ook weer toegang tot de poortadressen te krijgen. Het bedrijfssysteem laat zich echter niet meer zomaar "om de tuin leiden": we moeten een interface bijvoorbeeld eerst ordentelijk aanmelden voordat we met de bijbehorende hardware kunnen gaan stoeien.

Onder Windows NT lijkt het voor de hobby-programmeur helemaal afgelopen te zijn. Dit systeem is zo sterk met het oog op veiligheid en stabiliteit ontwikkeld, dat niemand meer rechtstreeks toegang tot de hardware krijgt. De enige manier is de officiële: via drivers. "Algemene" hardware dient daarbij via de seriële interface te worden aangesproken. Maar dan is de toegankelijkheid echter niet meer aan beperkingen onderworpen, omdat geen poortadressen worden aangesproken maar ordentelijke systeemroutines worden gebruikt.

Voor de goede orde wijzen we er hier op dat dit boek over experimenten met PC-poorten gaat. De hier behandelde voorbeeldprogramma's zijn onder Windows95 en Windows98 ontwikkeld met 32-bit systemen (Visual Basic 5 en Delphi 4). Daarbij ligt altijd het gevaar op de loer dat een programma op bepaalde computers niet wil draaien. In een bepaald systeem kan een interface bijvoorbeeld anders zijn geïnstalleerd dan voorzien. Ook is het mogelijk dat iemand een nieuwere Windows-versie gebruikt die de toegang tot de interfaces weer nèt even anders regelt. U kunt daarom beter niet proberen om op deze basis professionele applicaties te ontwikkelen: het kan anders maar al te gemakkelijk gebeuren dat u met het goede schip de Noorderzon moet vertrekken om de boze telefoontjes van gefrustreerde gebruikers te ontlopen.



1.2 Het aanroepen van de DLL in Visual Basic 5

Aan de hand van een eenvoudig voorbeeld zonder extra hardware zullen we hier eerst laten zien hoe een DLL in principe gebruikt wordt. De luidspreker van een PC wordt aangestuurd via componenten die op hun beurt weer via poort-instructies kunnen worden gestuurd. De luidspreker kan ofwel via een timer worden aangestuurd om een toontje met een bepaalde frequentie te produceren, of rechtstreeks via een uitgang van de PIO 8255 in de PC. Het is namelijk ook mogelijk een toon te produceren door een lijn in rap tempo in en uit te schakelen. Dit laatste gaan we hier doen, enerzijds om te zien hoe het invoegen van een DLL in principe in zijn werk gaat, en anderzijds om kennis te maken met het real-time gedrag van Windows.

De luidspreker wordt aangestuurd via bit 1 van poort B van de 8255. Dit IC neemt de adressen vanaf 60hex (96dec) in het I/O-bereik van de PC in beslag. Poort B ligt op adres 97. De uitvoer via een poort (schrijven naar een poort) verloopt altijd met 8 bits tegelijk, zodat 8 lijnen tegelijk worden geschakeld. In dit geval mag echter uitsluitend de tweede poortlijn (dat is bit 1) van toestand veranderen, omdat nog veel andere zaken in de PC via poort B van het PIO-IC worden geregeld. Dat betekent dat de toestand van de poort eerst moet worden gelezen, zodat alleen het gewenste bit veranderd kan worden. Als dit gegoochel met bits u momenteel nog boven de pet gaat, hoeft u zich geen zorgen te maken. In de eerste plaats staat het programmavoorbeeld kant-en-klaar op de CD, en in de tweede plaats gaat het er bij de voor de buitenwereld toegankelijke interfaces in veel opzichten gemakkelijker aan toe.

Voor de toegang tot de afzonderlijke poortadressen van de PC heeft de DLL twee speciale functies in de aanbieding:

• OutPort adr,dat   schrijven van data naar een adres
• InPort  adr          lezen van een I/O-adres

In Visual Basic kan OutPort als sub (procedure) worden ingevoegd; InPort moet daarentegen een functie zijn. De afzonderlijke elementen van een DLL worden met het DECLARE-statement gedeclareerd. Om de data-overdracht tussen Visual Basic en de DLL in goede banen te leiden, moeten alle door te geven parameters met ByVal gedeclareerd worden, dat wil zeggen als waarde -- in tegenstelling tot de doorgave van een referentie-adres. Onder het 32-bits besturingssysteem Windows95/98 moet u bij de declaratie op het gebruik van hoofdletters letten. In de DLL zijn alle functies uitsluitend met hoofdletters aangegeven, en die conventie moet ook worden aangehouden in het programma dat de functies aanroept. De declaraties moeten zich in een eigen module (hier Module1.bas) bevinden, die in het project TON ("toon") wordt ingevoegd.


Figuur 1.1 Het project "Ton" (toon).

Declare Sub OUTPORT Lib "PORT.DLL" (ByVal Adr As Integer, ByVal Dat As Integer)
Declare Function INPORT Lib "PORT.DLL" (ByVal Adr As Integer) As Integer
Declare Sub DELAY Lib "PORT.DLL" (ByVal Zeit As Integer)

Listing 1.1 De declaraties in Module1.bas.

InPort en OutPort zijn nu overal in het project bruikbaar. Daarnaast hebben we meteen ook de Delay-procedure ("vertraging") gedeclareerd, die verderop gebruikt zal worden. Bij het eerste experiment stuurt een snelle programmalus 100 pulsen naar de luidspreker die als toontje hoorbaar zouden moeten zijn. Voor de manipulatie van de afzonderlijke bits door OutPort worden de logische functies AND en OR gebruikt, zodat telkens slechts één lijn van de met InPort ingelezen poort wordt gewijzigd. Ook deze functies worden verderop in dit boek nog in meer detail behandeld.

Het programma gebruikt een eenvoudige form met een knop "Ton" ("toon"). De luidspreker wordt 100 maal in- en uitgeschakeld. Om bij (bijvoorbeeld) een 200-MHz-PC een hoorbaar toontje te produceren, moeten extra pauzes worden ingelast. Deze worden gegenereerd door middel van tellerlussen die tot 10000 tellen.


Figuur 1.2 Het programmavenster.

Private Sub Command1_Click()
  For n = 1 To 100
     OUTPORT 97, (INPORT(97) Or 2)
     For t = 1 To 10000: Next t
     OUTPORT 97, (INPORT(97) And 253)
     For t = 1 To 10000: Next t
  Next n
End Sub

Listing 1.2 Geluidsproductie via de PC-luidspreker (TON.FRM).

Na aanklikken van de knop "Ton" valt daadwerkelijk geluid te horen. De exacte toonhoogte hangt van de computer in kwestie af. Bovendien is de toon niet bepaald zuiver te noemen, maar klinkt die veeleer verruist en krasserig, om niet te zeggen astmatisch. De oorzaak daarvan moet worden gezocht in het real-time gedrag van Windows.

De door middel van wachtlussen gegenereerde pauzes kunnen zeer ongelijk uitvallen, omdat Windows tussen neus en lippen door nog een groot aantal andere taken moet vervullen. Zo moet bijvoorbeeld de muis in de gaten worden gehouden en moeten eventuele parallel verlopende processen in goede banen worden geleid. Men zegt daarom wel dat Windows geen echt real-time besturingssysteem is, met andere woorden dat snelle processen onder Windows niet betrouwbaar gestuurd kunnen worden. Die uitspraak moet echter enigszins worden gerelativeerd, want lang niet alle processen vereisen een supersnelle en super-betrouwbare real-time besturing. Maar zuivere toontjes hoeft u echt niet te verwachten als u te werk gaat als in het vorige programmavoorbeeld.

Tellerlussen verdienen eigenlijk helemaal niet de voorkeur wanneer er een vertraging moet worden ingebouwd. Windows zelf beschikt over betere middelen om beter gedefinieerde vertragingen in de orde van grootte van milliseconden te verkrijgen. Deze middelen zijn toegankelijk via de Delay-functie van de DLL. De kwaliteit van het geproduceerde toontje kan door middel van Delay aanmerkelijk worden verbeterd. Jammer genoeg bedraagt de hoogst mogelijke frequentie slechts 500 Hz wanneer het niveau van het poortbit éénmaal per milliseconde verandert.

Private Sub Command1_Click()
  For n = 1 To 100
     OUTPORT 97, (INPORT(97) Or 2)
     DELAY 1
     OUTPORT 97, (INPORT(97) And 253)
     DELAY 1
  Next n
End Sub

Listing 1.3 Geluidsproductie met Delay (TON2.FRM).

De zo geproduceerde toon klinkt al een stuk beter, zij het nog altijd niet zo goed als met puur elektronische middelen mogelijk zou zijn. Het resultaat kan met de DLL-procedure RealTime(true) nog aanzienlijk worden verbeterd (zie paragraaf 3.2). U krijgt hier echter al een redelijke indruk hoe het met de real-time mogelijkheden van Windows gesteld is. Met een oscilloscoop zou u dit nauwkeuriger kunnen onderzoeken. Daarvoor zijn echter programma's die een lijn van bijvoorbeeld een seriële COM-poort in- en uitschakelen, beter geschikt.

Naast een Delay-functie voor milliseconden zit er in de DLL ook nog een vertragingsfunctie in een microseconden-raster. Ook deze maakt intern gebruik van Windows-functies.

Alle aanroepen van DLL-functies moeten in een externe Basic-module PORTS.BAS worden gedeclareerd. Deze kan vervolgens probleemloos in elk nieuw project worden ingevoegd. De programmeur heeft dan toegang tot alle DLL-functies zonder zich te hoeven bekommeren over de precieze manier waarop die gedeclareerd moeten worden. Het PORT.DLL-bestand moet dan samen met het gecompliceerde programma worden verspreid en ofwel naar de Windows-systeemdirectory ofwel naar dezelfde directory als het EXE-programma worden gekopieerd.

Declare Function OPENCOM Lib "Port" (ByVal A$) As Integer
Declare Sub RTS Lib "Port" (ByVal b%)
....
Declare Sub SOUNDCAPIN Lib "Port" ()
Declare Sub SOUNCAPDOUT Lib "Port" ()

Listing 1.4 De module PORTS.BAS met declaraties voor VB5.

Op vergelijkbare wijze kunnen de DLL-functies ook in VBA worden gedeclareerd (zie [5]). In principe kunnen alle experimenten uit dit boek dus ook vanuit Word of Excel worden uitgevoerd. De programma's worden dan als macro vanuit de toepassing gestart. Vanaf Word 7 en Excel 7 zijn de declaraties hetzelfde als in Visual Basic 5. Het onderstaande voorbeeld voor het produceren van een toontje uit de PC-luidspreker werd in Word 97 uitgevoerd. Meer VBA-voorbeelden treft u in hoofdstuk 14 aan.


Figuur 1.3 Een macro in Word 97.

In Word 97 kan ook in een macro een user-form worden gebruikt met eigen bedieningselementen. In dat geval moet voor "declare" het sleutelwoord "private" worden gezet. Ook alle procedures en functies worden dan met "private" gevormd.



1.3 Het aanroepen van de DLL onder Delphi

Ook in Delphi 4 verloopt de toegang tot de poorten steevast via PORT.DLL. Als eerste voorbeeld laten we de PC-luidspreker weer een simpel piepje produceren. Het programma gebruikt twee knoppen: bij de eerste (Ton1) wordt een tellerlus gebruikt, terwijl bij de tweede (Ton2) de Delay-functie wordt aangeroepen.


Figuur 1.4 Het eerste Delphi-programma.

De DLL-extensies worden als normale procedures en functies in dezelfde unit opgenomen en bevatten in plaats van programmacode een verwijzing naar de externe DLL. De sleutelwoorden "stdcall" en "external" regelen de doorgave van de parameters aan de DLL en zijn dan ook bepalend voor een correcte afhandeling.

Van belang is ook de keuze van de juiste typen voor de door te geven variabelen. Het is ons gebleken dat veel integer-typen niet meer compatibel zijn tussen de verschillende Delphi-versies. In het bijzonder kunnen zich problemen voordoen wanneer u een broncode uit Delphi 3 in Delphi 4 wilt gebruiken (of omgekeerd). Er doen zich geen problemen voor met de typen Word (16 bit zonder teken), DWord (32 bit zonder teken) en Real.

In Delphi mag de eigenlijke unit (*.pas) niet dezelfde naam hebben als het project (*.dpr). In dit boek hanteren we daarom de conventie de letter P (voor "project") aan de naam van het projectbestand te "hangen". Zodoende hoort het project "TON1P.DPR" bij de unit TON1.PAS. Tijdens het compileren resulteert dat in het uitvoerbare programma TON1P.EXE, dat omwille van de overzichtelijkheid op de CD weer is hernoemd tot TON1.EXE.

Wanneer u voor het genereren van pauzes voor een tellerlus kiest, dan zal u al snel de ten opzichte van Visual Basic in totaal ongeveer 10 maal hogere snelheid opvallen. Bij een 200-MHz-PC resulteert een tellerlus die tot 10000 telt in een frequentie van ongeveer 5 kHz. Bij nog snellere computers is de toon nauwelijks meer hoorbaar.

unit Ton1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics,
  Controls, Forms, Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Ton1: TButton; Ton2: TButton;
    procedure Ton1Click(Sender: TObject);
    procedure Ton2Click(Sender: TObject);
end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure OUTPORT (adr: Word; Wert: Word);
    stdcall; external 'PORT.dll';

function INPORT (adr: Word):Integer;
    stdcall; external 'PORT.DLL';

procedure DELAY (ms: Word);
    stdcall; external 'PORT.DLL';

procedure TForm1.Ton1Click(Sender: TObject);
var n, t: Integer;
begin
  For n := 1 to 100 do begin
    OutPort (97, (InPort (97) OR 2));
    For t := 1 to 10000 Do;
    OutPort (97, (InPort (97) AND 253));
    For t := 1 to 10000 Do;
  end;
end;

procedure TForm1.Ton2Click(Sender: TObject);
var n: Integer;
begin
  For n := 1 to 100 do begin
    OutPort (97, (InPort (97) OR 2));
    Delay (1);
    OutPort (97, (InPort (97) AND 253));
    Delay (1);
  end;
end;

end.

Listing 1.5 Geluidsproductie in Delphi (Ton1.pas).

Voor veel toepassingen is een betrouwbare timing, onafhankelijk van de gebruikte computer, onontbeerlijk. Via de procedure Delay is de timing in principe in elke omgeving hetzelfde. Door aanklikken van de knop Ton2 verkrijgt u een constante toon van 500 Hz die op de Delay-procedure is gebaseerd.

Ook in Delphi is het handig alle declaraties van de DLL in een aparte module (hier in de vorm van een unit) onder te brengen. De unit PORTINC.PAS (in gecompileerde vorm PORTINC.DCU) kan dan in elk nieuw project worden ingevoegd zodat u zich nooit meer zorgen hoeft te maken over de afzonderlijke declaraties.

unit PORTINC;

interface

uses windows;

const THEDLL='PORT.DLL';

Function OPENCOM(S:PCHAR):Integer;stdcall; external THEDLL;
Procedure RTS(d:WORD);stdcall; external THEDLL;
....
Procedure SOUNDCAPIN; stdcall; external THEDLL;
Procedure SOUNDCAPOUT; stdcall; external THEDLL;

implementation

end.

Listing 1.6 Declaraties van PORT.DLL in de module PORTINC.PAS.


Opmerking bij de Nederlandse uitgave:
Om vergissingen en tikfouten te voorkomen, zijn alleen de commentaarteksten bij de in het boek afgedrukte listings vertaald. Namen van procedures, variabelen en dergelijke zijn onvertaald gebleven. Op de CD bij dit boek staan de originele Duitstalige (voorbeeld)programma's zoals deze door de schrijvers zijn gemaakt.

.
Startseite Bücher Software SatTV Musik Kontakt

Für Inhalt und weitere Verzweigung externer Links sind die Betreiber der dortigen Seiten verantwortlich - H.-J. Berndt