Source code for i2c_volt

import time,numpy as np,logging,struct
from i2c import I2C_DEVICE

logger = logging.getLogger(__name__)

[docs]class LTC2990(): """ Quad I2C Voltage, Current and Temperature Monitor """ DICT = dict() DICT[0x00] = { 'VCCREADY' : 0b1 << 6, 'V4READY' : 0b1 << 5, 'V3READY' : 0b1 << 4, 'V2READY' : 0b1 << 3, 'V1READY' : 0b1 << 2, 'TINTREADY' : 0b1 << 1, 'BUSY' : 0b1 << 0, } DICT[0x01] = { 'TEMPERATUREFORMAT' : 0b1 << 7, 'REPEATSINGLE' : 0b1 << 6, 'MODE1' : 0b11 << 3, 'MODE0' : 0b111 << 0, } DICT[0x02] = { 'TRIGGER' : 0b11111111 << 0,} DICT[0x04] = { 'TINTMSB' : 0b11111111 << 0,} DICT[0x05] = { 'TINTLSB' : 0b11111111 << 0,} DICT[0x06] = { 'V1MSB' : 0b11111111 << 0,} DICT[0x07] = { 'V1LSB' : 0b11111111 << 0,} DICT[0x08] = { 'V2MSB' : 0b11111111 << 0,} DICT[0x09] = { 'V2LSB' : 0b11111111 << 0,} DICT[0x0a] = { 'V3MSB' : 0b11111111 << 0,} DICT[0x0b] = { 'V3LSB' : 0b11111111 << 0,} DICT[0x0c] = { 'V4MSB' : 0b11111111 << 0,} DICT[0x0d] = { 'V4LSB' : 0b11111111 << 0,} DICT[0x0e] = { 'VCCMSB' : 0b11111111 << 0,} DICT[0x0f] = { 'VCCLSB' : 0b11111111 << 0,} MSB = dict() MSB['TEMP'] = { 'DV' : 0B1 << 7, 'SS' : 0B1 << 6, 'SO' : 0B1 << 5, 'DATA' : 0b111111 << 0, } MSB['VOLT'] = { 'DV' : 0B1 << 7, 'DATA' : 0b1111111 << 0, } MODE0={ 0:['v1', 'v2', 'tr2', 'tr2'], 1:['v1-v2', 'v1-v2', 'tr2', 'tr2'], 2:['v1-v2', 'v1-v2', 'v3', 'v4'], 3:['tr1', 'tr1', 'v3', 'v4'], 4:['tr1', 'tr1', 'v3-v4', 'v3-v4'], 5:['tr1', 'tr1', 'tr2', 'tr2'], 6:['v1-v2', 'v1-v2', 'v3-v4', 'v3-v4'], 7:['v1', 'v2', 'v3', 'v4'], } CDIFFERENTIAL = 19.42e-6 CSINGLEENDED = 305.18e-6 VCCBIAS = 2.5 TEMPFACTOR = 16.0 fmt = 0 # celsius -> 0, kelvin -> 1 repeat = 1 # repeat -> 0, single -> 1 mode1 = 0b11 # mode1 -> mode[4:3], 0b11 for all measurements per mode[2:0] mode0 = 0b0 # mode0 -> mode[2:0], 0b0 for V1, V2, TR2
[docs] def __init__(self, itf, addr=0x4f): self.itf=itf self.addr=addr
[docs] def init(self,fmt='celsius',repeat=False,mode1=3,mode0=7): """ Initialise LTC2990 +-------+----------------------+ | mode0 | Description | +-------+----------------------+ | 0 | V1, V2, TR2(Default) | +-------+----------------------+ | 1 | V1-V2, TR2 | +-------+----------------------+ | 2 | V1-V2, V3, V4 | +-------+----------------------+ | 3 | TR1, V3, V4 | +-------+----------------------+ | 4 | TR1, V3-V4 | +-------+----------------------+ | 5 | TR1, TR2 | +-------+----------------------+ | 6 | V1-V2, V3-V4 | +-------+----------------------+ | 7 | V1, V2, V3, V4 | +-------+----------------------+ +-------+-------------------------------------+ | mode1 | Description | +-------+-------------------------------------+ | 0 | Internal Temperature Only (Default) | +-------+-------------------------------------+ | 1 | TR1, V1 or V1-V2 Only per Mode[2:0] | +-------+-------------------------------------+ | 2 | TR2, V3 or V3-V4 Only per Mode[2:0] | +-------+-------------------------------------+ | 3 | All Measurements per Mode[2:0] | +-------+-------------------------------------+ """ if fmt not in ['celsius','kelvin']: logger.error('Invalid parameter') raise ValueError("Invalid parameter") if repeat not in [False,True]: logger.error('Invalid parameter') raise ValueError("Invalid parameter") if mode1 not in range(4): logger.error('Invalid parameter') raise ValueError("Invalid parameter") if mode0 not in range(8): logger.error('Invalid parameter') raise ValueError("Invalid parameter") self.fmt = 0 if fmt == 'celsius' else 1 self.repeat = 0 if repeat == 1 else 1 self.mode1 = mode1 self.mode0 = mode0 rid, mask = self._getMask(self.DICT, 'TEMPERATUREFORMAT') val = self._set(0x0, self.fmt, mask) rid, mask = self._getMask(self.DICT, 'REPEATSINGLE') val = self._set(val, self.repeat, mask) rid, mask = self._getMask(self.DICT, 'MODE1') val = self._set(val, self.mode1, mask) rid, mask = self._getMask(self.DICT, 'MODE0') val = self._set(val, self.mode0, mask) self.write(rid, val)
def _set(self, d1, d2, mask=None): # Update some bits of d1 with d2, while keep other bits unchanged if mask: d1 = d1 & ~mask d2 = d2 * (mask & -mask) return d1 | d2 def _get(self, data, mask): data = data & mask return data / (mask & -mask) def _getMask(self, dicts, name): for rid in dicts: if name in dicts[rid]: return rid, dicts[rid][name] return None,None
[docs] def write(self,reg=None,data=None): self.itf.write(self.addr,reg,data)
[docs] def read(self,reg=None,length=1): return self.itf.read(self.addr,reg,length)
[docs] def readTemp(self): name='TINT' self.setWord('TRIGGER',0xff) cnt=0 while self.getStatus('BUSY'): cnt+=1 time.sleep(0.01) if cnt>10: msg = "Voltage sensor at address {} failed to read its temperature!".format(hex(self.addr)) logger.warning(msg) return float('nan') msb = self.getWord(name+'MSB') lsb = self.getWord(name+'LSB') data = dict() data['DV'] = self._get(msb,self.MSB['TEMP']['DV']) data['SS'] = self._get(msb,self.MSB['TEMP']['SS']) data['SO'] = self._get(msb,self.MSB['TEMP']['SO']) value = self._get(msb,self.MSB['TEMP']['DATA']) << 8 | lsb data['DATA'] = value / self.TEMPFACTOR return data
[docs] def readVolt(self,name): """ Read Voltage Please switch to corresponding modes using init() before measuring voltage. Possible options are: * vcc * v1 * v2 * v3 * v4 * v1-v2 * v3-v4 .. code-block:: python readVolt('v1-v2') """ name = name.lower() if name not in self.MODE0[self.mode0]+['vcc']: logger.error('Invalid parameter') raise ValueError("Invalid parameter") if name != 'vcc': regs = ['V1', 'V2', 'V3', 'V4'] reg = regs[self.MODE0[self.mode0].index(name)] else: reg = 'VCC' self.setWord('TRIGGER',0xff) cnt=0 while self.getStatus('BUSY'): cnt+=1 time.sleep(0.01) if cnt>10: msg = "Voltage sensor at address {} failed to read {}!".format(hex(self.addr),name) logger.warning(msg) return float('nan') msb = self.getWord(reg+'MSB') lsb = self.getWord(reg+'LSB') value = self._get(msb,self.MSB['VOLT']['DATA']) << 8 | lsb value = -(value ^ 0x7fff) - 1 if value & 0x4000 else value if name in ['vcc']: data = value * self.CSINGLEENDED + self.VCCBIAS elif name in ['v1-v2','v3-v4']: data = value * self.CDIFFERENTIAL elif name in ['v1','v2','v3','v4']: data = value * self.CSINGLEENDED return data
[docs] def getStatus(self,name=None): if name not in self.DICT[0].keys() + [None]: logger.error('Invalid parameter') raise ValueError("Invalid parameter") data = self.read(0x0) if name == None: return dict([(n,self._get(data,m)) for (n,m) in self.DICT[0x0].items()]) else: return self._get(data,self.DICT[0x0][name])
[docs] def getRegister(self,rid=None): if rid==None: return dict([(regId,self.getRegister(regId)) for regId in self.DICT]) elif rid in self.DICT: rval = self.read(rid) return {name: self._get(rval,mask) for name, mask in self.DICT[rid].items()} else: logger.error('Invalid parameter') raise ValueError("Invalid parameter")
[docs] def getWord(self,name): rid, mask = self._getMask(self.DICT, name) return self._get(self.read(rid),mask)
[docs] def setWord(self,name,value): rid, mask = self._getMask(self.DICT, name) if mask == 0xff: data = self._set(0x0,value,mask) self.write(rid,data) else: data = self.read(rid) data = self._set(data,value,mask) self.write(rid,data)
[docs]class INA219(): """ INA219 Zero-Drift, Bidirectional Current/Power Monitor With I2C Interface """ DICT = dict() DICT[0x00] = { 'configuration' : 0xffff << 0, 'RST' : 0b1 << 15, 'BRNG' : 0b1 << 13, 'PG' : 0b11 << 11, 'BADC' : 0b1111 << 7, 'SADC' : 0b1111 << 3, 'MODE' : 0b111 << 0,} DICT[0x01] = { 'shuntvoltage' : 0xffff << 0,} DICT[0x02] = { 'busvoltage' : 0xffff << 0, 'BD' : 0b1111111111111 << 3, 'CNVR' : 0b1 << 1, 'OVF' : 0b1 << 0, } DICT[0x03] = { 'power' : 0xffff << 0,} DICT[0x04] = { 'current' : 0xffff << 0,} DICT[0x05] = { 'calibration' : 0xffff << 0,} BRNG = {16:0, 32:1,} PG = { 40:0b00, 80:0b01, 160:0b10, 320:0b11,} ADC = { '9b' : 0b0000, '10b' : 0b0001, '11b' : 0b0010, '12b' : 0b0011, 1 : 0b1000, 2 : 0b1001, 4 : 0b1010, 8 : 0b1011, 16 : 0b1100, 32 : 0b1101, 64 : 0b1110, 128 : 0b1111, }
[docs] def __init__(self, itf, addr=0x45): self.itf=itf self.addr=addr
[docs] def init(self,brng=16,pg=320,badc=128,sadc=128,mode=0b011): """ Initialise INA219 Mode, available options: MODE3 MODE2 MODE1 MODE 0 0 0 Power-down 0 0 1 Shunt voltage, triggered 0 1 0 Bus voltage, triggered 0 1 1 Shunt and bus, triggered 1 0 0 ADC off (disabled) 1 0 1 Shunt voltage, continuous 1 1 0 Bus voltage, continuous 1 1 1 Shunt and bus, continuous BRNG, Bus voltage range, available options: 16,32 PG, for choosing the full scale range. Available options: 40, 80, 160, 320, ADC, for bus voltage as well as shunt voltage measurement. Available options: Do one sample of the following resolution '9b', '10b', '11b', '12b', or do 12bit resolution and average over the following number of samples: 1, 2, 4, 8, 16, 32, 64, 128, """ if brng not in self.BRNG: logger.error('Invalid parameter') raise ValueError("Invalid parameter") if pg not in self.PG: logger.error('Invalid parameter') raise ValueError("Invalid parameter") if badc not in self.ADC: logger.error('Invalid parameter') raise ValueError("Invalid parameter") if sadc not in self.ADC: logger.error('Invalid parameter') raise ValueError("Invalid parameter") if mode not in range(8): logger.error('Invalid parameter') raise ValueError("Invalid parameter") rid, mask = self._getMask(self.DICT, 'BRNG') val = self._set(0x0, self.BRNG[brng], mask) rid, mask = self._getMask(self.DICT, 'PG') val = self._set(val, self.PG[pg], mask) rid, mask = self._getMask(self.DICT, 'BADC') val = self._set(val, self.ADC[badc], mask) rid, mask = self._getMask(self.DICT, 'SADC') val = self._set(val, self.ADC[sadc], mask) rid, mask = self._getMask(self.DICT, 'MODE') val = self._set(val, mode, mask) self.write(rid, val)
def _set(self, d1, d2, mask=None): # Update some bits of d1 with d2, while keep other bits unchanged if mask: d1 = d1 & ~mask d2 = d2 * (mask & -mask) return d1 | d2 def _get(self, data, mask): data = data & mask return data / (mask & -mask) def _getMask(self, dicts, name): for rid in dicts: if name in dicts[rid]: return rid, dicts[rid][name] return None,None
[docs] def write(self,reg=None,data=None): self.itf.write(self.addr,reg,[data>>8,data&0xff])
[docs] def read(self,reg=None,length=2): msb, lsb = self.itf.read(self.addr,reg,length) return (msb << 8) | lsb
[docs] def readVolt(self,name): """ Read Voltage Please switch to corresponding modes using init() before measuring voltage. Possible options are: 'shunt' 'bus' E.g. readVolt('shunt') """ name = name.lower() if name not in ['shunt','bus']: logger.error('Invalid parameter') raise ValueError("Invalid parameter") # trigger conf = self.getWord('configuration') self.setWord('configuration',conf) # check availability cnt=0 while not self.getStatus('CNVR'): cnt+=1 time.sleep(0.01) if cnt>10: msg = "Voltage sensor at address {} reading timeout!".format(hex(self.addr)) logger.warning(msg) return float('nan') # read and interpret if name == 'shunt': val = self.getWord('shuntvoltage') val = -1 * (~val + 1) if val & 0x8000 else val return val * 10.e-6 else: # name == 'bus': val = self.getWord('BD') return val * 4.e-3
[docs] def getStatus(self,name='CNVR'): if name not in ['CNVR','OVF']: logger.error('Invalid parameter') raise ValueError("Invalid parameter") return self.getWord(name)
[docs] def getRegister(self,rid=None): if rid==None: return dict([(regId,self.getRegister(regId)) for regId in self.DICT]) elif rid in self.DICT: rval = self.read(rid) return {name: self._get(rval,mask) for name, mask in self.DICT[rid].items()} else: logger.error('Invalid parameter') raise ValueError("Invalid parameter")
[docs] def getWord(self,name): rid, mask = self._getMask(self.DICT, name) return self._get(self.read(rid),mask)
[docs] def setWord(self,name,value): rid, mask = self._getMask(self.DICT, name) if mask == 0xffff: data = self._set(0x0,value,mask) self.write(reg=rid,data=data) else: data = self.read(rid) data = self._set(data,value,mask) self.write(reg=rid, data=data)
[docs]class MAX11644(I2C_DEVICE): LSB = 4.096/(2**12)
[docs] def __init__(self, itf, addr=0x36): super(MAX11644, self).__init__(itf, addr) self.DICT[0x00] = { 'setup' : 0xff << 0, 'REG' : 0b1 << 7, 'SEL' : 0b111 << 4, 'CLK' : 0b1 << 3, 'BIP' : 0b1 << 2, 'RST' : 0b1 << 1, 'SCAN' : 0b11 << 5, 'CS' : 0b1111 << 1, 'SGL' : 0b1 << 0, 'config' : 0xff << 0, }
[docs] def init(self, **kwargs): self.reset() _reg = 0x1 # setup _sel = 0b101 # internal reference _clk = 0x0 # internal clock _bip = 0x0 # unipolar _rst = 0x1 # no action if 'sel' in kwargs: _sel = str2int(kwargs['sel']) if 'clk' in kwargs: _clk = str2int(kwargs['clk']) if 'bip' in kwargs: _bip = str2int(kwargs['bip']) val = self._set(0x0, _reg, self.DICT[0x0]['REG']) val = self._set(val, _sel, self.DICT[0x0]['SEL']) val = self._set(val, _clk, self.DICT[0x0]['CLK']) val = self._set(val, _bip, self.DICT[0x0]['BIP']) val = self._set(val, _rst, self.DICT[0x0]['RST']) self.write(data=val) _reg = 0x0 # config _scan= 0b11 # Converts the input selected by CS0 _cs = 0x1 # select AIN1 after scaning AIN0 _sgl = 0x1 # single-ended if 'scan' in kwargs: _scan = str2int(kwargs['scan']) if 'cs' in kwargs: _cs = str2int(kwargs['cs']) if 'sgl' in kwargs: _sgl = str2int(kwargs['sgl']) val = self._set(0x0, _reg, self.DICT[0x0]['REG']) val = self._set(val, _scan,self.DICT[0x0]['SCAN']) val = self._set(val, _cs, self.DICT[0x0]['CS']) val = self._set(val, _sgl, self.DICT[0x0]['SGL']) self._config = val
[docs] def reset(self): self.write(data=0x80)
[docs] def readVolt(self, name=None): """ Read voltage Possible options are AIN0 AIN1 """ if name != None and name.upper() not in ['AIN0','AIN1']: raise ValueError('Invalid parameter {}'.format(name)) if name == None: ain0 = self.readVolt(name='AIN0') ain1 = self.readVolt(name='AIN1') return ain0, ain1 else: _cs = 0x0 if name.upper() == 'AIN0' else 0x1 val = self._set(self._config, _cs, self.DICT[0x0]['CS']) self.write(data=val) MASK = 0x0f d0 = self.read(length=2) ain = (((d0[0] & MASK) << 8) | d0[1]) * self.LSB return ain
[docs]def str2int(s): if s.startswith('0b'): val=int(s,2) elif s.startswith('0x'): val=int(s,16) else: val=int(s) return val