******* THE DIGITAL I/O WILDCARD DRIVERS *********

Below is a glossary of the functions in this package.  For information on using
the functions, see the following section.

CODE IO.C@  ( offset\module_num -- byte )
\ uchar IO_Fetch_Char ( int offset, int module_num )

\ fetch byte from io (wildcard module) region at specified offset and module_num
\ valid offsets are in the range 00-0xFF, valid module_nums are 0-7.


CODE IO.C!  ( byte\offset\module_num -- )
\ void IO_Store_Char ( uchar byte, int offset, int module_num )

\ store byte to io (wildcard module) region at specified offset and module_num
\ valid offsets are in the range 00-0xFF, valid module_nums are 0-7.


CODE IO.CHANGE.BITS ( data\mask\offset\module_num -- )
\ void IO_Change_Bits ( uchar data, uchar mask, int offset, int module_num )

\ in the io (wildcard module) region at specified offset and module_num,
\ changes bits specified by 1's in mask
\ to equal the corresponding bit values in data parameter;
\ all other bits are unchanged.
\ valid offsets are in the range 00-0xFF, valid module_nums are 0-7.


CODE IO.SET.BITS  ( mask\offset\module_num -- )
\ void IO_Set_Bits ( uchar mask, int offset, int module_num )

\ in the io (wildcard module) region at specified offset and module_num,
\ sets to 1's the bits specified by 1's in mask; all other bits are unchanged.
\ valid offsets are in the range 00-0xFF, valid module_nums are 0-7.


CODE IO.CLEAR.BITS  ( mask\offset\module_num -- )
\ void IO_Clear_Bits ( uchar mask, int offset, int module_num )

\ in the io (wildcard module) region at specified offset and module_num,
\ clears to 0's the bits specified by 1's in mask; all other bits are unchanged.
\ valid offsets are in the range 00-0xFF, valid module_nums are 0-7.


******* HOW TO USE THE DIGITAL I/O WILDCARD DRIVERS *********

The Digital I/O Wildcard implements 20 digital I/O lines.
The data lines are grouped as five 4-bit nibbles.  Each set of 4 lines has an
associated data register, plus a single bit in the direction register that
determines the direction (0=input, 1=output) of the associated nibble.
Lines 0 through 15 can be nibble-programmed as either input or output,
while lines 16 through 19 are always configured as inputs.

You must set the direction of any desired output register
before using the Digital I/O Wildcard.
A nibble is configured as an output by writing a 1 to the corresponding
bit in the direction register.  At powerup or after a hardware reset,
the direction register and all output data latches are zeroed,
causing all lines to be set to input mode.
A software restart (e.g., typing COLD at the Forth prompt) does not change the
state of the direction register or output latches.

Once a nibble has been configured as an output by writing a 1 to the corresponding
bit in the direction register, you can set a specified output bit high or low
by writing to the corresponding bit in its data register as explained below.
Named constants defined in this device driver make it easy
to invoke the IO control functions.

DEFINING THE MODULE NUMBER CONSTANT

Each IO function requires the module number to be passed as an argument.
Valid module numbers are 0 through 7.  The two least significant bits of
the module number are specified by the 2-position jumper on each Wildcard,
and the most significant bit is the Wildcard Port (0 or 1) into which
the Wildcard is installed.  For example, if both jumper caps are present
on the Digital I/O Wildcard, and it is installed in Port 1,
then its module number is 7 (binary 111).

We recommend that a module number constant be defined using a name of your choice.
Forth example:
  7 CONSTANT DIO_MODNUM   \ module number of the digital i/o wildcard
C example:
  #define DIO_MODNUM  7   // module number of the digital i/o wildcard

HOW TO SET THE DIRECTION REGISTER

Let's assume that we want to set the direction of
lines 0 through 11 to output while leaving
lines 12 through 19 in their default input mode.  As the following example shows,
we bitwise-OR the constants DIR_0_3, DIR_4_7, and DIR_8_11 to derive a bit mask
used to modify the direction register.
Forth example:
  DIR_0_3 DIR_4_7 OR DIR_8_11 OR  DIR_REG  DIO_MODNUM  IO.SET.BITS
C example:
  IO_Set_Bits( DIR_0_3 | DIR_4_7 | DIR_8_11, DIR_REG, DIO_MODNUM);

