Forum: Compiler & IDEs Wie External SRAM mit GCC Optimierung ansteuern?


von Hut S. (gock)


Lesenswert?

Hi!
An einen Mega 32 habe ich 512kB external SRAM über 3 Latches 
angeschlossen (1 Portbetrieb plus Steuerleitungen). Zum Zugriff darauf 
schrieb ich die Funktionen RemMem() und WriteMem(). Diese funtkionieren 
auch OHNE eingeschaltete Optierung, aber nicht mit.
Es ist mir klar, dass die Optimierung auch die Reihenfolge der Befehle, 
also den Programmfluss verändern kann. Das würde natürlich dazu führen, 
dass die strickt nacheinander auszuführenden Schritte in einer falschen 
oder zusammengefassten Folge ausgeführt werden, was den SRAM 
durcheinander bringt.
Frage:
1. Wie kann die Reihenfolge so genau festlegen?
2. Muss man sowas inlinen?
3. Kann man die Optimierung für bestimmte Programmteile abschalten?
4. Gibt es noch andere Möglichkeiten?
Ich bin übrigens kein Informatiker, schade eigentlich, aber ich muss es 
trotzdem hinbekommen.
Alle Variablen sind volatile, also daran sollte es nicht liegen.
Danke schon mal,
Gruß Gock

von Gast (Gast)


Lesenswert?

Die Reihenfolge kann der Optimierer nicht einfach so ändern.

Aber zeig doch einfach mal den Code oder Compilierfähige Ausschnitte die 
dein Problem noch beinhalten.

von Oliver (Gast)


Lesenswert?

>1. Wie kann die Reihenfolge so genau festlegen?
In Assembler. In C geht das wohl per Sprach-Definition gar nicht, aber 
der gcc hält sich zumindest bei Zugriffen auf volatile-Variablen an die 
vorgegebene Reihenfolge.

>2. Muss man sowas inlinen?
Nein.

3. Kann man die Optimierung für bestimmte Programmteile abschalten?
Nein.

4. Gibt es noch andere Möglichkeiten?
Ja ;-)

Aber schau doch erst einmal im generierten Assemblercode nach, was der 
Compiler aus deinem Code macht. Typische Ursachen für "funktioniert nur 
ohne Optimierung" sind fehlendes volatile, selbstgestrickte 
Delayschleifen, und natürlich Probleme durch verändertes Timing.

>Ich bin übrigens kein Informatiker,
Einer zu sein, würde dabei kaum helfen...

Oliver

von Hut S. (gock)


Lesenswert?

Ok, Assemblercode...
Ich glaube ich habe eine der Stellen im Assemblercode finden können:
Aus diesem nichtoptimierten Code
1
  PORTC |= (MemLowAdr << 4);      // AdressLowByte an Memory
2
    15bc:  a5 e3         ldi  r26, 0x35  ; 53
3
    15be:  b0 e0         ldi  r27, 0x00  ; 0
4
    15c0:  e5 e3         ldi  r30, 0x35  ; 53
5
    15c2:  f0 e0         ldi  r31, 0x00  ; 0
6
    15c4:  80 81         ld  r24, Z
7
    15c6:  80 61         ori  r24, 0x10  ; 16
8
    15c8:  8c 93         st  X, r24
9
  PORTC &= 0b00001111;
10
    15ca:  a5 e3         ldi  r26, 0x35  ; 53
11
    15cc:  b0 e0         ldi  r27, 0x00  ; 0
12
    15ce:  e5 e3         ldi  r30, 0x35  ; 53
13
    15d0:  f0 e0         ldi  r31, 0x00  ; 0
14
    15d2:  80 81         ld  r24, Z
15
    15d4:  8f 70         andi  r24, 0x0F  ; 15
16
    15d6:  8c 93         st  X, r24
17
  asm volatile ("nop");
wird das hier:
1
PORTC |= (MemLowAdr << 4);      // AdressLowByte an Memory
2
     4e6:  ac 9a         sbi  0x15, 4  ; 21
3
  PORTC &= 0b00001111;
4
     4e8:  85 b3         in  r24, 0x15  ; 21
5
     4ea:  8f 70         andi  r24, 0x0F  ; 15
6
     4ec:  85 bb         out  0x15, r24  ; 21
7
  asm volatile ("nop");
