The Device Kit: BDigitalPort

Derived from: public BObject

Declared in: <device/DigitalPort.h>


Overview

The BDigitalPort class is the programmer's interface to the GeekPort's two digital ports. Each digital port is an 8-bit wide device that can be set for input or output. The following illustration shows the disposition of the GeekPort connector pins as they are assigned to the digital ports:

Each pin in a digital port transmits the value of a single bit; the pins are labelled by bit position. Thus, A0 is the least significant bit of digital port A, and A7 is its most significant bit. You can use any of the seven ground pins (1, 6, 8, 10, 12, 14, and 19) in your digital port circuit. The unmarked pins (24-33) are the analog ports; see BA2D and BD2A for more information on these ports.

Devices that you connect to the digital ports should send and (expect to) receive voltages that are below 0.8 Volts or above 2.0 Volts. These thresholds correspond, respectively, to the greatest value for digital 0 and the least for digital 1 (as depicted below). The correspondence to bit value for voltages between these limits is undefined.

Although there's no lower voltage limit for digital 0, nor upper limit for digital 1, the BeBox outputs voltages that are no less than 0 Volts, nor no more than +5 Volts. Your input device can exceed this range without damaging the BeBox circuitry: Excessive input emf is clipped to fall within [-0.5V, +5.5V].

Be aware that behind each digital port pin lies a 1 kOhm resistor.


BDigitalPort Objects

To access a digital port, you construct a BDigitalPort object, open it on the port you want, assign the object to work as either an input or an output, and then read or write a series of bytes from or to the object.

In the following example, we open and read from digital port A:

   #include <DigitalPort.h>
   
   void ReadDigitalPortA()
   {
      char val;
      BDigitalPort *dPortA = new BDigitalPort();
   
      if (dPortA->Open("DigitalA") <= 0 ||
         dPortA->SetAsInput() != B_NO_ERROR) {
         ~dPortA;
         return;
      }
   
      while ( /* whatever */ ) {
   
         /* Read() returns the number of bytes that were
          * read; a successful read returns the value 1.
          */
         if (dPortA->Read(&val) != 1) 
            break;
         
         /* Do something with the value. */
         ...
   
         /* Snooze for a bit. */
         snooze(1000);
      }
      dPortA->Close();
      delete dPortA;
   }

As shown here, the BDigitalPort is constructed without reference to a specific port. It's not until you actually open the object (through Open() ) that you have to identify the port that you want; identification is by name, "DigitalA" or "DigitalB". The Read() function returns only one value per invocation, and is untimed --if you don't provide some sort of tethering (as we do with snooze(), above) the read loop will spin as fast as possible.

To safeguard against an inadvertant burst of equipment-destroying output, the digital port is set to be an input when it's opened, and automatically reset to be an input when you close it.


Using Both Digital Ports at the Same Time

To access both digital ports at the same time, you have to construct two BDigitalPort objects. One of the objects can be used as an output and the other an input, both as outputs, or both as inputs.

