Hallo zusamen,
ich habe eine Frage zum UART eine NXP Controllers; KEA128.
Den UART habe ich soweit am Laufen. Ich kann Zeichen senden und
empfangen, ich kann Strings senden.
Beim Empfangen von Strings habe ich allerdings ein Problem.
Der Controller empfängt einen String, vom PC aus gesendet, sendet diesen
dann auch wieder an den PC, aber
bleibt dann immer an einer Stelle hängen; in der Funktion "rx_char".
Hier an der Stelle "while(!(p->S1&UART_S1_RDRF_MASK));".
Ich kann dann weitere Strings an den Controller senden und er sendet
diese als Echo zurück an den PC, aber der Controller bleibt eben immer
an der oben genennten Stelle endlos hängen.
Kann mir da vielleicht jemand auf die Sprünge helfen?
Folgend mal die entsprechenden Funktionen:
Marco schrieb:> Hier an der Stelle "while(!(p->S1&UART_S1_RDRF_MASK));".> Kann mir da vielleicht jemand auf die Sprünge helfen?
Er wartet in der while Schleife auf den Empfang eines Zeichens.
Es ist allgemein eine schlechte Idee in einer Interrupt-Routine auf
etwas zu warten. Übergib die empfangenen Daten an das Hauptprogramm und
sende dort das Echo.
Marco schrieb:> void UART2_IRQHandler( void )
Das ist anscheinend eine ISR, die läuft parallel zu der while-Schleife.
Er sendet echo-Strings, hängt ansonsten aber in der while-Schleife fest
Ich würde in der Empfangs ISR lediglich einen Fifo befüllen und
im Hauptprogramm abfragen, ob etwas im Fifo ist. Wenn ja aus dem
Fifo auslesen und echo.
Der Fifo hat den Vorteil, dass er nicht mit der ISR synchronisiert
werden
muss, solange es nur einen Leser und nur einen Schreiber gibt.
Hallo zusammen,
vielen Dank für die Antworten.
Ich werde dann wohl mal versuchen das Ganze so umzubauen, dass die Daten
in der ISR nur ausgelesen werden und der Rest in der Hauptschleife
passiert.
Ich frage mich nur, warum der scheinbar selbst nach dem Empfang des
letzten Zeichens noch einmal in die ISR springt. Eigentlich sollte diese
und somit der Funktionsaufruf rx_echo_string( UART2 ); doch nur kommen,
wenn ein Zeichen empfangen wurde. Da aber der komplette String zurück an
den PC gesendet und keine weiteren Zeichen an den Controller gesendet
wurden, dürfte der doch eigentlich gar nicht mehr da landen und dann
natürlich auf ein Zeichen warten, weil ja auch keines gesendet wurde.
Gruß
Marco
Marco schrieb:> Da aber der komplette String zurück an den PC gesendet und keine> weiteren Zeichen an den Controller gesendet wurden
Bist du dir da sicher? Wird in deinem Fall das Zeichen '\0' wirklich an
den Controller geschickt?
Hallo,
ich habe mir mal die Zeichen direkt nach dem Empfang ausgeben lassen.
Das \0 wird an den Controller gesendet.
Erst nach dem Empfang wird der String ja wieder zurück an den PC
gesendet. Also sollte \0 erkannt werden.
Gruß
Marco
Bei meinen Programmen ist der USART_RX Handler so aufgebaut, daß er die
empfangenen Zeichen in einen Puffer schreibt und auf eine
String-Ende-Markierung (z.B. CR/LF oder NUL) ein Flag setzt, daß ein
vollständiger String empfangen wurde. Gleichzeitig wird auf einen
zweiten Puffer gewechselt, damit dort der nächste empfangene String
abgelegt werden kann, während sich das Hauptprogramm mit dem ersten
beschäftigt.
Das aktuelle Projekt (wobei dieser Teil schon so gut wie fertig ist),
ist ein Terminal für eine Alarmanlage. Da werkelt ein ATMega88, der
kümmert sich um das Tastenfeld und die LCD-Ausgabe. Dafür hat der 5x24
Zeichen Puffer (worst case 2x20 Zeichen fürs LCD mit Steuerzeichen und
drei Kurzkommandos).
Hallo,
ein Flag zu setzen und dann daraufhin in der Hauptschleife den
empfangenen String auszuwerten hatte ich mir hier nach den ganzen
Antworten hier auch schon mal skizziert.
Das mit dem zweiten Buffer, damit der erste in Ruhe ausgewertet werden
kann und neue Daten nicht verloren gehen hatte ich noch nicht dran
gedacht. Werde ich aber aifnehmen. Macht Sinn.
Vielen Dank!
Stellst du evtl. eine Teilfunktion mal zur Verfügung?
Ich weiß, selber von grundauf zu machen ist immer besser ;)
Vielen Dank!
Gruß
Marco
Würde Dir leider nicht viel nutzen, alles in AVR-Assembler.
Aber so schwer ist es nicht.
Die ISR merkt sich einfach welcher Puffer gerade der aktuelle ist und an
welche Stelle im Puffer zuletzt geschrieben wurde. Wird ein Zeichen
empfangen, wird es in den Puffer geschrieben und geprüft ob es eine
Ende-Markierung ist. Wenn ja, wird das "fertig-Flag" für diesen Puffer
gesetzt, der aktuelle Puffer wird auf den nächsten (0..4) gesetzt und
die gemerkte Schreibposition auf Null zurückgesetzt. Dazu noch ein
kleiner Schutz, daß der Puffer nicht überlaufen wird wenn mal mehr
Zeichen ankommen als eine Zeile haben darf (wenn der Puffer voll ist
werden alle Zeichen bis zur nächsten Ende-Markierung verworfen), mehr
macht die ISR nicht.
Das Hauptprogramm fragt fortlaufend alle 5 Puffer ab und führt quasi die
Befehle aus, die in den als fertig markierten Puffern drinstehen. Wenn
ein Befehl ausgeführt wurde, wird das fertig-Flag wieder gelöscht und
der Spaß kann von vorne beginnen.
Probleme (Datenverlust) gibt es erst wenn das Hauptprogramm es nicht
schafft, den ersten Befehl abzuarbeiten bis die restlichen 4 Puffer
gefüllt sind. In dem Fall würde der gerade ausgeführte Befehl
überschrieben werden und beide Routinen (die ISR und das Hauptprogramm)
kommen sich mit den Flags in die Quere. Wenn man das verhindern will,
muß die ISR noch prüfen, ob der neu zu verwendete Puffer als frei
markiert ist. In dem Fall würde der neu ankommende Befehl ins Leere
laufen weil die ISR ihn nirgendwo speichern kann. Vielleicht ändere ich
das bei mir nochmal, weils die elegantere Variante ist.
Marco schrieb:> Den UART habe ich soweit am Laufen. Ich kann Zeichen senden und> empfangen, ich kann Strings senden.> Beim Empfangen von Strings habe ich allerdings ein Problem.
Du bist völlig unsystematisch an die Sache herangegangen. Und dein
geposteter Code danach aus, nämlich wirr.
Beispiel:
void tx_char(Ptr p, uint8_t send)
Das ist Mumpitz, denn ein zu sendendes Zeichen ist ein char und keine
wie auch immer geartete Integer-Zahl!
Zuerst mal ne Klarstellung: Ein UART kennt keine Strings und auch der
Transport von Zeichen auf einer seriellen Strippe kennt keine Strings.
Folglich mußt du dir einen UART-Treiber schreiben, der Einzelzeichen
entgegennimmt und auch Einzelzeichen liefert und der alle Zeichen
zwischenpuffert, um die eigentliche Hardware-Schnittstelle von seinem
Software-Interface zu entkoppeln. Das Software-Interface sollte etwa so
ähnlich aussehen:
1
externcharV24Char_Out(charc);/* Zeichen senden */
2
externintV24numTxFree(void);/* Anzahl freier Plätze im Sendepuffer */
3
externboolV24RxAvail(void);/* ob Zeichen empfangen wurden */
externvoidV24TxDone(void);/* warten bis Sendepuffer geleert ist */
Und auf diesem Interface dürfen dann alle anderen Funktionen aufbauen,
also Senden von Strings oder Ausgabe von Zahlen im Klartext und so
weiter.
Der Interrupt dient IMMER nur zum Bedienen der Hardware und NICHT zu
irgendwelchen programminternen Dingen wie z.B. der Ende-Erkennung von
Strings und dergleichen.
So. Es sind 2 Ringpuffer nötig, dazu jeweils zwei Indizes pro Puffer
(einer zum Befüllen, der andere zum Entnehmen). V24Char_Out(..) befüllt
den Sendepuffer und veranlaßt die Freigabe des Interrupts für
UART-Sendepuffer-Leer. Diese Funktion benutzt NUR den Befüll-Index und
greift auf den Entnehmen-Index nur LESEND zu. Der Sende-Teil des
Interrupt-Handlers benutzt NUR den Entnehmen-Index und greift auf den
Befüllen-Index nur LESEND zu, um herauszukriegen, wann der Puffer leer
ist.
Die Empfangsseite geht sinngemäß.
So herum macht man das.
W.S.
Marco schrieb:> Das mit dem zweiten Buffer, damit der erste in Ruhe ausgewertet werden> kann und neue Daten nicht verloren gehen hatte ich noch nicht dran> gedacht. Werde ich aber aifnehmen. Macht Sinn.> Vielen Dank!
Dein Controller hat ja mit bis zu 16KByte RAM genug speicher. Es gäbe
auch die Möglichkeit das Senden und Empfangen von Daten komplett in eine
ISR mit einem größeren Ringpuffer zu erledigen.
Damit kannst du dir ganz einfach in deiner Hauptschleife die Empfangenen
Daten einsammeln und verarbeiten.
Habe dir mal ein Beispiel rauskopiert aus einer Schnitstelle die mit
einem ESP kommuniziert. Der Code unten läuft so auf einem STM32F103,
hatte den Code damals selber aus einem Beispiel kopiert ...
Header File:
1
#define ESP_TX_BUFFER_SIZE 256
2
#define ESP_RX_BUFFER_SIZE 256
3
4
#if ESP_TX_BUFFER_SIZE < 2
5
#error ESP_TX_BUFFER_SIZE is too small, It must be larger then 1.
Bei Strings ueber ein UART sollte man sich von der Idee der
nullterminierten Strings verabschieden. Und statt dessen die Laenge des
Strings zuerst senden. Das hat den Vorteil, dass man abzaehlen kann.
Jetzt ist G. schrieb:> Das hat den Vorteil...
Das geht den eigentlichen Handler überhaupt nichts an. Der nimmt Zeichen
an und sendet sie. Ob da welche zu irgend einem String oder zu sonstwas
gehören, ist ihm schnurz.
W.S.
Marco schrieb:> bleibt dann immer an einer Stelle hängen; in der Funktion "rx_char".> Ich kann dann weitere Strings an den Controller senden und er sendet> diese als Echo zurück an den PC
Wie meinst du das, er sendet weiterhin Echos an den PC zurück während er
hängt?
Hallo,
Stefanus F. schrieb:> Wie meinst du das, er sendet weiterhin Echos an den PC zurück während er> hängt?
Damit ist gemeint, dass nicht der Controller an sich hängen bleibt. Wenn
er in diesem Zustand verharrt oder eben in der While Schleife hängen
bleibt, kann man weiterhin Nachrichten vom PC an den Controller senden,
welche auch empfangen und wie gewünscht als Echo an den PC zurück
gesendet werden. Nur die Funktionen der Hauptschleife werden nicht mehr
durchlaufen.
Gruß
Marco
Marco schrieb:> Nur die Funktionen der Hauptschleife werden nicht mehr durchlaufen.> Hier an der Stelle "while(!(p->S1&UART_S1_RDRF_MASK));".> Ich kann dann weitere Strings an den Controller senden und er sendet> diese als Echo zurück an den PC, aber der Controller bleibt eben immer> an der oben genennten Stelle endlos hängen.
Diese Zeile befindet sich jedoch in der Funktion tx_char() welche von
der Empfangs-Interruptroutine aufgerufen wird, um das Echo zu senden.
Also kann die Funktion tx_char() nicht grundsätzlich falsch sein.
Was mir allerdings aufgefallen ist:
Der Receive-Interrupt wird vermutlich für jedes empfangene Zeichen
einzeln aufgerufen. Du empfängst darin aber nicht nur ein einzelnes
Zeichen, sondern wartest auf einen kompletten String! Während dieser
Warteschleife wird die Hauptschleife nicht ausgeführt (ist ja kein
Dual-Core Prozessor).
Ich denke, dort liegt irgendwo der Hund begraben.