From 83a46cd65785406d4a69924bc8f91961cbde42ef Mon Sep 17 00:00:00 2001 From: Dan White Date: Tue, 16 Oct 2012 15:19:21 -0500 Subject: [PATCH] Devboard module to handle everything hardware-specific --- python-lib/devboard.py | 312 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 python-lib/devboard.py diff --git a/python-lib/devboard.py b/python-lib/devboard.py new file mode 100644 index 0000000..a0f4b5a --- /dev/null +++ b/python-lib/devboard.py @@ -0,0 +1,312 @@ +#!/usr/bin/env python + +import usbio + + +# +# devboard conversion constants +# +#VREF = 2.5 #output of DAC Vref generator + +# Supply current sense circuitry +RS = { 'Vdd_ns430': 1.0, + 'DVdd_ns430': 100.0, + 'Vdd_digi': 1.0, + 'AVdd_atoi': 20.0 } + +IPS_CH = { 'Vdd_ns430': 0, + 'DVdd_ns430': 1, + 'Vdd_digi': 0, + 'AVdd_atoi': 1 } + +PGA = { 'Vdd_ns430': 1.0, + 'DVdd_ns430': 1.0, + 'Vdd_digi': 1.0, + 'AVdd_atoi': 1.0 } + +GM = { 'Vdd_ns430': 5e-6/1e-3, + 'DVdd_ns430': 5e-6/1e-3, + 'Vdd_digi': 5e-6/1e-3, + 'AVdd_atoi': 5e-6/1e-3 } + +RG = { 'Vdd_ns430': 10e3, + 'DVdd_ns430': 10e3, + 'Vdd_digi': 10e3, + 'AVdd_atoi': 10e3 } + + + +def count2volt(c, warn=True): + """Properly handle 14-b data from 12b ADC value. Average bits + originate from 12b samples, so max value is 0xfff0 NOT 0xfffc. + """ + if warn and c >= 0xfff0: + print 'WARNING: clipped ADC value' + return dac.VREF * (((c >> 0) / 16.0) / 2**12) + + +def ips(name, count, warn=True): + """For a given measurement name, take the ADC count and return the + corresponding supply current in A. + """ + return ((count2volt(count, warn) / PGA[name]) / + (GM[name] * RG[name] * RS[name])) + + +def isupply_resolution(printit=False): + """Return the supply current maximum measurable values and LSB + resolutions. Optionally print to stdout. + """ + r = {} + if printit: + print + print 'PS current sense max / resolution:' + for name in RS.keys(): + imax = ips(name, 0x10000, False) + ires = imax/2**12 + r[name] = [imax, ires] + if printit: + print 'Max current: %-10s %8.2f uA / %5.2f uA' % ( + name, 1e6*imax, 1e6*ires) + + +def meas_isupply(): + """Return a dict of measured power supply currents. + """ + result = {} + + # ensure IRQ pin is an input + PIN_IRQ = 2 + i2c.set_pindir(i2c.io & ~(1 << PIN_IRQ)) + + # ADC global setup + adc.reset() + adc.triggerMode(adc.MODE_IDLE) + adc.channelMode(0, adc.SE) + adc.average(16) + adc.convst_spi(1) + + # 430-specific settings + for name in ('Vdd_ns430', 'DVdd_ns430'): + adc.channelGain(IPS_CH[name], PGA[name]) + + # select 430 outputs, give time to settle + vatoi.gpo1 = 0 + v430.gpo1 = 1 + sleep(1e-3) + + # convert the two 430 results + adc.triggerMode(adc.MODE_MANUAL_MANUAL) + for name in ('Vdd_ns430', 'DVdd_ns430'): + adc.mux(IPS_CH[name]) + adc.read() + result[name] = ips(name, adc.read()) + + # atoi-specific settings + for name in ('Vdd_digi', 'AVdd_atoi'): + adc.channelGain(IPS_CH[name], PGA[name]) + + # select atoi outputs, give time to settle + v430.gpo1 = 0 + vatoi.gpo1 = 1 + sleep(1e-3) + + #get the two results + for name in ('Vdd_digi', 'AVdd_atoi'): + adc.mux(IPS_CH[name]) + adc.read() + result[name] = ips(name, adc.read()) + + return result + + +def dump_config(): + """Return a hierarchical dict of settings appropriate for passing to the + individual constructors. + """ + s = {} + # configuration + # chip SPI config + s['chain'] = chain.dump_config() + s['mux'] = mux.dump_config() + s['arb'] = arb.dump_config() + s['amux'] = amux.dump_config() + # power supply / bias Rpots + s['vatoi'] = vatoi.dump_config() + s['v430'] = v430.dump_config() + s['ibias'] = ibias.dump_config() + # interface busses + s['spi0'] = spi0.dump_config() + s['spi1'] = spi1.dump_config() + s['i2c'] = i2c.dump_config() + + # current information + s['date'] = dt.now().strftime('%Y-%m-%d_%H:%M:%S') + s['isupply'] = meas_isupply() + return s + + +def save_config(name=None): + """Write current config to a file. + """ + cfg = dump_config() + if name is None: + name = 'config.%s.yaml' % cfg['date'] + yaml.dump(cfg, open(name, 'wb')) + return cfg + + +# +# Module-level names. Define here so they are found in the module scope +# +spi0 = None +spi1 = None +i2c = None +ibias = None +vatoi = None +v430 = None +dac = None +adc = None +chain = None +mux = None +arb = None +amux = None + +def init_devboard(name='devboard-defaults.yaml'): + try: + load_config(name) + return + except: + print 'WARNING: could not find "%s", using built-in defaults' % name + + # No defaults available, fallback on object-level defaults + psdefaults() + + ############################################################################## + # Setup FTDI serial ports + spi0 = usbio.AtoiSPI0(1000e3) #port A + spi1 = usbio.AtoiSPI1(1000e3) #port B + i2c = usbio.I2C(interface=usbio.ftdi.INTERFACE_C, scl=0, sda=1, + vid=0x0403, pid=0x6011, delay=100e-6) + + ############################################################################## + # Analog bias + # (bias generator also uses DAC_1 and DAC_3, + # need to make a wrapper class to do e.g. ibias.buf = 10 uA) + ibias = usbio.AD524x(i2c, 0) + + ############################################################################## + # Power supplies + vatoi = usbio.DigiReg(i2c, 2, (0.5015, 1.3887), (1.9552, 2.8304)) + vatoi.alias('va', 'vdigi') + vatoi.alias('vb', 'avdd') + + v430 = usbio.DigiReg(i2c, 3, (1.9535, 2.7137), (0.5013, 1.2617)) + v430.alias('va', 'dvdd') + v430.alias('vb', 'vdd') + + ############################################################################## + # DAC + # default mode is dac.INPUT_UPDATE_SINGLE + dac = usbio.DAC_atoi(spi0, 'dac') + + VCM = 1.2500 # multimeter says 0.0 mV between ref/2 and vcmi(VCM) + dac.vcmi(VCM) + dac.vina(VCM) + dac.vinb(VCM) + dac.vbias_core(000e-3) + dac.vbias_buf(000e-3) + + ############################################################################## + # ADC + # (TODO: see 'ADC testing stuff' section below) + adc = usbio.ADS8201(spi0) + + ############################################################################## + # AtoI digital SPI config + # jumper on devboard accesses MISO for each chain + chain = usbio.Chain(spi1, 'chain0_conf', length=48) + mux = usbio.Mux(spi1, 'chain0_mux') + + # MISO is not available + arb = usbio.Chain(spi1, 'chain1_conf', length=16) + amux = usbio.Mux(spi1, 'chain1_mux') + + + +def load_config(name): + """Given a dict or a filename containing a yaml dump of settings, load them + and (re-)initialize all components. + """ + if isinstance(name, dict): + cfg = name + else: + cfg = yaml.load(name) + + ########################################################################## + # Setup FTDI serial ports + spi0 = usbio.AtoiSPI0(**cfg['spi0']) + spi1 = usbio.AtoiSPI1(**cfg['spi1']) + i2c = usbio.I2C(**cfg['i2c']) + + ########################################################################## + # Analog bias + # (bias generator also uses DAC_1 and DAC_3, + # need to make a wrapper class to do e.g. ibias.buf = 10 uA) + ibias = usbio.AD524x(i2c, **cfg['ibias']) + + ########################################################################## + # Power supplies + vatoi = usbio.DigiReg(i2c, **cfg['vatoi']) + #vatoi = usbio.DigiReg(i2c, 2, (0.5015, 1.3887), (1.9552, 2.8304)) + #vatoi.alias('va', 'vdigi') + #vatoi.alias('vb', 'avdd') + + v430 = usbio.DigiReg(i2c, **cfg['vatoi']) + #v430 = usbio.DigiReg(i2c, 3, (1.9535, 2.7137), (0.5013, 1.2617)) + #v430.alias('va', 'dvdd') + #v430.alias('vb', 'vdd') + + + ########################################################################## + # DAC + # default mode is dac.INPUT_UPDATE_SINGLE + dac = usbio.DAC_atoi(spi0, **cfg['dac']) + #VCM = 1.2500 # multimeter says 0.0 mV between ref/2 and vcmi(VCM) + #dac.vcmi(VCM) + #dac.vina(VCM) + #dac.vinb(VCM) + #dac.vbias_core(000e-3) + #dac.vbias_buf(000e-3) + + ########################################################################## + # ADC + # (TODO: see 'ADC testing stuff' section below) + adc = usbio.ADS8201(spi0) + + ########################################################################## + # AtoI digital SPI config + # jumper on devboard accesses MISO for each chain + chain = usbio.Chain(spi1, **cfg['chain']) + mux = usbio.Mux(spi1, **cfg['mux']) + #chain = usbio.Chain(spi1, 'chain0_conf', length=48) + #mux = usbio.Mux(spi1, 'chain0_mux') + + # MISO is not available + arb = usbio.Chain(spi1, **cfg['arb']) + amux = usbio.Mux(spi1, **cfg['amux']) + #arb = usbio.Chain(spi1, 'chain1_conf', length=16) + #amux = usbio.Mux(spi1, 'chain1_mux') + + + +# define power supply defaults +def psdefaults(): + vatoi.vdigi = 1.2 + v430.vdd = 1.2 + vatoi.avdd = 2.5 + v430.dvdd = 2.5 + + + + -- 2.25.1