Hallo, ich möchte eine UART Verbindung zwischen einem ATmega88 und einem RaspberryPi aufbauen. Ich hab es bisher geschafft, dass von beiden Seiten aus gesendet und empfangen werden kann, und das auch die richtigen Nachrichten ankommen. Ein Problem besteht leider noch, welches ich bisher nicht lösen konnte: Wenn ich vom ATmega88 mit fprintf("0123456789") einen String schicke, und im Raspberry diesen mit der read() Funktion "empfange" und in einen Buffer speichere, welcher 256 Zeichen fassen kann und am Ende diesen Buffer auf dem Terminal ausgebe, dann werden lediglich die ersten 8 Byte ausgegeben, bei einem zweiten Schleifendurchlauf die restlichen 2 Byte. Warum werden nur die ersten 8 Byte in den Buffer geschrieben? Gibt es dafür ein Flag, welchen ich falsch gesetzt haben könnte? Ich bin nach folgendem Beispiel vorgegangen: http://www.raspberry-projects.com/pi/programming-in-c/uart-serial-port/using-the-uart
:
Verschoben durch Moderator
Weiß niemand eine Antwort? Oder ist meine Frage unglücklich formuliert?
> Weiß niemand eine Antwort?
man 2 read weiss es latürnich:
1 | [..] On success, the number of bytes read is returned (zero indicates end |
2 | of file) [..]. It is not an error if this number is smaller than the |
3 | number of bytes requested; [..] |
Aber wieso ist das überhaupt relevant? Du darfst Dich eh unter keinen Umständen darauf verlassen, dass das Laufzeitverhalten immer gleich(tm) ist. Das übertragene Datum musst Du deswegen immer zusammensetzen (können).
g457 schrieb: > man 2 read weiss es latürnich: >[..] On success, the number of bytes read is returned (zero indicates end > of file) [..]. It is not an error if this number is smaller than the > number of bytes requested; [..] > > Aber wieso ist das überhaupt relevant? Du darfst Dich eh unter keinen > Umständen darauf verlassen, dass das Laufzeitverhalten immer gleich(tm) > ist. Das übertragene Datum musst Du deswegen immer zusammensetzen > (können). Versteh nicht was du mir mit dieser Antwort sagen möchtest!?
Gäst_5 schrieb: > Versteh nicht was du mir mit dieser Antwort sagen möchtest!? Dass du nicht erwarten darfst, dass du 10 hintereinander gesendete Bytes auch in einem einzigen Aufruf an read() zusammen zurück bekommst. Es ist völlig legal, dass (aus was auch immer für Gründen) zuerst nur die ersten 8 und im nächsten read() noch weitere zwei Bytes geliefert werden. Daher implementieren serielle Protokolle immer eine am Anfang versendete Paketlänge. Damit weiß der Empfänger, wie viele weitere Bytes zu erwarten sind.
Aber ich gebe doch der read() Funktion die Länge, bzw die Bytezahl mit.
Gäst_5 schrieb: > Aber ich gebe doch der read() Funktion die Länge, bzw die Bytezahl mit. Die wird aber nicht zum Empfänger übertragen. Die steuert nur, wieviele Zeichen read auf die Schnittstelle ausgibt. Davon weiß aber der Empfänger nichts. Der sieht ja nur die Zeichen, die tatsächlich übertragen werden. Entweder du teilst dem Empfänger im voraus mit, wieviele Bytes er zu erwarten hat, oder der Sender einigt sich mit dem Empfänger auf ein 'Jetzt ist alles fertig'-Zeichen, an dem er erkennen kann, wann zb eine Zahl vollständig übertragen wurde (in dem Sinn, dass jetzt alle Zeichen eingetrudelt sind, die die Zahl bilden sollen). Zb einen Zeilenvorschub '\n' oder einen ';'
Die seriellen Schnittstellen haben einen begrenzten Empfangsbuffer/FIFO. Deiner ist vermutlich 8 Bytes groß.
ich bin nun wie folgt vorgegangen: ATmega88 sendet einmalig: fprintf(f, "Test_Test_Test"); RaspberryPi: while (1) { uart_read(); } //********** UART Empfangen ********** void uart_read (void) { if (uart0_filestream != -1) { unsigned char rx_buffer[256]; int rx_length = read(uart0_filestream, (void*)rx_buffer, 255); if (rx_length < 0) { //printf("Error\n"); } else if (rx_length == 0) { //printf("No data waiting.\n"); } else { rx_buffer[rx_length] = '\0'; printf("UART: %i bytes - read : %s \n", rx_length, rx_buffer); printf("loop %i\n", x); x++; } } return; } Auf der Konsole sieht es folgendermaßen aus: UART: 8 bytes - read : Test_Tes UART: 6 bytes - read : t_Test Was sagt mir das? Ich sende einmalig "Test_Test_Test" welches übertragen wird und im AMA0 (FIFO) abgelegt ist. Wenn dieser wirklich nur 8 Byte fassen kann, warum werden die restlichen 6 Byte anständig übertragen und angezeigt? Mir ist klar das der RaspPi viel viel schneller läuft, als der ATmega und vorallem als die eingestellte Baudrate. Die Funktion uart_read() wird dann wahrscheinlich 10 mal aufgerufen, bevor ein Zeichen geschickt wurde. Warum werden dann aber immer 8 Byte "gesammelt" ?
Gäst_5 schrieb: > Was sagt mir das? > Ich sende einmalig "Test_Test_Test" welches übertragen wird Ja. Aber die Übertragung BRAUCHT ZEIT! Das ist ja nicht so, dass du den fprintf aufrufst und quasi instantan, in 0-Zeit, ist der komplette String zur Gegenstelle übertragen worden. Den Mega beginnt zu senden T e s t irgendwann, zu irgendeinem Zeitpunkt beginnt der Rasperry nachzusehen, ob auf der Schnittstelle was da ist. Ja! Da ist was da! Nämlich "Test" und das kriegst du (bei dir eben 8 Zeichen). Warum nur diese Zeichen? Weil der Rest vom Mega noch gar nicht weggeschickt worden ist! Der Mega ist noch damit beschäftigt, die letzten Zeichen auf den Weg zu bringen, währen der Raspberry schon damit anfängt, die bereits empfangenen Zeichen aus dem Buffer zu holen. Gewöhn dich daran, was asynchrone Verarbeitung bedeutet! Die beiden Rechner sind unabhängig voneinander! Die warten nicht aufeinander. Es gibt auf der UART keinen Mechanismus, der dafür sorgt, dass hier synchronisiert wird. Der eine sendet Zeichen, der andere holt sich Zeichen. Der der sich die Zeichen von seiner Schnittstelle holt, der kriegt dann eben den Teil, der bereits eingetroffen ist. Selbst wenn der Sender noch gar nicht alles vollständig weggeschickt hat. > bevor ein Zeichen geschickt wurde. Warum werden dann aber immer 8 Byte > "gesammelt" ? Weil irgendein Mechanismus am Pi zb bei 8 Zeichen im Buffer sich einklinkt und die Verarbeitung anstösst. Eine enstprechende Analyse, was da im Detail ganz genau abläuft, ist aufwändig. Du kannst sie ja führen, wenn du willst. Das spielen dann auch noch andere Designentscheidungen mit rein. Zb, eine Steuerung im read, welches zb in ein Timeout reinläuft oder auch nicht, weil nach einigen Zeichen in einer gewissen Zeitspanne kein Zeichen an der Schnittstelle eingetroffen ist und read() nicht länger warten will. Aber du musst endlich weg von der Vorstellung, dass du am Mega einen fprintf machst, und der ganze Text wandert in einem Rutsch ohne Unterbrechung zum Pi und kommt dort auch als solcher wieder in einem Stück an. DEM IST NICHT SO!
Gäst_5 schrieb: > Aber ich gebe doch der read() Funktion die Länge, bzw die Bytezahl mit. Die wird ausschließlich als Maximum betrachtet, wobei man über tcsetattr() einstellen kann, wie lange ggf. gewartet werden soll um zu sehen, ob noch Zeichen verfügbar sind.
hm... wie macht man denn sowas am besten? Pi sendet zum ATmega, dieser empfängt und soll Antworten, bis das geschehen ist, hat der Pi doch schon 10 mal neu gesendet, wenn alles in einer Schleife abgearbeitet wird. Ich müsste also irgendwo warten, bis der ATmega antwortet, und dann auch noch wissen wie viel er senden will. Und zu guter letzt auch noch die Nachricht zusammensetzen, wenn es mehr als 8 Byte sind. Wie kann ich das am besten realisieren? Könnte man beim Pi nicht eine Art ISR anstoßen, sobald er über den UART was empfängt?
Gäst_5 schrieb: > hm... wie macht man denn sowas am besten? > > Pi sendet zum ATmega, dieser empfängt und soll Antworten, bis das > geschehen ist, hat der Pi doch schon 10 mal neu gesendet, wenn alles in > einer Schleife abgearbeitet wird. > > Ich müsste also irgendwo warten, bis der ATmega antwortet, und dann auch > noch wissen wie viel er senden will. > Und zu guter letzt auch noch die Nachricht zusammensetzen, wenn es mehr > als 8 Byte sind. Ja, sowas nennt man "Protokoll". Eine Vorschrift, wer wann was sendet. Denk dir was aus. Zumindest für letzteres (die Sache mit den 8 Byte) haben wir uns ja mitlerweile ohnehin schon den Mund fusselig geredet :-)
Danke erstmal für die Hilfe :-)
> Denk dir was aus.
Es gibt doch bestimmt eine allg. Herangehensweise, oder?
Würde gern mal eine einfaches, verständliches Tutorial dazu lesen,
leider habe ich bisher nichts passendes gefunden...
Vor allem wie es beim Pi gemacht wird.
Kann ich denn den UART abfragen, ob er fertig ist und alle Zeichen gesendet hat?
Gäst_5 schrieb: > Kann ich denn den UART abfragen, ob er fertig ist und alle Zeichen > gesendet hat? Wozu? Was bringt dir das? Die Sende-Funktion kommt aus dem Aufruf zurück, wenn sie alle Zeichen an der UART abgeliefert hat. Reicht doch. zunächst mal brauchst du eine Vereinbarung mit dir selber. Da bis jetzt nur von Texten die Rede war, die ausgetauscht werden, bleibe ich mal dabei. Die Vereinbarung lautet: Jeder Text wird an der UART-Schnittstelle mit einem # abgeschlossen. Am # kann die Gegenstelle erkennen, dass sie jetzt alle Zeichen des zu empfangenden Strings hat. Ein #-Zeichen darf im Text nicht vorkommen. Das ist deine erste Zutat zum Protokoll: Eine Vereinbarung, mit der der Empfänger zweifelsfrei feststellen kann, wann eine Nachricht aufhört. Und der Rest ist jetzt einfach nur eine Vereinbarung wer welchen String zum jeweiligen Kommunikationspartner schickt um welche Aktion zu veranlassen. Die Gegenstelle muss zb die Ausführung des Befehls quittieren. zb. kannst du vereinbaren, dass der Mega die Befehle versteht SET p b# mit der Bedeutung: am Port, der mittels p angegeben wurde, das Bit b zu setzen. Will der Pi also, dass der Mega am PORTB das Bit 6 auf 1 setzt, dann überträgt er den Befehl SET B 6# zu Mega. Der führt die Aktion aus und Antwortet zb mit einem OK# Will der Pi wissen, welchen Zustand gerade der Pin 4 vom Port D hat, dann sendet er zb an den Mega die Anfrage ? D 4# der Mega erhält diesen Befehl, liest den Pin aus und antwortet mit 1 OK# Der Pi kann auch versuchen, vom Mega den Zustand des Pins 5 am Port J zu erfragen. Kann er - geht aber nicht. Der Mega hat diesen Port nicht. Der Pi schickt also die Anfrage ? J 5# und der Mega antwortet mit ERROR# Die Gesamtheit all dieser Vereinbarungen nennt man ein Protokoll. Es beginnt damit, dass man zunächst mal definiert, wie man aus einem Zeichenstrom eine 'Nachricht' eindeutig identifizieren kann und geht dann weiter damit, dass man definiert wer wem was als eine derartige Nachricht schickt bzw. was der so angesprochene antwortet. So ... und jetzt denk dir was aus, was für deine Anwendung vernünftig ist. Du darfst ruhig kreativ sein. Schreib das alles zusammen - gib deinem Kumpel eine Kopie deiner Mitschrift und setz ihn ins Nebenzimmer. Und dann rufst du ihm Buchstaben zu. Einen nach dem anderen. Wenn dein Protokoll was taugt, dann kann dein Kumpel mit der Protokollbeschreibung auf dem Zettel genau das ausführen, was du (im Rahmen deiner Anwendung) von dem Zusammenspiel aus Pi und Mega erwartest. Wenn er bei dir mündlich nachfragen muss, wie etwas gemeint ist, dann hast du einen weißen Fleck in deiner Protokollbeschreibung entdeckt. Und ja. Du hast in deinem Leben schon hunderttausendmal Protokolle befolgt. Zb war ein Protokoll der Lehrer stellt eine Frage weißt du die Antwort? Wenn nein, dann machst du nichts Wenn ja, dann geht dein Arm mit ausgestrecktem Zeigefinger hoch Der Lehrer wählt dich aus Du stehst auf Du nennst die Antwort Der Lehrer bewertet die Antwort Du setzt dich Auch das ist nichts anderes als ein Protokoll. Ein Prozedere, was in welcher Reihenfolge zu erfolgen hat.
ich verstehe noch nicht ganz wie ich vorgehen muss: Raspberry sendet etwas auf den UART ...Zeit vergeht bis Nachricht vollständig übermittelt wurde... ATmega bekommt Nachricht und wertet diese aus ATmega sendet Antwort auf UART ...Zeit vergeht bis Nachricht vollständig übermittelt wurde... Und erst jetzt sollte doch der Raspberry den UART lesen Also müsste ich doch beim Raspberry irgendein RX Flag abfragen können, was mir signalisiert das der UART etwas empfangen hat... Oder wie macht man das? Sorry, hab bisher nur mit µC gearbeitet...
Gäst_5 schrieb: > Also müsste ich doch beim Raspberry irgendein RX Flag abfragen können, > was mir signalisiert das der UART etwas empfangen hat... Zb. Uart Filestreams haben etwas das man ein Timeout nennt. Du versuchst einfach auf gut Glück mittels read etwas zu lesen. Ist was da, dann kriegst du es. Ist nichts da, dann wartet read ein bischen (aber nicht zu lange) und meldet dann: nichts da.
> Sorry, hab bisher nur mit µC gearbeitet...
Dann such dir ein Tutorial, welches dir beibringt, wie man auf Linux mit
einer UART umgeht.
Danke erstmal für die Mühe und die ausführliche Antwort. Karl Heinz Buchegger schrieb: > Dann such dir ein Tutorial, welches dir beibringt, wie man auf Linux mit > einer UART umgeht. Gäst_5 schrieb: > Würde gern mal eine einfaches, verständliches Tutorial dazu lesen, > leider habe ich bisher nichts passendes gefunden... ... Karl Heinz Buchegger schrieb: > Jeder Text wird an der UART-Schnittstelle mit > einem # abgeschlossen. Am # kann die Gegenstelle erkennen, dass sie > jetzt alle Zeichen des zu empfangenden Strings hat. Ein #-Zeichen darf > im Text nicht vorkommen. Das versuche ich grad umzusetzen. Leider hänge ich schon wieder an einer ganz dummen stelle Fest. Ich möchte mit: int length = strchr(rx_buffer, '#'); Nach deinem vorgeschlagenen Zeichen im String suchen, leider sagt der Compiler: Fehler: call of overloaded 'strchr(unsigned char [32], char)' is ambiguous Ich hab nun so viel probiert und gesucht, finde keine Lösung dafür?!
Der Prototyp für diese Funktion ist nicht unsigned char sondern char und als character erwartet er einen int. Der character könnte er konvertieren, aber zwischen unsigned char und char kann er nicht implizit konvertieren, das musst du explizit machen, oder gleich deinen Datentyp als char anlegen. Prototyp: char * strchr(char * str, int character);
...und jetzt nochmal für Anfänger? > Der Prototyp für diese Funktion ist nicht unsigned char sondern char An welcher Stelle hab ich denn festgelegt das es unsigned char ist? > und als character erwartet er einen int Warum? > Prototyp: > char * strchr(char * str, int character); Wie würde dies auf meinen Fall angewandt ausschauen?
Ich würde vorschlagen, dass du dich zuerst etwas intensiver mit C beschäftigst und danach mit POSIX/UNIX I/O.
Name schrieb: > An welcher Stelle hab ich denn festgelegt das es unsigned char ist? Offensichtlich bei der Definition von rx_buffer[32], denn genau das sagt dir bereits der Compiler. Übrigens lohnt es sich manchmal dazuzuschreiben, dass man nicht C, sondern C++ benutzt. Nicht alles ist zwischen beiden völlig gleich.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.