Single SPI, two devices, two SPI modes

Hello. I'm dealing with some problems conecting multiple devices to Arduino Uno via its SPI. - First device is PS021 by Acam, ADC, SPI: Data order: MSB first, Clock Phase Bit =1, Clock Polarity Bit =0. Already connected, works fine and is the key element in my design, so changing this IC is not an option. - New device I'm trying to implement in my design is bluetooth. I've found Nordic Semiconductor nRF8001 and nRF8002. SPI: Data order: LSB first, Clock polarity: 0(base value for the clock is zero), clock phase: 0(data is read on the clock’s rising edge). So, right here lies my problem, different clock and phase polarity. Can I change SPI_mode in the middle of code? For instance:

digital.write(select_PS021, LOW);
SPI.setDataMode(SPI_MODE1);
...
digital.write(select_PS021, HIGH)

and then change SPI mode for second device:

digital.write(select_nRF8001, LOW);
SPI.setDataMode(SPI_MODE0);
...
digital.write(select_nRF8001, HIGH)

How fast is this reconfiguration? Could there be any problems calling this function couple hundreds per seconds(I could try...)? I've got some kind of real time operating system, and time determined code is very important.

If changing SPI mode is not an option... What is simpler? Choose some other AVR with dual SPI interface, or find some other bluetooth device? If anyone know where to find some bluetooth ICs, please help, I did ask uncle Google, but this time he didn't participate... Standard bluetooth modules like HC-05 is not an option(two large) - I'm searching something, that could later be used for mass production... Even better is bluetooth IC with UART interface, but I couldn't find any.

thanks!

You can change modes and bit order on the fly. It will be changed when that SPI function returns.

I recommend changing the mode and bit order before you enable the SPI slave select for that device, and don't change it again until you disable the slave select for that device. In other words, all SPI devices should be disabled when those mode and bit order changes are done.

edit: Like this.

SPI.setDataMode(SPI_MODE1);
SPI.setBitOrder(MSBFIRST);
digital.write(select_PS021, LOW);
...
digital.write(select_PS021, HIGH)

SPI.setDataMode(SPI_MODE0);
SPI.setBitOrder(LSBFIRST);
digital.write(select_nRF8001, LOW);
...
digital.write(select_nRF8001, HIGH)

SurferTim, thanks! This could save me some money if functions SPI.setDataMode(); and SPI.setBitOrder(); would be fast enaugh... Do you have any idea, how fast is it done? I could measure it by inserting micros(), if anyone doesn't know. In fact, time is very important for my aplication, so, is there any informations about how much time(or clock cycles) certain code from arduino default library needs to complete? Let me say, how can I know how fast function millis() or micros() is completed? If this was discused in another topic, please redirect me.

Thanks!

I think it is going to be really fast. Here is the code from the SPI library:

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;
}

If you need to know how fast, use micros(). It will certainly be faster than 1ms.

Then problem is solved! Thanks!

Hallo Surfer Tim..

I tested your Idea on the last days with these function:

First my Setup: void Setup() {

Serial.begin(9600);

pinMode(A0,OUTPUT); // CS MOTOR digitalWrite(A0,HIGH); pinMode(10,OUTPUT); // Pin 10 select the Wiznet5100 Ethernet Chip

Ethernet.begin(myMac ,myIp); digitalWrite(10, HIGH);

server.sockOpen(serverPort);

}

Second :

byte L6470::Xfer(byte data) // This Function is called when i need to get and send any value from my motor (speed, Position) { // This simple function shifts a byte out over SPI and receives a byte over // SPI. Unusually for SPI devices, the dSPIN requires a toggling of the // CS (slaveSelect) pin after each byte sent. byte data_out; SPI.setDataMode(SPI_MODE3); digitalWrite(_SSPin,LOW); // SPI.transfer() both shifts a byte out on the MOSI pin AND receives a // byte in on the MISO pin. data_out = SPI.transfer(data); digitalWrite(_SSPin,HIGH); SPI.setDataMode(SPI_MODE0); return data_out; } I have just a cuestion :

I need to read the Position from my Stepper Driver on the fly at the same time that i worked with the Ethernet Shield in order to establish communication with the user interface If I understand well it means acctually I could not work really at the same time with the 2 devices..or... I need to do something like "SPI MODE SWITCHING" as you are suggesting

The issue:

The whole program works as soon remove the second line o my L6470::Xfer() code ( SPI.setDataMode(SPI_MODE3)) ; because L6470 works also "uncorrectly" with SPI(Mode0) as Ethernet Shield It results im getting wrong values from the position register..That means im not acttualy switching between the 2 SPI MODES

I dont really understand where and How should I implemented your idea:

Could You help me with these issue??

Thanks!

You might need to slow up the SPI bus. That device is pretty slow. It looks like 1MHz is about as fast as it will go, maybe as low as 600KHz. The code below will adjust the mode to MODE3 and speed to 500KHz, then set both back to default.

Here is the datasheet. http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Robotics/dSPIN.pdf

  SPI.setDataMode(SPI_MODE3);
  SPI.setClockDivider(SPI_CLOCK_DIV32);
  digitalWrite(_SSPin,LOW);
   // SPI.transfer() both shifts a byte out on the MOSI pin AND receives a
   // byte in on the MISO pin.
  data_out = SPI.transfer(data);
  digitalWrite(_SSPin,HIGH);
  SPI.setClockDivider(SPI_CLOCK_DIV4);
  SPI.setDataMode(SPI_MODE0);

