Hallo,
ich habe folgendes Problem. Ich möchte einen String welcher über die
UART Schnittstelle meines AVRs rein kommt auswerten. Dazu habe ich eine
Funktion geschrieben.
Für die UART Schnittstelle verwende ich die Lib von Peter Fleury.
Es kommt ein String in folgenden Format an: #Befehl
Das # ist also das Startzeichen, abgeschlossen wird der String mit einem
Return.
Ich möchte diesen Befehl dann ohne das # in ein Char Array Speichern.
Leider funktioniert das aber nicht, es scheint gar nichts in das Array
gespeichert zu werden.
Leider bin ich zu blöd den Fehler zu finden.
Hier mal mein Code.
1
voidBefehl()
2
{
3
4
uint8_tbufferzaehler=0;
5
uint8_tstring_complete=1;
6
charc;
7
8
do
9
{
10
c=uart_getc();
11
if(c!=UART_NO_DATA)
12
{
13
14
if(c==35)
15
{
16
Relais1ein();
17
c=uart_getc();
18
while(c!='\n')
19
{
20
if(c!=UART_NO_DATA)
21
{
22
Befehlsbuffer[bufferzaehler++]=c;
23
c=uart_getc();
24
}
25
}
26
27
Befehlsbuffer[bufferzaehler]='\0';
28
string_complete=0;
29
}
30
}
31
32
}while(string_complete);
33
34
35
}
Hier noch die Main-Funktion
1
charBefehlsbuffer[14]={"00000000000000"};
2
intmain(void)
3
{
4
//Hier sind noch einige Initialisierungen
5
6
7
8
do
9
{
10
Befehl();
11
12
uart_puts(Befehlsbuffer);
13
14
}while(1);
15
}
Generell funktioniert die UART Schnittstelle, nur meine Auswertung
funktioniert nicht.
Hat jemand einen Vorschlag an was es liegen könnte?
Danke
Josef
Josef F. schrieb:> char c;>> c = uart_getc();> if( c != UART_NO_DATA )
Sieh dir nochmal die Doku zu den Funktionen an, insbesondere den
Return-Typ der Funktion uart_getc. Sieh dir auch an, welcher Wert sich
hinter UART_NO_DATA (bzw. den anderen Konstanten) verbirgt und überleg
inwiefern das mit dem Return Typ der Funktion zusammenhängt bzw. warum c
kein char sein darf.
> if(c==35)
PS: wenn du # meinst, dann schreib auch #
1
if(c=='#')
ist doch viel lesbarer und verständlicher. Es gibt keinen Grund, warum
du da selbst den ASCII Code dafür raussuchen solltest. Das kann der
Compiler perfekt für dich erledigen.
Karl Heinz schrieb:> Sieh dir nochmal die Doku zu den Funktionen an, insbesondere den> Return-Typ der Funktion uart_getc. Sieh dir auch an, welcher Wert sich> hinter UART_NO_DATA (bzw. den anderen Konstanten) verbirgt und überleg> inwiefern das mit dem Return Typ der Funktion zusammenhängt bzw. warum c> kein char sein darf.
Vielen Dank für den Tipp. Sowas blödes, da habe ich mich vertan.
Ich habe den Datentyp der Variablen c nun von char zu uint16_t geändert.
Leider funktioniert mein Code noch immer nicht.
1
voidBefehl()
2
{
3
4
uint8_tbufferzaehler=0;
5
uint8_tstring_complete=1;
6
charc;
7
8
do
9
{
10
c=uart_getc();
11
if(c!=UART_NO_DATA)
12
{
13
14
if(c==35)
15
{
16
Relais1ein();
17
c=uart_getc();
18
while(c!='\n')
19
{
20
if(c!=UART_NO_DATA)
21
{
22
Befehlsbuffer[bufferzaehler++]=c;
23
c=uart_getc();
24
}
25
}
26
27
Befehlsbuffer[bufferzaehler]='\0';
28
string_complete=0;
29
}
30
}
31
32
}while(string_complete);
33
Relais1aus();
34
35
}
Aus Testgründen habe ich eine Funktion Relais1ein(); nach dem Erkennen
des # Zeichens eingefügt. Diese wird auch ausgeführt, das Relais wird
aktiviert.
Am Ende der do-while Schleife soll dieses Relais wieder ausgeschaltet
werden. Dies geschieht nicht. Ich gehe daher davon aus dass er die
Schleife niemals verlässt.
Kann jemand noch einen weiteren Fehler entdecken?
Vielen Dank
Josef
Josef F. schrieb:> char Befehlsbuffer[14]={"00000000000000"};
Dein Befehlsbuffer ist kürzer als dein String. In C passt ein 14-Zeichen
String leider nicht in einen 14-Byte Puffer.
Ich habe jetzt nochmal einiges versucht, unter anderem die if-Abfrage
umgeschrieben zu:
1
if(!(c&UART_NO_DATA)
Aber diese Bedingung scheint nicht erfüllt zu werden. Heißt das, die
UART Lib bekommt keine weiteren Daten außer dem # mehr?
Ich bin langsam echt am Verzweifeln.
Wäre für eine Hilfe sehr dankbar.
Schönen Abend
Josef
Josef F. schrieb:> Vielen Dank für den Tipp. Sowas blödes, da habe ich mich vertan.> Ich habe den Datentyp der Variablen c nun von char zu uint16_t geändert.
so , so
Wo, genau, in dem geposteten Code hast du das gemacht?
Josef F. schrieb:> Ich bin langsam echt am Verzweifeln.
erstens: trenne den Empfang eines Zeichens von der Verarbeitung des
Strings.
Beides hat erst mal nichts miteinander zu tun. D.h. in dere
Empfangsroutine, die eine Zeile von der UART liest, hat die Auswertung
nichts verloren. Die Empfangsroutine soll die Zeichen in einem BUffer
sammeln und Bescheid geben, wenn ein \n gesichtet wurde und die Zeile
somit vollständig ist.
zweitens: Die Empfangsroutine sollte im Idealfall nicht warten. Sie holt
ein Zeichen von der UART, wenn eines da ist, und hängt es an die bisher
empfangegen Zeichen hinten an. Wenn das Zeichen ein \n ist, dann hängt
sie nichts hinten an, sondern meldet dem Aurufer, dass der String
vollständig ist.
drittens: du hast eine funktionierende UART, die du benutzen kannst. Du
kannst sie nicht nur benutzen, um dir damit gewollte Meldungen
auszugeben, sondern du kannst sie auch zum Debuggen benutzen. Nichts und
niemand hindert dich daran, dass du ein Zeichen, welches du empfängst,
zum Sender (Terminal) zurücksendest um so dort zu sehen, was los ist.
Nichts und niemand hindert dich zb daran, dir mittels Sonderzeichen
'Markierungen' auszugeben, so dass du als Programmierer im Terminal
siehst, welche Schleife verlassen wird, welches if genommen wird. Du
hast eine UART und ein Terminal! Nutze sie, damit dir das Programm
selber hilft, es zu debuggen.
Und noch was:
wenn deine Eingabe von einem anderen Programm kommt, dann leg das erst
mal zur Seite und debugge bzw. entwickle deine Empfangs- bzw. Auswertung
erst mal mit einem Terminal. Denn dann hast du Debug-Möglichkeiten (weil
du auch was ausgeben kannst) und musst nicht im Nebel stochern.
Karl Heinz schrieb:> so , so> Wo, genau, in dem geposteten Code hast du das gemacht?
Vielen Dank für Deine Tipps.
Ich habe leider bei diesem Post nur die Zeile für das Relais eingefügt.
Die Variable habe ich in meiner Entwicklungsumgebung aber natürlich
angepasst. Tut mir Leid dass mir da beim Erstellen des Beitrages ein
Fehler unterlaufen ist.
Karl Heinz schrieb:> erstens: trenne den Empfang eines Zeichens von der Verarbeitung des> Strings.> Beides hat erst mal nichts miteinander zu tun. D.h. in dere> Empfangsroutine, die eine Zeile von der UART liest, hat die Auswertung> nichts verloren. Die Empfangsroutine soll die Zeichen in einem BUffer> sammeln und Bescheid geben, wenn ein \n gesichtet wurde und die Zeile> somit vollständig ist.
Ich habe leider folgendes Problem, weshalb ich das auch direkt
zusammengefasst habe. Der String welcher an meinen µC gesendet wird
enthält nicht nur meine Befehle sondern zwischendurch auch alles
mögliche andere. Auch etliche Zeichen lang ohne \n.
Darauf habe ich keinen Einfluss. Lese ich jetzt dauernd nur die
Schnittstelle ein und speichere was dort ankommt ist der Prozessor in
der Zeit nur damit beschäftigt den sinnlosen String zu speichern.
Dann müsste ich auch noch kontrollieren ob der Buffer übergeläuft, und
vielleicht steht dann schon ein Teil eines gültigen Befehls mit darin,
was dann beim löschen des übergelaufenen Buffers mit gelöscht wird.
Deswegen dachte ich ich prüfe dann immer nur ob ein # vorhanden ist.
Das ist in meinem Code natürlich auch noch nicht brauchbar, da er in
diesem Fall eben in einer Endlosschleife hängt bis ein # kommt.
Ich denke mein ganzer Ansatz ist schlecht. Wie sollte man das in diesem
Fall denn richtig angehen?
Mich würde dennoch interessieren warum er in meinem Code nicht in die
if-Abfrage springt.
Da ist sicher ein grundsätzlicher Fehler drin, aber ich komme nicht
darauf :(
Danke
Gruß Josef
Josef F. schrieb:> Ich habe leider folgendes Problem, weshalb ich das auch direkt> zusammengefasst habe. Der String welcher an meinen µC gesendet wird> enthält nicht nur meine Befehle sondern zwischendurch auch alles> mögliche andere. Auch etliche Zeichen lang ohne \n.> Darauf habe ich keinen Einfluss. Lese ich jetzt dauernd nur die> Schnittstelle ein und speichere was dort ankommt ist der Prozessor in> der Zeit nur damit beschäftigt den sinnlosen String zu speichern.
ok. Dann starte die Aufzeichnung erst mit der Erkennung des '#'.
>> Dann müsste ich auch noch kontrollieren ob der Buffer übergeläuft,
das musst du sowieso.
> und> vielleicht steht dann schon ein Teil eines gültigen Befehls mit darin,> was dann beim löschen des übergelaufenen Buffers mit gelöscht wird.
Dadurch, dass du die Aufzeichnung erst startest, wenn ein '#' gesichtet
wird, erledigt sich dann auch dieses Problem.
> Ich denke mein ganzer Ansatz ist schlecht. Wie sollte man das in diesem> Fall denn richtig angehen?
So wie ich das skizziert habe. Nur führst du dir noch ein Flag ein,
welches die Aussage erlaubt: können die Zeichen zu einem Kommando
gehören oder nicht. Wird ein '#' gesichtet, geht das Flag auf 1, kommt
der '\n' geht das Flag wieder (neben all dem anderen) auf 0. Die Zeichen
kommen nur dann in den Buffer rein, wenn das Flag auf 1 steht.
Josef F. schrieb:> Mich würde dennoch interessieren warum er in meinem Code nicht in die> if-Abfrage springt.
Ich kenn deinen letzten Code nicht. Aber
1
while(c!='\n')
2
{
3
if(c!=UART_NO_DATA)
4
{
5
Befehlsbuffer[bufferzaehler++]=c;
6
c=uart_getc();
7
}
8
}
du gehst davon aus, dass zu jedem beliebigen Zeitpunkt ein neues Zeichen
da ist. Dem ist aber nicht so. UART ist vergleichsweise langsam. D.h. in
der überwiegenden Mehrzahl der Fälle wirst du UART_NO_DATA kriegen.
Nur: wenn da einmal UART_NO_DATA gekommen ist, wie soll da jemals was
anderes kommen? Du rufst ja uart_getc nie wieder auf.
Machs so, wie ich das skizziert habe. Füg noch die
Aufzeichnungs-Start_Stop Kennung mit dem '#' hinzu und gut ists. So
vergeudet dein Programm am wenigsten Rechenzeit und bearbeitet nur dann
die UART, wenn es auch tatsächlich was zu tun gibt. Man muss nicht
mutwillig den µC durch die UART lahmlegen, zumal die Änderung nicht
aufwändig ist.
Karl Heinz schrieb:> So wie ich das skizziert habe. Nur führst du dir noch ein Flag ein,> welches die Aussage erlaubt: können die Zeichen zu einem Kommando> gehören oder nicht. Wird ein '#' gesichtet, geht das Flag auf 1, kommt> der '\n' geht das Flag wieder (neben all dem anderen) auf 0. Die Zeichen> kommen nur dann in den Buffer rein, wenn das Flag auf 1 steht.
Gut ich werde als nächstes mal versuchen Deine Ratschläge umzusetzen.
Ich habe inzwischen noch etwas probiert.
1
c=uart_getc();
2
if(!(c&UART_NO_DATA))
3
{
4
5
if(c=='#')
6
{
7
uart_putc('*');
8
uart_putc(c);
9
uart_putc('*');
10
c=0;
11
12
c=uart_getc();
13
uart_putc('*');
14
uart_putc(c);
15
uart_putc('*');
16
while(c!='\r')
Sende ich mit dieser Variante folgendes #test erscheint mir am Terminal
*#***
Scheinbar liefert mir also uart_getc beim zweiten Mal kein Zeichen.
Daraufhin habe ich den Teil wie folgt geändert, also eine kleine
Verzögerung eingebaut.
1
c=uart_getc();
2
if(!(c&UART_NO_DATA))
3
{
4
5
if(c=='#')
6
{
7
uart_putc('*');
8
uart_putc(c);
9
uart_putc('*');
10
c=0;
11
_delay_ms(10);
12
c=uart_getc();
13
uart_putc('*');
14
uart_putc(c);
15
uart_putc('*');
16
while(c!='\r')
Dies liefert nun wie gewünscht *#**t*
Warum ist hier eine Verzögerung nötig?
Komisch ist dann auch dass in der while-Schleife keine Verzögerung mehr
nötig ist.
Also in diesem Teil.
1
while(c!='\n')
2
{
3
if(c!=UART_NO_DATA)
4
{
5
Befehlsbuffer[bufferzaehler++]=c;
6
c=uart_getc();
7
}
Meine Main sieht ja wie oben bereits geschrieben wie folgt aus.
1
do
2
{
3
Befehl();
4
5
uart_puts(Befehlsbuffer);
6
7
}while(1);
8
}
Als Gesamtausgabe am Terminal erhalte ich nun:
*#**t*test
Also wie gewünscht.
Ich verstehe nur nicht weshalb an dieser einen Stelle eine Verzögerung
nötig ist, hat da jemand einen Tipp?
Vielen Dank
Josef
Josef F. schrieb:> Scheinbar liefert mir also uart_getc beim zweiten Mal kein Zeichen.
Wie oft denn noch?
Dein µC ist mindestens um einen Faktor 1000 schneller als die UART
übertragen kann. Zwischen 2 Zeichen kann der µC sprichwörtlich auf einen
Kaffee gehen und eine Partie Schach spielen!
Du musst bei JEDEM uart_getc abfragen, ob das UART Modul UART_NO_DATA
liefert!
> Daraufhin habe ich den Teil wie folgt geändert, also eine kleine> Verzögerung eingebaut.
Was soll der Quatsch?
Die Fleury Routinen melden dir mit UART_NO_DATA ob etwas da ist oder
nicht.
Abfragen, Auswerten und darauf reagieren. Wenn nichts da ist, ist nichts
da.
Was soll ich den noch tun, ausser dir Code zu geben? Soll ich kommen und
ihn eintippen? Mach ich glatt.
Karl Heinz schrieb:> Wie oft denn noch?>> Dein µC ist mindestens um einen Faktor 1000 schneller als die UART> übertragen kann. Zwischen 2 Zeichen kann die UART sprichwörtlich auf> einen Kaffee gehen und eine Partie Schach spielen!>> Du musst bei JEDEM uart_getc abfragen, ob das UART Modul UART_NO_DATA> liefert!
Leider hat sich Deine letzte Antwort mit meiner letzten überschnitten,
so dass ich sie nicht mehr lesen konnte. Das tut mir sehr Leid ich
wollte Dich nicht mit meiner Unfähigkeit verärgern. Entschuldige dass
ich Deine Zeit vergeudet habe...
Karl Heinz schrieb:> Was soll ich den noch tun, ausser dir code zu geben? Soll ich kommen und> ihn eintippen? Mach ich glatt.
Dann könnte ich wenigstens sicher sein dass das funktioniert...
Josef :(
Josef F. schrieb:> Spricht eigentlich etwas dagegen in den Schleifen
Warum bist du so auf die Schleife erpicht?
Die braucht keiner!
Ganz im Gegenteil: du sollst dir solche Dinge abgewöhnen. Mit Schleifen
kommst du auf lange Sicht nicht weiter. Genau sie sind es, die dir bei
einem µC-Programm, welches mehrere Dinge 'gleichzeitig' machen soll, nur
Prügel zwischen die Beine werfen.
Mit deinem Ansatz kommst du nicht weiter, wenn dein µC gleichzeitig die
Temperatur überwachen soll, abhängig davon eine Heizung regeln muss und
nebenher auch noch auf Befehle hören soll. Geh weg von diesen
Warteschleifen! Der Ansatz in der µC Programmierung ist praktisch immer
der, dass man nacheinander alle Ereignisquellen abklappert, feststellt,
ob es etwas zu tun gibt und wenn ja, dann macht man das. Bei einer UART
ist das leicht. Denn was bedeutet denn 'etwas zu tun'? Es gibt genau
dann etwas zu tun, wenn an der UART ein Zeichen vorliegt. Dieses holt
man sich und entscheidet dann was man mit dem Zeichen machen möchte. Das
kann sein, dass man nichts damit macht, das kann auch sein, dass man es
in einen Textstring anhängt. Und dann gibt es noch den Fall, dass im
Textstring auf die Art ein komplettes Kommando entstanden ist. Dann wird
das augewertet. Aber was du nicht machen willst, das ist ständig in der
Schleife zu hocken und nur und ausschliesslich darauf zu warten, dass
der Befehl dann irgendwann komplett ist. Wenn du Pech hast, dann hat der
µC kurz zuvor im Aquarium die Heizung eingeschaltet, der µC wartet auf
das Ende eines Befehls und weil der Benutzer zwischendurch sich noch
einen Kaffee holt und aufs Klo muss, gibt es plötzlich Fischsuppe.
Das alles muss nicht sein. Die Technik dazu ist einfach und sie führt
NICHT darüber, dass man in Schleifen auf irgendwelche Ereignisse wartet.
Es gibt nur eine einzige Schleife (abgesehen von technisch notwendigen
kleineren Schleifen): und das ist die Hauptschleife in main. Abgesehen
von der gibt es keine Schleifen die auf irgendwas warten würden
(zumindest nicht wenn es länger dauert).
Alles was du in deinem Fall noch brauchst, ist eine kleine Steuerung,
dass ein Kommando erst nach einem '#' anfangen kann, weil da noch andere
Dinge dazwischen sind. Das ist doch nicht weiter schwer:
1
#define DEBUG
2
3
uint8_tinCommand;
4
uint8_tlineLen;
5
charlineBuff[20];
6
7
...
8
uint8_tgetLine(void)
9
{
10
uint16_tc=uart_getc();
11
12
if(c==UART_NO_DATA)
13
return0;
14
15
#ifdef DEBUG
16
uart_putc('*');
17
uart_putc(c);
18
uart_putc('*');
19
#endif
20
21
if(c=='\n'){
22
lineBuff[lineLen]='\0';
23
inCommand=0;
24
return1;
25
}
26
27
if(c=='#'){
28
inCommand=1;
29
return0;
30
}
31
32
if(inCommand&&lineLen<sizeof(lineBuff)-1){
33
lineBuff[lineLen]=c;
34
lineLen++;
35
36
#ifdef DEBUG
37
// zum debuggen, damit der String immer
38
// gültig ist.
39
lineBuff[lineLen]='\0';
40
uart_puts('\n');
41
uart_puts(lineBuff);
42
#endif
43
}
44
45
return0;
46
}
47
48
49
intmain()
50
{
51
...
52
53
inCommand=0;
54
while(1){
55
56
// hole das nächste Zeichen von der UART wenn es einen gibt
57
// und hänge es an ein potentielles Kommando hinten drann
58
// wenn dadurch eine Befehl vollständig geworden ist, dann liefert
59
// getLine TRUE ansonsten FALSE
60
61
if(getLine())
62
{
63
uart_puts("Kommando: *");
64
uart_puts(lineBuff);
65
uart_puts("*\n");
66
67
if(strcmp(lineBuff,"ON")==0){
68
// ON Kommando
69
}
70
71
elseif(strcmp(lineBuff,"OFF")==0){
72
// OFF Kommando
73
}
74
75
else
76
uart_puts("kenn ich nicht\n");
77
78
lineBuff[0]='\0';
79
lineLen=0;
80
}
81
82
....
83
}
fertig ist die Laube. Das ist so einfach, wie einem Baby den Schnuller
klauen. Ganz ohne komplizierte Schleifensteuerungen.