Friday, December 25, 2015

Bitcoin mining hardware for Christmas?

Since I was a teenager, I've been interested in cryptography.  I've also been interested in business and finance, and started my first business selling computer accessories in high school.  Bitcoin, as a crypto-currency, is something that certainly intrigues me.  Recently I decided to do some research on bitcoin mining.  It would seem there may still be opportunities for individuals to make money mining bitcoins.

The image above is of a Antminer S7, which today (Dec 25) sells for 2.91BTC.  One bitcoin is trading for about US$455, making the price of the miner about US$1325.  If you are willing to wait for the Jan 20th production batch, the price drops to 2.4BTC.  The only other bitcoin miner on the market at this time is the Avalon 6, which sells for 3.2BTC.  Rated at 3.65 trillion hashes per second (TH/s), the Avalon is much less powerful than the Antminer at 4.73 TH/s.

So can you make money with bitcoin mining hardware?  Probably, but it depends on where you live, or more specifically, what you pay for electricity.  It also depends on fluctuations in the bitcoin market including the price of bitcoin.  I'll explain my guess on where the market will go, and do the math on whether buying a S7 would be profitable where I live in Eastern Canada.

The first thing to work out is the landed cost of the S7.  Allowing about $50 for shipping and adding local taxes that would be due on import brings the cost up to $1575.  Adding three 500W power supplies (cheaper than a single 1500W supply) brings the total to $1725.  From there I could create a complex spreadsheet to calculate predicted mining difficulty increases and power costs, but it is a lot easier to use an online mining mining calculator.

The first field in the form is the difficulty increment, which I'll leave to the end as it is a bit complicated.  While the average cost of electricity in North America is close to 10c/kWh, where I live it is about 12c US, so 0.12 goes in the electricity price box.  I think the Antpool fees are less than 2%, but I left in the default 2% just to be safe.  The hash rate for the S7 is 4730 GH/s, and my previously-calculated hardware price was $1725.  Power consumption is around 1250W.  I expect it would take about a week from the time payment is made until the S7 would be up and running, so I put in 7 days for the start date.

Now for that complicated difficulty increment.  Although the form defaults to 20%, that is much higher than the average over the past year.  .To estimate the expected difficulty increase with a constant price, I looked at mid-February and mid-October 2015, when the price was about US$250/BTC.  The difficulty increase during that time was 30%, and averaged over the 17 difficulty adjustment intervals, that works out to 1.56%.  I rounded that up to 2%, in case the increase in difficulty in the future is slightly faster.  Based on those numbers, the S7 would start generating a profit in November of 2016.

Another complication to estimating mining profitability is the block reward halving that will likely happen by July 2016.  At that time the reward for mining will drop from 25BTC per block to 12.5BTC per block.  I suspect that will impact the economics of mining so that it would take about a year to start generating a profit instead of  about 10 months.

My conclusion from all this: If you bought yourself a bitcoin miner for Christmas, I think the odds are better than 50/50 that you'll make money on your investmentbet within 2 years.  Personally, I'd put my money in a low-risk investment that is likely to return 5-7%, or at least wait and see if the economics of bitcoin mining significantly changes.

2015/12/31 update

I just noticed the specs on the recent batches of S7s has changed.  The original specs were 4.86 TH/s using 162 chips (54 per board) running at 600Mhz and drawing 1210W with a 93% efficient PSU.  The chips were configured in 3 chains of 18 chips per board, running at 0.667V ea (12V/18).  The newest S7s now have 135 chips (45 per board) running at 700Mhz and drawing 1293W, generating 4.73TH/s.  This likely means there are 3 chains of 15 chips per board, running at 0.8V.  The higher voltage allows for a higher clock rate, but at the cost of power efficiency, going from 249W/TH to 273W/TH.

I suspect two reasons for the change.  The BM1385 chips (pdf datasheet) were not always stable running at 600Mhz, so Bitmain changed the default to 575Mhz on batch 2 and batch 4 (for 4.66 TH/s).  Running at a higher voltage allows the chips to run at a higher clock rate.  The second reason is probably cost reduction; they now can produce 20% more S7s than before for a given number of ASICs.  For the short term I think that is a good idea since the S7 is still the most power-efficient miner available.  Before more power-efficient equipment using 16nm mining ASICs starts shipping, it may make sense for Bitmain to make another version of the S7 with 4 chains of 20 chips per board running at 0.6V and 400Mhz.  The power consumption would be about 1000W for 4.8TH/s (208W/TH), and they would be more reliable running at lower voltages and cooler temperatures.

Sunday, December 20, 2015

$6 AD584KH voltage reference


After buying a couple cheap REF5050s that turned out to be no good, I decided to buy a $6 AD584KH voltage reference.  A new AD584 costs about $20, so getting a whole module for $6 may seem like a scam at first.  However a close look at the photos from the vendor, and the photo of the one I received reveal that the AD584KH used on these modules is not new.  The one I received has a date code of 9703, so these are used parts, likely de-soldered from old equipment.  For voltage references, used parts are usually better than new, because their stability improves after they are aged for 250-1000 hours.  References that are hermetically sealed in a metal can like the AD584 are even more stable than the REF5050 since changes in humidity do not reach the IC.  As can be seen from the calibration sticker on the anti-static bag, the output voltages were measured with a 6-digit bench meter.

The reference was useful for testing and calibrating my 2 auto-ranging meters.  I adjusted my old meter so that it now reads about 0.025% high on the 0-4V range.  My new meter doesn't have any calibration pots, but I was able to determine that it reads about 0.11% low on the 0-6V range.  I've also concluded that my Wing Shing TL431A parts are 2.5V, not 2.495.  This is not too surprising, as many other 431 parts are 2.5V, like the LT1431 and the CJ431.  I also checked a cheap 3-digit voltage meter, and found it was reading low by about 0.5%; not bad considering it costs not much more than a dollar.

Spending $6 to check a $25 meter is probably not something most people will do.  Although I don't need to be able to measure voltages down to the nearest millivolt, the perfectionist in me likes that kind of precision.  And with only occasional use, it is likely to maintain better than 0.01% (100ppm) accuracy for decades to come.

Thursday, December 17, 2015

Lightweight AVR assembler functions

Over the past few years, I've written a few posts about small problems in the way avr-gcc generates code.  These problems are typically poor optimization, and since they don't cause problems in program functionality, they tend not to be fixed.  I believe the biggest opportunity in optimization is in inter-procedure register analysis, which is being worked on in GCC 5, but is unlikely to support 8-bit AVR MCUs.  Therefore I have developed a lightweight ABI for calling assembler functions:

