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
> 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.
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 ';'
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.
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.
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?
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.