User:Kerio/bq27k.py


 * 1) !/usr/bin/env python

import time import struct

RS = 20 # nominal, but sometimes 21 or 22 give more accurate results

class Bitstring(long): """Number with an easy way to access single bits or ranges of bits.

b[n] is the n-th bit of b, as a bool b[a:b] is the bits from the a-th to the (b-1)th

"""   def __getitem__(self, item):        # something that's not enough like a slice...        try:            start, stop = item.start, item.stop        except AttributeError:            return bool(self & 1 << item)

# ...or something that is       if start is None: start = 0

if stop is None: return Bitstring(self >> start) else: return Bitstring((self >> start) % (1 << (stop - start)))

class MODE(Bitstring): """Device Mode Register"""

@property def SHIP(self): """Ship mode""" return self[0]

@property def FRST(self): """Full reset""" return self[1]

@property def POR(self): """Power on Reset""" return self[2]

@property def PRST(self): """Partial reset""" return self[3]

@property def DONE(self): """Write LMD to NAC""" return self[4]

@property def WRTNAC(self): """Write AR to NAC""" return self[5]

@property def GPSTAT(self): """GPIO pin""" return self[6]

@property def GPIEN(self): """State of the GPIO pin""" return self[7]

@property def CIO(self): """Calibrate internal offset""" return self[4]

@property def CEO(self): """Calibrate external offset""" return self[5]

class FLAGS(Bitstring): """Status Flags"""

@property def EDVF(self): """Final End-of-Discharge-Voltage""" return self[0]

@property def EDV1(self): """First End-of-Discharge-Voltage""" return self[1]

@property def VDQ(self): """Valid Discharge""" return self[2]

@property def CALIP(self): """Calibration-In-Progress""" return self[3]

@property def CI(self): """Capacity Inaccurate""" return self[4]

@property def IMIN(self): """Li-ion taper current detection""" return self[5]

@property def NOACT(self): """No Activity""" return self[6]

@property def CHGS(self): """Charge State""" return self[7]

class DMFSD(Bitstring): """Digital Magnitude Filter and Self-Discharge Rate Constants"""

@property def SD(self): """Self-Discharge Rate (%)""" return 1.61 / self[:4]

@property def DMF(self): """Digital Magnitude Filter (mA)""" return self[4:] * 4.9 / RS

class TAPER(Bitstring): """Aging Estimate Enable, Charge Termination Taper Current"""

@property def TAPER(self): """Charge Termination Taper Current (mA)""" return self[:7] * 228.48 / RS

@property def AEE(self): """Aging Estimate Enable""" return self[7]

class PKCFG(Bitstring): """Pack Configuration Values"""

@property def TCFIX(self): """Fixed temperature compensation""" return self[0]

@property def DCFIX(self): """Fixed discharge compensation""" return self[1]

@property def BOFF(self): """Board offset value (uV)""" # 3-bit two's complement return ((self[2:5] + 2) % 4 - 2) * 2.45

@property def QV(self): """Qualification voltage for charge termination (mV)""" return 3968 + 48 * self[5:7]

@property def GPIEN(self): """State of the GPIO pin on initial power up""" return self[7]

class DCOMP(Bitstring): """Discharge Rate Compensation Constants"""

@property def DCOFF(self): """Discharge rate compensation threshold""" return [0, 0.5, 0.25, 0.125][self[:2]]

@property def DCGN(self): """Discharge rate compensation gain (%)""" return self[2:] / 2.56

class TCOMP(Bitstring): """Temperature Compensation Constants"""

@property def TOFF(self): """Temperature compensation offset (C)""" return self[:4]

@property def TCGN(self): """Temperature compensation gain (%)""" return self[4:] / 10.24

class Bq27kData(object): """Class that provides data descriptors to fetch converted bq27k register   data from an indexable object. The underlying object must return bytes    curresponding to the bq27k registers on item access; alternatively, known    two-byte registers might be stored on the "low" register - the "high" one    must be 0, in this case.

"""   @property    def CTRL(self):        """Device Control Register"""        return self[0x00]

@property def MODE(self): """Device Mode Register""" return MODE(self[0x01])

@property def AR(self): """At-Rate (mA)""" return word(self[0x02:0x04]) * 3.57 / RS

@property def ARTTE(self): """At-Rate Time-to-Empty (min)""" return word(self[0x04:0x06])

@property def TEMP(self): """Reported Temperature (C)""" return word(self[0x06:0x08]) * 0.25 - 273.15

@property def VOLT(self): """Reported Voltage (mV)""" return word(self[0x08:0x0a])

@property def FLAGS(self): """Status Flags""" return FLAGS(self[0x0a])

@property def RSOC(self): """Relative State-of-Charge (%)""" return self[0x0b]

@property def NAC(self): """Nominal Available Capacity (mAh)""" return word(self[0x0c:0x0e]) * 3.57 / RS

@property def CACD(self): """Discharge Compensated NAC (mAh)""" return word(self[0x0e:0x10]) * 3.57 / RS

