The hardware is adapted from the Microchip application note: Care and Feeding of the PIC16C74 and its peripherals.
The firmware also uses (with adaptations) the example code given in that document.

Here's the code
Inputs
There are 8 analog inputs (or 7 if you use an external reference voltage, to change the measurement range of the A to D convertor, see the section below), 8 digital inputs and a 32 bit count input. The counter counts the number of 0 - 1 transitions since the last poll.
Outputs
There are 8 digital outputs on PORT D and a PWM output with 8 bit resolution.
The PWM output has a period of 256 uSec.
Serial Interface
This runs at 9600 Baud 8N1, using a 16c74A with a 4MHz clock.
Serial Protocol
The unit only sends data in response to a command from a user. The command can be either a simple poll for input data or it can set the outputs. In either case the user will get back the input data from the unit.
To just poll input data send an MY_ID byte followed by a newline character E.g.
C <CR>
Where the MY_ID character is the one defined in the 16c74A firmware.
To set the output values, send a MY_ID character followed by a colon then followed by the values for the digital outputs then the value for the PWM output. Each value is sent as 2 characters of HEX and is separated by a colon character.
The string is terminated with a newline character (ASCII 10)
Here's an example
C:0F:80<CR>
The C is the ident (MY_ID in the code). The digital outputs are set to 0F (note the letters A - F must be upper case). The newline terminator is not shown, no action is taken until this is received.
This will give rise to a 47 byte response, e.g.
C:40:B6:BC:17:FF:E3:C7:94:86:00000000:0001D475
This is 46 bytes plus a newline
The breakdown of this record is
Position Data Type Origin Size
| 0 |
MY_ID |
Firmware |
1 byte |
| 2,3 |
Digital inputs |
Port B |
1 byte |
| 5,6 |
Analog input 0 |
RA 0 |
1 byte |
| 8,9 |
Analog input 1 |
RA 1 |
1 byte |
| 11,12 |
Analog input 2 |
RA 2 |
1 byte |
| 14,15 |
Analog input 3 (Vref |
RA 3 |
1 byte |
| 17,18 |
Analog input 4 |
RA 5 |
1 byte |
| 20,21 |
Analog input 5 |
RE 0 |
1 byte |
| 23,24 |
Analog input 6 |
RE 1 |
1 byte |
| 26,27 |
Analog input 7 |
RE 2 |
1 byte |
| 29,36 |
Event Count Port |
RA4 |
4 bytes |
| 38,45 |
Time since last poll
(Microseconds) |
TMR 1 |
4 bytes |
| 46 |
Termination |
  |
1 byte |
I use (and the code implements) three indicators, RC0, RC2 and RC5. These light LEDs on the following conditions:
- RC 0 when the interrupt routine is entered
- RC 2 when waiting for a MY_ID byte
- RC 5 when a received data error is detected.