inline void eelog(char logdata)
{
    // no output, x register input, no clobbers
    asm volatile (
    "rcall eelog_\n"
    :
    : "x" (logdata)
    : );
}

Before explaining the above code, it helps to understand the standard avr-gcc register usage.  Functions are free to use registers r18-r27 and r30-31, which means that if the code the calls a function is using any of these registers, they need to be saved before calling the function.   This applies even if the called function doesn't even modify any of the registers, since the compiler doesn't do inter-procedure register analysis.  The following small program and assembler code shows what I mean:

void main(void)
{
    volatile uint8_t* ioreg;
    uint8_t offset = 0x3f;
    eelog(42);
    ioreg = (uint8_t*)0x0020;
    do {
        eelog(*(ioreg + offset));
    } while ( offset-- );
}

00000092 <main>:
  92:   cf 93           push    r28
  94:   8a e2           ldi     r24, 0x2A       ; 42
  96:   f4 df           rcall   .-24            ; 0x80 <eelog>
  98:   cf e3           ldi     r28, 0x3F       ; 63
  9a:   ec 2f           mov     r30, r28
  9c:   f0 e0           ldi     r31, 0x00       ; 0
  9e:   80 a1           ldd     r24, Z+32       ; 0x20
  a0:   ef df           rcall   .-34            ; 0x80 <eelog>
  a2:   c1 50           subi    r28, 0x01       ; 1
  a4:   d0 f7           brcc    .-12            ; 0x9a <main+0x8>
  a6:   cf 91           pop     r28
  a8:   08 95           ret

The compiler uses r28 for the offset counter, since the function being called is not allowed to use r28/r29 (aka the Y register).  Since r30/31 (Z) can be modified (clobbered) by the function, the Z register needs to be initialized each time through the loop (at 0x9a and 0x9c).  The single parameter is passed in r24.

Although I've been programming in AVR assembler for a few years now, it has taken me a while to learn inline asm.  I still prefer writing in plain asm, but inline asm is the only way I've found to call assembler functions from C without having to follow the standard calling convention.  The sample code at the start of this post defines a function named eelog that takes a single parameter passed in r26 (the low byte of the X register).  Using this technique, the compiler can safely use r24 for the offset counter instead of r28:
00000080 <main>:
  80:   aa e2           ldi     r26, 0x2A       ; 42
  82:   08 d0           rcall   .+16            ; 0x94 <eelog_>
  84:   8f e3           ldi     r24, 0x3F       ; 63
  86:   e8 2f           mov     r30, r24
  88:   f0 e0           ldi     r31, 0x00       ; 0
  8a:   a0 a1           ldd     r26, Z+32       ; 0x20
  8c:   03 d0           rcall   .+6             ; 0x94 <eelog_>
  8e:   81 50           subi    r24, 0x01       ; 1
  90:   d0 f7           brcc    .-12            ; 0x86 <main+0x6>
  92:   08 95           ret

A careful reader will notice that more optimal code would use r30 for the offset counter, but even recent versions of avr-gcc aren't that smart.  Given other examples of poor optimization have persisted for several years, it is unlikely avr-gcc will get as good as hand-written asm anytime soon.  Another thing sharp readers may notice is that the volatile keyword in the "asm volatile" statement is superfluous because asm statements that have no output operands are implicitly volatile.  I think it is best to leave it in, in case the function is later changed to have an output operand, or if it is used as a template for another function.

If anyone is wondering why I didn't make a simplified version of the the standard calling convention that just used r24/25, it is because there is no inline asm constraint for r24/r25.  The constraint "w" might get the compiler to use r24/25, but it also could use r26/27 (X), r28/29 (Y) or r30/31 (Z).

Conclusion

Using this technique can significantly reduce register pressure on the compiler, which will reduce code size and increase speed.  Inline asm constraints could also be used for a function that returns multiple values, without having to use a C struct.  Code can be found in my new avrutils repository.


Sunday, December 6, 2015

Piggy-prog release version


Over the past few months I've been making progress with piggy-prog and picoWiring.  Since the beta version of piggy-prog, I've made a couple changes.  The first is a 8Mhz clock on PD6 (Arduino pin 6) using PWM.  That pin connects to physical pin 2 of the AVR 8-pin AVR underneath, which is the clock in (CLKI) pin on the ATtinyx5 and ATtiny13.  This allows programming AVRs that (sometimes accidentally) have the fuse settings for external clock.

The second change I've made is to reduce the SPI SCK timing to 128kHz to allow for programming AVRs with the CKDIV8 fuse set (factory default).  According to the datasheet specifications a 250kHz clock should work, but a few ATtiny13s I tested required a 128kHz clock to work when the CKDIV8 fuse was set.

To build piggy-prog you can download piggy-prog.ino and upload it to a Pro Mini using the Arduino or Wiring IDE.  If you have avr-gcc and avrdude installed, you can download the release, and run "make avrdude" to compile and flash the code to the Pro Mini.  Plug the Pro Mini into a breadboard so pins 9 and 10 are 1 row below pins 4 and 5 of the ATtiny, and pins 5 and A0 are in the same row as pins 1 (RST) and 8 (VCC) of the ATtiny.  Power will be supplied by the Pro Mini, so no jumper wires are needed.

In addition to working with the ATtinyx5 and ATtiny13 MCUs, it should also work with the ATtiny2313.  Even though pin 4 is not GND on the tiny2313, the input clamping diodes will make the ground connection.  The diode voltage drop of about 0.7V will mean the power to the tiny2313 will be about 4.3V, but that is still well within the operating range of the chip.

For the next version of piggy-prog, I plan to add detection of the position of the attiny, so that if it is one row up or down on the breadboard, piggy-prog will indicate that the chip needs to be moved.  This detection will be done by putting a weak pull-up voltage on one of the pins and then detecting if the other pins go high.  Since the RST pin does not have a VCC clamping diode, it will also be possible to safely detect if  the chip is in backwards (where VCC and GND are swapped).  For version 1, pay attention to the position of pin 1 (with a round dot close to it), as it is possible (in theory) to damage both the Pro Mini and the target MCU if you put the ATtiny in backwards.  I have accidentally reversed VCC and GND on AVRs without any apparent damage, but YMMV.

Wednesday, December 2, 2015

NiMH battery discharge tests


