Digitale Ein-/Ausgänge
Mit Hilfe der LIBAD4 Bibliothek ist es sehr einfach, einen Digitalkanal auszulesen oder zu setzen. Dazu öffnet das Beispiel Messsystem (ad_open
), stellt dann die Ein-/Ausgaberichtung des Kanals ein (ad_set_line_direction
) und liest dann entweder den Kanals aus (ad_discrete_in
) oder setzt den Kanal (ad_discrete_out
). Zuletzt wird das Messsystem wieder geschlossen (ad_close
). Natürlich würde in einem realen Programm das Messsystem nur einmal am Beginn des Programms geöffnent und am Ende wieder geschlossen werden. Auch die Richung des Digitalkanals muss nur einmal eingstellt werden, d.h. es ist es nicht nötig ad_set_line_direction
vor jedem Auslesen oder Setzen aufzurufen.
Der Sourcecode des Beispiels befindet sich im LIBAD4 SDK im Verzeichnis examples/digital_io
(und dort jeweils in den Verzeichnissen c
, cs
und vb
für C/C++, C# und Visual Basic™ .Net). Wie alle Beispiele, verzichtet auch dieses aus Gründen der Einfachheit auf eine solide Fehlerbehandlung...
Die Messsysteme stellen die digitalen Leitungen organisiert in Digitalkanälen zur Verfügung. Der Aufbau ist vom Messsystem abhängig, so verfügt zum Beispiel die USB-PIO über 3 Digitalkanäle mit je acht Leitungen. Es werden immer alle Leitungen eines Digitalkanals auf einmal gelesen, gesetzt oder in der Richtung verändert (also im Beispiel der USB-PIO immer acht Leitungen auf einmal). Die Anzahl der Ports und Leitungen in einem Port ist im LIBAD Handbuch in Kapitel 6, Messsystem, beschrieben.
Das Beispiel verwendet die Routine do_digital_io()
um den Aufruf der LIBAD4 Funktionen ad_open()
, ad_set_line_direction()
, ad_discrete_in()
, ad_discrete_out()
und ad_close()
zu zeigen. An diese Routine wird der Name des Messgeräts (driver
), die Ein-/Ausgaberichtung (dir
), die Nummern der Digitalkanäle (chav
) und die auszugebenden Werte (datav
) übergeben. Die C/C++ Variante übergibt zusätzlich noch die Anzahl der Kanäle (chac
). Alle Argumente werden in main()
von der Kommandozeile gewonnen, entsprechend aufbereitet und dann an do_digital_io()
übergeben.
Der erste Parameter auf der Kommandozeile wird in driver
übergeben und legt das Messsystem fest. Beispielsweise öffnet "usb-pio
:@100" die USB-PIO mit der Seriennumer 100. Ausführliche Hinweise zu den notwendigen Namen für die verschiedenen Messsysteme finden Sie im LIBAD4 Handbuch in Kapitel 6, Messsysteme.
Dieser Name wird von do_digital_io()
direkt an ad_open()
weitergegeben. Der Rückgabewert von ad_open()
ist ein Handle, der das geöffnete Messsystem repräsentiert und an alle Funktionen der LIBAD4 übergeben werden muss. Im Fehlerfall wird -1
zurückgegeben.
Der Parameter dir
definiert die Ein-/Ausgaberichtung und wird in main()
durch das optionale Kommandzeilenargument -o
oder -i
bestimmt. Per default steht dieser Wert auf INPUT
und sorgt so für das Auslesen des Digitalkanals.
Die restlichen Argumente auf der Kommandzeile werden von main()
in Zahlen konvertiert und in die Felder chav[]
und datav[]
geschrieben (wobei datav[]
natürlich nur im Ausgabefall gefüllt wird). In einer Schleife über alle Kanäle stellt do_digital_io()
dann die Richtung des Digitalkanals ein. Dazu wird an ad_set_line_direction
das Messsystem (adh
, wie von ad_open()
geliefert), der Kanal (bestehend aus dem Kanaltyp AD_DIGITAL_IO
und der Nummer des Kanals chav[i]
) und die Richtung (dir
) übergeben. Die übergebene Richtung ist ein Bitfeld, mit dem alle Leitungen eines Ports eingestellt werden. Dabei eintspricht ein gesetztes Bit einem Eingang (1
==> Input
) und ein zurückgesetzes Bit einem Ausgang (0
==> Ausgang
).
Abhängig von der eingestellen Richtung wird entweder ad_discrete_in
oder ad_discrete_out
aufgerufen. Für beide Funktionen spezifiert das erste Argument (adh
, wie von ad_open()
geliefert) das Messsystem, das zweite Argument den Kanal (bestehend aus dem Kanaltyp AD_DIGITAL_IO
und der Nummer des Kanals chav[i]
) und das dritte Argument den Messbereich (für Digitalkanäle immer 0
). Als letztes Argument wird datav[i]
übergeben, das bei ad_discrete_in
den aktuellen Wert am Digitalkanal aufnimmt, bzw. bei ad_discrete_out
den Ausgabewert zur Verfügung stellt. Beide Funktionen geben im Fehlerfall einen Wert ungleich null zurück, wobei diese Fehlernummer immer einer Fehlernummer des Hostrechners entspricht (also z.B. unter Windows wird eine Fehlernummer aus <winerror.h>
zurückgegeben).
Danach wird der gemessene (oder neu gesetzte) Wert auf der Konsole ausgegeben. Neben einer Ausgabe in Hex gibt das Beispiel auch noch den Zustand ein einzelnen Leitungen des Kanals aus (wobei immer 16 Leitungen ausgegeben werden, auch wenn beispielsweise die USB-PIO nur acht Leitungen in einem Digitalkanal hat und deswegen auch nur die unteren acht Bits sinnvoll sind).
Nach dem Schleifenende wird das Messsystem mit ad_close()
wieder geschlossen.
LIBAD - Programmierschnittstelle (API)
/* Libad Digital I/O Example
*
* Example showing how to set and get the state of some digital inputs
* using bmcm's LIBAD4.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include "libad.h"
/* Line Direction Masks
*/
#define OUTPUT 0x0000
#define INPUT 0xffff
/* Get/Set Digital input(s)/output(s).
*/
void
do_digital_io (const char *driver, int dir, int chac, int chav[], uint32_t datav[])
{
int32_t adh;
int i;
/* Open DAQ system.
*/
adh = ad_open (driver);
if (adh == -1)
{
printf ("failed to open %s: err = %d\n", driver, errno);
return;
}
/* Set/get digital i/o.
*/
for (i = 0; i < chac; i++)
{
uint32_t mask;
int rc;
/* Setup port's direction.
*/
ad_set_line_direction (adh, AD_CHA_TYPE_DIGITAL_IO | chav[i], dir);
/* Either read from port or write to port.
*/
if (dir == INPUT)
rc = ad_discrete_in (adh, AD_CHA_TYPE_DIGITAL_IO | chav[i], 0, &datav[i]);
else
rc = ad_discrete_out (adh, AD_CHA_TYPE_DIGITAL_IO | chav[i], 0, datav[i]);
if (rc == 0)
{
printf ("cha %d: port = 0x%04x, lines 16..1 = ", chav[i], datav[i]);
/* Separate lines of that port.
*/
for (mask = 0x8000; mask != 0; mask >>= 1)
{
if (datav[i] & mask)
printf ("1");
else
printf ("0");
}
printf ("\n");
}
else
printf ("error: failed to read/write cha %d: err = %d\n", chav[i], rc);
}
/* Close DAQ system again.
*/
ad_close (adh);
}
/* Show usage.
*/
void
usage ()
{
printf ("usage: digital_io <driver> [-i] <cha1> .. <chan>\n"
" digital_io <driver> -o [<cha1>,]<val1> .. [<chan>,]<valn>\n"
" <driver> string to pass to ad_open()\n"
" - will prompt for name\n"
" <cha1> .. <chan> number of digital port\n"
" <val1> .. <valn> value to set digital output to\n");
}
/* Main entry point.
*/
int
main (int argc, char *argv[])
{
if (argc > 1)
{
char *name, *p, tmp[80];
int i, start, dir, chac, chav[16];
uint32_t datav[16];
/* First command line argument is the DAQ's name.
* If "-" is passed, then let's read the name from
* the console.
*/
name = argv[1];
if (strcmp (argv[1], "-") == 0)
{
printf ("data acquisition system to open: ");
fgets (tmp, sizeof(tmp), stdin);
p = strchr (tmp, '\n');
if (p)
*p = 0;
name = tmp;
}
/* Direction defaults to input, but may get overridden by -o
* on the command line.
*/
start = 2;
dir = INPUT;
if (argc > 2)
{
if (strcmp (argv[start], "-o") == 0)
{
dir = OUTPUT;
start++;
}
else if (strcmp (argv[start], "-i") == 0)
{
dir = INPUT;
start++;
}
}
/* Convert remaining command line arguments into channel
* numbers and values. Add those to the appropriate array.
*/
chac = 0;
for (i = start; i < argc; i++)
{
if (dir == INPUT)
{
/* Input case, parse channel numbers only.
*/
chav[chac] = atoi (argv[i]);
}
else
{
char *delim;
/* Output case, parse (optional) channel number
* and value to output.
*/
delim = strchr (argv[i], ',');
if (delim)
{
chav[chac] = atoi (argv[i]);
datav[chac] = atoi (delim+1);
}
else
{
chav[chac] = 1;
datav[chac] = atoi (argv[i]);
}
}
chac++;
if (chac >= 16)
break;
}
/* Set/get digital i/o and print results
* to the console.
*/
do_digital_io (name, dir, chac, chav, datav);
if (strcmp (argv[1], "-") == 0)
{
printf ("press return to continue...\n");
fgets (tmp, sizeof(tmp), stdin);
}
return 0;
}
else
{
usage ();
return 1;
}
}
// Libad Digital I/O Example
//
// Example showing how to set and get the state of some digital inputs
// using bmcm's LIBAD4.
using System;
using LIBAD4;
static class Example
{
// Line Direction Masks
const uint OUTPUT = 0x0000;
const uint INPUT = 0xffff;
// Get/Set Digital input(s)/output(s).
static void
do_digital_io (string driver, uint dir, int[] chav, uint[] datav)
{
// Open DAQ system.
int adh = LIBAD.ad_open (driver);
if (adh == -1)
{
Console.WriteLine ("failed to open {0}: err = {1}", driver, LIBAD.errno);
return;
}
// Set Set/get digital i/o.
for (int i = 0; i < chav.Length; i++)
{
uint mask;
int rc;
// Setup port's direction.
LIBAD.ad_set_line_direction (adh, LIBAD.AD_CHA_TYPE_DIGITAL_IO | chav[i], dir);
// Either read from port or write to port.
if (dir == INPUT)
rc = LIBAD.ad_discrete_in (adh, LIBAD.AD_CHA_TYPE_DIGITAL_IO | chav[i], 0, ref datav[i]);
else
rc = LIBAD.ad_discrete_out (adh, LIBAD.AD_CHA_TYPE_DIGITAL_IO | chav[i], 0, datav[i]);
if (rc == 0)
{
Console.Write ("cha {0,2}: port = 0x{1,4X}, lines 16..1 = ", chav[i], datav[i]);
// Separate lines of that port.
for (mask = 0x8000; mask != 0; mask >>= 1)
{
if ((datav[i] & mask) != 0)
Console.Write ("1");
else
Console.Write ("0");
}
Console.WriteLine ();
}
else
Console.WriteLine ("error: failed to write cha {0}: err = {1}", chav[i], rc);
}
// Close DAQ system again.
LIBAD.ad_close (adh);
}
// Show usage.
static void
usage ()
{
Console.WriteLine ("usage: digital_io <driver> [-i] <cha1> .. <chan>");
Console.WriteLine (" digital_io <driver> -o [<cha1>,]<val1> .. [<chan>,]<valn>");
Console.WriteLine (" <driver> string to pass to ad_open()");
Console.WriteLine (" - will prompt for name");
Console.WriteLine (" <cha1> .. <chan> number of digital port");
Console.WriteLine (" <val1> .. <valn> value to set digital output to");
}
// Main entry point.
static int
Main (string[] argv)
{
if (argv.Length > 0)
{
// First command line argument is the DAQ's name.
// If "-" is passed, then let's read the name from
// the console.
string name = argv[0];
if (argv[0] == "-")
{
Console.Write ("data acquisition system to open: ");
name = Console.ReadLine ();
}
// Direction defaults to input, but may get overridden by -o
// on the command line.
int start = 1;
uint dir = INPUT;
if (argv.Length > 1)
{
if (argv[start] == "-o")
{
dir = OUTPUT;
start++;
}
else if (argv[start] == "-i")
{
dir = INPUT;
start++;
}
}
// Convert remaining command line arguments into channel
// numbers and values. Add those to the appropriate array.
int[] chav = new int[argv.Length - start];
uint[] datav = new uint[argv.Length - start];
for (int i = start; i < argv.Length; i++)
{
if (dir == INPUT)
{
// Input case, parse channel numbers only.
chav[i - start] = int.Parse (argv[i]);
}
else
{
// Output case, parse (optional) channel number
// and value to output.
int delim = argv[i].IndexOf (',');
if (delim >= 0)
{
chav[i - start] = int.Parse (argv[i].Substring (0, delim));
datav[i - start] = uint.Parse (argv[i].Substring (delim+1));
}
else
{
chav[i - start] = 1;
datav[i - start] = uint.Parse (argv[i]);
}
}
}
// Set/get digital i/o and print results
// to the console.
do_digital_io (name, dir, chav, datav);
if (argv[0]== "-")
{
Console.WriteLine ("press return to continue...");
Console.ReadLine ();
}
return 0;
}
else
{
usage ();
return 1;
}
}
}
' Libad Digital I/O Example
'
' Example showing how to set and get the state of some digital inputs
' using bmcm's LIBAD4.
Imports System
Imports LIBAD4
Module Example
' Line Direction Masks
Const OUTPUT As UInteger = &H0000
Const INPUT As UInteger = &Hffff
' Get/Set Digital input(s)/output(s).
Sub do_digital_io (driver As String, dir As UInteger, ByVal chav As Integer(), ByVal datav As Integer())
' Open DAQ system.
Dim adh As Integer
adh = LIBAD.ad_open (driver)
If adh = -1 Then
Console.WriteLine ("failed to open {0}: err = {1}", driver, LIBAD.errno)
Exit Sub
End If
' Set Set/get digital i/o.
For i = 0 To chav.Length-1
Dim mask As Uinteger
Dim rc As Integer
' Setup port's direction.
LIBAD.ad_set_line_direction (adh, LIBAD.AD_CHA_TYPE_DIGITAL_IO or chav(i), dir)
If dir = INPUT Then
rc = LIBAD.ad_discrete_in (adh, LIBAD.AD_CHA_TYPE_DIGITAL_IO or chav(i), 0, datav(i))
Else
rc = LIBAD.ad_discrete_out (adh, LIBAD.AD_CHA_TYPE_DIGITAL_IO or chav(i), 0, datav(i))
End If
If rc = 0 Then
Console.Write ("cha {0,2}: port = &H{1:X4}, lines 16..1 = ", chav(i), datav(i))
' Separate lines of that port.
mask = &H8000
Do While mask <> 0
If (datav(i) And mask) <> 0 Then
Console.Write ("1")
Else
Console.Write ("0")
End If
mask = mask >> 1
Loop
Console.WriteLine ()
Else
Console.WriteLine ("error: failed to write cha {0}: err = {1}", chav(i), rc)
End If
Next
' Close DAQ system again.
LIBAD.ad_close (adh)
End Sub
' Show usage.
Sub Usage
Console.WriteLine ("usage: digital_io <driver> [-i] <cha1> .. <chan>")
Console.WriteLine (" digital_io <driver> -o [<cha1>,]<val1> .. [<chan>,]<valn>")
Console.WriteLine (" <driver> string to pass to ad_open()")
Console.WriteLine (" - will prompt for name")
Console.WriteLine (" <cha1> .. <chan> number of digital port")
Console.WriteLine (" <val1> .. <valn> value to set digital output to")
End Sub
' Main entry point.
Sub Main (ByVal argv As String())
If argv.Length > 0 Then
' First command line argument is the DAQ's name.
' If "-" is passed, then let's read the name from
' the console.
Dim name As String
name = argv(0)
If argv(0) = "-" Then
Console.Write ("data acquisition sytem to open: ")
name = Console.ReadLine ()
End If
' Direction defaults to input, but may get overridden by -o
' on the command line.
Dim start As Integer
Dim dir As UInteger
start = 1
dir = INPUT
If argv.Length > 1 Then
If argv(start) = "-o" Then
dir = OUTPUT
start = start + 1
Else If argv(start) = "-i" Then
dir = INPUT
start = start + 1
End If
End If
' Convert remaining command line arguments into channel
' numbers and values. Add those to the appropriate array.
Dim chav(argv.Length-1 - start) As Integer
Dim datav(argv.Length-1 - start) As Integer
For i = start To argv.Length-1
If dir = INPUT Then
' Input case, parse channel numbers only.
chav(i - start) = Int32.Parse (argv(i))
Else
Dim delim As Integer
delim = argv(i).IndexOf (",")
If delim >= 0 Then
chav(i - start) = Int32.Parse (argv(i).SubString (0, delim))
datav(i - start) = Int32.Parse (argv(i).SubString (delim+1))
Else
chav(i - start) = 1
datav(i - start) = Int32.Parse (argv(i))
End If
End If
Next
' Set/get digital i/o and print results
' to the console.
do_digital_io (name, dir, chav, datav)
If argv(0) = "-" Then
Console.WriteLine ("press return to continue...")
Console.ReadLine ()
End If
Environment.Exit (0)
Else
Usage
Environment.Exit (1)
End If
End Sub
End Module