@property def CACT(self): """Temperature Compensated CACD (mAh)""" return word(self[0x10:0x12]) * 3.57 / RS

@property def LMD(self): """Last Measured Discharge (mAh)""" return word(self[0x12:0x14]) * 3.57 / RS

@property def AI(self): """Average Current (mA)""" return word(self[0x14:0x16]) * 3.57 / RS

@property def TTE(self): """Time-to-Empty (min)""" return word(self[0x16:0x18])

@property def TTF(self): """Time-to-Full (min)""" return word(self[0x18:0x1a])

@property def SI(self): """Standby Current (mA)""" return word(self[0x1a:0x1c]) * 3.57 / RS

@property def STTE(self): """Standby Time-to-Empty (min)""" return word(self[0x1c:0x1e])

@property def MLI(self): """Max Load Current (mA)""" return word(self[0x1e:0x20]) * 3.57 / RS

@property def MLTTE(self): """Max Load Time-to-Empty (min)""" return word(self[0x20:0x22])

@property def SAE(self): """Available Energy (mWh)""" return word(self[0x22:0x24]) * 29.2 / RS

@property def AP(self): """Average Power (mW)""" return word(self[0x24:0x26]) * 29.2 / RS

@property def TTECP(self): """Time-to-Empty At Constant Power (min)""" return word(self[0x26:0x28])

@property def CYCL(self): """Cycle Count Since Learning Cycle""" return word(self[0x28:0x2a])

@property def CYCT(self): """Cycle Count Total""" return word(self[0x2a:0x2c])

@property def CSOC(self): """Compensated State-of-Charge (%)""" return self[0x2c]

@property def CRES(self): """Calibration Result (uV)""" # 16-bit two's complement return ((word(self[0x5e:0x60]) + 0x8000) % 0x10000 - 0x8000) * 1.225

@property def EE_EN(self): """EEPROM Program Enable""" return self[0x6e]

@property def ILMD(self): """Initial Last Measured Discharge (mAh)""" return self[0x76] * 913.92 / RS

@property def EDVF(self): """EDVF Threshold (mV)""" return (self[0x77] + 256) * 8

@property def EDV1(self): """EDV1 Threshold (mV)""" return (self[0x78] + 256) * 8

@property def ISLC(self): """Initial Standby Load Current (mA)""" return self[0x79] * 7.14 / RS

@property def DMFSD(self): """Digital Magnitude Filter and Self-Discharge Rate Constants""" return DMFSD(self[0x7a])

@property def TAPER(self): """Aging Estimate Enable, Charge Termination Taper Current""" return TAPER(self[0x7b])

@property def PKCFG(self): """Pack Configuration Values""" return PKCFG(self[0x7c])

@property def IMLC(self): """Initial Max Load Current (mA)""" return self[0x7d] * 456.96 / RS

@property def DCOMP(self): """Discharge Rate Compensation Constants""" return DCOMP(self[0x7e])

@property def TCOMP(self): """Temperature Compensation Constants""" return TCOMP(self[0x7f])

@property def ID(self): """Identification Bytes""" return struct.pack("3B", *self[0x7f:0x7c:-1])

class Bq27kAuto(Bq27kData): def __init__(self, grace=5): if grace < 5: raise ValueError("grace must be at least 5 seconds") self.grace = grace self._cache = [0] * 128 self.refresh(refresh_all=True, force=True)

def __getitem__(self, item): return self._cache[item]

def wait(self, minimum=0, refresh_all=False): time.sleep(max(self._expire - time.time, minimum)) self.refresh(refresh_all=refresh_all, force=True)

def refresh(self, refresh_all=False, force=False): if not force and time.time < self._expire: return if refresh_all: self._read_all else: self._read_ram self._expire = time.time + self.grace

def _read_all(self, stop=0x80): for line in open("/sys/class/power_supply/bq27200-0/registers", "rt"): addr, _, val = line.strip.partition("=") addr, val = int(addr, base=16), int(val, base=16) if addr >= stop: break self._cache[addr] = val

def _read_ram(self): self._read_all(stop=0x2d)

class Bq27kList(list, Bq27kData): """A list with data descriptors to convert bq27k register data."""

def read_registers(path="/sys/class/power_supply/bq27200-0/registers"): reg = [0] * 128 for line in open(path, "rt"): addr, _, val = line.strip.partition("=") addr, val = int(addr, base=16), int(val, base=16) reg[addr] = val return reg

def word(pair): return pair[1] << 8 | pair[0]