A couple weeks ago my wife got a few ornamental Christmas trees lit by RGB LEDs.  Each of them takes 3 AA batteries,  I bought a pack of Amazon Basics batteries since they were the best deal I could find for good quality batteries, since they are OEM eneloop batteries.  The ones I received were manufactured 201412, which suggests they are 4th generation eneloops.  I used 3 of the Amazon Basics batteries in 2 of the trees, and 3 Rayovacs I had lying around for the third tree.  After the batteries ran out, I tested their voltages.  The Amazons were consistently in the 1V range, but one of the Rayovacs was completely drained, reading 0 volts, even after the 2nd fill charge/discharge cycle.  The three Rayovacs looked similar, but the 0V battery was a bit different than the other two.  After looking through all of my rechargeable batteries, I realized I had 3 different kinds; the "0V" battery shown in the top of the picture above, one labeled "pre-charged", in the lower part of the photo above, and a couple labeled "ready-to-use".  None of them had a capacity visible anywhere on the battery.

While I was confident that the capacity of the regular "0V" Rayovac was less than the "ready-to-use", I wanted to get a better idea of the actual capacity of the batteries.  I also have some 1st-generation eneloops "3UTG" I purchased over 5 years ago that I wanted to compare to the new Amazon Basics batteries.  I don't have smart battery charger/tester, but with a bunch of AVR boards around I thought it would only take a couple dozen lines of code to time the battery discharge.  Before I started, I decided to see if somebody had already made a battery discharge project I could use.  It didn't take long to find Denis Hennessy's battery capacity tester.  I'm not a fan of the Arduino core, but if it worked, it didn't matter much if the code isn't pretty.

After reviewing Denis' code, I determined it work work well with a couple small changes.  The AVR board I already had plugged into my breadboard was running at 3.3V, and Denis' code assumes an AVR running at 5.0V for the ADC calculations.  I changed the code to use a "VCC" define.  Even for boards running at 5V, using the exact voltage instead of assuming 5.0 could give more accurate ADC readings.  I also modified the code so the "test complete!" message is output to the serial log, since wasn't hooking up a 1602 LCD (the code works fine without the LCD or DS18B20 attached).  The last change I made was making the serial log output every minute instead of every second.  I connected a 3.7 Ohm (nominal 4 Ohm) 15W speaker in lieu of a power resistor, and single AA battery holder for the battery.

The 3.7 Ohm load means the discharge current will be in the 300-350mA range, which is a bit higher than typical battery-powered electronics projects, but still within manufacturer specs for AA NiMH discharge rates.  After a couple days, I had tested 5 different batteries:

The battery capacity tester calculates joules rather than mAh, so I've done a rough calculation in mAh since that is the typical rating for NiMH batteries.  The regular Rayovac (purple line) was about 1100mAh, with the pre-charged Rayovac (green line) around 1300mAh.  The ready-to-use Rayovac (red line) was around 1350mAh, which matches the capacity specified for the new Rayovac "recharge" batteries.  The 1st-generation eneloop is still doing pretty well at around 1900mAh, and the Amazon Basics give a very respectable 2050mAh.

My updates to the battery capacity tester were merged a couple days ago, so you just need to download the zip from Denis' github repository.  If you are not hooking up the temperature sensor, you can comment out the OneWire and DallasTemperature references.  You will need a copy of the Time library, which is now maintained by Paul Stoffregen.

Tuesday, November 17, 2015

DS18b20 temperature sensor calibration and correction

I recently bought a couple of DS18b20 temperature sensors in stainless steel probes.  Although I prefer to do AVR development using the Atmel AVR toolchain, I decided to test out the temperature sensors using the Arduino IDE.  I found a OneWire library that will use the AVR internal pullup resistor, so all I had to do was solder on some header pins and plug into one of my Pro Mini boards.

I soldered a 2-pin male header on one of the sensors, with the red power and black ground connected for parasitic power mode.  On the 2nd sensor, I connected a 3-pin header so I could use it in powered mode. I flashed NoPullupTester (after modifying it to use pin 2 for the onewire bus) to the Pro Mini, opened the serial monitor at 9600 baud, and started getting readings.  At first I thought there was something wrong, as I was getting readings in the 67-68 range.  Then I realized the output was in Fahrenheit; some Americans still dance to a different tune!  I removed the Fahrenheit conversion from the program, re-flashed the Pro Mini, and started getting results in familiar Celsius units.

I used some ice cubes and water in a coffee mug to check the calibration of the sensors.  The first problem I noticed was that the temperature readings would rise by about 0.2C once the sampling started.

I believe this was due to self-heading.  I reduced the sampling interval to every 10 seconds in order to reduce the change of self-heating.  Even then, my lowest readings were 0.06C, which seemed a bit high.  The datasheet shows a mean error of -0.14C at 0C, and I couldn't get either of my sensors to read below 0.06C.

While it is possible that both of my sensors were off from the mean error by about +0.2C, I suspected my ice/water mixture was a bit warmer than 0C.  Water's maximum density is at 3.8C, so I thought I could be getting some stratification with the water at the bottom of the mug slightly warmer than the water and ice at the top.  I then set out to find the best way to make a 0C temperature reference bath, and hit the jackpot with one of Measurement Canada's lab standards.

From previous experience with Mr. Boston, I knew that chopping ice cubes could ruin a cheap blender.  I didn't have a readily available supply of distilled water either, so I came up with another solution.  I chipped enough frost out of the freezer to fill a blender jug.  The frost is not as hard as ice, so it would be less likely to damage the blender. and since the frost forms from sublimation of water vapor, it should have few impurities.

After about 15 minutes of adding water and blending, I had a bunch of slush about the consistency of sorbet.  I drained the cold water and saved it to pre-chill the temperature probes, and transferred the slush into a dewar's flask.  After another 10 minutes I tested the temperature of the slush:

The other sensor gave similar results stabilizing at -0.19C.  I suspect my ice bath was a bit too cold.  I had added (chlorinated) municipal water to the frost in order to make the slush, which would add impurities.  Those impurities would lower the bath temperature by several millikelvins as noted by Measurement Canada.  I also hadn't maintained a full immersion of all the ice in water while blending, so some of the ice slush may not have come in contact with water long enough to raise it's temperature to 0C.  The results were good enough though; making the perfect 0.00C reference can wait for another day.

Not quite ready to finish with the temperature sensors, after looking at the mean error graph from the datasheet, I realized I could improve the accuracy in software.  The mean error looks like part of a parabolic curve, so by flattening the curve the error would be reduced.  Although a quadratic equation would be ideal, for simplicity I used a linear formula which reduces the mean error to under 0.05C from -25C to +65C:
float correctTemp(float temperature) {
  return temperature + 0.2 - (abs(temperature - 20) * .005);
}

I modified the test program to output the raw and corrected temperature,  As expected, with a raw temperature of 20.0C, the corrected temperature was 20.2C.

