www.mikrocontroller.net

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


Autor: Hut Schugh (gock)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Hut Schugh (gock)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, Assemblercode...
Ich glaube ich habe eine der Stellen im Assemblercode finden können:
Aus diesem nichtoptimierten Code
  PORTC |= (MemLowAdr << 4);      // AdressLowByte an Memory
    15bc:  a5 e3         ldi  r26, 0x35  ; 53
    15be:  b0 e0         ldi  r27, 0x00  ; 0
    15c0:  e5 e3         ldi  r30, 0x35  ; 53
    15c2:  f0 e0         ldi  r31, 0x00  ; 0
    15c4:  80 81         ld  r24, Z
    15c6:  80 61         ori  r24, 0x10  ; 16
    15c8:  8c 93         st  X, r24
  PORTC &= 0b00001111;
    15ca:  a5 e3         ldi  r26, 0x35  ; 53
    15cc:  b0 e0         ldi  r27, 0x00  ; 0
    15ce:  e5 e3         ldi  r30, 0x35  ; 53
    15d0:  f0 e0         ldi  r31, 0x00  ; 0
    15d2:  80 81         ld  r24, Z
    15d4:  8f 70         andi  r24, 0x0F  ; 15
    15d6:  8c 93         st  X, r24
  asm volatile ("nop");
wird das hier:
PORTC |= (MemLowAdr << 4);      // AdressLowByte an Memory
     4e6:  ac 9a         sbi  0x15, 4  ; 21
  PORTC &= 0b00001111;
     4e8:  85 b3         in  r24, 0x15  ; 21
     4ea:  8f 70         andi  r24, 0x0F  ; 15
     4ec:  85 bb         out  0x15, r24  ; 21
  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?

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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
     15bc:  a5 e3         ldi  r26, 0x35  ; 53
     15be:  b0 e0         ldi  r27, 0x00  ; 0
     15c0:  e5 e3         ldi  r30, 0x35  ; 53
     15c2:  f0 e0         ldi  r31, 0x00  ; 0
     15c4:  80 81         ld  r24, Z
     15c6:  80 61         ori  r24, 0x10  ; 16
     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
      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
  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.

Autor: A. K. (prx)
Datum:

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

Autor: Hut Schugh (gock)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Verdammt, Du hast Recht (sbi 0x15 usw...). Vielleicht ist er dann doch 
einfach zu schnel für den Speicher...
#define MemLowAdr  1
#define MemHighAdr  2
#define MemKonfigAdr  3
#define MemoryAdress  4
[...]
void WriteMem(volatile uint16_t DataAdress, volatile uint8_t MemQuart, volatile uint16_t MemData){      // Schribt MemData an DataAdress, wobei MemQuart (Quartal) ein Quartal definiert
  cli();
  MemQuart = MemQuart << 1;        // MemQuart x 2, weil Low und HighByte getrennt gespeichert werden müssen (64k Bytes *2 *4)
// LowAdress anlegen  
  PORTD = DataAdress;
  PORTC |= (MemLowAdr << 4);      // AdressLowByte an Memory
  PORTC &= 0b00001111;
// HighAdress anlegen
  PORTD = (DataAdress >> 8);      // AdressHighbyte an Memory
  PORTC |= (MemHighAdr << 4);
  PORTC &= 0b00001111;
// KonfigByte anlegen
  PORTD = ((MemQuart | (1 << CUSPtoDRDY)));    //Wahl des Quartals, CUSP aus, WRITE = Low
  PORTD &= ~(1 << PD3);
  PORTC |= (MemKonfigAdr << 4);
  PORTC &= 0b00001111;      
// Chipselect Memory
  PORTC |= (MemoryAdress << 4);    
// DatanLowByte ausgeben  
  PORTD = MemData;          // DataLow auf Bus
  asm volatile ("nop");        // Speicher benötigt Zeit WICHTIG !!!!!!!!!!!!!!!!!!
// CS-Adresse  für Memory wegnehmen
  PORTC &= 0b00001111;
// KonfigByte ändern um HighByte in 2.Teil des Quartals zu schreiben
  PORTD = (((MemQuart + 1) | (1 << CUSPtoDRDY)));    //MemKonfig_Last &= ~((1 << PD0) | (1 << PD1) | (1 << PD2));  // Adresse 0, 1 und 2 (16, 17 und 18) = 0
  PORTD &= ~(1 << PD3);
  PORTC |= (MemKonfigAdr << 4);
  PORTC &= 0b00001111;  
// DatenHighbyte ausgeben
  PORTD = MemData >> 8;
  asm volatile ("nop");        // Speicher benötigt Zeit ???  WICHTIG !!!!!!!!!!!!!!!!!!
// Chipselect Memory
  PORTC |= (MemoryAdress << 4);    
  asm volatile ("nop");        // Speicher benötigt Zeit ???  WICHTIG !!!!!!!!!!!!!!!!!!
// CS-Adresse  für Memory wegnehmen
  PORTC &= 0b00001111;
// WE wieder High (active low) -> Read
  PORTD |= (1 << CUSPtoDRDY) | (1 << PD3);        
  PORTC |= (MemKonfigAdr << 4);
  PORTC &= 0b00001111;
  MemQuart = MemQuart >> 1;
  sei();
}

