Shop OBEX P1 Docs P2 Docs Learn Events
Just learning i2c — Parallax Forums

Just learning i2c

T ChapT Chap Posts: 4,223
edited 2007-01-17 15:24 in Propeller 1
I made some relay boards that will be driven by the Propeller>PCF8575>ULN2803. The i2c part is a 16bit expansion i/o part. What I need to accomplish is very simple if you know i2c, but with no Spin examples on the exact or similar part, there are a few things to sort out still. There are a maximum or 8 boards that will be used, with 16 relays per board, I have shown a few boards below of the boards. In most cases there will only be one relay on per board at a time, so for example to turn on relay 1, here is what I think the entire Write should look like:

Start 'is start automatically part of the i2c object Write?
01000001 ' 0100 is device type 000 is addres 1 = write mode
ack
00000000
ack
00000001 'relay 1 is on
ack
Stop


For each relay board there is a dip switch to set the address. Would the best method to accomplish the example be the i2c object? If so, here is a snippet I found that seems like the rightidea, but I need to understand how to convert this to simulate the example above.

pub command1 : success                                             'sends first command
  success := i2c.writelocation (deviceaddress, $AC, $00, 8, 8)     'sends device's address, then the register, then the data.  8 address bits, 8 data bits
                                                                   'saves acknowledge bit from device to "success"




I appreciate any direction on what to do with this example or suggestions on where to look to learn this.
614 x 461 - 119K
614 x 461 - 109K
702 x 274 - 37K

