LCD + SD on SPI

Hello,

I try to get Display and an SD-Card working on one SPI-bus (with 2 CS-Pins). The other 3 Pins (MOSI/MISO/SCLK) are connected to both.

If i add the code for the SD-Card, the Display stops working.

Any Idea?
Maybe the both SPI-Implementations disturbing each other.

Regards Frank

sketch_aug31a.zip (6.87 KB)

Any Hints?

You could bit-bang the LCD SPI.

Make sure that the relevant SS (slave select) lines are brought low at the right time. Only one at a time.

in my own class, i take care of that and disable the client after sending data:

  void send(uint8_t value, uint8_t command)
  {
    //Serial.println("send()");
    // take the SS pin low to select the chip:
    digitalWrite(slaveSelectPin,LOW);
    
//sending data...

    // take the SS pin high to de-select the chip:
    digitalWrite(slaveSelectPin,HIGH);
  }

i've looked in sd2card.h/cpp, but don't understand how they initialize the bus. maybe SPI-Speed, bitorder or datamode is incompatible to the display

frank:
i've looked in sd2card.h/cpp, but don't understand how they initialize the bus. maybe SPI-Speed, bitorder or datamode is incompatible to the display

@frank

Yes, that is the problem (as I see it). Below is a snippet of the SD init code:

uint8_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) {
  errorCode_ = inBlock_ = partialBlockRead_ = type_ = 0;
  chipSelectPin_ = chipSelectPin;
  // 16-bit init start time allows over a minute
  uint16_t t0 = (uint16_t)millis();
  uint32_t arg;

  // set pin modes
  pinMode(chipSelectPin_, OUTPUT);
  chipSelectHigh();
  pinMode(SPI_MISO_PIN, INPUT);
  pinMode(SPI_MOSI_PIN, OUTPUT);
  pinMode(SPI_SCK_PIN, OUTPUT);

#ifndef SOFTWARE_SPI
  // SS must be in output mode even it is not chip select
  pinMode(SS_PIN, OUTPUT);
  digitalWrite(SS_PIN, HIGH); // disable any SPI device using hardware SS pin
  // Enable SPI, Master, clock rate f_osc/128
  SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);
  // clear double speed
  SPSR &= ~(1 << SPI2X);
#endif  // SOFTWARE_SPI
...
}

As you can see it uses the SPI hardware very different than your display. The "easy" way around this is to do as Nick said and use software SPI for one of them.

This shows one of the problems with the Arduino SPI implementation and how this ripples into libraries and basically makes it a single SPI device software interface. The current implementation is also very difficult to use with interrupts and threads (i.e. concurrent programming).

Cheers!

Which options are different and which value (const)? Maybe i can set my values after access the sdcard.

What LCD are you using? A link to the datasheet might help.

SD cards do not usually play nicely with other devices on an SPI bus. While they have an "SPI compatible" mode of operation that is not 100% true SPI. There are times when the SD card requires SPI clocks with the CS pin deasserted, for example. Also it doesn't always cleanly release the SPI bus when you want to use it for some other device.

It is recommended that if you wish to share the SPI bus between an SD card and any other device that you isolate the SD card from the rest of the bus via a tri-state buffer, like a 74HC245.

Also, sharing disparate devices on an SPI bus with the control structures as simple as the Arduino has them is troublesome at best. If two devices require different data modes, or operate at different speeds, you will need to wrap each and every call to SPI.transfer() (or whatever method the library in question uses) with code to set the right mode, otherwise one device will, as you have seen, stop the other from working.

SD cards do not usually play nicely with other devices on an SPI bus. While they have an "SPI compatible" mode of operation that is not 100% true SPI. There are times when the SD card requires SPI clocks with the CS pin deasserted, for example. Also it doesn't always cleanly release the SPI bus when you want to use it for some other device.

I do not agree. I use mine with my w5100 all the time, as does zoomkat and several others here. There are some SD shields and breakout boards that do not play well with other devices because they do not have a logic level converter, but that is not the fault of the SD card or library.

The SPI mode and bit order can be changed on the fly easily. I've helped many people on the forum with this. Here is the latest:
http://forum.arduino.cc/index.php?topic=185306.0

SurferTim:

SD cards do not usually play nicely with other devices on an SPI bus. While they have an "SPI compatible" mode of operation that is not 100% true SPI. There are times when the SD card requires SPI clocks with the CS pin deasserted, for example. Also it doesn't always cleanly release the SPI bus when you want to use it for some other device.

