'''
SPI Interface Hardware Drivers
==============================
Created on Feb 23, 2015
Heavily modified August 2016 to be more generic.
@author: JKapasi
@author: DSimmons
The SPI interface is composed of two separate classes:
1) shift_register
abstracts individual bit-fields into integer representing contents of full-length shift register
2) spiInterface: Defines the hardware interface including baudrate, mode (CPOL/CPHA), CS operation.
Specific hardware implementations should overload this class and implement _shift_data method.
'''
# Notes:
# 1) spiSlave removed.
# Generically, SPI data does not represent registers or addresses memory.
# There's no guarantee that returned data has same meaning as transmitted data.
# Likewise, SPI doesn't fit well with the SPI_instrument class generically.
# Specifically, the 7-bit address and command code is very SMBus specific and should probably be removed to a slave-specific file.
# 2) The beaglebond SPI master got broken.
# The spiInterface parent class had significant changes to support generic-length SPI DUTs and arbitrary SPI Master hardware requirements.
# Because we can't test/debug operation of the beaglebone port, it's commented out until someone has a physical test bench.
try:
from Adafruit_BBIO.SPI import SPI
SPI_BBIO_missing = False
except ImportError as e:
SPI_BBIO_missing = True
import struct, array, time, numbers, collections, random
from abc import ABCMeta, abstractmethod
[docs]class shift_register(object):
'''helper class to assemble multiple bit-fields together into a single larger integer and to disassemble received data into individual bit-fields.'''
def __init__(self, bit_field_name=None, bit_field_bit_count=None):
'''Instance constructor.
Optionally add single bit_field.
Otherwise, add bit_fields later using repeated calls to add_bit_field()
'''
self._bit_field_bit_counts = []
self._bit_field_names = []
if bit_field_name is not None and bit_field_bit_count is not None:
self.add_bit_field(bit_field_name, bit_field_bit_count)
elif bit_field_name is None and bit_field_bit_count is None:
pass
else:
raise Exception('If either bit_field_name or bit_field_bit_count is specified, then the other must also be specified.')
def __len__(self):
'''support len() builtin to return total number of bits in shift register'''
bit_count = 0
for bfc in self._bit_field_bit_counts:
bit_count += bfc
return bit_count
def __str__(self):
'''graphical view of register structure if object is printed'''
hr = '--'
line1 = ' |'
line2 = ' |'
offset = 0
for name,size in reversed(zip(self._bit_field_names,self._bit_field_bit_counts)):
if size == 1:
sr = '{}'.format(offset)
bf = '{}[0]'.format(name)
else:
sr = '{}:{}'.format(offset+size-1,offset)
bf = '{}[{}:0]'.format(name,size-1)
offset += size
chars = max(len(sr),len(bf))
hr += '-' * (chars + 3)
line1 = ' | {}{}'.format(sr.center(chars),line1)
line2 = ' | {}{}'.format(bf.center(chars),line2)
line1 = line1[1:] + '\n'
line2 = line2[1:] + '\n'
hr = hr[1:] + '\n'
return 'SPI Shift Register Data Mapping Object\n' + hr + line1 + hr + line2 + hr
def __add__(self,other):
'''support concatenation of multiple instances using '+' operator'''
assert isinstance(other,shift_register)
for bf in self.keys():
if bf in other.keys():
raise Exception('Duplicated bit field name not allowed when concatenating shift_register instances: {}'.format(bf))
new_sr = shift_register()
new_sr._bit_field_bit_counts = self._bit_field_bit_counts + other._bit_field_bit_counts
new_sr._bit_field_names = self._bit_field_names + other._bit_field_names
return new_sr
def __iter__(self):
'''iterate over bit field names ms field to ls field'''
return iter(self._bit_field_names)
def __getitem__(self, key):
'''return size (bit count) of named bit field.
use with dictionary-style lookup: my_shift_register['my_key']'''
index = self._bit_field_names.index(key)
return self._bit_field_bit_counts[index]
def _check_size(self, data, clk_count):
'''make sure externally supplied data can fit within allocated bit field width'''
assert isinstance(data, numbers.Integral)
assert isinstance(clk_count, numbers.Integral)
assert data >= 0
assert data < 2**clk_count
return True
[docs] def add_bit_field(self, bit_field_name, bit_field_bit_count):
'''build SPI shift register data protocol sequentially MSB->LSB with repeated calls to add_bit_field'''
assert isinstance(bit_field_name, str)
assert isinstance(bit_field_bit_count, numbers.Integral)
assert bit_field_bit_count > 0
try:
dup_index = self._bit_field_names.index(bit_field_name)
except ValueError: #bit field name not already used
self._bit_field_names.append(bit_field_name)
self._bit_field_bit_counts.append(bit_field_bit_count)
else:
raise ValueError('Duplicated bit field name: {} at position: {}.'.format(bit_field_name, dup_index))
[docs] def keys(self):
'''return list of bit-field names registered with instance'''
return self._bit_field_names[:]
[docs] def display(self):
'''print ascii register structure graphic'''
print str(self)
[docs] def pack(self, bit_field_data_dict):
'''pack bit fields into single larger integer. also return accumulated clk_count.
Suitable for passing directly to spiInterface.transceive(*shift_register.pack(bit_field_data_dict))
bit_field_data_dict should contain one key-value pair for each defined bit_field'''
#don't check for extra entries in bit_field_data_dict that don't go with this shift register. Any reason to???
val = 0
offset = 0
for name,size in reversed(zip(self._bit_field_names,self._bit_field_bit_counts)):
self._check_size(bit_field_data_dict[name], size)
val += bit_field_data_dict[name] << offset
offset += size
return (val, offset)
[docs] def unpack(self, data):
'''unpack single integer representing full-width shift register data into individual bit field values according to instance-defined boundaries.
return dictionary with key-value pairs for each defined bit_field_name and bit_field data.
'''
bf_data = []
for size in reversed(self._bit_field_bit_counts): #iterate backwards because accumulated shift offsets are not yet known.
bf_data.insert(0,int(data & 2**size-1))
data = data >> size
res = collections.OrderedDict()
for i,name in enumerate(self._bit_field_names): #reverse order back to MSByte first
res[name] = bf_data[i]
return res
class spiInterface(object):
__metaclass__ = ABCMeta
#If using a GPIO to bit bang the slave select provide a control function (ss_ctrl)
def __init__(self,CPOL,CPHA,ss_ctrl,word_size):
'''mode controls polarity and phase: 0 => CPOL=0, CPHA=0
1 => CPOL=0, CPHA=1
2 => CPOL=1, CPHA=0
3 => CPOL=1, CPHA=1
ss_ctrl funtion takes boolean argument to control SS/_CS if necessary for hardware.
Select slave if argument evalutes to True.
word size defines SPI master shirt register length. ex 1,8,16 bits. Transactions must be (automatically) padded by modulo word_size. to align correctly.'''
self.CPOL = CPOL
self.CPHA = CPHA
if self.CPOL == 0 and self.CPHA == 0:
self.mode = 0
elif self.CPOL == 0 and self.CPHA == 1:
self.mode = 1
elif self.CPOL == 1 and self.CPHA == 0:
self.mode = 2
elif self.CPOL == 1 and self.CPHA == 1:
self.mode = 3
else:
raise SPIMasterError('Invalid CPOL/CPHA setting')
self.set_ss_ctrl(ss_ctrl_function = ss_ctrl)
self.word_size = word_size
self.set_strict_alignment(True)
def set_strict_alignment(self, strict):
'''If true, enforce that SPI master and slave hardware lengths match.
If false, enable automatic padding to correct alignment.'''
self._strict_alignment = strict
self._warned_once = False
def set_ss_ctrl(self, ss_ctrl_function):
'''change ss_ctrl function after instantiation.
function should take single boolean argument.
If true, assert slave select. If false, deassert slave select.
There will typically be a logic inversion inside ss_ctrl to achieve active low _cs.
'''
self._ss_ctrl = ss_ctrl_function
@abstractmethod
def _shift_data(self, data_out, clk_count):
'''Hardware-specific Function that will shift data in/out of the SPI interface.
data_out is transmitted via MOSI
return data received bia MISO
data formats are as in integer, MSB (first clock) to LSB (last clock)
pack and unpack methods may be helpful to break integer data into interface hardware-aligned chunks.
shift_register class may be helpful to pack dut bit field chunks into full shift register width integer
Subclass implementation should raise SPIMasterError exception if unable to send message of length clk_count
(ie hardware limited to byte-aligned messages)'''
raise SPIMasterError('Overload required.')
@staticmethod
def _check_size(data, bits):
'''make sure data fits within word of length "bits"'''
assert isinstance(data, numbers.Integral)
assert isinstance(bits, numbers.Integral)
assert data >= 0
assert data < 2**bits
return True
@staticmethod
def pack(data_list, word_size=8):
'''pack byte,word aligned pieces (list) from communication hardware into single integer comprising full shift register width.
integer can then be broken up by shift_register object into bit field aligned pieces.'''
res = 0
offset = 0
for i in reversed(data_list):
res += i << offset
offset += word_size
return res
def unpack(self, data, bit_count, word_size=8):
'''break full shift register width integer into byte,word aligned pieces. Return list of pieces MS first, LS last.
helper to send byte-aligned pieces to hardware even if bit fields span bytes (or 1-bit, 16-bit 32-bit, etc words for other hardware)'''
assert bit_count % word_size == 0 #partially filled blocks create aligment ambiguity
self._check_size(data, bit_count)
res = []
while bit_count > 0:
res.insert(0, data & 2**word_size-1)
data = data >> word_size
bit_count -= word_size
return res
def transceive(self, data, clk_count):
'''send data word out MOSI with clk_count clocks.
return word of same size read simultaneously on MISO.
Frame entire transaction with slave select.'''
self._check_size(data,clk_count)
framing_excess = clk_count % self.word_size
if framing_excess != 0:
if self._strict_alignment:
raise SPIMasterError('Slave shift register length: {} does not match SPI Master hardware word size: {}. {} bit{} are left over. Call spiInterface.set_strict_alignment(False) to enable automatic data padding.'.format(clk_count, self.word_size, framing_excess, 's' if framing_excess > 1 else ''))
else:
pad_bits = self.word_size - framing_excess
if not self._warned_once:
print "WARNING: SPI transaction padded {} bit{} to {} bits to align with {} bit SPI master hardware".format(pad_bits, 's' if pad_bits > 1 else '', clk_count+pad_bits, self.word_size)
self._warned_once = True
else:
pad_bits = 0
result = None
self._ss_ctrl(True)
try:
result = self._shift_data(data, clk_count+pad_bits) #MSB first; pad bits are zero, go in first, and should shift out the end of slave hardware.
finally:
self._ss_ctrl(False) #don't leave bus hung
if result is not None:
result = result >> pad_bits #MSB first; Slave data comes out first, followed by meaningless data caused by extra pad clocks.
return result
[docs]class spi_bbone(spiInterface):
'''The Beaglebone black will use the Adafruit BBIO,
thus we can initialize this package for all purposes
This instrument probably got a broken when the parent class interface was modified to support multiple interface hardware boards and more general SPI communication.
Needs testing/repair.'''
# def __init__(self,baudrate,timeout,device_num, mode=0, ss_ctrl=lambda ss: None):
# '''device num is a tuple eg. (0,0)
# '''
# spiInterface.__init__(self,mode,ss_ctrl)
# self.baudrate = baudrate
# self.timeout = timeout #does this do anything???
# if not isinstance(device_num, tuple):
# raise TypeError
# if SPI_BBIO_missing:
# print "The SPI module is missing. This is expected if you are not using a Beaglebone single board computer."
# raise SPIMasterError("AdaFruit_BBIO is missing on this computer")
# else:
# self.spi = SPI(*device_num)
# self.spi.msh=baudrate
# self.spi.mode=mode #CPOL/CPHA
# def __del__(self):
# self.spi.close()
# def _shift_data(self, transfer_list):
# return self.spi.xfer2(transfer_list)
# @staticmethod
# def get_byte_list(input_num, format_code='>B'):
# '''Take number as input and output as a byte list.
# The format_code should be of the form "XY", where
# X is > (Big-endian) or < (Little-endian) and
# Y is H/L/Q (unsigned short/long/long_long)'''
# # eg. input_int = 0xC0DEDBAD
# s = struct.pack(format_code, input_num)
# a = array.array("B") # B: Unsigned bytes
# a.fromstring(s)
# result = a.tolist()
# # print "byte list", result # [192, 222, 219, 173]
# return result
# @staticmethod
# def get_data(input_list, format_code):
# '''Take an input_list, a list of bytes, and pack into an unsigned short/long/long_long.
# The format_code should be of the form "XY", where
# X is > (Big-endian) or < (Little-endian) and
# Y is H/L/Q (unsigned short/long/long_long)'''
# a = array.array("B") # B: Unsigned bytes
# a.fromlist(input_list)
# s = a.tostring()
# h = struct.unpack(format_code, s)[0]
# return h
class spi_cfgpro(spiInterface):
def __init__(self,visa_interface, CPOL, CPHA, baudrate=1e6, ss_ctrl=None):
self.interface = visa_interface
if ss_ctrl is None:
self.ss_ctrl = self.cs
else:
self.ss_ctrl = ss_ctrl
spiInterface.__init__(self, CPOL, CPHA, ss_ctrl=self.ss_ctrl, word_size=8)
self.interface.write(':SPI:ENable 0')
self.interface.write(':SPI:ENable 1')
self.interface.write(':SPI:Dorder 0') #MSB first
if float(baudrate) == 4e6:
self.interface.write(':SPI:CLOCk:RATE 0')
elif float(baudrate) == 1e6:
self.interface.write(':SPI:CLOCk:RATE 1')
elif float(baudrate) == 250e3:
self.interface.write(':SPI:CLOCk:RATE 2')
elif float(baudrate) == 125e3:
self.interface.write(':SPI:CLOCk:RATE 3')
elif float(baudrate) == 8e6:
self.interface.write(':SPI:CLOCk:RATE 4')
elif float(baudrate) == 2e6:
self.interface.write(':SPI:CLOCk:RATE 5')
elif float(baudrate) == 500e3:
self.interface.write(':SPI:CLOCk:RATE 6')
elif float(baudrate) == 250e3: #yes, it's really duplicated
self.interface.write(':SPI:CLOCk:RATE 7')
else:
raise SPIMasterError('Invalid SPI baud rate for Configurator Pro interface: {}'.format(float(baudrate)))
if CPOL == 0:
self.interface.write('SPI:CLOCk:POLarity 0')
else:
self.interface.write('SPI:CLOCk:POLarity 1')
if CPHA == 0:
self.interface.write('SPI:CLOCk:PHASe 0')
else:
self.interface.write('SPI:CLOCk:PHASe 1')
def cs(self, select):
if select:
self.interface.write('SPI:SSELect:ENable')
else:
self.interface.write('SPI:SSELect:DISable')
def __del__(self):
self.interface.close()
def _shift_data(self, data_out, clk_count):
resp = []
for byte in self.unpack(data_out, clk_count, self.word_size):
resp.append(int(self.interface.ask(':SPI:TRANsceive? {:02X}'.format(byte)),16))
return self.pack(resp, self.word_size)
class spi_dc590(spiInterface):
def __init__(self,interface_stream, ss_ctrl=None):
'''no control over CPOL/CPHA mode or baudrate...'''
self.interface = interface_stream
if ss_ctrl is None:
ss_ctrl = lambda ss: self.set_cs(not ss) #active low
spiInterface.__init__(self, CPOL=0, CPHA=0, ss_ctrl=ss_ctrl, word_size=8)
self.init_spi()
def set_cs(self, level):
'''control DC590 CS pin.
If true, pin high. No active low inversion here.'''
if level:
self.interface.write('X')
else:
self.interface.write('x')
def set_gpio(self, level):
'''control DC590 Pin 14 GPIO pin as output.
If true, pin high.'''
if level:
self.interface.write('COG')
else:
self.interface.write('COg')
def init_spi(self):
time.sleep(2.5) #Linduino bootloader delay!
self.interface.write('\n'*10)
time.sleep(2.5) #Linduino bootloader delay!
print 'DC590 init response: {}'.format(self.interface.read(None)[0]) #discard any responses
self.interface.write('O') #Enable isolated power
try:
self.spi_mode()
except SPIMasterError as e:
print e
def spi_mode(self):
'''Switch DC590 I2C/SPI mux to SPI'''
self.interface.write('MS') #Switch to isolated SPI Mode
time.sleep(0.1)
buffer = self.interface.read(None)[0]
if len(buffer):
raise SPIMasterError('Error switching DC590 to SPI Mode. Unexpected data in buffer:{}'.format(buffer))
def __del__(self):
self.interface.close()
def _shift_data(self, data_out, clk_count):
resp = []
for byte in self.unpack(data_out, clk_count, self.word_size):
self.interface.write('T{:02X}'.format(byte))
stream_resp = self.interface.read(2)
if len(stream_resp[0]) != 2:
raise SPIMasterError('Short response to SPI transceive: {}. SPI mode enabled?'.format(stream_resp[0]))
elif stream_resp[1] != 0:
raise SPIMasterError('Long response to SPI transceive: {} then {}'.format(stream_resp[0]), elf.interface.read(None)[0])
else:
resp.append(int(stream_resp[0], 16))
return self.pack(resp, self.word_size)
class spi_buspirate(spiInterface):
def __init__(self,interface_raw_serial, CPOL=0, CPHA=0, baudrate=1e6, ss_ctrl=None):
self.ser = interface_raw_serial
if ss_ctrl is None:
self.ss_ctrl = self.cs
else:
self.ss_ctrl = ss_ctrl
spiInterface.__init__(self, CPOL, CPHA, ss_ctrl=self.ss_ctrl, word_size=8)
self.__init_spi()
self.set_baudrate(baudrate)
self.set_mode() #uses self.mode from parent class __init__
def __init_spi(self):
self.ser.write('\n'*10) #exit any menus
self.ser.write('#') #reset
self.ser.read(self.ser.inWaiting())
#get into binary mode
resp = ''
tries = 0
while (resp != 'BBIO1'):
tries += 1
if tries > 20:
raise SPIMasterError('Buspirate failed to enter binary mode after 20 attempts')
print 'Buspirate entering binary mode attempt {}: '.format(tries),
self.ser.write('\x00') #enter binary mode
time.sleep(0.05)
resp = self.ser.read(self.ser.inWaiting())
print resp
#get into i2c mode
self.ser.write('\x01') #enter binary SPI mode
time.sleep(0.05)
resp = self.ser.read(self.ser.inWaiting())
if resp != 'SPI1':
raise SPIMasterError('Buspirate failed to enter SPI mode: {}'.format(resp))
#set voltage levels
#self.ser.write('\x4C') #power and pullups on
self.ser.write('\x4D') #power and pullups on
resp = self.ser.read(1)
if resp != '\x01':
raise SPIMasterError('Buspirate failed to enable supply and pullups: {}'.format(resp))
#vpullup select not yet implemented 7/16/2013. 3.3V shorted to Vpu on board temporarily
#self.ser.write('\x51') #3.3v pullup
#resp = self.ser.read(1)
#if resp != '\x01':
# raise SPIMasterError('Buspirate failed to set pullup voltage to 3.3v: {}'.format(resp))
def set_baudrate(self, baudrate):
baudrate = float(baudrate)
self.baudrate = baudrate
if baudrate == 30e3:
self.ser.write('\x60')
elif baudrate == 125e3:
self.ser.write('\x61')
elif baudrate == 250e3:
self.ser.write('\x62')
elif baudrate == 1e6:
self.ser.write('\x63')
elif baudrate == 2e6:
self.ser.write('\x64')
elif baudrate == 2.6e6:
self.ser.write('\x65')
elif baudrate == 4e6:
self.ser.write('\x66')
elif baudrate == 8e6:
self.ser.write('\x67')
resp = self.ser.read(1) #does it really return a byte? Undocumented...
if resp != '\x01':
raise SPIMasterError('Buspirate failed to set SPI baudrate to {}: {}'.format(baudrate,resp))
def set_mode(self):
#looks like CKE is reversed from CPHA standard....
#bit 8 CKE: SPIx Clock Edge Select bit(1)
#1 = Serial output data changes on transition from active clock state to Idle clock state (see bit 6)
#0 = Serial output data changes on transition from Idle clock state to active clock state (see bit 6)
if self.mode == 0:
self.ser.write('\x8A')
elif self.mode == 1:
self.ser.write('\x89')
elif self.mode == 2:
self.ser.write('\x8E')
elif self.mode == 3:
self.ser.write('\x8C')
else:
raise SPIMasterError('Something went wrong setting Mode')
resp = self.ser.read(1) #does it really return a byte? Undocumented...
if resp != '\x01':
raise SPIMasterError('Buspirate failed to set SPI mode to {}: {}'.format(self.mode,resp))
def cs(self, select):
'''select is active low'''
if select:
self.ser.write('\x02')
else:
self.ser.write('\x03')
resp = self.ser.read(1)
if resp != '\x01':
raise SPIMasterError('Buspirate failed to set SPI _SS: {}'.format(ord(resp)))
def __del__(self):
self.ser.close()
def _shift_data(self, data_out, clk_count):
resp = []
bytes = self.unpack(data_out, clk_count, self.word_size)
while len(bytes) > 16:
self.ser.write('\x1F') #bulk write and read 16 bytes
resp_chr = self.ser.read(1)
if resp_chr != '\x01':
raise SPIMasterError('Buspirate failed response to SPI bulk read/write: {}'.format(ord(resp_chr)))
for byte in bytes[0:16]:
self.ser.write(chr(byte)) #send MOSI byte
resp.append(ord(self.ser.read(1))) #get MISO byte back
bytes = bytes[16:]
remaining_bytes = len(bytes)
self.ser.write(chr(0x10 + remaining_bytes - 1)) #bulk write and read remaining bytes
resp_chr = self.ser.read(1)
if resp_chr != '\x01':
raise SPIMasterError('Buspirate failed response to SPI bulk read/write: {}'.format(ord(resp_chr)))
for byte in bytes:
self.ser.write(chr(byte)) #send MOSI byte
resp.append(ord(self.ser.read(1))) #get MISO byte back
return self.pack(resp, self.word_size)
class spi_bitbang(spiInterface):
def __init__(self, SCK_channel, MOSI_channel=None, MISO_channel=None, SS_channel=None, CPOL=0, CPHA=0, SS_POL=0, low_level=0, high_level=1):
'''bit-bangable SPI port made of any writeable channels (power supply, gpio, etc).
SCK_channel, MOSI_channel and SS_channel are writable channel objects.
MISO_channel is a readable channel object.
MOSI_channel or MISO_channel may be set equal to None for unidirectinal communication.
SS_channel may be set equal to None if not required.
CPOL, CPHA set SPI clock polarity and clock phase respectively. CPHA != 0 not currently supported.
SS_POL is the active state of slave/chip select (ie 0 for typical active low).
low_level and high_level will be written to the writable channels to set the logic low and logic high states respectively. The average will set the ADC threshold for the MISO channel.
If a channel is not required, it can be faked with lab_core.master.add_channel_dummy('fake_MISO_channel').
'''
self.SCK_channel = SCK_channel
self.MOSI_channel = MOSI_channel
self.MISO_channel = MISO_channel
self.SS_channel = SS_channel
self.SS_POL = SS_POL
self.set_levels(low_level, high_level)
spiInterface.__init__(self, CPOL, CPHA, ss_ctrl=self.cs, word_size=1)
if not self.CPOL:
#clock resting state 0
self.SCK_channel.write(self.low)
else:
#clock resting state 1
self.SCK_channel.write(self.high)
assert CPHA == 0 #CHPA == 1 not implemented. https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Clock_polarity_and_phase
def set_levels(self, low=0, high=1):
'''set values to write to SCK_channel, MOSI_channel and SS_channel.
values are also used to digitize MISO_channel readings.
for example, to use a power supply at 5V logic levels set high=5'''
self.low = low
self.high = high
def cs(self, select):
'''select is active low'''
if self.SS_channel is not None:
if select and not self.SS_POL:
self.SS_channel.write(self.low)
elif not select and not self.SS_POL:
self.SS_channel.write(self.high)
elif select and self.SS_POL: #active high SS
self.SS_channel.write(self.high)
elif not select and self.SS_POL: #active high SS
self.SS_channel.write(self.low)
def _shift_data(self, data_out, clk_count):
resp = []
bits = self.unpack(data_out, clk_count, self.word_size)
for bit in bits:
#set up data
if self.MOSI_channel is not None:
self.MOSI_channel.write(self.high if bit else self.low)
#clk active
self.SCK_channel.write(self.high if not self.CPOL else self.low)
#receive data
if self.MISO_channel is not None:
raw_data = self.MISO_channel.read()
if raw_data > (self.high + self.low)/2.0: #ADC
resp.append(1)
else:
resp.append(0)
#clk inactive
self.SCK_channel.write(self.low if not self.CPOL else self.high)
if self.MISO_channel is not None:
return self.pack(resp, self.word_size)
class spi_dummy(spiInterface):
def __init__(self, delay=0, word_size=1):
self.delay = delay
spiInterface.__init__(self, CPOL=0, CPHA=0, ss_ctrl=self.cs, word_size=word_size)
def cs(self, select):
if select:
print "Writing slave_select ACTIVE."
else:
print "Writing slave_select INACTIVE."
def _shift_data(self, data_out, clk_count):
resp = []
words = self.unpack(data_out, clk_count, self.word_size)
for word in words:
resp.append(random.randint(0,2**self.word_size-1))
ret_val = self.pack(resp, self.word_size)
print "{{}} bit dummy SPI transaction. Wrote: 0x{{:0{digits}X}} Read:0x{{:0{digits}X}}".format(digits=(clk_count-1)/4+1).format(clk_count, data_out, ret_val)
time.sleep(self.delay)
return ret_val
class SPIMasterError(Exception):
pass
if __name__ == "__main__":
import lab_core
m = lab_core.master()
def dummy_print(ch_name, data):
print '{}:{}'.format(ch_name, data)
clk_ch = m.add_channel_virtual('sck', write_function = lambda data: dummy_print('sck',data))
mosi_ch = m.add_channel_virtual('mosi', write_function = lambda data: dummy_print('mosi',data))
ss_ch = m.add_channel_virtual('_ss', write_function = lambda data: dummy_print('/ss',data))
miso_ch = m.add_channel_virtual('miso', read_function = lambda: dummy_print('miso','Read'))
sr = shift_register()
sr.add_bit_field(bit_field_name='top_nibble', bit_field_bit_count=4)
sr.add_bit_field(bit_field_name='bottom_half_nibble', bit_field_bit_count=2)
sr.add_bit_field(bit_field_name='bit_1', bit_field_bit_count=1)
sr.add_bit_field(bit_field_name='bit_0', bit_field_bit_count=1)
print sr
for bf in sr:
print '{}:{}'.format(bf,sr[bf])
#sp = spi_bitbang(SCK_channel=clk_ch, MOSI_channel=mosi_ch, MISO_channel=miso_ch, SS_channel=ss_ch)
sp = spi_dummy()
sp.transceive(0x05a,9)
data = {}
data['top_nibble'] = 0xA
data['bottom_half_nibble'] = 1
data['bit_1'] = 0
data['bit_0'] = 1
print sr.unpack(sp.transceive(*sr.pack(data)))