Hi zusammen!
Zu alles erst, wenn Ihr eine bessere und sichere Lösung habt, bitte
posten. Ich muss in der Main zyklisch prüfen ob etwas empfangen wurde,
wenn ja was für Zeichen. Es sind Kommandos, welche immer mit einem
Buchstabe anfangen (wird Dezimal im ASCII vom PC gesendet), danach
folgen byteweise zahlen mit einer 16er Basis (HEX Zeichen), diese muss
ich dann erst in Dezimal umwandeln um diese weiter zu verarbeiten).
Zum Code:
Ich benutze einen ATMega32, u.a. auch das USART Interface um Daten vom
und zum PC zu schicken.
Dazu benutze ich den "USART_RXC_vect" Interrupt.
Wenn ein Zeichen rein kommt, wird dieses erst in einen puffer-array
geschrieben, dann wird der Index (schreibindex) des arrays erhöht (bedes
geschiet in der ISR).
Parallel prüfe ich in der Main ob mein leseindex != schreibindex ist.
Wenn ja, dann wurde ein Zeichen empfangen, erhöhe mein leseindex um 1
und lese die stelle vom array aus (das ist ja das neue Zeichen).
Funktioniert auch, jedoch bemerke ich, das der gesamt puffer (also die
definierte array größe) nie immer komplett durchlaufen wird.
Ich habe normalerweise eine arraygröße von 200 zeilen (0 - 199) á 8 Bit
breite (uint8_t).
Ich prüfe in der ISR und in der Funktion, welche von der Main zyklisch
aufgerufen wird, ob der lese- bzw. schreibindex < 200 ist, wenn nicht,
dann setze ihn wieder auf 0.
Wenn ich mir jetzt den leseindex und den schreibindex über zwei PORTS
über LEDs ausgebe, sehe ich, das manchemal nur bis 22, 135 ... gezählt
wird und nicht bis 199 (aber beide indexe setzen sich wieder
gleichzeitig auf 0).
Ein paar mal hatte ich sogar, dass (NUR) der leseindex nach dem überlauf
(>= 200) nicht auf 0 zurückgesetzt wird, sondern weiter erhöht wird
(LEDs haben wild geflackert -> schnell hochgezählt).
Anbei mal ein paar code schnipsel.
Habe Ihr eine Idee woran da liegen kann? Oder einen Vorschlag, welcher
zu 100% funktioniert.
Ich muss einfach prüfen ob ein Zeichen rein kam, wenn ja muss ich prüfen
welches ZEichen es ist, dann ob wieder ein Zeichen empfangen wurde, wenn
ja passt es zu meinem erwarteten Muster usw.
Z.b. habe ich ein Kommando: "Q1234;"
"Q" wird in ASCII geschickt, d.h. ich prüfe in der Main ob der ASCII
code von "Q" rein kam. Wenn ja, dann müssen 4 Zahlen mit der Basis 16
folgen -> ich springe in eine for-schleife und hol mir jedesmal das neue
Zeichen. Am schluss muss ein ";" in ASCII folgen. Wenn alles passt mache
etwas, wenn wo ein Fehler bemerkt wurde, schicke ein Error code an den
PC.
Code schnipsel:
Funktion, welche von der Main aus zyklisch aufgerufen wird:
Wenn nichts empfangen wurde, dann return 255, ansonsten das empfangene
Byte.
Die erste Zeile in dieser Funktion ruft eine andere Funktion auf, diese
wartet keine kurze Zeit da ich mit 19200 baud sende und die Main
eventuell zu schnell sein kann mit dem abrufen eines Zeichens da das
neue noch nicht ankam. diese func wartet je nach Bausrate eine gewisse
Zeit, hier ~520µs.
1
intUSART_get_Byte(void)
2
{
3
DelayWaitTimeSerial();//Description is in this Function
4
if(read_idx!=write_idx)//If something received in array "puffer"
5
{
6
uint8_tByte=0;
7
if(read_idx<200)
8
{
9
Byte=puffer[read_idx];//Read out current Byte
10
read_idx++;//increment the read-index to next array position.
11
}
12
else
13
{
14
read_idx=0;
15
}
16
returnByte;
17
}
18
return255;
19
}
Hier der Interrupt welche beim Empfangen eines Zeichen ausgelößt wird:
1
ISR(USART_RXC_vect)
2
{
3
if(write_idx<200)
4
{
5
puffer[write_idx]=UDR;
6
write_idx++;
7
}
8
else
9
{
10
write_idx=0;
11
}
12
}
Schon mal vielen Dank für eure Hilfe!!
Grüße
Thomas D.
Von hinten noch vorn:
write_idx++;
if(write_idx > 200) write_idx = 0;
Anstatt Schreib- und Lesezeiger zu vergleichen, kann man auch die Anzahl
der Bytes im Puffer mitzählen: anzahl++ bei neuem Zeichen, anzahl-- wenn
es aus dem Puffer gelesen wird.
Hat Dein Datenstring ein festes Endzeichen? ";" beispielsweise?
Dann setze in der ISR ein Flag, wenn das Endzeichen empfangen wurde und
starte dann erst mit der Auswertung. Oder verwende einen Zwischenpuffer,
der die Zeichenkette bis zum Endzeichen einliest und dann konvertiert.
Die Routinen für Zwischenpufferung und Auswertung werden von main() zwar
aufgerufen, aber nicht in main() selbst eingefügt.
Ich hoffe, meine Ausführungen sind nicht zu knapp :-)
Hi m.n.!
Ja, die Zeichenketten haben immer einen "delimiter" (";" also in ASCII
== 59).
D.h. es kommt ein Zeichen (Kommando) rein, je nach kommando gibt es
unterschiedliche anzahl von folgenden zahlen mit der basis 16 und dann
immer ein ";".
Jupp, dass ich die anzahl der empfangenen Zeichen hoch (beim empfangen)
und runter (beim lesen) zähle wäre auch eine Möglichkeit! D.h ich lese
dann immer so lang Zeichen für Zeichen bis eine zählvariable z.b. wieder
== 0 ist.
Jedoch fange ich erst an zu zählen wenn ein Flag gesetzt wurde, dass
z.B. ein ";" empfangen wurde.
Ich hoff nur, dass das Problem mit der Arraygröße dann nicht mehr ist.
Denn es gibt auch Kommandos wo ~60 Zeichen empfangen werden. Wenn dann
plötzlich nach 35 Einträge der Puffer wieder bei 0 beginnt gehen mir
Daten verloren, so wie es jetzt passieren könnte. Das ist das ja was ich
nciht verstehe. Eigentlich kann ich jetzt bis zu 200 Zeichen empfangen
bis der puffer wieder überläuft.
Am einfachsten und am schnellsten wäre ein Funktion, die mir beim
zyklischen aufrufen in der Main sagt ob ein Zeichen empfangen wurde oder
nicht und wenn eins empfangen wurde, welches empfangen wurde. Dann
könnte ich schauen welchen ASCII wert dieses Zeichen hat, z.B. "Q" und
springe mit "if (empfangenesZeichen == 81)" (Q in ASCII = 81) in eine
unterfunktion. Da ich dann weis welches Kommando an kam (hier das "Q")
weis ich auch wieviel Zeichen folgen und könnte dann mit einer
for-schleife die restlichen Zeichen abholen, welche im puffer liegen.
Nach dem auslesen der ZEichen prüfe ich ob das letzte Zeichen ein ";"
ist, wenn ja, gut, wenn icht, schicke ein Fehler.
Gruß
Thomas D.
> Ich hoff nur, dass das Problem mit der Arraygröße dann nicht mehr> ist. Denn es gibt auch Kommandos wo ~60 Zeichen empfangen werden.
Dann muss dein Buffer, in dem die ISR die empfangenen Zeichen
zwischenlagert eben so groß sein. 60 Bytes sind jetzt nichts, was einen
groß umbringt.
> Am einfachsten und am schnellsten wäre ein Funktion, die mir> beim zyklischen aufrufen in der Main sagt ob ein Zeichen> empfangen wurde oder nicht und wenn eins empfangen wurde,> welches empfangen wurde.
Das klingt zwar logisch.
Aber am einfachsten sind die Dinge tatsächlich, wenn die ISR sich schon
darum kümmert, aus den eingehenden Zeichen komplette 'Datensätze' zu
machen und die entsprechend zu gruppieren.
> Da ich dann weis welches Kommando an kam (hier das "Q")> weis ich auch wieviel Zeichen folgen und könnte dann mit> einer for-schleife die restlichen Zeichen abholen, welche> im puffer liegen. Nach dem auslesen der ZEichen prüfe ich> ob das letzte Zeichen ein ";" ist, wenn ja, gut, wenn icht,> schicke ein Fehler.
Denn siehst du, genau da brichst du dir jetzt das Genick.
In der Hauptschleife müsstest du jeweils 1 Zeichen holen. Das prüfst du,
ob es ein ';' ist und wenn ja, beginnst du mit der Auswertung des
Kommandos. Wenn nicht (kein ';') dann wird es in einen Buffer angehängt,
in dem Zeichen für Zeichen nacheinander das Kommando entsteht, solange
bis dann irgendwann der ';' kommt. Mit deiner 'for-Schleife' und 'wir
wissen das' Methode holst du dir nur Fehler ins System.
> ob das letzte Zeichen ein ";" ist, wenn ja, gut, wenn icht,> schicke ein Fehler.
Dein Mega32 ist schon mit der neuen 'Ich kann in die Zukunft sehen -
Glaskugel' ausgerüstet? Oder wie weiß der, dass der Sender schon alles
weggeschickt hat und das ';' schon angekommen sein müsste?
Und das Sammeln von Zeichen bis zum ';', das kann die ISR genausogut
machen. Deine main() muss nur überprüfen, ob ein vollständiges Kommando
vorhanden ist oder nicht und wenn eines da ist, dann wird es ausgewertet
und die Antwort generiert.
Hi!
@m.n.
Klasse, danke für die Datei! Ich werde versuchen mir die spätestens am
WE an zu schauen!
@Buchegger
Zu deinem ersten quote:
Mein Puffer ist momentan 200 Bytes groß. Das ist ja auch nicht das
Problem.
Es geht mir um das von mir ganz am Anfang beschriebenes Problem mit der
Test-Ausgabe über LEDs (incrementiert dauerhaft den readidx, warum auch
immer, d.h. er wird nicht auf 0 gesetzt wenn er größer 200 ist).
Quote 2:
Jupp, wird wohl das beste sein, dachte halt, das ich die ISR so kurz wie
möglich gestalte, ohne viel mit Bits und Bytes zu arbeiten.
Quote 3:
Dies hatte zwar funktioniert, hat mir aber nicht gefallen. Ich habe dann
immer so viel "if" anweisungen wie ich kommandos habe, wenn der richtige
Kommandobuchstabe kommt, springt er in die richtige If anweisung rein
und holt sich mit USART_get_Byte das nächste Zeichen usw.
Quote 4:
Ich weis durch die Protokollbeschreibung wieviel Zeichen für welches
Kommando kommen müssen. Wenn z.B. "Q" kommt, weis ich das z.B. 8 Zeichen
folgen und dann der delimiter (";"). Wie einen Absatz weiter oben
geschrieben, wird geprüft welches Zeichen an kommt (mit IF anweisung),
dann geht er in die Anweisung und in eine for schleife, diese macht so
viel Runden wie ich Zeichen empfangen muss (die sind bei jedem Befehl
bekannt), danach prüfe ich ob ein ";" kommt, wenn ja dann hat alles
gepasst.
IC hoff ich konnte es richtig schreiben wie ich das meine.
Jedoch frage ich mich warum der µC ab und an nicht bis 199 zählt (Puffer
von 0- 199), sondern schon früher den readidx &writeidx auf 0 setzt oder
nicht mehr aufhört zu incrementieren, ist doch eigentlich wasserdicht:
If Anweisung in USART_get_Byte:
1
if(read_idx<200)
2
{
3
Byte=puffer[read_idx];//Read out current Byte
4
read_idx++;//increment the read-index to next array position.
Thomas D. schrieb:> Hier der Interrupt welche beim Empfangen eines Zeichen ausgelößt wird:>
1
>ISR(USART_RXC_vect)
2
>{
3
>if(write_idx<200)
4
>{
5
>puffer[write_idx]=UDR;
6
>write_idx++;
7
>}
8
>else
9
>{
10
>write_idx=0;
11
>}
12
>}
13
>
Wenn du "puffer[write_idx] = UDR;" in die IF-Abfrage rein nimmst, wird
UDR beim Überlauf des Schreibindexes nicht in das Array übertragen!
Schreib es besser so:
DelayWaitTimeSerial();//Description is in this Function
4
if(read_idx!=write_idx)//If something received in array "puffer"
5
{
6
uint8_tByte=0;
7
if(read_idx<200)
8
{
9
Byte=puffer[read_idx];//Read out current Byte
10
read_idx++;//increment the read-index to next array position.
11
}
12
else
13
{
14
read_idx=0;
15
}
16
returnByte;
17
}
18
return255;
19
}
Was mir als erstes auffällt ist, dass Du nicht unterscheiden kannst, ob
Du den Wert 255 per UART empfangen hast oder kein Zeichen empfangen
wurde. Nimm als Rückgabewert bei keinem Zeichen also lieber -1 (oder EOF
= End of File aus stdio.h, ist auch als -1 definiert).
Als zweites: Wenn read_idx == 199, wird er auf 200 erhöht. Im nächsten
Durchlauf wird er auf 0 gesetzt, aber kein Wert gelesen, sondern 0
zurückgegeben. Erst im dritten Durchgang wird dann der richtige Wert
gelesen.
Und als drittes: Die Funktion DelayWaitTimeSerial() ist überflüssig.
Wenn noch kein neues Zeichen empfangen wurde, machst Du eben nichts:
1
intUSART_get_Byte(void)
2
{
3
if(read_idx!=write_idx)//If something received in array "puffer"
4
{
5
uint8_tByte=puffer[read_idx];//Read out current Byte
6
if(read_idx<200)
7
{
8
read_idx++;//increment the read-index to next array position.
9
}
10
else
11
{
12
read_idx=0;
13
}
14
returnByte;
15
}
16
return-1;
17
}
[c]int main(void) {
[...]
while (1) {
int zeichen = USART_get_Byte(void);
if (zeichen != -1) {
// lese Zeichen
}
[...]
}
Ansonsten musst Du aufpassen, dass Deine Indizes nur uint8_t sein
dürfen. Wenn write_idx 16 Bit breit ist, musst Du den Zugriff darauf
atomar machen.
Hi!
@Magnus M.
Hey Stimmt, werde ich sofort ändern! Danke!
@xfr
Ich bekomme nie einen Wert der 200 ist (0xFF). Ich bekomme HEX Werte pro
Byte geschickt. D.h. in dezimal ist jedes empfangene Byte max "15". Also
ich empfange maximal von "0" bis "15" in dezimal welche in das 8Bit
breite array geschrieben werden. D.h. es ist noch massig platz um einen
rückgabewert zu definieren der mir sagt, dass nix empfangen wurde.
Anfangs hatte ich als Rückgabewert "0" wenn nix gekommen ist, da war die
Übertragung vom PC aber auch komplett in ASCII. Das geht jetzt nicht
mehr da ich keine ASCII Werte mehr, sondern "HEX" Werte vom PC bekomme,
welche jeweils in einem Byte übertragen werden. Später könnte man zwei
Werte pro Byte übertragen, dann passt das mit der 255 natürlich nicht
mehr. Aber so ist das nioht angedacht, obwohl ich dadurch nur halb so
viel Datenstrom über die RS232 Schnittstelle hätte da ich ja dann gleich
zwei statt nur einen Wert pro Byte übertrag (vom PC zum µC).
Nein, die indizes sind nur uint8_t, also keine 16 Bit, daher brauche ich
nicht an eine Atomare variante denken, aber trozdem danke fürs mit
denken!
Zu deinem Einwand, dass der writeidx bei 199 auf 200 erhöht wird stimmt
natürlich auch! Vielen Dank auch an Dich!
So jetzt werde ich erst mal probieren. Melde mich spätestens nächste
Woche ob alles funzt oder nicht.
@m.n.
natürlich schau ich mir noch Deine file mit an!
Grüße
Thomas D.
Nachtrag:
@xfr
Da ich momentan in der Main, polling betrieb mit den einzelnen Zeichen
habe (ob ein empfangen wurde, wenn ja, welches) könnte es passieren ,
dass ich ein Kommando empfange, dann in die If Anweisung springe und
dann die nächsten Zeichen abholen muss, da ich aber nich weis wie
schnell die While schleife in der Main ist (also wie offt diese in einer
Sekunde durchläuft [ist ja auch je nach Befehl schneller oder kurzer, je
nach dem was der µC zu tun hat]), kann es passieren, dass er das erste
Steuerzeichen wie z.B. "Q" empfangen und erkannt hat, dann die nächsten
Zeichen vom Puffer holen will, da aber ncih nix drin ist, weil die
Übertragung im gegensatz zum µC (16MHz) mit 19200baud recht langsam ist
(19200/10Bit Wortlänge = 1920 Zeichen pro Sekunde).
Daher warte ich je nach eingestellter Bautrate min. die Zeit von einem
Zeichen + 20% bis ich zurück gebe ob ein Zeichen empfangen wurde oder
nicht.
Nachteil: Ich kann max~ 1000 Zeichen am stück empfangen da mir sonst
mein puffer überläuft weil ich ja bei jedem empfangenen Zeichen etwas
länger warte wie das neue Zeichen braucht um übertragen und abgearbeitet
zu werden -> alle 5 empfange Zeichen liegt ein Zeichen mehr im Puffer (5
* 20% = 100% -> ein Zeichen).
Somit ist aber sichergestellt, dass, wenn der ein Steuerzeichen erkannt
hat (If abfrage) und dann in eine for-schleife geht um die nächsten
folgezeichen zu holen, immer eins da ist und nicht in einen puffer
unterlauf rennt weil er die daten schneller abarbeitet wie diese rein
kommen.
Gruß
Thomas D.
Thomas D. schrieb:> Somit ist aber sichergestellt, dass, wenn der ein Steuerzeichen erkannt> hat (If abfrage) und dann in eine for-schleife geht um die nächsten> folgezeichen zu holen, immer eins da ist und nicht in einen puffer> unterlauf rennt weil er die daten schneller abarbeitet wie diese rein> kommen.
Nö, das ist nicht sichergestellt. Wer sagt denn, dass die Gegenstelle
(PC) die Zeichen so schnell hintereinander sendet? Wenn Du die von Hand
ins Terminal eintippst, wird das sehr viel länger dauern ...
Der Ansatz, dass Du USART_get_Byte() so oft hintereinander aufrufen
kannst, wie Du Zeichen erwartest und immer eins da ist, ist grundfalsch.
Du musst in der Hauptschleife immer erst nachfragen, ob ein neues
Zeichen angekommen ist. Nur wenn das der Fall ist, wertest Du das
Zeichen aus. Zwischendurch darf USART_get_Byte() beliebig oft
zurückgeben, dass noch kein neues Zeichen da ist. Anders geht das
garantiert in die Hose.
Wenn Du die Zeichen alle hintereinander verarbeiten willst, musst Du sie
vorher puffern und erst mit der Verarbeitung anfangen, wenn alle da
sind. Das haben Dir im Prinzip ja auch die anderen vorgeschlagen.