Hallo,
wie sollte ich vorgehen wenn ich einen String einlesen will.
z.B. der String in hex sieht so aus
AA BB 04 01 02 03 04 22
dabei ist
AA BB = Startzeichen
04 = die länge der daten
22 = checksumme
wie solle ich nun vorgenen wenn ich den String empfange.
Komplett einlesen und danach aus werten?
oder während des rinlesens auf AA BB prüfen und dann den rest erst
weiterverwerten?
Also ich würde so spontan gesehen auf AA BB warten, dann die Länge
auswerten und Speicher für die Daten besorgen, um dann, wenn alle Daten
empfangen sind, die Prüfsumme auzuwerten.
Da AA BB auch in den Daten auftauchen kann (sieht jedenfalls so aus)
dann ist das als Kennzeichen eine schlechte Wahl. Nimm lieber ein
Zeichen das keine Hex-Ziffer ist.
ok, danke für die tipps
Habe jetzt mal weitergemacht und versuche nun den String komplett zu
empfangen dann zu analysieren und auf Gültigkeit zu prüfen.
mit dem Warten auf AA BB hatte ich zu erst versucht bin dann aber nicht
weitergekommen da beim Uart empfangen dann immer ein zeichen verschluckt
wurde, da es kein ende zeichen gibt ist das doof.
Jetzt habe ich noch ein doofes anders problem. In dem String wird auch
hex 00 empfangen.
Die Uart routine steckt mir aber nicht 00 in den string sondern einfach
garnichts, was kann ich da machen?
>...da beim Uart empfangen dann immer ein zeichen verschluckt wurde...
Es gibt keinen plausiblen Grund das hinzunehmen. Untersuche warum da
Zeichen verschluckt werden. Das kann Dir nämlich auch sonst an anderen
Stellen der Übertragung passieren.
Ludwig schrieb:> Die Uart routine steckt mir aber nicht 00 in den string sondern einfach> garnichts, was kann ich da machen?
Aus der UART kommt ganz sicher auch ein Zeichen mit dem Hex-Wert 00
raus. Die Frage ist dann was du weiter damit machst. Bedenke auch, dass
ein C-String immer nur bis zum Zeichen mit dem Hex-Wert 0x00 geht. D.h.
alle C-Stringfunktionen werten das als String-Ende Zeichen.
Wie sieht jetzt eigentlich dein String wirklich aus? Hast du wirklich
Strings (also Texte) oder dann doch eher im weitesten Sine einfach nur
Byte-Folgen?
Es ist die hier aus dem Tutorial:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Der_UART
Unten die Interrupt routine.
Da wird das aber nicht ausgewertet,
Zum testen gebe ich den string per printf aus kann es sein das der mit
das 00 wegfrisst?
printf("%s", uart_string);
Karl Heinz Buchegger schrieb:> Wie sieht jetzt eigentlich dein String wirklich aus? Hast du wirklich>> Strings (also Texte) oder dann doch eher im weitesten Sine einfach nur>> Byte-Folgen?
Ich habe Byte folgen also AA BB CC ... FF als Hex werte auf der Leitung,
keine ascii zeichen.
Ludwig schrieb:> Ich habe Byte folgen also AA BB CC ... FF als Hex werte auf der Leitung,> keine ascii zeichen.
ALso keine Strings.
> Es ist die hier aus dem Tutorial:
Und was willst du mit der?
Die ist für Strings ausgelegt.
> printf("%s", uart_string);
Auch das ist auf Strings ausgelegt.
Du hast keine Strings!
Du musst schon die Dinge trennen. Nicht alles was man in einem
char-Array speichert (oder in einem uart8_t Array) kann wie ein String
behandelt werden. Wenn du keine Strings hast, dann hast du keine und es
ist grundfalsch da mit Stringmethodik ranzugehen.
ok, also die Tut Routine empfängt das 0x00.
ich habe noch die zeile auskommentiert:
//uart_string[uart_str_count] = '\0';
und das char geändert.
volatile unsigned char uart_string[UART_MAXSTRLEN +1] = "";
Da drinnen liegen nun auch sauber die 00er
Die echte länge der empfangenen Zeichen weiß ich wenn ich erst alles
empfangen will ja erst dann wenn ich alles empfangen habe, das
uart_str_complete setzte ich einfach nach 20ms.
nun ist aber der String mit
#define UART_MAXSTRLEN 30
eingestellt und empfangene Zeichen sind es z.b. 18, es werden aber nie
mehr als 30.
Kann ich nun uart_string irgendwie abschneiden auf die 18 oder müsste
ich einen neue variable machen und die dann dementsprechenend lang
machen und dann übergeben?
Das Problem ist das sizeof(uart_string) dann immer 30 ist.
Das sind meiner Meinung nach nur Workarounds. Du musst bedenken, das
diese Routinen dafür gedacht sind Bytefolgen mit Endekennzeichen zu
empfangen.
Das einzige Kennzeichen für die Länge ist das Längenbyte.
Das einzige Kennzeichen für den Anfang ist 0xAA 0xBB.
Du musst also eine eigene Routine schreiben, die diese auswertet.
Dabei aber das Problem beachten, das ich schon oben genannt habe. 0xAA
0xBB kann auch in den Daten vorkommen. Suche Dir ein anderes Zeichen für
den Telegrammbeginn das in den Daten nicht vorkommt.
Ludwig schrieb:> Die echte länge der empfangenen Zeichen weiß ich wenn ich erst alles> empfangen will ja erst dann wenn ich alles empfangen habe
?
Steht doch in deinen Daten drinn!
> AA BB 04 01 02 03 04 22>> dabei ist> AA BB = Startzeichen> 04 = die länge der daten> 22 = checksumme
Hier. Das 3.te Byte ist die Länge der Daten. Damit ist auch klar,
wieviele Bytes danach noch kommen werden. Nämlich diese Anzahl und dann
noch 1 Byte für die Checksumme.
> Kann ich nun uart_string irgendwie abschneiden auf die 18 oder müsste> ich einen neue variable machen und die dann dementsprechenend lang> machen und dann übergeben?
Du musst dir einfach nur eine zusätzliche Variable machen, in der du die
Länge der Daten ablegst.
Bei Strings brauchst du das nicht, weil dir das \0 Byte in den Daten
verrät, wie lang der String ist.
Aber - du errätst es schon - du hast keine Strings.
Also musst du 2 Informationen haben:
Einmal die Daten selbst, in dem sie in einem uint8_t Array abgelegt
werden.
Und zum zweiten, wie lang diese Daten sind. Und wie lang die sind, das
hat dir der Absender das Daten dankeswerterweise ja mitgeteilt.
> Das Problem ist das sizeof(uart_string) dann immer 30 ist.
Nein. Das Problem ist, dass du ganz dringend ein C-Buch brauchen
würdest, aber keines hast.
sizeof hat zwei Semantiken, die aber nichts mit dem momentanten Inhalt
einer Variablen zu tun haben.
Was Du meinen würdest, wenn Du es wüsstest, ist strlen. Das aber kannst
Du nicht gebrauchen, weil Du keine Strings verarbeitest.
Ludwig schrieb:> ok, also die Tut Routine empfängt das 0x00.
Nochmal zum mitmeisseln:
Mit dieser Routine kannst du nichts anfangen. Die geht von ganz anderen
Voraussetzungen aus.
Ob du willst oder nicht, aber du wirst dir wohl oder übel selbst was
schreiben müssen. Es ist doch alles da, wo liegt denn das Problem dabei
diese Pipifax-Empfangsroutine zu schreiben?
Ja das wäre sauberer, Bin dann aber leider nicht mehr weitergekommen.
Wie sollte ich vorgehen um das AA BB zu erkennen, wenn ich
UART_MAXSTRLEN auf 2 setzte wir der 3. Wert weschluckt also der 3.
aufruf des Interrupts.
Dann wollte ich den 3. Wert gleich in eine Variable stecken und
UART_MAXSTRLEN auf den wert setzen, das gibt dann eine redefine warning
im compiler (wie kann ich den String mit der Länge "herrichten").
Dann denke ich mir immer wenn ich während dem Empfangen mich so
verkünstel und dann schon gleich nach AA BB das nächste Zeichen kommt
der Prozessor mit dem "herrichten" der variable noch nicht fertig ist
wenn der Interrupt kommt, oder kann das nicht passieren?
Empfangen wird mit 19200 der Mega32 läuft mit 14,7456MHz.
Wenn dann AA BB in den Daten kommt ich aber noch z.b. 5 werte lesen muß
ist es ja klar das AA BB in den Werten ist.
Karl Heinz Buchegger schrieb:> Es ist doch alles da, wo liegt denn das Problem dabei>> diese Pipifax-Empfangsroutine zu schreiben?
Wenn ich wüsste wie bräuchte ich nicht zu fragen. Ich weiß ja nichtmal
wiso es mit der nicht gehen sollte.
Du kannst schon ein Array fester Länge verwenden, falls Du weisst, das
gültige_ Telegramme eine Maximallänge _nie überschreiten. Trotzdem
prüfen ob die Länge auch passt und falls nicht den Rest ignorieren.
Du kannst eine Konstante im Programm nicht ändern.
>...gleich nach AA BB das nächste Zeichen kommt>der Prozessor mit dem "herrichten" der variable noch nicht fertig ist>wenn der Interrupt kommt
Das Problem hast Du im speziellen nicht, wenn Du ein Array fester Länge
nimmst. Im allgemeinen hast Du es nicht, weil die UART Übertragung im
Vergleich zur Ausführungsgeschwindigkeit eines malloc gähnend langsam
ist.
>Wenn dann AA BB in den Daten kommt ich aber noch z.b. 5 werte lesen muß>ist es ja klar das AA BB in den Werten ist.
Das ist zwar richtig, setzt aber voraus, das Du immer das anfängliche
0xAA 0xBB mitbekommst. Aber es kann auch mal Übertragungsfehler geben.
Dann ist das 0xAA 0xBB, das Du siehst eigentlich Teil der Nutzlast und
wenn dann zufällig noch das Längenbyte und die Checksumme stimmt (was
zugegebenermaßen wenig wahrscheinlich ist), dann hast Du den Salat.
Das ist kein guter Stil sich darauf zu verlassen, das schon alles gut
wird.
Na, den Rest wird K.H. schon richten. :-)
Ludwig schrieb:> Wie sollte ich vorgehen um das AA BB zu erkennen, wenn ich> UART_MAXSTRLEN auf 2 setzte wir der 3. Wert weschluckt also der 3.> aufruf des Interrupts.> Dann wollte ich den 3. Wert gleich in eine Variable stecken und> UART_MAXSTRLEN auf den wert setzen, das gibt dann eine redefine warning> im compiler (wie kann ich den String mit der Länge "herrichten").
Oh Mann. Bei dir fehlts aber auch an allen Ecken und Enden an den
Grundlagen.
Das Array lässt du auf den 30, die kannst du sowieso zur Laufzeit nicht
ändern.
Dann machst du dir eine 2. Variable, die die Anzahl der gültigen Bytes
im Array halten wird.
Weiters machst du dir einen Empfangsstatus. Den brauchst du, damit du
auf den Datenanfang sauber synchronisieren kannst.
Am Anfang ist der Empfangscode im Status 'wartend'. Wird das 0xAA
gesehen dann geht die ISR in den Status 'erstes Sync-Byte gesehen'. Ist
das nächste Byte kein 0xBB dann geht der Status wieder zurück auf
'wartend'. Ansonsten gehts weiter in den Status 'Längenbyte lesen'. Beim
nächsten ISR Aufruf landet daher das nächste Byte in einer Variablen,
die angibt, wieviele bytes ab jetzt noch kommen werden und die Daten
bilden. Der Status wechselt zu 'Daten lesen'. Ab jetzt wird 1 Byte nach
dem anderen ins Array geschrieben. Solange bis alle Datenbytes da sind.
Nach Empfang des letzten Datenbytes geht der Status auf 'Checksumme'.
Kommt dann das nächste Byte wird die Checksumme über die empfangenen
Daten berechnet und mit der empfangenen Checksumme verglichen. Stimmen
die beiden überein, dann werden die Daten im Array (samt zugehöriger
Länge selbstverständlich) freigegeben und der Status wechselt wieder zu
'wartend'.
Hmm schrieb:> Das ist zwar richtig, setzt aber voraus, das Du immer das anfängliche> 0xAA 0xBB mitbekommst. Aber es kann auch mal Übertragungsfehler geben.> Dann ist das 0xAA 0xBB, das Du siehst eigentlich Teil der Nutzlast und> wenn dann zufällig noch das Längenbyte und die Checksumme stimmt (was> zugegebenermaßen wenig wahrscheinlich ist), dann hast Du den Salat.> Das ist kein guter Stil sich darauf zu verlassen, das schon alles gut> wird.
Da hast du allerdings recht.
Daher die Frage: Wer sendet eigentlich die Daten? Wer hat das
Übertragungsprotokoll festgelegt?
Kann man das ändern?
Wenn nicht, dann wird man wohl erst mal damit leben müssen.
(Hmm ... lass uns die Sache nicht zu kompliziert machen. Er hat momentan
noch viel primitivere Probleme und mit 2 Sync-Bytes samt Checksumme ist
die Wahrscheinlichkeit, wie du richtig sagst, nicht all zu hoch)
>Ich weiß ja nichtmal wiso es mit der nicht gehen sollte.
Das wurde doch schon haarklein erklärt. Lies einfach nochmal die
Antworten und frag nach wenn Du daran etwas nicht verstanden hast.
Hmm schrieb:> @ K.H.> Ja gut. Lassen wir das erstmal weg. Hab mich schon gewundert, das Du> darüber nichts schrubst.
Jep. Ich seh auch ein, dass man nicht alles gleichzeitig lernen kann
(obwohl ich es immer frustrierend finde, wenn .... das erinnert mich
immer an die Auswanderer-Serien im Fernsehen, in denen Leute ohne die
Sprache zu können noch xxxx auswandern und sich dann wundern warum man
sie nicht gleich von der Strasse weg .... lassen wird das).
Bist du einverstanden, mit dem Versuch mit ihm eine State-Machine
aufzubauen. Irgendwann muss er ja schliesslich die Technik auch mal
sehen. Da hat er dann wenigstens ein universelles Hilfsmittel, mit dem
er auch in Zukunft viel erschlagen kann.
@ K. H.
>...State-Machine...
Klar.
Mache ich in solchen Fällen fast immer so. Wenn auch in
unterschiedlichen Varianten. In solchen Fällen (vier Zustände schätze
ich mal) einfach nur ein kleiner switch oder auch nur if-then-else.
@ Ludwig
Bist Du eigentlich noch da? Hey, alles keine grosse Sache. Das haben wir
alle mal durchgemacht. Nur Mut.
Ja, klar noch da, aber ich tue mich halt ewas schwerer, bin schon über
35 ;)
Von der Logical-State-Machine hab ich schonmal was gehört, mehr aber
auch nicht. Leider habe ich niemand im Bekanntenkreis der sich damit
auskennen könnte.
Also:
Nehmen wir mal an Du hättest die bittere Pille schon geschluckt, das es
sich hier nicht um Strings handelt.
Karl Heinz hat Dir hier
Beitrag "Re: Wie vorgehen beim String einlesen?" mal so einen Ablauf
skizziert. Damit erschlägst Du eigentlich alle Fehlerfälle aber
natürlich den gewünschten Fall auch. Ist das für Dich soweit
verständlich? Wenn nicht, dann frage einfach.
Ich frage ja auch nicht nach ner fertiglösung, ich möchte mich schon
sleber damit befassen. Habe auch das Kernighan und Dennis Ritchie C Buch
hier, aber es ist halt nicht so einfach wie früher das C64 Basic.
>...bin schon über 35 ;)
Lach. Ich bin 49. Alter ist keine Entschuldigung, mein Lieber.
>Von der Logical-State-Machine hab ich schonmal was gehört, mehr aber>auch nicht. Leider habe ich niemand im Bekanntenkreis der sich damit>auskennen könnte.
Das macht überhaupt nichts. Wir sprechen Dich da durch. Hast Du die
Erklärung von Karl Heinz hier
Beitrag "Re: Wie vorgehen beim String einlesen?" ein wenig verstehen
können?
>Ich frage ja auch nicht nach ner fertiglösung, ich möchte mich schon>sleber damit befassen.
OK. Das sollst Du ja auch. Du wirst schon Dein Gehirnschmalz dafür
brauchen.
Es ist halt so, das die Methoden die Du versucht hast, auf falschen
Voraussetzungen basieren. Also musst Du was Neues lernen, oder?
Hmm schrieb:> Das macht überhaupt nichts. Wir sprechen Dich da durch. Hast Du die>> Erklärung von Karl Heinz hier>> Beitrag "Re: Wie vorgehen beim String einlesen?" ein wenig verstehen>> können?
Ja ich sage mal generell weiß ich wie es funktionieren soll, auch das
Übertragungsfehler abgefangen werden sollten und die Logic an sich ist
nicht das problem, nur wie ich das in C in ein Program bekomme ist
schwierig für mich.
Eine Sende Routine konnte ich für mein Beispiel anpassen. Dort werden
die Befehle die ich schicke so im Programm hinterlegt:
const unsigned char Cmd_GetCSN[] ={0x00,0x00,0x00,0x02,0x02,0x00};
Das Senden an sich funktioniert, es wird einfach jedes Wort mit einem
Pointer an das uart gegeben und gewartet bis das nächste darf, das geht
solange bis die länge durch ist.
Wo ist aber nun der Unterschid zwischen einem String und dem oben
Cmd_GetCSN is das dann ein Array?
Hmm schrieb:>>...bin schon über 35 ;)>> Lach. Ich bin 49. Alter ist keine Entschuldigung, mein Lieber.
:-)
50
>>Von der Logical-State-Machine hab ich schonmal was gehört, mehr aber>>auch nicht. Leider habe ich niemand im Bekanntenkreis der sich damit>>auskennen könnte.>> Das macht überhaupt nichts. Wir sprechen Dich da durch. Hast Du die> Erklärung von Karl Heinz hier> Beitrag "Re: Wie vorgehen beim String einlesen?" ein wenig verstehen> können?
Ich denke, da braucht man noch eine Erläuterung dazu.
Denn die Beschreibungen klingen immer so hochtrabend. Aber eigentlich
ist es ganz einfach.
Der Status ist einfach nur eine Variable. Da es nur wenige Zustände
sind, ein uint8_t. Die die einzelnen Zustände sind verschiendene Werte,
die die Variable annehmen kann.
Also zb
1
// die möglichen Zustände
2
#define WAITING 0
3
#define FRST_SYNC 1
4
#define LENGTH 2
5
#define DATA 3
6
#define CHECKSUM 4
7
8
volatileuint8_tuartDataBytes[30];
9
volatileuint8_tuartDataLength;
10
11
ISR(...)
12
{
13
// Am Anfang ist der Empfangscode im Status 'wartend'.
14
staticuint8_tuartStatus=WAITING;
15
staticuint8_tdataBytesToRead;
16
17
uint8_tnextByte=UDR;
18
19
// Am Anfang ist der Empfangscode im Status 'wartend'.
20
// Wird das 0xAA gesehen dann geht die ISR in den Status
21
// 'erstes Sync-Byte gesehen'.
22
if(uartStatus==WAITING&&nextByte==0xAA)
23
uartStatus=FRST_SYNC;
24
25
// Ist dann das nächste Byte kein 0xBB dann geht der Status
26
// wieder zurück auf 'wartend'.
27
// Ansonsten gehts weiter in den Status 'Längenbyte lesen'.
28
elseif(uartStatus==FRST_SYNC){
29
if(nextByte==0xBB)
30
uartStatus=LENGTH;
31
else
32
uartStatus=WAITING;
33
}
34
35
// Beim nächsten ISR Aufruf landet daher das nächste Byte in
36
// einer Variablen, die angibt, wieviele bytes ab jetzt noch
37
// kommen werden und die Daten bilden. Der Status wechselt
38
// zu 'Daten lesen'.
39
elseif(uartStatus==LENGTH)
40
....
41
42
....
43
}
Die Variable uartStatus ist der 'Status' in der sich die Maschine
befindet. und die verschiedene Werte annehmen kann. Abhängig von diesem
Status wird das empfangene Byte untersucht und ausgewertet. Je nach
Auswertung kann es dann einen neuen Status geben bzw. passiert irgendwas
mit diesem Byte.
Spiel einfach deine Folge durch die ISR durch, indem du sie in Gedanken
mehrfach aufrufst und dir vorstellst, dass dann jeweils immer das
nächste Byte aus UDR rauskommt. Was muss damit passieren, wie muss die
Statemaschine darauf reagieren?
Ach ja. In uartDataBytes und uartDataLength kommt dann letzten Endes das
Ergebnis raus.
Und schreck nicht davor zurück, weitere Variablen einzuführen, wenn du
sie brauchst! Was du brauchst, das brauchst du.
(dataBytesToRead wirst du brauchen. Wenn der Teil 'daten lesen' beginnt,
dann schreibst du dort rein, wieviele Bytes noch kommen müssen und bei
jedem nächsten byte wird die Variable dann einfach um 1 runtergezählt.
Wird sie 0, dann sind logischerweise alle Datenbytes da und als nächstes
kommt dann die Checksumme in ihrem eigenen Zustand)
>nur wie ich das in C in ein Program bekomme ist schwierig für mich.
Aha. Gehen wir mal Karl Heinz' Beschreibung durch.
>Weiters machst du dir einen Empfangsstatus. Den brauchst du, damit du>auf den Datenanfang sauber synchronisieren kannst.
Das Schlüsselwort ist hier "Status".
Das ist einfach eine Variable. Sie speichert den Zustand (ein anderes
Wort für Status) in dem die "Empfangsmimik" sich gerade befindet.
>Am Anfang ist der Empfangscode im Status 'wartend'.
OK. Wie "codierst" Du, wie schreibst Du den Status "wartend"?
Am einfachsten wäre ein "enum". Kennst Du? Oder? Da Du den K&R hast,
lies einfach nochmal nach.
Wir wollten ja eine Variable für den Status. Die Variable soll unter
anderen den Status "wartend" enthalten können.
In C sieht das dann so aus:
>Wird das 0xAA gesehen dann geht die ISR in den Status 'erstes Sync-Byte gesehen'.
Hier hast Du zwei Dinge. Einen neuen Status "erstes Sync-Byte gesehen".
Den bastelst Du Dir gleich in die Deklaration.
Aber das darf natürlich nicht immer geschehen, wenn 0xAA gelesen wird,
da das ja auch in den Daten vorkommen kann. Das darf nur im Status
"wartend" geschehen.
Also musst Du das noch prüfen.
1
if(Status==Warten)
2
if(GelesenesZeichen==0xAA)
3
Status=ErstesSyncEmpfangen;
Versuche mal den nächsten Schritt einfzufügen, bitte.
>Ist das nächste Byte kein 0xBB dann geht der Status wieder zurück auf
'wartend'. Ansonsten gehts weiter in den Status 'Längenbyte lesen'.
Verdammt, bei der Vorschau sehe ich, das Karl Heinz auch schon was
geschrieben hat. :-)
Hmm schrieb:> Verdammt, bei der Vorschau sehe ich, das Karl Heinz auch schon was> geschrieben hat. :-)
Ja, wir bewegen uns aufs gleiche hin. Die Variablen heissen anders und
du hast einen enum benutzt. Aber das ist alles unter "ferner liefen."
Das Prinzip ist EXAKT dasselbe.
(Das sollte dir Ludwig durchaus etwas sagen :-)
(und auch ich habe natürlich nicht alles ausformuliert :-)
Ok, ich versuche es zu verstehen :)
> #define WAITING 0
Schreibt hier der (compiler/präprozessor) überall wo WAITING steht eine
0 rein? Dient das also nur der Übersichtlichkeit?
Man setzt also Bedingungen geknüpft an einen neue Bedingung um in den
nächsten Status zu kommen, ok denke verstanden.
Nun aber ab ins Bett.
Karl Heinz Buchegger schrieb:> Hmm schrieb:>>> @ Karl Heinz> >>>:-)>>>>50> >> Lach. Noch so ein alter Sack. :-)> > ALso das 'alt' nimmst du zurück!>> (Der Sack geht in Ordnung)
Wenigstens weiß ich jetzt wie alt ich werden muss um es zu verstehen ;)
>Schreibt hier der (compiler/präprozessor) überall wo WAITING steht eine>0 rein? Dient das also nur der Übersichtlichkeit?
Ja. Genau so ist es.
>Man setzt also Bedingungen geknüpft an einen neue Bedingung um in den>nächsten Status zu kommen, ok denke verstanden.
Im Prinzip ja. Leider nicht ganz exakt ausgedrückt, denn eine "neue"
Bedingung gibt es hier nicht.
Besser wäre es wenn Du Dir folgendes denkst:
Du gehst davon aus, das ein gewisser Zustand besteht. Diese
Voraussetzung schreibst Du auf und teilst sie dem Compiler mit, indem Du
sie als bedingte Anweisung, eben ein "if" schreibst.
Von diesem Zustand aus gibt es unter bestimmten zusätzlichen Bedingungen
Übergänge zu einem neuen Zustand. Auch das schreibst Du als bedingte
Anweisung.
>Nun aber ab ins Bett.
Wie jetzt? Jetzt geht die Programmierernacht erst los. :-)
Ludwig schrieb:> Ok, ich versuche es zu verstehen :)>>> #define WAITING 0> Schreibt hier der (compiler/präprozessor) überall wo WAITING steht eine> 0 rein? Dient das also nur der Übersichtlichkeit?
Das sind Präprozessor Makros.
Und ja, das sind einfach nur Textersetzungen. So wie du in deinem Editor
ein 'Ersetzen' hast. Der Clou ist, dass diese 'Textverarbeitung' vom
'Compiler' gemacht wird und zwar bevor er sich dann das Ergebnis dieser
Ersetzungen vorknöpft.
Du solltest wirklich deinen K&R durcharbeiten. So ungefähr bis zum
ersten Drittel. Dieser Wissensumfang ist so ungefähr das Minimum dessen,
was man beherrschen muss, wenn man echte Programme schreiben will (und
nicht nur kleinere Übungen). Alles darunter ist im Grund indiskutabel.
Das ist wie auswandern in die USA und nur 3 Worte Englisch können.
> Man setzt also Bedingungen geknüpft an einen neue Bedingung um in den> nächsten Status zu kommen, ok denke verstanden.
Ganz genau.
Wie eine Maschine.
Die ist in einem Zustand. Dann passiert irgendwas (eine Münze wird
eingeworfen, der Benutzer tritt gegen die Maschine, Hochwasser kommt,
... also im weitesten Sinne: die Maschine bekommt Input) und als Folge
davon macht die Maschine irgendwas (ein Türchen geht auf, die
Cola-Flasche wird freigegeben, ein Licht geht an, .... sie produziert
Output). Und die Maschine kann dann in einen anderen Zustand übergehen
(muss sie aber nicht).
Wobei man auch den Begriff 'Maschine' sehr weit fassen kann. Auch ein
Rollo kann eine Maschine sein. Es hat zb 4 Zustände.
Das Rolle kann
* oben sein
* gerade rauffahren
* unten sein
* gerade runtenfahren
Input in die Maschine, sind Benutzereingaben, mit denen der Benutzer
mitteilt ob das Rollo rauf oder runter fahren soll. WEiters ist ein
Input der Endschalter oben/unten, mit dem die Maschine feststellt, ob
der Motor, der die ganze Zeit das Rollo nach oben gekurbelt hat,
abgestellt werden muss und die Maschine von 'ich fahre gerade rauf' in
den Zustand 'bin oben' übergeht.
Es geht einfch nur um die Organisation, die einen komplexeren 'Ablauf'
in Zwischenstufen zerlegt. Dabei festlegt, durch welchen Input der
Übergang von einer Zwischenstufe in die nächste erfolgen soll und welche
Aktionen dabei passieren soll.
Hmm schrieb:> Wie jetzt? Jetzt geht die Programmierernacht erst los. :-)
Ja ich weiß ein Tag hat 24 Stunden und wenn das nicht reicht nimmt noch
die Nacht dazu :)
Hmm schrieb:>>Nun aber ab ins Bett.>> Wie jetzt? Jetzt geht die Programmierernacht erst los. :-)
Das junge Gemüse hält auch nichts mehr aus.
Ja, damals, als ich noch jung war .....
:-) :-) :-)
Ludwig schrieb:> Was mir noch nicht gefällt, die States sollten zeitlich überwacht sein> damit das ganze nicht auf einem State kleben bleibt und zufällig> weiterhoppelt.
Immer langsam mit den jungen Pferden.
Jetzt bringen wirs erst mal zum Laufen
Und dann kümmern wir uns um Fehlerbehandlung.
Denn Fehlerbehandlung hat die Tendenz den Code unübersichtlich zu machen
und die Prinizpien zu verschleiern.
Auch einer der Vorteile von State-Maschines. Durch die gute Organisation
bleibt es übersichtlich und man kann auch im Nachhinein noch relativ gut
neue States einfügen.
>> Was mir noch nicht gefällt, die States sollten zeitlich überwacht sein> damit das ganze nicht auf einem State kleben bleibt und zufällig> weiterhoppelt.
Wie Karl Heinz schon schrieb. Darum kümmern wir uns später. Im Prinzip
aber ist ein Zeitablauf nichts weiter als eine zusätzliche Bedingung und
ein zusätzlicher Übergang in der State-Machine; wie ein neues
empfangenes Zeichen. Aber wiegeschrieben: Das kommt am Schluss.
"Zufällig" weiterhoppeln aber kann es nach der Methode nicht. Es gibt
genau beschriebene Bedingungen. Anders kann das Ding nicht. Wenn das
doch passiert dann liegt es am Input.
Moin, hoffe da waren keine plünderer am Werk da viel gelöscht ist :)
>> else if( uartStatus == FRST_SYNC ) {>> if( nextByte == 0xBB )>> uartStatus = LENGTH;>> else>> uartStatus = WAITING;>>
Bisher habe ich immer
if(wenn erfüllt){
mach das;
}
else {
ansonsten das;
}
geschrieben. Brauhe ich die {} nur wenn es mehr sache zu tun gibt, also
bei mehr Zeilen?
else if = wenn das erste if nicht erfüllt gilt else if?
>Moin, hoffe da waren keine plünderer am Werk da viel gelöscht ist :)
Nein. Alles OK. Da hat nur jemand Off-Topic geschwatzt.
1
if(wennerfüllt){
2
machdas;
3
}
4
else{
5
ansonstendas;
6
}
>Brauhe ich die {} nur wenn es mehr sache zu tun gibt, also>bei mehr Zeilen?
Ja. Genau.
Du brauchst nur:
1
if(wennerfüllt)
2
machdas;
3
else
4
ansonstendas;
Im K&R, 2. Auflage 1990, Prentice Hill ist das auf den Seiten 55 und 56,
Kapitel 3 "Kontrollstrukturen" gezeigt. Man muss wissen, das ein if
selbst nur eine einzelne Anweisung ist, genau wie while oder do oder
switch etc.
Im Anhang ist in den Abschnitten A9. A9.3, A9.4. das Ganze nochmal
formal d
>else if = wenn das erste if nicht erfüllt gilt else if?
Etwas unglücklich ausgedrückt, aber ich schreib mal: Ja.
Man kann aber nicht eigentlich sagen, daß das "else if" gilt .
Zunächst folgt auf das else einfach nur eine Anweisung. Die wird
ausgeführt, aber sie "gilt" nicht. Wenn diese Anweisung nun wiederum ein
if ist, dann wird eben die Bedingung geprüft, aber sie gilt nicht.
Ich weiss ich bin ein alter Besserwisser, aber es ist letztlich nützlich
auch in Gedanken nahe an den wirklichen Verhältnissen zu bleiben.
Ansonsten denkst Du noch zwei Wochen, das die Anweisung nach dem else
"gilt", d.h. das die Bedingung wahr ist und nicht das die Anweisung
ausgeführt, die Bedingung geprüft wird.
Gerade noch gesehen das ich doch keinen K&R habe :(, ein bekannter
damals hat mir das Buch gegeben und gesagt das es besser zu lesen sei
das von K&R.
Ich habe das vom Herbert Schildt aus dem OsborneMcGraw-Hill Verlag:
"C THE COMPLETE REFERENCE"
isbn 0-07-881263-1
Wenn Du fertig bist mit lesen, dann nimm am besten den Code von Karl
Heinz hier Beitrag "Re: Wie vorgehen beim String einlesen?" und
versuche ihn zu vervollständigen; also die nächsten Zustände und
Übergänge zu schreiben
>"C THE COMPLETE REFERENCE"
Kenne ich nicht. Aber wenn es gut ist, dann ist es ohnehin nur eine
Paraphrase auf den K&R. Jedenfalls sollte es ein Kapitel über
Kontrollstrukturen (if, while, do, switch) enthalten und eine formale
Syntaxbeschreibung. Seitenzahlen kann ich Dir natürlich nicht angeben.
Versuch es mal damit.
Online gibt es noch: http://openbook.galileocomputing.de/c_von_a_bis_z/
Ansonsten gilt: Andere Bücher sind nur Nachahmungen. :-)
Hmm schrieb:>>"C THE COMPLETE REFERENCE">> Kenne ich nicht. Aber wenn es gut ist, dann ist es ohnehin nur eine> Paraphrase auf den K&R.
KOmmt drauf an.
Ich hab jetzt mal die PDF Version des Schildt überflogen. Das Buch
erhebt ganz sicher nicht den Anspruch, ein Lehrbuch zu sein, welchem man
sich anvertraut, und das einen dann von Beginn weg systematisch und
sacht durch die Untiefen der Sprache führt. Der Name ist Programm: Es
ist mehr ein Nachschlagewerk. D.h. du musst schon wissen, wonach du
suchst um es zu finden. Und dann erschlägt dich als Anfänger die dazu
gebotene Informationsfülle (wie nicht anders zu erwarten). Das Buch ist
wie ein fachspezifisches Lexikon: von der ersten Seite weg müsstest du
das Buch bereits gelesen haben um zu verstehen, worüber es auf der
ersten Seite geht.
Es ist, als ob man versuchen würde, mit dem Brockhaus Biologie zu
erlernen. Und so wie der Brockhaus seine Berechtigung hat, hat auch
dieses Buch seine Berechtigung. Aber ob die ausgerechnet in der
Ausbildung eines Neueinsteigers liegt - da habe ich so meine Zweifel.
Über die Qualität der dargebotenen Information kann ich nach einem
kurzen Überfliegen wenig sagen, sie scheint aber nicht allzugut zu sein,
wenn ich einigen Kommentaren trauen darf. Wobei die angekreideten Dinge
(die ich gesehen habe) für einen Wissenden nicht allzu gravierend sind.
Worin der Fehler in
double t;
printf( "%f", sizeof t );
besteht, sollte jetzt niemanden groß aus der Bahn werfen, der mal
schnell die eher selten gebrauchten Optionen im Format-String
nachschlagen muss.
>Aber wenn es gut ist, dann ist es ohnehin nur eine> Paraphrase auf den K&R.
Naja. Eine nicht ganz ernst gemeinte Übertreibung. Aber für den Anfänger
missverständlich. Ich nehme sie zurück.
Hmm schrieb:>>Moin, hoffe da waren keine plünderer am Werk da viel gelöscht ist :)>> Nein. Alles OK. Da hat nur jemand Off-Topic geschwatzt.
Ich hoffe du bist mir nicht böse. Aber wir waren zum Schluss schon
soweit weg vom Thema .... des kompletten Forums, dass ....
Du weißt was ich geschrieben habe und ich weiß was du geschrieben hast.
Und ich denke, das genügt unter uns Klosterschwestern :-)
>Ich hoffe du bist mir nicht böse.
Aber keinesfalls. Im Gegenteil war mir das durchaus recht.
Ich habe das schon in dem Moment mitbekommen, wo Du gelöscht hast.
>Ok, is das das original?
Ich kenne die Datei. Soweit ich weiss, ja. Die Kapitelbezeichnungen und
Nummern sind gleich der in der Ausgabe vom Hanser-Verlag. Nur die
Seitenzahlen weichen ab. Habe das allerdings nie erschöpfend
kontrolliert.
Sehr schön. Leider nicht ganz korrekt aber schon sehr nett.
>Mir kommt es aber immer so vor als würde ich unnütz zuviele Variablen>verbrauchen.
Ich bin nicht dieser Ansicht. Ein paar überflüssige Zuweisungen. Aber es
geht. Vielleicht findest Du sie selbst.
Nun, falls Du uns erklärst was Dich konkret auf diesen Gedanken bringt,
dann kann man darüber diskutieren.
Ich möchte Dir sehr empfehlen die Checksumme beim Übergang von LENGTH
nach DATA zu initialisieren und Dich nicht darauf zu verlassen das das
im übergeordneten Block passiert. Ähnlich mit uartDataCount.
Dann kannst Du Dir im letzten Zustand fast alles sparen.
1
elseif(uartStatus==CHECKSUM){
2
if(nextByte==uartDataChkChk){
3
uartDataComplete=1;
4
}
5
uartStatus=WAITING;
Du hast da immer noch so was im Kopf mit dem if else.
Wenn zwei Variablen nicht gleich sind, dann können sie nur noch ungleich
sein. Wenn Du aber in beiden Fällen die gleiche Operation ausführst,
dann führst Du sie effektiv unabhängig davon aus, ob die Variablen
gleich sind oder nicht. Also immer.
Hmm schrieb:> Sehr schön. Leider nicht ganz korrekt aber schon sehr nett.
Ja, find ich auch. Ich dachte eigentlich, dass die Sache mit der Anzahl
der zu erwartenden Bytes noch ein Problem werden würde, weil das ja
implizit eine Schleife ist und man das erst mal rausfinden muss, wie man
die in der State-Maschine abbildet - aber, das hat er sauber
hingekriegt.
> Ich bin nicht dieser Ansicht. Ein paar überflüssige Zuweisungen. Aber es> geht. Vielleicht findest Du sie selbst.
Ludwig: Wie Hmm schon sagte: Ist aber nicht das große Problem
> Ich möchte Dir sehr empfehlen die Checksumme beim Übergang von LENGTH> nach DATA zu initialisieren und Dich nicht darauf zu verlassen das das> im übergeordneten Block passiert. Ähnlich mit uartDataCount.
Ja, diesen Gedanken unterstütze ich.
Er ist besser, als überall daran denken zu müssen, dass diese Variablen
zurückgesetzt werden müssen.
Egal wie die State-Maschine in die Situation kommt, diesen Übergang
machen zu müssen, setzt du sie hier zurück, dann passiert das auch immer
hier. Speziell wenn im Zuge der Fehlererkennung es dann passiert, dass
die Maschine wieder im Zustand WAITING anfangen muss, ohne jemals bei
CHECKSUM gewesen zu sein, muss sicher gestellt sein, dass die Variablen
dann wenn es darauf ankommt, korrekte Werte haben.
Aber alles in allem: keine schlechte Arbeit - weiter so!
> in der Main schleife habe ich zum testen:
mach da mal
1
if(uartDataComplete==1){
2
uint8_ti;
3
4
printf("Daten gültig im uartDataBytes Array\n");
5
printf(buffer,"Len %d\n",uartDataCount);
6
7
for(i=0;i<uartDataCount;i++)
8
printf("%02x ",uartDataBytes[i]
9
10
printf("\n");
11
12
uartDataComplete=0;
13
}
Da fällt mir was auf. Sieh dir in deinem Code an, was du mit
uartDataCount machst! Den willst du nicht wirklich mit der CHecksumme
auf 0 setzen! Denn: DU brauchst den Wert! Er ist deine einzige
Verbindung, mit der du an anderer Stelle feststellen kannst, wieviele
Bytes im Array gültig sind! D.h. den darfst du nicht vorzeitig
verwerfen.
Ok, jetzt mal der Reihe nach.
>> Ich möchte Dir sehr empfehlen die Checksumme beim Übergang von LENGTH>> nach DATA zu initialisieren und Dich nicht darauf zu verlassen das das>> im übergeordneten Block passiert. Ähnlich mit uartDataCount.
Was ist mit Initialisieren gemeint, 0 zuweisen oder die variable
erstellen?
Wenn die Variable im gesamten Programm da sein soll muß ich diese doch
volatile machen oder?
uartDataChkChk = 0; zwischen den else if?
dann sind ja die daten die ich brauche weg.
wenn ich das letzte nun so mache
1
elseif(uartStatus==CHECKSUM){
2
if(nextByte==uartDataChkChk){
3
uartDataComplete=1;
4
}
5
uartStatus=WAITING;
wo leere ich dann
uartDataCount=0;
uartDataChkChk=0;
?
Und ich will die ja nicht immer leeren doch nur wenns dann zum schluss
so weit ist.
-----------------
Den Teil der Main teste ich gleich, danke.
>> Da fällt mir was auf. Sieh dir in deinem Code an, was du mit>> uartDataCount machst! Den willst du nicht wirklich mit der CHecksumme>> auf 0 setzen! Denn: DU brauchst den Wert! Er ist deine einzige>> Verbindung, mit der du an anderer Stelle feststellen kannst, wieviele>> Bytes im Array gültig sind! D.h. den darfst du nicht vorzeitig>> verwerfen.
bekomme ich nicht die Länge mit:
sizeof(uartDataCount)
?
Ludwig schrieb:>>> Ich möchte Dir sehr empfehlen die Checksumme beim Übergang von LENGTH>>> nach DATA zu initialisieren und Dich nicht darauf zu verlassen das das>>> im übergeordneten Block passiert. Ähnlich mit uartDataCount.>> Was ist mit Initialisieren gemeint, 0 zuweisen oder die variable> erstellen?
Respekt.
Hmm meinte 0-setzen.
Denn Initialisieren geht ja nur bei der Definition einer Variablen. Gut,
in C ist das oft Wortklauberei aber streng genommen hast du Recht. Das
kann keine Initialisierung sein sondern muss eine Zuweisung sein.
> Wenn die Variable im gesamten Programm da sein soll muß ich diese doch> volatile machen oder?
Nein. So pauschal kann man das nicht sagen
FAQ: Was hat es mit volatile auf sich> uartDataChkChk = 0; zwischen den else if?> dann sind ja die daten die ich brauche weg.
Wieso sollen die weg sein?
Ich denke wir reden da jetzt gerade aneinander vorbei.
> wenn ich das letzte nun so mache>
1
>elseif(uartStatus==CHECKSUM){
2
>if(nextByte==uartDataChkChk){
3
>uartDataComplete=1;
4
>}
5
>uartStatus=WAITING;
6
>
>> wo leere ich dann> uartDataCount=0;> uartDataChkChk=0;> ?
Im Status 'LENGTH', also unmittelbar vor dem Zeitpunkt an dem es spätest
möglich ist.
> bekomme ich nicht die Länge mit:> sizeof(uartDataCount)
Nein.
sizeof ist eine Compile-Time Operation. Damit kannst du den Compiler
befragen, wie groß (in Bytes) er eine Variable gemacht hat. Ein Array,
welches du mit 30 Bytes reserviert hast, wird immer 30 Bytes haben. Egal
was drinnen steht. sizeof ist deine Möglichkeit, wie du im C Programm
die Größe von Datentypen benutzen kannst, ohne konkrete Zahlen schreiben
zu müssen.
>Was ist mit Initialisieren gemeint, 0 zuweisen oder die variable>erstellen?
Ja, aber...: Damit ist die Zuweisung eines "sinnvollen Anfangswertes"
gmeint. Das kann eine 0 sein, ist es aber nicht immer. In diesem Fall
aber schon.
Das "erstellen" einer Variable mit der Drechselbank wurde durch das
dritte von-Neumann Konzil durch die "Deklaration" abgelöst. C. Babbage
war zwar strikt dagegen, aber er wurde in einen Schal eingehäkelt und so
mundtot gemacht. :-) Lady Ada spielt dabei ein nicht unbedeutende Rolle,
die alte Häklerin.
>Wenn die Variable im gesamten Programm da sein soll muß ich diese doch>volatile machen oder?
Nein. Der erste Teil des Satzes der sich auf das "Da-Sein" von Variablen
bezieht hat mit dem zweiten Teil, dem "flüssig" sein nichts zu tun. Das
zweite meint, das die Variable durch irgendwelche Umstände die der
Compiler nicht selbst feststellen kann (hier ein Interrupt) geändert
werden kann. D.h. der Compiler darf sich niemals darauf verlassen das
der Wert etwa konstant ist oder seine letzte Lesung dieser Variablen
noch gültig ist. Lies mal bitte in dem C-Buch.
>uartDataChkChk = 0; zwischen den else if? dann sind ja die daten die ich >brauche
weg.
Hrm. Ich muss doch gleich mal Zigaretten holen. :-) Die Daten sind
dadurch nicht weg. Wie sollten sie? Und die Information das die
Checksumme korrekt ist, ist dann in der Regel schon erkannt worden.
Karl Heinz: Da müssen wir noch was tun, das er nicht noch bei der
Verarbeitung der alten Daten ist, wenn schon neue ankommen.
Synchronisation.
>sizeof(uartDataCount)
Nein. Das ist in zweifacher Hinsicht falsch. Bitte schau mal in das
C-Buch was sizeof macht. In der zweiten Hinsich "ist" der Inhalt der
Variablen uartDataCount schon die Länge.
Ich gehe mich jetzt ersäufen. Bin in einer Viertelstunde wieder da. :-)
Das mit dem "Initialisieren" ist so eine Sache. Zum einen ist das
Terminus der Sprache C. Insofern hat Karl Heinz recht, das man das nur
einmal tun kann.
Aber das ist auch ein Terminus in Bezug auf Algorithmen wie etwa eine
Prüfsumme zu ermitteln. Wenn sie wiederholt durchgeführt werden, dann
muss man zwar eigentlich von "Re-Initialisieren" sprechen, aber so ganz
falsch ist das nun nicht.
Tatsächlich habe ich mir falsch ausgedrückt. Für das Weglassen von "Re"
verordne ich mir eine Zeile Logarithmentafel von Hand schreiben als
Strafe.
Ok, denke habe den größten teil verstanden.
Hmm schrieb:> Karl Heinz: Da müssen wir noch was tun, das er nicht noch bei der>> Verarbeitung der alten Daten ist, wenn schon neue ankommen.>> Synchronisation.
Passiert eigentlich nicht da Daten nur kommen wenn ich zuvor das
Kommando zum schicken gegeben habe.
>> if (uartDataComplete==1){>> uint8_t i;>>>> printf("Daten gültig im uartDataBytes Array\n");>> printf( buffer, "Len %d\n", uartDataCount );>>>> for( i = 0; i < uartDataCount; i++ )>> printf( "%02x ", uartDataBytes[i] //fehlt da eine klammer?>>>> printf( "\n" );>>>> uartDataComplete=0;>> }
Was ist buffer?
Hmm schrieb:> Tatsächlich habe ich mir falsch ausgedrückt. Für das Weglassen von "Re"> verordne ich mir eine Zeile Logarithmentafel von Hand schreiben als> Strafe.
Sei nicht so streng mit dir. Tatsächlich mixen wir Menschen oft die
Wörter durcheinander und trotzdem kennt sich jeder aus. Ich plädiere ja
auch schon seit 15 Jahren dafür die Termini 'Adresse' und 'Pointer'
nicht so durcheinander zu werfen. Trotzdem passiert es laufend und
trotzdem kennen sich die meisten aus, was gemeint ist.
Ludwig schrieb:> Was ist buffer?
Autsch. Ein Überbleibsel.
Ursprünglich bin ich den Weg über sprintf gegangen, bis ich gemerkt
habe, dass dein printf offenbar direkt an die Ausgabe gehen kann. An
dieser Stelle habe ich dann vergessen, den 'buffer' rauszunehmen.
Hi,
die gesamte Routine wurde mich mal interessieren, denn das mit der
Checksumme und der variablen Länge wäre auch beim meinem Projekt toll.
Cu Oliver
der Vergleich hier
else if( uartStatus == DATA && uartDataCount < dataBytesToRead ){
ist noch etwas übertrieben.
Denn an dieser Stelle kann uartDataCount nicht gleich dataBytestoRead
werden. Denn wenns zum Ende der Daten hin kommt, dann schaltet
if (uartDataCount == dataBytesToRead)
uartStatus=CHECKSUM;
zum nächsten Zustand weiter.
Ich weiß: Ich selbst war es, der mit solchen Dingen angefangen hat.
Heute könnte ich mich dafür ....
Mach es dir zur Gewohnheit, an dieser Stelle immer nur den Status
abzufragen. Denn: du musst dafür sorgen, dass auch im Fehlerfall die
Zustandsweiterschaltungen passieren. Wenn dieser if bei dir nicht
genommen wird, weil die Counter durcheinander laufen, dann hängt deine
Zustandsmaschine auf immer und ewig im Zustand DATA und kommt da nicht
mehr raus.
Was auf jeden Fall noch fehlt:
Dein Array hat eine begrenzte Länge. OK, ich weiß du wirst jetzt sagen,
das reicht dicke, so lang sind meine Daten nicht.
Aber: Im Fehlerfall kann es passieren, dass du ein völlig falsches Byte
als Länge auffasst, und dann kann es passieren, dass du hinten aus dem
Array rausrauscht. Wichtig: du musst so tun, als ob du das Byte
tatsächlich verarbeitest, schliesslich muss es ja auch irgendwann in den
CHECKSUM Zustand gehen - das einzige was du nicht tun darfst, ist in
diesem Fall das Array beschreiben, weil du dann irgendwas anderes im
Speicher zerstörst.
d.h. deine erste Fehlerbehandlung ist es hier
1
elseif(uartStatus==DATA){
2
if(uartDataCount<sizeof(uartDataBytes)
3
uartDataBytes[uartDataCount]=nextByte;
4
5
uartDataChkChk=uartDataChkChk^nextByte;
6
uartDataCount++;
7
8
if(uartDataCount>=dataBytesToRead)
9
uartStatus=CHECKSUM;
10
}
gegen diesen Fall vorzusorgen.
In eine ähnliche Kategorie fällt der Austausch von == durch >= beim
abschliessenden Vergleich zur Statusweiterschaltung. Das ist einfach
defensives Programmieren.
Und schreib nicht so dicht!
Seit du 6 Jahre alt bist, ist dein Gehirn darauf trainiert worden, dass
zwischen den Wörtern ein Leerraum steht. Bei
1
uartDataChkChk=uartDataChkChk^nextByte;
muss man erst mal innerhalb der Zeile identifizieren, wo ein Wort
anfängt und wo es aufhört. Und erst dann kann man analysieren was da
eigentlich steht. Schreibst du aber
1
uartDataChkChk=uartDataChkChk^nextByte;
dann nutzt du dein Lesetraining aus. Die bewusste Wortanalyse fällt
schon mal weg.
Nur weil du es so dicht schreiben darfst, bedeutet das nicht, dass du es
musst.
Ok, danke für die Hinweise, das mit der Längenüberwachung baue ich noch
ein.
Ich habe mir gedacht wenn ich jeden State zeitüberwache denn bei 19200
muß das nächste Byte in absehbarer Zeit kommen, wenn nicht würde ich die
Schnappermatik wieder zurücksetzten. Das muß ich mir aber noch ausdenken
wie.
Hier kommst Du in einen Bereich von dem wir noch garnicht gesprochen
haben. Du musst eine Zeit abmessen. Hast Du schon eine Idee wie Du das
machen kannst?
>müsste gehen mit dem oszi
Was meinst Du mit "Oszi"? Oszilloskop oder Oszillator?
>welche zeit?
Naja. Das musst Du ja eigentlich wissen. Welche Zeiten willst Du messen?
Sagen wir mal eine Zeit in der Grössenordnung der Bitlänge bei 19200
Baud.
Also sagen wir mal 52us.
Das dient erstmal nur als Beispiel.
Es ist kaum sinnvoll schon nach einer Bitzeit oder auch einer Bytezeit
schon von einem Timeout auszugehen. Es sei denn Du fügtst hier noch
weitere Informationen zu den Geröten oder Protokollen hinzu.
Aber wie machst Du das grundsätzlich?
OK. Ehe wir jetzt noch drei Jahre um den heissen Brei rumreden:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Die_Timer_und_Z%C3%A4hler_des_AVR
Karl Heinz Buchegger schrieb:> Aber: Im Fehlerfall kann es passieren, dass du ein völlig falsches Byte>> als Länge auffasst, und dann kann es passieren, dass du hinten aus dem>> Array rausrauscht.
Habe ich so den Fehlerfall abgefangen?
Michi schrieb:> Karl Heinz Buchegger schrieb:>> Aber: Im Fehlerfall kann es passieren, dass du ein völlig falsches Byte>>>> als Länge auffasst, und dann kann es passieren, dass du hinten aus dem>>>> Array rausrauscht.> Habe ich so den Fehlerfall abgefangen?
Kommt drauf an.
Solange du deine Timeouts nicht implementiert hast, hast du eine
wunderbare "Endlosschleife" gebaut.
Ist deine Zustandsmaschine erst mal im Zustand "DATA" und ist der
Fehlerfall eingetreten, kommt sie da ohne fremde Hilfe nicht mehr raus.
Im schlimmsten Fall ist das dann ein Reset.
Denk daran, das Du hier einen Fehler abfangen willst, der durch eine
Begrenzung der Implementierung des Protokolls entstehen kann.
Das Protokoll, kann bis zu 255 Bytes empfangen. Nur Dein Buffer ist
kleiner. Daher musst Du die Daten weiter annehmen, wenn Dein Buffer am
Ende ist auch wenn Du sie nicht speicherst. Ausserdem musst Du dann in
einen anderen Folgezustand, wenn die Checksumme kommt, denn das Telegram
darf insgesamt dann nicht als korrekt angenommen werden. Du hättest ja
nur einen Teil der Daten.
Ich denke nicht, dass Karl Heinz das gemeint hat, aber nimm einmal an,
in Deinen Nutzdaten treten 0xAA 0xBB auf, nachdem Du einen Überlauf
erkannt hast. Dann werden Nutzdatenbytes als Synchronisation erkannt
obwohl sie das nicht sind.
Ah ok, denke verstanden, der Interrupt wird nicht dann ausgelöst wenn
was esmpfangen wird sondern solange noch was im Buffer ist, deswegen
muss es weiterlaufen bis der Buffer leer ist?
>...der Interrupt wird nicht dann ausgelöst wenn>was esmpfangen wird sondern solange noch was im Buffer ist, ...
Jein. Vermutlich muss man sogar sagen: Nein.
Der Interrupt wird ausgelöst, wenn ein Zeichen empfangen wurde. Das
liegt natürlich einem Register, das man mit gutem Willen auch als Buffer
bezeichnen kann. Deswegen das Jein. Vermutlich beziehst Du Dich aber auf
den Buffer, den ich in meiner letzten Antwort gemeint habe, die Variable
uartDataBytes; dann muss es ganz klar Nein heissen.
"Es" muss weiterlaufen, d.h. die Interrupts müssen weiter abgearbeitet
werden bis das Telegramm beendet ist. Der Sender weiss ja nicht, das
Du festgestellt, hast, das Dein Buffer namens uartDataBytes zu klein
ist. In Deinem Protokoll ist nicht vorgesehen, das der Empfänger dem
Sender sowas mitteilen kann. Daraus folgt, Du musst die Nachricht
vollständig entgegennehmen als ob Du genug Platz hast. Du darfst sie
aber nicht speichern (in uartDataBytes). Dennoch musst Du auch die
Checksumme entgegennehmen. Du brauchst Sie nicht vergleichen. Du darfst
auch nicht uartDataComplete = 1 setzen, weil Du eben das Telegramm nicht
vollständig gespeichert hast. Du hast nur ein Fragment, das Du (solange
Du nichts anderes sagst, muss man davon ausgehen) überhaupt nicht
sinnvoll verarbeiten kannst.
Die Forderungen erfüllt aber Dein Fragment von hier
Beitrag "Re: Wie vorgehen beim String einlesen?" nicht!
Ok, danke, dann muß ich mir das noch anschauen.
Im Moment hänge ich noch an der Zeitüberwachung, und das mir die
Abfreage in einer while schleife welche eine Bedingung hat die von einem
interrupt verändert wird, wegoptimiert wird.
Aber wie kann ich das verhindern?
beispiel:
Starte_Zeit(100);
while(( uartDataComplete == 0 ) && ( zeitvorbei == 0) ){}
Stoppe_Zeit();
in der Overflow Interrupt Routiene Zähle ich die Überläufe und wenn die
Überläufe nach 100 vorbei ist wird zeitvorbei = 1
Ich habe bisher nur gefunden das es warscheinlich wegoptimiert wird aber
nicht wie man das verhindern kann.
>Ok, danke, dann muß ich mir das noch anschauen.
Mach das mal zuerst. Es macht, meiner Meinung nach, keinen Sinn nun mit
etwas anderem zu beginnen. Das ist auch mein Fehler, muss ich zugeben.
>in der Overflow Interrupt Routiene Zähle ich die Überläufe und wenn die>Überläufe nach 100 vorbei ist wird zeitvorbei = 1
Das ist schonmal gut gemacht.
Was das "wegoptimieren" betrifft, muss man das mal hinterfragen und wir
kommen auch an einen Wendepunkt.
Zum einen musst Du ganz klar stellen, an welchen konkreten ,
objektiven Kriterien Du feststellst das da etwas wegoptimiert wird.
Die einfache Vermutung reicht nicht, für konkrete Änderungen des
Codes.
Was den Wendepunkt betrifft, so hast Du jetzt folgendes Problem: Der
Interrupt für die Verarbeitung wird nur dann aufgerufen, wenn ein
Zeichen empfangen wurde. (Das wir das irgendwann ändern müssen war klar,
aber ich habe das so weiterlaufen lassen, weil es ohne Zeitüberwachung
erstmal unwichtig war).
Das hat zur Folge, das Zeitüberschreitungen nur dann erkannt werden wenn
ein Zeichen empfangen wurde. Das ist natürlich ein Widerspruch. Kommt
ein Zeichen zu spät, dann wird das erst bei dem späten auftreten
erkannt. Kommt es garnicht, dann wird das nie erkannt.
Nun kannst Du aber (beim AVR) einen Interrupt nicht durch einen weiteren
Interrupt unterbrechen. Selbst wenn das (bei anderen CPUs ausnahmsweise
geht - das hängt von weiteren Umständen ab - ist das keine allgemein
empfehlenswerte Technik.
Du musst also zunächst Deinen Code aus dem Interrupt herauslösen. In dem
neuen Interrupt wird nur das empfangene Zeichen in eine globale Variable
gespeichert und ein globales Flag gesetzt. In dem neuen main wird dieses
Flag abgefragt (es sagt nun, das ein Zeichen empfangen wurde) und die
State-Machine anhand des empfangenen Zeichens weitergeschaltet.
Wenn Du das erledigt hast, kannst Du in dem Timer-Interrupt auch so
einen Flag-Mechanismus implementieren. Wenn in main dieses Flag gesetzt
ist, so kannst Du nach entsprechender Erweiterung auch damit die
State-Machine weiterschalten.