AVR SPI2X hat keine Wirkung

Ich habe einen ATMega88A, der so konfiguriert ist, dass er mit einem internen 8-MHz-Takt läuft:

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

Dies wird in der 'realen Welt' durch Blinken einer LED mit _delay_ms(1000) bestätigt. Es ist ~ 1 Sek., nicht 8 Sek. oder 1/8 Sek.

Ich habe versucht, den SPI so einzustellen, dass er mit Fosc/2 läuft, indem ich nur SPI2X auf 1 gesetzt habe. (SPR1, SPR0 werden initialisiert und sollten auf 0 bleiben.) Dies sollte zu einer SPI-Geschwindigkeit von 4 MHz führen.

Ich habe den Timer auf eine 256-fache Vorskalierung eingestellt.

Ich versuche, 5000 Bytes über SPI zu senden. (Es ist kein Gerät angeschlossen, es werden nur Bits aus dem Port verschoben.) Der Timer wird vor der Übertragung auf Null gesetzt und der Wert wird nach der Übertragung als LEDs angezeigt.

Das Senden eines Bits über einen 4-MHz-SPI sollte 1/4000000 = 250 ns dauern, ein Byte 8 * 250 ns = 2 us, 5000 Byte 2 us * 5000 = 10 ms .

Aus meinem Timer geht jedoch hervor: Die Systemuhr beträgt 8 MHz, also beträgt die Periode 1/8000000 = 125 ns, der Timer-Prescaler von 256 bedeutet einen Timer-Tick von 125 ns * 256 = 32 us .

Wie ist die Wirklichkeit schneller als die Theorie?

Der Kicker? Das Kommentieren SPSR = (1 << SPI2X);hat keinen Einfluss auf den Wert, der in den Timer-LEDs angezeigt wird.

Beispielcode:

#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;
}

Einige Probleme, die ich getestet habe:

  1. TCNT1 (16 Bit) könnte PORTD (8 Bit) überlaufen? Nein, Schleifen von weniger Iterationen reduziert den Timerwert proportional. (5000 Schleifen = 175 Ticks; 1000 Schleifen = 35 Ticks)
  2. SPI2X wird nicht erfolgreich eingestellt? Nein, nachdem die Schleife if (SPSR & (1<<SPI2X))true zurückgibt.
  3. SPI2X muss eingestellt werden, bevor SPI aktiviert wird? Nein, es zuerst zu setzen, hat keine Wirkung.
  4. Keine Änderung zwischen Optimierungen -O3und -Os, aber großer Anstieg des Timerwerts für -O0.
  5. Das Schreiben eines anderen Werts in SPDR hat keine Auswirkung.

Werde ich verrückt?

BEARBEITEN: Der Teil des fehlerhaften Codes, wie unten beantwortet, wurde von mir ohne Überprüfung von hier aus nachlässig kopiert.

Hast du aus Interesse den SPI-Code irgendwo ausgeschnitten/eingefügt? Gerade habe ich kürzlich eine Frage beantwortet, bei der das gleiche Problem wie in der Antwort angegeben war, und ich habe mich gefragt, ob es irgendwo herkommt, wo Sie auf den Fehler hinweisen könnten, um in Zukunft ein paar andere zu retten.
Ja. Es ist hier zu finden.

Antworten (1)

Bist du sicher, dass die Zeile while (!(SPSR) & (1 << SPIF));richtig ist? Ich vermute vermeintliche Funktionalität:while (!(SPSR & (1 << SPIF)));

Welp, das ist peinlich ... Danke. Dieser Teil des Codes wurde von hier kopiert/eingefügt. Ich bin jetzt mal länger als Theorie; Tatsächlich viel länger. Das Senden von 5000 Bytes sollte 10 ms dauern, aber ich messe etwas mehr als 19 ms. SPI2X hat jetzt eine Wirkung, aber bei weitem keine 2-fache Zeitersparnis. Gibt es wirklich so viel Overhead? Vielleicht sollte dies eine separate Frage sein ...