Today I wasted a good portion of my day running around my University trying to get the forms I need for my Norwegian Visa application for my internship at Atmel in November. Ever seen the Hitchhiker’s Guide to the Galaxy movie, the scene where they try to free Trillian by filling out all the bureaucratic forms, and are forced to line up several times, each time with a different form? Today was like that, but worse, since half the staff are away on leave during the mid-year break, and the remaining staff are on lunch break. After all my trouble, I’ve now got to go back next week and pick up my $25 form letter stating that yes, I am indeed a student at Latrobe. Bah.
Now, back to the good stuff. I believe that every programmer has an Achilles Heel, that one algorithm that everyone else seems to have no trouble with, but for that person it causes endless frustration and misery. For me, that algorithm is the implementation of a software U(S)ART. I’ve now written and re-written different implementations several times, and although they eventually work, my bidirectional software UARTs always seem to miss the occasional character.
A software UART is supposed to be a simple affair – after all, with the most common 8-bit, no parity, one stop bit setting that many devices use, each frame is just the raw bytes framed by a low start and a high stop bit. This translates to some fairly simple code, which is usually run in a timer ISR to ensure that the timing of each bit is constant, to prevent reception errors at either end.
A unidirectional UART is possibly the easiest algorithm to get working, with the exception perhaps of a software SPI port. It’s when the requirements are for a bidirectional software UART that things get hairy, since now you’ve got to work out how to prevent the bit transmission and reception ISRs from tripping each other up and delaying the other long enough to corrupt the transfer. This is needed in my XPLAIN Bridge firmware for Atmel’s XPLAIN board, when the bridge operates as a USB to Serial converter. Since the AVR’s hardware UART is instead used as the PDI programmer transport to the XMEGA chip on the board, the serial channel is instead implemented in software on the USB AVR side.
It’s not just me that’s had trouble with this algorithm however; recognizing that it’s not as simple as it appears I originally outsources the software UART code on AVRFreaks, and ended up with an ASM and a C implementation from two kind members there. Both of these worked, but things got wonky when both ends of the link tried to send at the same time, as the transmission and reception code blocked each other.
After some complaints about my less-than-perfect UART code, I took yet another crack at it last night, trying to fix up the implementation so that lost characters would be the exception, rather than the rule. This has led to my new implementation of the software UART, which uses two timers and an external interrupt pin. This new implementation uses one timer for bit transmissions – each bit period, either the next bit to send is transmitted, or the next byte to send is loaded into a temporary buffer so that it can be shifted out. The reception code relies on an external interrupt pin to sense the start bit, which then starts the bit reception timer to shift in the incoming bits.
Additionally, I found that the Windows CDC driver does not like mostly-empty incoming packets, even though it is quite happy to issue them to attached devices – I found my Windows 7 machine locking up when I barraged it with a constant stream of single-byte packets (one for each incoming character). This also lead to the reception buffer being overflown due to the host not processing the packets fast enough, which corrupted the incoming data stream occasionally. To combat this, the bridge code has a third timer which flushes the current reception buffer contents to the host in a single packet rapidly (30 flushes a second) to aggregate multiple bytes into a single USB IN packet.
Tests on the latest code show this new code to be far, far more reliable that the old, although large streams in both directions can still cause some issues. I’d like some more testers on this before the next official release to get a wider opinion on the new code’s performance – I’ve started uploading pre-built versions of the project to the XPLAIN Bridge project page for convenience.