uint16_t ReadMem(volatile uint16_t DataAdress, volatile uint8_t MemQuart){            // bisher nur 8Bit Adresse
  cli();
  uint16_t MemData;
  uint8_t temp2;
  MemQuart = MemQuart << 1;
// Output Enable von Memory = low (active LOW)  
  PORTB &= ~(1 << PB4);    
// KonfigByte ausgeben: Quartal wählen, CUSP aus, READ
  PORTD = (MemQuart | (1 << CUSPtoDRDY) | (1 << PD3));    // WE auf High (active low) -> Read und CUSP aus
  PORTC |= (MemKonfigAdr << 4);
  PORTC &= 0b00001111;
// AdressLowByte an Memory  
  PORTD = DataAdress;
  PORTC |= (MemLowAdr << 4);    
  PORTC &= 0b00001111;
// AdressHighbyte an Memory  
  PORTD = (DataAdress >> 8);      
  PORTC |= (MemHighAdr << 4);
  PORTC &= 0b00001111;
// Port konfigurieren
  DDRD = 0;              // PortD ist EIngang
  PORTD = 0;              // keine PullUps
// Chipselect Memory  
  PORTC |= (MemoryAdress << 4);    
  asm volatile ("nop");        // Speicher benötigt Zeit  WICHTIG !!!!!!!!!!!!!!!!!!
  asm volatile ("nop");        // Speicher benötigt Zeit  WICHTIG !!!!!!!!!!!!!!!!!!
// DatenLowByte einlesen
  MemData = PIND;
  PORTC &= 0b00001111;        // Chipselect wegnehmen
// Port konfigurieren  
  DDRD = 255;              //Port als Ausgang
// WE auf High (active low) -> Read und CUSP aus  
  PORTD = ((MemQuart + 1) | (1 << CUSPtoDRDY) | (1 << PD3));  
  PORTC |= (MemKonfigAdr << 4);
  PORTC &= 0b00001111;        // Chipselect wegnehmen
// Port konfigurieren    
  DDRD = 0;              // PortD ist EIngang
  PORTD = 0;              // keine PullUps
// Chipselect Memory  
  PORTC |= (MemoryAdress << 4);    
//DatenHighByte einlesen  
  asm volatile ("nop");        // Speicher benötigt Zeit  WICHTIG !!!!!!!!!!!!!!!!!!
  asm volatile ("nop");        // Speicher benötigt Zeit  WICHTIG !!!!!!!!!!!!!!!!!!
  temp2 = PIND;  
  PORTC &= 0b00001111;        // Chipselect wegnehmen
// Output Enable von Memory = high  
  PORTB |= (1 << PB4);        
  DDRD = 255;              // PORTD ist wieder AUsgang
  MemData = MemData | (temp2 << 8);
  MemQuart = MemQuart >> 1;
  return MemData;
  sei();
}
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...

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Hut Schugh (gock)
Datum:

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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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,

Autor: Oliver (Gast)
Datum:

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

Oliver

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es wäre besser wenn du ein Schaltbild lieferst. Sonst etwas 
undurchsichtig.

Autor: Hut Schugh (gock)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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
// LowAdress anlegen  
  PORTD = DataAdress;
  PORTC |= (MemLowAdr << 4);      // AdressLowByte an Memory
  asm volatile ("nop");
  PORTC &= 0b00001111;

und verschaff dem Latch ein bischen Zeit

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Hut Schugh (gock)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hier die Verschaltung, ich prüfe nochmal die Logik...

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und beim Schreibzyklus fehlt CS völlig.

Autor: Hut Schugh (gock)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stimmt, habe mich in den Spinnweben der Leitungen verheddert und mit OE 
verwechselt.

Autor: Hut Schugh (gock)
Datum:

Bewertung
0 lesenswert
nicht 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:
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
  cli();
  MemQuart = MemQuart << 1;        // MemQuart x 2, weil Low und HighByte getrennt gespeichert werden müssen (64k Bytes *2 *4)
// LowAdress anlegen  
  PORTD = DataAdress;
  PORTC |= (MemLowAdr << 4);      // AdressLowByte an Memory
  PORTC &= 0b00001111;
