SPI Pins

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

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.

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.

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.

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.

Well, not really disconnect, more to indicate "this message is not for you!"

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.

What does hardware SPI vs software SPI mean?

Thanks
John

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.

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.

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. :wink:

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

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. :wink:

scswift:

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. :wink:

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

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

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:

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:

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:

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

  }

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:

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.

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:

   // 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.

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:

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.)

Wow that is quite a lot do digest. In my case the MAX6675 only sends data.

From the data sheet.

The Typical Application Circuit shows the MAX6675
interfaced with a microcontroller. In this example, the
MAX6675 processes the reading from the thermocouple
and transmits the data through a serial interface.
Force CS low and apply a clock signal at SCK to read
the results at SO. Forcing CS low immediately stops
any conversion process. Initiate a new conversion
process by forcing CS high.
Force CS low to output the first bit on the SO pin. A
complete serial interface read requires 16 clock cycles.
Read the 16 output bits on the falling edge of the clock.
The first bit, D15, is a dummy sign bit and is always
zero. Bits D14–D3 contain the converted temperature in
the order of MSB to LSB. Bit D2 is normally low and
goes high when the thermocouple input is open. D1 is
low to provide a device ID for the MAX6675 and bit D0
is three-state.

So for the MAX6675 my setup might be like this if I understand correctly. Darn the page for SPI.setDataMode() doesn't tell you what each mode is and gives you a link to a wiki page... so I'm guessing that SPI_MODE1 is the one I want.

Searching the data sheet I find "Serial Clock Frequency max 4.3 MHz". On the SPISetClockDivider page I find this "Sets the SPI clock divider relative to the system clock. The dividers available are 2, 4, 8, 16, 32, 64, or 128. The default setting is SPI_CLOCK_DIV4, which sets the SPI clock to one-quarter the frequency of the system clock. " I'm coming up blank on the system clock speed for the Uno...

// Initialize Serial-Parallel 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_MODE1); // Base value of clock is 1, data is captured on clock's falling edge.
        SPI.setClockDivider(SPI_CLOCK_DIV4); // Set SPI data rate to 16mhz/4. IE: 4mhz.

Thanks
John

Ok, I finally found it the system clock speed for the Uno is 16MHz so

SPI.setClockDivider(SPI_CLOCK_DIV4);

should be ok for the MAX6675

Thanks
John