Wednesday, September 19, 2012

Testing Battery Pulse Characteristics

Low power wireless devices typically sleep most of the time and then wake up to send a message and then go back to sleep again. In a previous post I discussed battery selection for low power wireless devices. One issue with using coin cells is that they have poor pulse characteristics, and this performance varies across manufacturers.

To figure out which battery will work for you (and get a feel for battery life) you will need to run a test. This test will involve pulsing the battery, waiting, and then pulsing it again; continuing until the battery voltage under load will no longer be sufficient for your product.

Pulse Waveform
First, you need to get the waveform of what a typical pulse looks like. The easiest way is to supply your circuit through a one-ohm resistor and capture a typical pulse on an oscilloscope. Below is an example.

To test battery life you will need to be able to reproduce this current draw. So, measure the current pulse. You don't need to get that fancy; just measure the width and height of the main pulse. For example, it may be 30mA for 5mSec. To reproduce the pulse you will need a suitable circuit. A simple MOSFET and load resistor will work. For 30mA at 3V use a 100 ohm resistor (V=IR).

Pulse Generation
To generate the pulses you can use an arbitrary waveform generator, or if it's just simple on/off, you could even just write code to do it on a microcontroller development board if that's easier. For the most accurate estimate of battery life as measured by number of pulses you will want to allow as much "recovery time" as possible for the battery between pulses. Obviously you can't wait too long though; if you pulse every 6 seconds and your battery lasts for 45,000 pulses then this will take 3 days to measure. Typically we will run a "slow" test and "fast" test simultaneously to see if wait time has any affect.

Remember that you need to measure the voltage of each pulse too to ensure that it it above your threshold voltage. This can be done in a few ways:
  • Analog DAQ, like those from National Instruments, although often these are not fast enough
  • Multimeter with output, like an Agilent 34410A DMM. This is what we used with good results. Some custom processing of the output may be required though.
  • Custom code on a microcontroller. Come to think, it might just be the easiest way, especially if you are using the microcontroller to generate the pulses too.
Whatever option you select, record how many pulses it took before failing. It might also be good to record a typical voltage measurement (like voltage readings every millisecond of the pulse) so you can see how it fades.

Test Strategy
For good measurement accuracy, perform the test on a sample of identical batteries, not just one. Five or so is sufficient. If you see lots of variability then you know to definitely not use that vendor. Test batteries from a few different vendors and use this information to determine which battery is best for you.

You also now have a good indication of real battery life and which vendor's battery is best for you. When calculating total battery life be sure to include sleep current consumption as well as the current consumed by receiving any data too.

Monday, September 17, 2012

Battery Selection for Zigbee and Low Power Wireless

Low Power Wireless Standards include Zigbee, Bluetooth Low Energy, Dash7, you name it. Often times the newest wireless standard makes a claim like "lasts 4 years on a coin cell battery!" This is usually in the earliest days of the standard before anyone has actually tried it.

