Thursday, June 21, 2018

A second look at the TM1638 LED & key controller

A few weeks ago I released a small TM1638 library.  My goal was to make the library small and efficient, so I had started with existing libraries which I refactored and optimized.  While looking at the TM1638 datasheet, I thought other libraries might not be following the spec, but since they seemed to work, I followed the same basic framework.  My concern had to do with the clock being inverted from the clock generated by the Arduino shiftOut function.

Figure 8.1 from the datasheet shows the timing for data clocked from the MCU to the TM1638, which the TM1638 samples on the rising edge.  The clock for SPI devices, which shiftOut is intended to control, is low when idle, whereas the TM1638 clock is pulled high when idle with a 10K pull-up. For data that is sent from the TM1638 to the MCU, data is supposed to be sampled after the falling edge of the clock.  After doing some testing at high speed (>16Mhz) that revealed problems reading the buttons pressed, I decided to rewrite the library using open-drain communication, and trying to adhere to the TM1638 specs.

With open-drain communications, the MCU pin is switched to output low (0V) to signal a zero, and switched to input to signal a 1.  When the pin is in input mode, the pull-up resistor on the line brings it high.  The Arduino code to do this is the pinMode function, using OUTPUT to signal low, and INPUT to signal high.  The datasheet specifies a maximum clock rate of 1Mhz, but that appears to be more related to the signal rise time with a 10K pullup than a limit of the speed of it's internal shift registers.  For example I measured a rise time from 0V to 3V of 500ns with my scope:


To account for possibly slower rise times when using longer and therefore higher capacitance wires, before attempting to bring the clock line low, I read it first to ensure it is high.  If is not, the code waits until the line reads high.  As an aside, you may notice from the scope shot, the lines are rather noisy.  I experimented with changing the decoupling capacitors on the board, but it had little effect on the noise.  I also noticed a lot of ringing due to undershoot when the clock, data, or strobe lines are brought low:

I found that adding a series resistor of between 100 and 150 Ohms eliminates the ringing with minimal impact on the fall time.

The updated TM1638 library can be found in my nerdralph github repo:
https://github.com/nerdralph/nerdralph/tree/master/TM1638NR

Friday, June 15, 2018

Hacking LCD bias networks


