Forum: Mikrocontroller und Digitale Elektronik USART, die 4220.


von Ralf G. (ralg)


Angehängte Dateien:

Lesenswert?

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.

von Ralf G. (ralg)


Lesenswert?

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.

von Ralf G. (ralg)


Lesenswert?

Noch was neues: Es kommt im Fall der Übertragung von (n-1)Bytes immer 
das 3.Byte nicht an!?

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

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?

von Stefan M. (celmascant)


Lesenswert?

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

von Ralf G. (ralg)


Lesenswert?

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:
1
  if ( TasteGedrueckt1(0,2) )
2
  {
3
   IRStatus &= ~(1 << B_USART_BUSY);
4
 /* TEST */ PORTB &= ~(1 << 5);
5
   _delay_ms(200);
6
 /* TEST */ PORTB |= (1 << 5);
7
  }

von Stefan E. (sternst)


Lesenswert?

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.

von Ralf G. (ralg)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von Ralf G. (ralg)


Lesenswert?

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 ;-)

von Ralf G. (ralg)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von Ralf G. (ralg)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von Ralf G. (ralg)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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

von Ralf G. (ralg)


Lesenswert?

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 :-)

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.