Small and simple debugWire debugger dongle
So, I got back to this little project since I've got parts arriving for the other projects that will be a good week or so.
One thing I got stuck on is using the serial port in half-duplex mode. While I can do things in hardware and software so that the loopback is cancelled out and the Tx line doesn't override the Rx, I still wanted to see if I could get a serial driver to behave when Tx and Rx are effectively shorted.
Turns out this isn't so difficult, even with interrupt-driven communications, once you figure out the sequence of things.
The USART module in the ATMega32U4 has two transmit-related interrupts:
UDRE fires when the transmit buffer is empty (the last byte is on its way out the serial port) and
TXC fires when a byte has been sent.
I naïvely turned both on, and wrote interrupt handlers for both, thinking I could deal with things that way. Things seemed to work fine in full-duplex mode where both transmitter and receiver were enabled, but if I went to half-duplex mode, all hell broke loose.
At the time, I had no way to easily interface to a TTL UART, I was relying on shorting the pins to loop back the data, and so I was in the dark as to what was going on.
Now that I have a couple of TTL serial cables, I hooked one up and had a closer look. Receiving from the USART was fine, it was transmitting that was causing problems. It would lock up on transmit,
UDRE would continually ping the CPU, even if I tried manually clearing the flag.
What worked in the end was this procedure:
USR1B: turn on
UDRIE, turn off
UDREfires, see if there's more data waiting:
USR1B, turn off
UDRIE, turn on
TXCfires, again, check for new data:
USR1B: turn off
TXCIE, turn on
Having done this, the Tx line stays Hi-Z when idle, and only drives the line when actually transmitting. More over, characters sent by the USART are not seen in the receive buffer.
So last time I tackled this, I did get serial comms working. I was able to write something in a terminal, have it round-trip through the FIFO buffers, out the USART, back through the FIFOs, and back to the host. Take the alligator clip off that shorted D0 and D1 on the LeoStick, and comms (predictably) stopped. Put the clip back and everything works again.
I've expanded on this to support flashing the LEDs when data is received or sent on either interface. This is with a simple LED abstraction library I've written that allows me to set the LED to flash for a set duration so that it is visible.
Now, debugWire is a half-duplex protocol. It relies on an open-drain configuration to pull down on the reset line, which is pulled up by an external resistor connected to the MCU. It's not the only such protocol, anything that goes over 2-wire RS-485 is the same.
So there's good reason to see if I can't get half-duplex serial going. The idea is simple. Instead of enabling both Tx and Rx, I just enable Rx. When there's something in my transmit FIFO, I switch to Tx. This means the transmit pin can become high-impedance when not in use.
It can be bound directly to the Rx pin, and then conventional bi-directional level-shifting techniques as might be used for I²C can be used. These are usually good for up to 400kbps, so plenty fast enough for debugWire at 125kbps.
I've still got a bit of work to do on this. In my last posting there were a few good suggestions put forward for interfacing. One being to use a proper open-collector buffer chip.
@K.C. Lee had an interesting one: use a resistor on the Tx line. On idle, it'd behave like a pull-up, and when transmitting a logic '0', it'd pull the reset line down as needed. As a solution I like the simplicity, however thinking about it tonight, I wonder what happens if the device being debugged is running at 2-3V instead of 5V like the debugger (powered from USB).
An alternative option along these lines is to just use a signal diode, cathode towards the Tx pin: then the USART sending a '0' will pull the reset pin down as expected, and the external pull-up on the reset line will do the rest. Receive still may need level shifting (even if 3V will be taken a a '1' by the debugger chip), and it's possible a similar diode (anode to pin) with a local pull-up may do the job.
As for debugging the debugger (yo dawg!), another trick I've been employing… I'm using USB for my comms to the host, and one nice feature of USB (and Dean Camera's LUFA stack) is that you can present multiple interfaces to the host.
So; primary interface (ultimately for avarice) will be "port 1", and my debugging information can go out on "port 2". I've #ifdef'd the code so that I can turn this feature on and off for debug and production builds of the firmware. I'm able to use printf-style debugging (at the cost of firmware bloat and some reduced performance) using this method.
It does make me wonder whether it'd be practical to do some stack/register voodoo to basically run the user application in a separate "thread", allowing an on-chip debugger to single-step the application, but that's beyond the scope of this project.
So, as mentioned in the description, this is borne out of frustration of trying to debug what's going on inside the ATTiny24A I'm using for the #Solar-powered cloud computing project. This one chip hasn't got to do much, turn on a charger when the battery gets low, turn it off when it gets up to voltage, and manage the cooling fans. The challenge is figuring out what the thresholds should be, especially for temperature.
But, debugging is a chore, because of limited I/O. The chip supports debugWire, so why not use it? Except, I haven't got a debugger.
I have a couple of LeoSticks around, these are actually clones of the Arduino Leonardo, but I find they are really nice ATMega32U4 development boards. Solder on the ISP header, plug the programmer in and bang, you can leave Arduino behind and program it bare.
So this will be my prototype. Other possibilities include the Adafruit Pro Trinket, PJRC Teensy series, probably others too, but this is my first point of call.
My initial code so far just gets VUSB set up, based on the VirtualSerial demo, and I've managed to set up the USART port. I did some testing by shorting RX and TX out, and making the code pass through the USB serial data to the USART and back again, this worked.
My next steps now are to implement the JTAGICE mkII protocol, then to incorporate an implementation using debugWire. This will require some physical-layer adaption, I'm thinking along the lines of a small inverting buffer connected to the '32U4 transmit line driving a BJT.
Collector of the BJT connects to reset pin of the target and the input of a second buffer, which connects to the receive line on the '32U4. That will mean I'll "see" the data I'm transmitting, probably not a bad thing as there's the opportunity to verify it matches.
Long term, it might be worth implementing full JTAG, SPI support and other features, almost approaching BusPirate levels of functionality, but these are stretch goals for now. I'll be happy if I can read memory addresses and single-step.