Hallo liebe Wissende!
Ich verwende ein ATMEGA 88a-PU, WinAVR-20100110, urat lib von Peter
Felury mit 38.000 Baud 8N1
Sobald ich eeprom_write_block() benutze kommt aus der seriellen
Schnittstelle nur noch schrott. Die Funktion eeprom_read_block()
funktioniert einwandfrei. Zum Test habe ich ein kleines simples Programm
geschrieben damit das Problem nachvollziehbar ist.Das PRINT Macro
benötige ich später für eine formatierte Ausgabe...
Hat jemand eine Idee was hier so furchtbar schief läuft?
Karsten K. schrieb:> eeprom_read_block(Vdata, eedata, sizeof(Vdata));
Hier fehlt ein
eeprom_busy_wait ();
> eeprom_write_block(Vdata, eedata, sizeof(Vdata));
Und hier erst recht.
auserdem vorher:
uint8_t tmp_sreg = SREG;
cli();
und nacher:
SREG = tmp_sreg;
Frank M. schrieb:> Hier fehlt ein> eeprom_busy_wait ();>>> eeprom_write_block(Vdata, eedata, sizeof(Vdata));>> Und hier erst recht.>> auserdem vorher:>> uint8_t tmp_sreg = SREG;> cli();>> und nacher:>> SREG = tmp_sreg;
Unsinn.
"All of the read/write functions first make sure the EEPROM is ready to
be accessed."
busy_wait() ist also unnötig.
Und die EEPROM-Funktionen sperren selber die Interrupts wenn nötig, da
muss man nichts selber dran rumfummeln.
Zum Thema:
seltsames Problem. Selbst wenn die EEPROm-Funktion ewig die Interrupts
sperrt, kann da eigentlich nichts passieren, außer dass die UART-Ausgabe
verzögert wird. Falsche Zeichen können da eigentlich nicht vorkommen.
Wie sieht deine RAM-Auslastung aus?
chris schrieb:> Unsinn.> "All of the read/write functions first make sure the EEPROM is ready to> be accessed."> busy_wait() ist also unnötig.
Stimmt, Du hast recht. Danke für den Hinweis.
> Und die EEPROM-Funktionen sperren selber die Interrupts wenn nötig, da> muss man nichts selber dran rumfummeln.Das kann ich allerdings nicht nachvollziehen, denn:
"As these functions modify IO registers, they are known to be
non-reentrant. If any of these functions are used from both, standard
and interrupt context, the applications must ensure proper protection
(e.g. by disabling interrupts before accessing them)."
Danach muss man sich um die Sperrung der Interrupts schon selber
kümmern.
Hallo Ihr Besten!
Daaanke für die guten Tipps. Es ist nur das Testprogramm wie oben
beschrieben geladen, es sollte also kein Problem mit RAM geben.
Ich habe den Vorschlag das SREG zu sicher und die INterrupts
auszuschalten ausprobiert. Es hilft aber leider nicht. Zumal, als
Unwissender, cli() und danach sei() sollte doch das gleiche machen wie
das SREG manuell sichern, cli() und dann SREG zurückschreiben oder?
Ich bin komplett ratlos... :-(
Aber ehrlich: Ich möchte nicht etwas einsetzen, das "funktioniert und
keiner weis warum". Es mag ja nun gerade jetzt eine Konstellation
eingetreten sein, die das Funktionieren möglich macht. Später, im
wirklichen Code, gibt´s dann wieder Probleme.
Entscheidend ist hier das eeprom_busy_wait(). Ohne diesem habe ich trotz
des sperrens der Interrupts wieder "Müll" auf der seriellen
schnittstelle...
Gruß
Karsten
Karsten K. schrieb:> Unwissender, cli() und danach sei() sollte doch das gleiche machen wie> das SREG manuell sichern, cli() und dann SREG zurückschreiben oder?
Nein, ist nicht dasselbe. Die Sicherung und das Zurückschreiben von SREG
ist universeller. Es macht nämlich keine Annahme darüber, ob Interrupts
gerade eingeschaltet sind oder nicht. Das Zurückschreiben stellt einfach
den alten Zustand wieder her.
Ein sei() hingegen..... schaltet die Interrupts ohne Wenn und Aber ein -
egal, ob sie vorher aus oder an waren.
> Mit folgendem CodeTeil läuft es:
Gratulation! Offenbar stimmt da das libc-Manual nicht ganz. Ich
jedenfalls benutze immer eeprom_busy_wait() - auch wenn andere das als
Unsinn abtun.
Frank M. schrieb:> Gratulation! Offenbar stimmt da das libc-Manual nicht ganz. Ich> jedenfalls benutze immer eeprom_busy_wait() - auch wenn andere das als> Unsinn abtun.
Du liegst relativ falsch.
1) Das Manual ist korrekt. Es verspricht ja nur, dass vor einem
EEPROM-Zugriff geprüft wird, ob das EEPROM bereit ist. Hier geht es aber
um ein Abwarten hinterher.
2) Auch bezüglich der Interrupt-Sperren liegst du falsch. Die absolut
nötigen Sperren erledigt die Lib selber. Der von dir zitierte Teil
bezieht sich auf Reentrace, die hier aber nicht vorliegt.
Ich wette auch, dass das Problem hier rein gar nichts mit Interrupts zu
tun hat, und es auch nur mit dem eeprom_busy_wait() funktioniert. Der
Knackpunkt wird nämlich das Benutzen von LPM während eines laufenden
EEPROM-Schreibens sein.
Stefan Ernst schrieb:> Du liegst relativ falsch.
Ich lerne gerne dazu.
> 1) Das Manual ist korrekt. Es verspricht ja nur, dass vor einem> EEPROM-Zugriff geprüft wird, ob das EEPROM bereit ist. Hier geht es aber> um ein Abwarten hinterher.
Gut, verstanden. Dann hatte ich das Manual missverstanden.
Das heißt dann aber auch, dass mein ursprünglicher Tipp, nach dem
Schreiben eeprom_busy_wait() aufzurufen, korrekt und kein Unsinn war,
wie hier behauptet wurde.
> Ich wette auch, dass das Problem hier rein gar nichts mit Interrupts zu> tun hat, und es auch nur mit dem eeprom_busy_wait() funktioniert.
Das kann der OP ja leicht testen.
Ich werde die Wette aber nicht eingehen. Es kann gut sein, dass allein
der wait ausreicht, das will ich nicht abstreiten. Trotzdem wird das
meine Einstellung, bei solchen Sachen wie EEPROM lieber defensiv
vorzugehen, nicht ändern. Verkehrt ist es jedenfalls nicht. Schaden tut
es auch nicht.
Hallo Frank, Hallo Stefan
@FRank,
Danke für die Ausführung zu sei() und dem Sichern von SREG. Nun,
zwischen Zuweisung von SREG an eine tmp variable und dem Aufruf von
cli() kann ja noch gut und gern das SREG verändert werden.
Korrekterweise müsste dann das Sichern und das Abschalten der Interrupts
atomar erfolgen.
@Stefan,
Ahh, ich hatte es so verstanden, dass der Zugriff via eeprom_xx_yy()
komplett "gesichert" ist. Also auch das warten auf den kompletten
Vollzug des Schreib/Lesevorganges.
Wenn an dem nicht so ist, kann es in Bezug auf Interrupts ja dann doch
noch Probleme geben, wenn z.B. ein Schreibvorgang noch nicht
abgeschlossen ist, aber schon ein Interrupt aktiviert wird. Dann würde
ein eeprom_busy_wait() auch nur "zufällig" funktionieren. Nämlich dann,
wenn zwischen dem Schreib/lese Aufruf kein Interrupt erzeugt wird.
Es sei denn, ein Interrupt hat keinerlei Auswirkung auf den
Schreib/Lesevorgang selbst...
Jungs: Ihr wart eine MEGA Hilfe! Leider gibt´s noch keine
Mail-3D-Drucker für Bier; sonst würde es jetzt bei euch zu Hause rappeln
und ein schönes kühles Flens "gedruckt" werden...
gruß
Karsten
Frank M. schrieb:> Das heißt dann aber auch, dass mein ursprünglicher Tipp, nach dem> Schreiben eeprom_busy_wait() aufzurufen, korrekt und kein Unsinn war,> wie hier behauptet wurde.
Generell nötig ist eeprom_busy_wait() nicht (und so hattest du es doch
ursprünglich gemeint, oder?), aber im konkreten Fall ist es (vermutlich)
die Lösung des Problems.
Deshalb das "relativ". ;-)
Karsten K. schrieb:> Wenn an dem nicht so ist, kann es in Bezug auf Interrupts ja dann doch> noch Probleme geben, wenn z.B. ein Schreibvorgang noch nicht> abgeschlossen ist, aber schon ein Interrupt aktiviert wird.
Welche Probleme?
Karsten K. schrieb:> Also auch das warten auf den kompletten> Vollzug des Schreib/Lesevorganges.
Das hat keinen Einfluß.
Warten muß man nur, wenn man das nächste mal lesen oder schreiben will
oder ein SPM ausführen.
Interrupts und LPM werden davon nicht beeinflußt.
Zeig dochmal den Schaltplan.
Ich hatte mal komische Effekte, als ich vergessen hatte, AVCC
anzuschließen.
Peter Dannegger schrieb:> Interrupts und LPM werden davon nicht beeinflußt.
Ich kann bezüglich des LPM auch nichts im Datenblatt finden, aber die
Symptome deuten schon recht deutlich in diese Richtung.
Hallo Peter,
Danke für die lib :-)
Ich bin zu doof den richtigen Cast auszuführen.
1
uint8_tVdata[3];
2
uint8_teedata[]EEMEM={18,3,72};
3
4
...
5
eeprom(Vdata,eedata,sizeof(Vdata),EE_READ);
6
..
Natürlich meckert der Compiler bei Argument 2 da der Prototyp von
eepron() ja ein uin16_t als zweites Argument fordert um EEAR in einem
Rutsch zu füllen.
Ein Schaltplan gibt es noch nicht; Es ist nichts weiter als ein kleines
Rs232 Modul mit einem MAX232 an TX/RX, einem Reset taster und die
"normale" Reset beschaltung aus 4K7 und 100nF. AVcc habe ich mit +5V
belegt.
Ich habe schon alles weggelassen was irgendwie nicht unbedingt notwendig
ist ( SPI, Taster, LED´s, SIS command decoder usw... )
Gruß
Karsten
Karsten K. schrieb:> @FRank,> Danke für die Ausführung zu sei() und dem Sichern von SREG. Nun,> zwischen Zuweisung von SREG an eine tmp variable und dem Aufruf von> cli() kann ja noch gut und gern das SREG verändert werden.> Korrekterweise müsste dann das Sichern und das Abschalten der Interrupts> atomar erfolgen.
Nein.
ENTWEDER beim Zuweisen von SREG waren Interrupts deaktiviert. Dann KANN
sich SREG zwischen der Zuweisung und dem cli() gar nicht verändern.
ODER beim Zuweisen von SREG waren Interrupts aktiv. Dann könnte
tatsächlich ein Interrupt zwischen der Zuweisung und dem cli()
stattfinden. Nur: Ein Interrupthandler endet normalerweise mit einem
RETI, der alle Interrupts wieder aktiviert. Dieses Spielchen könnte
sogar mehrmals vor dem cli() passieren. Es macht aber nichts. Nach dem
schlussendlichen cli() läuft dann nur noch das Hauptprogramm. Am Ende
des geschützten Bereichs wird SREG aus tmp zurückgeladen und die
Interrupts werden wieder aktiviert. Der restliche Inhalt von SREG, und
ob er zwischendurch geändert wurde, ist uns ja schnurzegal.
Insofern muss das Sichern von SREG und das anschliessende cli() nicht
notwendigerweise atomar sein.
LG, Sebastian