Electronic – AVR SPI slower than expected

avrspitiming

I'm running an ATMega88A at 8MHz and have the SPI configured to run at Fosc/2 = 4MHz.

In theory, shifting out 5000 bytes over SPI should take 1/4000000 * 8 * 5000 = 10ms. But according to the internal timer, it's taking just over 19ms. This seems like a ton of overhead. Is this typical?

Sample code:

#include <avr/io.h>

#define set_output(portdir,pin) portdir |= (1<<pin)

void init() {
    // Set MOSI, SCK, SS as Output
    set_output(DDRB, DDB5);
    set_output(DDRB, DDB3);
    set_output(DDRB, DDB2);
    // Enable SPI, Set as Master, Set CPOL & CPHA to 1 (SPI mode 3)
    SPCR = (1 << SPE) | (1 << MSTR) | (1 << CPOL) | (1 << CPHA);
    SPSR = (1 << SPI2X); // Enable SPI clock doubler
    DDRD = 0xff; // Set PORTD as output
    TCCR1B |= (1 << CS12) | (1 << CS10); // Setup Timer with 1024 prescaling
}

int main(void) {
    unsigned int i;
    init();
    TCNT1 = 0; //zero the timer
    for (i = 0; i < 5000; i++) {
        SPDR = 0; // Load data into the SPI data reg
        while (!(SPSR & (1 << SPIF))); //Wait until transmission complete
    }
    PORTD = (unsigned char) TCNT1; // Display the timer on PORTD
    for (;;) {}
    return 0;
}

Best Answer

What is your code optimization set to?? I would look at the disassembly of your generated code, you have to remember that there are some instructions that must performed to carry your for loop and integrated over 5000 repetitions might up to the 9 milliseconds. I recommend you check this application note by atmel called Tips and Tricks to Optimize Your C Code for 8-bit AVR Microcontrollers, and read the loop index section and try their tips to see if you can reduce the instructions required to perform the for loop operation