Wie man sieht, gibt er nach Optimierung die MemLowAdress an PORTC nicht 
aus (kein out 0x15...), sondern wartet auf die nächste Veränderung des 
Werts um diesen dann auszugeben. Dadurch fehlt aber die Adresse am 
Speicher.
Das passiert übrigens noch öfters...
Ich habe auch schon ein "asm volatile ("nop");" eingefügt, aber das hat 
es auch nicht gebracht.
Wie bekomm ich ihn dazu, die Daten jedesmal am Port auszugeben?

von (prx) A. K. (prx)


Lesenswert?

Der Code ist korrekt und mit und ohne Optimierung exakt äquivalent, aber 
deine Reihenfolge ist falsch. Erst setzt du die Adresse (|), nur um sie 
im nächsten Befehl wieder zu löschen (&). Andersrum ist besser.

von Karl H. (kbuchegg)


Lesenswert?

Hut Schugh schrieb:

> Wie man sieht, gibt er nach Optimierung die MemLowAdress an PORTC nicht
> aus (kein out 0x15...),

doch tut er.

Diese Sequenz
1
     15bc:  a5 e3         ldi  r26, 0x35  ; 53
2
     15be:  b0 e0         ldi  r27, 0x00  ; 0
3
     15c0:  e5 e3         ldi  r30, 0x35  ; 53
4
     15c2:  f0 e0         ldi  r31, 0x00  ; 0
5
     15c4:  80 81         ld  r24, Z
6
     15c6:  80 61         ori  r24, 0x10  ; 16
7
     15c8:  8c 93         st  X, r24

holt über den Z-Pointer den momentanen Inhalt vom PORTC, setzt Bit 
Nummer 4 (0x10, 16) und gibt den neuen Wert am PORTC über den X Pointer 
wieder aus.

Das hier
1
      4e6:  ac 9a         sbi  0x15, 4  ; 21

macht genau dasselbe: Setze Bit Nr 4 am PORTC
Nur 2 Gänge schneller :-)

> Werts um diesen dann auszugeben. Dadurch fehlt aber die Adresse am
> Speicher.

Mir ist sowieso nicht ganz klar, was du da vor hast.
Du setzt Bit Nr 4 um es gleich darauf mit dem
1
  PORTC &= 0b00001111;

wieder zu löschen.

Ich denke auch, dass du in Wirklichkeit ein Timing/Logik Problem hast 
und dein ursprünglicher Code mehr zufällig funktioniert hat.

von (prx) A. K. (prx)


Lesenswert?

Probier's mal so herum:
1
PORTC &= 0b00001111;
2
PORTC |= (MemLowAdr << 4);
3
asm volatile ("nop");
oder besser gleich
1
PORTC = (PORTC & 0b00001111) | (MemLowAdr << 4);
2
asm volatile ("nop");

von Hut S. (gock)


Lesenswert?

