Hallo Leute, ich habe mich heute das erste Mal an die EEprom-Programmierung herangewagt, werde allerdings nicht so ganz schlau aus dem Tutorial: http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#EEPROM Mein Ziel ist es in einem Abstand von einigen Sekunden einen 16-Bit Wert im EEprom abzulegen. Diese Werte sollen dann nacheinander (irgendwann später, wenn der Befehl vom PC kommt) an den Computer übertragen werden. So sehen meine Gedankenansätze dazu aus (s. Anhang) Bitte nicht wundern: Das Programm ist extrem gekürzt - habe nur das relevant stehen gelassen. Mein Problem ist auch, dass es schon zu lange her ist, dass ich mit Pointer programmiert habe - in C habe ich das noch gar nicht gemacht! Könntet ihr mir bitte den Umgang mit Pointern anhand meines Beispiels erklären und meine Fehler korrigieren, denn so lerne ich das am Besten.
Wieso hantierst du manuell mit den Adressen rum? Das sollte der Linker für dich übernehmen. So zeigt es ja auch das Tutorial. Dein Code sieht völlig anders aus als der dort beschriebene. Da jetzt ein Beispiel hinzuschreiben bringt eigentlich nicht viel, da das Tutorial das meines Erachtens schon ganz gut tut.
Naja, wie soll ich das sonst machen? Ich will ja So lange Messwerte im EEprom speichern, bis ich die Messung abbreche! Die Anzahl ist also variabel. Da ist es doch am einfachsten mit Adressen zu arbeiten, oder sehe ich das falsch? Wie würdest du das denn machen? Im Tutorial werden immer nur einzelne Werte gespeichert und nicht eine ganze Reihe davon.
Ohne mir das ganze jetzt anzusehen, machs doch so: 1. Du setzt den Pointer auf den Anfang des EEPROMs 2. Du schreibst einen Messwert an die Stelle, auf die der Pointer zeigt, ins EEPROM hinein 3. Du inkrementierst den Pointer 4. Schritt 2-4 solange wiederholen, bis der Abfrage-Befehl kommt 5. Du sendest den EEPROM-Inhalt ab dem ersten Messwert (= Anfang EEPROM) 6. Mit Schritt 1 weitermachen Du musst dir natürlich überlegen, was passieren soll, wenn das EEPROM voll ist, denn dann hast du mehrere Möglichkeiten: 1. Du schreibst am Anfang des EEPROMs weiter. Das nennt man dann Ring-Speicher. Allerdings verlierst du dann die ältesten Messwerte. Und du musst dann beachten, dass die ältesten Messwerte nicht mehr am Anfang des EEPROMs stehen. 2. Du ignorierst alle weiteren Messungen (= Speicher voll) 3. Du sendest bei vollem EEPROM den Inhalt automatisch zum PC. Der Vorteil wäre, dass du keine Daten verlierst... Du brauchst im Prinzip nur zwei Adressen, und das sind die Start- und die Ende-Adresse des EEPROMs, damit du erkennen kannst, wann das EEPROM voll ist. In C übernimmt der Compiler das Aufaddieren der Adresse durch das Pointer-Inkrementieren automatisch, also wenn du 8-Bit-Werte speicherst, wird die Adresse immer um 1 Byte erhöht, bei 16-Bit-Werten um 2 Byte. Das erkennt der Compiler deswegen, weil du bei der Pointer-Deklaration angeben musst, auf welchen Datentyp der Pointer zeigt. HTH Ralf
Du bist genau der richtige Ansprechpartner für mich - genau so wollte ich es machen, nur dass ich noch nicht genau weiß, wie ich das in den C-Code umsetze. Im Folgenden werde ich mal meinen Quellcode stück für Stück darstellen und dahinter schreiben, was dieser Code machen sollte - wäre nett, wenn du meinen Code dann verbessern könntest (ist nicht so viel), damit ich das lerne und verstehe. WEnn der Speicher voll ist, dann wird die Messung halt abgebrochen, aber das progge ich später. Direkt an den PC übertragen geht nicht, da die Daten erst später abgerufen werden sollen, d.h. zwischendrin besteht keine Verbindung zum PC. Los gehts: (uint16_t *) (EE_ADR = 2); // Damit möchte ich den Pointer auf Adresse 2 setzen, da ich in 0-1 die anzahl der aufgenommenen Messwerte speichern möchte uint16_t * EE_ADR = (uint16_t *) 2; // Deklaration des EEprom-Pointers - Startadresse = 2 // EEprom Speichervorgang void Save_EE(uint16_t eingang) { eeprom_write_word ((uint16_t *) EE_ADR, eingang); (uint16_t *) (EE_ADR + 2); } // Diese Funktion (Save_EE) soll einen Messwert (2Byte) in der aktuellen Adresse (EE_ADR) ablegen und den Pointer um 2 erhöhen. // EEprom Datenausgabe an den PC void Copy_EE(void) { uint16_t cnt; uint16_t cntmax; uint16_t Ausgabe; char *Temp; cntmax = EE_ADR + 1; for (cnt = 2; cnt < cntmax; cnt = cnt + 2) { Ausgabe = eeprom_read_word ((uint16_t *) cnt); Temp = (char *) Ausgabe; uart_puts(Temp); } } // in cntmax soll die anzahl der abgelegten Messwerte gespeichert werden. Anschließend soll über eine Schleife von 2 (startwert) bis zu cntmax ein Datensatz nach dem anderen an den PC übertragen werden. Dazu werden die EEprom-Adressen angesprochen (eeprom_read_word), in einen String umgewandelt und über den UART verschickt. Das waren eigentlich schon alle Stellen, die mir Probleme machen - bitte nicht über einige Fehler lachen - denke schon, dass dort teilweise erhebliche Mängel drin sind, aber ich kenn mich halt noch nicht mit der EEprom-Programmierung aus. Vielen Dank im Vorraus und ein fröhliches Weihnachtsfest euch allen!
Ausgabe = eeprom_read_word ((uint16_t *) cnt); Temp = (char *) Ausgabe; uart_puts(Temp); Das geht so nicht. Ich nehme an, dass im EEPROM deine Messwert als 16bit Integer stehen. Diese kannst du nicht mit uart_puts() ausgeben, da das eine String-Ausgabefunktion ist (es sei denn, du hast sie geändert), d.h. das sie bei der ersten 0 abbricht, was bei jedem Messwert <256 der Fall ist. Du meinst sicherlich:
1 | char str[80]; |
2 | |
3 | sprintf(str,"%d ",Ausgabe); |
4 | uart_puts(str); |
*******die erste Zeile weglassen: (uint16_t *) (EE_ADR = 2); // Damit möchte ich den Pointer auf Adresse 2 setzen, da ich in 0-1 die anzahl der aufgenommenen Messwerte speichern möchte ******* diese ist richtig uint16_t * EE_ADR = (uint16_t *) 2; // Deklaration des EEprom-Pointers - Startadresse = 2 // EEprom Speichervorgang void Save_EE(uint16_t eingang) { ***** das (uint16_t *) kannste dir sparen: eeprom_write_word (EE_ADR, eingang); *** so nicht: (uint16_t *) (EE_ADR + 2); *****sondern so: EE_ADR += 1; den Rest habe ich mir nicht angeschaut, vielleicht erst mal die Fehler raus machen Grüße Walter
> d.h. das sie bei der ersten 0 abbricht, was bei jedem Messwert <256 >
der Fall ist.
Nein, weil Werte little-endian abgespeichert werden, dass heißt also
dann bei 0, 256, ...
Aber egal, geht so oder so nicht :-)
Ich weiß nicht genau um was für ein Gerät es sich handelt, aber wenn es
im Dauerbetrieb ist (z.B. Heizungsregler oder so was) ist es keine gute
Idee den EEPROM als Datenpuffer zu verwenden, weil der EEPROM für solch
einen Zweck doch relativ schnell kaputt geht. Z.B. ATMega 16, 512 Bytes
EEPROM = Platz für 256 Integer Werte, jede Sekunde ein Wert = ein
kompleter Zyklus in 4,2 Minuten, EEPROM 100.000x beschreibbar -> 0.8
Jahre, würde mich nerven von meinem Heizungsregler rund alle 9 Monate
den Controller austauschen zu müssen ;-)
Stefan
Vielen Dank schon einmal für eure Tipps - werde ich wohl morgen oder übermorgen ausprobieren, da ich gerade noch Weihnachten feiere! :-) Es handelt sich bei der ganzen Geschichte um einen Datenlogger für einen ferngesteuerten Wagen, d.h. ich erreiche die 100000 Schreibzugriffe des EEproms eh nicht, da der Logger längst nicht im Dauerbetrieb arbeitet, sondern sporadisch genutzt wird. Wie kann den Sourcecode eigentlich so darstellen, wie Fritz Ganter das hier im Forum gemacht hat? Gibt es da einen BB-Code?
So, ich habe die Verbesserungsvorschläge mal eingearbeitet, doch ich bekomme immernoch Fehler angezeigt: main.c: In function `Copy_EE': main.c:109: warning: assignment makes integer from pointer without a cast 109>> cntmax = EE_ADR + 1; main.c: In function `__vector_11': main.c:430: warning: unused variable `EE_ADR' 430>> uint16_t * EE_ADR = (uint16_t *) 2; // Deklaration des EEprom-Pointers - Startadresse = 2 @Stefan Seegel: Was meinst du mit "Aber egal, geht so oder so nicht :-)" Warum wird hier nur 1 addiert (EE_ADR += 1;) wir arbeiten doch mit Words, da muss ich doch normalerweise 2 addieren!? Für EE_ADR += 1 könnte ich doch auch EE_ADR++ schreiben, oder?
Ja, das kannst du auf jeden fall! Die beiden Ausdrücke sind gleich. Gruß Timo
"Nein, weil Werte little-endian abgespeichert werden, dass heißt also dann bei 0, 256, ..." Wo steht, dass die Werterepräsentation Little Endian ist? Der AVR ist doch sicherlich Big Endian?! Little Endian kommt aus der Steinzeit, wenn ich das richtig sehe, und wird heute überwiegend auf so Sparc-Kisten im medizinischen Bereich verwendet...
> Wo steht, dass die Werterepräsentation Little Endian ist? Der AVR > ist doch sicherlich Big Endian?! Little Endian kommt aus der > Steinzeit, wenn ich das richtig sehe, und wird heute überwiegend auf > so Sparc-Kisten im medizinischen Bereich verwendet. ROTFL Erstens hast du ganz offensichtlich little endian und big endian verwechselt. Sie heißen nicht umsonst Intel und Motorola byte order (in dieser Reihenfolge), d.h. aller Intel-Krempel ist little endian. Nun, man kann natürlich mit Fug und Recht sagen, dass der tatsächlich aus der Steinzeit käme... letztlich schleppt er den Ballast des alten 8086 immer noch mit sich rum, und dieser wiederum schleppte eigentlich nur den Ballast des 8080 mit sich rum. ;-) Anyway, bei byte order gibt's keine Steinzeit, sondern zwei gleichberechtigte Modelle, von denen man für jedes einige Punkte finden kann. Auch wenn du eine steinzeitlich-abgeleitete little endian CPU in deinem PC hast, big endian ist genauso verbreitet. Erstens ist es die network byte order, d.h. alle nennenswerten Netzwerkprotokolle benutzen little endian für ihre Zahlendarstellung (IP-Adressen, Portnummern, Sequenznummern, ...). Zweitens können wohl nahezu alle ,,großen'' RISC-Prozessoren beide Varianten (über ein Steuerregister der CPU definiert), werden aber typisch big endian betrieben. Das sind keinesfalls nur die SPARC-CPUs (nun UltraSPARC, die waren schon 64-bittig lange bevor davon in der Intel-Welt überhaupt jemand nur geträumt hätte), sondern auch MIPS und POWER. Letztere gibt's auch in PCs, nur eben nicht von Intel: dem Macintosh. Ansonsten tauchen viele davon aber auch in Form aller möglicher Microcontroller auf (ein bis zwei Nummern größer als ein AVR halt) und sind dadurch auch in deiner Umgebung stärker vertreten als du vermuten wirst. Zurück zum AVR: erstens hat er als 8-bit-CPU gar keinen richtigen byte sex. Schließlich könnte man denken, dass er ja nur einzelne Bytes verarbeitet und es daher an der Software liegt, wie sie Zahlen aus mehreren Bytes verarbeitet. Letzteres geschieht wohl in der Tat als little endian, zumindest in den gängigen Compilern. Zu aller Verwirrung besitzt aber auch die AVR-CPU zwei Dinge, die ein bisschen endianess durchgucken lassen, und noch dazu zwei verschiedene: der ROM ist 16-bittig organisiert, und die Zugriffe auf ihn sind m. W. big endian. In neueren CPU-Cores sind nun auch Datenbefehle für 16-bit- Zahlen hinzugekommen, diese hingegen sind little endian (und stimmen damit mit den existierenden Compilern und Bibliotheken überein). Ganz kurz zurück zum Subject: wie man die Werte im EEPROM abspeichert, bleibt einem natürlich gut und ganz selbst überlassen, da es für diesen in keiner Hinsicht Wortbefehle gibt. Sowohl little endian als auch big endian sind also möglich. Wenn die eeprom_*_word()-Routinen der avr-libc nimmt, diese arbeiten little endian.
Hallo (mal ganz kleinlaut), ich habe grade nochmal ein Vorlesungsskript "Modelle und Standarts zur Bildverarbeitung und Kommunikation" gewälzt und stellte fest, dass dort nicht direkt gezeigt wird, welche VR auf welchem System heute noch läuft, bzw. früher lief. Also schaute ich nochmal in wikipedia und muss sagen, dass ich in der Tat ganz offensichtlich Little Endian und Big Endian vertauscht habe. Allerdings wundere ich mich darüber. Nicht, dass ich mich nicht irre, sondern dass Intel little Endian ist... Ich hatte das anders in Erinnerung. Sorry für das verbreiten falscher Tatsachen!
Das klingt ja sehr interessant. Um mal zurück zu meinem Problem zu kommen - ich weiß immer noch nicht, wie ich die Fehler behebe!
main.c: In function `Copy_EE': main.c:109: warning: assignment makes integer from pointer without a cast 109>> cntmax = EE_ADR + 1; main.c: In function `__vector_11': main.c:430: warning: unused variable `EE_ADR' 430>> uint16_t * EE_ADR = (uint16_t *) 2; // Deklaration des EEprom-Pointers - Startadresse = 2
du verwendest in deinem Code EE_ADR, bevor es initialisiert wurde. Das hat Walter ja oben schon behoben. Dann kann ich nicht erkennen, wieso du den Pointer auf folgende Art deklarierst: uint16_t * EE_ADR = (uint16_t *) 2; Wenn der erstellte Pointer = 2 werden soll, ist es nicht nötig die 2 erst auf ein 16-Bit Value zu casten. Ein Pointer ist auch nur ein integer-Wert. Die erste Warnung kannst du meiner Meinung nach ignorieren, denn es ist ja gewollt, dass dein cntmax = dem aktuellen Pointer + 1 ist. Du willst ja den Wert des Pointers haben, also die Adresse, die er in sich trägt. Um die zweite Warnung richtig zu verstehen, brauche ich mehr code. Was sie sagt ist, dass du EE_ADR nicht verwendest. Er wird also die ganze Zeile wegoptimieren, weil sie für ihn keinen Sinn ergibt...
"Warum wird hier nur 1 addiert (EE_ADR += 1;) wir arbeiten doch mit Words, da muss ich doch normalerweise 2 addieren!?" EE_ADR ist ein Pointer auf einen 16Bit Wert, also auf 2 Bytes. Wenn du in C den Pointer um 1 erhöhst zeigt er auf den nächsten 16Bit Wert. Bei einem byteorientierten System wie dem AVR zeigt der Pointer also jetzt auf die Adresse die 2 Bytes weiter ist. Grüße Walter
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.