// HighAdress anlegen
  PORTD = (DataAdress >> 8);      // AdressHighbyte an Memory
  PORTC |= (MemHighAdr << 4);
  PORTC &= 0b00001111;
// KonfigByte anlegen
  PORTD = ((MemQuart | (1 << CUSPtoDRDY)));    //Wahl des Quartals, CUSP aus, WRITE = Low
  PORTD &= ~(1 << PD3);
  PORTC |= (MemKonfigAdr << 4);
  PORTC &= 0b00001111;      
// DatanLowByte ausgeben  
  PORTD = MemData;          // DataLow auf Bus// Chipselect Memory
  PORTC |= (MemoryAdress << 4);    
// CS-Adresse  für Memory wegnehmen
  PORTC &= 0b00001111;
// KonfigByte ändern um HighByte in 2.Teil des Quartals zu schreiben
  PORTD = (((MemQuart + 1) | (1 << CUSPtoDRDY)));    //MemKonfig_Last &= ~((1 << PD0) | (1 << PD1) | (1 << PD2));  // Adresse 0, 1 und 2 (16, 17 und 18) = 0
  PORTD &= ~(1 << PD3);
  PORTC |= (MemKonfigAdr << 4);
  PORTC &= 0b00001111;  
// DatenHighbyte ausgeben
  PORTD = MemData >> 8;
// Chipselect Memory
  PORTC |= (MemoryAdress << 4);    
// CS-Adresse  für Memory wegnehmen
  PORTC &= 0b00001111;
// WE wieder High (active low) -> Read
  PORTD |= (1 << CUSPtoDRDY) | (1 << PD3);        
  PORTC |= (MemKonfigAdr << 4);
  PORTC &= 0b00001111;
  MemQuart = MemQuart >> 1;
  sei();
}

uint16_t ReadMem(uint16_t DataAdress, uint8_t MemQuart){            // bisher nur 8Bit Adresse
  cli();
  uint16_t MemData;
  uint8_t temp2;
  MemQuart = MemQuart << 1;
  PORTD = (MemQuart | (1 << CUSPtoDRDY) | (1 << PD3));    // WE auf High (active low) -> Read und CUSP aus
  PORTC |= (MemKonfigAdr << 4);
  PORTC &= 0b00001111;
// AdressLowByte an Memory  
  PORTD = DataAdress;
  PORTC |= (MemLowAdr << 4);    
  PORTC &= 0b00001111;
// AdressHighbyte an Memory  
  PORTD = (DataAdress >> 8);  
  PORTC |= (MemHighAdr << 4);
  PORTC &= 0b00001111;
// Port konfigurieren
  DDRD = 0;              // PortD ist EIngang
  PORTD = 0;              // keine PullUps
// Chipselect Memory  
  PORTC |= (MemoryAdress << 4);    
  asm volatile ("nop");
  asm volatile ("nop");    // Speicher benötigt Zeit  WICHTIG !!!!!!!!!!!!!!!!!!
// Output Enable von Memory = low (active LOW)  
  PORTB &= ~(1 << PB4);  
  asm volatile ("nop");
  asm volatile ("nop");    // Wichtig !!!!!!!!!!!!!!!!
// DatenLowByte einlesen
  MemData = PIND;
// Output Enable von Memory = High (active LOW)  
  PORTB |= (1 << PB4);    
  PORTC &= 0b00001111;        // Chipselect wegnehmen
// Port konfigurieren  
  DDRD = 255;              //Port als Ausgang
// WE auf High (active low) -> Read und CUSP aus  
  PORTD = ((MemQuart + 1) | (1 << CUSPtoDRDY) | (1 << PD3));  
  PORTC |= (MemKonfigAdr << 4);
  PORTC &= 0b00001111;        // Chipselect wegnehmen
// Port konfigurieren    
  DDRD = 0;              // PortD ist EIngang
  PORTD = 0;              // keine PullUps
// Output Enable von Memory = low (active LOW)  
  PORTB &= ~(1 << PB4);    
// Chipselect Memory  
  PORTC |= (MemoryAdress << 4);    
//DatenHighByte einlesen  
  asm volatile ("nop");
  asm volatile ("nop");
  asm volatile ("nop");
  asm volatile ("nop");
  asm volatile ("nop");      // Speicher benötigt Zeit  WICHTIG !!!!!!!!!!!!!!!!!!
  temp2 = PIND;  
// Output Enable von Memory = high  
  PORTB |= (1 << PB4);        
  PORTC &= 0b00001111;        // Chipselect wegnehmen
  DDRD = 255;              // PORTD ist wieder AUsgang
  MemData = MemData | (temp2 << 8);
  MemQuart = MemQuart >> 1;
  return MemData;
  sei();
}


Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.