I do not agree. I use mine with my w5100 all the time, as does zoomkat and several others here. There are some SD shields and breakout boards that do not play well with other devices because they do not have a logic level converter, but that is not the fault of the SD card or library.

The SPI mode and bit order can be changed on the fly easily. I've helped many people on the forum with this. Here is the latest:
Single SPI, two devices, two SPI modes - Networking, Protocols, and Devices - Arduino Forum

That logic level conversion acts as a crude form of buffer and helps to keep the SD separated from the rest of the bus. Ideally for proper isolation and combined level shifting you'd use a proper dual supply buffer, like a 74LVC8T245.

As long as enough time is given between finishing an SD card transaction and the next SPI transaction with another device to give the SD card time to release the bus (the time required depends on the card, not the shield - some do it faster than others, and some are a right pain) then it is possible to get away with no buffering at all - but that impacts your maximum throughput rate of the SPI bus.

In general, on systems that run at 3.3v so don't need any level shifting, I tend to dedicate an SPI bus to the SD card and use a second bus for all other devices. But then on the devices I use at 3.3v I have 4 SPI busses at my disposal anyway...

I don't agree with any of that either, except the part about the 3.3v Arduinos not needing a converter.

SurferTim:
I don't agree with any of that either, except the part about the 3.3v Arduinos not needing a converter.

Well, I'm sorry you feel that way, I really am. But this isn't about opinions, this is about cold hard facts. The fact is that some cards, cheap ones especially, do not play nicely on shared busses. It's not an opinion, it's a fact. Better shields take this into account and incorporate buffering with their level translation. Cheaper shields don't.

On the Arduino it's less of a problem anyway because it is too slow to matter. The Arduino isn't capable of communicating at the full speed of an SD card, so by the time the next SPI transaction comes along the SD card has had a chance to settle anyway.

Oh, and the newer specifications for the SD one-bit interface specify that cards should be connected separately, even though the bus was envisaged to allow up to 30 cards to be ganged together.

I'm good with differing opinions. Now the test. If frank posts a link to the device with a datasheet...

with code of lcd and SD enabled, the SD-Card works correctly, but the display ist not showing anything. disabling the init-code of the SD-Card...and the display works

so it seems really that different SPI-Modes used...so my thought that i can set the mode of the device before using it in my code without building a separate SPI-Bus (more cables and Ports blocked)
but i cannot decode the Modes (SPI-Mode,Data-Mode) used for SD-Card from the code

Datasheet of display:

SPI-Modes I'm using:

    SPI.setBitOrder(LSBFIRST);
    SPI.setDataMode(SPI_MODE3);
    SPI.setClockDivider(SPI_CLOCK_DIV64);

That is correct. Mode 3 and LSBFIRST for the LCD display. Mode 0 and MSBFIRST for the SD. I like this datasheet better. It has all the SPI timing stuff that I needed.

So to use both together, I suggest changing the mode and bit order before each LCD transfer, and changing it back when complete. I try to leave the SPI bus in default when finished with any SPI bus functions, which is MODE0 and MSBFIRST.

    // change to LCD settings
    SPI.setDataMode(SPI_MODE3);
    SPI.setBitOrder(LSBFIRST);

    // do your LCD stuff

    // change back to default (SD, w5100 etc) settings
    SPI.setDataMode(SPI_MODE0);
    SPI.setBitOrder(MSBFIRST);

edit: I forgot to mention that all SPI devices should be disabled (all SS pins HIGH) when changing the mode and bit order, or you will have problems.

thank you SurferTim
can i read out the actual SPI-Modes?

so i can read them, set my own and reset them to old value after doing my LCD-Stuff (making it more flexible)?

When you get comfortable with the operation of different modes and bit orders, you can get fancy. I recommend keeping it simple until then.

You can use a variable to keep track of the settings, and change the mode and bit order if it doesn't match what you need for that device.

BTW, those functions that change the mode and bit order are really fast. You are not losing much time calling them like I suggested.

how can i read the the SPI-Mode-Values?

There are no read functions for the SPI settings. You would need to check the SPI SPCR register. Here are the write functions if that will help you.

void SPIClass::setBitOrder(uint8_t bitOrder)
{
  if(bitOrder == LSBFIRST) {
    SPCR |= _BV(DORD);
  } else {
    SPCR &= ~(_BV(DORD));
  }
}

void SPIClass::setDataMode(uint8_t mode)
{
  SPCR = (SPCR & ~SPI_MODE_MASK) | mode;
}

i tried this with initializing and get both running (but strange chars on display).

maybe the speed differs?

sketch_aug31a.ino (2.01 KB)