Forum: Mikrocontroller und Digitale Elektronik Hardware- und Software-SPI mischen


von Michael (Gast)


Lesenswert?

Ich möchte an den SPI-Leitungen eines ATMega Sofware-SPI und 
Hardware-SPI mischen (Software-SPI mit grossen Delays für Befehle, die 
absolut sicher übertragen werden müssen, Hardware-SPI für Datenblöcke). 
Meine Funktion wird aber nur einmal ausgeführt, danach funktioniert das 
SPIF-Flag nicht mehr (??). Kann man das lösen? Code:
1
void
2
SPI_Write_N_hardware_spi(uint8_t *data, uint16_t n)
3
{
4
    uint8_t dummy;
5
6
    PORTB &= ~BITMASK4;  // B4 = CS (select chip; low-active)
7
8
    SPSR |= (1 << SPIE); // enable hardware SPI (other bits configured elsewhere)
9
10
    // the SPIF bit (transmission completed) is cleared by first reading SPSR with SPIF set, then accessing SPDR
11
12
    if (SPSR & (1<<SPIF))
13
14
        dummy = SPDR;
15
16
    while (n--)
17
    {
18
        SPDR = *data++;
19
20
        while (!(SPSR & (1<<SPIF)))
21
            ;
22
23
        dummy = SPDR; // must read SPDR to clear SPIF bit
24
    }
25
26
    PORTB |= BITMASK4; // B4 = CS  (deselect chip)
27
28
    SPCR &= ~(1 << SPIE); // disable hardware SPI
29
}

Wenn ich im Code das while (!(SPSR & (1<<SPIF))) ; auskommentiere, 
funktioniert es. Ansonsten hängt diese Funktion beim zweiten Aufruf an 
dieser Stelle. Ich habe dummy schon mal als volatile deklaiert, falls 
der Compiler diese Variable wegoptimieren sollte.

von Jim M. (turboj)


Lesenswert?

Michael schrieb:
1
  SPSR |= (1 << SPIE); // enable hardware SPI 
2
  ^^^^


Ist SPIE nicht im SPCR?

von Michael (Gast)


Lesenswert?

Du hast natürlich recht - aber das war's leider nicht, es funktioniert 
auch nach Korrektur nicht (der Tippfehler muss bei den frustrierten 
Versuchen, das umzuschreiben, dazugekommen sein, ich habe z.B. 
ausprobiert, in SPCR nicht nur SPIE zu toggeln, sondern alle Bits zu 
setzen).

von Bernd K. (prof7bit)


Lesenswert?

Michael schrieb:
> Software-SPI mit grossen Delays für Befehle, die
> absolut sicher übertragen werden müssen, Hardware-SPI für Datenblöcke

1. Datenverlust ist nicht vorgesehen bei SPI, wenn er dennoch auftritt 
dann ist was faul und wenn er so oft auftritt daß Du ihn live beobachten 
kannst ohne vorher vor Langeweile gestorben zu sein dann ist was 
oberfaul.

2. Warum Software-SPI? setz doch einfach den Takt soweit runter bis er 
klein genug ist, kleiner als der erlaubte Takt aller Slaves (-> 
Datenblatt).

von Bernd K. (prof7bit)


Lesenswert?

Michael schrieb:
> Ich habe dummy schon mal als volatile deklaiert

unnötig. SPDR wird schon volatile sein, also kann er die Leseoperation 
darauf niemals wegoptimieren. dummy darf und soll er wegoptimieren.

von Michael (Gast)


Lesenswert?

> Warum Software-SPI? setz doch einfach den Takt soweit runter bis er
klein genug ist

Weil langsames Hardware-SPI bei mir Fehler produziert. Das OLED läuft 
mit folgendem Software-SPI feherfrei:
1
void
2
SPI_Write(uint8_t out)
3
{
4
    uint8_t bit = 8;
5
6
    PORTB &= ~BITMASK4; // B4 = CS  (select chip)
7
8
    do
9
    {
10
        DELAY_100NS(); // 2 x NOP
11
12
        if (out & 128)
13
            PORTB |=  BITMASK5;  // B5 = MOSI
14
        else
15
            PORTB &= ~BITMASK5;
16
17
        out <<= 1;
18
19
        PORTB &= ~BITMASK7; // B7 = SCK
20
21
        DELAY_100NS();
22
23
        PORTB |=  BITMASK7; // B7 = SCK (high when idle)
24
25
    } while (--bit);
26
27
    DELAY_100NS();
28
29
    PORTB |= BITMASK4; // B4 = CS  (deselect chip)
30
}

Mit Hardware-SPI gibt es Fehler. Die MCU hat 18.432 MHz, getestet wurde 
SPI mit 1/4 FCPU + doublespeed = ca. 9MHZ bis 1/16 F_CPU = 1MHz. Der 
SPI-Modus (CPHA = 1, CPOL= 1) stimmt. Ich sehe ein paar Frames, dann 
akkumulieren sich Störungen. Offensichlich werden die Befehle gestört, 
denn das Display dreht durch (alle folgende Frames zeigen Fehler). Dass 
es keinen Fehler bei Setzen der Geschwindigkeit gibt, sieht man am 
langsamer laufenden Display-Test.

Könnte es daran liegen, dass man bei Hardware-SPI keinen Einfluss hat, 
wie früh nach dem Setzen von MOSI die Clock-Flanke kommt?

von holger (Gast)


Lesenswert?

>Du hast natürlich recht - aber das war's leider nicht,

Natürlich nicht. Du musst SPE nehmen und nicht SPIE.

von Bernd K. (prof7bit)


Lesenswert?

