Hallo,
ich bin relativ neu bei der Programmierung von Atmel Controllern und hab
ein kleines Verständnisproblem mit dem Umgang des UART. (Ja hab im Forum
gesucht und auch das Tutorial angeschaut)
Ich hab an meinem Controller vier Sensoren angeschlossen, von denen ich
die Seriennummer, Justierungsdaten, und dann die aktuellen Sensordaten
auslesen will. Zudem will ich die gemessenen Daten an einen PC schicken.
Hab soweit auch alles am laufen (messen, auswerten und verschicken der
Daten).
Jetzt ist es so, dass z.B die Seriennummer 24 Bit lang ist. Wenn ich die
Daten über UART auf den PC schicke sende ich 7 Zahlen, die dann als
ASCII-Zeichen gesendet werden (oder nur vom Hyperterminal so angezeigt
werden??) und ich somit 7 Byte statt 3 Byte (wenn ich 24 Bit binär
übertrage) benötige. Prinzipiell kein Problem, aber wenn ich sekündlich
SN(24Bit) und die Messwerte (2* 16Bit) bei vier Sensoren übertragen
will, dann summiert sich das ganz schön....
Jetzt meine Frage:
Zeigt mir nur der Terminal automatisch Bytes an oder sendet der µC über
den Uart ASCII-Zeichen? Denn eigentlich wird ja über uart auch nur binär
gesendet....
Ich habe auch schon versucht die Daten binär umzuwandeln und zu senden,
was aber irgendwie auch nicht so sinnvoll war, weil ich dann die Nullen
und Einsen einzeln als ASCII-Zeichen gesendet hab....^^
Sorry, aber ich komm einfach nicht drauf....
Schon mal vielen Dank für eure Antworten
Gruß
Perni
Wenn dir das Hyperterminal Klartext anzeigt, dann sendest du die Daten
schon fertig von binär in einen ASCII-String umgewandelt über den USART
raus.
Wie sieht das in deinem Programm aus? Stell mal die Senderoutine und
deren Aufruf hier rein, dann können wir dir erklären, wo das passiert.
An genau der Stelle müsstest du es dann auch ändern, wenn du die Daten
binär rausschicken möchtest.
Du brauchst dann allerdings ein passendes Anzeigeprogramm auf dem PC,
Hyperterminal wird die Binärdaten nicht für dich interpretieren und
umwandeln.
Erstmal danke für die schnelle Antwort!
Vorab: Im Moment gebe ich die Daten absichtlich in float und hex aus,
damit ich es eben besser lesen kann....
Die Daten - Rohdaten und nicht die berechneten die ich im Moment ausgebe
- sollen aber als Binärzahl am PC ausgegeben und dort erneut berechnet
werden. (Protokoll hab ich mir schon ausgedacht bzw. erstellt)
Wie gesagt, dass ich die Werte als Hex bzw. Gleitkommazahl ausgebe ist
mir klar, ich kenn aber leider keinen Operator, der mir das als
Binärzahl ausgibt....
Eine Umwandlung wie
1
for(i=(sizeof*8)-1;....
brachte mir wie oben erwähnt auch nur Nullen und Einsen die in ASCII
angezeigt wurden....
Danke!!
Gruß
Michael
printf ist, wie der Name schon sagt, für die formatierte Ausgabe von
Daten zuständig. printf selbst baut im Speicher einen String anhand
deiner Angaben zusammen und benutzt dann eine Low-Level-Funktion, die
einzelne Zeichen auf dem gewünschten Gerät ausgibt.
Du greifst also eine Ebene zu hoch an. Benutze einfach die Funktion, die
einzelne Zeichen auf den USART schreibt direkt - anstelle von printf.
Wenn du nicht weißt, wie die heißt, weil du sie nicht selbst geschrieben
hast, musst du in der Doku der verwendeten Lib nachsehen.
Irgendetwas in der Art von usart_putc(unsigned char c) gibt einzelne
Bytes aus. Bei floats oder ints, die breiter als 8 Bit sind, schreibst
du dir entweder nach dem vorhandenen Muster eine eigene Ausgaberoutine,
oder baust eine Schleife mit sizeof und benutzt die Variable als Pointer
auf das erste Byte. Dann kannst du die einzelnen Bytes in beliebiger
Reihenfolge über die serielle Schnittstelle ausgeben.
Vielen Dank für den Hinweis!
Da fällt mir ein, dass ich sowas schon mal definiert gesehen habe - es
aber nicht verwendet und somit auch nicht weiter verfolgt habe....
Also, ich hab mir da jetzt mal aus dem vorherigen Bastler-Projekt (das
ich nicht selbst erstellt habe sondern fast fertig übernommen und nur
weiter "programmiert" habe) nachgeschaut und da war eben jenes "putchar"
schon definiert.
Jetzt habe ich das ganze in mein Projekt eingefügt (es funktioniert
auch) und jetzt sieht meine Ausgabe wie folgt aus:
Wie man daraus schon erkennen kann, habe ich die Seriennummer jetzt auf
Bytes aufgeteilt, allerdings wohl nicht sehr elegant:
1
Serial_Bytewise[i][0]=Serial[i];
2
Serial_Bytewise[i][1]=Serial[i]>>8;
3
Serial_Bytewise[i][2]=Serial[i]>>16;
Das gleiche droht mir jetzt bei den anderen Werten (12 & 14 Bit lang)
auch. Fällt da jemandem eine elegantere Lösung ein??
Nochmal vielen Dank an dich Oliver!!
Deine Beiträge waren mir sehr hilfreich!!
Perni schrieb:> // Irgendwo im Code> for(int w=0; w < 3; w++)> uart1_putchar(Serial_Bytewise[i][w], &uart_str);> [/c]>> Wie man daraus schon erkennen kann, habe ich die Seriennummer jetzt auf> Bytes aufgeteilt, allerdings wohl nicht sehr elegant:>
1
>Serial_Bytewise[i][0]=Serial[i];
2
>Serial_Bytewise[i][1]=Serial[i]>>8;
3
>Serial_Bytewise[i][2]=Serial[i]>>16;
4
>
> Das gleiche droht mir jetzt bei den anderen Werten (12 & 14 Bit lang)> auch. Fällt da jemandem eine elegantere Lösung ein??
Wie auch immer du das machst, schreib dir auf jeden Fall eigene
Funktionen dafür. Es ist unsinnig die Byte-Zerlegung jedesmal wieder
aufs neue in den Hauptcode mit aufzunehmen.
1
voiduart_put24Bit(uint32_tdata,FILE*stream)
2
{
3
uart1_putchar(data,stream);
4
uart1_putchar(data>>8,stream);
5
uart1_putchar(data>>16,stream);
6
}
7
8
...
9
10
// Irgendwo im Code
11
uart_put24Bit(Serial[i],&uart_str);
12 und 14 Bit sind beides 2 Bytes, geht also über dieselbe Funktion
uart_put16Bit.
Einer der Tricks beim Programmieren besteht darin, sich einen
sinnvollen Satz an Funktionen zurechtzulegen, die einem die
eigentliche Arbeit dann erleichtern.
(Wozu brauchst du eigentlich den ganzen stream Quatsch. Nur damit du
printf verwenden kannst? Du schleifst da massenhaft Argumente durch für
einen mehr als zweifelhaften Komfort)
Danke für die Info, die Funktionen hab ich bereits erstellt, nur eben
auf meine Anforderung abgestimmt (zwecks Protokoll). Hab mir ein Array
erstellt und schreib dort die Daten an die Stelle, an der ich sie
brauche und lass mir dann über eine for-Loop die Daten der Reihe nach
ausgeben...
Wie oben schon erwähnt, hab ich mich mit dem putchar noch nicht wirklich
befasst und hab es nur so aus einem vorherigen Projekt übernommen.
Im Moment funktioniert es so und ich habe noch keine Ahnung wie ich es
ohne den "stream Quatsch" realisieren soll...
Die Optimierung werde ich wohl noch in Angriff nehmen müssen....
Perni schrieb:> Im Moment funktioniert es so und ich habe noch keine Ahnung wie ich es> ohne den "stream Quatsch" realisieren soll...
Einfach das FILE* Argument überall weglassen und die
Hast Du Dir schon überlegt, wie Du den Anfang einer Msg feststellst?
Natürlich kannst Du sagen: ok, Byte 1-3 sind die Seriennummer, Byte 4+5
der Messwert, etc., und danach startet alles wieder von vorne. Wenn Du
aber irgendwo mal ein Byte verpasst (oder eine Störung ein zusätzliches
Byte "generiert"), dann hast Du ein Problem, und zwar bis zum nächsten
Reset der Messwerterfassung.
In der Praxis wird Dir das beim Entwickeln garnicht auffallen (weil der
fehler selten vorkommt), später im Praxiseinsatz können dann aber ganze
Messwertreihen unbrauchbar werden.
Bei der Klartext-Variante hast Du da erheblich weniger Probleme, Du
schliesst einfach jeden Messwert mit CR ab und synchronisierst im PC
darauf.
Wenn Du wirklich nur 1* je Sek die Daten überträgst, würde ich mir
keinen Stress machen, ob wenig oder sehr wenig Daten übertragen werden,
ist relativ egal.
Viele Grüße, Stefan
Ich hab mir da was überlegt, was relativ sicher sein sollte. Aber wenn
konstruktive Vorschläge kommen bin ich sehr offen ;-)
Ich hab meine MSG wie folgt aufgebaut:
(|Text| entspricht immer 1 Byte)
|MSG-ID|MSG-Index|SN1_1|SN1_2|SN1_3|SN2_1|.....|CRC|
Durch die MSG-ID weiß ich wieviel Daten ankommen sollen (das ist fix).
Die SN z.B. sende ich nur am Anfang einmal, danach sollte der Sensor bis
zum Abschalten so und so nicht ausgetauscht werden...
MSG-Index lass ich hochlaufen, damit ich weiß ob ich auch alle MSG
bekommen habe, oder ob eine verloren ging.
Danach kommen meine Daten und am Ende kommt eine CRC (weiß noch nicht ob
1 oder 2 Byte - damit beschäftige ich mich jetzt erst ;-) )
Danke für die konstruktive Kritik!! Gerne mehr davon...
Perni schrieb:> (|Text| entspricht immer 1 Byte)>> |MSG-ID|MSG-Index|SN1_1|SN1_2|SN1_3|SN2_1|.....|CRC|
Das Problem ist, dass in deinen Seriennummern Bytes grundsätzlich jeder
Byte Wert vorkommen kann. Auch einer dessen ASCII Code deinem |Text|
entspricht.
Solange alles in geordneten Bahnen läuft, ist das kein Thema.
Interessant wird es erst, wenn eine Störung auftritt und dein Empfänger
das Problem hat, dass er aus dem Datenstrom das Zeichen |Text|
rausfischen und als Anfang einer Msg erkennen muss. Und zwar eindeutig!
Denn wenn er das nicht kann, fällt deine Übertragung nie wieder von
alleine auf die Füsse. Dass der Empfänger durch den CRC Vergleich
feststellen kann, dass die Msg insgesammt nicht gültig ist, hilft dann
herzlich wenig.
Wenn du Übertragungsprotokolle auf dem Papier testen willst, dann denk
dir den fiesesten Datensatz aus, den du dir vorstellen kannst (zb die
Seriennummer Bytes sind alle identisch zu dem Zeichen in |Text|). Und
dann stell dir vor, während eine Übertragung läuft zieht die Putzfrau
das Kabel ab und steckt es erneut an.
Dein Empfänger erhält also nicht das
MSG_index|SN1_1|SN1_2|SN_2_1|...|CRC|und weitere Bytes die danach kommen.
Kann der Empfänger zweifelsfrei und absolut eindeutig dieses Problem
identifizieren und sich selbsttätig hier
1
MSG_index|SN1_1|SN1_2|SN_2_1|...|CRC|und weitere Bytes die danach kommen.
2
^
auf die nächste Msg einklinken? Das die vorhergehende Msg (da nicht
vollständig) verworfen wird bzw. eine Fehlermeldung generiert ist klar.
Aber danach muss der Empfänger mit der nächsten Msg weitermachen können,
und zwar richtig und ohne das Bytes falsch zugeordnet werden.
Bei einem guten Protokoll kann er das.
Vielleicht hab ich mich bisschen blöd und unausreichend ausgedrückt.
Nur um sicher zu gehen, dass es richtig verstanden wurde:
|Text| sollte nur sagen, dass zwischen zwei "|" immer 1 Byte ist.
-> MSG-ID = 1 Byte
-> MSG-Index = 1 Byte
-> SN1_1 = 1Byte
usw
.
.
.
D.h. ich fange immer mit einer MSG-ID an, was aber das Problem natürlich
noch nicht löst. Zusätzlich erwarte ich immer noch eine Antwort des PC's
auf richtigen Empfang, sprich ein ACK, mit dem MSG-Index oder der
CRC....soweit bin ich aber noch (lang) nicht....
Man merkt aber sofort, dass hier jede Menge erfahrener Mitglieder
unterwegs sind ;-)
Danke!!
Perni schrieb:> D.h. ich fange immer mit einer MSG-ID an,
Wenn dein Empfänger diese Bytes bekommt (hexadezimale Schreibweise)
(Aus Sicht des Empfängers beginnt die Übertragung genau jetzt zu laufen.
Der Sender sendet schon seit 10 Minuten)
0x56 0x76 0x79 0x02 0x03 0x79 0x48 0x29 0x49
woher weiß er, dass das erste Byte 0x79 einfach nur ein Datenbyte im
Datensatz der Msg darstellt, in die der Empfänger zufällig
reingeschlittert ist, und das zweite 0x79 die Msg-Id der nächsten Msg
darstellt? Oder anders ausgedrückt: Wie stellt der Empfänger fest, dass
er die ersten 5 Bytes, die er empfangen hat ignorieren soll und beim 6.
Byte die erste vollständige Msg anfängt?
Wenn du dieses Problem vollständig verstanden hast, hast du auch
verstanden, warum sich textuelle Übertragungen (also: ASCII) auch heute
noch großer Beliebtheit erfreuen, wenn man sich den Overhead leisten
kann. Denn in Textform ist dieses Problem meistens trivial zu lösen,
weil eben in normalem Text nicht jeder Bytewert vorkommt und man einige
Werte (meistens Sonderzeichen) dafür reservieren kann, genau diese
Synchronisierung zu leisten. (Jeder Command Line Interpreter macht das.
Das Zeichen Return ist das Kennzeichen, dass eine Zeile zu Ende ist und
eine neue anfängt).
Ich habe das Problem soweit schon verstanden, bei mir ist es nur so,
dass ich regelmäßig (alle 0,5s bis 1s) die Daten schicke und nebenbei
noch ein paar andere Werte berechnen und vergleichen will. Wenn ich
diese dann per printf und somit in ASCII übertrage, dann benötige ich
110 Byte - Binär brauch ich dagegen nur 24 Byte....
Außerdem werden die Systeme gleichzeitig gestartet und laufen nicht
länger als 12 h an einem Stück, von daher gehe ich davon aus, dass ich
das Problem in den Griff bekomme. Sollte ich die Probleme nicht in den
Griff bekommen, weiß ich zum Einen woran es liegen könnt und zum Anderen
an wen ich mich (hoffentlich) wieder wenden kann ;-)
Danke nochmal für die Unterstützung!
Perni schrieb:> Ich habe das Problem soweit schon verstanden, bei mir ist es nur so,> dass ich regelmäßig (alle 0,5s bis 1s) die Daten schicke
Aus Sicht eines µC also massig Zeit.
> und nebenbei> noch ein paar andere Werte berechnen und vergleichen will.
Das ist gut. Dann ist ihm nicht so langweilig.
> Wenn ich> diese dann per printf und somit in ASCII übertrage, dann benötige ich> 110 Byte - Binär brauch ich dagegen nur 24 Byte....
OK. Baudrate hernehmen und ausrechnen, wieviel Zeit für die Übertragung
drauf geht. bzw. umgekehrt ausrechnen welche Baudrate mindestens
benötigt wird um all 1 Sekunde 110 Byte zu übertragen. Die Ergebnisse
werden dein Weltbild ins Wanken bringen :-)
(Baud hat die Einheit Bit/Sekunde. Um 1 Byte, also auch 1 Zeichen zu
übertragen benötigt man 10 Bit, wegen Start und Stopp-Bit. Bei 9600Baud,
kannst du also rund 960 Bytes pro Sekunde übertragen. Deine 110 Byte
sind gerade mal über den Daumen gepeilt ein Neuntel davon und sind daher
in etwas mehr als einer Zehntel Sekunde übertragen. Und 9600Baud ist
verdammt langsam)
PS: Sowohl Senden als auch Empfangen kann man mittels Interrupts
erledigen lassen, d.h. das kostet nur die Zeit die Daten aufzubereiten
und in einen Buffer zu stecken. Auf den Weg gebracht werden die Daten
dann mittels Interrupt, d.h. die eigentliche Übertragung belastet dein
Programm so gut wie gar nicht.
Umgekehrt genauso: Der andere µC macht seinen Empfang ebenfalls per
INterrupt und erst dann wenn er einen Datensatz komplett hat,
benachrichtigt er das Hauptprogramm sich den Datensatz abzuholen.
Mir persönlich ist es egal, ob du eine rein binäre Übertragung machst
oder nicht. Aber bis jetzt hast du kein stichhaltiges Argument gebracht,
warum du unbedingt rein binär brauchst. Deine Zeitbedenken sind nicht
stichhaltig.
Perni schrieb:> int uart1_putchar(char ch, FILE *stream)> {> if (ch == '\n')> uart1_putchar('\r', stream); //add line feed> while ( !(UCSR1A & (1<<UDRE1)) );> UDR1 = ch;> return 0;> }
das solltest du nicht für eine binäre AUsgabe verwenden, wegen add line
feed