Mahlzeit - ich häng' fest!
Ein Master (atmega162) soll ein Datenpaket über RS485 an einen Slave
(attiny2313) schicken.
Nach dem Einschalten und auf Tastendruck funktioniert das auch genau
einmal. Gaaanz selten mehrmals hintereinander. Danach kommen (am
meisten) nur noch (Datenlänge-1)Bytes (kontrolliert mit Pinwackeln an
PIND6 vom tiny) oder weniger an. Extrem selten mal ein komplettes
Datenpaket. Ein Übertragungsfehler (DatenUSARTin() mit 'return false')
wird nie angezeigt. Nach längerer Pause (ca. 1min kein Tastendruck)
hängt sogar die Senderoutine (kontrolliert mit PINB7 am mega).
Die obigen Dateien gehören folgendermaßen zusammen:
- Projekt 'Master': MainMaster.c, Timer0.c, USART.c
- Projekt 'Slave': MainSlave.c, USART.c
Ist alles etwas amateurmäßig, vielleicht kann trotzdem mal jemand
drüberschauen und mich auf die richtige Fährte schicken.
Vielen Dank.
Neue Erkenntnis: Es muss (müsste) am Master liegen.
Habe den Slave 'im laufenden Betrieb' neu geflasht: Master gestartet -
Taste - ordentliche Datenübertragung - Taste - Slave schließt Empfang
nicht ab, da letztes Byte fehlt - Slave neu beschrieben - Taste - Slave
schließt Empfang nicht ab, da letztes Byte fehlt ... - Taste - Master
schließt Senden nicht ab, bleibt im USART-Busy-Status. Also alles wie
immer.
Ralf G. schrieb:> Noch was neues: Es kommt im Fall der Übertragung von (n-1)Bytes immer> das 3.Byte nicht an!?
Moin!
Ich hab zwar keine Ahnung, aber ich werf mal Fragen in den Raum:
- Zeitbasis verlässlich (Quarzoszillator bei beiden AVR)?
- Anzahl der Stoppbits mal verändert (verschiedene Kombinationen
probieren)?
P.S.: Hast du einen Schaltplan zur Hand?
Ralf G. schrieb:> Ein Master (atmega162) soll ein Datenpaket über RS485 an einen Slave> (attiny2313) schicken.
Welche RS485-Treiber benutzt du denn?
Ich habe vor kurzem ein RS485 Bussystem mit dem SN75176 als Treiber
aufgebaut. An den Slaves kam nurnoch Murks raus.
Die Ursache war ziemlich einfach: Die Ausgangsspannung des SN75176 war
zu niedrig. Der MC hat das nur sporadisch als High erkannt.
Abhilfe hat das einschalten des Pullups des Rx-Pins geschaffen. Seitdem
geht das Problemlos (mittlerweile 2 Monate).
Gruss Stefan
Ich teste dann mal weiter.
Markus W. schrieb:> Ich hab zwar keine Ahnung, aber ich werf mal Fragen in den Raum:> - Zeitbasis verlässlich (Quarzoszillator bei beiden AVR)?
ja, die 6.144MHz
> - Anzahl der Stoppbits mal verändert (verschiedene Kombinationen> probieren)?
noch nicht.
Stefan M. schrieb:> Welche RS485-Treiber benutzt du denn?MAX485> Ich habe vor kurzem ein RS485 Bussystem mit dem SN75176 als Treiber> aufgebaut. An den Slaves kam nurnoch Murks raus.> Die Ursache war ziemlich einfach: Die Ausgangsspannung des SN75176 war> zu niedrig. Der MC hat das nur sporadisch als High erkannt.> Abhilfe hat das einschalten des Pullups des Rx-Pins geschaffen.
auch mit Pullups dasselbe.
Micht wundert eins sehr stark:
ISR:
1
ISR(V_USART_TXC_vect)
2
{
3
V_UCSRB&=~(1<<B_TXCIE);
4
IRStatus|=1<<B_USART_TXC;
5
IRStatus&=~(1<<B_USART_BUSY);
6
};
in der Hauptschleife:
1
...
2
if(IRStatus&(1<<B_USART_TXC))
3
{
4
IRStatus&=~(1<<B_USART_TXC);
5
/* TEST */PORTB|=(1<<5);
6
}
7
...
Wieso kann das gelegentlich passieren, dass die if-Abfrage
abgearbeitet wird - also das Bit
1
B_USART_TXC
gesetzt ist - aber das
1
B_USART_BUSY
-Bit nicht gelöscht wird? Erkennbar daran, dass die LED an PINB7 nicht
mehr blinkt und die Taste nicht mehr reagiert.
In der Hauptschleife am Anfang:
1
...
2
if(!(IRStatus&(1<<B_USART_BUSY)))
3
{
4
/* TEST */if(!verz)
5
/* TEST */PORTB^=(1<<7);
6
...
An das Ende der while-Schleife habe ich zum Rücksetzen nochmal mit
eingefügt:
Ich habe das Ganze nur überflogen (also nicht bis ins letzte Detail
nachvollzogen), dabei sind mir drei Dinge aufgefallen:
1) IRStatus wird sowohl im Interrupt-Kontext, als auch im Main-Kontext
verändert, aber keiner der Main-Kontext-RMW-Zugriffe ist geschützt.
2) Der TXC-Interrupt wird verwendet, ist aber nur zeitweise
eingeschaltet. Es wird aber nirgendwo das Interrupt-Flag "von Hand"
gelöscht. Wenn also zum Zeitpunkt des Einschaltens das Flag bereits
gesetzt ist (*), dann hast du ein Problem.
(*): z.B. weil ein UDRE-Interrupt so lange verzögert wurde, dass der
UART "zwischendrin" schon mal "leer gelaufen" ist.
3) Du verwendest sleep_mode(), was potenzielle Race-Conditions
beinhaltet. Überhaupt bringt man so was erst mal ohne Sleep zum laufen,
und baut das dann danach ein.
Stefan Ernst schrieb:> 1) IRStatus wird sowohl im Interrupt-Kontext, als auch im Main-Kontext> verändert, aber keiner der Main-Kontext-RMW-Zugriffe ist geschützt.
Könnte sein. Ist auf jeden Fall sicherer, wenn ich mal eine
Interruptsperre einbaue.
> 2) Der TXC-Interrupt wird verwendet, ist aber nur zeitweise> eingeschaltet. Es wird aber nirgendwo das Interrupt-Flag "von Hand"> gelöscht. Wenn also zum Zeitpunkt des Einschaltens das Flag bereits> gesetzt ist (*), dann hast du ein Problem.> (*): z.B. weil ein UDRE-Interrupt so lange verzögert wurde, dass der> UART "zwischendrin" schon mal "leer gelaufen" ist.
Der TXC-Interrupt wird nach Übernahme des letzten Bytes nach UDR
eingeschaltet und der UDRE-Interrupt abgeschaltet. Das TXCIE-Bit wird im
TXC-Interrupt wieder gelöscht. Ist nach meiner Ansicht erstmal eine
logische Reihenfolge.
> 3) Du verwendest sleep_mode(), was potenzielle Race-Conditions> beinhaltet. Überhaupt bringt man so was erst mal ohne Sleep zum laufen,> und baut das dann danach ein.
Ich habe da gleich in den Sendepausen den Timer0-Interrupt
(Tastenentprellung) als Zeitbasis für Kontrollblinken der LEDs.
Also, ich leg dann mal los.
Ralf G. schrieb:> Der TXC-Interrupt wird nach Übernahme des letzten Bytes nach UDR> eingeschaltet und der UDRE-Interrupt abgeschaltet. Das TXCIE-Bit wird im> TXC-Interrupt wieder gelöscht. Ist nach meiner Ansicht erstmal eine> logische Reihenfolge.
Ich habe auch nichts anderes behauptet.
Aber ich an deiner Stelle würde auf Nummer sicher gehen und zusätzlich
das TXC-Flag direkt nach dem Schreiben des letzten Bytes nach UDR
löschen.
Stefan Ernst schrieb:> 1) IRStatus wird sowohl im Interrupt-Kontext, als auch im Main-Kontext> verändert, aber keiner der Main-Kontext-RMW-Zugriffe ist geschützt.
Äähm...
Das war's! Der Timer-Interrupt hat mir dazwischengefunkt. Dort wird auch
ein Bit gesetzt. (Brauch ich später noch, habe das Programm zur
Fehlersuche erstmal eingekürzt) Hab' ich völlig ignoriert.
Vielen Dank, für den Hinweis. Liest man (also ich) zwar ständig, dass
man sowas beachten muss, aber es betrifft einen ja nicht ;-)
Stefan Ernst schrieb:> Aber ich an deiner Stelle würde auf Nummer sicher gehen und zusätzlich> das TXC-Flag direkt nach dem Schreiben des letzten Bytes nach UDR> löschen.
Da ist es doch aber noch gar nicht gesetzt? An der Stelle schalte ich
den Interrupt doch erst ein. Denk' ich jedenfalls.
Ralf G. schrieb:> Da ist es doch aber noch gar nicht gesetzt? An der Stelle schalte ich> den Interrupt doch erst ein. Denk' ich jedenfalls.
Du verwechselst hier Interrupt-Flag mit Interrupt-Enable-Bit.
Das Flag wird immer beim Eintreten des entsprechenden Ereignisses
gesetzt, egal ob der dazugehörige Interrupt gerade eingeschaltet ist,
oder nicht. Und wenn das Flag dann schon gesetzt ist, wenn der
entsprechende Interrupt eingeschaltet wird, kommt der Interrupt sofort.
Daher zur Sicherheit vor dem Einschalten das Flag löschen.
Stefan Ernst schrieb:> Du verwechselst hier Interrupt-Flag mit Interrupt-Enable-Bit.
Ich meinte: Der TXC-Interrupt ist ja noch gar nicht aktiviert! Wird erst
im UDRE-Interrupt eingeschaltet um sich dann selbst in der ISR wieder
auszuschalten.
Meinst du IR aktivieren und gleich das Interrupt-Flag löschen? Aber ich
will doch gerade sicher sein, dass ich die Meldung vom µC bekomme, dass
die Daten richtig raus sind. Wegen späterer Sende-/Empfangsumschaltung.
Ralf G. schrieb:> Ich meinte: Der TXC-Interrupt ist ja noch gar nicht aktiviert!
Spielt keine Rolle. Auch ohne aktivierten Interrupt wird das TXC-Flag
gesetzt, falls mal die Daten nicht schnell genug nach UDR nachgeliefert
werden können, so dass auch zwischendurch der Zustand "alles ist raus"
(aus Sicht des µC) eintrifft. Und wenn du dann den TXC-Interrupt
einschaltest, kommt er sofort (weil das Flag ja gesetzt ist), obwohl zu
dem Zeitpunkt dann noch gesendet wird.
Aaaah!
Also, das (jedes?) IR-Flag wird gesetzt, wenn das entsprechende Ereignis
eintritt. Mit dem entsprechenden Enable-Flag zeige ich sozusagen nur an,
ob's mich interessiert. Wenn das so ist, dann verstehe ich deine
Erklärung. Ich bin davon ausgegangen, dass das Eintreten eines IR
durch das Enable-Flag gesteuert wird.
Ralf G. schrieb:> Also, das (jedes?) IR-Flag wird gesetzt, wenn das entsprechende Ereignis> eintritt. Mit dem entsprechenden Enable-Flag zeige ich sozusagen nur an,> ob's mich interessiert.
Richtig.
Das sind im Grunde zwei unabhängige Vorgänge:
a) Ereignis tritt auf -> Flag wird gesetzt
b) Flag und Enable-Bit gleichzeitig 1 -> Interrupt wird ausgelöst
Stefan Ernst schrieb:> Das sind im Grunde zwei unabhängige Vorgänge:> a) Ereignis tritt auf -> Flag wird gesetzt> b) Flag und Enable-Bit gleichzeitig 1 -> Interrupt wird ausgelöst
Na, da hat sich die Zusatzstunde doch gelohnt :-)