I2C and SPI, which one to bash!

Discussion in 'Embedded Systems and Microcontrollers' started by geoffers, Oct 9, 2013.

  1. geoffers

    Thread Starter Senior Member

    Oct 25, 2010
    Hi all,

    Yet more questions I'm afraid! I'm trying to design a circuit at the moment, I would like to use ic's and modules attached to one pic that use I2C and SPI.

    I will have 3 I2C chips and a SPI radio module running, I think I probably know the answer but its best to ask first I feel! Most pic's have the I2C and spi in one module, I would like to use the smallest pic I can, (8pin) but am gussing pull ups for I2C would mess up SPI coms?

    The good news is I still have 4 io left if I use the I2C module. Has any bit bashed SPI or I2C? which might be easier? As I want to use 3 I2C devices I guess I will want to write a routine for the SPI (1 device) anyone know of any pitfalls or problems I might encounter?

    Cheers Geoff
  2. shteii01

    AAC Fanatic!

    Feb 19, 2010
    Is the same set of pins used for I2C and SPI?

    I have worked with Arduino Due, it has one set of pins for I2C, second set of pins for SPI.
  3. geoffers

    Thread Starter Senior Member

    Oct 25, 2010
    Unfortunatly it is! SCL and SCK are on one pin SDA and SDI on another.
    Cheers Geoff
  4. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    You should have little trouble finding "soft" solutions for either bus, so pick the one that looks easier to you to implement in software and reserve the hardware for the other.

    First time I did I2C was on a device that had no module. Happened to be a Basic Stamp, so there's one place to find examples.

    One thing to state: most I2C code (for both soft and hard solutions) I've seen tend to ignore the ACK status bit tat informs you the transfer was successful.
    geoffers and absf like this.
  5. JohnInTX


    Jun 26, 2012
    Neither is particularly hard to do if you are coding the master talking to a slave but if you have to choose, use the hardware SSP module for I2C and bit-bang the SPI.

    SPI master is easier to do in bit bang. Once the SPI slave is selected, just fire away with clock and data, receiving one byte for each you send.

    I2C has port-direction changes (to detect ACK) and you must monitor SCL for stretches by the slave and be ready for unexpected NAKs etc.

    As a slave, bit banging either is a pain. You can do it but either SPI or I2C would require careful programming probably using interrupts (for detecting SS/ for SPI / S-P for I2C) to keep things manageable.

    I've seen that too and I'm not sure what they are doing. NAK is used for many things not the least of which is knowing you have actually addressed a particular slave and if its happy with the data transfer. Peevish of me, but those are useful things to know - its also I2C spec.

    Just my own experiences - YMMV
    Last edited: Oct 9, 2013
    geoffers likes this.
  6. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    While a slave *may* pull the clock down to give itself some extra time to process info... nothing requires a slave to do such and most do not need this time.

    If you are building the system you can pick parts that don't need this time, have one less thing to worry about, and skip the test in your code (like 2 or 3 extra statements were gonna kill you anyway).
  7. geoffers

    Thread Starter Senior Member

    Oct 25, 2010
    Thanks Guys, I've been looking at the data sheet and think I will bit bang the spi port:) Shall post my code when I get it sorted!
  8. Markd77

    Senior Member

    Sep 7, 2009
    Some PICs don't support i2c master mode. Here's something I bashed together a while ago, I used it for storing in serial EEPROM, the project is unfinished, but this bit works on PIC16F819 (yes, the TRIS command still works):
    Code ( (Unknown Language)):
    1. i2c_store_32_bytes
    2.     bcf INTCON, GIE
    3.     call i2c_start
    4.     movlw b'10100000'       ;control byte for write
    5.     movwf i2c_buffer
    6.     call i2c_sendbyte
    7.     movlw b'00000000'       ;address high byte
    8.     movwf i2c_buffer
    9.     call i2c_sendbyte
    10.     movlw b'00000000'       ;address low byte
    11.     movwf i2c_buffer
    12.     call i2c_sendbyte  
    14.     movlw d'32'
    15.     movwf d1
    16. saveloop
    17.     movf INDF, W
    18.     movwf i2c_buffer
    19.     call i2c_sendbyte
    20.     incf FSR, F
    21.     decfsz d1, F
    22.     goto saveloop
    23.     call i2c_stop
    24.     return
    28. i2c_start
    29.     bsf PORTB, 2            ;scl high
    30.     call delay5us
    31. bcf PORTB, 3
    32.     movlw b'00000010'       ;sda low
    33.     TRIS PORTB
    34.     call delay5us
    35.     bcf PORTB, 2            ;scl low
    36.     return
    40. i2c_stop
    41. bcf PORTB, 3
    42.     movlw b'00000010'       ;sda low
    43.     TRIS PORTB
    44.     bsf PORTB, 2            ;scl high
    45.     call delay5us
    46.     movlw b'00001010'       ;sda high
    47.     TRIS PORTB
    48.     call delay5us           ;??
    49.     return
    51. i2c_sendbit
    52.     movlw b'00000010'       ;assume 0 bit sda low
    53.     btfsc i2c_buffer, 7
    54.     movlw b'00001010'       ;bit is 1 sda high
    55. bcf PORTB, 3
    56.     TRIS PORTB
    57.     call delay5us
    58.     bsf PORTB, 2            ;scl high
    59.     call delay5us
    60.     bcf PORTB, 2            ;scl low
    61.     movlw b'00001010'       ;release sda
    62.     TRIS PORTB
    63.     return
    65. i2c_receivebit              ;result in STATUS, C
    66.     movlw b'00001010'       ;bit is 1 sda high
    67.     TRIS PORTB
    68.     call delay5us
    69.     bsf PORTB, 2            ;scl high
    70.     call delay5us
    71.     bcf STATUS, C       ;assume sda low
    72.     btfsc PORTB, 3
    73.     bsf STATUS, C       ;sda high
    74.     bcf PORTB, 2            ;scl low
    75.     return
    77. i2c_send_ack                ;result in STATUS, C
    78.     movlw b'00000010'       ;bit is 0 sda low
    79. bcf PORTB, 3
    80.     TRIS PORTB
    81.     call delay5us
    82.     bsf PORTB, 2            ;scl high
    83.     call delay5us
    84.     bcf PORTB, 2            ;scl low
    85.     movlw b'00001010'       ;release sda
    86.     TRIS PORTB
    87.     return
    91. i2c_sendbyte
    92.     movlw d'8'
    93.     movwf count
    94. i2c_sendbyte_loop
    95.     call i2c_sendbit
    96.     rlf i2c_buffer, F
    97.     decfsz count, F
    98.     goto i2c_sendbyte_loop
    100.     call i2c_receivebit     ;ack bit, ignore it
    101.     return
    103. i2c_receivebyte
    104.     movlw d'8'
    105.     movwf count
    106. i2c_receivebyte_loop
    107.     call i2c_receivebit         ;result in STATUS, C
    108.     rlf i2c_buffer, F           ;move into buffer
    109.     decfsz count, F
    110.     goto i2c_receivebyte_loop
    111.     btfsc flags, send_ack
    112.     goto ack_send
    113.     call i2c_receivebit     ;ack bit, ignore it
    114.     return
    115. ack_send
    116.     call i2c_send_ack
    117.     return
    Eric007 likes this.
  9. ErnieM

    AAC Fanatic!

    Apr 24, 2011

    It looks like some formatting got lost, I don't see any place where you control SDA and SCL with TRIS. (Is "TRIS PORTB" an undefined macro?)

    TRIS is the only way to control these lines as you are simulating an open collector output. To do that you make the Port (or preferably the LATCH if available) a low, then use TRIS to set the pin as output (to pull it low) or as an input to let the resistors pull it high.

    Driving either line high will be trouble for the slave when it tries to pull the line low.
  10. Markd77

    Senior Member

    Sep 7, 2009
    I should have qualified it a bit, clock is on pin 3, data on pin 2. Because I was only using one master and I knew that the EEPROM wouldn't be doing any clock stretching I used only normal output on the clock pin, but the data pin uses TRIS to switch it between input and output low. The TRIS instruction (not a macro) continues to be included in a lot of PICs even though it isn't mentioned in the datasheet any more.
    It's a lot faster to write:
    movlw x
    movlw x
    banksel TRISB
    movwf TRISB
    banksel PORTA
  11. WBahn


    Mar 31, 2012
    I haven't bit banged SPI but I have both bit-banged SPI master and slave and implemented them in digital logic in both FPGAs and on ASICs. Both are pretty straight forward. My feeling is that I2C would be a bit harder because it has to deel with collision detection and arbitration. They way it does it is pretty simple, but it still adds to the complexity somewhat.
  12. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    @Mark: Be that as it may you should never actively drive SCL or SDA high.

    @WBahn: collision detection and arbitration are not issues in a single master system.
  13. WBahn


    Mar 31, 2012
    I guess I'm not seeing where it was specified that this was a single master system. For that matter, I didn't see where it was indicated that it was reasonable to assume that none of the devices would stretch the clock.

    Since I haven't used I2C much, it may well be that something in the OP's description implies a constraint that makes these reasonable assumptions, but I don't see it.
  14. djsfantasi

    AAC Fanatic!

    Apr 11, 2010
    See post 10
  15. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    Post 10 was not made by the op. <grin>

    However, post 1 doesn't not indicate any other bus masters, just slaves requiring different buses.
  16. WBahn


    Mar 31, 2012
    Post #1 doesn't indicate any bus masters one way or another. Again, it's been well over a decade since I tinkered even briefly with I2C, so maybe this is an unreasonable reading, but when he says he has three chips that use I2C that we wants to connect to his PIC, couldn't one of those be a bus master that he has no control over?
  17. WBahn


    Mar 31, 2012
    I don't see where using pullups on SPI pins would cause problems as long as the pullups are strong enough to get the line HI in time. And if you reconfigure the pins to drive active HI when the port is SPI, then the presence of pullup resistors will be largely immaterial.

    If you did share port pins, how are you planning to do the context switching so that the other devices know when they can assert signals on the lines? With SPI this is easy, of course since you have explicit chip enable pins for each device. But with I2C how are you going to ensure that the bus activity when communicating with the SPI device won't be seen by one of the I2C devices as a message addressed to it?
  18. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    While it is not completely impossible to do things that way it is extremely unlikely.

    Masters on the bus tend to be microcontrollers, with a single controller you have a single master system.
  19. JohnInTX


    Jun 26, 2012
    You can probably get away with it as long as any transitions on the shared SDA line are done only when SCL is low to avoid generating Start/stoP conditions on the I2C bus. The SPI bus and connected devices would have to use that clocking scheme. The connected I2C devices should ignore everything on the bus until Start is detected and an address is sent.

    That said, sharing the bus is something to be done with care and attention paid to pullups etc. and plenty of testing in all modes. Keep in mind that not all I2C devices implement the complete I2C spec and may behave badly when things are not just so on the bus.

    IMHO, it would be impractical or impossible to do a multi-master configuration with a composite I2C/SPI bus.
  20. geoffers

    Thread Starter Senior Member

    Oct 25, 2010
    I didn't expect so many response's to this!

    Just to confirm a few things, I'm planning to have 3 slave I2C devices and one slave SPI, I'm still not sure how to go about it? Its going to take me a while to write a spi routine so if I think I can 'get away?!' with mixing things I might?

    My main concern was the pull ups, I2C needs them spi doesn't. My inital plan was to use the mssp module on a 12f1822, configure it for I2C for the I2C stuff then reconfigure for spi when needed, if I do write a routine it will be for spi!

    John, you said sharing the bus is possible if you make sure a transition on the sda/sci pin only occurs while scl/sck (they are shared to) is low. I've had a look at the data sheet and there a different clock options, change on rising/falling edge, if I have it on the falling edge would this avoid messing up any I2C stuff while using the mssp module for both?

    Thanks for all the input, Geoff