Go Down

Topic: SPI Pins (Read 2520 times) previous topic - next topic

J T

I see on the Arduino SPI reference page that pins 10, 11, 12, 13 are for the SPI. I see examples and tutorials that use other pins.

Are the examples that use other pins for SPI wrong?

Here is one example.

http://www.ladyada.net/learn/sensors/thermocouple.html

I have an Arduino Uno.

Thanks
John

johnwasser

You can do software SPI on whatever data pins you like.  I expect that is what the thermocouple adapter library is doing.  The hardware SPI is on pins 10-13.
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

scswift

If I'm not mistaken, the hardware SPI is actually pins 11-13.  Pin 10 seems to be most often reccomended as the chip select pin, but as you have to set it high and low manually before you send data with the SPI lib, you could use any pin you want really, and if you want to have more than one chip using the hardware SPI bus then you'll have to if you want to select each chip in turn.

johnwasser


If I'm not mistaken, the hardware SPI is actually pins 11-13.  Pin 10 seems to be most often reccomended as the chip select pin, but as you have to set it high and low manually before you send data with the SPI lib, you could use any pin you want really, and if you want to have more than one chip using the hardware SPI bus then you'll have to if you want to select each chip in turn.


You are right for the most common case of the Arduino being the SPI Master.  In that case the SS pin (D10) is left to user control so it, or any other free data pin, can be used for Slave Select.  If the Arduino is acting as an SPI Slave then the SS pin (D10) is used by the hardware to disconnect the Arduino from the SPI buss when it is not being addressed.
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

CrossRoads

Well, not really disconnect, more to indicate "this message is not for you!"
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

J T


You can do software SPI on whatever data pins you like.  I expect that is what the thermocouple adapter library is doing.  The hardware SPI is on pins 10-13.


What does hardware SPI vs software SPI mean?

Thanks
John

CrossRoads

software spi is the shiftout command, basically:
digitalWrite (output_pin, bit0_of_data_byte);
digitalWrite (clock_pin, LOW);
digitalWrite (clock_pin, HIGH);
repeat  7 times for bits 1-7.
Only written more efficiently. Still controlled bit by bit.

Hardware spi is basically a built-in shift-out hardware register that spits the same data out way quicker.
See section 18 of the ATMega328 data sheet.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

retrolefty


software spi is the shiftout command, basically:
digitalWrite (output_pin, bit0_of_data_byte);
digitalWrite (clock_pin, LOW);
digitalWrite (clock_pin, HIGH);
repeat  7 times for bits 1-7.
Only written more efficiently. Still controlled bit by bit.

Hardware spi is basically a built-in shift-out hardware register that spits the same data out way quicker.
See section 18 of the ATMega328 data sheet.


I did effectivly that for a led cube project, as I was needing to shift out a 32 bit long variable and it was easier on my head to do it that way then to figure out how to breakup the variable into byte size chunks and use SPI four times.  ;)


Graynomad

Most AVRs have hardware to to the SPI shifting, this hardware it conncted to 4 pins and that connection cannot be changed so you have to use those four pins.

However you can replicate the functionality by "bit banging" the bits on any pins you like, this is quite easy (as a master anyway) and just needs a loop that fiddles with the pins or on the Arduino the shiftOut() function.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

scswift

#9
Mar 03, 2011, 01:34 pm Last Edit: Mar 03, 2011, 02:13 pm by scswift Reason: 1
Quote

I did effectivly that for a led cube project, as I was needing to shift out a 32 bit long variable and it was easier on my head to do it that way then to figure out how to breakup the variable into byte size chunks and use SPI four times.  ;)



retrolefty


Quote

I did effectivly that for a led cube project, as I was needing to shift out a 32 bit long variable and it was easier on my head to do it that way then to figure out how to breakup the variable into byte size chunks and use SPI four times.  ;)





Quite serious, here is the ISR routine that performs the 32 bit serial shifting out to the shift registers:

Quote
void refresh_display() {
if (!updateinprogress) {
  unsigned long shiftdata = active_display[active_row]; // get row data for 25 leds
  unsigned long shiftmask = 1;

  for (byte i = 0; i < 32; i++) {                     // shift 32 bits (only 25 bits are wired up to cube) of data out for the 25 display columns

    if (shiftdata & shiftmask) {
      digitalWrite(dataPin, HIGH);
    }
    else {
      digitalWrite(dataPin, LOW);
    }

    digitalWrite(clockPin, HIGH);                     //  clock each bit into shift register
    digitalWrite(clockPin, LOW);

    shiftmask = shiftmask * 2;       //right shift bit mask one bit
  }

  if (active_row == 0) {
    digitalWrite(row4Pin, LOW);            // turn off row 4 driver if row 0 is now active, this is wrap around case
  }
  else {
    digitalWrite((active_row + 1), LOW);  // turn off driver from prior row update interrupt
  }

  digitalWrite(latchPin, HIGH);           // turn on shift register output pins, column drivers
  digitalWrite(latchPin, LOW);
  digitalWrite((active_row + 2), HIGH);   // turn on active row driver transistor

  // Set next active row number for next timer interrrupt 
  if (active_row >= 4) {   
    active_row = 0;  // wraparound case
    }
  else {
    active_row++;  // increament row number for next interrupt cycle
    }
}
}  // end of ISR code



