bugfix SPI class, change API
authorDan White <dan@whiteaudio.com>
Tue, 17 Jul 2012 02:18:57 +0000 (21:18 -0500)
committerDan White <dan@whiteaudio.com>
Tue, 17 Jul 2012 02:18:57 +0000 (21:18 -0500)
For the weird SPI modes 1,3, SK must be in the opposite state than idle before
clocking in/out data.  The constructor uses a different data structure for
argument 'cs'.  A tuple of (mode, pinstate) is provided for each key, e.g.:
    {'_mask': (-1, 0xff),
     '_idle': (-1, 0xff),
     'dev1': (0, 0xf0)}

python-lib/usbio.py

index ead50e782533b899527ece5ef6345c3f59f411bf..47bf3ab7d411a9174267e246df5ee14b7f812830 100644 (file)
@@ -313,7 +313,6 @@ class SPI(object):
     MPSSE_LSB_FIRST = ftdi.MPSSE_LSB
 
     def __init__(self, interface=ftdi.INTERFACE_A, cs=None,
-                    mode=0,
                     freq=1e6,
                     vid=0x0403, pid=0x6011,
                     pindir=0xc0, pinstate=0xc0,
@@ -328,17 +327,17 @@ class SPI(object):
         Hence this class..
 
         interface = ftdi.INTERFACE_x or [1,2,3,4]
-        cs = dict specifying pin states to affect chip-select lines
+        cs = dict specifying pin states and spi modes for each device with values as
+            (mode, pinstate)
             special keys are '_idle': no-cs-asserted pin state
                              '_mask': bitmask with 1's in CS-related pin bits
+                             ^^^^^^^ for these, set 'mode'=-1
             other keys specify the pin states to assert that device's CS pin
-        mode = SPI mode [0,1,2,3]; bitfield of [CPOL, CPHA]
         pindir = initial bitfield of pin directions
         pinstate = initial pin state
         """
         self.interface = interface
         self.cs = cs
-        #defer mode until chip is init'd
         #defer freq until chip is init'd
         self.vid = vid
         self.pid = pid
@@ -367,14 +366,11 @@ class SPI(object):
         self.set_freq(freq)
         self._pindir = 0
         self._pinstate = 0
-        self._mode = mode #hack to get an initial value
         self.set_pins(pinstate, pindir)
-        self.set_mode(mode)
 
         #cs doesn't (shouldn't) change, cache the values for SPEED
         self._cs_cmd_cache = {}
-        for dev in self.cs.keys():
-            self._cs_cmd_cache[dev] = self._cs_cmd(dev)
+        self._mk_cs_cmd_cache()
 
         self.ftdi.usb_purge_buffers(self.context)
 
@@ -435,28 +431,6 @@ class SPI(object):
         """
         return self._freq
 
-    def set_mode(self, mode):
-        if mode in range(4):
-            #ensure pin 0/SK starts in its idle state in case this is a mode change
-            #yes, the check is done in set_pins(), but paranoia never hurts..
-            p = self._pinstate
-            if mode in (0, 1):
-                #cpol = 0
-                p &= 0xFE
-            else:
-                #cpol = 1
-                p |= 0x01
-            self.set_pins(p)
-            self._mode = mode
-        else:
-            raise Exception, 'Invalid mode %i, must be in range(4)' % mode
-
-    @property
-    def mode(self):
-        """Return the current SPI mode.  See set_mode() to change it.
-        """
-        return self._mode
-
     def set_pins(self, pinstate=None, pindir=None, audit_cs=True):
         """Change the GPIO pins [3..7] to the given state and direction.
         Argument values of None mean "do not change".  Audits the values to
@@ -468,7 +442,7 @@ class SPI(object):
             self._pindir = pindir
             #we are changing pin directions, audit them
             self._pindir |= 0x03 # pins 0/SK and 1/DO are outputs for sure
-            self._pindir |= self.cs['_mask'] # chip-select pins are always outputs
+            self._pindir |= self.cs['_mask'][1] # chip-select pins are always outputs
             self._pindir &= 0xFB # pin 2/DI is an input for sure
             # most implementations force pin 3/CS to an output, IT DOES NOT
             # NEED DO BE.  The top 5 pins are available for GPIO, CS, or both
@@ -477,19 +451,13 @@ class SPI(object):
 
         if pinstate is not None:
             if audit_cs:
-                self._pinstate = ((pinstate & ~self.cs['_mask']) +
-                                    (self.cs['_idle'] & self.cs['_mask']))
+                mask = self.cs['_mask'][1]
+                idle = self.cs['_idle'][1]
+                self._pinstate = ((pinstate & mask) +
+                                    (idle & mask))
             else:
                 self._pinstate = pinstate
 
-            #ensure pin 0/SK stays in its idle state, 1/DO stays as-was
-            if self.mode in (0, 1):
-                #cpol = 0
-                self._pinstate &= 0xFE
-            else:
-                #cpol = 1
-                self._pinstate |= 0x01
-
         cmd = chr(self.ftdi.SET_BITS_LOW) + chr(self._pinstate) + chr(self._pindir)
         self._raw_write(cmd)
 
@@ -516,22 +484,35 @@ class SPI(object):
         """Construct a pin-setting command which asserts the given device.  See
         __init__() for more info.
         """
-        mask = self.cs['_mask']
+        sham, mask = self.cs['_mask']
+        mode, pins = self.cs[device]
+
         p = self._pinstate & ~mask
-        p += self.cs[device] & mask
+        p += pins & mask
+
+        # ensure SK starts in the correct state
+        if mode in (0, 1):
+            p |= 0x01
+        elif mode in (2, 3):
+            p &= 0xfe
 
         self._pinstate = p
         cmd = chr(self.ftdi.SET_BITS_LOW) + chr(p) + chr(self._pindir)
         return cmd
 
-    def _write_cmd(self, data):
+    def _mk_cs_cmd_cache(self):
+        for dev in self.cs.keys():
+            self._cs_cmd_cache[dev] = self._cs_cmd(dev)
+
+    def _write_cmd(self, device, data):
         """Return a MPSSE command string which sends data out via the current SPI
         mode.  data is either a string or converted by map(chr, data).
         """
-        cmd = chr(self.MPSSE_SPI_MODE_CONFIG[self.mode] |
+        cmd = chr(self.MPSSE_SPI_MODE_CONFIG[self.cs[device][0]] |
                   self.MPSSE_WRITE)
         cmd += uint16str(len(data) - 1)
 
+
         #translate data to a string
         if not isinstance(data, str):
             data = ''.join(map(chr, data))
@@ -539,13 +520,13 @@ class SPI(object):
         cmd += data
         return cmd
 
-    def _exchange_cmd(self, data):
+    def _exchange_cmd(self, device, data):
         """Return a MPSSE command string which sends data out via the current SPI
         mode and reads the same amount back.  data is either a string or
         converted by map(chr, data).  Interface read buffer contains the read
         data.
         """
-        cmd = chr(self.MPSSE_SPI_MODE_CONFIG[self.mode] |
+        cmd = chr(self.MPSSE_SPI_MODE_CONFIG[self.cs[device][0]] |
                   self.MPSSE_WRITE |
                   self.MPSSE_READ)
         cmd += uint16str(len(data) - 1)
@@ -558,42 +539,43 @@ class SPI(object):
         cmd += chr(self.ftdi.SEND_IMMEDIATE)
         return cmd
 
-    def _read_cmd(self, n):
+    def _read_cmd(self, device, n):
         """Return a MPSSE command string which reads n-bytes via the current
         SPI mode.  NB: The DO line stays in its last state.
         """
-        cmd = chr(self.MPSSE_SPI_MODE_CONFIG[self.mode] |
+        cmd = chr(self.MPSSE_SPI_MODE_CONFIG[self.cs[device][0]] |
                   self.MPSSE_READ)
         cmd += uint16str(n - 1)
         cmd += chr(self.ftdi.SEND_IMMEDIATE)
         return cmd
 
-    def write(self, data, device):
+    def write(self, device, data):
         """Write data out to the selected device via the current SPI mode.
         The device is used to select the desired CS pin state as cs_active =
         self.cs[device].
         """
         cmd = (self._cs_cmd_cache[device] +
-                self._write_cmd(data) +
+                self._write_cmd(device, data) +
                 self._cs_cmd_cache['_idle'])
         self._raw_write(cmd)
 
-    def read(self, n, device):
+    def read(self, device, n):
         """Read data from the selected device, DO pin stays in its last
         state.
         """
-        cmd = (self._cs_cmd_cache[device] + self._read_cmd(n) +
+        cmd = (self._cs_cmd_cache[device] +
+                self._read_cmd(device, n) +
                 self._cs_cmd_cache['_idle'])
         self._raw_write(cmd)
         return self._raw_read(n)
 
-    def exchange(self, data, device):
+    def exchange(self, device, data):
         """Write data out to the selected device while also reading data in via
         the current SPI mode.  The device is used to select the desired CS pin
         state as cs_active = self.cs[device].
         """
         cmd = (self._cs_cmd_cache[device] +
-                self._exchange_cmd(data) +
+                self._exchange_cmd(device, data) +
                 self._cs_cmd_cache['_idle'])
         self._raw_write(cmd)
         return self._raw_read(len(data))
@@ -1132,7 +1114,7 @@ class DAC8568(object):
 
     def __str__(self):
         #return ''.join(map(chr, self.bytes()))
-        return int2str(self.word, self.CTL_WIDTH)
+        return int2str((self.word & 0xffffffff), self.CTL_WIDTH)
 
     @property
     def word(self):
@@ -1168,9 +1150,9 @@ class DAC8568(object):
         self._clear()
         self._control(0x4)
         #self._word[10:8] = intbv(mode, max=2**2)
-        self._word += (mode << 8) #intbv(mode, max=2**2)
+        self._word += ((mode & 0x3) << 8) #intbv(mode, max=2**2)
         #self._word[8:0] = intbv(bitfield, max=2**8)
-        self._word += (bitfield << 0) #intbv(bitfield, max=2**8)
+        self._word += ((bitfield & 0xff) << 0) #intbv(bitfield, max=2**8)
         self.send()
 
     def reference(self, mode):
@@ -1186,7 +1168,7 @@ class DAC8568(object):
         return b
 
     def send(self):
-        self.bus.write(str(self), 'dac')
+        self.bus.write(self.cs, str(self))
 
     #
     # internal functions to help construct a command
@@ -1197,21 +1179,21 @@ class DAC8568(object):
 
     def _control(self, value):
         #self._word[28:24] = value #intbv(value, max=2**4)
-        self._word += (value << 24) #intbv(value, max=2**4)
+        self._word += ((value & 0xf) << 24) #intbv(value, max=2**4)
 
     def _address(self, value):
         #other values are no-op addresses
         if (value <= 7) or (value == 15):
             #self._word[24:20] = value #intbv(value, max=2**4)
-            self._word += (value << 20) #intbv(value, max=2**4)
+            self._word += ((value & 0xf) << 20) #intbv(value, max=2**4)
 
     def _data(self, value):
         #self._word[20:20-self.DAC_WIDTH] = value #intbv(value, max=2**self.DAC_WIDTH)
-        self._word += (value << (20-self.DAC_WIDTH)) #intbv(value, max=2**self.DAC_WIDTH)
+        self._word += ((value & 0xffff) << (20-self.DAC_WIDTH)) #intbv(value, max=2**self.DAC_WIDTH)
 
     def _feature(self, value):
         #self._word[4:0] = value #intbv(value, max=2**4)
-        self._word += (value << 0)#intbv(value, max=2**4)
+        self._word += ((value & 0xf) << 0)#intbv(value, max=2**4)
         
 
 class ADS8201(object):
@@ -1251,7 +1233,7 @@ class ADS8201(object):
         w[8:] = value #intbv(value, max=2**8)
         #self.bus.SetCS('adc')
         #self.bus.Start()
-        self.bus.write(int2str(w, 16), 'adc')
+        self.bus.write('adc', int2str(w, 16))
         #self.bus.Stop()
 
     def getRegister(self, address):
@@ -1260,7 +1242,7 @@ class ADS8201(object):
         #print 'sending:', b(w, 16)
         #self.bus.SetCS('adc')
         #self.bus.Start()
-        rval = self.bus.exchange(int2str(w, 16), 'adc')
+        rval = self.bus.exchange('adc', int2str(w, 16))
         #self.bus.Stop()
         #i = 8 * (len(rval)-1)
         #r = 0
@@ -1406,7 +1388,7 @@ class ADS8201(object):
         #self.bus.SetCS('adc')
         #self.bus.Start()
         #rval = self.bus.exchange(int2str(w, 16), 'adc')
-        rval = self.bus.exchange('\x00\x00', 'adc')
+        rval = self.bus.exchange('adc', '\x00\x00')
         #self.bus.Stop()
         i = 8 * (len(rval)-1)
         r = 0
@@ -1537,13 +1519,12 @@ class AtoiSPI0(SPI):
     def __init__(self, freq):
         super(AtoiSPI0, self).__init__(
                 interface=ftdi.INTERFACE_A,
-                cs={'_idle': 0xc8,
-                    '_mask': 0xff, #TODO
-                    'flash': 0xc0,
-                    'dac':   0xd8,
-                    'adc':   0xe8,
-                    'convst': 0xf8},
-                mode=1, #flash is mode 0, others are 1
+                cs={'_idle': (-1, 0xc8),
+                    '_mask': (-1, 0x38), #TODO
+                    'flash': (0, 0xc0),
+                    'dac':   (1, 0xd8),
+                    'adc':   (1, 0xe8),
+                    'convst': (1, 0xf8)},
                 freq=int(freq),
                 vid=0x0403,
                 pid=0x6011,
@@ -1552,39 +1533,6 @@ class AtoiSPI0(SPI):
                 latency=1
                 )
 
-    def SetCS(self, name):
-        """Setup the chip-select pins for a given 'name'."""
-        if name == self._cs:
-            return
-
-        if name not in self._cs_mode:
-            raise Exception, 'Unknown CS name: %s' % name
-
-        # different SPI modes require restarting the bus for some reason
-        if self.context.mode != self._cs_mode[name]:
-            self.Close()
-            self.Open(0x0403, 0x6011,
-                    self._cs_mode[name],
-                    self._freq,
-                    interface=mpsse.IFACE_A)
-
-        if name == 'flash':
-            self.context.pidle = self.context.pstop = 0xc8
-            self.context.pstart = 0xc0
-        elif name == 'dac':
-            self.context.pidle = self.context.pstop = 0xc8
-            self.context.pstart = 0xd8
-        elif name == 'adc':
-            self.context.pidle = self.context.pstop = 0xc8
-            self.context.pstart = 0xe8
-        elif name == 'convst':
-            self.context.pidle = self.context.pstop = 0xc8
-            self.context.pstart = 0xf8
-        else:
-            raise Exception, 'Unknown CS name: %s' % name
-
-        #self._cs = name
-
 
 class AtoiSPI1(mpsse.MPSSE):
     def __init__(self, freq):
@@ -1686,7 +1634,7 @@ class DigiReg(AD524x):
 
 class DAC_atoi(DAC8568):
     """Specific configuration and calibration for devboard DAC."""
-    VREF = 2.5
+    VREF = 2.5012
     GAIN = 1.0 #part options A/B
     #GAIN = 2.0 #part options C/D
     POR_VALUE = 0 #part A/C