diff options
author | Nick Sanders <nsanders@chromium.org> | 2018-02-05 18:50:12 -0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-03-28 19:34:27 -0700 |
commit | 2da18f938d64462ac62f6a77f46d05237ed30096 (patch) | |
tree | c8382814f7bb025a8f708bf6455bd2517f74e1d3 /extra | |
parent | bb1a079a628cc2a53c4c282da41692f3f29b0a3d (diff) | |
download | chrome-ec-2da18f938d64462ac62f6a77f46d05237ed30096.tar.gz |
sweetberry: add current and voltage logging
This adds a config to specify whether sweetberry
should measure power, current, voltage per powerlog entry.
The json format is slightly revised to allow data type
per channel. powerlog and sweetberry fw are updated
to handle the new functionality.
BUG=b:72973433
BRANCH=None
TEST=./powerlog.py -b marlin.board -c marlin_a.scenario -s .5
Signed-off-by: Nick Sanders <nsanders@chromium.org>
Change-Id: I231fc6600495146fad30583872bf14c660d5a50b
Reviewed-on: https://chromium-review.googlesource.com/905427
Commit-Ready: Nick Sanders <nsanders@chromium.org>
Tested-by: Nick Sanders <nsanders@chromium.org>
Reviewed-by: Mengqi Guo <mqg@chromium.org>
Diffstat (limited to 'extra')
-rw-r--r-- | extra/usb_power/board.README | 7 | ||||
-rw-r--r-- | extra/usb_power/board/marlin/marlin_pvc.scenario | 1 | ||||
-rw-r--r-- | extra/usb_power/marlin_v.scenario | 1 | ||||
-rwxr-xr-x | extra/usb_power/powerlog.py | 130 |
4 files changed, 112 insertions, 27 deletions
diff --git a/extra/usb_power/board.README b/extra/usb_power/board.README index b898e16540..41c30c8e38 100644 --- a/extra/usb_power/board.README +++ b/extra/usb_power/board.README @@ -45,10 +45,16 @@ Scenario files: Scenario files contain the set of rails to monitor in this session. The file format is simply a list of rail names from the board file. +Optionally, you can specify the type of measurement, from the set of +"POWER", "BUSV", "CURRENT", "SHUNTV". If not specified, the default is +power. + example.scenario: [ "railname", "another_railname", +["railname", "BUSV"], +["railname", "CURRENT"], ... ] @@ -82,6 +88,7 @@ VBAT uW: microwatt reading from this rail, generated on the INA voltage. ... uW: Further microwatt entry columns for each rail specified in your scenario file. +... xX: Measurement in uW, mW, mV, uA, uV as per config. Calculate stats and store data and stats: diff --git a/extra/usb_power/board/marlin/marlin_pvc.scenario b/extra/usb_power/board/marlin/marlin_pvc.scenario new file mode 100644 index 0000000000..426cd1479c --- /dev/null +++ b/extra/usb_power/board/marlin/marlin_pvc.scenario @@ -0,0 +1 @@ +["VBAT", ["VBAT", "BUSV"], ["VBAT", "CURRENT"], ["VBAT", "SHUNTV"]] diff --git a/extra/usb_power/marlin_v.scenario b/extra/usb_power/marlin_v.scenario new file mode 100644 index 0000000000..99c132cb27 --- /dev/null +++ b/extra/usb_power/marlin_v.scenario @@ -0,0 +1 @@ +[["VBAT", "BUSV"], ["VDD_1V8_PANEL", "BUSV"], ["V_EMMC_2V95", "BUSV"], ["V_SR_2V85", "BUSV"], ["V_USBSS_SW_1V8", "BUSV"], ["V_AUDIO_2V15", "BUSV"]] diff --git a/extra/usb_power/powerlog.py b/extra/usb_power/powerlog.py index 81d050bb91..6128ca2027 100755 --- a/extra/usb_power/powerlog.py +++ b/extra/usb_power/powerlog.py @@ -46,7 +46,19 @@ class Spower(object): _write_ep: pyUSB write endpoint for this interface """ - INA231 = 1 + # INA interface type. + INA_POWER = 1 + INA_BUSV = 2 + INA_CURRENT = 3 + INA_SHUNTV = 4 + + # usb power commands + CMD_RESET = 0x0000 + CMD_STOP = 0x0001 + CMD_ADDINA = 0x0002 + CMD_START = 0x0003 + CMD_NEXT = 0x0004 + CMD_SETTIME = 0x0005 # Map between header channel number (0-47) # and INA I2C bus/addr on sweetberry. @@ -180,7 +192,8 @@ class Spower(object): """ Clear INA description struct.""" self._inas = [] - def append_ina_struct(self, name, rs, port, addr, data=None): + def append_ina_struct(self, name, rs, port, addr, + data=None, ina_type=INA_POWER): """Add an INA descriptor into the list of active INAs. Args: @@ -189,18 +202,22 @@ class Spower(object): port: I2C channel this INA is connected to. addr: I2C addr of this INA. data: Misc data for special handling, board specific. + ina_type: INA function to use, power, voltage, etc. """ ina = {} ina['name'] = name ina['rs'] = rs ina['port'] = port ina['addr'] = addr + ina['type'] = ina_type # Calculate INA231 Calibration register # (see INA231 spec p.15) # CurrentLSB = uA per div = 80mV / (Rsh * 2^15) # CurrentLSB uA = 80000000nV / (Rsh mOhm * 0x8000) ina['uAscale'] = 80000000. / (rs * 0x8000); ina['uWscale'] = 25. * ina['uAscale']; + ina['mVscale'] = 1.25 + ina['uVscale'] = 2.5 ina['data'] = data self._inas.append(ina) @@ -265,7 +282,7 @@ class Spower(object): def send_reset(self): """Reset the power interface on the stm32""" - cmd = struct.pack("<H", 0x0000) + cmd = struct.pack("<H", self.CMD_RESET) ret = self.wr_command(cmd, rtimeout=50, wtimeout=50) debuglog("Command RESET: %s" % "success" if ret == 0 else "failure") @@ -292,7 +309,7 @@ class Spower(object): def stop(self): """Stop any active data acquisition.""" - cmd = struct.pack("<H", 0x0001) + cmd = struct.pack("<H", self.CMD_STOP) ret = self.wr_command(cmd) debuglog("Command STOP: %s" % "success" if ret == 0 else "failure") @@ -306,22 +323,23 @@ class Spower(object): Returns: actual sampling interval in ms. """ - cmd = struct.pack("<HI", 0x0003, integration_us) + cmd = struct.pack("<HI", self.CMD_START, integration_us) read = self.wr_command(cmd, read_count=5) actual_us = 0 if len(read) == 5: ret, actual_us = struct.unpack("<BI", read) - debuglog("Command START: %s %dus" % ("success" if ret == 0 else "failure", actual_us)) + debuglog("Command START: %s %dus" % ( + "success" if ret == 0 else "failure", actual_us)) else: debuglog("Command START: FAIL") return actual_us - def add_ina_name(self, name): + def add_ina_name(self, name_tuple): """Add INA from board config. Args: - name: readable name of power rail in board config. + name_tuple: name and type of power rail in board config. Returns: True if INA added, False if the INA is not on this board. @@ -329,6 +347,8 @@ class Spower(object): Raises: Exception on unexpected failure. """ + name, ina_type = name_tuple + for datum in self._brdcfg: if datum["name"] == name: channel = int(datum["channel"]) @@ -337,7 +357,7 @@ class Spower(object): if board == self._board: port, addr = self.CHMAP[channel] - self.add_ina(port, self.INA231, addr, 0, rs, data=datum) + self.add_ina(port, ina_type, addr, 0, rs, data=datum) return True else: return False @@ -350,7 +370,7 @@ class Spower(object): timestamp_us: host timestmap in us. """ # 0x0005 , 8 byte timestamp - cmd = struct.pack("<HQ", 0x0005, timestamp_us) + cmd = struct.pack("<HQ", self.CMD_SETTIME, timestamp_us) ret = self.wr_command(cmd) debuglog("Command SETTIME: %s" % "success" if ret == 0 else "failure") @@ -360,20 +380,22 @@ class Spower(object): Args: bus: which i2c bus the INA is on. Same ordering as Si2c. - ina_type: which model INA. 0x1 -> INA231 + ina_type: Ina interface: INA_POWER/BUSV/etc. addr: 7 bit i2c addr of this INA extra: extra data for nonstandard configs. resistance: int, shunt resistance in mOhm """ # 0x0002, 1B: bus, 1B:INA type, 1B: INA addr, 1B: extra, 4B: Rs - cmd = struct.pack("<HBBBBI", 0x0002, bus, ina_type, addr, extra, resistance) + cmd = struct.pack("<HBBBBI", self.CMD_ADDINA, + bus, ina_type, addr, extra, resistance) ret = self.wr_command(cmd) if ret == 0: if data: name = data['name'] else: name = "ina%d_%02x" % (bus, addr) - self.append_ina_struct(name, resistance, bus, addr, data=data) + self.append_ina_struct(name, resistance, bus, addr, + data=data, ina_type=ina_type) debuglog("Command ADD_INA: %s" % "success" if ret == 0 else "failure") def report_header_size(self): @@ -396,12 +418,12 @@ class Spower(object): """Read a line of data from the setup INAs Returns: - list of dicts of the values read, otherwise None. - [{ts:100, vbat:450}, {ts:200, vbat:440}] + list of dicts of the values read by ina/type tuple, otherwise None. + [{ts:100, (vbat, power):450}, {ts:200, (vbat, power):440}] """ try: expected_bytes = self.report_size(len(self._inas)) - cmd = struct.pack("<H", 0x0004) + cmd = struct.pack("<H", self.CMD_NEXT) bytesread = self.wr_command(cmd, read_count=expected_bytes) except usb.core.USBError as e: print("READ LINE FAILED %s" % e) @@ -455,11 +477,23 @@ class Spower(object): for i in range(0, size): idx = self.report_header_size() + 2*i - raw_w = struct.unpack("<H", data[idx:idx+2])[0] - uw = raw_w * self._inas[i]['uWscale'] name = self._inas[i]['name'] - debuglog("READ %d %s: %fs: %fuW" % (i, name, ftimestamp, uw)) - record[self._inas[i]['name']] = uw + name_tuple = (self._inas[i]['name'], self._inas[i]['type']) + + raw_val = struct.unpack("<h", data[idx:idx+2])[0] + + if self._inas[i]['type'] == Spower.INA_POWER: + val = raw_val * self._inas[i]['uWscale'] + elif self._inas[i]['type'] == Spower.INA_BUSV: + val = raw_val * self._inas[i]['mVscale'] + elif self._inas[i]['type'] == Spower.INA_CURRENT: + val = raw_val * self._inas[i]['uAscale'] + elif self._inas[i]['type'] == Spower.INA_SHUNTV: + val = raw_val * self._inas[i]['uVscale'] + + debuglog("READ %d %s: %fs: 0x%04x %f" % (i, + name, ftimestamp, raw_val, val)) + record[name_tuple] = val return record @@ -532,7 +566,7 @@ class powerlog(object): with open(cfgfile) as data_file: names = json.load(data_file) - self._names = names + self._names = self.process_scenario(names) for key in self._pwr: self._pwr[key].load_board(brdfile) @@ -562,6 +596,37 @@ class powerlog(object): else: self._pwr[key].set_time(0) + def process_scenario(self, name_list): + """Return list of tuples indicating name and type. + + Args: + json originated list of names, or [name, type] + Returns: + list of tuples of (name, type) defaulting to type "POWER" + Raises: exception, invalid INA type. + """ + names = [] + for entry in name_list: + if isinstance(entry, list): + name = entry[0] + if entry[1] == "POWER": + type = Spower.INA_POWER + elif entry[1] == "BUSV": + type = Spower.INA_BUSV + elif entry[1] == "CURRENT": + type = Spower.INA_CURRENT + elif entry[1] == "SHUNTV": + type = Spower.INA_SHUNTV + else: + raise Exception("Invalid INA type", "Type of %s [%s] not recognized," + " try one of POWER, BUSV, CURRENT" % (entry[0], entry[1])) + else: + name = entry + type = Spower.INA_POWER + + names.append((name, type)) + return names + def start(self, integration_us_request, seconds, sync_speed=.8): """Starts sampling. @@ -585,8 +650,18 @@ class powerlog(object): # CSV header if self._print_raw_data: title = "ts:%dus" % integration_us - for name in self._names: - unit = "mW" if self._use_mW else "uW" + for name_tuple in self._names: + name, ina_type = name_tuple + + if ina_type == Spower.INA_POWER: + unit = "mW" if self._use_mW else "uW" + elif ina_type == Spower.INA_BUSV: + unit = "mV" + elif ina_type == Spower.INA_CURRENT: + unit = "uA" + elif ina_type == Spower.INA_SHUNTV: + unit = "uV" + title += ", %s %s" % (name, unit) title += ", status" logoutput(title) @@ -625,10 +700,11 @@ class powerlog(object): csv = "%f" % aggregate_record["ts"] for name in self._names: if name in aggregate_record: - multiplier = 0.001 if self._use_mW else 1 - power = aggregate_record[name] * multiplier - csv += ", %.2f" % power - self._data.AddValue(name, power) + multiplier = 0.001 if (self._use_mW and + name[1]==Spower.INA_POWER) else 1 + value = aggregate_record[name] * multiplier + csv += ", %.2f" % value + self._data.AddValue(name, value) else: csv += ", " csv += ", %d" % aggregate_record["status"] |