While the DS18B20 sensors are cheap and reasonably accurate, they have some drawbacks.  The -55C to +125C temperature range is less than diode temperature sensors that work from -65C to +200C.  The Arduino libary is not very efficient since it converts the native 7.8 bit fixed-point readings into 32-bit float.  It's quite simple to use, and given the low cost of the sensors, I think they are a great solution for remote temperature sensing.

Friday, October 30, 2015

Getting started with Jumpstart Microbox

Richard Man at Imagecraft recently offered a free Jumpstart Microbox to bloggers willing to review it.  The kit includes ST Nucleo board with a STM32F030R8 ARM MCU, an add-on board, the Imagecraft compiler, and a book on embedded C programming.  I've done a lot of 8-bit AVR development, and had intended to test out some of the STM32 MCUs, so I took Richard up on his offer.  The price point is much higher than a $4 Maple Mini clone with stm32duino, but Richard says they're trying to compete with IAR & Keil, not arm-gcc.

Before the hardware arrived I downloaded the IDE, which is based on Code::Blocks.  It includes an annoying license manager (I haven't found a licence manager I didn't hate).  On Windows 7E-64, the IDE identifies itself as Imagecraft 8.12.  Unlike gcc, Imagecraft is not a full C++ compiler.  Once the loads, the project options require you to pick your MCU, which defaults to the STM32F030R8.

For debug/download interface it only supports SWD.  All STM32 chips have a UART bootloader in ROM that allows flashing the chip without a SWD link, using software like stm32flash.  When I pointed this out to Richard he replied saying they would likely add support for it.  The project template includes a main.c with the implementation of putchar for printf to work.  I'll look at the compiler and Jumpstart libraries in more detail in a future post.

The jumpstart tutorial book is very basic.  I've been programming in C and C++ for 25 years, so there was nothing in it I didn't already know.  I didn't like the examples that included preprocessor directives (i.e. #define END 90) and code.  Good coding practice is to put preprocessor defines in a separate file like config.h.  The tutorial doesn't mention fixed-point, which I think should be preferred over floating point in embedded systems.  Richard responded that their compiler doesn't (yet) support fixed point (gcc does), so he hadn't mentioned it in the tutorial.  He said it will eventually be added to the compiler, but no promises as to the release date.

I'll finish this first part of my review with saying that I think, on one hand, that it would be better to have noobs reviewing the Jumpstart Microbox since the target market is not old C programmers like me that live by bash, vi, gcc, and objdump.  On the other hand, noobs aren't going to know about the UART ROM bootloader, so I suppose I make a good (and cheap) beta tester for Imagecraft.

Tuesday, October 27, 2015

Using a 74HC595 as a 74HC164 shift register

The 74595 and 74164 serial-in parallel-out shift registers are a popular way of adding extra outputs to a MCU.  There's lots of good guides online about how to use these shift registers such as this one by Ido Gendel.  I recently found a great deal on a bunch 74595s, and wondered if there is an easy way to modify circuits intended for a '164 to use the '595.  The solution turned out to be as simple as connecting two pins with a resistor.


There's actually a bit more than just connecting two pins together with a resistor, but the other requirements are simple and obvious.  Pin 13 on the '595 is Output Enable, and is active low, so it should be tied to ground.  Pin 10 is the clear pin (like MR on the '164), and should be tied high to keep the shift register from clearing.  As a small aside, when doing this on a breadboard you may want to connect pin 10 to Vcc with a 1K resistor.  That way if you accidentally connect to pin 9 (Qh') instead of pin 10 you won't risk shorting out the chip and damaging it.  I got my batch of '595s for about 2c ea, so I'm not concerned about frying one of them.  The key part of this solution is the resistor between pin 11 (SRCLK) and 12 (RCLK).  RCLK is what updates the 8 outputs, and it needs to be clocked high at least 19ns after SRCLK (Ti datasheet table 7.6).

The resistor, when combined with the parasitic capacitance of the RCLK input forms an RC circuit which delays the clock pulse.  If this delay is at least 19ns, then the outputs will be automatically updated (almost) instantly after data is clocked in, just like it is on the 74164.  The input high level for the '595 is 0.7 times the supply voltage.  The RC time constant is based on the time to charge (or discharge) by 63%, so after adding a bit to go to 70% and a safety margin for slower operation down to 3.3V, R*C should be at least 50ns.  The datasheet says the pin capacitance is typically 3pF, and a maximum of 10pF, so something in the 10-15K Ohm would probably work.  The first resistor I tried was 11.3K, and the scope trace above shows the rise time on RCKL in blue compared to SRCLK in yellow.  The delay to 3.5V is around 250ns, and based on the 200ns delay to reach 3.15V (63%), the total capacitance is about 18pF.  Subtracting the 13pF capacitance of my scope probe leaves a total of 5pF for the pin and resistor parasitic capacitance.  The net 5pF capacitance with a 11.3K would mean RCLK reaching 3.5V around 70ns after SRLK.

With this simple modification you can turn a 74HC595 into a 74HC164, and avoid having to stock your parts bin with both kinds of chips.  Since the RCLK and SRCLK pins are adjacent to one another, a surface mount chip resistor could be soldered between the pins if you want the modification to be permanent.  I did something similar with on-chip decoupling capacitors.  SMD 0805 parts work fine with the 0.1" (2.54mm) DIP spacing, and 0603 will just work with 1.27mm SOP pin spacing.  Although 0402 would be a more ideal fit for 1.27mm SOP, soldering those tiny 0402 parts is not something you'll see me doing!

Friday, October 23, 2015

ATmega328p bandgap voltage reference


All modern AVRs have a bandgap voltage reference that can be used with the ADC instead of AVCC.  On most AVRs it is around 1.1V, and allows for improved ADC resolution when measuring voltages below 1V.  The bandgap is also used as the reference for the internal temperature sensor.  Although the datasheet says the bandgap can vary between 1.0 and 1.2V, I was curious to see how much it actually varied in practice.  I found many other people posting in forums looking for information on real-world bandgap measurements, but couldn't find anyone who tested more than one MCU.  Since I have several ATmega328p MCUs in my electronics collection now, I decided to take some measurements.

I used the technique mentioned on Martin's blog of setting the ADC reference to the internal bandgap reference, turning on ADC, and measuring the voltage at the AREF pin.  Here are my results, taken at an ambient temperature of about 20C, and using my 0.1% calibrated meter.
Pro Mini #1 (qfn): 1.045V
Pro Mini #2 (qfn): 1.058V
Generic Uno (qfp): 1.055V
Nano (qfp): 1.089V