In the following example, digital port A is used to write data to an external device, while digital port B is used for acknowledgement signalling: Before each write we set port B to 0, and after the write we wait for port B to be set to 1. We're assuming that the external device will write a 1 to port B when it's ready to receive the next 8-bits of data.

   void WriteAndAck()
   {
      char val;
      BDigitalPort *dPortA = new BDigitalPort();
      BDigitalPort *dPortB = new BDigitalPort();
   
      if (dPortA->Open("DigitalA") <= 0 ||
         dPortA->SetAsOutput() != B_NO_ERROR) 
         goto error_tag;
   
      if (dPortB->Open("DigitalB") <= 0 ||
         dPortB->SetAsOutput() != B_NO_ERROR) {
         goto error_tag;
   
      while ( /* whatever */ ) {
   
         /* Clear the acknowledgement signal. */
         val = 0;
         if (dPortB->Write(&val) != 1) 
            break;
   
         /* Reset val to the data we want to send. */
         val = ...;
   
         if (dPortA->Write(val) != 1) 
            break;
         
         /* Reset digital port B to be an input. */
         if (dPortB->SetAsInput() != B_NO_ERROR)
            break;
   
         /* Wait for the acknowledgement. */
         while (1) {
            if (dPortB->Read(&val) != 1)
               goto error_tag;
            if (val == 1)   
               break;
            snooze(1000);
         }
   
         /* Reset digital port B to be an output. */
         if (dPortB->SetAsOutput() != B_NO_ERROR)
            break;
      }
   error_tag:
      delete dPortA;
      delete dPortB;
   }

Notice that the acknowledgement signal only takes one bit of digital port B. This leaves seven bits that the external device can use to send additional data (triggers or gates, for example). The restriction in this scheme, given the structure shown above, is that this additional data would have to be synchronized with the acknowledgement signal.

By extension, if the data that you want to write to the external device is, at most, only seven-bits wide, then you could rewrite this example to use a single port: You would mask one of the bits as the acknowledgment carrier, and let the other seven bits carry the data, toggling the port between input and ouput as needed; the actual implementation is left as an exercise for the reader.


Overdriving an Output Pin

One of the features of the digital ports is that you can "overdrive" a pin from the outside. This means that you can set a port to be an output, and then force a voltage back onto the pin from an external device and read that voltage with the Read() function without having to reset the port to be an input . Keep in mind that there's a 1 KOhm resistor behind the pin (on the BeBox side), so your "overdrive" circuit has to be hot enough to balance the resistance.

When you overdrive an output pin, the voltage on the pin is altered for as long as the external force keeps it there. If you write an "opposing" value to an overdriven pin (through Write()), the written value won't pull the pin --the overdriven value will still be enforced. As soon as the overdrive voltage is removed, the pin will produce the voltage that was more recently written to it by the Write() function.


Constructor and Destructor


BDigitalPort()

      long BDigitalPort(void) 

Creates a new object that can open one of the digital ports. The particular port is specified in a subsequent Open() call.


~BDigitalPort

      virtual ~BDigitalPort(void)

Destroys the object, but not before closing the port that the object holds open (if any).

Deleting a BDigitalPort object sets the port (at the driver level) to be an input. The values at the port's pins are, at that point, undefined.


Member Functions


Open(), IsOpen(), Close()

      long Open(const char *name)
      bool IsOpen(void)
      void Close(void)

Open() opens the named digital port; the name argument should be either "DigitalA" or "DigitalB". See the GeekPort illustration in the Overview section for the correspondences between the port names and the GeekPort connector pins.

A digital port can only be held open by one BDigitalPort object at a time; you should close the port as soon as you're finished with it. Furthermore, each BDigitalPort object can only hold one port open at a time. When you invoke Open(), the port that the object currently has open is automatically closed--even if the port that you're attempting to open is the port that the object already has open.

When you open a digital port, the device is automatically set to be an input. If you want the port to be an output, you must follow this call with a call to SetAsOutput(). Just to be safe, it couldn't hurt to explicitly set the port to be an input (through SetAsInput()) if that's what you want.

Open() returns a positive integer if the named port is successfully opened. Otherwise, it returns B_ERROR.

IsOpen() returns TRUE if the object currently has a port open, and FALSE if not.

Close() does the obvious. When a digital port is closed, it's set to be an input at the driver level.


Read()

      long Read(char *buf)

Reads the data that currently lies on the digital ports pins, and returns this data as a single word in buf. Although you usually read a digital port that's been set to be an input, it's also possible to read an output port. In any case, the port must be open.

If the port was successfully read, the function returns 1 (the number of bytes read). Otherwise, it returns B_ERROR.


SetAsInput(), SetAsOutput() , IsInput(), IsOutput()

      long SetAsInput(void)
      long SetAsOutput(void)
      bool IsInput(void)
      bool IsOutput(void) 

SetAsInput() and SetAsOutput() set the object's port to act as an input or output. They return B_ERROR if the object isn't open, and B_NO_ERROR otherwise.

IsInput() and IsOutput() return TRUE and FALSE much as you would expect them to.


Write()

      long Write(char value)

Sends value to the object's port. The port continues to produce the written data until another Write() call changes the setting.

The object must be open as an output for this function to succeed. Success is indicated by a return value of 1 (the number of bytes that were written). Failure returns B_ERROR.






The Be Book, HTML Edition, for Developer Release 8 of the Be Operating System.

Copyright © 1996 Be, Inc. All rights reserved.

Be, the Be logo, BeBox, BeOS, BeWare, and GeekPort are trademarks of Be, Inc.

Last modified September 6, 1996.