User:Kerio/bq27k.py
Line 1: | Line 1: | ||
This script provides details regarding bq27k registers; it also doubles as a python library to access bq27k registers in a programmatic way. | This script provides details regarding bq27k registers; it also doubles as a python library to access bq27k registers in a programmatic way. | ||
+ | |||
+ | As it is, it will only work if the bq27x00_battery module is loaded; i.e. if Pali's BME replacement is installed. It should be possible to change Bq27kAuto._read_ram() and Bq27kAuto._read_all(), even by subclassing, to allow for other ways to get the bq27k register data. | ||
<pre>#!/usr/bin/env python | <pre>#!/usr/bin/env python |
Latest revision as of 19:22, 22 April 2013
This script provides details regarding bq27k registers; it also doubles as a python library to access bq27k registers in a programmatic way.
As it is, it will only work if the bq27x00_battery module is loaded; i.e. if Pali's BME replacement is installed. It should be possible to change Bq27kAuto._read_ram() and Bq27kAuto._read_all(), even by subclassing, to allow for other ways to get the bq27k register data.
#!/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)
- This page was last modified on 22 April 2013, at 19:22.
- This page has been accessed 413 times.