It’s great when a plan comes together. Having independently played around with my DottyMatrix serial-to-parallel printer interface and the Virtual Printer (designed to simulate my still-dead Epson MX-80 F/T III dot matrix printer), it was time to hook them together. And it worked!
On the left is the DottyMatrix using my prototyping board for the ATMEGA328PB microcontroller. Its serial port is connected via an FTDI cable to the CoolTerm terminal program running on my iMac. I’ve abandoned my original parallel breakout board because I want to prototype the full DottyMatrix setup. This has an 74HC595 shift register to control the eight data lines and a 74HC541 buffer for the other signal lines, ingoing and outgoing. There’s a 16×2 LCD display driven via the I2C port. The microcontroller is running at 8MHz using its internal clock. Soon I will add three LEDs, too, although their purpose is somewhat mysterious.
On the right is the Virtual Printer using my ATMEGA328P prototyping board. Its serial port is connected via an Adafruit FTDI Friend board to a Tera Term Pro terminal running in a Win10 VM running on the iMac. This is the VM I use to run Atmel Studio. It has a 74HC165 shift register to read the incoming data lines and also has a 74HC541 buffer for the other signals. The incoming data lines also go via an eight-segment bar LED (not really visible in this image, alas). The microcontroller is running at 16MHz using an external crystal.
Finally, there’s a ground wire connecting the two boards.
There was one slight glitch. It seems that when wiring up the data lines I’d reversed the order somewhere. Probably when hooking up to one of the shift registers. I’ll need to investigate that, but it was no problem. The C++ classes I’ve written for the shift registers allow me to specify whether data should be written (in the case of the 595) or read (in the case of the 165) as most significant bit (MSB) first or least significant bit (LSB) first. I simply changed one method call in the code for DottyMatrix to use LSB first as a parameter instead of MSB and all worked fine.
So now I can type a message in CoolTerm and see it printed (at early 1980s dot matrix speeds) in Tera Term.
One thing you’ll note in the image above (click on it for a larger version if it’s not clear) is that the last message timed out.
Dotty Matrix starts pulling characters from the serial connection as soon as they appear, going into a loop to stuff them into a buffer array. It keeps doing this until: a) the array is full; b) it encounters a null byte (0); or c) it encounters a linefeed (10, or 0x0A if you prefer). Both null bytes and linefeeds, then, are treated as terminators. But what if they never come? What if the host sends a few characters and then stops? DottyMatrix will wait only so long (actually, about 3 secs at the moment, but it’s configurable). If no termination character has been received by then, DottyMatrix clears the buffer, sends the READ_TIMEOUT message to the host and goes back into its ‘waiting for data’ loop.
So, after initialisation, here’s how the two things work together during the process of printing.
Dotty matrix has a print buffer of 254 characters (the buffer size is 255 but the last element always has to be a null terminator). The idea is that it will take a line’s worth of data at a time. The Epson MX-80 F/T III is nominally an 80-column printer, but can manage 132 characters per line in condensed mode. The buffer size also allows for the embedding of as many non-printing control characters as I’m likely to want.
After pulling the data from the serial port, DottyMatrix sets the CTS line LOW to tell the host machine that it shouldn’t send any more for the time being.
The process of printing the buffer’s worth of data is pretty much explained in the diagram, but there are a couple of things worth noting.
DottyMatrix checks the /ERROR, BUSY and PE (‘paper end’) lines before attempting to print a character. If either /ERROR or PE show a problem, the buffer is dumped and DottyMatrix comes to a screeching halt – on the basis that there’s something wrong with the printer. An error message is sent to the host to let it know that all is not well.
If a BUSY signal is detected, then DottyMatrix pauses before trying again. But it will try only so many times before timing out, reporting an error to the host and giving up trying to print.
Similarly, it goes into a wait loop after sending the character, expecting the printer to send an /ACK pulse. This too will time out if it’s not received within about 50µs. (I’ve played with this figure and that one seems to work.) The wait loop here pauses for just 1µs each time because the ACK condition is set by an interrupt. This means things will move on pretty much immediately once an ACK is received. If a timeout occurs, once again the buffer is cleared. We don’t stop running this time, though. The host gets an error message but can use that as a signal to try again.
I’ve written the code so that a simple change in a global variable determines whether DottyMatrix waits for ACK pulses or not. Not all printers or printer interfaces bothered with ACK, instead just using BUSY for flow control. I’m also working on a way of allowing the host to send special signals to turn ACK mode on or off (as well as other configuration settings). But more on this another time.
There’s still stuff to do. I need to experiment with pullup resistors on some lines. I also want to get the oscilloscope hooked up to check that the signals are what I think they should be. And then there are those three mystery LEDs…