I’m planning to build a EEVblog-ish constant dummy load for battery and power supply testing. Dave in his build used a LCD voltmeter for the display. In the senseless pursuit of difference, I tried other display solutions including character LCD, graphics LCD, OLED, TFT, AMOLED, IPS, which resulted in several previous blogs but nothing ends up to be satisfactory. Along with the complexity grows exponentially from one solution to another, I slowly start to feel the importance of KISS concept, keep it simple stupid. All I need is just a number display, nothing fancy, nothing pricey, nothing takes my attention away from the analog circuitry. I decided to go back to the basic 7-segment LED display. “Simplicity is the ultimate sophistication”, how elegant it is!
I ordered several LED voltmeters from a taobao seller. It costs RMB16 each, about US$2.6 excluding shipping. The original design was for battery measurement on scooters. The advertised accuracy is 0.2%, which turns on my bullshit detector immediately. So the first thing I did after receiving it, was “don’t turn it on, tear it apart”. The manufacture did not scratch off the markings so the schematic can be easily traced out.
The heart of the voltmeter is “STM8S003F3P6”, a 8-bit MCU with 16MHz clock and 8K flash by STMicroelectronics. Datasheet shows this MCU has built-in 10-bit ADC. Driving 4 digits 10000 counts with 10-bit ADC is definitely overrating. While somebody may argue that ADC resolution can be increased using oversampling, without proper noise injection circuitry I cannot confirm it actually works. Furthermore the MCU does not have a dedicated ADC reference input. Supply voltage is used as reference. As the display needs to scan each digit continuously, supply fluctuation is inevitable. How to maintain ADC accuracy with changing reference? The original designer may have considered this problem initially. He/She added D9 (TL431 2.5V reference) and connected its output to analog input AIN6. So before measuring the input voltage at AIN5, 2.5V at AIN6 can be measured and then AIN5 calculated relatively. However D9 was not populated on the board I receive.
I was in the verge of desolder the LED and suddenly an idea bumped into me: why not reprogram this module and use it as a sole display? By doing so I can offload the LED driving task to the STM8. The original designer has reserved footprint for a 6-pin connector with SWIM programming/debugging interface and TX/RX serial port, reprogram should not be too difficult. A second thought is that it would be even better if I can emulate a popular serial LED module so that all existing codes can be used. The “Adafruit 4-digit 7-segment LED backpack” is soon identified. Ladyada did some remarkable work to develop Arduino library and wrote a tutorial for it. Make something compatible with her work is my honor.
However the Adafruit LED backpack is using I2C interface. The hardware I2C pins on the voltmeter is already occupied for driving LED, which means I need to bit banging I2C slave protocol on GPIO pins. There are many online resources about I2C master emulation, but I2C slave emulation is very limited. The best one I found is an article published by TI “Software I2C Slave Using the MSP430“. It depicts an entire state-driven process of interpreting I2C signals, together with some assembly code for MSP430. To help understanding I made a flowchart, and rewrote the library using C code. As I’m not planning to explain the detail of the process here, all codes and documentation can be found at https://github.com/baoshi/I2C-LED
Some notes to potential users:
- The code is compiled using “IAR Embedded Workbench for STMicroelectronics STM8”. The “KickStarter” edition is free (but needs registration) and limited to 8K code size, just nice for STM8S003F3.
- The library is written in C. The efficiency is not as good as assembly code. As a result, at full 16Mhz clock and best optimization, I’m only able to archive maximum I2C clock of 63KHz, conservatively I use 50KHz clock during testing.
- I tried the COSMIC compiler but the efficiency seems to be worse.
- To change I2C clock in Arduino environment, edit TWI_FREQ variable in <arduino>librariesWireutilitytwi.h, or follow the great article by Nick Gammon.
- STM8s does not support per-pin interrupt configuration, which is really pain-in-the-xxxx. Simulate the interrupt mode cost a lot of CPU time and hence is directly responsible for the slow I2C clock support. Migrate to a better MCU platform should give better result. But if you can change a MCU, why not use hardware I2C directly?
- I2C reading is not implemented. The LED display is write-only.
- Adafruit’s LED backpack uses Holtek HT16K33. All HT16K33 display modes are emulated, but not keyboard scan functions. Anyway Adafruit’s LED backpack does not have keyboard interface either.
- Averaging current consumption (at 3.3V) is about 2.6mA, which is larger than HT16K33’s 1-2mA.
- Developing and debugging this library is not possible without Bus Pirate’s 2Wire mode. Using 2Wire mode I can clock each bit individually so as to trace the I2C state.
- Other than lower cost, there is nothing better than the Adafruit’s LED backpack. I did it because I can.
- Hardware modification is shown in the picture below. Only the MCU, two resistor network and some bypass capacitors are needed.
All source code, schematic (in KiCad format), documents are available at https://github.com/baoshi/I2C-LED