Forum: Mikrocontroller und Digitale Elektronik String mit variabler Länge über UART empfangen


von Andreas G. (andy1024)


Angehängte Dateien:

Lesenswert?

Hallo Ihr,

ich hab folgendes Problem. Das CD-Laufwerk das ich benutze kommuniziert 
mit 115200 baud mit einme ATMEGA 644. Dem Laufwerk Befehle zu schicken 
ist kein Problem. Nur die Aubereitung der Daten die vom Laufwerk kommen 
bereitet mir etwas Kopfzerbrechen.
Das erste empfangene bit ist OK, nur die folgenden bekomme ich nicht 
mit.
Die Idee war: auf das erste bit mit einem Interrupt reagieren und die 
weiteren dann durch polling einlesen und in einem String abspeichern. 
Zwischen den einzelnen Byte ist keine Pause.

Mein Code bis jetzt hab ich angehängt.

Sieht einer von euch den Fehler? Oder geht das so garnicht?


Danke
Andy

PS:

Das zweite Byte das übertragen wird ist die anzahl der byte die noch 
kommen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Andreas Geissler schrieb:
> Mein Code bis jetzt hab ich angehängt.
Ein Text-Programm. Cool.

> Das erste empfangene bit ist OK
Du meinst wohl Byte...
Wenn du mit einem Schlosser sprichst und M8 sagst, dann hat der sofort 
ein Bild vor dem Auge und kann nicht wissen, dass du "eigentlich" eine 
M22 Schraube meinst.

Gut soweit. Zur Sache:
> Die Idee war: auf das erste bit mit einem Interrupt reagieren und die
> weiteren dann durch polling einlesen
Und das in der Interruptroutine. Du hältst also solange den Prozessor 
von der Arbeit ab und sperrst andere Interrupts. Die Idee scheint mir 
ungünstig.
Aber dein Problem ist schon seit Ende des letzten Jahrtausends gelöst 
und die Lösung heißt FIFO.

Andreas Geissler schrieb:
> Zwischen den einzelnen Byte ist keine Pause.
Doch: zwischen den einzelnen Bytes ist von Sicht deiner Software aus 
jeweils ca. 90us Pause. Denn so lange ist der Empfänger mit dem nächsten 
Byte beschäftigt...

Andreas Geissler schrieb:
> Das zweite Byte das übertragen wird ist die anzahl der byte die noch
> kommen.
Woran erkennst du das erste Byte?

von Andreas G. (andy1024)


Lesenswert?

Hallo Lothar,

das erste Byte erkenne ich am Interrupt. Das erste byte kann alles 
mögliche sein. Es ist kein einheitliches Zeischen. Von daher war die 
Idee den Interrupt zu sperren und die weiteren Bytes zu pollen. Die 
kommen mehr oder weniger ohne Pause, Maximal 10 Stück. Wenn der player 
läuft kommen die Bytets im Sekunden Rhytmus.

von M. K. (sylaina)


Lesenswert?

Andreas Geissler schrieb:
> Die Idee war: auf das erste bit mit einem Interrupt reagieren und die
> weiteren dann durch polling einlesen und in einem String abspeichern.
> Zwischen den einzelnen Byte ist keine Pause.

Mal abgesehen davon, dass du Byte meinst und nicht Bit: Warum zur Hölle 
beginnst du in der ISR mit Polling? Das ist ja mal völlig sinnbefreit.
Überlege doch jetzt einfach mal, was passiert wenn die ISR des USART 
ausgelöst wird. Genau, immer dann wenn ein Zeichen empfangen wurde (wäre 
übrigens die Antwort auf Lothars letzte Frage). Wie Lothar schon schrieb 
hast du nun 90 µs Zeit den Eingangsbuffer auszulesen und auf das nächste 
Zeichen zu warten. Sollte also mehr als genug Zeit sein um den Buffer 
wieder zu leeren und sich auf das folgende Byte vorzubereiten.

von Peter D. (peda)


Lesenswert?

Das Laufwerk wird mit Sicherheit ein Protokoll sprechen, d.h. irgendwie 
ist festgelegt, was das erste und was das letzte Byte ist.

Der Interrupt liefert Dir nur die Aussage es ist irgend ein Byte da, 
aber nie, welches.
Dir bleibt also nichts anderes übrig, als das Protokoll zu 
implementieren.

von Andreas G. (andy1024)


Lesenswert?

Hallo Peter,

ich möchte das Protokol implementieren, ganz klar, da bin ich dran.
Wie gesagt das erste byte ist kein Problem das bekomme ich nur dann hört 
es auf, obwohl noch weitere bytes übertragen werden.
Ich überlege wie ich es besseer machen könnte, ich werde mir nachher den 
FIFO puffer anschauen den ich im Forum gefunden habe.


Andy

von M. K. (sylaina)


Lesenswert?

Andreas Geissler schrieb:
> Wie gesagt das erste byte ist kein Problem das bekomme ich nur dann hört
> es auf, obwohl noch weitere bytes übertragen werden.