The constants DIR_0_3, DIR_4_7, etc. are bit masks that identify the bit
in the direction register for the specified nibble.  DIR_REG is a constant
that returns the offset of the direction register.  DIO_MODNUM is the user-defined
constant that returns the module number.  The example command sets the specified
bits high, thereby configuring the corresponding nibbles as outputs.

The following command configures bits 12-15 as inputs.
Forth example:
  DIR_12_15  DIR_REG  DIO_MODNUM  IO.CLEAR.BITS
C example:
  IO_Clear_Bits( DIR_12_15, DIR_REG, DIO_MODNUM );


HOW TO MODIFY OUTPUT BITS

Each of the constants LINE_0 through LINE_15 returns the bitmask of the associated
output line.  Each of the constants DATA_0_3, DATA_4_7, etc. returns the offset
of the data register for the named lines.  Note that the signal names incorporated
into these constants are in the decimal base.  To set or clear an output line after
the nibble has been configured as an output, use the IO set or clear bits functions.

The following example sets lines 5 and 6 high in the DATA_4_7 register.
Forth example:
  LINE_5 LINE_6 OR  DATA_4_7 DIO_MODNUM IO.SET.BITS
C example:
  IO_Set_Bits( LINE_5 | LINE_6,  DATA_4_7, DIO_MODNUM );

To set lines 7 and 8 low, we note that they are split across two nibbles
(namely, DATA_4_7 and DATA_8_11), so two commands are needed.
Forth example:
  LINE_7 DATA_4_7 DIO_MODNUM IO.CLEAR.BITS
  LINE_8 DATA_8_11 DIO_MODNUM IO.CLEAR.BITS
C example:
  IO_Clear_Bits( LINE_7, DATA_4_7, DIO_MODNUM );
  IO_Clear_Bits( LINE_8, DATA_8_11, DIO_MODNUM );

Let's assume that lines 0-15 have been configured as outputs,
and you want to clear all of them to 0.  This could be accomplished
by writing to each of the four relevant data registers as follows.
Forth example:
  0 DATA_0_3   DIO_MODNUM IO.C!
  0 DATA_4_7   DIO_MODNUM IO.C!
  0 DATA_8_11  DIO_MODNUM IO.C!
  0 DATA_12_15 DIO_MODNUM IO.C!
C example:
  IO_Store_Char( 0, DATA_0_3, DIO_MODNUM );
  IO_Store_Char( 0, DATA_4_7, DIO_MODNUM );
  IO_Store_Char( 0, DATA_8_11, DIO_MODNUM );
  IO_Store_Char( 0, DATA_12_15, DIO_MODNUM );

Similarly, to set lines 0-15 high, you could write 0x0F to each of the four
data registers.
Forth example (in HEX base):
  0F DATA_0_3   DIO_MODNUM IO.C!
  0F DATA_4_7   DIO_MODNUM IO.C!
  0F DATA_8_11  DIO_MODNUM IO.C!
  0F DATA_12_15 DIO_MODNUM IO.C!
C example:
  IO_Store_Char( 0x0F, DATA_0_3, DIO_MODNUM );
  IO_Store_Char( 0x0F, DATA_4_7, DIO_MODNUM );
  IO_Store_Char( 0x0F, DATA_8_11, DIO_MODNUM );
  IO_Store_Char( 0x0F, DATA_12_15, DIO_MODNUM );

The hex literal 0x0F has the least significant 4 bits set, so storing 0x0F
to a data register sets all 4 bits in the nibble high.

Recall that lines 16-19 are always inputs.  Writes to lines configured
as inputs have no effect.

HOW TO READ I/O BITS

Data reads of nibbles configured as inputs return the level on the pins,
and data reads of nibbles configured as outputs
return the most recently written data (which is 0 after a power-up or hardware
reset).  The following example reads the contents of the DATA_16_19 register
and stores it in the variable read_contents.
Forth example:
  variable read_contents
  DATA_16_19 DIO_MODNUM IO.C@  read_contents C!
C example:
  static uint read_contents;
  read_contents = IO_Fetch_Char( DATA_16_19, DIO_MODNUM );



******* HOW TO USE THE AC AND DC RELAY WILDCARD DRIVERS *****************

