The Device Kit: BDigitalPort


The Device Kit: BDigitalPort

Derived from: (none)

Declared in: be/device/DigitalPort.h

Library: libdevice.so


Overview

The GeekPort classes apply to BeBox hardware only.

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()
   {
      uint8 val;
      BDigitalPort *dPortA = new BDigitalPort();
   
      if (dPortA->Open("DigitalA") == B_ERROR ||
         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()
   {
      uint8 val;
      BDigitalPort *dPortA = new BDigitalPort();
      BDigitalPort *dPortB = new BDigitalPort();
   
      if (dPortA->Open("DigitalA") == B_ERROR ||
         dPortA->SetAsOutput() != B_NO_ERROR) 
         goto error_tag;
   
      if (dPortB->Open("DigitalB") == B_ERROR ||
         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:

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()


      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


      ~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()


      status_t 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.

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.

RETURN CODES

The status codes apply to Open() only.


Read()


      ssize_t Read(uint8 *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.

RETURN CODES


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


      status_t SetAsInput(void)

      status_t SetAsOutput(void)

      bool IsInput(void)

      bool IsOutput(void) 

SetAsInput() and SetAsOutput() set the object's port to act as an input or output.

IsInput() and IsOutput() tell you if the port is an input or an output.

RETURN CODES

These apply to the SetAs...() functions.


Write()


      ssize_t Write(uint8 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.

RETURN CODES






The Be Book, in lovely HTML, for the BeOS Preview Release.

Copyright © 1997 Be, Inc. All rights reserved.

Be is a registered trademark; BeOS, BeBox, BeWare, GeekPort, the Be logo, and the BeOS logo are trademarks of Be, Inc.

Last modified June 30, 1997.