How can I be sure I am getting true hardware SPI speed in this?

Is there a way to be sure if I am using the hardware SPI and getting that fast SPI speed? I have it all on the pins assigned as needed, but I am still having a speed issue in my program. When I have the display on, there is some slowdown, even if I only send a tiny bit of data to the display. (Using an Uno)

Can I be sure I'm enabling the fast hardware SPI here in this sample code?

#define LOAD 10      
#define DIN 11      
#define DOUT 12      
#define SPICLOCK 13  

int counterNumber1 = 0;
int counterNumber10 = 0;

void setup()
{
  int clr;
  pinMode(DIN, OUTPUT);     
  pinMode(SPICLOCK,OUTPUT);
  pinMode(LOAD,OUTPUT);
  digitalWrite(LOAD,HIGH); //disable device

  // SPCR = 01010000
  //interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
  //sample on leading edge of clk,system clock/4 (fastest)
  SPCR = (1<<SPE)|(1<<MSTR);
  clr=SPSR;
  clr=SPDR;
  delay(10);

  //clear MAX7219 and format to receive data
  write_7seg(0x0C,1);
  write_7seg(0x09,0xFF);
  write_7seg(0x0A,0x0F);
  write_7seg(0x0B,0x04);
}

void loop()
{
  write_7seg(1, counterNumber1); 
  write_7seg(2, counterNumber10);  
  delay (10)
}

//spi transfer function (from ATmega168 datasheet)
char spi_transfer(volatile char data)
{
  SPDR = data; // Start the transmission
  while (!(SPSR & (1<<SPIF))) // Wait the end of the transmission
  {
  };
  return SPDR; // return the received int
}

//MAX7219 data transfer function
int write_7seg(int digAddress, int displayValue) 
{
  digitalWrite(LOAD,LOW); //MAX7219 chip select is active low
  //2 int data transfer to MAX7219
  spi_transfer(digAddress);
  spi_transfer(displayValue);
  digitalWrite(LOAD,HIGH); //release chip, signal end transfer
}

The fastest speed of the hardware SPI interface is actually F_OSC/2 - which is 8 MHz for a normal arduino.

If you need top-speed, use direct port manipulation for twiddling the chip-select line. You also seem to feed 'int'-s to a function that would run as well with an uint8_t. SPI sends bytes, no need for ints.

From what I can see you are currently running at fosc/4, you can get to fosc/2 by setting the SPI2X bit in the SPSR.

To see what rate you are running at (I assume you don't have any proper equipment) just write a loop to send 100000 bytes and time it.


Rob

Thank you both for you replies. I took quite a lot of that code from elsewhere, so I don't fully understand your comments.
I don't need absolute max speed, but I'd like to avoid bottlenecks anywhere I can.

If you can be more specific about what to change, I'd really appreciate that!

I haven't done it but looking at the data sheet I would say that

SPSR = 1;

or

SPSR = (1 << SPI2X); // same thing because SPI2X is bit 0 and the other bits are RO (read only)

Should give you fosc/2, however...

When I have the display on, there is some slowdown, even if I only send a tiny bit of data to the display.

What is slowing down? There's nothing else happening in the code you posted. Is there more to this than meets the eye?


Rob

(This is just a test routine - the slowdowns are in the real program.)

I haven't used bitshift before so am trying to make sense of these lines:

SPSR = (1 << SPI2X); // same thing because SPI2X is bit 0 and the other bits are RO (read only)
and
SPCR = (1<<SPE)|(1<<MSTR);

For example (1 << 2) means the value 1 shifted left 2 times,

00000001 << 2 == 00000100 == 4

The "values" SPI2X, SPE, MSTR etc are just #defines for numbers in a file somewhere so

(1<<SPE)

is just 1 shifted left by SPE bits.

That's fine to get a single bit set in a register but it clears all the other bits so to set multiple bits you have to OR several of these expressions.

Let's assume SPE = 2 and MSTR = 4 (they probably aren't but I couldn't be bothered looking them up)

(1<<SPE)|(1<<MSTR) == (1<<2)|(1<<4) == 00000010 | 00010000 == 000100010 == 0x12 ERROR fixed

so the value 0x12 is written into the register.

In my previous example

SPSR = (1 << SPI2X); // same thing because SPI2X is bit 0 and the other bits are RO (read only)

SPI2X is actually defined as 0 so (1 << SPI2X) == (1 << 0) == 1.

There's no real need to use these expressions but it saves using magic numbers, after a while you will look at (1 << SPI2X) and know exactly what it means, if you write SPSR = 1 you will forever have to look it up in the data sheet.


Rob

VERY useful answer - thanks.

Is this right though?
"Let's assume SPE = 2 and MSTR = 4 (they probably aren't but I couldn't be bothered looking them up)
(1<<SPE)|(1<<MSTR) == (1<<1)|(1<<4) == 00000010 | 00010000 == 000100010 == 0x12"

Should it be this? :
(1<<SPE)|(1<<MSTR) == (1<<2)|(1<<4) == 00000100 | 00001000 == 00001100 == 0x12"

In the end, do I want to set SPCR as I have, then set the SPSR right after?
// SPCR = 01010000
//interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
//sample on leading edge of clk,system clock/4 (fastest)
SPCR = (1<<SPE)|(1<<MSTR);
SPSR = (1 << SPI2X); //toggle this 0 to a 1 in order to switch on "SPI2X" register value
clr=SPSR;
clr=SPDR;

ALSO, as an example, if I wanted to slow things down I would set that rightmost SPRO bit on, by basically making that 0 a 1 like so:
SPCR = (1<<SPE)|(1<<MSTR) |(1<< SPRO);

Thanks!

Should it be this? :

Yep, well done, you spotted the deliberate error :slight_smile:

In the end, do I want to set SPCR as I have, then set the SPSR right after?

Yes although the order doesn't matter AFAIK.

ALSO, as an example, if I wanted to slow things down I would set that rightmost SPRO bit on, by basically making that 0 a 1 like so:
SPCR = (1<<SPE)|(1<<MSTR) |(1<< SPRO);

That seems right, should give you fosc/8.


Rob

Awesome, your assistance was really great, and very much appreciated!