20 June 2012

Experiment: Battery Profiling


Most of us probably purchase batteries with little regard to the brand.  We may purchase whatever is cheapest at the time; we may get a particular brand out of loyalty; we may have just had better luck with one particular brand.  Is there actually any difference between them, though?

I had an experience that  indicates there is, though it may not manifest itself in ways that are apparent to the general consumer.  On a sounding rocket experiment, we had trouble with a particular power supply.  Some investigation revealed that the brand of battery in the supply had problems if it was squeezed in a particular way, shorting out the battery internally.  After taking a few batteries apart, we found that they were, in fact, constructed with slight differences.  How those differences affect performance can be examined using a technique that is ideally suited to a microcontroller.

Here's the idea: run a battery through a particular configuration and monitor its voltage throughout.  In this experiment, I'll keep it simple, but you could easily come up with all kinds of scenarios and configurations in which to compare different batteries.  This demonstration uses only the two major brands, in the AAA form factor, and compares the voltage profile over the lifetime of the batteries at equal current draws.  This is done by having two matched resistors (as measured by a multimeter) draining each battery.  If there is any difference between the two brands, it will be seen by a different discharge rate over time.  Four samples are run, using two resistance values.  Each run uses one of each brand, to eliminate temperature differences.  Two samples are taken at each resistance, swapping the circuits for the two brands in each.  The batteries purchased were selected to have the same expiration date marked on the packages.

Searching online, we find that many people have done a similar experiment, though typically done in ways that are more subjective, such as putting different brands in identical flashlights, see which one gets dim first. Using a microcontroller, we can measure the actual voltage at the battery at specified intervals to get a picture of how the energy is drained from the battery over time.

MSP430 Peripherals Used

  • TimerA with interrupts: Continuous mode, interrupts at CCR0, CCR1, and TAR Rollover
  • ADC10 with interrupts: Sequence Mode, Single Conversion
  • Software UART (Full-Duplex)

Equipment Needed

  • LaunchPad with 32 kHz crystal
  • MSP430G2231 (or any device with ADC10)
  • Computer that can be left on for a few days
  • Multimeter
  • Breadboard
  • 2 single AAA battery holders
  • 2 150 Ω, 1/4 W Resistors
  • 2 33 Ω, 1/4 W Resistors
  • DPST Toggle Switch, rated at least 0.5 A @ 6 V
  • Various Wires

Experiment Design

Schematic for Battery Profiler
The circuit is quite simple: R3, R4, and the LED on P1.6 are built into the LaunchPad.  Q1 is soldered to the LaunchPad and used solely to calibrate the DCO; if this is already done, Q1 is optional.  Remove the jumpers for the Rx, Tx, and LED on P1.0 from the LaunchPad, and jumper Rx to P1.4, Tx to P1.5.

R1 and R2 are matched to be equal resistance, measured with a multimeter.  S1 is a DPST or DPDT toggle switch.  Note that you must use P1.1 and P1.0 to use Sequence Mode for the ADC10 module.

The code for the MSP430G2231 is found in battery_profiler.c.  Most of this code should be straightforward for anyone who has followed through Tutorial 18.  There are a few things to point out, though:

  • Each sample is an average of four measurements.  Since floating point math (particularly division) is a bit expensive in the MSP430, you can use a trick: note that shifting right by one bit is dividing by two. If you are averaging a number of samples equal to a power of 2, you can add the samples together and then shift right by the appropriate number of bits to get division.  Rounding can be done by adding 1 to the highest bit not being included-- in this example, using 4 samples, the second to last bit is the highest not included, so if V holds the sum of  4 measurements, ((V + BIT1) >> 2) gives the rounded average.  Since ADC10 uses only 10 of the 16 bits available in the register, you can average 2, 4, 8, 16, 32, or 64 samples with this technique.  If necessary, floating point numbers and division can be used instead, but it does require more resources of your MSP430 device; plan accordingly.
  • Another issue is transmitting the data; you can transmit the raw bytes back easily, but the data is more easily analyzed afterward if the values are sent in ASCII text.  The MSP430 can use the C printf function in the stdio.h library, but this library is large and increases the size of your code significantly.  This code makes use of a slick scheme to reduce the code size-- the tx_uint() function will transmit a 5 character string corresponding to the zero-padded value of whatever value is passed to it by making use of integer division and modulus operations.  Transmitting the floating point value of the measured voltage is a bit trickier, so the ADC10 conversion is transmitted instead.  These values are converted to a voltage during analysis, using 2.5 V = 1024.
  • The code makes use of the Sequence Mode for the ADC10.  This mode samples each channel starting at that specified by INCH_x in ADC10CTL1, and stepping through to channel A0.  In this example, A1 is measured first, then A0.  This mode operates by waiting for the ENC and SC bits to be set, starting the conversions.  When the first conversion is done, an interrupt is flagged.  In this code, the interrupt resets the flag bit in UART_FG, signaling the code to continue to the next step.  The next channel is sampled when ENC sees another rising edge.  This tripped me up for a while; waiting for a rising edge on ENC allows you to control the timing between samples in the sequence arbitrarily.  If you would rather the ADC10 automatically start the next conversion immediately, you must set the MSC bit in ADC10CTL0 as well when initializing the ADC.  Once the whole sequence is done, ADC10 will wait to have ENC and SC set again to start the next sequence.  This code is timed to do one sequence every 10 seconds.
  • Finally, this code uses all three Timer_A interrupts possible on this device: CCR0, CCR1, and TAR overflow.  This example should help anyone trying to understand the various interrupts that can happen for the Timer_A module.


This experiment takes a good deal of time to finish, and I've decided to post the experiment before getting the actual results.  Here is an example of what we'll be looking at to start-- this is a trial run using two random AAA batteries I had on hand, done just to be certain everything behaved as expected.  As you can see, at 150 Ω it takes days to drain the batteries completely in this setup.  After a few days, I decided to call it a good proof-of-concept, and the experiment is ready to run.  I'll let it run for the next little while, and when I have official results I'll post them here, along with a detailed write-up of what is happening.

Don't worry-- I have two LaunchPads, so I'll be able to post a couple more things while waiting for the experiment to run.

Let me know if you like this post design-- is there any more information that you think would be helpful to have here?