Those of you who follow my blog know that I like to tinker with character LCD displays.  Until recently, that tinkering was focused on the software side.  What started me down the road to hacking the hardware was an attempt to reduce the power consumption of the displays.  The photo above shows the modified resistor network used to provide the bias voltages used by the LCD controller.  One end of the resistor ladder is connected to Vcc, and the other is connected to the bias/constrast pin (#3).  My modules came with 5 resistors of 2.2K Ohms each for a total of 11K.  Powered from 5V, the bias network will use about 450uA even when the LCD display is off.  When I changed the middle resistor in the ladder to 15K, the display stopped working, so I started investigating how the voltages are used to drive the LCD.

I remembered coming across an AVR application note on driving LCD displays, which helped explain some of the basic theory.  I also read a Cypress application note on driving LCD displays.  What I lacked was a full description of how the HD44780 drives the LCD, as the manual only describes the waveform for the common lines and not the segment lines.  From the application notes, I knew that the difference between the segment and common lines would be around the LCD threshold voltage.  The first information I found indicated a threshold voltage of over 2V, but modifying the bias resistors for a 2V threshold voltage resulted in a blank display.  Further investigation revealed the threshold voltage to be much lower, close to 1V.  Therefore the segment waveform looks something like the green line in the following diagram:

With the stock 2.2K resistor ladder, my modules gave the best contrast with 0.3-0.4V on the contrast pin.  With Vcc at about 5.05V, that means the difference between each step in the bias ladder was 0.94V.  To confirm the theory, I calculated the resistance values needed to maintain 1.2V between V2 and V3, and ~0.95V between the rest, for a total of 5V.  This means the middle resistor in the ladder needs to be 1.2/0.95 = 1.26 x the resistance of the rest.  Using some 7.5K 0805 resistors I have on hand meant I needed something around 9.5K for the middle resistor.  The next size up I have in chip resistors is 15K, which was too much, but I could re-use one of the original 2.2K resistors in series with a 7.5K to get a close-enough 9.7K.  After the mod, I plugged it into a breadboard, and it worked:

With additional hacking to the bias network, it should even be possible to get the modules to work at 3.3V.  That would require flipping V2 and V3, so that V3 > V2, and V3-V4 = ~1V.  And that likely requires removing the resistor ladder, and adding an external ladder with taps going back to the PCB pads on the LCD module.  That's a project for another day.

Wednesday, June 6, 2018

Fast 1-wire shift register control


One-wire shift register control systems are an old idea, with the benefit of saving an IO pin at the cost of usually much slower speed than standard SPI.  I'm a bit of a speed nut, so I decided to see how fast I could make a 1-wire shift system.

The maximum speed of 1-wire shift control systems is limited by the charge time of the resistor-capacitor network used.  The well-known RC constant is the resistance in Ohms times the capacitance in Farads, giving the time in seconds to reach 63.2% charge or discharge.  To determine the discharge of a capacitor at an arbitrary time, look at a graph for (1/e)^x:

In a 1-wire shift system, the RC network must discharge less than 50% in order to transmit a 1, and it must discharge more than 50% in order to transmit a 0.  That 50% threshold is 0.7*R*C.  The hysteresis for the shift register and the system error margin will determine how far from 50% those thresholds must be, and therefore the difference between the low times for transmitting a 1 or 0 bit.  The SN74HC595 datasheet indicates a typical margin of about 0.05*Vcc, so an input high must be more than 0.55*Vcc, and input low must be less than 0.45*Vcc.  After writing some prototype code, and a bunch of math, I settled on an order of magnitude difference between the two.  That means in an ideal setup, a transmitted one discharges the RC network by 16.5%, and a zero discharges the network by 83.5%.  That gives a rather comfortable margin of error, and it does not entail significant speed compromises.

Half of the timing considerations in previous 1-wire shift systems is the discharge time, and the other half is the charge time.  That is because after transmitting a bit, the RC network needs to charge back up close to the high value.  In order to eliminate the charge delay, I simply added a diode to the RC network as shown in the schematic.  If you are thinking I forgot the "C" of the RC network, you are mistaken.  Using the knowledge I gained in Parasitic capacitance of AVR MCU pins and Using a 74HC595 as a 74HC164 shift register, I saved a component in my design by using parasitic capacitance in the circuit.

I'm using a silicon diode that has a capacitance of 4pF.  The total capacitance including the 74HC595, the diode, and the resistor on a breadboard is about 13pF.  A permanent circuit with the components soldered on a PCB would likely be around 10pF.  The circuit is designed for AVRs running at 8-16Mhz, so the shortest discharge period for a transmitted zero would be 10 cycles at 16Mhz, or 625ns.  With R*C = 330ns, a 625ns discharge would be 1.9*RC, and the discharge fraction would be 1/e^1.9, or 0.1496.  The discharge time for a transmitted one would be 62.5ns, and the fraction would be 1/e^0.189, or 0.8278.  Considering the diode forward voltage drop keeps the circuit from instantly charging to 100%, the optimal resistor value when running at 16Mhz would be close to 47K Ohm.  In my testing with a tiny13 running at 9.3Mhz on 3.3V, the circuit worked with as little as 12K Ohm and as much as 110K Ohm.  The "sweet spot" was around 36K Ohm, hence my use of 33K in the schematic above.

For debugging with my scope, I needed to count the probe capacitance of ~12pF, which gives a total capacitance of 25pF.  Here's a screen shot using a 22K Ohm resistor, transmitting 0xAA:


I've posted example code on github.  It uses the shiftOne function for software PWM, creating a LED fade effect on all 8 outputs of the shift register.  I tested it with MicroCore, and the shiftOne function can be copied verbatim and used with avr-gcc.  Since it uses direct port access instead of Arduino's slow digitalWrite, the references to PORTB & PINB will need to be changed in order to use a pin on a different port.

Sunday, June 3, 2018

Writing small and simple Arduino code


Although Arduino isn't my primary development platform, but I have still used it many times over the past few years.  The intent of Arduino prioritized ease-of-use over efficiency, so when experienced software developers work with it, some degree of holding your nose may be necessary.  Lately I've been making contributions to MicroCore, and therefore the Arduino IDE and libraries.  My most recent impulse buy on Aliexpress is a 7-segment LED & pushbutton module using a TM1638 controller that sells for $1.50, so I decided to test it out using MicroCore.

I found a few existing Arduino libraries for the TM1638, all of which were rather large.  The smallest one I found is TM1638lite, which still uses over half the 1KB flash in a tiny13 for anything more than a minimal use of the library.  At first I considered improving the library, but soon decided the best course would be a full rewrite.

The first problem with the library is a common one since it follows the Arduino library example.  That problem is creating an instance of a class when the use case is that only one instance of the class will be created.  By only supporting a single attached LED&Key module, I can make the library both smaller and simpler.  It also solves another problem common to classes that take pin numbers in their constructor.  It is very easy to mix up the pin numbers.  Take the following example:

TM1638lite tm(4, 7, 8);

Without looking at the library source or documentation, you might think data is pin 4, clock is pin 7 and strobe is pin 8.  Named parameters would solve that problem, but the issue of unnecessarily large code size would remain.  I decided to use a static class in a single header file, which minimizes code size and still maintains type safety.  Writing "const byte TM1638NR::STROBE = 4;" makes it obvious that strobe is pin 4.  With the default Arduino compiler optimization setting, no space is used to sore the STROBE, CLOCK, and DATA variables; they are compiled into the resulting code.

Another improvement I made was related to displaying characters on the 7-segment display.  TM1638lite uses a 128-byte array  to map the segments to light up for a given ASCII character.  Since 7-segment displays can't display characters like "K" and "W", or distinguish "0" from "O", I limited the character set to hex digits 0-9 and "AbCdEF".  The second improvement I made was to store the array in flash (PROGMEM).

I made some additional improvements by code refactoring, and I added the ability to set the display brightness with an optional parameter to the reset() method.  With the final version of the library, the buttons example compiles on the tiny13 to only 270 bytes of code.  You can find the library in my github repo:
https://github.com/nerdralph/nerdralph/tree/master/TM1638NR

Thursday, May 24, 2018

Picoboot Adruino with autobaud


Since the v1 release of picobootSTK500, I've been able to test it on many different Arduino compatible boards.  The biggest problem I've had relates to the various baud rates used by different bootloaders.  Optiboot, which is used on the Uno, uses 115.2kbps, while the Pro Mini m328 uses 57.6kbps.  The default baud rate for the Pro Mini m168 is even lower at 19.2kbps.  While modifying the boards.txt file is not difficult, it introduced unexpected problems.  For testing purposes I keep a couple boards with a stock bootloader, and they won't work unless I change the baud rate back to the default.  I also keep a couple versions of the Arduino IDE for compatibility tests, which adds to the confusion of changing boards.txt files.  Having had some time to think about the best solution, I decided to add automatic baud rate detection to picoboot.

From reading the avrdude source, I knew that it starts communication with the target by sendingthe GET_SYNC command '0' at least 3 times.  '0' is ASCII character 48, and when sent over a serial UART connection, the bitstream including start and stop bits is 0 00001100 1.  With 5 assembler instructions, I can count the low duration of the frame:
1:  sbic UART_PIN, 0 ; wait for start bit
    rjmp 1b
1:  adiw XL, 1 ; count low time
    sbis UART_PIN, 0
    rjmp 1b

The counting loop takes 5 cycles, and since there are 5 low bits, the final counter value is the number of cycles per bit.  Dividing that number by 8 and then subtracting one gives the AVR USART UBRR value in double-speed (U2X) mode.

The Arduino bootloader protocol is almost identical to the stk500 protocol, and so it is relatively easy to write a bootloader that is compatible with both.  For this version I decided to eschew stk500 compatibility in order to save a few bytes of code.  Because of that, and to make it more obvious that it is an Arduino-compatible bootloader, I renamed it to picobootArduino.

With picobootSTK500 v1, a LED on PB5 (Arduino pin 13) was used to indicate that the bootloader was waiting for communication from the host.  In order to make it simpler to use with a bare ATmega168/328 chip on a breadboard, while the bootloader is waiting it brings the Tx pin (PD1) low, which will light up the Rx LED on the connected USB-TTL converter.  As with v1, resetting the AVR will cause it to toggle between the bootloader and the loaded app.  This means it works easily with TTL adapters that do not have a DTR pin for the Arduino auto-reset feature, as well as simplifying it's use with a bare breadboarded AVR.

Despite adding the extra code for the autobaud function, the bootloader is still less than 256 bytes.  In my testing with a 16Mhz Pro Mini clone, the bootloader worked reliably with baud rates from 19,200 to 115,200.  The code and pre-built versions for ATmega328 & ATmega168 are available from my github repo.
https://github.com/nerdralph/picoboot/tree/master/arduino

I also plan to test the bootloader on the mega88 and mega8, where a small bootloader is more beneficial given their limited code size.

Tuesday, May 15, 2018

PiggyFuse HVSP AVR fuse programmer


Although I've been working with AVR MCUs for a number of years now, I had never made a high voltage programmer.  I've seen some HVSP fuse resetter projects I liked, but I don't have a tiny2313.  I think I was also hesitant to hook up 12V to an AVR, since I had fried my first ATMega328 Pro Mini by accidentally connecting a 12V source to VCC.  However, if you want to be an expert AVR hacker, you'll have to tackle high-voltage programming.  Harking back to my Piggy-Prog project, I realized I could do something similar for a fuse resetter, which would simplify the wiring and reduce the parts count.

I considered using a charge pump to provide 12V, like some other HVSP projects do, but adding at least 3 diodes and capacitors would more than double the parts count.  I also realized that most AVR hackers probably have a 12V power source available.  Old ATX power supplies have 12V on the 3.5" floppy connector, which 0.1" pin headers easily plug into.  Old DSL modems and home routers often run from a 12V DC supply.  I decided to use a 14.4V tool battery with a small switching converter.  I even thought of using one of my TL431s, but hooking up a few alligator clips to the switching converter was quicker.

Instead of just copying another program verbatim, I decided to implement the core of the programming algorithm myself.  The AVR datasheets list two algorithms, though all of the HVSP programs I could find followed the first algorithm and not the alternative one.  Both algorithms are somewhat obtuse, and even seem to contradict the datasheet specifications that state the minimum latching time for Prog_enable is only 100ns.

After debugging with my oscilloscope on a tiny13 and a tiny85, I realized that the two parts have different ways of entering HVSP mode.  The tiny13 will enter programming mode when it powers up and finds 12V on the reset pin, while the tiny85 requires the reset pin to be at 0V on power-up before applying 12V.  Although the datasheet doesn't explicitly state it, the target drives SDO high when it has entered HSVP mode and is ready for commands.  In the case of the tiny85, that happens about 300us after applying 12V to the reset pin.  However with the tiny13, that happens much sooner, around 10us.  This means the datasheet's recommendation to hold SDO low for at least 10us 12V has been applied is not only wrong, it's potentially damaging.  During my experimenting, I observed my tiny13 attempting to drive SDO high while the programmer was still holding it low.  That caused VCC to drop from 5V to 4V, likely approaching the 40mA maximum I/O current maximum.  And since the datasheet specifies a minimum VCC of 4.5V for HSVP, the droop to 4V could cause programming errors.  In the scope image, the yellow line is VCC, and the blue line is SDO.

Instead of waiting 300us after applying 12V to send any commands, I considered just waiting for SDO to go high.  While this would work fine for the ATtiny13 and ATtiny85, it's possible some other parts drive SDO high before they are ready to accept commands.  Therefore I decided to stick with the 300us wait.  To avoid the contention on SDO shown in the scope image above, I switch SDO to input immediately after applying 12V.  Since it is grounded up to that point removing any charge on the pin, it's not going to float high once it is switched to input.

Another source of potential problems with other HSVP projects is the size of the resistor on the 12V reset pullup.  I measured as much as 1mA of current through reset pin on a tiny13 when 12V was applied, so using a 1K resistor risks having the voltage drop below the 11.5V minimum required for programming.
I recommend using around 470 Ohms, and use a 12.5V supply if possible.

Putting it all together

As shown in the very first photo, I used a mini-breadboard with the 8-pin ATtiny positioned so that the Pro Mini will plug in with its raw input at the far right.  In the photo I have extra wires connected for debugging.  The only required ones are as follows:
12V supply to RAW on the Pro Mini
GND supply to GND on the Pro Mini
Pullup resistor from 12V supply to ATtiny reset (pin 1)
NPN collector to ATtiny reset
NPN base to Pro Mini pin 11
NPN base to Pro Mini pin 12 & ATtiny GND
ATtiny VCC (pin 8) to Pro Mini pin 6

The Pro Mini must be a 5V version.  The optional resistor from the Pro Mini pin 6 lights the green LED when the Pro Mini has successfully recognized a target ATtiny.  Although the program outputs logs via the serial UART at 57.6kbps, it uses the on-board pin 13 LED to allow for stand-alone operation.  Failure is a long flash for a second followed by 2 seconds off.  Success is one or two short flashes.  One flash is for my preferred debug fuse settings with 1.8V BOD and DWEN along with RSTDISBL.  Two flashes is for my "product" fuse settings with RSTDISBL.  The program will alternate between debug and product fuses each time the Pro Mini is reset.

The code is in my github repo:

And finally, the money shot:

Saturday, April 21, 2018

Debugging debugWire


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.