''' This is a two part i2c network protocol module written in Python. The first part allows i2c devices to communicate with your PC via parallel port. The second part is specifically for OOPic microcontrollers to communicate with the PC. OOPic specific functions are noted. When you instantiate the i2c class the first argument is your parallel port number. The default is 956. The second argument is the address of the device you want to talk to, for OOPic the node is the oopic network ID * 2 You must consult the specifications of the device you are using to figure out how to get this address. the third argument is oopic specific and may not apply to all devices, it is the address of the specific object in the device you want to talk to. All devices have different ways of assigning these addresses again, consult your documentation. The functions of the i2c module range from very low level to high level. For example, PortInp() and PortOut() literally set or read a pin on your parallel port, whereas ReadOOPicDDE() and WriteOOPicDDE() are high level OOPic constructs which take care of all the tricky parts for you. I recommend reading the Phillips i2c bus specification to anyone interested in using the protocol in any way. Certainly this module will be much easier to use after reading the specification. The OOPic specific functions at the end, hopefully can serve as models for the i2c procedure for any device The module only runs on Windows and needs dlportio.dll in order to function. You also must have the baseconvert module and the windll module installed in your python path. See examples at bottom. This module is based on an i2c program written in Visual Basic by Tony Brenke. I hope this is helpful to someone! If you have any questions feel free to send me an email: hdeweyha@bennington.edu Heather Dewey-Hagborg 2003 ''' import windll from baseconvert import * class i2c: ######## Part1: General i2c functions ##################################################################### def __init__(self, lptport=956, node=0, ddelinkobject=0): self.port = windll.module('dlportio') self.lptport = lptport self.DelayTime = 84 #154 self.ResetPort = self.lptport+2 self.ResetHigh = 0 self.ResetLow = 1 self.ClockOutPort = self.lptport+2 self.SCLoHigh = 0 self.SCLoLow = 8 self.DataOutPort = self.lptport self.SDAoHigh = 255 self.SDAoLow = 127 self.ClockInPort = self.lptport+1 self.SCLMask = 8 self.SCLiLow = 0 self.SCLiHigh = 8 self.DataInPort = self.lptport+1 self.SDAMask = 128 self.SDAiLow = 128 self.SDAiHigh = 0 self.I2CAck = 0 self.I2CNAck = 1 self.I2CCableError = 2 self.oopicnode = node self.ddelink = ddelinkobject #might be OOPic specific # gives starting line status def begin(self): linestatus = self.PortInp(self.ClockInPort) # looking for level of 120 = clock high and data high print "beginning line status : ", linestatus self.SetReset(1) self.Delay() self.PortOut(self.ClockOutPort, 0) return linestatus def PortInp(self, zPortAddress): input = self.port.DlPortReadPortUchar(zPortAddress) return input def PortOut(self, zPortAddress, zValue): self.port.DlPortWritePortUchar(zPortAddress,zValue) def ReadData(self): dataread = self.PortInp(self.DataInPort) if dataread == 248: databit = 0 elif dataread == 255: databit = 0 elif dataread <= 120: databit = 1 elif dataread == 127: databit = 1 else: databit = 0 print "unknown datain value", dataread # raise exception return databit def SetDataLine(self, LogicLevel): if LogicLevel == 0: self.PortOut(self.DataOutPort, self.SDAoLow) elif LogicLevel == 1: self.PortOut(self.DataOutPort, self.SDAoHigh) else: print "unknown dataout value : ", LogicLevel # raise exception self.Delay() def ReadClock(self): clockread = self.PortInp(self.ClockInPort) if clockread == 248: clockbit = 1 elif clockread == 120: clockbit = 1 elif clockread == 240: clockbit = 0 else: clockbit = 0 # print "unknown clockin value", clockread # raise exception return clockbit def SetClockLine(self, LogicLevel): if LogicLevel == 0: self.PortOut(self.ClockOutPort, self.SCLoLow) elif LogicLevel == 1: self.PortOut(self.ClockOutPort, self.SCLoHigh) else: print "unknown clockout value : ", LogicLevel # raise exception self.Delay() def SendStart(self): # Just in case, set the I2C-Data & I2C-Clock High self.SetDataLine(1) self.SetClockLine(1) # While the I2C-Clock is High, set the I2C-Data Low self.SetDataLine(0) # Now that the Start signal is done, set the I2C-Clock Low so that the # I2C-Data can be changed later without it looking like a stop command self.SetClockLine(0) def SendStop(self): # Just in case they are not set, set the I2C-Data Low & I2C-Clock High self.SetDataLine(0) self.SetClockLine(1) # While the I2C-Clock is High, set the I2C-Data High self.SetDataLine(1) # sendbyte, address the object to write to def SendAddressW(self): # for writing address write bit is 0 bitaddress = self.oopicnode ackw = self.SendByte(bitaddress,1,1) # 1=yes convert, 1=yes acknowledge return ackw # sendbyte, address the object to read from def SendAddressR(self): # for reading address read bit is 1 bitaddress = (self.oopicnode) + 1 ackr = self.SendByte(bitaddress,1,1) # 1=yes convert, 1=yes acknowledge return ackr # bytetosend can be a binary string or a decimal number, but if it is # binary string convertyn must be set to 0, default is convert def SendByte(self, bytetosend, convertyn=1, ackyn=1): self.SetClockLine(0) if convertyn==1: bytetosend=baseconvert((bytetosend),BASE10,BASE2) # if byte is not 8 bits add zeros to front else: pass while len(bytetosend) < 8: bytetosend = '0' + bytetosend for i in bytetosend: # Put bit on the data line if int(i) == 0: self.SetDataLine(0) elif int(i) == 1: self.SetDataLine(1) else: print "There has been a data send error" # Strobe the I2C-Clock self.SetClockLine(1) self.SetClockLine(0) # Acknowledge self.SetDataLine(1) self.SetClockLine(1) print1 = self.ReadClock() # necessary? if ackyn == 1: ack = self.ReadData() else: pass self.SetClockLine(0) if ack == self.I2CNAck: print "The i2c object did not respond, dataline status : ", ack else: pass return ack def ReadByte(self, convert=1): self.SetClockLine(0) x = 7 ReadBits = '' while x >= 0: # Set I2C-Clock high self.SetClockLine(1) # Read the bit on the data stream (X.......) ReadBits = ReadBits + str(self.ReadData()) # Do a I2C-Clock strobe self.SetClockLine(0) x=x-1 if convert == 1: try: ReadByte = float(baseconvert((ReadBits),BASE2,BASE10)) # returns string except: ReadByte = 0.0 else: ReadByte = ReadBits return ReadByte def SendAck(self): # Set the I2C-Data Low to signal the Ack self.SetDataLine(0) # Strobe the I2C-Clock self.SetClockLine(1) self.SetClockLine(0) # Release the I2C-Data for future reads. self.SetDataLine(1) def SendNAck(self): # Set the I2C-Data High to signal the NAck self.SetDataLine(1) # Strobe the I2C-Clock self.SetClockLine(1) self.SetClockLine(0) def FlushBitStream(self): # Set the I2C-Data high so that the 9th bit I2C-Clock strobe will look like a # NAck to the I2C device self.SetDataLine(1) # Prepare the I2C-Clock for strobing. self.SetClockLine(1) # Strobe the I2C-Clock 10 times. x = 10 while x > 0: self.SetClockLine(0) self.SetClockLine(1) x = x-1 # Finish up by sending a Start/Stop command. self.SetDataLine(0) self.SetDataLine(1) # Send the address of the DDELink Object to talk to # For writing, the address must be adjusted by 129 def SetReset(self, LogicLevel): # this sets the reset level??? if LogicLevel == 0: self.PortOut(self.ResetPort, self.ResetLow) self.SCLoHigh = 0 + self.ResetLow self.SCLoLow = 8 + self.ResetLow else: self.PortOut(self.ResetPort, self.ResetHigh) self.SCLoHigh = 0 + self.ResetHigh self.SCLoLow = 8 + self.ResetHigh self.Delay() def Delay(self): x = 1 while x < self.DelayTime: x = x + 0.1 #################### Part2: oopici2c functions ############################################### #To Read OOPic values: def ReadOOPicDDE(self): #Send an I2C network start start1 = self.SendStart() # returns tuple # Address the OOPic to write the address to read from OOPicAddrW = self.SendAddressW() if OOPicAddrW == self.I2CNAck: print "The OOPic Address did not go through" #? end operation? else: #Send the address of the DDELink Object to talk to #For reading, the address must be adjusted by 128 DDEAddrR = self.SendByte((self.ddelink + 128), convertyn=1, ackyn=1) if DDEAddrR == self.I2CNAck: print "The DDELink Address did not go through" else: #Send the mask for the data to send. # For the data request bit, its 127 DRBM = self.SendByte(127, convertyn=1, ackyn=1) if DRBM == self.I2CNAck: print "The Data Request Bitmask did not go through" #? end operation? else: # Send a data request bit DRB = self.SendByte(128, convertyn=1, ackyn=1) if DRB == self.I2CNAck: print "The Data Request Bit did not go through" else: # Reset for read self.SendStart() # returns tuple # Address the OOPic to read from it OOPicAddrR = self.SendAddressR() if OOPicAddrR == self.I2CNAck: print "The OOPic Address did not go through" else: # read the low byte of the data DDEVal = self.ReadByte() print "The OOPic out value is : ", DDEVal self.SendStop() return DDEVal def Read2bytesOOPicDDE(self, convert=0): #Send an I2C network start self.SendStart() # returns tuple # Address the OOPic to write the address to read from OOPicAddrW = self.SendAddressW() if OOPicAddrW == self.I2CNAck: print "The OOPic Address did not go through" else: #Send the address of the DDELink Object to talk to #For reading, the address must be adjusted by 128 DDEAddrR = self.SendByte((self.ddelink + 128), convertyn=1, ackyn=1) if DDEAddrR == self.I2CNAck: print "The DDELink Address did not go through" else: #Send the mask for the data to send. # For the data request bit, its 127 DRBM = self.SendByte(127, convertyn=1, ackyn=1) if DRBM == self.I2CNAck: print "The Data Request Bitmask did not go through" else: # Send a data request bit DRB = self.SendByte(128, convertyn=1, ackyn=1) if DRB == self.I2CNAck: print "The Data Request Bit did not go through" else: # Reset for read self.SendStart() # returns tuple # Address the OOPic to read from it OOPicAddrR = self.SendAddressR() if OOPicAddrR == self.I2CNAck: print "The OOPic Address did not go through" else: if convert == 0: # read the low byte of the data DDEValbyte1 = self.ReadByte(0) print "The OOPic out value is : ", DDEValbyte1 self.SendAck() # read the high byte of data DDEValbyte2 = self.ReadByte(0) print "The OOPic out value is : ", DDEValbyte2 self.SendStop() else: # read the low byte of the data DDEValbyte1 = self.ReadByte() print "The OOPic out value is : ", DDEValbyte1 self.SendAck() # read the high byte of data DDEValbyte2 = self.ReadByte() print "The OOPic out value is : ", DDEValbyte2 self.SendStop() return DDEValbyte1, DDEValbyte2 # To Write OOPic values: def WriteOOPicDDE(self, ValueToSend): #Send an I2C network start self.SendStart() # returns tuple # Address the OOPic to write the address to write to OOPicAddrW = self.SendAddressW() # Send the address of the DDELink Object to talk to. # For writing, the address must be adjusted by 129 DDEAddrW = self.SendByte((self.ddelink + 129), convertyn=1, ackyn=1) # Send the mask for the data to send. # For a DDELink Object, no mask is needed. # is this step really necessary? DRBM = self.SendByte(0, convertyn=1, ackyn=1) # Send the DDELink Buffer byte 0 BuffByte0 = self.SendByte(ValueToSend, convertyn=1, ackyn=1) # Send the DDELink Buffer byte 1 BuffByte1 = self.SendByte(0, convertyn=1, ackyn=1) # Send a data ready bit DRB = self.SendByte(128, convertyn=1, ackyn=1) self.SendStop() def WritebytesOOPicDDE(self, bytelist): for i in bytelist: #Send an I2C network start self.SendStart() # returns tuple # Address the OOPic to write the address to write to OOPicAddrW = self.SendAddressW() # Send the address of the DDELink Object to talk to. # For writing, the address must be adjusted by 129 DDEAddrW = self.SendByte((self.ddelink + 129), convertyn=1, ackyn=1) # Send the mask for the data to send. # For a DDELink Object, no mask is needed. # is this step really necessary? DRBM = self.SendByte(0, convertyn=1, ackyn=1) # Send the DDELink Buffer byte 0 BuffByte0 = self.SendByte(i, convertyn=1, ackyn=1) # Send the DDELink Buffer byte 1 BuffByte1 = self.SendByte(0, convertyn=1, ackyn=1) # Send a data ready bit DRB = self.SendByte(128, convertyn=1, ackyn=1) self.SendStop() ''' ********* An OOPic example: *********************** pressure_i2c = i2c(888,1,41) pressure_i2c.begin() pressure_i2c.WriteOOPicDDE(115) pressure_i2c.WriteOOPicDDE(4) value = pressure_i2c.ReadOOPicDDE() print value ******** A more generic example of reading data: **************************** pressure_i2c = i2c(888,1,41) pressure_i2c.begin() pressure_i2c.SendStart() # Address the object to write the address to read from pressure_i2c.SendAddressW() #Send the address of the DDELink Object to talk to pressure_i2c.SendByte(self.ddelink, convertyn=1, ackyn=1) #Send the mask for the data to send. pressure_i2c.SendByte(127, convertyn=1, ackyn=1) # Send a data request bit pressure_i2c.SendByte(128, convertyn=1, ackyn=1) # Reset for read pressure_i2c.SendStart() # Address the OOPic to read from it pressure_i2c.SendAddressR() # read the low byte of the data value = self.ReadByte() self.SendStop() print value ********** and sending data: ***************************************** pressure_i2c = i2c(888,1,41) pressure_i2c.begin() self.SendStart() # Address the OOPic to write the address to write to pressure_i2c.SendAddressW() # Send the address of the DDELink Object to talk to. pressure_i2c.SendByte(self.ddelink, convertyn=1, ackyn=1) # Send the mask for the data to send. pressure_i2c.SendByte(0, convertyn=1, ackyn=1) # Send the DDELink Buffer byte 0 pressure_i2c.SendByte(ValueToSend, convertyn=1, ackyn=1) # Send the DDELink Buffer byte 1 pressure_i2c.SendByte(0, convertyn=1, ackyn=1) # Send a data ready bit pressure_i2c.SendByte(128, convertyn=1, ackyn=1) pressure_i2c.SendStop() '''