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:
// 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.
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).
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).
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.
> 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?
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.
> 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