Go Down

Topic: How can I be sure I am getting true hardware SPI speed in this? (Read 873 times) previous topic - next topic

db2db

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?

Code: [Select]
#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
}

madworm

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.
• Upload doesn't work? Do a loop-back test.
• There's absolutely NO excuse for not having an ISP!
• Your AVR needs a brain surgery? Use the online FUSE calculator.
My projects: RGB LED matrix, RGB LED ring, various ATtiny gadgets...
• Microsoft is not the answer. It is the question, and the answer is NO!

Graynomad

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
Rob Gray aka the GRAYnomad www.robgray.com

db2db


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!


Graynomad

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

Quote
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
Rob Gray aka the GRAYnomad www.robgray.com

db2db


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

Graynomad

#6
Feb 27, 2012, 06:11 am Last Edit: Feb 27, 2012, 09:27 am by Graynomad Reason: 1
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

Rob Gray aka the GRAYnomad www.robgray.com

db2db

#7
Feb 27, 2012, 06:52 am Last Edit: Feb 27, 2012, 07:14 am by db2db Reason: 1
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!

Graynomad

Quote
Should it be this? :

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

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

Quote
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
Rob Gray aka the GRAYnomad www.robgray.com

db2db


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

Go Up