Fuel gauge for batteriesApril 16, 2014
Lithium polymer (LiPo) batteries are strange beasts. I can’t simply measure the current voltage and tell you how full it is. (You can on throw-away AAs.) Worse, a nearly-full battery and a nearly-empty read about the same voltage until it become really empty and the battery dies.
Determining the LiPo battery’s state of charge requires an algorithm that monitors the battery over time and over a few charge cycles. The simplest way for me to do it for the are-you-ok widget is to buy the monitoring in the form of a small board: the LiPo Fuel Gauge.
However, while not adverse to reading datasheet, I just wanted to plug this board into my system and have it work. I did read the example code: it reads the percent from a register and sets up an alarm-interrupt I don’t care about.
I should have been more suspicious when my full battery read a state of charge (SOC) that was in the 30,000s but really, I didn’t care the actual number as long as I could see the power go down over several days. Except, with deep sleep working, the power is taking much longer than several days to go down. Last time I read the battery, it said 18664. Reading the voltage with a DVM showed it to be very high.
Then I broke that battery’s wire so I need to switch to another module (a very small one this time so I can check the fuel gauge better). Also, this time I’m going to read the datasheet, to get it set up properly.
I figured I might as well take notes here. Maybe note a few things about how to write instructions since that’s also on my mind. The intro starts off pretty good and a line caught my eye in the third paragraph.
A quick-start mode provides a good initial estimate of the battery’s SOC.
Good, that’s what I need. And I already know I can connect to it, at least to read registers. I can probably write registers but I don’t have that code for this chip. (That’s like 3 minutes of coding and 4 minutes of testing so this isn’t a major deal.)
Next in the datasheet is a bunch self-congratulatory blahblahs that don’t help me solve my problem. Why do they do that? I already bought the thing, quit selling it to me and tell me the good stuff. I get through the “for the electric engineer” tables (those can be important to me but on first skimming, I tend to let the data slide over my brain), then messy graphs followed by more coy hinting at their algorithm and finally to a section called IC Power-Up.
When the battery is first inserted into the system, there is no previous knowledge about the battery’s SOC.
Since the LiPo’s state of charge depends on it’s history, the first power up is tricky. It goes on to say about how the fancy-schmancy algorithm will converge with time. This was what I was depending on before (and about where I stopped reading the datasheet before). Eventually, the fuel gauge figures itself out, after a few charge and discharge cycles. Of course, if you are talking about months, then that isn’t so useful.
I want a way to charge the battery fully (because the charger says it is done and it have been charging for several hours) and then tell the fuel gauge it is full. Ideally, that will be covered in the Quick-Start section that is next.
A quick-start is initiated by a rising edge on the QSTRT pin, or through software by writing 4000h to the MODE register
Yay? Ok, so now I know how to go into quick start mode but what does that do? It doesn’t tell me. This is why I hate reading datasheets sometimes. It is like talking to a recalcitrant four-year old.
Moving on, maybe things will become clear if I keep reading. The next section is ALERT Interrupt. I don’t want that (not enough pins on the Electric Imp, maybe I could use pulldowns to double up the wakeup pin but that seems too much like work; I don’t mind polling the fuel gauge every hour since the unit needs to wake up and check-in to the server anyway).
The next section is on Sleep Mode. That should be interesting but since I can’t kill batteries, I haven’t done the last few power optimizations: put this and the accelerometer into sleep mode when they aren’t needed.
Let’s see, I can reset the fuel gauge, as though I power cycled it. Whee. (That was an unenthusiastic whee in case you couldn’t tell from the tone.)
Now for the Registers section. As a software person, this is the section I usually skip to. If this datasheet was a walnut, this would be the delicious meaty core.
The SOC Register really should be giving me a percent full.
Units of % can be directly determined by observing only the high byte of the SOC register. The low byte provides additional resolution in units 1/256 %.
Ok! So my last reading was 18664, in hex that is 48 EB. Looking at only the high byte hex 48 equals decimal 72. My battery is 72% full. Look at that, it makes sense now.
On April 8th, my reading was 24839, in hex that is 61 07. So hex 61 is 97%.
Essentially I’ve been reading it wrong. To be fair to my previous self, I did look at the figure that showed how to read it which says something different than the text. (Different and implausible but I know why I decided I could just read the 16-bits and concatenate them together.)
This is an easy fix to my software. Where the code used to have
local tmp = (data << 8) + data;
I modified it to
local tmp = data;
As I only care about the top byte.
Well, while I knew I needed to fix the output issue, the information was decreasing reasonably as I discharged the battery, I know there was just a units or conversion issue. That isn’t why I’m reading the datasheet. I need to know how to tell it that my battery is full. But I should only do it in a manufacturing mode or something, definitely not on every boot or even on every power cycle.
Happily, the next table is about the MODE register which mentions the Quick-Start command. That references the “Quick-Start description section”, what I read before that mentions there is a Quick-Start mode but not how to use it. Searching through the document for Quick-Start leads to nothing.
The MODE register section says the only valid setting is the quick-start setting. I feel like I’m reading an Escher print turned into words.
Ahhh, the version register is next to be described. What could possibly go wrong? Well, for one, it doesn’t tell me what to expect. I read the register and get a value. Is it a good value? Many (most!) vendors suggest what to expect, reading the version register (or whoami) is a great way to verify that I’m communicating properly with the chip. Alas, not for this IC monstrosity. (Ahh, it isn’t that bad, I’ve read far worse datasheets but this one is remarkably easy to pick on.)
I mean, in the next section, it goes over the CONFIG register (which incidentally is where the sleep mode is set). There is a bit in CONFIG called X (Don’t Care).
This bit reads as either a logic 0 or logic 1. This bit cannot be written.
I suppose it is my scotch-and-ice-cream dinner or the last nine pages of nonsense, but this statement strikes be as funny. “This bit cannot be written”, wanna see me try? Because I can write it. The IC may ignore it but I can so write it if I want to.
Next there are some applications of this chip, how to use it for multiple LiPo cells. That’s all very interesting but actually not.
Then there are several pages describing the communication method (two-wire so they don’t have to license from whoever holds I2C’s patent, how can you patent such things?). I don’t care about this as I have that part working.
And then the end. It includes an address only about five miles away. I want to go and ask them to explain to me if there is a way to tell their chip that the battery should be pretty full since I just finished charging it during my hokey manufacturing process.
Instead, with the data format fix, I’ll just plug in another battery and see if it is recognized as full and discharges normally. It is supposed to converge, might as well let it.
And the next time I see a MAXIM part, I will (once again) read the internet-supplied example code and not bother with the datasheet until I absolutely have to. Though, I’d still rather buy MAXIM than Infineon parts.
But that’s a separate rant, for another day.