Verdammt, Du hast Recht (sbi 0x15 usw...). Vielleicht ist er dann doch 
einfach zu schnel für den Speicher...
1
#define MemLowAdr  1
2
#define MemHighAdr  2
3
#define MemKonfigAdr  3
4
#define MemoryAdress  4
5
[...]
6
void WriteMem(volatile uint16_t DataAdress, volatile uint8_t MemQuart, volatile uint16_t MemData){      // Schribt MemData an DataAdress, wobei MemQuart (Quartal) ein Quartal definiert
7
  cli();
8
  MemQuart = MemQuart << 1;        // MemQuart x 2, weil Low und HighByte getrennt gespeichert werden müssen (64k Bytes *2 *4)
9
// LowAdress anlegen  
10
  PORTD = DataAdress;
11
  PORTC |= (MemLowAdr << 4);      // AdressLowByte an Memory
12
  PORTC &= 0b00001111;
13
// HighAdress anlegen
14
  PORTD = (DataAdress >> 8);      // AdressHighbyte an Memory
15
  PORTC |= (MemHighAdr << 4);
16
  PORTC &= 0b00001111;
17
// KonfigByte anlegen
18
  PORTD = ((MemQuart | (1 << CUSPtoDRDY)));    //Wahl des Quartals, CUSP aus, WRITE = Low
19
  PORTD &= ~(1 << PD3);
20
  PORTC |= (MemKonfigAdr << 4);
21
  PORTC &= 0b00001111;      
22
// Chipselect Memory
23
  PORTC |= (MemoryAdress << 4);    
24
// DatanLowByte ausgeben  
25
  PORTD = MemData;          // DataLow auf Bus
26
  asm volatile ("nop");        // Speicher benötigt Zeit WICHTIG !!!!!!!!!!!!!!!!!!
27
// CS-Adresse  für Memory wegnehmen
28
  PORTC &= 0b00001111;
29
// KonfigByte ändern um HighByte in 2.Teil des Quartals zu schreiben
30
  PORTD = (((MemQuart + 1) | (1 << CUSPtoDRDY)));    //MemKonfig_Last &= ~((1 << PD0) | (1 << PD1) | (1 << PD2));  // Adresse 0, 1 und 2 (16, 17 und 18) = 0
31
  PORTD &= ~(1 << PD3);
32
  PORTC |= (MemKonfigAdr << 4);
33
  PORTC &= 0b00001111;  
34
// DatenHighbyte ausgeben
35
  PORTD = MemData >> 8;
36
  asm volatile ("nop");        // Speicher benötigt Zeit ???  WICHTIG !!!!!!!!!!!!!!!!!!
37
// Chipselect Memory
38
  PORTC |= (MemoryAdress << 4);    
39
  asm volatile ("nop");        // Speicher benötigt Zeit ???  WICHTIG !!!!!!!!!!!!!!!!!!
40
// CS-Adresse  für Memory wegnehmen
41
  PORTC &= 0b00001111;
42
// WE wieder High (active low) -> Read
43
  PORTD |= (1 << CUSPtoDRDY) | (1 << PD3);        
44
  PORTC |= (MemKonfigAdr << 4);
45
  PORTC &= 0b00001111;
46
  MemQuart = MemQuart >> 1;
47
  sei();
48
}
49
50
uint16_t ReadMem(volatile uint16_t DataAdress, volatile uint8_t MemQuart){            // bisher nur 8Bit Adresse
51
  cli();
52
  uint16_t MemData;
53
  uint8_t temp2;
54
  MemQuart = MemQuart << 1;
55
// Output Enable von Memory = low (active LOW)  
56
  PORTB &= ~(1 << PB4);    
57
// KonfigByte ausgeben: Quartal wählen, CUSP aus, READ
58
  PORTD = (MemQuart | (1 << CUSPtoDRDY) | (1 << PD3));    // WE auf High (active low) -> Read und CUSP aus
59
  PORTC |= (MemKonfigAdr << 4);
60
  PORTC &= 0b00001111;
61
// AdressLowByte an Memory  
62
  PORTD = DataAdress;
63
  PORTC |= (MemLowAdr << 4);    
64
  PORTC &= 0b00001111;
65
// AdressHighbyte an Memory  
66
  PORTD = (DataAdress >> 8);      
67
  PORTC |= (MemHighAdr << 4);
68
  PORTC &= 0b00001111;
69
// Port konfigurieren
70
  DDRD = 0;              // PortD ist EIngang
71
  PORTD = 0;              // keine PullUps
72
// Chipselect Memory  
73
  PORTC |= (MemoryAdress << 4);    
74
  asm volatile ("nop");        // Speicher benötigt Zeit  WICHTIG !!!!!!!!!!!!!!!!!!
75
  asm volatile ("nop");        // Speicher benötigt Zeit  WICHTIG !!!!!!!!!!!!!!!!!!
76
// DatenLowByte einlesen
77
  MemData = PIND;
78
  PORTC &= 0b00001111;        // Chipselect wegnehmen
79
// Port konfigurieren  
80
  DDRD = 255;              //Port als Ausgang
81
// WE auf High (active low) -> Read und CUSP aus  
82
  PORTD = ((MemQuart + 1) | (1 << CUSPtoDRDY) | (1 << PD3));  
83
  PORTC |= (MemKonfigAdr << 4);
84
  PORTC &= 0b00001111;        // Chipselect wegnehmen
85
// Port konfigurieren    
86
  DDRD = 0;              // PortD ist EIngang
87
  PORTD = 0;              // keine PullUps
88
// Chipselect Memory  
89
  PORTC |= (MemoryAdress << 4);    
90
//DatenHighByte einlesen  
91
  asm volatile ("nop");        // Speicher benötigt Zeit  WICHTIG !!!!!!!!!!!!!!!!!!
92
  asm volatile ("nop");        // Speicher benötigt Zeit  WICHTIG !!!!!!!!!!!!!!!!!!
93
  temp2 = PIND;  
94
  PORTC &= 0b00001111;        // Chipselect wegnehmen
95
// Output Enable von Memory = high  
96
  PORTB |= (1 << PB4);        
97
  DDRD = 255;              // PORTD ist wieder AUsgang
98
  MemData = MemData | (temp2 << 8);
99
  MemQuart = MemQuart >> 1;
100
  return MemData;
101
  sei();
102
}
Der Speicher ist eine 55n Version und keins seiner timings liegt unter 
55ns. Der µCTakt ist 16MHz, also 62,5ns. Trotzdem war es nötig 
(möglicherseise wegen Siganllaufweg, einige "nop" einzufügen, damit 
alles reibungsfrei läuft.
Was ist da sonst noch falsch???
Die PORT befehle kann ich nicht vertauschen:
PORTC |= (MemLowAdr << 4); schaltet über einen 4 zu 16 DeMux den 
Chipselect vom LowAdressLatch frei, nachdem vorher die Adresse angelegt 
wurde.
PORTC &= 0b00001111; nimmt den CS wieder weg und schaltet auf 0 (frei)
Daher ist die Reihenfolge so wichtig...

von (prx) A. K. (prx)


Lesenswert?

Karl heinz Buchegger schrieb:

> Ich denke auch, dass du in Wirklichkeit ein Timing/Logik Problem hast
> und dein ursprünglicher Code mehr zufällig funktioniert hat.

Da die Inputs bei den AVRs zur Vermeidung metastabiler Zustände einen 
Takt verzögert werden, ist durchaus denkbar, dass ein IN kurz nach dem 
löschenden OUT noch den richtigen SRAM Wert für die richtige Adresse 
liefert. Sofern kein Interrupt und kein störendes(!) NOP dazwischen 
kommt.

von Hut S. (gock)


Lesenswert?

Da fällt mir noch was anderes ein: Ich gebe einen 16Bit Wert auf ein 
8Bit Port. Gefällt ihm das vielleicht nicht???

von (prx) A. K. (prx)


Lesenswert?

Hut Schugh schrieb:

> PORTC &= 0b00001111; nimmt den CS wieder weg und schaltet auf 0 (frei)
> Daher ist die Reihenfolge so wichtig...

Bei inaktivem CS lesen ist aber auch nicht der Brüller. Erst Adresse 
raus, dann CS aktivieren, dann lesen, dann CS deaktivieren.

Und bei 16 Bit auf 8 Bit fehlt halt die Hälfe. Die obere,

von Oliver (Gast)


Lesenswert?

Da gibt er mit und ohne Optimierung nur die unteren 8 Bits aus, und der 
Compiler dazu eine Warnung.

Oliver

von (prx) A. K. (prx)


Lesenswert?

Es wäre besser wenn du ein Schaltbild lieferst. Sonst etwas 
undurchsichtig.

von Hut S. (gock)


Lesenswert?

A. K. schrieb:

> Bei inaktivem CS lesen ist aber auch nicht der Brüller. Erst Adresse
> raus, dann CS aktivieren, dann lesen, dann CS deaktivieren.
>
> Und bei 16 Bit auf 8 Bit fehlt halt die Hälfe. Die obere,

Ja, so soll es doch auch sein: Adresse LOW anlegen -> CS schalten -> 
Latch hält Adresse -> nächste (HIGH) Adresse anlegen -> CS High Adresse 
usw. ... Lesen lassen.
Das Latch hat timings von max 50ns.
Aber ich glaube mittlerweile tatsächlich, dass es vorher zufällig 
funktioniert hat und werde die gesamte Kette nochmal überprüfen.

von Karl H. (kbuchegg)


Lesenswert?

> PORTC |= (MemLowAdr << 4); schaltet über einen 4 zu 16 DeMux
> den Chipselect vom LowAdressLatch frei, nachdem vorher die
> Adresse angelegt wurde.
> PORTC &= 0b00001111; nimmt den CS wieder weg und schaltet auf 0 (frei)
> Daher ist die Reihenfolge so wichtig...

machs mal so
1
// LowAdress anlegen  
2
  PORTD = DataAdress;
3
  PORTC |= (MemLowAdr << 4);      // AdressLowByte an Memory
4
  asm volatile ("nop");
5
  PORTC &= 0b00001111;

und verschaff dem Latch ein bischen Zeit

von (prx) A. K. (prx)


Lesenswert?

Hut Schugh schrieb:

> Ja, so soll es doch auch sein: Adresse LOW anlegen -> CS schalten ->
> Latch hält Adresse -> nächste (HIGH) Adresse anlegen -> CS High Adresse
> usw. ... Lesen lassen.

Mein Text basierte noch auf den anfänglichen 2 Zeilen statt dem gesamten 
Code, und da meine Glaskugel ein paar Sprünge hat, kann es bei Raten 
auch mal zu Fehlern kommen.

von Hut S. (gock)


Angehängte Dateien:

Lesenswert?

Hier die Verschaltung, ich prüfe nochmal die Logik...

von (prx) A. K. (prx)


Lesenswert?

Eins ist jedenfalls Mist: Beim Lesezyklus CS und OE aktivieren und dann 
den Port D gegen den Datenbus vom SRAM Ausgang gegen Ausgang kämpfen zu 
lassen. Du darft CS erst aktivieren, wenn der ganze Adresszirkus durch 
ist und DDRD auf 0 steht.

von (prx) A. K. (prx)


Lesenswert?

Und beim Schreibzyklus fehlt CS völlig.

von Hut S. (gock)


Lesenswert?

Also die Stelle im ReadMem, wo das Output enable nicht ausgeschaltet 
wird habe ich gefunden und behoben, danke. Ich habe jetzt erst den Port 
auf Eingang geschaltet, dann CS aktiviert und dann OE eingeschaltet. Das 
hat eine Änderung in der Zahl gebracht die ich schreibe und anschließend 
wieder lese. Aber sie ist noch nicht identisch.
Das fehlende CS im WriteMem kann ich nicht finden, besser gesagt, da ist 
doch eins, oder?

von (prx) A. K. (prx)


Lesenswert?

Stimmt, habe mich in den Spinnweben der Leitungen verheddert und mit OE 
verwechselt.

von Hut S. (gock)


Lesenswert?

Es funktioniert ! ! !
In allen 3 Optimierungsstufen!

Fehler behoben:
- Output Enable am Speicher aktiviert und Port auf Ausgang
- Reihenfolge von OE und CS Aktivierung vertauscht (jetzt erst OE und 
dann CS ein, beim Ausschalten umgekehrt)
- "Ruhezeiten" zT vergrößert und an anderer Stelle eingelegt

Ergebnis:
Ohne Optimierung benötigte ich eine "nop"s, weil der DeMux nicht der 
schnellste ist. Durch die Optimierung hat sich der Codeaufwand 
verschoben und  nun brauche ich mehr "nop"s und zT an anderer Stelle.
Zur Sicherheit werde ich noch ein paar nops einbauen, wegen 
Temperaturschwankung oder Alterung.

Vielen Dank an Euch alle, Ihr habt sehr geholfen und ich kann endlich 
wieder ruhig schlafen!!!

Hier nochmal der funktionierende Code:
1
void WriteMem(uint16_t DataAdress, uint8_t MemQuart, uint16_t MemData){      // Schribt MemData an DataAdress, wobei MemQuart (Quartal) ein Quartal (4x(2x64k)) (16kx16Bit = 128k) des Speichers bedeutet, da nur 16BitAdressen verwendet werden können
2
  cli();
3
  MemQuart = MemQuart << 1;        // MemQuart x 2, weil Low und HighByte getrennt gespeichert werden müssen (64k Bytes *2 *4)
4
// LowAdress anlegen  
5
  PORTD = DataAdress;
6
  PORTC |= (MemLowAdr << 4);      // AdressLowByte an Memory
7
  PORTC &= 0b00001111;
8
// HighAdress anlegen
9
  PORTD = (DataAdress >> 8);      // AdressHighbyte an Memory
10
  PORTC |= (MemHighAdr << 4);
11
  PORTC &= 0b00001111;
12
// KonfigByte anlegen
13
  PORTD = ((MemQuart | (1 << CUSPtoDRDY)));    //Wahl des Quartals, CUSP aus, WRITE = Low
14
  PORTD &= ~(1 << PD3);
15
  PORTC |= (MemKonfigAdr << 4);
16
  PORTC &= 0b00001111;      
17
// DatanLowByte ausgeben  
18
  PORTD = MemData;          // DataLow auf Bus// Chipselect Memory
19
  PORTC |= (MemoryAdress << 4);    
20
// CS-Adresse  für Memory wegnehmen
21
  PORTC &= 0b00001111;
22
// KonfigByte ändern um HighByte in 2.Teil des Quartals zu schreiben
23
  PORTD = (((MemQuart + 1) | (1 << CUSPtoDRDY)));    //MemKonfig_Last &= ~((1 << PD0) | (1 << PD1) | (1 << PD2));  // Adresse 0, 1 und 2 (16, 17 und 18) = 0
24
  PORTD &= ~(1 << PD3);
25
  PORTC |= (MemKonfigAdr << 4);
26
  PORTC &= 0b00001111;  
27
// DatenHighbyte ausgeben
28
  PORTD = MemData >> 8;
29
// Chipselect Memory
30
  PORTC |= (MemoryAdress << 4);    
31
// CS-Adresse  für Memory wegnehmen
32
  PORTC &= 0b00001111;
33
// WE wieder High (active low) -> Read
34
  PORTD |= (1 << CUSPtoDRDY) | (1 << PD3);        
35
  PORTC |= (MemKonfigAdr << 4);
36
  PORTC &= 0b00001111;
37
  MemQuart = MemQuart >> 1;
38
  sei();
39
}
40
41
uint16_t ReadMem(uint16_t DataAdress, uint8_t MemQuart){            // bisher nur 8Bit Adresse
42
  cli();
43
  uint16_t MemData;
44
  uint8_t temp2;
45
  MemQuart = MemQuart << 1;
46
  PORTD = (MemQuart | (1 << CUSPtoDRDY) | (1 << PD3));    // WE auf High (active low) -> Read und CUSP aus
47
  PORTC |= (MemKonfigAdr << 4);
48
  PORTC &= 0b00001111;
49
// AdressLowByte an Memory  
50
  PORTD = DataAdress;
51
  PORTC |= (MemLowAdr << 4);    
52
  PORTC &= 0b00001111;
53
// AdressHighbyte an Memory  
54
  PORTD = (DataAdress >> 8);  
55
  PORTC |= (MemHighAdr << 4);
56
  PORTC &= 0b00001111;
57
// Port konfigurieren
58
  DDRD = 0;              // PortD ist EIngang
59
  PORTD = 0;              // keine PullUps
60
// Chipselect Memory  
61
  PORTC |= (MemoryAdress << 4);    
62
  asm volatile ("nop");
63
  asm volatile ("nop");    // Speicher benötigt Zeit  WICHTIG !!!!!!!!!!!!!!!!!!
64
// Output Enable von Memory = low (active LOW)  
65
  PORTB &= ~(1 << PB4);  
66
  asm volatile ("nop");
67
  asm volatile ("nop");    // Wichtig !!!!!!!!!!!!!!!!
68
// DatenLowByte einlesen
69
  MemData = PIND;
70
// Output Enable von Memory = High (active LOW)  
71
  PORTB |= (1 << PB4);    
72
  PORTC &= 0b00001111;        // Chipselect wegnehmen
73
// Port konfigurieren  
74
  DDRD = 255;              //Port als Ausgang
75
// WE auf High (active low) -> Read und CUSP aus  
76
  PORTD = ((MemQuart + 1) | (1 << CUSPtoDRDY) | (1 << PD3));  
77
  PORTC |= (MemKonfigAdr << 4);
78
  PORTC &= 0b00001111;        // Chipselect wegnehmen
79
// Port konfigurieren    
80
  DDRD = 0;              // PortD ist EIngang
81
  PORTD = 0;              // keine PullUps
82
// Output Enable von Memory = low (active LOW)  
83
  PORTB &= ~(1 << PB4);    
84
// Chipselect Memory  
85
  PORTC |= (MemoryAdress << 4);    
86
//DatenHighByte einlesen  
87
  asm volatile ("nop");
88
  asm volatile ("nop");
89
  asm volatile ("nop");
90
  asm volatile ("nop");
91
  asm volatile ("nop");      // Speicher benötigt Zeit  WICHTIG !!!!!!!!!!!!!!!!!!
92
  temp2 = PIND;  
93
// Output Enable von Memory = high  
94
  PORTB |= (1 << PB4);        
95
  PORTC &= 0b00001111;        // Chipselect wegnehmen
96
  DDRD = 255;              // PORTD ist wieder AUsgang
97
  MemData = MemData | (temp2 << 8);
98
  MemQuart = MemQuart >> 1;
99
  return MemData;
100
  sei();
101
}

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.