Arduino software serial - full duplex

I need two serial ports for this Atmega328 Arduino project, but this processor has only one hardware UART. The processor, hardware design, and programming environment are already set, and I cannot change the hardware or processor at all, so a software solution is required.

The included Arduino libraries provide a SoftwareSerial library which I have discovered is only half duplex - during the send routine, interrupts are disabled, which of course means that the interrupt driven receive routine is shut down.

Before I implement my own full-duplex library, I wanted to find out if others have found a simple solution for this, or if there are libraries out there that implement it correctly.

I've not used the Arduino, but have written efficient soft UARTs on quite a number of platforms. Which approach is best on a given platform will depend upon what types of bit-munging operations it can perform most efficiently. I would offer a few suggestions, though:

1. Assign maximum priority to the serial-polling ISR, and run it at preferably 3x the desired data rate. Sample all your inputs and write all your outputs at the start of this interrupt routine, and then figure out what the outputs should be for the next pass. This will help to minimize any timing skew that might otherwise be caused by variable interrupt processing time.
2. For the receiver, instead of using a state machine as such, it may be helpful to effectively shift the incoming data into a big shift register. If the pattern of bits indicates a byte was received, grab the data and clear the appropriate bytes.
  ... near start of interrupt (for consistent timing)
shiftreg >>= 1;
if (IN_PORT)
shiftreg |= 0x20000000;
... other interrupt logic, then...
if ((shiftreg & 0x20000007) == 0x20000001)
{
int result = 0;
if (shiftreg & 0000000040) result |= 1;  // Note: constants are OCTAL!
if (shiftreg & 0000000400) result |= 2;
if (shiftreg & 0000004000) result |= 4;
if (shiftreg & 0000040000) result |= 8;
if (shiftreg & 0000400000) result |= 16;
if (shiftreg & 0004000000) result |= 32;
if (shiftreg & 0040000000) result |= 64;
if (shiftreg & 0400000000) result |= 128;
// Do something appropriate with result, then...
shiftreg |= 0x3FFFFFFFF;
}
else if (shiftreg = 1)
{
... Do something with long-break (will be detected exactly once)
}


Note that while the worst-case time may be significant, the normal-case time will be quite fast. Further, when an incoming byte is detected, one could copy it to another word of memory and do the bit-munging on a later interrupt. Since serial-transmit will only need to do something every third interrupt, the bit-munging could be done on interrupts where the serial-transmit routine doesn't run.

You say that you can't change existing hardware - can you add? If both serial lines will not need to be active at the same time then you could use a multiplexer or analog switch on the UART lines and switch back and forth between the two devices needing to be served. You'll just have to make sure when you switch that the level on your TX pin remains high when it's switch away from the UART (to prevent a false START condition).

• Both serial lines need to be active simultaneously. I cannot add hardware in this design. – Adam Davis Dec 6 '11 at 7:58

When I needed a second hardware UART on a AVR, I used a MAX3100 device. It has an SPI interface and is very easy to use. They are quite expensive, but save a lot of work.

• i think he's up a creek if he can't add to or change the hardware... also that price he may as well throw in an ATMega644 which has 2 hardware UARTs (and write his own SPI interface for it)... but I guess that defeats the "saves a lot of work" part :) – vicatcu Dec 5 '11 at 18:49
• At that point they'd be better off shoving an atmega1280 in there with 4 hardware UARTs. Sadly, a hardware solution is not going to meet their requirements. – Adam Davis Dec 5 '11 at 18:50
• @AdamDavis lol you one up'ed me on a more capable AVR :) – vicatcu Dec 5 '11 at 18:53
• @AdamDavis I obviously don't know all of the specifics of what you are dealing with, but generally even if a client says they need to stay with a specific chip, if you can give them good enough reason to switch, they will switch. – Kellenjb Dec 5 '11 at 18:53
• He's using an Arduino and can't change the processor. It's quite easy to interface a chip like that using a suitable prototyping shield. – Leon Heller Dec 5 '11 at 18:57

Will the client permit you to use an Arduino Mega which has 4 hardware serial ports?

http://www.arduino.cc/en/Main/ArduinoBoardMega

• Why the negative vote? – JonnyBoats Dec 5 '11 at 19:21
• Because he can't change the processor, presumably. – Leon Heller Dec 5 '11 at 19:36
• I did not downvote you, but the first part of the question does indicate that changing the processor is not an option. Suggesting something the OP already ruled out may result in downvotes... – Adam Davis Dec 5 '11 at 20:50
• This probably would have been better as a comment than an answer, but probing the boundary of the "arduino" requirement seems like a worthwhile idea, as arduino is technically a brand name encompassing a variety of boards, despite it often being shorthand for the currently popular mega328p&mega8u assembly. – Chris Stratton Dec 5 '11 at 21:29

Don't use SoftSerial, at least use NewSoftSerial... I think it's a really challenge (maybe even impossible) feat to pull off multi-channel full-duplex UART communication without two hardware UARTs... with NewSoftSerial, I think the sacrifice you make is that you can only "use" one channel at a time. I think if it could be done with Arduino it would have been done by now.

See discussion here about using multiple instances of NewSoftSerial.

• The SoftwareSerial built into the arduino 1.0 development environment is essentially NewSoftSerial. I did try NewSoftSerial just in case, but the issue is the same - it cannot receive while it's sending. – Adam Davis Dec 5 '11 at 18:47
• You can see the details of the failure here: arduino.cc/forum/index.php/topic,81756.0.html I've asked them to update the documentation so later users don't fall into this trap. – Adam Davis Dec 5 '11 at 20:36
• I would say the feasibility of multi-channel communication really depends on the ratio of baud rate to clock rate. Slow enough and you have plenty of time to handle multiple transmit and receive channels. – Chris Stratton Dec 5 '11 at 21:33