Devboard module to handle everything hardware-specific
authorDan White <dan@whiteaudio.com>
Tue, 16 Oct 2012 20:19:21 +0000 (15:19 -0500)
committerDan White <dan@whiteaudio.com>
Tue, 16 Oct 2012 20:37:07 +0000 (15:37 -0500)
python-lib/devboard.py [new file with mode: 0644]

diff --git a/python-lib/devboard.py b/python-lib/devboard.py
new file mode 100644 (file)
index 0000000..a0f4b5a
--- /dev/null
@@ -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
+
+
+
+