Comments

  • parskoparsko Posts: 501
    edited 2007-01-15 12:51
    Originator

    I think "1" is read, to "0" is write, regarding the last bit of your contol byte sent to slave...

    Was that a typo?

    -Parsko
  • T ChapT Chap Posts: 4,223
    edited 2007-01-15 13:11
    0 is write, I was going to edit the post, but yes I caught the mistake. Here is my starting point:


    CON
      _clkmode          = xtal1 + pll16x
      _xinfreq          = 5_000_000
    
    
      i2cSDA        = 26     
      i2cSCL        = 25
      LCD_Pin       = 10
      LCD_Baud      = 9600
      LCD_Lines     = 20
      Address = %0100_000_0     'address is 000     0 = write 
      deviceRegister = 16    '????
    VAR
    OBJ
      i2cObject      : "i2cObject"
    
    PUB Start
      i2cObject.Init(i2cSDA, i2cSCL, false)
      i2cObject.Start 
      repeat
        relayTest
    
    PUB  RelayTest
      i2cobject.writeLocation(Address  ??????)  '0000_0000  0000_0001  relay 1 on
          waitcnt(80_000_000 + cnt)
      i2cobject.writeLocation(Address  ??????)  '0000_0000  0000_0000  relay 1 off
          waitcnt(80_000_000 + cnt)  
    
    
  • parskoparsko Posts: 501
    edited 2007-01-15 13:20
    Dude,

    I'm gonna watch this post closely. I've been trying to get a 24FC515 eeprom to work via the same object and I2C for the last month, with no luck what-so-ever. I was actually going to start a thread myself (this week!) to try to figure out what is wrong, but I haven't completely ruled everything out (that I can find...) From what I can make it, you're going in the correct direction. I can explain the bytes you'll need for the "...writelocation" command:
    deviceaddress - you're control byte
    deviceregister - the address you want to write to in the slave
    i2cdatavalue - the data value you want to write at the address (register)
    addressbits - the number of bits in your address (register) (1 word = 16, 1 byte = 8)
    databits - the number of bits in your data variable (1 byte = 8, 1 word = 16)

    Anything past that, I'm a numbskull. Funny thing is, I'm scripting a 1Mhz I2C bitbanging protocol, and I can't even reliably write 1 byte to memory... grrrrrrr

    Success!

    -Parsko
  • Beau SchwabeBeau Schwabe Posts: 6,568
    edited 2007-01-15 17:12
    parsko,

    I'm sorry that you have been having problems for the last month with the I2C object located in the object exchange.· I looked at the datasheet for the 24FC515, and it looks very similar to the datasheet for the 24LC256.· The main difference other than one being half the memory capacity of the other is that with the 24LC256 there are 3-lines for chip selection, while on the 24FC515 there are only 2.· The remaining bit is used to switch between memory banks to address the doubled memory size.· Here is an object that talks to the 24LC256... the main difference is in the "low level I2C routines".· The object exchange version in some cases will drive the SDA and SCL lines·HIGH even when you tell it not to.· The example below expects a pull-up resistor on BOTH the SCL and SDA lines and does not drive the lines HIGH.







    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Beau Schwabe

    IC Layout Engineer
    Parallax, Inc.

    Post Edited (Beau Schwabe (Parallax)) : 1/15/2007 5:51:05 PM GMT
  • Mike GreenMike Green Posts: 23,101
    edited 2007-01-15 17:29
    Here is another set of I2C routines in SPIN. ReadPage and WritePage read/write a block of data from an EEPROM. The i2cSCL parameter is the pin number of the clock pin while the data pin is always the next numerically higher pin. The EEPROM address consists of 19 bits. The lower 16 bits is used as the starting address within an EEPROM while the upper 3 bits is used for the device address select code. Do notice in the datasheet that you must connect the A2 address select to Vcc or the device won't work. This is particular to the Microchip device. The Atmel equivalent works differently.

    The WriteWait routine is used to check for the end of a write cycle if you don't want to just wait a fixed period. It attempts to read from the device. Once the EEPROM begins a write cycle, it won't respond until it's done. You can use this to tell when the write is finished. The ReadPage and WritePage routines will handle more than one byte at a time. In the case of a read, you can read any size block from within one EEPROM device. In the case of a write, you are limited to within one page. For the 24FC512, this is a 64 byte page. Be careful! You may think you're writing to 64 successive bytes, but the address wraps around at the 64 byte boundary. If you write 64 bytes starting at location 32, you may think you're writing to locations 32-95, but you're actually writing to 32-63,0-31. The advantage to doing these block writes is that they're all written at once taking only one write cycle time (about 5ms).

    Update: I added some constant definitions I had left out in cutting and pasting the routines for posting.

    Post Edited (Mike Green) : 1/15/2007 7:55:45 PM GMT
  • Martin HebelMartin Hebel Posts: 1,239
    edited 2007-01-15 17:41
    Mike,
    If we use a 24LC512 instead of the 256 for Propeller EEPROM, can this driver be used to access the higher 256 for storage?

    I've search the forums about using a 512, but haven't found anything.

    Thanks,
    Martin
  • Mike GreenMike Green Posts: 23,101
    edited 2007-01-15 17:55
    Martin,
    Yes!
    The 24LC256 that's been used with most of the Props so far (Demo Board, PropStick, PEkit) ignores bit 15 of the address that's presented to it. The 24LC512 works exactly the same, but uses bit 15 for addressing. The Protoboard will be using a 24LC512, so will have 64K x 8 EEPROM memory.

    The driver I posted will work happily with a 24LC512. On a Hydra, with one or two Atmel 24LC1024 (128K x 8), it will happily handle a 256K x 8 address space. Beau's driver has an address mask in it of $7FFF. If you take that out, it will also handle a 24LC512 EEPROM. If you need more speed, the routines in the Propeller OS will handle up to a 512K x 8 address space using 8 - 64K x 8 devices. The OS_loaderInit.spin object is written to be usable by itself (without the rest of the OS) for general I2C I/O.
  • Martin HebelMartin Hebel Posts: 1,239
    edited 2007-01-15 18:01
    Thanks Mike, great to hear since the analog telemetry boards I built have 512's connected the same as the protoboards schematics. I'll have to test out access soon!
    -Martin
  • Martin HebelMartin Hebel Posts: 1,239
    edited 2007-01-15 18:24
    Cool, it works, I'm using Beau's, and if I modify the highorder byte mask correctly, I can use addresses 0 to 32767 but have it place it in the upper 256 instead.

    Thanks much!
    -Martin
  • T ChapT Chap Posts: 4,223
    edited 2007-01-15 18:51
    Thanks parkso for the breakdown. I still need to understand what is a register. I thought the address(0100_0000) + the two data bytes was all the device needed according to chart I posted. You say "device address", is that not what I wrote: 0100_0000? Then you say "device register - the address you want" I thought 0100_0000 was the address! If the register is either the high or the low byte of the word, is that a 0 or 1 speciftying either? The data sheet doesn't discuss registers, only the address + data byte 1 + data byte 2.

    So taking the example below again:
    01000000      '0100 is device type 000 is address, 0 = write mode  
    00000000      'first byte  P07-P00
    00000001      'second byte P17-P10  relay 1 is on
    
    



    I may have the bytes written backwards to make relay 1 on, I am calling relay 1 : 00000000_00000001

    but, trying to use your instructions:
    i2c.writelocation ($0100_000_0, resister??, $00000000_00000001, 8, 16)     'address, register unknown, 8 address bits, 16 data bits  
    
    



    deviceaddress - you're control byte
    deviceregister - the address you want to write to in the slave
    i2cdatavalue - the data value you want to write at the address (register)
    addressbits - the number of bits in your address (register) (1 word = 16, 1 byte = 8)
    databits - the number of bits in your data variable (1 byte = 8, 1 word = 16)
  • Mike GreenMike Green Posts: 23,101
    edited 2007-01-15 19:51
    All I2C devices require a "device address" byte. This contains an identification code for the type of device and sometimes a select code to select one of several of the same devices on a single I2C buss. For EEPROMs, the identification code is %10100000 through %10101110. The low order bit always indicates whether this is a write (0) or a read (1) cycle for any I2C transaction.

    An EEPROM access consists of a device address byte followed by two address bytes that go into the address register (MSB first, LSB last) to specify where to begin the transfer, followed by one or more data bytes if you're writing. If you're reading, you have to reselect the EEPROM with a read transaction. The address register stays unchanged and is used for the address of the first data byte.

    All this is described in Philips' documentation on I2C I/O and there are examples given in pretty much any datasheet for an EEPROM.
  • Mike GreenMike Green Posts: 23,101
    edited 2007-01-15 19:52
    Martin & originator,
    I left out a couple of constant definitions when I cut and pasted the I2C routines. Here they are:
    CON
       i2cACK    = 0                       ' I2C Acknowledge
       i2cNAK    = 1                       ' I2C No Acknowledge
       i2cXmit   = 0                       ' I2C Direction Transmit
       i2cRecv   = 1                       ' I2C Direction Receive
       i2cBoot   = 28                      ' I2C Boot EEPROM SCL Pin
       i2cEEPROM = $A0                ' I2C EEPROM Device Address
    
    
  • T ChapT Chap Posts: 4,223
    edited 2007-01-15 22:00
    Here is some hacked up ideas to get this running. Right now it says the i2c part is missing, but more work is needed. I used a schematic that had 10k pullups, I assume it should still function. Maybe someone could at least see if this is in the right direction.

    The next attempt is with Mike's minimal object. It would be great to see an example of a code block using Mike's minimal routine. Would this be close?

    i2cstart(clock_pin)
    i2cWrite(i2cSCL, byte1) 'clock_pin followed by byte 1
    i2cWrite(i2cSCL, byte2) 'clock_pin followed by byte 2
    i2cStop(i2cSCL)


    Is a wait for ack required after each write in this example above?

    The goal is to send this:

    01000000 'address
    00000000 'port a data
    00000001 'port b data

    Post Edited (originator) : 1/15/2007 10:11:43 PM GMT
  • Mike GreenMike Green Posts: 23,101
    edited 2007-01-15 22:25
    The PCF8575 is completely different from the MCP23016 and the MCP23016 code won't work with it. The PCF8575 doesn't have any registers to set, you simply read and write 2 bytes of data. Try putting zero for the number of bits of registers and 16 for the number of bits of data. If you do a readLocation or writeLocation that way, it should read/set the I/O pins to/from a 16 bit value.
  • parskoparsko Posts: 501
    edited 2007-01-15 22:48
    Hey guys,

    Thanks for the info. I used the AtomPro before, and it had canned I2C, so I never learned the protocol. I'm getting into it, and its basically like SPI. They all seem roughly the same at this point. My point, was that I now think I may have a hardware issue on the plug-in board I'm using. Breadboarded one of the spares, but too many distractions tonight (my point, it think it's me, not the objects, wanted to make sure before foot/mouth issues arise). Now it's time to take out the garbage and go to bed...

    Originator, I think you missed a write in there. You are writing 3 bytes, from the diagram you have in your first post above. I think it's start, write control, write data0, write data1, stop. Also take care of your MSB/LSB issue.

    -Parsko
  • Mike GreenMike Green Posts: 23,101
    edited 2007-01-15 23:14
    originator,
    I think your sequence for reading the registers would be
    i2cStart(pin) ' start the transaction
    i2cWrite(pin,$40+i2cRecv) ' this is a read
    Byte0 := i2cRead(pin,i2cACK) ' get the 1st byte
    Byte1 := i2cRead(pin,i2cNAK) ' get the 2nd and last byte
    i2cStop(pin) ' end of transaction
    
    


    For writing
    i2cStart(pin) 'start the transaction
    i2cWrite(pin,$40+i2cXmit) ' this is a write
    i2cWrite(pin,Byte0) ' send the 1st byte
    i2cWrite(pin,Byte1) ' send the 2nd byte
    i2cStop(pin) ' end of transaction
    
    
  • T ChapT Chap Posts: 4,223
    edited 2007-01-16 00:59
    Mike Thanks for the code, that made some significant progress. I am using a small 250ms wall wart for testing and that is likely producing part of the problem. The PCF8575 is running on 3.3v which should be fine. The high outputs that are hitting the two ULN2803's are only at .9V, not enough to turn on the transistors obviously, as they aren't making any sound. I'll bump the supply up in a few.

    The next problem is, I can turn on any one of 16 outputs as you see in the code, but the pin never turns back off. This again could be a result of the PS.

    At least the IC seems to be attempting to do it's job! You know how it is when months of work come together as planned.

    * 1250mA supply is the same, .9V on every out.

    **I removed both i2cstop lines, changed the pauses to any size length and the outputs now follow the data.

    Post Edited (originator) : 1/16/2007 1:24:42 AM GMT
  • T ChapT Chap Posts: 4,223
    edited 2007-01-16 01:29
    Here is the circuit, does it make sense to have a voltage drop to .9V using the ULN2803?

    I am wondering if I swap the PCF8575 Vdd with 5V instead of 3.3V, maybe it will bump it up enough to turn on the darlinton and take the relay to GND. There is no current limiting resistor between the Prop and the PCF8575 in case the IC were to output 5 volts.

    Post Edited (originator) : 1/16/2007 2:19:53 AM GMT
    1188 x 723 - 113K
    2803.jpg 113.2K
  • Mike GreenMike Green Posts: 23,101
    edited 2007-01-16 02:48
    You will need something to protect the Prop if you run the PCF8575 off 5V. Look at the sticky thread about interfacing with 5V logic for specific suggestions for I2C. As an alternative, you might consider the TPIC6595 from Texas Instruments. This is a drop in replacement for a combination of 74HC595 and ULN2803, but with MOSFET outputs and pure logic inputs. It's not I2C and you wouldn't be able to piggyback it onto the boot EEPROM bus, but it's a nice part. Another option would be to drop the ULN2803 and just run some low threshold MOSFETs off the PCF8575 running at 3V.
  • T ChapT Chap Posts: 4,223
    edited 2007-01-16 02:56
    Thanks Mike. I don't get why there is the drop though. I wish I could find a simpler solution than splicing in other parts since I have plenty of the boards, but I'll order the parts you suggested. There must be something I am overlooking here. The wall wart I am testing with has two 5 volt regulators(1.5amp rated) and two 3.3 regulators(500ma) attached, plus a Prop, MAX3232, 8575 and LCD. I would think there would be enough voltage/current to push the 2803's.


    I tried a 6amp 12vdc supply, still only around .95 volts on the outs.

    Post Edited (originator) : 1/16/2007 3:04:30 AM GMT
  • Mike GreenMike Green Posts: 23,101
    edited 2007-01-16 03:08
    The PCF8575 datasheets show the output logic high voltage to be approx. Vcc - 0.1V at currents < 5ma. This would be 3.2V which should be more than high enough for the ULN2803 even at load currents over 300ma.
  • T ChapT Chap Posts: 4,223
    edited 2007-01-16 03:11
    I just discovered that if I set the pins to inputs, then all the i/o's sit at .94 volts.
  • Mike GreenMike Green Posts: 23,101
    edited 2007-01-16 03:30
    I just reread the datasheet for the PCF8575. I don't think it will work with your setup. It has these "quasi-" outputs. When you write a zero to the port, you get a high current (10-20ma) sink capability. When you write a one to the port, you get a momentary high source current that drops quickly (by the end of the I2C clock pulse) to a 100ua source which is way not enough to hold the ULN2803 in conduction. Running the PCF8575 at 5V won't help. You may have to go to using PNP transistors to drive the relays so that the sink current will switch them on.
  • T ChapT Chap Posts: 4,223
    edited 2007-01-16 03:44
    Or put pullups on all the ULN2803 inputs, that might be an easier hack, plus I can maybe place some SO16 resistor arrays on top of the 2803.



    **10k or 20k does the job. Thanks for solving that for me Mike.

    Post Edited (originator) : 1/16/2007 4:12:19 AM GMT
  • OzStampOzStamp Posts: 377
    edited 2007-01-16 05:35
    Hi All

    I have used numerous PCF8574 chips over the years for exactly the same purpose.
    These are the 8 way type ..
    These chips as Mike G. mentioned have Quasi outputs.
    The pins can be either an input or an output ..(on the PCF8575 )
    As an output they can only really sink about 25mA according to the spec sheet I have but they
    can source no current at all as the output is an open drain type output.
    You might be able to get enough drive to the input of the ULN2803 with a smallish pull up resistor
    but be carefull not to exceed the max output current the PCF chip can drive.(max sink current of the PCF chip)
    You would have to invert the signalling as well ( as off (output from PCF) means high to the ULN chip)
    The ULN chips are neat as they have diodes built in for back emf surpression for when the relay turns off.
    I have used also small opto looking like relay driver chips single bit type PC852 from SHARP
    They work great but do need a diode for the back EMF surpression when driving inductive loads.

    Cheers to all its is Damm Hot here today 41 Celsius ...
    Ronald Nollet
    Melbourne Australia
  • T ChapT Chap Posts: 4,223
    edited 2007-01-17 11:33
    These PCF8575 chips are great. I have 3 daisy chained on the i2c bus, each with it's own address. Couldn't be simpler to use. There is a slight downside, on power up, the pins are all High by design. On boot up, all 48 relays are turnng on, and it takes the Propeller about a second to trun them all off. The fact that the PCF8575 outputs require a pullup turned out to be a nice feature in this case, as the bus to the pullups can be routed to an "Enable" relay controlled by a free pin on the Propeller. On bootup, the Enable relay is off while the Prop initializes all the PCF8575's to Low, then it can turn on the Enable relay to feed the pullups. This prevents any problems in case of power failure etc. Only if the Propeller is operating can the relays be turned on. For I/O expansion, this is a nice system. On one i2c bus you can hang 8 addressable 16 I/O chips. Anyone know if you could run multiple i2c objects on multiple pins? If so, the potential would be (16 i/o's * 8 ic's per i2c bus) * 14 pin sets correct? Potential 1792 leds, relays etc?

    (16 i/o's * 8 ic's per i2c bus) * 8 cogs I meant to say

    Post Edited (originator) : 1/17/2007 1:00:52 PM GMT
  • Mike GreenMike Green Posts: 23,101
    edited 2007-01-17 15:24
    Originator,
    You can have as many I2C busses as you are willing to dedicate pairs of pins, each with 8 addressable expanders. The routines I posted require you to supply the pin number of the clock pin on each call. This is deliberate so that you can use more than one buss easily. If you want to change the routines to allow for separate specification of clock and data pins, you could even share all the data pins as long as each buss had its own clock pin. You'd have to be careful about buss loading with a lot more than 8 devices on a pair of pins. Note that you can mix different kinds of devices on the same buss (8 I/O expanders plus 8 EEPROMs).
    Mike
Sign In or Register to comment.