i2c and SPI together

I’ve looked around these boards, and darned if I can see if anyone has been able to implement SPI and I2C (master) in the same sketch.

What I find is that as soon as I run SPI.begin();, I2c stops responding.

As a matter of fact, if I just set digital pin 11 to output - DDRB=40 ; - it kills I2C. In other words DDRB=32 ; (digital pin 13 is output) and I2C is fine. DDRB=40 ; (digital pins 13 and 11 are output) and I2C is dead.

Why would the IO status of pin 11 have anything to do with I2C?!

At the moment, I’m working to redo the necessary functionality of Wire until I either A) discover what the problem with Wire is, and fix it, or B) redo it myself.

So, who else has SPI and I2C working side-by-side? Thanks!

Anyone using the adafruit RTC/SD shield would have both working together.

What arduino board do you have?

I’m using the LinkSprite Diamondback Arduino-Wifi board, and trying to add on the LSM303 Compass / Tiltmeter from Sparkfun. I had the problem with the Wifi + Compass combo sketches, and substituted the sketch from the examples for the SPI barometric pressure sensor. After it executes SPI.begin, the I2C gets stuck in an infinite loop.

I’m new at this, and just kinda combined the Compass and SPI sketches:

#include <SPI.h>
#include <Wire.h>
#include <LSM303DLH.h>

LSM303DLH compass;

void setup() {
Serial.begin(57600);
Wire.begin();
compass.enable();
// SPI.begin();
DDRB=40 ;

There is no particular reason why it shouldn’t work, and indeed I got SPI and I2C to work together between two Unos.

Master:

// Written by Nick Gammon
// July 2011
// SPI/I2C master

#include <SPI.h>
#include <Wire.h>
#include "pins_arduino.h"

#define SLAVE_ADDRESS 42

// command we might send
#define CMD_ID     1

void setup (void)
{
  Wire.begin();   
  Serial.begin(115200);  // start serial for output
  
  // Put SCK, MOSI, SS pins into output mode
  // also put SCK, MOSI into LOW state, and SS into HIGH state.
  // Then put SPI hardware into Master mode and turn SPI on
  SPI.begin ();
  // Slow down the master a bit
  SPI.setClockDivider(SPI_CLOCK_DIV16);
  
}  // end of setup


void loop (void)
{

  char c;

  // enable Slave Select
  digitalWrite(SS, LOW);    // SS is pin 10

  // send test string
  for (const char * p = "Hello, world!\n" ; c = *p; p++)
    SPI.transfer (c);

  // disable Slave Select
  digitalWrite(SS, HIGH);

  delay (200);  // 200 mS delay 

  Wire.beginTransmission (SLAVE_ADDRESS);
  Wire.send (CMD_ID);
  Wire.endTransmission ();
  
  Wire.requestFrom (SLAVE_ADDRESS, 1);  
  
  if (Wire.available ())
    {
    Serial.print ("Slave is ID: ");
    Serial.println (Wire.receive (), DEC);
    }
  
  delay (1000);  // 1 second delay 
}  // end of loop

Slave:

// Written by Nick Gammon
// July 2011
// SPI/I2C slave

#include "pins_arduino.h"
#include <Wire.h>

char buf [100];
volatile byte pos;
volatile boolean process_it;

// command we might get
#define CMD_ID     1
#define MY_ADDRESS 42

char command;

void setup (void)
{
  Serial.begin (115200);   // debugging

  // have to send on master in, *slave out*
  pinMode(MISO, OUTPUT);
  
  // turn on SPI in slave mode
  SPCR |= _BV(SPE);
  
  // get ready for an interrupt 
  pos = 0;   // buffer empty
  process_it = false;

  // now turn on interrupts
  SPCR |= _BV(SPIE);
  
  Wire.begin (MY_ADDRESS);
  Wire.onReceive (receiveEvent);  // interrupt handler for incoming messages
  Wire.onRequest (requestEvent);  // interrupt handler for when data is wanted

}  // end of setup


// SPI interrupt routine
ISR (SPI_STC_vect)
{
byte c = SPDR;  // grab byte from SPI Data Register
  
  if (!process_it)
    {
    // add to buffer if room
    if (pos < sizeof buf)
      {
      buf [pos++] = c;
      
      // example: newline means time to process buffer
      if (c == '\n')
        process_it = true;
        
      }  // end of room available
  } // if not waiting for previous one to be processed
  
}  // end of interrupt routine SPI_STC_vect

// main loop - wait for flag set in interrupt routine
void loop (void)
{
  if (process_it)
    {
    buf [pos] = 0;  
    Serial.println (buf);
    pos = 0;
    process_it = false;
    }  // end of flag set
    
}  // end of loop

void receiveEvent (int howMany)
 {
   command = Wire.receive ();  // remember command for when we get request
} // end of receiveEvent


void requestEvent ()
{
 switch (command)
   {
   case CMD_ID:      Wire.send (0x55); break;   // send our ID 

   }  // end of switch
  
}  // end of requestEvent

I connected together pins 10 … 13 (for SPI) A4/A5 (for I2C) and +5V and Gnd.

Then uploaded both sketches and opened the serial monitor on each one.

One of them echoed back the “hello, world” that was being sent by SPI:

Hello, world!

Hello, world!

Hello, world!

Hello, world!

Hello, world!

Hello, world!

Hello, world!

Hello, world!

Hello, world!

The other one echoes back the slave ID that was requested by I2C:

Slave is ID: 85
Slave is ID: 85

The slave uses interrupts to process both SPI and I2C incoming.

gknight4: As a matter of fact, if I just set digital pin 11 to output - DDRB=40 ; - it kills I2C. In other words DDRB=32 ; (digital pin 13 is output) and I2C is fine. DDRB=40 ; (digital pins 13 and 11 are output) and I2C is dead.

Personally I wouldn't be doing this:

DDRB=40 ;

For one thing it isn't clear which bits you are changing (as 40 is decimal). 0x28 would be slightly better. But why not do the pinMode function calls? That makes it much more self-documenting. Then you are not changing other pins which might have been initialized elsewhere (eg. in compass.enable(); ).

However you don't even need to change any pin modes. SPI.begin () does that. As explained in the Atmega328 manual on page 168:

When the SPI is enabled, the data direction of the MOSI, MISO, SCK, and SS pins is overridden according to Table 18-1 on page 168. For more details on automatic port overrides, refer to ”Alternate Port Functions” on page 80.

Nick, THANKS for explaining this; I didn't understand it...

One of the details that can Bite!

Regards, Terry King ...In The Woods In Vermont terry@yourduino.com

Oh, Arrgh. Sorry!

It was a, um, intershield short between a header to attach a daughter board that was put in with the long pins down, and the ICSP pins sticking up. Hence the Digital Pin 11 (PB3) connection. (Darned reliable short! I've had connectors that didn't mate as reliably as these shorted pins did.)

OK, so far I've done this, I've attached Rx to Rx, I've bought shields with incompatible pin usage (The motor shield is a real pin hog! That's way too little utility for way too many pins!).

Is there a directory of classic beginners' stupidity out there, or do we all have to make it up for ourselves?