Digital In- / Outputs

It is very simple to read and set digital channels with the help of the LIBAD4 library. Therefore the example opens the measurement system (ad_open), then sets the in- and output direction of the channel (ad_set_line_direction) and then wether reads the channel (ad_discrete_in) or sets the channel (ad_discrete_out). At last it closes the measurement system again (ad_close). Evidently the measurement system would be opened just once at the start of the program and closed at the end in a real program. The direction of the digital channel needs to be set just once and it is not necessary to activate ad_set_line_direction before every reading or setting.

You will find the source code of the example in LIBAD4 SDK in the directory examples/digital_io (and in there in the according directories c, cs and vb for C/C++, C# and Visual Basic™.Net). Like all examples, this one does away with a proper error treatment due to simplicity of handling…

The measurement systems provide the digital lines organized in digital channels. The setup is depending on the measurement system, the USB-PIO for example holds three digital channels with each eight lines. All lines of a digital channel are always read, denoted or changed in direction all at once (for example eight lines at once in the USB-PIO). The amount of ports and lines in a port is described in the LIBAD manual in chapter 6, measurement systems.

The example provides the routine do_digital_io() showing how to use the LIBAD4 functions ad_open(), ad_set_line_direction(), ad_discrete_in(), ad_discrete_out() and ad_close(). The name of the measurement device (driver), the input/output direction (dir), the number of digital channels (chav) and the output value (datav) are passed on to this routine. In addition the C/C++ code passes on the number of channels (chac). All arguments are retrieved from the command line in main(), processed accordingly and then passed on to do_digital_io().

The first parameter on the command line gets passed as driver and denotes the measurement system. For example "usb-pio:@100" opens the USB-PIO with the serial number 100. You will find detailed descriptions for the necessary names of the different measurement systems in the LIBAD4 manual in chapter 6, measurement systems.

This name will be passed on by do_digital_io() directly to ad_open(). The return value of ad_open() is a handle, that represents the opened measurement system and has to be returned to all functions of the LIBAD4. In case of an error -1 will be returned.

The parameter dir defines the input/output direction and denotes main() by the optional command line argument -o or -i. This value defaults to INPUT and therefore provides the readout of the digital channels.

The remaining arguments on the command line are converted into numbers by main() and filled in the arrays chav[] and datav[] (whereas datav[] is just passed in case of output). The function write_analog_outputs() processes the array in a loop over all channels and denotes the directions of the digital channel. The measurement system (adh, as delivered by ad_open()), the channel (consisting of the channel type AD_DIGITAL_IO and the channel number chav[i]) and the direction (dir) are passed to ad_set_line_direction. All lines in a port are denoted by the direction in the bit array. A denoted bit equals an input (1 ==> Input) and a denoted back bit equals an output (0 ==> Ausgang).

Depending on the passed direction, the ad_discrete_in or the ad_discrete_out is being activated. The first argument (adh, as delivered by ad_open()) specifies the measurement system for both functions, the second argument specifies the channel (consisiting of the channel type AD_DIGITAL_IO and the channel number chav[i]) and the third argument specifies the measurement range (for digital channels always 0).

The last argument is passed as datav[i], that acquires the recent value on the digital channel at ad_discrete_in, respectively provides the output value at ad_discrete_out. On error the function returns a non-zero error code. This error code corresponds to the host computer's operating system (for example in Windows an error number is returned from <winerror.h>).

Then the measured (or reset) value of the console is dumped. Aside the output in hex the example releases the status of the single lines of the channel (it will always be 16 lines read out, even if – for example the USB-PIO – has just got eight lines in the digital channel and therefore only the lower eight bits are useful).

The measurement system will be closed at the end of the loop ad_close().

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