Electronic – AVR SPI2X has no effect

atmegaavravr-gccspitiming

I've got an ATMega88A configured to run on internal 8MHz clock:

lfuse = 0xE2
hfuse = 0xDF
efuse = 0x01
F_CPU defined as 8000000

This in confirmed in the 'real world' by blinking an LED with _delay_ms(1000). It's ~1sec, not 8 sec or 1/8 sec.

I've attempted to set the SPI to run at Fosc/2 by setting only SPI2X to 1. (SPR1, SPR0 initialize and should stay 0.) This should result in a SPI speed of 4MHz.

I've set the timer to a 256x prescale.

I'm attempting to send 5000 bytes over SPI. (There's no device connected, just shifting bits out the port.) The timer is zeroed before the transmission and then the value is displayed as LEDs after the transmission.

To send one bit over a 4MHz SPI should take 1/4000000 = 250ns, one byte 8*250ns = 2us, 5000 byte 2us*5000 = 10ms.

However, from my timer: system clock is 8MHz so period is 1/8000000 = 125ns, timer pre-scaler of 256 mean timer tick of 125ns*256 = 32us LEDs show binary of 176 so loop time is 32us*176 = 5.632ms.

How is actual faster than theory?

The kicker? Commenting SPSR = (1 << SPI2X); has no effect on the value displayed in the timer LEDs.

Sample code:

#include <avr/io.h>
#include <util/delay.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); // Setup Timer with 256 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;
}

Some problems I've tested:

  1. TCNT1 (16 bits) could be overflowing PORTD (8 bits)? Nope, looping
    less iterations reduces the timer value proportionally. (5000 loops = 175 ticks; 1000
    loops = 35 ticks)
  2. SPI2X isn't being successfully set? Nope, after the loop if (SPSR & (1<<SPI2X))
    returns true.
  3. SPI2X must be set before enabling SPI? Nope, setting it first has no
    effect.
  4. No change between optimizations -O3 and -Os, but large increase in
    timer value for -O0.
  5. Writing some other value to SPDR has no effect.

Am I going insane?

EDIT: The portion of code in error as answered below was carelessly copied my me without verification from here.

Best Answer

Are you sure the line while (!(SPSR) & (1 << SPIF)); is correct? I suspect supposed functionality: while (!(SPSR & (1 << SPIF)));