Es handelt sich hierbei um einen String in dem der empfangene String vom
UART abgespeichert wird - sprich vom PC.
Aber ich verstehe einfach nicht wofür diese Schleife genau dient.
Viel dank im Voraus
Auszug aus der While Schleife der main.c um die es geht!
... schrieb:> Die Schleife schmeißt LF und das> Zeichen davor (CR?) aus dem String und beendet ihn.
Nur das Zeichen vor dem LF wird mit Null überschrieben. In der ISR
werden empfangene Daten in einen Buffer stringReceived[] geschrieben.
Wenn eines dieser Zeichen den Wert 0x0A aufweist wird das Zeichen davor
auf den ASCII-Code NUL (=0x00) gesetzt.
Dafür muss die Schleife aber schneller aufgerufen und abgearbeitet
werden als Interrupts auftreten, sonst kann es passieren dass nicht
jedes empfangene Byte dem Vergleich mit 0x0A zugeführt wird.
Hallo,
es wird in main ja offensichtlich der komplette empfangene String
durchsucht, bis 0x0A auftaucht und davor ein \0 als Stringende gesetzt.
Die ISR packt nur jedes empfamgene Zeichen in den Puffer und erhöht den
Zeiger.
Woran erkannt wird, wann alle Zeichen vollständig empfangen wurden, ist
hier nicht ersichtlich, genauso, wie der Aufrufzeitpunkt der
Ersetzroutine nicht einzuordnen ist, ohne die komplette
Empfangsgeschichte zu sehen.
Gruß aus Berlin
Michael
... schrieb:> Sieht aus wie eine Zeilenendeerkennung. Die Schleife schmeißt LF und das> Zeichen davor (CR?) aus dem String
Soweit ja.
> und beendet ihn.
Das ist hieraus nicht ersichtlich. Da fehlt offenbar wesentlicher Code,
denn k=0 beendet die Schleife nicht.
"Etwas" unsauber ist gut. Was passiert denn, wenn die ISR direkt vor dem
"k=0" in der Hauptschleife aufgerufen wird? Offenbar soll doch
stringReceived[] nach einem CRLF als String weiter verarbeitet werden
und man verlässt sich darauf, dass ein String-Terminator (\0) da ist.
Das wird dann aber nicht der Fall sein, weil dieser dann mit dem gerade
neu empfangenen Zeichen überschrieben wurde.
Außerdem kann der Inhalt noch während der Auswertung in der ISR schon
wieder überschrieben werden. Das ist auch nicht gut.
Detlev T. schrieb:> "Etwas" unsauber ist gut.
Der Autor des Codes legt an gewissen Stellen einige .. ähem...
"Unsicherheiten" an den Tag.
1. Er weiß nicht, dass '\n' statt 0x0A lesbarer im Code ist.
2. Er weiß nicht, dass das Zeichen unmittelbar vor dem '\n' ein '\r' ist
und nullt daher "vorsichtshalber" ein beliebiges Zeichen davor.
3. Als Bedingung im for-Statement steht ein strlen-Funktionsaufruf,
d.h. er berechnet für jeden Schleifendurchgang die Zeichenkettenlänge
neu. Das kostet jede Menge Zeit...
4. Steht an der Stelle k == 0 ein '\n', wird an der Stelle
StringReceived[-1] ein '\0' geschrieben. Damit wird eine beliebige
Variable im Speicher "gepatched".
5. Nach dem Speichern der '\0' sollte die Schleife mit 'break' verlassen
werden. Stattdessen wird k auf 0 gesetzt und der ganze String
"vorsichtshalber" nochmal durchgescannt... wieder mit zig strlen-
Aufrufen...
Alles in allem: Nicht benutzen, die Schleife ist Quark.
Abgesehen davon, daß strlen viel zu umständlich ist, es funktioniert
auch nur dann, wenn vorher der gesamte stringReceived mit memset
ausgenullt wurde.
Da ja eh die einkommenden Zeichen gezählt werden, ist es auch völlig
unnnötig.
Man könnte ganz profan im Interupt jedes Byte auf '/n' oder '/a' testen,
durch '/0' ersetzen und ein Flag setzen.
Das Main guckt dann nur nach, ob das Flag gesetzt ist und erst dann
schnappt es sich den Puffer.
Diese unsaubere for-Schleife entfällt dann komplett.
Peter
Ja. Ist scheiße. Ich würde die Tauscherei in der Service-Routine machen.
Und:
for(k=0;k<strlen(stringReceived);k++)
{
if(stringReceived[k] == 0x0A)
{
k--;
stringReceived[k] = '\0';
k=0;
}
}
Ist auch Scheiße. Funktioniert zwar, aber die Schleife wird zweimal
durchlaufen bzw. solange immer wieder von vorn bis keine 0x0As mehr drin
sind. Zeitvergeudung.
Die ganze Schleife ist, ehrlichgesagt, mieß programmiert. Man könnte
alle durch "if" gefundenen vorkommen bereits beim ersten Schleifen
durchlauf ersetzen lassen. Durch k=0 wird die Schleife aber immer wieder
durchlaufen, solange "if" auf "true" geht.
Wesentlich besser bei gleichem Effekt:
1
for(k=0;k<strlen(stringReceived);k++){
2
if(stringReceived[k]==0x0A){
3
stringReceived[k-1]='\0';
4
}
5
}
Davon aber abgesehen frage ich mich, warum die Zeichenkette erst
komplett eingelesen und anschließend geprüft wird?! Whatever... Ohne den
ganzen Quelltext zu sehen um auch den Kontext zu verstehen, ist das ein
Stochern im dunkeln.
KLez schrieb:> Wesentlich besser bei gleichem Effekt:>>
1
for(k=0;k<strlen(stringReceived);k++){
2
>if(stringReceived[k]==0x0A){
3
>stringReceived[k-1]='\0';
4
>}
5
>}
Der strlen-Aufruf ist hyperfluid. Ausserdem fehlt da immer noch das
break. Vorausgesetzt, dass ab dem '\r' (das Zeichen vor dem '\n')
genullt werden soll, würde ich es so machen:
1
for(k=0;stringReceived[k];k++)
2
{
3
if(stringReceived[k]=='\r')
4
{
5
stringReceived[k]='\0';
6
break;
7
}
8
}
Einen Ticken effizienter könnte es noch mit einem Pointer gehen... aber
das kommt auf den Compiler an.
> Davon aber abgesehen frage ich mich, warum die Zeichenkette erst> komplett eingelesen und anschließend geprüft wird?! Whatever... Ohne den> ganzen Quelltext zu sehen um auch den Kontext zu verstehen, ist das ein> Stochern im dunkeln.
Das kann schon manchmal sinnvoll sein, wenn man wiederverwendbaren Code
benutzen will oder die Codierung auf verschiedenen Software-Schichten
abläuft. Stell Dir ein simples Schichtenmodell vor:
1. Applikation (hier: simpler Kommando-Interpreter)
2. Sicherungsschicht (hier: sorgt für saubere Trennung der Kommandos)
3. Übertragungsschicht (hier UART)
Sicht PC:
Die Applikationsschicht versendet Kommandos (beliebige Strings)
Die Sicherungsschicht trennt die Strings durch CRLF ("\r\n")
Die Übertragungsschicht pustet den String raus
Sicht µC:
Die Übertragungsschicht liest den String ein
Die Sicherungsschicht entfernt den Trenner (CRLF) wieder
Die Applikationsschicht interpretiert die Kommandos
Deshalb mache ich so etwas auch in verschiedenen Modulen/Funktionen. Das
ist auch kein Problem, wenn man effizient programiert - und NICHT
strlen() verwendet ;-)
Die Übertragungsschicht schickt einfach "dumm" irgendwas durch die
Gegend. Daher kann/darf sie die Trenner nicht entfernen.
@FrankM: Das break habe ich bewusst nicht reingesetzt, weil Du damit
nach dem ersten Fund die Schleife verlässt. Dadurch werden nicht alle
eventuellen vorkommen im String ersetzt.
KLez schrieb:> @FrankM: Das break habe ich bewusst nicht reingesetzt, weil Du damit> nach dem ersten Fund die Schleife verlässt. Dadurch werden nicht alle> eventuellen vorkommen im String ersetzt.
Es ist Quatsch, mehrere '\0' da reinzusetzen. Sobald du den String
kappst, indem Du das erste '\0' schreibst, kommst Du später an den Rest
nicht mehr so ohne viele Klimmzüge dran. Nein, da geht es um eine Zeile,
nichts weiter.
Ausserdem zeigt mit das Setzen von
1
k=0;
nach dem Kappen des Strings (siehe Source im ersten Posting), dass der
Autor niemals(!) mehrere NUL-Zeichen schreiben wollte. Deshalb geht er
brav zurück wieder an den Anfang des Strings. Mit strlen() wird er da
auch niemals wieder über sein eigens gesetztes '\0' hinauskommen! :-)
KLez schrieb:> @FrankM: Das break habe ich bewusst nicht reingesetzt, weil Du damit> nach dem ersten Fund die Schleife verlässt. Dadurch werden nicht alle> eventuellen vorkommen im String ersetzt.
In Deiner Version glaube ich auch nicht, denn sobald das \r durch 0
ueberschrieben wird ist der string kuerzer (strlen gibt kleineren Wert
zurueck), und die Schleife wird verlassen.
Eine Moeglichkeit dies zu umgehen waere es, die Laenge des String
zunaechst einer Variablen zuzuweisen.
Wow, erstmal danke für die vielen Antworten, leider steige ich da jetzt
wirklich garnicht mehr durch xD
Um es mal auf den Punkt zu bringen was ich machen möchte:
Ich möchte einfach einen String der über UART empfangen wird (per
interrupt) in eine Variable speichern. Ich habe schon wirklich tausende
von Beiträgen durchsucht aber ich finde immer nur was für für
Atmels//avrs.
Vielen vielen Dank
Chris Meier schrieb:> Ich möchte einfach einen String der über UART empfangen wird (per> interrupt) in eine Variable speichern.
Also du willst eine als String vorliegende Zahl in Dezimalform in eine
Variable überführen?
Dann schau mal nach Funktionen wie atoi und sscanf. Beispiel:
Also ich möchte bei mir zB den String "getTemp" empfangen, dieser soll
dann in eine variable geschrieben werden, so das ich ihn später zb so
abfragen kann:
Chris Meier schrieb:> aber ich finde immer nur was für für> Atmels//avrs.
Die Register mögen anders heißen (z.B. UDR statt RCREG), die
Funktionalität ist doch aber sehr ähnlich. Da dürfte die Adaption der
ISR zumindest für den Empfang nicht so schwer sein. Außerhalb ist das
dann nicht mehr Controller-spezifisch.
A. K. schrieb:> Alternative zu atoi wäre strtol/strtoul, weil da nicht wie bei atoi> bloss Mist-rein-Mist-raus gilt.
Das kann aber durchaus auch praktisch sein, wenn man weiß, wie atoi()
funktioniert :-)
Ok ich glaube irgendwie reden wir aneinander vorbei, das Problem was ich
habe fängt wirklich schon von Grund aus an das ich nicht genau weiß wie
die variable korrekt auslese bzw danach weiter verarbeiten kann !
hier mal mein programm zum besseren Verständnis
Chris Meier schrieb:> die variable korrekt auslese bzw danach weiter verarbeiten kann !
Welche Variable?
Chris Meier schrieb:> sprintf(Buffer,"YEY");
Das ergibt keinen Sinn. Was soll "YEY" bedeuten?
Wenn du eine Zahl aus einer Variablen in einen String konvertieren
willst:
Chris Meier schrieb:> Ok ich glaube irgendwie reden wir aneinander vorbei, das Problem was ich> habe fängt wirklich schon von Grund aus an
Da hast du nicht ganz unrecht.
Das Problem, dass du hast, ist in erster Linie, dass du in der
abgeschlossenen Welt eines µC C-programmieren lernen willst, ohne dich
auf einem PC (mit wesentlich besseren Debug-Möglichkeite) mit einem
vernünftigen Lehrbuch erst mal durch die ersten Kapitel 'gelernt' zu
haben
> interrupt isr()> {>> GIE = 0;> PEIE = 0;> if(RCIF==1)> {> RCREG=zeichen[k];> }>> PEIE = 1;> GIE = 1;>> }
Das erste Problem beginnt bereits hier.
Die INterrupt Routine sammelt Zeichen. Das ist soweit auch in Ordnung.
Aber wenn du Zeilenweise übertragen willst (was grundsätzlich immer eine
Gute Idee ist), dann solltest du bereits hier die Erkennung des
Zeilendes einbauen und mittels einer globalen Variablen dem Rest des
Codes mitteilen: "Ich habe eine komplette Zeile zusammengesammelt".
Das wäre erst mal eine vernünftige Vorgehensweise.
(Mal ganz davon abgesehen, dass die Zuweisung in die falsche Richtung
geht: Du willst dir ja das Zeichen aus RCREG abholen und in zeichen
sammeln. Und auch k erhöhen wäre eine extrem gute Idee. Anstelle von k
sollte man die Variable anders benennen. k ist zu allgemein. Diese
Variable erfüllt einen Zweck und dieser Zweck sollte sich auch im
Variablennamen niederschlagen)
Denn dann braucht hier nicht dauern
if(strcmp(zeichen,"getTemp\0")==0)
ein unvollständiger C-String (deine Interrupt Routine erzeugt nämlich
keinen gültigen C-String, weil die 0-Terminierung fehlt) mit etwas
verglichen zu werden, was so schon mal in die Hose gehen wird. Ausserdem
muss man in ein String-Literal nicht selber eine 0-Terminierung
einbauen, das macht der Compiler schon ganz von alleine.
Und die Variablen, mit denen eine Interrupt Routine mit dem rest der
Welt kommuniziert, müssen zwingend als 'volatile' markiert werden, damit
der Compiler sich mit den Optimierungen auf diesen Variablen etwas
zurückhält.