edit: I forgot to mention to disable the L6470 SPI before starting the w5100. That could trash up the SPI bus and cause unpredictable results in both devices. Disable the SD card also if you have one in the shield's SD card slot.

void Setup() {

 Serial.begin(9600);                

  // disable L6470 SPI while starting w5100
  pinMode(_SSPin,OUTPUT);
  digitalWrite(_SSPin,HIGH);

  // disable SD card SPI while starting w5100
  pinMode(4,OUTPUT);
  digitalWrite(4,HIGH);

  pinMode(A0,OUTPUT);                 // CS MOTOR  
  digitalWrite(A0,HIGH);
  pinMode(10,OUTPUT);                // Pin 10 select the Wiznet5100 Ethernet Chip

  Ethernet.begin(myMac ,myIp); 
  digitalWrite(10, HIGH);

Thank you for your quicky answer..im very thankfully for your help I tried the idea but unfortunately it doesn´t works..i tested will all the posible clock div combinations.. but the whole system come down..

My second cuestion ist: I Have this function void L6470::init(){ // This is the generic initialization function to set up the Arduino to // communicate with the dSPIN chip. // set up the input/output pins for the application. //pinMode(SSPin, OUTPUT);

pinMode(_SSPin, OUTPUT); digitalWrite(_SSPin, HIGH); pinMode(MOSI, OUTPUT); pinMode(MISO, INPUT); pinMode(SCK, OUTPUT); pinMode(BUSYN, INPUT); pinMode(RESET, OUTPUT);

// reset the dSPIN chip. This could also be accomplished by // calling the "L6470::ResetDev()" function after SPI is initialized. digitalWrite(RESET, HIGH); delay(1); digitalWrite(RESET, LOW); delay(1); digitalWrite(RESET, HIGH); delay(1);

// initialize SPI for the dSPIN chip's needs: // most significant bit first, // SPI clock not to exceed 5MHz, // SPI_MODE3 (clock idle high, latch data on rising edge of clock) SPI.begin(); SPI.setBitOrder(MSBFIRST); SPI.setClockDivider(SPI_CLOCK_DIV4); // or 2, 8, 16, 32, 64 SPI.setDataMode(SPI_MODE3);

}

I call this init function all the time on my Ardio loop () function.. Do you think that im conflicting the whole thing?? using these function.. should i do these init procedure in other place?

Thanks...

You are making too much of this. You should not call that L6470::init() function at all, and certainly not in the loop() function.

What pin are you using for the L6470 slave select?

edit: And what pin for the L6470 RESET?

Im using the Analog Pin 0 as Select chip for the l6470 Reset Pin ist the DigitalPin 6

Do you have an example or test sketch for the L6470 that works ok? If so, post that sketch or a link to it. If not, you need one.

Sorry i just calll my init function one time on don´t all the time

Hi Surfer Tim
Thanks for your help
The Code is here:
Attached…

MotionControl_03_test.ino (8.96 KB)

and the library

L6470.rar (511 KB)

Any time you reference one of these, you must set the mode to 3. The example code also reduces the SPI bus speed to SPI_CLOCK_DIV16 (1MHz).

L6470 stepper1(A0);                   // Select_Pin Arduino A0,A1,A2
L6470 stepper2(A1);
L6470 stepper3(A2);

Then when finished, set the mode to 0 and the speed to SPI_CLOCK_DIV4.

edit: Like this.

void loop()
{  
// here
  SPI.setDataMode(SPI_MODE3);
  SPI.setClockDivider(SPI_CLOCK_DIV16);
  stepper1.setMicroSteps(128); 
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV4);

  if(server.available()){ 
    rcvMes=server.getMessage();
    if (!strcmp( rcvMes->getZ_OSCAddress() ,  "/motor/linear" )) { 
      setVariables();

//here
      SPI.setDataMode(SPI_MODE3);
      SPI.setClockDivider(SPI_CLOCK_DIV16);
      useMode(stepper1, mode);
      SPI.setDataMode(SPI_MODE0);
      SPI.setClockDivider(SPI_CLOCK_DIV4);
    }
    else if (!strcmp( rcvMes->getZ_OSCAddress() ,  "/motor/pan" )) { 
      setVariables();

// here
      SPI.setDataMode(SPI_MODE3);
      SPI.setClockDivider(SPI_CLOCK_DIV16);
      useMode(stepper2, mode);
      SPI.setDataMode(SPI_MODE0);
      SPI.setClockDivider(SPI_CLOCK_DIV4);
    }
    else if (!strcmp( rcvMes->getZ_OSCAddress() ,  "/motor/tilt" )) { 
      setVariables();

// here
      SPI.setDataMode(SPI_MODE3);
      SPI.setClockDivider(SPI_CLOCK_DIV16);
      useMode(stepper3, mode);
      SPI.setDataMode(SPI_MODE0);
      SPI.setClockDivider(SPI_CLOCK_DIV4);

    }
  }
  else {
// here
    SPI.setDataMode(SPI_MODE3);
    SPI.setClockDivider(SPI_CLOCK_DIV16);
    useMode(stepper1, mode);
    SPI.setDataMode(SPI_MODE0);
    SPI.setClockDivider(SPI_CLOCK_DIV4);
  }
}

And there are calls to the OSC part (ethernet) embedded in the stepperX calls like the useMode() function. Those should go or you must change the SPI settings there also.