The AC Relay Wildcard implements 4 relay outputs, and the DC Relay Wildcard
implements 3 relay outputs. The relay lines are controlled via a 4-bit data nibble
at the offset specified by the constant RELAY_DATA.  The constants
RELAY_0, RELAY_1, RELAY_2, and RELAY_3 specify the bit masks of the associated
relay outputs.  Note that RELAY_3 is not implemented on the DC Relay Wildcard.

It is important to note that the AC and DC Relay Wildcards use ACTIVE LOW LOGIC.
Thus, to activate/turn on/close the contacts of a relay, write a 0 to it.
To deactivate/turn off/open the contacts of a relay, write a 1 to it.
After a power up or hardware restart, relays are in the off state.
Reading a bit value of 1 for a given relay means that it is off,
while reading a bit value of 0 means that it is on.


DEFINING THE MODULE NUMBER CONSTANT

Each IO function requires the module number to be passed as an argument.
Valid module numbers are 0 through 7.  The two least significant bits of
the module number are specified by the 2-position jumper on each Wildcard,
and the most significant bit is the Wildcard Port (0 or 1) into which
the Wildcard is installed.  For example, if neither jumper cap is present
on the Relay Wildcard, and it is installed in Port 1,
then its module number is 4 (binary 100).

We recommend that a module number constant be defined using a name of your choice.
Forth example:
  4 CONSTANT RELAY_MODNUM   \ module number of the relay wildcard
C example:
  #define RELAY_MODNUM  4     // module number of the relay wildcard


HOW TO SET THE DIRECTION REGISTER (only for pre-2004 relay wildcards)

On AC and DC Relay Wildcards shipped in 2004 or later, there is no direction
register, and the relay lines are automatically configured as outputs.

On AC or DC Relay Wildcards shipped before 2004, you must configure the relay
lines as outputs after every power-up or hardware reset as follows.
Forth example:
  01 DIR_REG RELAY_MODNUM IO.C!
C example:
  IO_Store_Char( 01, DIR_REG, RELAY_MODNUM );

Executing this command has no effect on post-2004 Relay Wildcards, as the
relay lines are pre-configured as outputs.


HOW TO MODIFY OUTPUT BITS

Each of the constants RELAY_0 through RELAY_3 returns the bitmask of the
associated relay output.  The constant RELAY_DATA returns the offset
of the relay data register.  To turn on (close the contacts of)
a relay output line, use the IO clear bits function.
To turn off (open the contacts of) a relay output line,
use the IO set bits function.  This reversal of the set/clear meaning is
due to the active low control logic used on the relay wildcards.
The outputs default to the off (open contact)
state after each power-up or hardware reset.  A software restart (such as
typing COLD at the QED-Forth prompt) does not change the output states.

This example sets relays 1 and 2 to the "on" (closed contact) state.
Forth example:
  RELAY_1 RELAY_2 OR  RELAY_DATA RELAY_MODNUM IO.CLEAR.BITS
C example:
  IO_Clear_Bits( RELAY_1 | RELAY_2, RELAY_DATA, RELAY_MODNUM );

To set relay 0 to the "off" (open contact) state, follow this example.
Forth example:
  RELAY_0  RELAY_DATA RELAY_MODNUM IO.SET.BITS
C example:
  IO_Set_Bits( RELAY_0, RELAY_DATA, RELAY_MODNUM );

To turn off all the relays to their open contacts states, execute the following.
Forth example (in HEX base):
  0F RELAY_DATA RELAY_MODNUM IO.C!
C example:
  IO_Store_Char( 0x0F, RELAY_DATA, RELAY_MODNUM );

The hex literal 0x0F has the least significant 4 bits set, so storing 0x0F
to a data register sets all 4 bits in the nibble high. Due to the active low
control logic, this turns off the relays.

Similarly, to turn on all the relays to their closed contacts states,
execute the following command.
Forth example:
  0 RELAY_DATA RELAY_MODNUM IO.C!
C example:
  IO_Store_Char( 0, RELAY_DATA, RELAY_MODNUM );

Storing zero to the relay data register sets all outputs bits high.
Due to the active low control logic, this turns on the relays.

HOW TO READ I/O BITS