Michael schrieb:
> Könnte es daran liegen, dass man bei Hardware-SPI keinen Einfluss hat,
> wie früh nach dem Setzen von MOSI die Clock-Flanke kommt?

Das dauert genau eine halbe SCK-Periode. Bei der einen Taktflanke wird 
das Datenbit angelegt, bei der entgegengesetzten Taktflanke werden die 
Daten übernommen. Die Polarität und Phasenlage kann man einstellen, gibt 
glaub ich 4 Kombinationen.

Im Zweifel hilft ein Oszi um das was im Datenblatt im Timing-Diagramm 
abgebildet ist mit dem zu vergleichen was man wirklich sendet, ein Bild 
sagt oft mehr als tausend womöglich falsch interpretierte Worte.

: Bearbeitet durch User
von Michael (Gast)


Lesenswert?

> Natürlich nicht. Du musst SPE nehmen und nicht SPIE.

Das war's! Danke.

> Im Zweifel hilft ein Oszi

Ich vermute inzwischen eine elektrische Störung, vielleicht durch das 
Schaltnetzteil neben der SPI-Buchse, habe aber kein Oszi. Ich nutze MISO 
als Reset-Ausgang für das OLED. MISO ist bei Hardware-SPI aber ein 
Eingang. Ich kann darauf nur 1 ausgeben (= kein Reset), indem ich den 
Pull-Up für MISO einschalte bzw. einen in Hardware vorsehe. Der dadurch 
enstehende Pegel scheint das OLED massiv zu stören. Sobald man MISO als 
Ausgang auf 1 legt (was nur bei Software-SPI geht), läuft es 
einwandfrei. Dafür, dass es nicht an der Geschwindigkeit liegt, spricht, 
dass das folgende schnelle Software-SPI ohne Delays problemlos läuft:
1
// send <n> bytes to SPI interface (faster, used for pixel data)
2
3
void
4
SPI_Write_N(uint8_t *data, uint16_t n)
5
{
6
    PORTB &= ~BITMASK4;                              // B4 = CS  (select chip)
7
8
    while (n--)
9
    {
10
        uint8_t out = *data++;
11
12
        // unrolled loop, bit 7
13
14
        PORTB |= (BITMASK5 | BITMASK7);              // set SCK high (idle) and MOSI high
15
16
        if ((out & 128) == 0)
17
18
            PINB = BITMASK5;                         // toggle B5 (MOSI): setting a bit in PINx toggles corresponding bit in PORTx
19
20
        PINB = BITMASK7;                             // toggle clk (now low)
21
22
        // unrolled loop, bit 6
23
24
        PORTB |= (BITMASK5 | BITMASK7);              // set SCK high (idle) and MOSI high
25
26
        if ((out & 64) == 0)
27
28
            PINB = BITMASK5;                         // toggle B5 (MOSI): setting a bit in PINx toggles corresponding bit in PORTx
29
30
        PINB = BITMASK7;                             // toggle clk (now low)
31
32
        // unrolled loop, bit 5
33
34
        PORTB |= (BITMASK5 | BITMASK7);              // set SCK high (idle) and MOSI high
35
36
        if ((out & 32) == 0)
37
38
            PINB = BITMASK5;                         // toggle B5 (MOSI): setting a bit in PINx toggles corresponding bit in PORTx
39
40
        PINB = BITMASK7;                             // toggle clk (now low)
41
42
        // unrolled loop, bit 4
43
44
        PORTB |= (BITMASK5 | BITMASK7);              // set SCK high (idle) and MOSI high
45
46
        if ((out & 16) == 0)
47
48
            PINB = BITMASK5;                         // toggle B5 (MOSI): setting a bit in PINx toggles corresponding bit in PORTx
49
50
        PINB = BITMASK7;                             // toggle clk (now low)
51
52
        // unrolled loop, bit 3
53
54
        PORTB |= (BITMASK5 | BITMASK7);              // set SCK high (idle) and MOSI high
55
56
        if ((out & 8) == 0)
57
58
            PINB = BITMASK5;                         // toggle B5 (MOSI): setting a bit in PINx toggles corresponding bit in PORTx
59
60
        PINB = BITMASK7;                             // toggle clk (now low)
61
62
        // unrolled loop, bit 2
63
64
        PORTB |= (BITMASK5 | BITMASK7);              // set SCK high (idle) and MOSI high
65
66
        if ((out & 4) == 0)
67
68
            PINB = BITMASK5;                         // toggle B5 (MOSI): setting a bit in PINx toggles corresponding bit in PORTx
69
70
        PINB = BITMASK7;                             // toggle clk (now low)
71
72
        // unrolled loop, bit 1
73
74
        PORTB |= (BITMASK5 | BITMASK7);              // set SCK high (idle) and MOSI high
75
76
        if ((out & 2) == 0)
77
78
            PINB = BITMASK5;                         // toggle B5 (MOSI): setting a bit in PINx toggles corresponding bit in PORTx
79
80
        PINB = BITMASK7;                             // toggle clk (now low
81
82
        // unrolled loop, bit 0
83
84
        PORTB |= (BITMASK5 | BITMASK7);              // set SCK high (idle) and MOSI high
85
86
        if ((out & 1) == 0)
87
88
            PINB = BITMASK5;                         // toggle B5 (MOSI): setting a bit in PINx toggles corresponding bit in PORTx
89
90
        PINB = BITMASK7;                             // toggle clk (now low)
91
    }
92
93
    PORTB |= BITMASK7;                               // toggle clk (idle high)
94
95
    PORTB |= BITMASK4;                               // B4 = CS (deselect chip)
96
}

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.