Saturday, April 21, 2018
Many modern AVRs have an on-chip one-wire debugger called debugWire that uses the RESET pin when the DWEN fuse is programmed. The AVR manuals provide no details on the protocol, and the physical layer description is rather terse: "a wire-AND (open-drain) bi-directional I/O pin with pull-up enabled". While much of the protocol has been reverse-engineered, my initial experiments with debugWIRE on an ATtiny13 were unreliable. Suspecting possible issues at the physical layer, I got out my scope to do some measurements.
I started with a single diode as recommended by David Brown. I used a terminal program to send a break to the AVR, which responds with 0x55 about 150us after the break. As can be seen in the scope image above, the rise time can be rather slow, especially with Schottky diodes since they have much higher capacitance than standard diodes like a 1N4148. Instead of a diode, Matti Virkkunen recommends a 1K resistor for debugWIRE. For UPDI, which looks like an updated version of debugWire, a 4.7K resistor is recommended. I ended up doing a number of tests with different resistor values, as well as tests with a few transistor-based circuits. While the best results were with a 2N3904 and a 47K pull-up to Vcc on the base, I achieved quite satisfactory results with a 1.4K resistor:
The Tx low signal from both the Pl2303 and the AVR are slightly below 500mV, which both sides consistently detect as a logic level 0. With 3.5Vcc, I found that levels above 700mV were not consistently detected as 0. As can be seen from the scope image, the signal rise time is excellent. The Tx low from the Pl2303 is slightly lower than the Tx low from the AVR, so a 1.5K resistor would likely be optimal instead of 1.4K.
You might notice in the scope image that there are two zero frames before the 0x55 response from the AVR. The first is a short break sent to the AVR, and the second is the break that the AVR sends before the 0x55. While some define a break as a low signal that lasts at least two frame times, the break sent by the AVR is 10 bit-times. Since debugWire uses 81N, a transmitted zero will be low for 8 bits plus the start bit before going high for the stop bit. That means the longest valid frame will be low for 9 bit-times, and anything more than 10 bit-times low can be assumed to be a break. Another thing I discovered was that the AVR does not require a break to enter dW mode after power-up. A zero frame (low for 9 bit-times) is more than enough to activate dW mode, stopping the execution on the target. Once in dW mode, a subsequent zero frame will cause the target to continue running, while continuing to wait for additional commands.
My results with a 1.4K resistor are specific to the ATtiny13 + Pl2303 combination I am using. A different USB-TTL dongle such as a CP2102 or CH340G could have different Tx output impedance, so a different resistor value may be better. A more universal method would be to use the following basic transistor circuit:
One caveat to be aware of when experimenting with debugWIRE is that depending on your OS and drivers, you may not be able to use custom baud rates. For example under Windows 7, I could not get my PL2303 adapters to use a custom baud rate. Under Linux, after confirming from the driver source that custom baud rates are supported, I was eventually able to set the port to the ~71kbps baud rate I needed to communicate with my tiny13. That adventure is probably worthy of another blog post. For a sample of what I'll be discussing, you can look at my initial attempt at a utility to detect the dW baud rate of a target AVR.
Tuesday, April 3, 2018
For micro-controller projects, a TTL serial UART has a multitude of uses. At a cost that is often under $1, it's not hard to justify having a few of them on hand. I happen to have several
The first and probably simplest use is a breadboard power supply. Most USB ports will provide at least 0.5A of 5V power, and the 3.3V regulator built into the UART chip can supply around 250mA. With a couple of my dongles, I used a pair of pliers to straighten the header pins in order to plug them easily into a breadboard.
I've previously written about how to make an AVR programmer, although now that USBasp clones are widely available for under $2, there is little reason to go through the trouble. Speaking of the USBasp, they can also be used along with TTL USB dongle to do 2.4Msps 2-channel digital capture.
Since TTL dongles usually have Rx and Tx LEDs, they can be used as simple indicator lights. To show that a shell script has finished running, just add:
$ cat < /dev/zero > /dev/ttyUSB0
to the end of the script. The continuous stream of zeros will mean the Tx LED stays brightly illuminated. To cause the Tx LED to light up or flash for a specific period of time, set the baud rate, then send a specific number of characters:
$ stty -F /dev/ttyUSB0 300
$ echo -n '@@@@@@@@' > /dev/ttyUSB0
Adding a start and a stop bit to 8-bit characters means a total of 10 bits transmitted per characters, so sending 8 characters to the port will take 80 bit-times, which at 300 baud will take 267ms.
It's also possible to generate a clock signal using a UART. The ASCII code for the letter 'U' is 0x55, which is 01010101 in 8-bit binary. A UART transmits the least significant bit first, so after adding the start bit (0) and stop bit (1), the output is a continuous stream of alternating ones and zeros. Simply setting the port to a high baud rate and echoing a stream of Us will generate a clock signal of half the baud rate. Depending on OS and driver overhead, it may not be possible to pump out a continuous stream of data sending one character at a time from the shell. Therefore I created a small program in C that will send data in 1KB blocks. Using this program with a 3mbps baud rate I was able to create a 1.5Mhz clock signal.
If you have server that you remote monitor, you can use TTL dongles to reset a server. This works best with paired servers, where one server can reset the other. The RESET pin on a standard PC motherboard works by being pulled to ground, so the wiring is very basic:
RESET <---> TxD
GND <---> GND
When one server hangs, log into the other, and send a break (extended logic level 0) to the serial port. That will pull RESET on the hung server low, causing it to reboot.