The relay data register can be read using the IO fetch function.  The read
returns the most recently written data.  Owing to the active low control logic,
reading a 1 indicates that the relay is off (which is the default state
after a power-up or hardware reset), while reading a 0 indicates that the relay
is on.  The following example reads the contents of the RELAY_DATA register
and stores it in the variable active_low_contents.
Forth example:
  variable active_low_contents
  RELAY_DATA RELAY_MODNUM IO.C@  active_low_contents C!
C example:
  static uint active_low_contents;
  active_low_contents  = IO_Fetch_Char( RELAY_DATA, RELAY_MODNUM );


******* HOW TO USE THE POWER I/O WILDCARD DRIVERS *****************

The Power I/O Wildcard implements 8 opto-isolated high current output lines,
and 4 opto-isolated high voltage input lines.

The output lines are grouped as one 8-bit byte, and the input lines
are grouped as one 4-bit nibble.  Each group has an associated data register,
named POWER_OUT_DATA and POWER_IN_DATA respectively.

There is no direction register on the Power I/O Wildcard.

You can set a specified output bit high or low
by writing to the corresponding bit in its data register using the IO functions.
The IO fetch function is used to read the output data latches or the input lines.
Named constants defined in this device driver make it easy
to invoke the IO functions.

DEFINING THE MODULE NUMBER CONSTANT

Each IO function requires the module number to be passed as an argument.
Valid module numbers are 0 through 7.  The two least significant bits of
the module number are specified by the 2-position jumper on each Wildcard,
and the most significant bit is the Wildcard Port (0 or 1) into which
the Wildcard is installed.  For example, if both jumper caps are present
on the Power I/O Wildcard, and it is installed in Port 0,
then its module number is 3 (binary 011).

We recommend that a module number constant be defined using a name of your choice.
Forth example:
  3 CONSTANT POWER_IO_MODNUM    \ module number of the power i/o wildcard
C example:
  #define POWER_IO_MODNUM  3      // module number of the power i/o wildcard


HOW TO MODIFY OUTPUT BITS

Each of the constants POWER_OUT_0 through POWER_OUT_7
returns the bitmask of the associated output line on the Power I/O Wildcard.
The constant POWER_OUT returns the offset of the output data register.
To set (turn on, sinking current) or clear (turn off) an output line,
we typically use the IO set bits or clear bits functions.

This command turns on power output lines 5 and 6.
Forth example:
  POWER_OUT_5 POWER_OUT_6 OR  POWER_OUT_DATA POWER_IO_MODNUM IO.SET.BITS
C example:
  IO_Set_Bits( POWER_OUT_5 | POWER_OUT_6,  POWER_OUT_DATA, POWER_IO_MODNUM );

To turn off outputs 1 and 3, execute the following command.
Forth example:
  POWER_OUT_1 POWER_OUT_3 OR  POWER_OUT_DATA POWER_IO_MODNUM IO.CLEAR.BITS
C example:
  IO_Clear_Bits( POWER_OUT_1 | POWER_OUT_3, POWER_OUT_DATA, POWER_IO_MODNUM );

To clear all the outputs to the off state, execute the following command.
Forth example:
  0 POWER_OUT_DATA  POWER_IO_MODNUM IO.C!
C example:
  IO_Store_Char( 0, POWER_OUT_DATA, POWER_IO_MODNUM );

Similarly, to set all the output lines to the on state (sinking current),
execute the following command.
Forth example (in HEX base):
  FF POWER_OUT_DATA  POWER_IO_MODNUM IO.C!
C example:
  IO_Store_Char( 0xFF, POWER_OUT_DATA,  POWER_IO_MODNUM );

The hex literal 0xFF has all 8 bits set, so storing it to a data register
turns all the bits on.

HOW TO READ I/O BITS

The data registers can be read using the IO fetch function.
Inputs return the level on the pins.  Outputs return the most recently
written data, which is 0 (off, not accepting current) after a power-up
or hardware reset.  The following example reads the contents of the
POWER_IN_DATA register and stores it in the variable read_contents.
Forth example:
  variable read_contents
  POWER_IN_DATA POWER_IO_MODNUM  IO.C@  read_contents C!
C example:
  static uint read_contents;
  read_contents = IO_Fetch_Char( POWER_IN_DATA, POWER_IO_MODNUM  );