I also measured a Pro Mini 168pa, and found it had a much lower reference voltage of 0.994V.  With the measurements being all much lower than the "typical" values from the datasheet, I wondered if I might be affecting the voltages by measuring them.  Searches for information on the output impedance of the bandgap reference were fruitless, though I did find a stackexchange discussion about the 32KOhm pulldown for the ADC reference.  So to get an idea of the output impedance, I measured the voltage with and without a 1.4KOhm external pulldown.  The AREF voltage dropped by 6mV, so even a meter with 1MOhm input impedance would affect the voltage reading by less than 10uV.

While I only have four samples (five counting Martin's), I would say that typical bandgap reference voltages are a bit lower than 1.1V.  I would also guess that if I had more samples, 9/10 would be within 3% of 1070mV.  If anyone has done their own measurements, please post them in the comments.

I've read a couple forum and blog posts about calibrating the bandgap reference to a known VCC, but haven't seen any code.  With oversampling, it would not be difficult to measure the bandgap within 1mV (0.1% accuracy).  I could use the same technique of using init sections that I did with the internal temperature sensor, so this will likely find it's way on my list of projects to work on.

Tuesday, October 20, 2015

beta picoWiring Arduino compatible library

Before getting into the technical details, I'm going to start with a bit of history of Arduino.  No, I won't be talking the arduinogate dispute over who makes the hardware, it's the software I want to talk about.  From the way Arduino LLC's mouthpiece Marchese Banzi talks, he and his Arduino team came up with all the ideas that now many others have copied.  However the Arduino library was copied (or forked for those who prefer to speak git) from Wiring.  Wiring also took some ideas from MIT's Processing.  Although there have been a lot of changes to the Arduino library over the past four years, staying compatible with pre-1.0 Arduino has meant staying (mostly) compatible with Wiring.

I suspect if I had not mentioned Wiring, most people would mistake the above screenshot as coming from the Arduino IDE.  But it is actually Wiring 1.0.1.  Both Wiring and Arduino add a lot of code bloat and have examples of bad interface design, but I will use them for quick tests.  Between the two of them I prefer Wiring, since after the Arduino fork, Wiring implemented an optimized digitalWrite that is much faster than Arduino.

While it is the rare Arduino library that is well written, when it comes to testing new electronics the convenience of having something that works is worth holding your nose over the poor coding.  Although digitalWrite may not be the best way to abstract IO, it can be implemented efficiently as demonstrated by Wiring.  The same API can be used on the esp8266 with CHERTS or the esp8266 Arduino core.  For command-line development of Arduino code for the AVR Ino is an option, however the lack of optimization is still a problem.  Therefore I have written a library that has very little overhead and is compatible with the Arduino/Wiring API.

My goals were to make the library as small and as fast as possible, while supporting the ATmega168/328 series.  Since it has the fast digitalWrite, I started with some of the code from the Wiring AVR 8-bit core.  I removed the millis() timer, and implemented delay() using _delay_ms.  I modified the SPI class so it is a static class - instance variables unnecessarily take up memory when only one instance of the SPI class is created.  The Serial class took a lot of work to minimize.  Like the SPI class, I removed instance variables to make it a static class AKA singleton.  The AVR UART has a hardware FIFO, so removed the software FIFO the Serial class was using.

I've posted the code, which I still consider beta, to my github account.  I've done some testing with my (beta as well) piggy-prog project.  Both picoWiring and my dmbs fork are git submodules, so I can easily keep piggy-prog synced with bug fixes and improvements to picoWiring.  For piggy-prog, the code size improvement is significant.  Building piggy-prog with the 1.6.3 Arduino IDE results in a program that requires 4.5KB of flash.  Compiled with avr-gcc 4.9.2 with picoWiring, the flash size is just 1772 bytes.

Monday, October 12, 2015

Parasitic capacitance of AVR MCU pins

Parasitic capacitance of MCU pins is not something I've seen discussed much.  Look in the electrical characteristics of an AVR datasheet, and you can find input current leakage, but not parasitic capacitance.  Over a year ago I did some experiments to see how long a floating pin would stay high after switching from output high to input mode.  The result, which is a factor of both leakage and capacitance, was in the tens of seconds.  Now that I have a Rigol 1054Z, I can do some tests to try to specifically measure the input capacitance.


The above screen shot shows the rise time of an unconnected pin on a Pro Mini with the internal 35K pullup enabled.  The Pro Mini has female headers attached (much like the full-size Arduino), and running some simple code that toggled the pullups for PORTB.


The RC time constant means that a capacitor will charge to 63% in R*C seconds.  So a 1 Farad capacitor charging through a 1 Ohm resistor will take 1 second to charge to 63% of the input voltage.  I'm running the Pro Mini at 5V, so 63% of that is 3.15V.  The Pro Mini pin takes ~700ns to reach just over 3V.  Knowing that the internal pullup on the AVR is 35K, we start with 35K * C = 700ns, and solve for C to come up with 20pF.  I've read that 10pF is a typical value for IC pins, so something probably isn't right with my 20pF measurement.  After a bit more searching through datasheets I found table 29-19 of the ATmega328p datasheet, in the TWI section, which says 10pF max.

I tested an Arduino Nano compatible board with no headers attached that used a QFP ATmega328p instead of the QFN chip used on the Pro Mini.  The measurements equate to a parasitic capacitance of about 25pF.  A second Pro Mini with male headers measured at 20pF, indistinguishable from the first Pro Mini.

After a bit of pondering, I realized my scope probe is going to have some capacitance, and after digging out the spec sheet for the Rigol RP2200 probes, I found a specification of 17 +- 5pF.  So it seems most of the 20pF capacitance I measured is from my scope probes, but I wasn't sure how much.  My next idea was to test an 11K resistor connected to a pin on the Pro mini which cycled between output high and output low.  Here's the screen shot from the end of the resistor that was left floating:


Measuring the 63% rise time and solving for C this time gives 14pF for the capacitance.  This means at least 6pF of the previous 20pF measurement is the parasitc capacitance of the AVR pin.  The measured 14pF consists of the probe capacitance plus the parasitic capacitance of the resistor, so there is still the problem of measuring one or the other.  You may notice the sharp 4-5ns rise at the beginning before the rise turns into a steady logarithmic curve.  After seeing it on my first measurement, I repeated it to make sure it wasn't a glitch.  This EDN article explains that resistors are complex components with inductance, capacitance and resistance.  Charging of the small series capacitance of the carbon resistor I used may explain this initial fast rise.



Back to the problem of measuring my scope probe capacitance, I found a pdf document from Keysight that offers the simple solution of attaching two probes and comparing to the measurements from one probe.  I attached two probes, solved for C again, with a result of 27pF this time.  The 13pF increase should be the probe capacitance, with 1pF from the resistor lead.  The EDN article said resistors with 1/4" leads have a capacitance of 1-2pF, so the 1pF measurement from the resistor I used (with 10mm leads) is reasonable.

Taking 13pF for the scope leads from the 20pF measurement of the QFN AVR pin gives a net parasitic capacitance of about 7pF.  The measurement from the Nano with the QFP chip indicate a net parasitic capacitance of 12pF.  Considering the datasheet specification of 10pF max per pin for the ATmega328p, the extra capacitance on the nano likely comes from the board design, as things such as the distance between traces and a ground plane can affect capacitance.  For the chips themselves, both QFP and QFN, the parasitic capacitance is lower than 10pF, perhaps as low as 6-7pF per pin.

Wednesday, October 7, 2015

$2 esp8266 4MB ESP-12-E module

One of the recent goodies to arrive in the mail was a new esp8266 module.  When I ordered it, the price was 208c, but as of the time of this writing it has gone up.  Although it is advertised as an ESP-12-E module, the module I received is labeled ESP-12-Q, as can be seen in the photo above.  Other vendors are selling the same modules for 201c now.

One of the reason I ordered the module is because I intend to test the ADC function, and the ADC pin is not broken out on my ESP-01 module.  The other reason is that I read a couple postings from people that had received ESP8266 modules with a larger 4MB flash instead of the 512KB flash typical of the first ESP8266 modules released.

Since the modules have pads at 2.0mm spacing, I made an adapter board with some protoboard.  I just hot glued the module to the protoboard, and soldered short wires from the pads to the 0.1" spaced pin headers.  It is a bit more tedious work than using one of the pre-made adapter boards on the market, but I didn't have to wait for an adapter board, and my solution leaves more available space for connections on a breadboard.  Aside from the wire connecting EN with VCC, the rest of the connections are wired to the respective protoboard header pins.

After connecting power and Tx/Rx to a USB-TTL module, and pulling down GPIO15 to ground with a 1K resistor (to select boot from serial flash), I opened a terminal program to 9600bps, pulsed the reset line with a brief 1K short to ground, and got the following output:
NodeMcu 0.9.6 (Doit.am Version) build 20150701  powered by Lua 5.1.4

ESP8266 Started
Start soft AP
>

I also did a wifi scan with my phone and found an SSID named DoItWifi.  Since eLua was pre-flashed to the module, I could easily check the flash size with the node.info() function:
> =node.info()
0       9       6       16398478        1458400 4096    2       40000000

The 4096 indicates 4096KB (4MB) of flash.  The 512KB of flash on the ESP-01 module had been enough for the kind of progams I was writing using esp8266/Arduino, but now with a 4MB module I have plenty of memory to start experimenting with the ESP RTOS.

I measured the power consumption of the module at idle and with a wifi client connecting.  Idle consumption was consistently around 60mA, and with a wifi client connecting power consumption was around 90mA with a couple peaks to 140mA.

With this module I also decided to do some tests on the boot modes.  While the boot modes are reasonably well documented, there is little documentation on the default state of GPIO0, 2, & 15 at boot time.  This posting on the espressif bbs states they all have weak pullups, and my testing confirms this.  I held reset low with a 1K resistor to ground, and measured the voltage on the pins.  TXD0, RXD0, GPIO0, 2, & 15 all read 3.26V, while GPIO4 & 5 were 0V.  Connecting a 10K resistor to GPIO0, 2 & 15 caused the voltage to drop to 0.8V for each of them.  Calculating for the voltage divider ratio results in an internal pullup resistor value of around 33K Ohms.

I also tested the pins after booting from the on-board flash.  GPIO15 was low, GPIO0 was still pulled high, however GPIO2 was in output high state (a 10K resistor to ground resulted in a voltage drop of less than 100mV).  I also did some tests without GPIO15 pulled low, which, according to the documentation, should put it in the boot from SD card mode.  Every time I reset the module without GPIO15 pulled low, the module started outputting a 26Mhz signal on GPIO0.  Some googling indicates that this is what some people are calling the "zombie" mode.  Perhaps all that is required to avoid the zombie mode is to ensure GPIO15 is pulled low.

The final thing I tested was the deep sleep wakeup pin, GPIO16.  By connecting this to RST, it is possible to reset the module out of deep sleep.  I had previously tried connecting GPIO16 to RST on a ESP-03 module, but then had problems resetting the module.  The module was faulty in other ways, so I wasn't certain of the problem.  With the ESP-12-Q module, GPIO16 is driven high during reset and after reset.  Perusing the ESP8266 GPIO control files in the SDK indicates that GPIO16 does not have an internal pullup available, explaining why it is in output high mode.  This means that in order to be able to manually reset the module, a diode or a resistor of about 1K should be used between GPIO16 and RST.  Otherwise connecting RST to ground with a 1K resistor will not result in enough of a voltage drop to reset the module, and a dead short from RST to ground could result in enough current through GPIO16 to damage the chip.

With the 4MB ESP modules available for $2, I think they will now be my preferred choice for projects requiring an MCU.  The modules use too much power to work from a CR2032 coin cell, so for such things I'd likely use an AVR.

Tuesday, September 29, 2015

Under $3 Chinese Arduino UNO compatible board

With UNO compatible boards selling for under $3 on AliExpress, I decided to order one.  Some people might think you're only going to get cheap crap for that price, but in some ways it is even better than the original Arduino UNO R3.

Some boards can be found with a microUSB connector, but I prefer the full-size connector since it is much more robust, and I have half a drawer full of the old cables with no more use for them.   One thing that could be better on the board is the choice of USB-ttl chip.  It uses the CH340G chip, which, depending on your OS, can be problematic when it comes to drivers.  Prolific and FTDI have both done some nasty things with their drivers to deal with clone chips, so I wouldn't recommend either of them.  The Silicon Labs CP2102 would probably be my first choice.

The board arrived with the female headers and ICSP header already soldered on.  The vendor could have done a better job with the packing, as the ICSP header pins were bent (though easily straightened out).  The red power LED, like many other boards I've seen, is a little bright for my liking.  It uses a 1K SMD current limiting resistor, which I may replace if it gets too annoying.

There's a part by the ICSP header that I haven't been able to identify, that looks like some kind of diode.  It is in parallel with the adjacent 10K reset pullup resistor.  If anyone knows what it is, drop a line in the comments.  The board uses a 16Mhz SMD resonator to clock the ATmega328p, and strangely also has a couple of (22pf?) loading capacitors.  While crystal oscillators usually require loading capacitors, ceramic resonators do not.  A quick frequency check by toggling a pin at 2Hz confirmed it is clocked slow.  With the frequency counter on my multimeter I measured 1.984Hz, or 0.8% slow.

To make a more exact frequency measurement, I used my Rigol 1054z to probe the oscillator pins, and measured 15.8537, or 0.9% slow.  I then decided to remove the caps.  I have a hot air gun, but was concerned the heat could damage the SMD oscillator.  Instead I used the 2 soldering-iron technique to heat both ends of the SMD capacitors and remove them.  With the caps removed, the frequency measured 15.9410Mhz, or 0.4% slow, within the +- 0.5% typical rating of ceramic resonators.

I think the board is well worth 268c price, for someone starting out with AVR MCUs I think one of the Nano clones is a better idea.  For less than $2 you get a board that is simpler to use with a breadboard than a full-size UNO.

Thursday, September 24, 2015

145c ATmega328 Pro Mini

Last month when I saw an Aliexpress listing for a 145c Pro Mini (no longer listed), and it was a version that I hadn't seen before, I quickly ordered it.  The board design has pads for a regulator, although I haven't seen a version of this board for sale with the regulator populated.  I power most of my Pro Mini projects from 5V or even 3V battery power, so the lack of an on-board regulator is not an issue for me.

While waiting for the board to arrive, I reviewed the photo posted by the seller and noticed there doesn't seem to be any decoupling capacitors on the board.  The two small (0603) caps appeared to be connected to the reset button (for the DTR-controlled auto-reset), and to AREF for reducing ADC noise.  So when the board arrived, the first thing I did was probe the connections, confirming that there is no on-board decoupling cap.  The pre-tinned pads for a regulator provided a good spot for a 0.1uF 0805 decoupling cap.  So in addition to soldering the side header pins, I added a decoupling capacitor.

When I powered up the module, the green LED connected to pin 13 (PB5) started flashing every two seconds or so.  The power LED is also green, and almost blindingly bright.  A quick check with my meter revealed the current-limiting resistors for both LEDs were 470 Ohm.  Most of my SMD parts are 0805, but I do have a reel of 15K 0603 resistors.  Changing the resistor for the power LED to 15K changed it to a modest but still clearly visible glow.  Having a 470 Ohm resistor with a low-Vf LED (I measured ~2V) risks attenuating the slew rate of the SPI SCK signal which is the secondary function for PB5.  While I was able to check the fuse settings with a USBasp without problems, I still decided to change the resistor from 470 Ohm to 7.5K Ohm.

Speaking of the fuse settings, here's the readings from avrdude:
avrdude: safemode: Fuses OK (E:00, H:DA, L:FF)

Those fuses correspond to a 2K bootloader, 4.3V brown-out detection, and low-power clock 8-16Mhz, slow rising power (65ms from reset).  The clock fuse settings are detailed in table 9-4 of the ATMega328 datasheet (pdf).  I flashed my picobootSTK500 bootloader to free up 1.5K of space for code, and I may also change the brownout fuse bits to 2.7V so I can power it at 3.3V.

Unlike the Baite Pro Mini's I've bought before that have a crystal oscillator, these modules have a SMD ceramic resonator.  While the resonators take up much less board space, they are not as accurate as a crystal oscillator.  The accuracy of crystal oscillators is typically +-20ppm, and ceramic resonators are typically +- 0.5% (5000ppm).  I probed PB6/PB7 of the ATmega with my scope, and measured a frequency of 15.9837Mhz, which is 0.1% slow.  The timing wouldn't be good enough for a clock (it would run about 1.5 minutes slow per day), but it is perfectly adequate for high-speed (115kbps) serial UART or even low-speed (1.5mbps) USB.

While I am satisfied with my purchase, I've noticed that the price for the Baite Pro Minis with the on-board L05 regulator and large MLCC capacitors are now selling for 149c.  Even if I don't use the regulator, I'd spend the extra 4c just for the on-board decoupling caps.  The latest Baite boards also use > 1K Ohm resistors for the LEDs.

Monday, September 21, 2015

Diodes, diodes everywhere

I recently was breadboarding a circuit and needed a couple diodes.  I couldn't find my 1n4148s, so I used a couple of 1n4006s.  With their heavy gauge leads for high current carrying, the 1n4006 diodes are hard to plug into a breadboard.  Then I remembered the bag of tl431s I have, and that just like the zener diodes they are meant to replace, they can be used forward biased.  The tl431 block diagram above shows the diode from the anode to cathode.

One other difference I noticed with the 1n4006 diodes is their lower voltage drop.  As I discussed in my 5c lithium ion battery charger post, diodes don't have a fixed voltage drop, but have a voltage drop that increases with increased current.  At 1mA, 1n4148 diodes have voltage drop of 0.6 - 0.65V, while 1n4006 diodes have a voltage drop around 0.5V.  None of the datasheets for the 1n4148 or 1n400x had detailed specs for low current.  The first couple of 1n400x datasheets I found didn't even show current below 100mA.  However Jack Smith has a great write-up on the forward voltage of the 1n400x diodes including curves going down to 10uA.   I measured the forward voltage of the 1n4006 diodes using my multimeter (which uses a current of about 500uA) in the .502V to .515V range.  Jack's tests don't include the 4006, though my measurements would suggest that it falls in between the curves of the 4005 and the 4007.

Next I measured the voltage drop across the tl431 anode-cathode diode:
.542, .546, .540, .543, .539, .546, .541V

I also measured reference-cathode diode.  It isn't a discrete diode like one between the anode and cathode, it is the diode formed by the base-collector of the reference input transistor:
The variability of the A-K diode was about +-0.7%, the R-K diode was more consistent at around +-0.2%:
.729, .730, .728, .728, .727, .728, .730V

I also measured the base-emitter voltage of some old 2N2222a NPN transistors:
.601, .608, .612, .608V

and I measured the base-collector voltage too:
.605, .613, .616, .614V

Another common but often forgotten diode is the clamping or protection diodes on MCU input pins.  Here's a diagram from an Atmel AVR datasheet:

Although the datasheet and schematic suggest there is no difference between the protection diodes, there is a noticeable difference in the voltage drop.  The measurements for the protection diodes going to Vcc on an ATtiny13a were as follows:
.631, .631, .633, .629, .628V
and from ground:
.560, .561, .538, .537, .538V

The measurements from an ATtiny85 going to Vcc and from ground were as follows:
.634, .612, .613, .633, .634V
and
.554, .555, .555, .551, .553V

The voltage drop across the ground protection diodes is close to the 1N4006, which would suggest a current handling ability in the hundreds of mA.  Although circuits that take advantage of the ground protection diodes are extremely rare, the Vcc protection diodes allow for simple level shifting.  An AVR running at 3.3V can receive 5V signal simply by using a current limiting series resistor of around 1K Ohm on the input.

Sunday, August 30, 2015

Calibrating a cheap crappy tire multimeter


Anyone living in Canada is likely familiar with CT products carrying the Mastercraft branding.  They are significantly overpriced to allow for heavy sale discounts of 50-60% that usually happen a few times a year.  About 10 years ago I bought a Mastercraft model 52-0052-2 auto-ranging multimeter when it was on sale for C$20 (about US$15 at current exchange rates).  I use it for low-voltage, and have another multimeter with a current clamp that I use for household (mains) 120/240V testing.

I've been using it a lot over the last few years, and started getting a feeling that it was reading a bit high, based on readings from 3.3 and 5V regulators, and even from comparisons with battery voltage readings.  After testing a batch of TL431 voltage references, I was able to confirm that it is reading between 0.6% and 0.7% high.  I also ordered a couple 0.05% REF5050s, which will allow me to double-check my TL431 measurements.

After opening up the meter, I found there are 8 trimmer pots, and no indications on the board as to which of these adjusts the voltage.  I eventually found a discussion on EEVblog  that indicates the meter is made by the Hong Kong company Colluck, and is spec'd for 0.8% accuracy on voltage readings.  I have what DIPlover calls the old model, which measures 9cm wide and 18.5cm long.  While I still couldn't find documentation on how to adjust the calibration, I did find a review of another cheap multimeter that had several trimmer pots, and the pot labled VR1 was the voltage calibration trimmer.  I figured I had little to loose by trying the same pot on my meter.

In case VR1 didn't adjust the voltage, I needed to adjust it back to it's original position.  I used an ultra-fine sharpie to mark a small line on the top and the base of the trim pot so I could locate the original position.  I used a TL431 which was reading 2.513V, which I though should read around 2.496V, connecting it to my meter probes with a hook probes.  With the first small adjustment of the pot, the voltage reading went up to over 2.53V, so I had the right pot.  The sensitivity was a bit of a problem, as the tiniest adjustments I could make were undershooting and overshooting.  After several tries, I got a reading of 2.496V, which I think is within 0.1%.  With a couple of REF5050's it should be possible to calibrate it to +- 1mV, or 0.04%.  But given how sensitive the trim pot is, I won't touch it as long as it is within 2mV.

Thursday, August 27, 2015

Cheap TL431 voltage references


Until a year ago I had never heard of the TL431.  Then I read Ken Shirriff's blog post, as well as other mentions of the TL431 on hackaday.com and eevblog.com.  I found out the 431 is useful not only as a voltage reference, but also as a constant current control, and even a voltage controlled oscillator.

I had started suspecting my cheap (~$20) auto-ranging multimeter was reporting voltages a bit on the high side, and when I found 100 TL431s selling for less than 150c, I ordered them.  While waiting for them to arrive I tried to find out more information about the manufacturer, Wing Shing Computer Components of Hong Kong.   I could not find an active web site (at least in English), and although I found an old datasheet for the WS-TL431, I could not find anything current.  I did find another Aliexpress seller that posted a photo of a box full of WS-TL431A showing a 0.3% accuracy rating, which, considering the low price, is quite good.  Even 1% rated genuine TI TL431 parts are difficult to find for less than 2c each.

Once I received the package, I checked out the chip markings, which were all the same:
WS
TL431A
155SD

I suspect the 155 is a date code for 2015, 5th week, indicating these are new parts.  The old datasheet from Wing Shing shows the TL431A part as only 1%, and a TL431AA as 0.5%, and nothing listed for a 0.3% part.  I don't think I'm perpetuating an unfair stereotype to say that the Chinese are notorious for bad or non-existent documentation.  I think that the parts I received are actually rated to within 0.3% at 25C, and the manufacturer has not undertaken to produce an updated datasheet (or English website, for that matter).  Other compatible parts such as Linear's LT1431 is rated at a 0.4% initial tolerance, and the  price is in line with similar Chinese TL431 parts such as the ALJ TL431A and the CJ431.  After checking the WS TL431 chip markings, I setup a simple circuit on my breadboard with a 270 Ohm input resistor (which should give about 9.5mA) from a ~5V USB power supply to test the parts.

I tested a total of 25 parts with ambient temperature of 24C.  The average voltage reading was 2.513, and the range was from 2.506 to 2.517, or 2.5115V +-0.23%  The measurements are consistent with the parts being 0.3% rated, as well as suggesting my meter is reading about 0.6-0.7% high.

The next thing I tried was to crack open the TO-92 package with a pair of pliers in an attempt to expose the die.  Like Ken, I was able to expose the copper anode (seen in the very first picture), but was not able to expose the die.  The die appears to be around 0.6mm x 1mm, so even if I was able to expose the die, with only a magnifying glass, I doubt I would have been able to see much.

My intention in trying to expose the die was to see if the Wing Shing parts are fuse trimmed like the TI part depicted by Ken.  Two fuses give four different combinations of trimming options, which should show up as more than one peak in the distribution of the voltages.  Without a die, I could still analyze my measurements and look for peaks.   A simple shell command was all I needed:
sort voltages.txt | uniq -c
      1 2.506
      1 2.508
      2 2.509
      3 2.510
      2 2.511
      3 2.512
      1 2.513
      3 2.514
      3 2.515
      3 2.516
      3 2.517

Even with only a quarter of the parts tested, it is evident the voltages are concentrated around 2.510V, 2.512V, and 2.515/2.516V.  While more data points would be helpful, the testing is consistent with fuse-trimmed 0.3% parts.

The first practical circuit I made with the TL431 uses it as a 2.5V zener for battery reconidtioning.  I had been using a 270 Ohm resistor to discharge the batteries.  With the TL431 acting as a 2.5V zener, a high current red LED and 160 Ohm resistor add up to an addition 2.5V drop, very close to the 4.8V total when discharging a 12-cell battery to 0.4V/cell.

I'd like to have a discharge closer to 0.1C, which would be around 130mA, but the red LED is rated for 50mA maximum continuous current.  The TL431 datasheet has a simple constant current circuit, and by making a couple small modifications to that circuit I think I can make a 130mA constant current discharge circuit with a cut-off voltage just below 5V.

2015/09/22 Update

I did a quick test of the dynamic impedance, or the change in reference voltage vs change in shunt current.  Increasing the current from 3mA to 15mA resulted in a 2mV increase in reference voltage, indicating a dynamic impedance in the range of 0.15 to 0.2 Ohms.

2015/12/20 Update

Based on my testing with an AD584, I now believe these are 2.5V references, not 2.495.