Battery selection for low power wireless standards is very important. These systems typically need to run for a long time (months, if not years) and have many demands. Our ideal battery for these systems:
* Lots of capacity (measured in amp-hours)
* Small size
* Excellent peak current capability (a wireless node can pulse up to 100mA)
* Fairly flat voltage curve
* Very low self-discharge
* Dimensional stability (doesn't swell)
* Low cost
The challenge is that we're asking quite a lot from a battery. We want it to discharge very little while the device is sleeping but then also to discharge in large pulses when we transmit. Usually batteries that have excellent peak current capability have a higher self-discharge rate, and vice-versa.

When starting a new project, people will often leap into creating a fancy battery lifetime spreadsheet, showing how based on estimated current consumption the battery will last something like 13.7 years or so. If only it were so! Unfortunately reality is quite a bit different. Battery lifetime is usually quite a bit shorter and often people don't quite know why.

One of the biggest reasons why is internal resistance. This causes the battery's output voltage to drop under load, limiting the effective amount of current that can be delivered. In general, the larger the battery, the smaller the internal resistance. This is one reason why coin cells don't work well in low power wireless devices - they have too high of internal resistance and therefore cannot supply enough peak current. They'll work fine for data-logging or applications where the peak current is low (below 10mA) but for wireless devices that pulse at 30-100mA they will start to fade. Most coin cells are not specified at all for pulse current, let alone that much. Even worse, the pulse current capability will be different for different vendors' batteries. That means that Energizer may be able to handle a 15mA pulse but Maxell might be able to do 20mA pulses. It all depends on the coin cell size, geometry, etc.

My favorite battery for low power wireless devices is the Energizer L91 (AA size) or L92 (AAA size). These lithium batteries have low self-discharge, nice voltage curve, good peak capability, and you can buy them in most grocery stores.

Next I'll talk about how to test different batteries...

Friday, September 14, 2012

The Joy of Bit-Banging, part 2: RX

I previously discussed how to implement the Tx side of a bit-bang serial port, in The Joy of Bit-Banging, part 1: TX. This time I'll describe how to implement the receive side. This implementation uses blocking waits, as a result it should not be used unless you can tolerate delays.

In this implementation the receive pin is P2.5

Required definitions, etc:
#include "msp430x24x.h"
//Timings calculated using DCO_8_MHZ - modify if different oscillator speed or different hardware
#define BIT_LENGTH_9600 160
#define HALF_BIT_LENGTH_9600 76
#define BIT_LENGTH_19200 75
#define HALF_BIT_LENGTH_19200 27

/** Simple delay for bit width. Used in transmit and receive. */
void delayFullBit() {for (unsigned int i = 0; i < BIT_LENGTH_19200; i++) ; } //104uSec delay

/** Simple delay for 1/2 of bit width. Used in receive. */
void delayHalfBit() {for (unsigned int i = 0; i < HALF_BIT_LENGTH_19200; i++) ; } //104uSec delay

#define TOGGLE_DEBUG_PIN() (P4OUT ^= BIT5)  //debugging only

Output function:
* Blocking UART receiver. Called by BIT_BANG_RX_PORT Interrupt Service Routine upon detection of start bit (high to low transition).
* BIT_BANG_RX_BIT should be configured as high-low edge triggered interrupt.
* Receives a byte and then processes it.
* @note BIT_BANG_RX_PORT, BIT_BANG_RX_BIT, DISABLE_BIT_BANG_RX_INTERRUPT(), and ENABLE_BIT_BANG_RX_INTERRUPT() must be #defined. Modify accordingly for other hardware.
* @pre Oscillator configured for 8MHz
* @pre A high-to-low transition has occurred on BIT_BANG_RX_BIT
* @return the byte received
char bitBangInput()
    unsigned char receivedBitIndex = 0;
    unsigned char receivedByte = 0;
    delayHalfBit();     //Since this method was called on detection of an edge, need to wait until we're in the middle of the bit width.
    //Read this start bit - should be zero
        //error - re-enable interrupt and return  

    for (int i =0; i    {
        receivedByte |= (BIT_BANG_RX_PORT & BIT_BANG_RX_BIT) ? (1 << receivedBitIndex) : 0;     //Sample the bit, and if '1' then increase receivedByte accordingly
    //THIS IS STOP BIT (line should be back high)
     //error - line is low; should be high because this is stop bit  
    //all bits have been received!
    return receivedByte;

Thursday, September 13, 2012

Implementing I2C

Lately I've been doing a lot of I2C development. In just about every way you can do it. Over the course of a month I've had to write code to communicate with an EEPROM and two sensors via:
  • MSP430 using USCI
  • MSP430 using bit-bang
  • Stellaris using StellarisWare
  • Stellaris using bit-bang
Over the course of that experience I've gotten to know the I2C protocol very well. I won't go into the details of the protocol; there are plenty of sources for that.

When you're first getting started, the best way to verify the i2c interface is configured correctly is to not connect the peripheral, but just watch the i2c signals to see that they are being toggled correctly. By definition I2C is not a push-pull interface but uses open drain I/O. It's really quite elegant, as it prevents a bus conflict if one IC outputs a '1' and another outputs a '0'. As a result if you want to see anything you will need external pull-up resistors. I like to first develop code first without the peripheral ICs, just the pull-up resistors. This way you can connect a logic analyzer to the two signals and verify that your code is working correctly. This is especially important if you are implementing a bit-bang solution and need to check that the GPIOs are configured correctly.

If you are using a hardware based solution then it is fairly straightforward; configure the baud rate, etc. and the hardware does all the signaling. The hard part is determining how to use the various hardware registers to get the desired output. This is much easier if you're using StellarisWare, as it handles all the register settings for you.

If you're implementing a bit-bang solution, there are two ways of doing this. The first is with simple delays; the second is using a timer interrupt. I've done both. There both take about the same amount of time to implement. The first way is simpler but requires hand-tuning the amount of delay between bits to ensure the correct baud rate and a 50% duty cycle. Using a timer is a bit more elegant as you're not waiting the processor but requires a state machine to iterate through the various steps. If you're using a StellarisWare it includes a nifty little softI2c implementation that can be used decently easily.

The first test you should do is to just do a simple write of 4 bytes or so, and observe it on the logic analyzer. Of course the ACK bit will not be pulled down since there's no peripheral IC but you'll at least be able to observe proper timing and framing behavior. After you get that working also verify that reads work too.

After you get basic interfacing working, the next step is to write a simple address tester. This just writes to an address and checks to see if the write is acknowledged. This function is handy for verifying that the I2C peripheral is attached properly. This function can also be used for acknowledgment polling if you're implementing an EEPROM or FLASH interface. These types of memories take a few milliseconds to write and you must check that they are no longer in their write cycle before trying to access them again.

Once basic functionality is working you can implement the basic read/write routines. Write is easier since it's a single step operation. Reading typically requires two steps: First writing an address (e.g. register or memory address), then doing a repeat start, and then doing a read.

Since I2C has a fairly low bit rate (100kHz or 400 kHz under normal conditions) it can take awhile to write or read a lot of bytes (6mSec to read a full 64B page from an EEPROM at 100kHz). If you're using the I2C interface in a simple sensor it's fine to wait while communicating since you're not doing anything else until you receive the result. However, that would waste quite a few clock cycles; 160k if using a 25MHz clock. If you're using an RTOS or would otherwise like to minimize wait states then you'll want to implement it differently. In this case you should use DMA or at least an interrupt driven approach.

A few miscellaneous I2C hints:

Logic Analyzer
I2C is much, much easier to troubleshoot with a Logic Analyzer since it will parse the serial data stream and show you the I2C start, stops, and data. I really like one from Saleae, they're USB based and great for microcontroller use.

Mission Critical
I've had occasions whereby one of the peripheral ICs would get into a funky state and cause the I2C bus to lock up. If you're dealing with mission critical application or you just have an extra GPIO pin then I recommend controlling the power of each peripheral IC from the GPIO. That way you can "reboot" a peripheral IC if there is an issue.

Repeat Start
Several peripherals require you to implement a repeat start condition. This isn't well documented in the processor's documentation and you may need to do a bit of research to find out how to do it.