SmartParallel: mission accomplished

It’s done. For now.

After weeks of dithering, I finally got around to making up one of my SmartParallel serial-to-parallel printer interface PCBs. I chose to solder the components – mostly surface mount – by hand and that went easier than I expected.

It’s not a densely populated board, so the size could be squeezed down a little. Still need to do a bit of post-soldering clean-up.

The only snag was that it didn’t work. Not right away, at least. And finding out where the problem lay was always going to be a challenge. Was there a mistake in the design? In the PCB layout? In the soldering? Or was it a software issue? And at which end – with the C++ code running on the ATMEGA328PB or the Go code on the Raspberry Pi? Or both?

A quick recap

The SmartParallel takes input via a serial port from a host – in my case, a Raspberry Pi. That input is assumed to be either lines of text to be printed, each terminated with a null character (ASCII 0), or commands for the interface (prefixed with ASCII 1).

The board then outputs the text via a parallel printer interface – I designed it to make use of my Epson MX-80 F/T III dot matrix printer.

The interface is ‘smart’ because using an onboard microcontroller makes it programmable.

We have a problem…

Although it didn’t work right off the bat, there were certain signs that were encouraging. The LEDs light up exactly as they should under the control of the ATMEGA328PB microcontroller. Also, I could program the ATMEGA via the ISP pins without trouble. And the interface was sending its start-up messages via the serial interface.

There were three little gotchas that tripped me up for a while.

One leg on one of the ICs (the 74LV541 buffer) had a bad joint. This took an hour or so to identify and two seconds with the soldering iron to fix.

During the layout of the PCB, I’d swapped the RTS and CTS signals for the serial port in terms of which GPIO pins they connected to on the ATMEGA328PB – just for ease of routing the lines. I’m not actually using RTS, so that didn’t matter. CTS is important, though, and this not only changed pin but also which port it’s on (it moved from PD4 to PE0, and therefore from PORT D to PORT E). I’d failed to make a note when I did this in Kicad and the code – developed in Atmel Studio 7 – was still configured for the prototype rig. It took me a couple of hours to realise that I hadn’t changed to code to reflect the pin change on the PCB version.

I defined some macros thus:

#define CTS_PIN PE0
#define CTS_REG PORTE
#define CTS_DDR DDRE
#define CTS_ONLINE  0 // CTS is active low
#define CTS_OFFLINE 1

Except that’s not quite how it started out. For CTS_REG – the register I’d need to write to in order to change the state of the CTS pin – I’d accidentally put PINE instead of PORTE. The PINn register for each port (PINA, PINB etc) is used to read the values of inputs to pins. To write values as outputs, you write to PORTn – or PORTE in this case. I was trying to write to PINE and, weirdly, it was working sometimes. But I think it can probably be best described as ‘undefined behaviour’.

It took me many hours to track down this problem. I tried writing messages to the serial port and LCD screen to show the status of the CTS pin. I attached the logic analyser and the oscilloscope, both of which were useful in showing me that, while the CTS line went high when the SmartParallel was busy sending stuff to the printer –  which is what’s meant to happen – it wasn’t coming down again.

I went into debug mode to watch the state of that pin: this was informative because I could see that the register was being set incorrectly, and that told me that the problem wasn’t with the CTS line being held high by something else in the circuit, or the remote machine (the Raspberry Pi). It had to be a code problem.

Garbled message

Once that issue was fixed, by changing PINE to PORTE, the SmartParallel was at the state where text was being printed, but not well.

I’m using a Go program on the Raspberry Pi which opens a text file and sends the contents line-by-line to the SmartParallel. But not all lines of text were getting printed. Some were coming through garbled.

I had the logic analyser probes attached to the serial port on the SmartParallel board and, using the UART decoder in PulseView, I could see that, indeed, many characters were mangled.

As seen in PulseView. This shows the end of an incoming line of text and the SmartParallel responding with the message: PRT_PRINTING. Note how the CTS line goes high.

The test setup has 30cm jumper wires running from the Raspberry Pi’s GPIO pins to a level shifter board, which then connects to the SmartParallel via another 30cm set of wires. I wondered whether the 3.3V signal level on the Raspberry Pi’s TX pin isn’t enough for the RX on the SmartParallel, which operates at 5V. I mean, it should be.

PulseView again. You can see incoming text being greeted with outgoing messages and the CTS line doing its thing – being high while the SmartParallel is printing.

But then I realised that there appears to be corruption of data on the SmartParallel’s output (its TX pin), and I’m capturing that right on the board. Huh?

In the end, this turned out not to be a SmartParallel or Raspberry Pi problem – it was a decoding issue. I simply wasn’t using a high enough sampling rate. The serial port is running at 19,200 baud and I was using a sampling rate of 50KHz. Upping this to 100KHz solved that problem – but not all the others!

Final hurdles

The Go program seemed to be interacting properly with the SmartParallel – waiting for the CTS line to go high then low again between each line of text. But many of the lines were not appearing on the printer. In some cases, I was getting only the latter part of some lines.

It turned out that the SmartParallel was simply throwing away incoming data.

In the function that prints the incoming line of text, once the text has been sent to the printer, the code clears the buffer used to hold the text. But it was also clearing the serial buffer!

In this shot, you can see that status LED 3, just to the left of the ISP socket, is lit. That’s because there’s no printer connected and so the SmartParallel has taken the CTS line high.

I think I’d done this on the assumption that there wouldn’t be anything in the serial stream, on account of the sending program waiting for the all-clear before sending any more data. But I think I may have got some timing wrong, or maybe the order in which things happen. Removing the line that cleared the serial buffer fixed the problem.

The SmartParallel now works well with the Go program. It could be a little faster, so there is tweaking to be done. But I’m confident about the hardware, so I’m happy to call this project effectively completed. I’ll be messing around with code, and if I do anything significant will make a blog post about it. Otherwise, I’ll just make notes on the project page about updates. And the code is on GitHub.

Blowing a fuse

I did hit one weird issue with the reset button – and this turned out to be a valuable lesson about using Atmel Studio 7 and its debug mode.

The reset button (PC6, aka pin 29) just stopped working. Normally, on the SmartParallel board the reset pin on the ATMEGA328PB is pulled high with a 1KΩ resistor. A tactile button shorts it to ground. And it had been working fine. Then suddenly it wasn’t.

I checked that the button was indeed pulling the pin to 0V. The microcontroller was just ignoring this.

It turned out that, after I’d finished my debugging session, I’d forgotten to switch back the programming device setting from ‘debugWire’ to ‘ISP’. I’d even tried reflashing the microcontroller several times after this (with no complaints from Atmel Studio).

I tried so many things to sort this that I can’t remember the exact chronology or error messages. What I did notice, however, is that I could pop back into debugWire debugging mode without Atmel Studio asking me if I wanted it to set the DWEN (debugWire enable) fuse. Normally, Atmel Studio handles this for you, but the fuse does need to be set.

And when DWEN is set, it disables the reset pin.

It took a couple of tries, but eventually I was able to go into debugging mode, then come out of it using ‘Disable debugWire and Close’ (which disabled the DWEN fuse), reset the programmer device mode to ISP and all was well again.

Lessons learned

What did I learn from all this?

Tools are useful: The logic analyser, oscilloscope, multimeter and debug tools in Atmel Studio were all extremely useful in this debugging process. You can’t have too many tools. But…

Use the tools properly: Tools are only as good as your knowledge of how to use them. That sampling rate error of mine wasted a lot of time and nearly sent me down the wrong path (assuming there was a problem with the serial communication).

» Go to the project page »

 

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.