Das Problem ist dein Polling denke ich. Wie schon gesagt, der ISR kommt 
ja bei jedem Zeichen, dass empfangen wird. Du musst also in der ISR gar 
kein Polling verwenden sondern nur eben das empfangene Zeichen aus dem 
Empfangsspeicher in einen Puffer schreiben (das dauert 1-2 Zyklen) und 
die ISR beenden und zack musst du warten bis das CD-Laufwerk dir das 
nächste Zeichen überhaupt sendet (ich geh mal davon aus, dass dein 
Atmega nicht nur mit 128 kHz Taktrate läuft sondern eher so mit 8 MHz 
oder schneller).

Während du dann also quasi auf das nächste Byte wartest kannst du prüfen 
ob das letzte empfangene Byte ein Start- oder Endbyte war. Das sollte 
aber schon das Protokoll machen. Ist halt wie bei RS232. Da definiert 
man ja auch, dass man z.B. 1 Start-Bit und 2 Stopp-Bits hat aber damit 
hat es sich dann auch, man schaut dann nicht mehr nach ob man das 
Startbit oder ein Stoppbit empfangen hat. Das macht der µC dann alleine.

von Frank (Gast)


Lesenswert?

Also nochmal: Du musst das Protokoll implementieren. In den Unterlagen 
steht ganz bestimmt, wie das Laufwerk seine Antworten formatiert. 
Entweder steht irgendwo in der Antwort (meist am Anfang), wie viele 
Bytes jetzt kommen werden oder es gibt ein dezitirtes Endekennzeichen 
... evtl. auch Beides und möglicherweise eine Prüfsumme.

Nachdem das erste Byte hereingekommen ist, musst du es in einen Puffer 
"wegschaffen", ein par Zeilen Auswertung ausführen und einen Zähler 
hochzählen, damit du weisst, wieviel schon da ist ... dann auf das 
nächste Byte warten. Pause ist immer, es gibt garantiert mind. 1 
Stopbit.

Wenn du das Protokoll des Laufwerkes kennst, weisst du wann die 
Mengenangabe kommt - auf die must du warten und entsprechend reagieren - 
das meine ich mit "einige Zeilen Code ausführen" ...

von Bernd K. (prof7bit)


Lesenswert?

Michael Köhler schrieb:

> nur eben das empfangene Zeichen aus dem
> Empfangsspeicher in einen Puffer schreiben (das dauert 1-2 Zyklen)

Naja.

2 Zyklen UDR0 lesen,
4 Zyklen Zeiger lesen,
2 Zyklen byte speichern,
2 Zyklen Zeiger inkrementieren,
4 Zyklen auf Überlauf testen
4 Zyklen Zeiger speichern

Dann noch ~30..40 Zyklen Interrupt + Prolog + Epilog.

Aber ja. Die Antwort ist dennoch richtig. Jedes Zeichen löst einen 
Interrupt aus und die Interrupt-Routine soll jedes einzelne Byte so 
schnell wie möglich behandeln (speichern, vielleicht noch Ende des 
Pakets erkennen falls möglich und globales flag setzen). Der rest 
passiert in der main() wo alle Zeit der Welt ist.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Bernd K. schrieb:
> Dann noch ~30..40 Zyklen Interrupt + Prolog + Epilog.
Also überschlagen etwa 100 Zyklen, das sind bei 10MHz dann gerade mal 
10us. Kein Grund, dafür dann 80us (und damit gut 85% der Rechenleistung) 
mit Polling  zu verplempern...

von Sebastian W. (wangnick)


Lesenswert?

Andreas Geissler schrieb:
> Das erste empfangene bit ist OK, nur die folgenden bekomme ich nicht
> mit.
> Die Idee war: auf das erste bit mit einem Interrupt reagieren und die
> weiteren dann durch polling einlesen und in einem String abspeichern.
> Zwischen den einzelnen Byte ist keine Pause.
>
> Mein Code bis jetzt hab ich angehängt.
>
> Sieht einer von euch den Fehler? Oder geht das so garnicht?

Hallo Andreas,

du machst in deinem Programm einen grundsätzlichen Fehler. Du 
programmierst so:
1
while (!(UCSR0A & (1<<RXC0))) {
2
    rc_array[i] = UDR0;
3
    PORTA ^= (1<<PA5);
4
}
5
i++;
Das bedeutet aber nichts anderes als dass du UDR0 ausliest solange da 
noch nichts drin angekommen ist. Sobald etwas angekommen ist liest du 
UDR0 nicht mehr und verlässt die Schleife.

Der richtige Weg ist im Datenblatt beschrieben und besteht aus einem 
sehr leicht zu übersehenden Semikolon:
1
while (!(UCSR0A & (1<<RXC0))) ;
2
rc_array[i] = UDR0;
3
PORTA ^= (1<<PA5);
4
i++;

Ich hasse diese Notation und benutze in solchen Fällen daher immer 
folgendes Konstrukt:
1
while (!(UCSR0A & (1<<RXC0))) continue;
2
rc_array[i] = UDR0;
3
PORTA ^= (1<<PA5);
4
i++;

Das Schlüsselwort continue ist zwar unnötig, führt aber dazu dass man 
das Semikolon nicht überliest!

LG, Sebastian

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.