Lefty

scswift

#11
Mar 03, 2011, 07:33 pm Last Edit: Mar 03, 2011, 07:36 pm by scswift Reason: 1
Quote

Code: [Select]

unsigned long shiftdata = active_display[active_row]; // get row data for 25 leds
 unsigned long shiftmask = 1;

 for (byte i = 0; i < 32; i++) {                     // shift 32 bits (only 25 bits are wired up to cube) of data out for the 25 display columns

   if (shiftdata & shiftmask) {
     digitalWrite(dataPin, HIGH);
   }
   else {
     digitalWrite(dataPin, LOW);
   }

   digitalWrite(clockPin, HIGH);                     //  clock each bit into shift register
   digitalWrite(clockPin, LOW);

   shiftmask = shiftmask * 2;       //right shift bit mask one bit
 }




Well this is how to do it with hardware SPI:

Code: [Select]

unsigned long shiftdata = active_display[active_row]; // get row data for 25 leds

digitalWrite(pinSPI_SS, LOW); // Select chip.

SPI.transfer(shiftdata & 255); // Write first byte.
SPI.transfer((shiftdata>>8) & 255); // Shift second byte into first byte and write out first byte.
SPI.transfer((shiftdata>>16) & 255);
SPI.transfer((shiftdata>>24) & 255);

digitalWrite(pinSPI_SS, HIGH); // Deselect chip.


And here's a simpler method to do it in software:

Code: [Select]

unsigned long shiftdata = active_display[active_row]; // get row data for 25 leds

digitalWrite(pinShiftLatch, LOW); // Select chip

shiftOut(pinShiftData, pinShiftClock, MSBFIRST, shiftdata);
shiftOut(pinShiftData, pinShiftClock, MSBFIRST, shiftdata>>8);
shiftOut(pinShiftData, pinShiftClock, MSBFIRST, shiftdata>>16);
shiftOut(pinShiftData, pinShiftClock, MSBFIRST, shiftdata>>24);

digitalWrite(pinShiftLatch, HIGH);  // Deselect chip


I don't know when the shiftout function became available so maybe you didn't have the option to use it though.  And you don't need the & 255 on the shifted bits because the shiftout fucntion only shifts out the lower 8 bits of the int passed to it.

I also assume you're selecting the chip outside the function, but I included the code to do that here so others reading it wouldn't be confused about the need to do that.

And just for kicks, here's another way you could have done it manually:

Code: [Select]

unsigned long shiftdata = active_display[active_row]; // get row data for 25 leds

 for (byte i = 0; i < 32; i++) {                     // shift 32 bits (only 25 bits are wired up to cube) of data out for the 25 display columns

   if ((shiftdata>>byte) & 1) {
     digitalWrite(dataPin, HIGH);
   }
   else {
     digitalWrite(dataPin, LOW);
   }

   digitalWrite(clockPin, HIGH);                     //  clock each bit into shift register
   digitalWrite(clockPin, LOW);

 }

johnwasser

Just a note that SPI is bi-directional and SPI.transaction(outByte) returns the byte that was sent by the Slave while the Master was sending.

In software SPI this would be something like:

Code: [Select]

uint8_t SPITransaction(uint8_t outByte)
    {
    uint8_t inByte = 0;
    for (uint8_t i = 0; i < i; i++)
        {
        digitalWrite(MOSIpin,  ((outData>>i) & 1);

        digitalWrite(SCKpin, HIGH);                     //  clock each bit into shift register
        digitalWrite(SCKpin, LOW);

        if (digitalRead(MISOpin))
            inByte += (1<<i);

        }
    return inByte;
   }


That would be one place where a shiftout() call would not work.
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

scswift

And just for the sake of completeness, here's the code to set up the SPI.  You'll need to modify it to suit whatever chip you're using:

Code: [Select]

  // Initialize Serial-Parralel Interface:
             
       pinMode(pinSPI_SS, OUTPUT); // Set the slave/chip select pin to output.
       
       SPI.begin();      
       SPI.setBitOrder(MSBFIRST); // Send most significant bit first when transferring a byte.
       SPI.setDataMode(SPI_MODE0); // Base value of clock is 0, data is captured on clock's rising edge.
       //SPI.setClockDivider(SPI_CLOCK_DIV4); // Set SPI data rate to 16mhz/4. IE: 4mhz.
       SPI.setClockDivider(SPI_CLOCK_DIV2); // Set SPI data rate to 16mhz/2. IE: 8mhz.

scswift


Just a note that SPI is bi-directional and SPI.transaction(outByte) returns the byte that was sent by the Slave while the Master was sending.

In software SPI this would be something like:

Code: [Select]

uint8_t SPITransaction(uint8_t outByte)
    {
    uint8_t inByte = 0;
    for (uint8_t i = 0; i < i; i++)
        {
        digitalWrite(MOSIpin,  ((outData>>i) & 1);

        digitalWrite(SCKpin, HIGH);                     //  clock each bit into shift register
        digitalWrite(SCKpin, LOW);

        if (digitalRead(MISOpin))
            inByte += (1<<i);

        }
    return inByte;
   }


That would be one place where a shiftout() call would not work.



That's in the case where you have a slave that's sending data back to you though, right?  So it wouldn't apply to a shift register.  (Or a dac, as it is being used in my code.)

Go Up