if __name__ == "__main__": import sys if len(sys.argv) > 1: RS = int(sys.argv[1]) reg = Bq27kAuto mode, flags, pkcfg = reg.MODE, reg.FLAGS, reg.PKCFG print("Sense resistance (RS): %s mohm" % RS) print("Device Control Register (CTRL): 0x%02x" % reg.CTRL) print("Device Mode Register (MODE):") print("   Ship mode (SHIP): %s" % mode.SHIP) print("   Full reset (FRST): %s" % mode.FRST) print("   Power on Reset (POR): %s" % mode.POR) print("   Partial reset (PRST): %s" % mode.PRST) print("   Write LMD to NAC (DONE): %s" % mode.DONE) print("   Write AR to NAC (WRTNAC): %s" % mode.WRTNAC) print("   GPIO pin (GPSTAT): %s" % mode.GPSTAT) print("   State of the GPIO pin (GPIEN): %s" % mode.GPIEN) print("   Calibrate internal offset (CIO): %s" % mode.CIO) print("   Calibrate external offset (CEO): %s" % mode.CEO) print("At-Rate (AR): %s mA" % reg.AR) print("At-Rate Time-to-Empty (ARTTE): %s min" % reg.ARTTE) print("Reported Temperature (TEMP): %s C" % reg.TEMP) print("Reported Voltage (VOLT): %s mV" % reg.VOLT) print("Status Flags (FLAGS):") print("   Final End-of-Discharge-Voltage (EDVF): %s" % flags.EDVF) print("   First End-of-Discharge-Voltage (EDV1): %s" % flags.EDV1) print("   Valid Discharge (VDQ): %s" % flags.VDQ) print("   Calibration-In-Progress (CALIP): %s" % flags.CALIP) print("   Capacity Inaccurate (CI): %s" % flags.CI) print("   Li-Ion taper current detection (IMIN): %s" % flags.IMIN) print("   No Activity (NOACT): %s" % flags.NOACT) print("   Charge State (CHGS): %s" % flags.CHGS) print("Relative State-of-Charge (RSOC): %s%%" % reg.RSOC) print("Nominal Available Capacity (NAC): %s mAh" % reg.NAC) print("Discharge Compensated NAC (CACD): %s mAh" % reg.CACD) print("Temperature Compensated CACD (CACT): %s mAh" % reg.CACT) print("Last Measured Discharge (LMD): %s mAh" % reg.LMD) print("Average Current (AI): %s mA" % reg.AI) print("Time-to-Empty (TTE): %s min" % reg.TTE) print("Time-to-Full (TTF): %s min" % reg.TTF) print("Standby Current (SI): %s mA" % reg.SI) print("Standby Time-to-Empty (STTE): %s min" % reg.STTE) print("Max Load Current (MLI): %s mA" % reg.MLI) print("Max Load Time-to-Empty (MLTTE): %s min" % reg.MLTTE) print("Available Energy (SAE): %s mWh" % reg.SAE) print("Average Power (AP): %s mW" % reg.AP) print("Time-to-Empty At Constant Power (TTECP): %s min" % reg.TTECP) print("Cycle Count Since Learning Cycle (CYCL): %s cycles" % reg.CYCL) print("Cycle Count Total (CYCT): %s cycles" % reg.CYCT) print("Compensated State-of-Charge (CSOC): %s%%" % reg.CSOC) print("Calibration Result: %s uV" % reg.CRES) print("EEPROM Program Enable (EE_EN): 0x%02x" % reg.EE_EN) print("Initial Last Measured Discharge (ILMD): %s mAh" % reg.ILMD) print("EDVF Threshold (EDVF): %s mV" % reg.EDVF) print("EDV1 Threshold (EDV1): %s mV" % reg.EDV1) print("Initial Standby Load Current (ISLC): %s mA" % reg.ISLC) print(       "Digital Magnitude Filter and Self-Discharge Rate Constants (DMFSD):") print("   Self-Discharge Rate (SD): %s%%" % reg.DMFSD.SD) print("   Digital Magnitude Filter (DMF): %s mA" % reg.DMFSD.DMF) print(       "Aging Estimate Enable, Charge Termination Taper Current (TAPER):") print("   Charge Termination Taper Current (TAPER): %s mA"          % reg.TAPER.TAPER) print("   Aging Estimate Enable (AEE): %s" % reg.TAPER.AEE) print("Pack Configuration Values (PKCFG):") print("   Fixed temperature compensation (TCFIX): %s" % pkcfg.TCFIX) print("   Fixed discharge compensation (DCFIX): %s" % pkcfg.DCFIX) print("   Board offset value (BOFF): %s uV" % pkcfg.BOFF) print("   Qualification voltage for charge termination (QV): %s mV"          % pkcfg.QV) print("   State of the GPIO pin on initial power up (GPIEN): %s"          % pkcfg.GPIEN) print("Initial Max Load Current (IMLC): %s mA" % reg.IMLC) print("Discharge Rate Compensation Constants (DCOMP):") print("   Discharge rate compensation threshold (DCOFF): %s"          % reg.DCOMP.DCOFF) print("   Discharge rate compensation gain (DCGN): %s%%"          % reg.DCOMP.DCGN) print("Temperature Compensation Constants (TCOMP):") print("   Temperature compensation offset (TOFF): %s C"          % reg.TCOMP.TOFF) print("   Temperature compensation gain (TCGN): %s%%"          % reg.TCOMP.TCGN) print("Identification Bytes (ID): %r" % reg.ID)