Does the application require 4 _bi_directional UARTs? Or can some of your
MIDI ports be unidirectional (ie: how many MIDI ins and MIDI outs do you
need). Bit banging the transmit side is _much_ easier than the receive
side. You could set up an interrupt source at the bit rate (31250) and
easily have time to blast out the bits. I would suggest, perhaps, the 18C
series chips, running at a 10MHz crystal, with the PLL x4 to get 10MIPS.
This would give you 320 instructions per bit time, which is nearly
'forever'.

Unfortunately, to receive you need to run your interrupt handler somewhat
faster than the bit time. I have successfully built a software UART using a
interrupt 6x of the bit rate. I expect 5x would work pretty well.

So, you would have to take 1 interrupt every 64 instruction times. I'm
guessing about a 8 instruction time overhead in the interrupt handler (the
18C can be quite efficient at this) and perhaps 15 instructions per
interrupt per receiver. You could handle 5 transmitters by processing them
round-robin, one per interrupt (say another 10 instructions).

Assuming 2 receivers, this would take about 48 instructions, leaving you
with about 2.5 MIPS for 'task-level' code.

I really think this could be done on the 18C chips!

BTW: I have developed an 18C application that directly generates (no
hardware other than the PIC and 3 resistors) a full-screen animated NTSC
(monochrome) image -- this chip can really make things happen!

This thread has fascinated me from the beginning. I was sure that it was
possible to do this on a PIC.

What I have come up with so far:

I have designed, and written the critical code for, a scheme permitting 5
full-duplex software UARTs. This code requires a timer-driven interrupt at 5
times the baud rate. Code path in the interrupt handler, counting all
overhead, with all 5 channels going full blast in both directions, is 55
instruction times max, 51 instruction times average. This drops down
somewhat if the channels are running at less than full speed.

The simulated UARTS sample the input at 5x the baud rate, and determine bits
using the majority vote of the center 3 samples for each bit.

They detect framing errors, and can handle receive data streams slightly
faster than the nominal rate (ie: the stop bit can be less than 5 samples
long, so the UART can slip properly to handle a slightly overspeed input).

Both receive and transmit are double-buffered so that task level has an
entire byte time to handle a received character or prepare the next
character for transmission.

Relating this to the problem at hand:

On a 10MIP 18CXX2 chip (10MHz clock, PLL'd to 40MHz on chip), with a baud
rate of 31250, the interrupt rate would be every 64 instruction times. This
will leave at least 9 and on average 13 instructions between interrupts.

Thus, 'task level' code will get about 13/64 = 20% or so of the 10MIPs. You
can get quite a bit done with the remaining 2 MIPS!

For anyone who is interested, the basic structure of the interrupt handler
is something like this (expressed in a combination of pseudocode and "C" to
make it easier to follow, but the real code is of course assembly):

send values computed by last interrupt for output bits to the hardware

fetch current values input bits from the hardware into 5 5-bit shift
registers, one per channel.

The phrase 'compute next output bit for channel N' simply figures out what
start, data, or stop bit is to be sent.

The phrase 'process last 5 input bits received by channel N' involves using
the 5 input bits, treated as a 5 bit number, as an input to a large state
machine stored as a table in program memory. This will determine a new
state, and possibly an action to be performed (setting a bit in the receiver
register, or marking a byte received (correctly or with a framing error)).

The original requirement was for 4 channels, not 5. The interrupt rate
doesn't change as channels are deleted, but both maximum and average
interrupt handler times will go down.

For 5 channels: max is 55, average is 51 (2.0 MIPs available for task-level)
For 4 channels: max is 53, average is about 44 (3.1 MIPs available for
task-level)
For 3 channels: max is 51, average is about 38 (4.1 MIPs available for
task-level)
For 2 channels: max is 49, average is about 31 (5.2 MIPs available for
task-level)
For 1 channel: max is 44, average is about 22 (6.6 MIPs available for
task-level)