Hallo,
ich sitze nun schon 2 Tage über meinen Code und probiere und probiere
aber ich bekomm einfach den Compiler-Error nicht gelöst, nun suche ich
hier um Hilfe
1
//UART definieren
2
#ifndef F_CPU
3
#define F_CPU 16000000UL
4
#endif
5
#define UART_BAUD_RATE 38400
6
7
//Includes
8
#include<stdlib.h>
9
#include<avr/io.h>
10
#include<avr/interrupt.h>
11
12
#include<util/delay.h>
13
14
#include"uart.h"
15
#include<avr/pgmspace.h>
16
17
//PORTS definieren
18
#define LED1 (1 << PE3)
19
#define LED2 (1 << PE4)
20
#define LED3 (1 << PE5)
21
22
#define OUT1 (1 << PC7)
23
#define OUT2 (1 << PC6)
24
#define OUT3 (1 << PC5)
25
#define OUT4 (1 << PC4)
26
#define OUT5 (1 << PC3)
27
#define OUT6 (1 << PC2)
28
#define OUT7 (1 << PC1)
29
#define OUT8 (1 << PC0)
30
31
#define OUT9 (1 << PD7)
32
#define OUT10 (1 << PD6)
33
#define OUT11 (1 << PD5)
34
#define OUT12 (1 << PD4)
35
36
//Zeichen empfangen
37
unsignedcharuart_get(void)
38
{
39
intc;
40
inti=0;
41
unsignedcharstring[10];
42
43
do
44
{
45
c=uart_getc();
46
if(c&UART_NO_DATA)
47
{
48
/*
49
* no data available from UART
50
*/
51
}
52
else
53
{
54
/*
55
* new data available from UART
56
* check for Frame or Overrun error
57
*/
58
if(c&UART_FRAME_ERROR)
59
{
60
/* Framing Error detected, i.e no stop bit detected */
61
uart_puts_P("UART Frame Error: ");
62
}
63
if(c&UART_OVERRUN_ERROR)
64
{
65
/*
66
* Overrun, a character already present in the UART UDR register was
67
* not read by the interrupt handler before the next character arrived,
68
* one or more received characters have been dropped
69
*/
70
uart_puts_P("UART Overrun Error: ");
71
}
72
if(c&UART_BUFFER_OVERFLOW)
73
{
74
/*
75
* We are not reading the receive buffer fast enough,
76
* one or more received character have been dropped
../Visuschni_v2.c:129: error: incompatible types in assignment
5
make: *** [Visuschni_v2.o] Error 1
6
Build failed with 1 errors and 0 warnings...
Alles unter AVRStudio4, somit AVR GCC
Ich hoffe ihr könnt mir helfen und vlt noch ein paar Programmiertipps
geben, ich arbeite mich erst wieder so richtig ein, nach längerer
Abstinenz
Für mich heißt der Fehler das mir die Funktion einen Wert bringt, den
die Variable EING nicht aufnehmen kann. Ich versteh es aber nicht da es
überall ein "unsigned char" ist.
mfg
Es wäre zweckmäßig den ganzen Code zu posten (vorzugsweise als Anhang),
dann würde auch die Zeilenangabe in der Fehlermeldung passen..
Hier ist ein Auszug wo der Compiler einen error wirft (Zeile 120..):
106> unsigned char EING[10];
..
120> EING = uart_get();
..wundert micht nicht dass ihm das nicht gefällt. Mach mal einen Index
dran an das EING :-)
Markus B. schrieb:> Für mich heißt der Fehler das mir die Funktion einen Wert bringt, den> die Variable EING nicht aufnehmen kann. Ich versteh es aber nicht da es> überall ein "unsigned char" ist.
Nein, EING ist ein Array von "unsigned char". Kein Wunder, dass der
Compiler sich da beschwert, Arrays können in C nicht zugewiesen werden,
und schon garnicht mit einem Typ der einem einzelnen Element entspricht.
Folgendes würde gehen:
1
EING[0]=uart_get();
Kleine Anmerkung am Rande: üblicherweise werden in C nur Makros komplett
in Großbuchstaben benannt. Für Variablen ist es genauso wie bei
Funktionen üblich, diese in Kleinbuchstaben zu benennen, oder ggf. auch
in Mixed Case ("Eingabe"). Künstliche Abkürzungen wie "EING" sollte man
auch eher vermeiden.
Andreas
Ich wollte ja eben in der Funktion den ganzen String wieder zurückgeben,
aber als C-String?
Und OK wusste da war was mit den Großschreiben, werd ich dann wohl
wieder ändern
Ansonsten macht mir das immer noch einen etwas unausgegorenen Eindruck.
Du hast in uart_get(void) ein lokales Feld string[10].
Erstens ist das nur ein lokales Feld, und nach dem Ende der
Funktion verloren. Trotzdem speicherst du munter mehrere Werte drin,
und lieferst den untersten an den Aufrufer zurück.
Willst du wirklich den Rest wegwerfen?
Zweitens spicherst du bis zu 10 Zeichen darin, und hängst dann noch eine
abschließende 0 an.
Das wird etweas knapp so rein platzmäßig; Reise nach Jerusalem.
Wolltest du vielleicht stattdessen den ganzen String liefern?
Das geht aber anders, und schon gar nicht mit einem lokalen Feld.
Ja genau das wollte ich eben
Hatte es davor immer in der Main direkt eingebaut und wollte es nun eben
extrahieren als einzelne Funktion, daran scheitere ich aber und verstehe
nicht wieso
Markus B. schrieb:> Es ist ja nicht so das ich das nicht schon könnte...
Hm.
Einen String als lokale (automatische) Variable zusammen zu
schrauben und zurückgeben zu wollen, deutet nicht allzu
stringent darauf hin :-)
Eine klare Vorstellung von Zeigern und der Lebensdauer von
Variablen sollte man in C schon haben, sonst kommt man nicht weit.
Literaturhinweis:
Brian Kernighan & Dennis Ritchie, "Programmieren in C", 2. Auflage,
Hanser-Verlag.
Auch wenn dieses Buch mittlerweile 20 Jahre alt ist, ist es immer noch
verdammt lesenswert.
ok, jetzt bin ich satt und gut gelaunt.
Dann fangen wir mal an..
Schon aus der Entfernung: Du bist dir nicht ganz im Klaren, was du
willst.
Was soll die Funktion uart_get() denn liefern?
Einen String? Oder ein Zeichen?
Das kann man erstens mit einem vernünftigen Namen klarmachen, und
zweitens natürlich entsprechend programmieren.
Ich vermute, die Funktion soll mehrere Zeichen lesen, und einen
kompletten String liefern. Dann ist der Name uart_get() zu
schlapp. Besser wäre gleich zu sagen, was sie liefert. Da du bei einem
\n abbrichst zu lesen, geht es um Zeilen.
Dann würde ich uart_get_line() oder sowas ähnliches als Name
vorschlagen.
Als Kommentar steht bei dir drüber: //Zeichen empfangen
Das passt natürlich nicht zu dem, was die Funktion macht.
Vorschlag:
1
// liest eine Zeile (maximal ... Zeichen) und liefert sie als
2
// nullterminierten String zurück:
3
char*uart_get_line()
4
{
5
...
6
}
Für einen nullterminierten C-String ist char* der passende
Rückgabetyp.
char wäre der passende Typ, wenn nur ein Zeichen zurück kommen
sollte.
Das unsigned sollte man weglassen, wenn es nicht einen guten Grund
dafür gibt. Du willst mit den Zeichen wohl nicht rechnen; warum
sollten sie also signed oder unsigned sein? Nur char ohne etwas
davor ist letztlich signed char oder unsigned char, aber es sollte dir
egal sein - also nicht festlegen.
Das void in der Parameterliste einer Funktion, die keine Parameter
bekommt, ist veraltet und inzwischen überflüssig.
Nächstes Problem: ein String benötigt Platz.
Als Programmierer muß man sich in C Gedanken machen, wo dieser Platz
herkommen soll.
Bisher würde das bei dir so aussehen:
1
// liest eine Zeile (maximal 9 Zeichen) und liefert sie als
2
// nullterminierten String zurück:
3
char*uart_get_line()
4
{
5
charstring[10];
6
for(...)
7
{
8
...
9
string[i]=...;
10
...
11
}
12
string[i]='\0';
13
returnstring;// nicht *string
14
}
Das ist jetzt schon etwas besser, aber immer noch falsch.
Gebessert hat sich ja schon der Rückgabetyp; dazu passt das return
string statt return *string.
Mit *string würdest ja ein char liefern, und zwar das unterste in
string, mithin string[0].
Du willst ja aber nicht das eine Zeichen liefern, sondern den ganzen
String. Und das geschieht in C, indem man einen Zeiger auf das erste
Zeichen nimmt. Das ist string.
Falsch (und zwar richtig falsch!) ist es aus folgendem Grund:
string ist eine automatische Variable (alle lokalen Variablen in einer
Funktion oder in einem Block sind automatisch, wenn nicht static davor
steht).
Eine automatische Variable existiert aber nur, solange der umgebende
Block (hier die Funktion) abgearbeitet wird.
Sofort danach kann der Speicherplatz für etwas anderes genutzt werden,
meist für Parameter einer aufgerufenen Funktion, oder deren lokale
Variablen, oder Rücksprungadressen, temporäre Zwischenwerte oder was
auch immer.
Normalerweise ist das in Ordnung, weil man ja auf eine Variable mit
ihrem Namen nur zugreifen kann, solange man in demselben Block ist.
1
{
2
inti=12;
3
i+=12;
4
}
5
printf("%d",i);// geht nicht
So etwas verhindert der Compiler aus gutem Grund: i ist eine Variable
in dem Block mit den geschweiften Klammern und existiert nur solange
der Block abgearbeitet wird.
Das printf() steht außerhalb, hier lässt der Compiler die Verwendung
von i nicht mehr zu - die Variable existiert dann auch gar nicht mehr.
Ändert man das etwas ab, hat man den Compiler überlistet:
1
int*zeiger_auf_i;
2
{
3
inti=12;
4
i+=12;
5
zeiger_auf_i=&i;
6
}
7
printf("%d",*zeiger_auf_i);
Jetzt macht man im Prinzp dasselbe wie eben, nur etwas umständlicher.
In *zeiger_auf_i merkt man sich die Adresse von i, und im printf()
benutzt man die Adresse um auf i zuzugreifen.
Jetzt verhindert Compiler das nicht.
Es ist aber noch genauso falsch: wenn das printf() läuft, wird über
den Zeiger auf i zugegriffen, obwohl i gar nicht mehr existiert
bzw. möglicherweise schon lange für etwas anderes benutzt wird.
Mit solchen Konstruktionen kann man sich in C schön selbst verarschen,
wenn man nicht weiß was man tut.
Im Prinzip etwas ähnliches macht man mit dem obigen falschen Beispiel:
1
// liest eine Zeile (maximal ... Zeichen) und liefert sie als
2
// nullterminierten String zurück:
3
char*uart_get_line()
4
{
5
charstring[10];
6
for(...)
7
{
8
...
9
string[i]=...;
10
...
11
}
12
string[i]='\0';
13
returnstring;// nicht *string
14
}
15
16
...
17
char*meinezeile;
18
meinezeile=uart_get_line();
19
printf("gelesen: %s",meinezeile);
20
...
(Es soll jetzt mal egal sein, daß es ein printf() in dieser Form gar
nicht auf einem AVR gibt; es geht nur darum, den String irgendwie zu
verwenden.)
Was passiert bei diesem Beispiel?
in uart_get_line() gibt es eine lokale (automatische) Variable string;
das ist ein Feld mit 10 Zeichen.
Beim Rücksprung wird die Adresse des ersten Elements zurückgegeben und
beim Aufrufer in meinezeile gespeichert.
meinezeile ist also ein Zeiger auf das erste Zeichen von string,
obwohl die 10 Zeichen von string vielleicht schon für etwas anderes
verwendet werden.
Bei soetwas muß man in C ziemlich nervös werden!
So ist es jedenfalls Murks.
Es gibt mehrere Möglichkeiten, das zu verbessern,
Leider sind sie alle nicht richtig schick, jede Variante ist irgendwie
etwas doof.
Erste Möglichkeit: Den String static machen, dann ist es keine
automatische Variable mehr:
1
// liest eine Zeile (maximal ... Zeichen) und liefert sie als
2
// nullterminierten String zurück:
3
char*uart_get_line()
4
{
5
staticcharstring[10];
6
for(...)
7
{
8
...
9
string[i]=...;
10
...
11
}
12
string[i]='\0';
13
returnstring;// nicht *string
14
}
15
16
...
17
char*meinezeile;
18
meinezeile=uart_get_line();
19
printf("gelesen: %s",meinezeile);
20
...
Damit klappt es auf einmal wundersamerweise.
Eine static-Variable wird nicht immer neu angelegt und wieder
wegegworfen, sondern existiert einmal von Programmstart bis
Programmende.
Aber wo ist dabei der Haken?
Daß sie eben nur einmal existiert.
1
...
2
char*meinezeile1;
3
char*meinezeile2;
4
meinezeile1=uart_get_line();
5
printf("gelesen1: %s",meinezeile1);// gibte erste Zeile aus
6
meinezeile2=uart_get_line();
7
printf("gelesen2: %s",meinezeile2);// gibte zweite Zeile aus
8
9
printf("gelesen1: %s",meinezeile1);// gibte zweite Zeile aus (!)
10
printf("gelesen2: %s",meinezeile2);// gibte zweite Zeile aus
11
...
In diesem Beispiel sieht es erst vernünftig aus: mit meinezeile1 wird
die erste gelesene Zeile ausgegeben, mit meinezeile2 dann die zweite.
Mit der folgenden Ausgabe wird sowohl für meinezeile1 als auch für
meinezeile2 nur noch die letzte gelesene Zeile (also die zweite)
ausgegeben, weil in der static-Variable string ja mit der zweiten
Zeile die erste überschrieben wurde.
Das wird der Aufrufer so wahrscheinlich nicht erwarten; das ist also
doch etwas problematisch.
Richtig krank werden static-Variablen bei Programmen, die mit mehreren
Threads arbeiten, ebenso wie bei rekursiven Funktionsaufrufen.
Leider sind mehrere Funktionen der Standard-Lib von C mit statischen
Variablen gebaut und deshalb nicht benutzbar in
multitasking-Programmen (strtok() z.B. fällt auf die Klappe).
Das wäre in deinem Beispiel aber kein Problem, also würde es mit
static erstmal gehen.
Eine andere Lösung wäre: in der Funktion wird für den gelesenen String
Speicher allokiert und der Zeiger darauf zurückgegeben:
1
// liest eine Zeile (maximal ... Zeichen) und liefert sie als
2
// nullterminierten String zurück:
3
char*uart_get_line()
4
{
5
char*string=malloc(genugzeichen....);
6
for(...)
7
{
8
...
9
string[i]=...;
10
...
11
}
12
string[i]='\0';
13
returnstring;
14
}
15
16
...
17
char*meinezeile;
18
meinezeile=uart_get_line();
19
printf("gelesen: %s",meinezeile);
20
free(meinezeile);// Speicher nach letzter Benutzung freigeben
21
...
Das würde jetzt auch bei Multitasking funktionieren ebenso wie bei
Rekursion.
Und wo ist hier der Haken?
In der Funktion wird Speicher allokiert, und erst der Aufrufer kann den
Speicher freigeben - wenn er es nicht vergisst.
Leider wird es mehr oder weniger häufig vergessen, weswegen es als
höchst unelegant gilt, in einer Funktion Speicher zu allokieren, der
vom Aufrufer wieder freigegeben werden muß.
Auf einem Controller wird man diese Lösung auch nicht gerne sehen,
weil es da wegen des knappen Speichers selten ratsam ist, dynamische
Speicherverwaltung mit malloc() zu nutzen.
Ich hatte hier ja auch die Fehlerbehandlung unterschlagen: Was soll
passieren, wenn gar kein Speicher allokiert werden kann? Auf einem
Controller kann man ja nicht einfach eine kurze Meldunbg ausgeben und
das Programm beenden. Ein Absturz wird auch nicht gern gesehen und
nicht so leicht akzeptiert wie unter Windows.
Nächste Möglichkeit: Der Aufrufer selbst beschafft den Platz, übergibt
ihn und die Funktion schreibt nur rein:
1
// liest eine Zeile (maximal ... Zeichen) und liefert sie als
2
// nullterminierten String zurück:
3
char*uart_get_line(char*puffer,size_tl_puffer)
4
{
5
for(...&&i<l_puffer-1)
6
{
7
...
8
string[i]=...;
9
...
10
}
11
string[i]='\0';
12
returnstring;
13
}
14
15
...
16
charmeinezeile[10];
17
uart_get_line(meinezeile,10);
18
printf("gelesen: %s",meinezeile);
19
...
Weil die Funktion nicht feststellen kann, wie groß der Puffer ist, muß
der Aufrufer auch gleich die Länge übergeben.
Das hat nebenbei den Vorteil, daß man in der Funktion nicht mehr
spekulieren muß, wieviel Platz nötig (wie kommst du gerade auf die 10?
Woher soll man das in der Funktion wissen?)
Die Funktion kann also je nach Situation mal mit einem großen oder mal
mit einem kleinen Puffer aufgerufen werden.
Nachteil dieser Lösung ist, daß der Aufruf etwas umständlich ist (2
Parameter mehr).
Man hat also mehrere Möglichkeiten, das zu lösen.
Eleganter geht es dann erst in C++, aber das geht jetzt zu weit.
Als "bisschen Hilfe" sollte das reichen.
Klaus Wachtler schrieb:> Als "bisschen Hilfe" sollte das reichen.
Erstklassig ausgeführt, Klaus! Genau dieselben Gedankengänge gehe ich
durch, wenn ich versuche, jemandem C bzw. Stringverarbeitung in C
beizubringen. Aber ich hätte mir niemals die Mühe gemacht, es in einem
Forum so detailiert rüberzubringen, wo ich schon Hopfen und Malz
aufgegeben habe. Kompliment!
> ok, jetzt bin ich satt und gut gelaunt.
Daran muss es gelegen haben ;-)
Frank
Klaus Wachtler schrieb:> Man hat also mehrere Möglichkeiten, das zu lösen.
Schöne Zusammenfassung.
Hast du etwas dagegen, wenn ich mir den größten Teil davon für die FAQ
(direkt nach dem Kapitel Stringverarbeitung) klaue?
Hallo,
ersteinmal danke für die erstklassige Erklärung, hat mir einiges näher
gebracht vom Verständnis her, aber ein bischen was wusste ich auch schon
;)
1
// liest eine Zeile (maximal ... Zeichen) und liefert sie als
2
// nullterminierten String zurück:
3
char*uart_get_line(char*puffer,size_tl_puffer)
4
{
5
for(...&&i<l_puffer-1)
6
{
7
...
8
string[i]=...;//Puffer?
9
...
10
}
11
string[i]='\0';//Puffer?
12
returnstring;//Puffer?
13
}
14
15
...
16
charmeinezeile[10];
17
uart_get_line(meinezeile,10);
18
printf("gelesen: %s",meinezeile);
19
...
Müsste man dort nicht auch dann "puffer" schreiben? Sonst hab ich doch
wieder die unbekannte Variable "string"#
Was ich aber nicht verstehe, warum muss ich den Rückgabewert der
Funktion nicht auffangen? Wird die Variable durch den Zeiger im Aufruf
direkt bearbeitet?
Habe aber schon einiges von der Form verbessert, wie das auskommentieren
und die Namen der Variablen und Funktion
mfg Rusticus
Ich hab mich mit dem Thema Pointer jetzt nochmal befasst und habs nun
verstanden, das ich da nichts mehr auffangen muss. Ich lieg jetzt
richtig der Annahme das ich ja keine neue Variable erzeuge sondern meine
Variable aus dem Main-Teil bearbeite und ich deshalb nichts mehr
auffangen muss?
Mein Problem ist jetzt folgendes:
Markus B. schrieb:> Müsste man dort nicht auch dann "puffer" schreiben? Sonst hab ich doch> wieder die unbekannte Variable "string"#
Ja.
Nimms als Tippfehler, der bei direktem Eintippen von Code in einem Forum
schon mal passiert.
> Was ich aber nicht verstehe, warum muss ich den Rückgabewert der> Funktion nicht auffangen?
Weil die Funktion ja sowieso einen Pointer bekommt und damit das Array
des Aufrufers direkt manipuliert.
> Wird die Variable durch den Zeiger im Aufruf> direkt bearbeitet?
Ja.
Sagtest du nicht, du hättest schon mal C programmiert?
Markus B. schrieb:> Ich nutze die normale UART-Libary von Peter Fleury und so was in der> Richtung hatte ich noch nicht damit
Dann sieh dir die erste Meldung an
../uart.c:216:3: error: #error "no UART definition for MCU available"
Peter war so freundlich und hat den COmpiler eine Fehlermeldung ausgeben
lassen, wenn du eine MCU (also einen Prozessor) benutzt, den er nicht
kennt und von dem er daher auch nicht weiß, welche Bits in welchem
Register zu setzen sind, etc.
Karl heinz Buchegger schrieb:>> Müsste man dort nicht auch dann "puffer" schreiben? Sonst hab ich doch>> wieder die unbekannte Variable "string"#>> Ja.
Sorry, war natürlich mein Fehler.
Denkt euch davor noch ein #define puffer string, dann klappt es :-)
Karl heinz Buchegger schrieb:> Hast du etwas dagegen, wenn ich mir den größten Teil davon für die FAQ> (direkt nach dem Kapitel Stringverarbeitung) klaue?
nee, natürlich nicht.
Auch wenn ich vermute, daß es die meisten eh nicht lesen, sondern gleich
wieder fragen :-(
Klaus Wachtler schrieb:> Karl heinz Buchegger schrieb:>> Hast du etwas dagegen, wenn ich mir den größten Teil davon für die FAQ>> (direkt nach dem Kapitel Stringverarbeitung) klaue?>> nee, natürlich nicht.> Auch wenn ich vermute, daß es die meisten eh nicht lesen, sondern gleich> wieder fragen :-(
LOL
Aber zumindest hat man dann mit einem Link eine erschöpfende Auskunft
gegeben.
> Müsste man dort nicht auch dann "puffer" schreiben? Sonst hab ich doch> wieder die unbekannte Variable "string"#
Ich würde das Interface der Funktion noch etwas ändern, nämlich dass
uart_get_line nicht den string/puffer zusätzlich als return zurückgibt,
sondern besser die Anzahl der eingelesenen Zeichen. Dann ist das
Interface weitgehend kompatibel mit der
UNIX/Linux-Standard-System-Funktion read() - bis auf den Filedescriptor
fd, den man hier nicht braucht.
Gruß,
Frank
Kann durchaus auch sinnvoll sein.
So wie von mir ist die Funktion halt kompatibel zu strcpy()/strcat()
und man kann sie direkt zum Weiterverwenden des Strings nutzen.
Je nachdem, was man braucht.
Genau genommen hast du recht, weil man sonst nie erfährt, ob der
der String gereicht hat.
Die Weiterverwendung des String könnte man auch mit einem
Kommaoperator erreichen, also gebe ich klein bei.
Kann man nicht neben dem Textfeld zum Antworten gleich
eine Auswahlbox installieren mit den Top-Ten-Antworten:
- K&R lesen
- K&R nochmal lesen
- AVR-Tutorial lesen
- AVR-gcc-Tutorial lesen
- Strings zurückgeben
- für LCD mit 44780 gibt es fertigen Quelltext
- google
- Hausaufgaben selber machen
- nur verständlichen Quelltext posten, Formatierung nutzen ...
- ...
Jeweils mit einem kleinen Link.
Dankeschön für die Hilfe, muss ich mich halt im Internet umsehen oder
versuchen selbst zu portieren, um es auf meinen µC zum laufen zu
bekommen
Und zum Pointer muss ich gestehen, das ich mich da nach wie vor sehr
schwer tue das zu verstehen, obwohl ich glaub ich jetzt ein gutes Stück
weiter bin!
Noch ne Frage zur Stringlänge, wenn ich zu wenig Buffer übergebe, also
zu wenig Zeichen, erfahre ich dann überhaupt effektiv wie lang es war?
Weil ein übergelaufenes Array gibt mir doch sicher nicht die richtige
Anzahl der Zeichen zurück oder?
Und wie sollte man die Länge am besten auslesen? Mit der strlen-Funktion
( http://www.cplusplus.com/reference/clibrary/cstring/strlen/) ?
Markus B. schrieb:> Noch ne Frage zur Stringlänge, wenn ich zu wenig Buffer übergebe, also> zu wenig Zeichen, erfahre ich dann überhaupt effektiv wie lang es war?
Nein.
Deshalb übergibt man die Länge ja mit, eben weil die Funktion keine
Chance hat das selber rauszufinden
> Weil ein übergelaufenes Array gibt mir doch sicher nicht die richtige> Anzahl der Zeichen zurück oder?
Wenn dir das Array überläuft, dann ist das schlecht. Sehr schlecht
http://www.mikrocontroller.net/articles/FAQ#Beispiele_2> Und wie sollte man die Länge am besten auslesen? Mit der strlen-Funktion> ( http://www.cplusplus.com/reference/clibrary/cstring/strlen/) ?
entweder so oder die Funktion liefert anstelle des Pointers die Länge
des eingelesenen Strings. Die Funktion kennt die ja, wenn sie den String
aufbaut. Also kann sie den Wert auch zurückgeben anstelle eines
Pointers, den im Prinzip kein Mensch braucht, weil er ihn schon hat :-)
OK dann versteh ich den Sinn nicht ganz
Was bringt es mir zu wissen wie viele Zeichen übertragen wurden, wenn
das nur dann klappt, wenn alles gut läuft, wäre es nicht interesanter zu
sehen das er überläuft? Und dann dementsprechend was dagegen zu machen?
In der Funktion sollte man sicherstellen, daß es keinen Überlauf gibt.
In deiner Originalversion hattest du ja schon vorgesehen. höchstens 10
Zeichen abzuspeichern. Das ist im Prinzip in Ordnung (außer, daß du dich
verzählt hattest und 10 Zeichen plus abschließender Null in einem
10 Zeichen großen Feld halt nicht passen, sondern nur 9 plus die Null).
So ähnlich würde man das in der Funktion wieder machen.
Die Funktion könnte jetzt die Anzahl der gelesenen Zeichen
zurückliefern. Wenn das 10 ist, könnte der Aufrufer (falls er es
kontrollieren will) an der letzten Stelle (vor der Null) nachsehen,
ob da der Zeilenvorschub steht; dann ist alles in Ordnung.
Wenn da etwas anderes steht, war der Puffer zu klein.
Einen Überlauf gab es trotzdem nicht, weil ja vorher mit dem
Übertragen abgebrochen wurde.
Es liegt jedenfalls dann am Aufrufer, entsprechend zu reagieren.
Zum Beispiel könnte er es mit einem größeren Feld nochmal versuchen,
oder erst den bisherigen Teil verarbeiten und sich anschließend das
nächste Stück holen, oder wie auch immer.
Die Idee ist wieder eine saubere Trennung der Zuständigkeiten:
Die Funktion soll maximal die angegebene Anzahl Zeichen holen,
mehr kann sie nicht tun.
Wenn der Platz nicht reicht, kann der Aufrufer (und nur er)
sinnvoll darauf reagieren.
Also wenn ich das mal so rekapitulieren darf
if (strlen(uart_get_line(...))==10 && Eing[9] != '\0')
{
==> Fehlermeldung ausgeben
==> Erneut senden
==> etwas anderes ähnliches tun
}
Was wären dann sinnvolle Optionen? Ich überlege so etwas einzubaun
Für mich macht im moment am meisten Sinn, der Gegenstelle mitzuteilen
das der Buffer vollgelaufen ist und dieser die Option neu senden soll
Markus B. schrieb:> OK dann versteh ich den Sinn nicht ganz>> Was bringt es mir zu wissen wie viele Zeichen übertragen wurden,
nicht 'übertragen wurden'!
Die Frage ist: wieviele Zeichen kann deine Empfangsfunktion empfangen,
ehe es zu einem Überlauf kommt!
Kein Mensch sagt, dass du den Buffer voll machen musst. Aber du darfst
auf keinen Fall hinten rausschreiben!
Markus B. schrieb:> Was wären dann sinnvolle Optionen?
Mit der Gegenstelle einen Pakt schliessen.
Maximal 80 Zeichen pro Zeile und nicht mehr
Wenn du das nicht kannst, dann muss der Aufrufer der Funktion
entsprechende Mechanismen einbauen, um zb 2 Teilergebnisse zu einem
größeren zusammenzusetzen. Wobei sich dann allerdings die Frage erhebt,
warum er nicht gleich der Funktion einen größeren BUffer gegeben hat.
Markus B. schrieb:> Also wenn ich das mal so rekapitulieren darf>> if (strlen(uart_get_line(...))==10 && Eing[9] != '\0')> {> ==> Fehlermeldung ausgeben> ==> Erneut senden> ==> etwas anderes ähnliches tun> }
so ähnlich, ja.
Allerdings kostet das strlen() schon wieder Rechenzeit, weil es
den String entlanglaufen muß.
Deshalb schon seit einiger Zeit der Vorschlag, daß die Funktion
(z.B. uart_get_line()) als Rückgabewert gleich diesen Wert liefert,
in der Funktion fällt er ja als Nebenprodukt an.
Dann braucht der Aufrufer nicht mehr zählen (lassen), sondern nimmt
gleich den Rückgabewert.
>>> Was wären dann sinnvolle Optionen? Ich überlege so etwas einzubaun
Da ist deine Phantasie gefragt, nur du kennst deine Aufgabenstellung.
>> Für mich macht im moment am meisten Sinn, der Gegenstelle mitzuteilen> das der Buffer vollgelaufen ist und dieser die Option neu senden soll
Hört sich plausibel an.
Auf jeden Fall sollte der Sender informiert werden, daß sein
Auftrag/Befehl/etc. nicht ausgeführt werden kann.
Klaus Wachtler schrieb:>> Für mich macht im moment am meisten Sinn, der Gegenstelle mitzuteilen>> das der Buffer vollgelaufen ist und dieser die Option neu senden soll>> Hört sich plausibel an.> Auf jeden Fall sollte der Sender informiert werden, daß sein> Auftrag/Befehl/etc. nicht ausgeführt werden kann.
Wobei das auch nicht der Weisheit letzter Schluss ist.
Wenn die Gegenstelle ein Mensch ist, dann geht das ja noch an.
Aber wenn das ein Programm ist, was soll denn dieses Programm dann tun?
Es will/muss etwas übertragen und kann es aufgrund Platz-Beschränkungen
des Empfängers nicht machen. Bei so ziemlich jeder Strategie des Senders
(ausser den Fehler an den Benutzer zu melden und die Arbeit
einzustellen) kann ich sofort mit der Frage kontern: Warum hast du es
nicht gleich so gemacht anstatt es auf einen Fehler ankommen zu lassen?
"Fehler melden und Arbeit einstellen" ist zwar aus theoretischer Sicht
die einzig sinnvolle Möglichkeit. Aus Benutzersicht, der das System
eigenständig ohne ständige Überwachung laufen lassen will, ist das aber
völlig inakzeptabel.
Kann sein, muß nicht.
Wenn ich auf dem PC ein Programm habe, muß ich sehr wohl
irgendwie mit Fehlern umgehen können (Gerät nicht an? Kabel
fehlt? Schnittstelle nicht ansprechbar?)
Es gibt nun mal Situationen, wo man die Funktion nicht mehr
erzwingen kann.
Dazu kann auch gehören, daß man ein Kommando schickt, das eine
Gegenseite mit einem aktuellen SW-Stand verstehen würde, es hängt
aber ein altes Gerät ohne Update dran. Mit solchen Fehlern muß
man irgendwie umgehen.
Eigentlich ist alles Spekulation, solange keiner weiß, um was
es hier wirklich geht.
Klaus Wachtler schrieb:> Eigentlich ist alles Spekulation,
Yep.
Ich hab mich jetzt nur auf den Fall konzentriert, dass der PC eine Zeile
mit 80 Zeichen schickt, der der µC nicht annehmen kann, weil der
Zeilenbuffer nur 40 Zeilen lang ist.
Was soll der PC denn dann machen? Selbst wenn er eine Fehlermeldung
zurückbekommt: Seine Daten sind nun mal 80 Zeichen lang und die müssen
übertragen werden, da hilft auch keine Fehlermeldung (ausser in der
Entwicklungsphase, dann weiß man das man hier ändern muss).
Wenn PC-seitig eine Strategie existiert, wie man im Fehlerfall diese 80
Zeichen in mehrere Teile aufteilt, die einzeln übertragen werden, dann
hätte man diese Strategie auch gleich von vorne herein benutzen können
und es wäre gar nicht zum Buffer-Overflow Problem auf der Empfängerseite
gekommen.
Das war die Überlegung. Und daher bin ich der Meinung, dass eine
derartige Fehlermeldung, ausser während der Entwicklungszeit der
Programme, nur von beschränktem Nutzen ist (*). Die Idee "Da meldet der
Empfänger bei Bufferoverflow einen Fehler und dann muss der Sender das
eben anders schicken" ist IMHO sehr zweifelhaft in ihrer
Durchführbarkeit.
(*) was nicht heißen soll, dass ich dieses Problem nicht dem Benutzer
melden würde. Würde ich schon. Nur würde ich keinen Gedanken an eine
Lösung ala "Wie kann ich die Übertragung in diesem Fehlerfall dann doch
machen?" verschwenden.
Das hier
> Für mich macht im moment am meisten Sinn, der Gegenstelle> mitzuteilen das der Buffer vollgelaufen ist und dieser die> Option neu senden soll
ist IMHO nicht machbar :-)
Nur zu melden, daß was schiefläuft, ist keine Lösung.
Man muß dem Sender schon irgendwie mitteilen, wie er das lösen kann.
Ich hab z.B. in meinem Bootloaderprotokoll ne Funktion, mit der der
Sender die Puffergröße abfragt und dann kann er die Daten in
entsprechende Pakete unterteilen.
Beim ATtiny13 ist der Puffer sehr klein und beim ATmega1284 riesengroß.
Peter
Karl heinz Buchegger schrieb:> Das war die Überlegung. Und daher bin ich der Meinung, dass eine>> derartige Fehlermeldung, ausser während der Entwicklungszeit der>> Programme, nur von beschränktem Nutzen ist (*). Die Idee "Da meldet der>> Empfänger bei Bufferoverflow einen Fehler und dann muss der Sender das>> eben anders schicken" ist IMHO sehr zweifelhaft in ihrer>> Durchführbarkeit.
Das sehe ich genauso, deshalb war auch die Frage nach einer sinnvollen
Option.
Aber mir ist noch was aufgefallen und hoffe mal es stimmt:
1
[...]
2
3
string[i]=c
4
i++
5
6
}while(c!='n'&&i<l-string-1);//Ende von do-while-Schleife
7
string[i]='\0'
So steht es bei mir im Code, nun folgendes:
Sieht mein String durch die do-while-Schleife am Schluss nicht so aus
[..][x][y][z]['\n']['0'] ??
Markus B. schrieb:> Das sehe ich genauso, deshalb war auch die Frage nach einer sinnvollen> Option.
sinnvolle Option (da du ja Textübertragung in Zeilenform machst) sind
(ohne Anspruch auf Vollständigkeit):
* Beide Partner sind sich einig, dass eine Zeile nicht länger
als x Zeichen sein darf. Alle Kommandos, jegliche Übertragung
mussen in 1 Zeile passen.
* Bei der Protokollerstellung ist darauf Rücksicht genommen worden,
indem man darauf achtet, dass längere Daten in mehreren Teilen
übertragen werden, wobei jeder Teil kleiner als der Buffer ist
(zb ein kompletter Zeitpunkt wird übertragen indem Datum und Uhrzeit
getrennt übertragen werden)
* Im Empfänger Aufwand getrieben wird, dass der Empfänger auch
bisher nur teilweise übertragene Datensätze zumindest teilweise
auswerten kann, so dass die Operation empfängerseitig in
mehreren Schüben erfolgen kann.
Nachteilig: Das schöne Feature "man kann eine Übertragung als
ganzes auf Fehler untersuchen und die Änderung wird erst dann
durchgeführt, wenn gesichert ist, das die Übertragung korrekt war"
geht dadurch verloren bzw. ist nicht mehr so einfach möglich.
Ausserdem kann es dann passieren, dass plötzlich das Thema Handshake
interessant wird.
>
1
>[...]
2
>
3
>string[i]=c
4
>i++
5
>
6
>}while(c!='n'&&i<l-string-1);//Ende von do-while-Schleife
7
>string[i]='\0'
8
>
>> So steht es bei mir im Code, nun folgendes:> Sieht mein String durch die do-while-Schleife am Schluss nicht so aus> [..][x][y][z]['\n']['0'] ??
Das kommt drauf an, was sonst noch in der Schleife enthalten ist :-)
1
....
2
3
if(c!='\n'){
4
string[i]=c;
5
i++;
6
}
7
}while(c!='n'&&i<l-string-1);
8
string[i]='\0';
jetzt ist der '\n' nicht mehr Teil des Strings :-)
Eine Lösung für das Problem, mit längeren Strings vom PC zum µC mit
kleinerem Puffer, gibt es doch schon seid einigen Jahren!
Man benötigt lediglich 2 weitere Pins am µC die man dann mit RTS/CTS
bezeichnet. Na klingelts?
RTS = REQUEST TO SEND (Hallo ich möchte was senden!)
CTS = CLEAR TO SEND (Jupp schick rüber!)
Manche Terminals unterbrechen das senden auch wenn plötzlich CTS auf LOW
geht, ansonsten kann man sich ja selbst nen Terminal basteln.
Markus B. schrieb:> So steht es bei mir im Code, nun folgendes:> Sieht mein String durch die do-while-Schleife am Schluss nicht so aus> [..][x][y][z]['\n']['0'] ??
Nein, nicht ganz.
Es ergibt sich:
[..]['x']['y']['z']['\n']['\0']
'0' ist ein darstellbares Zeichen, das auf dem Bildschirm wie eine
Ziffer 0 aussieht (falls der Rechner ASCII verwendet, ist es das Byte
mit dem Wert 48 dezimal).
'\0' ist ein Byte mit dem Wert 0 und nicht darstellbar.
AVR-Frickler schrieb im Beitrag #1841723:
> RTS = REQUEST TO SEND (Hallo ich möchte was senden!)> CTS = CLEAR TO SEND (Jupp schick rüber!)
Das hilft hier nicht weiter, weil dann halt keine Zeichen mehr kommen.
Der Puffer wird aber nie mehr frei werden, wenn der Empfänger liest bis
zu einem \n.
Klaus Wachtler schrieb:> '0' ist ein darstellbares Zeichen, das auf dem Bildschirm wie eine> Ziffer 0 aussieht (falls der Rechner ASCII verwendet, ist es das Byte> mit dem Wert 48 dezimal).>> '\0' ist ein Byte mit dem Wert 0 und nicht darstellbar.
Hab nur das "\" vergessen, meinte das schon ;)
Also muss ich bei mir noch alles umstellen damit mein String auch
richtig ankommt, da mir ja sonst ein Zeichen verloren geht
Markus B. schrieb:> Also muss ich bei mir noch alles umstellen damit mein String auch> richtig ankommt, da mir ja sonst ein Zeichen verloren geht
?
Wieso "ein Zeichen verloren geht"?
Ich denke wir nähern uns schön langsam dem Punkt, wo du uns von den
Ratespielchen entbinden solltest und dein Projekt (samt Code) einfach
mal vorstellst.
Es ist ja nicht so, dass eine Funktion die eine Zeile über UART empfängt
(String welcher mit einem '\n' abgeschlossen wird. Das '\n' zeigt das
Ende der Zeile an) jetzt irgendwie komplizierte Raketentechnik wäre oder
das noch nie jemand so etwas gemacht hätte. Dafür, dass so etwas
eigentlich eine eher triviale Fleißaufgabe für einen engagierten Amateur
ist, dauert das nämlich jetzt schon zu lange.
MIr gehts ein Zeichen verloren weil ich es nicht für die eigentlich
Nachricht nutzen kann?! So war das gemeint, ich habe ein Zeichen im
String das ich gar nicht will
Und mal so zur Vorstellung
Ich will eigentlich nur ein VB.net-Programm (nicht meine Idee) mit
meinen µC kommunzieren lassen und mithilfe des Programms Ports ein und
ausschalten und die Eingänge auslesen
Und jetzt bin ich in Version 2 meiner Hardware (nun gelayoutet, vorher
nur Lochraster) und wollte wegen des neuen Prozessers (AT90CAN128 statt
ATMega16) einfach ein paa Sachen besser machen und stoße halt jetzt
wieder an meine Grenzen, da ich wie gesagt schon ewig nicht mehr hab und
wohl auch noch nie der Guru war. Ich arbeite mich aber gern in ein Thema
ein und such mir immer alles was ich brauch anstatt das ich mal
prinzipiell ein Buch les wo ich dann schon 80% kenn. Davon mag jeder
halten was er will, hatte halt nie einen "lehrer".
Und vorgestellt hab ich deshalb einfach nichts, weils nicht relevant war
für mich, ich halte das für keine große Errungenschaft wenn ich mir die
anderen Sachen hier so anschaue
Markus B. schrieb:> MIr gehts ein Zeichen verloren weil ich es nicht für die eigentlich> Nachricht nutzen kann?! So war das gemeint, ich habe ein Zeichen im> String das ich gar nicht will
Na ja.
Bleiben ja noch jede Menge andere aus denen man den Text zusammensetzen
kann.
Das <Return> zur Kennzeichnung eines Zeilenendes benutzt wird, ist
eigentlich seit was weiß ich wievielen Jahren üblich auch wenn es
natrülich nicht unbedingt so sein muss.
Hmm.
Wenn du von einem Programm aus wegschickst, wäre es unter Umständen
tatsächlichj sogar geschickter ein anderes Zeichen zu benutzen. ; wird
auch ganz gerne genommen.
Das Problem mit \n ist immer das man aufpassen muss, ob ein Return jetzt
nur ein Return ist oder ob da auch noch ein LineFeed mit dazukommt.
Markus B. schrieb:> wohl auch noch nie der Guru war. Ich arbeite mich aber gern in ein Thema> ein und such mir immer alles was ich brauch anstatt das ich mal> prinzipiell ein Buch les wo ich dann schon 80% kenn.
Schwerer Fehler.
Du wirst ewig auf Halbwissen sitzen bleiben und dich über seltsame Dinge
wundern, weil dir der Blick fürs Ganze fehlt.
Und die 80% :-)
Über die reden wir ein anderes mal. zb wenn du ein Buch zur Hand nimmst
und feststellst, dass es neben den 5 Kapiteln die du so leidlich intus
hats, noch 15 andere gibt, von denen du noch nicht einmal gehört hast,
das es sie gibt :-)
Karl heinz Buchegger schrieb:> Schwerer Fehler.> Du wirst ewig auf Halbwissen sitzen bleiben und dich über seltsame Dinge> wundern, weil dir der Blick fürs Ganze fehlt.>> Und die 80% :-)> Über die reden wir ein anderes mal. zb wenn du ein Buch zur Hand nimmst> und feststellst, dass es neben den 5 Kapiteln die du so leidlich intus> hats, noch 15 andere gibt, von denen du noch nicht einmal gehört hast,> das es sie gibt :-)
Da muss ich dir schon recht geben, aber im moment mache ich das in der
Arbeit (bin Lehrling) und zwar selbstständig. Dort kann ich mich nicht
reinhocken und ein Buch lesen ;) Somit les ich was ich lesen kann.
Privat hab ich mehrere C-Bücher und auch BÜcher für Elektronik wie zB
"Das große 51er Anwendungsbuch". Wie gesagt ich erarbeite mir im moment
vieles selbst und da kann schon mal ein Denkfehler reinkommen, dann bin
ich froh wenn es so Leute wie dich gibt, die hier fleisig antworten!
Danke mal so am Rande ;)
Von daher lass ich mir gern weiterhelfen, kann aber nicht alles so
preisgeben weil ich nicht weiß wie meine Firma darüber denkt. Aber ich
denke der C-Code und die Tatsache das ich mit einem VB.net-Programm
kommunziere ist keine Kunst.
Ich nutze übrigens das NewLine weil es in VB eine nette Funktion gibt
die sich "WriteLine" nennt und ich somit nur noch meine Daten eingeben
muss und der Rest zuverlässigt erledigt wird.
So und nun zum Abschluss ein neues Problem
Als ich heute getestet habe, ging alles Einwandfrei bis zu eine
Stringlänge von 7 Zeichen (dazu kommt dann noch das \n)
Meine uart_get_line-Funktion ist noch die selbe wie in der angehängten
Datei. Aufrufparameter war "uart_get_line(Eing, 10)", übergeben wird
"char Eing[10]". Für mich wären 8 Zeichen logisch, da ja das \n noch mit
in den String übernohmen wird, aber 7 versteh ich im moment nicht
Ich glaub ich hab leider im moment nicht den aktuellen Quellcode bei
mir, wie gesagt, Arbeitsprojekt
Markus B. schrieb:> Von daher lass ich mir gern weiterhelfen, kann aber nicht alles so> preisgeben weil ich nicht weiß wie meine Firma darüber denkt. Aber ich> denke der C-Code und die Tatsache das ich mit einem VB.net-Programm> kommunziere ist keine Kunst.
:-)
> Ich nutze übrigens das NewLine weil es in VB eine nette Funktion gibt> die sich "WriteLine" nennt und ich somit nur noch meine Daten eingeben> muss und der Rest zuverlässigt erledigt wird.
OK. Kein Problem
> Meine uart_get_line-Funktion ist noch die selbe wie in der angehängten> Datei. Aufrufparameter war "uart_get_line(Eing, 10)", übergeben wird> "char Eing[10]".
Schreibs besser so
1
uart_get_line(Eing,sizeof(Eing));
dann brauchst du nicht zählen, bzw. kannst nicht vergessen die Zahl
höher zu setzen, wenn Eing mal länger wird. Solche wichtige magischen
Zahlen überlässt man besser dem Compiler. Der übersieht nichts und macht
keine Fehler.
> Für mich wären 8 Zeichen logisch, da ja das \n noch mit> in den String übernohmen wird, aber 7 versteh ich im moment nicht
Ohne aktuellen Quellcode schwer zu sagen
Die Frage ist zb auch, ob sich da irgendwo ein versteckter Newline
('\r') eingeschlichen hat.
Du kannst aber auch mal das hier machen:
mit anderen Worten:
Du hast eine UART zur Verfügung. Benutze sie, damit dir das Programm
mitteilt, was es gelesen hat, wieviele Buchstaben etc.
Den Text in Eing lass ich mir bei der Ausgabe in # einschliessen. Damit
kann ich an der Ausgabe im Terminal sehen, ob sich irgendwo ein
Leerzeichen zuviel oder ein '\n' eingeschlichen hat, der nicht da sein
sollte.
Edit:
Ach ja.
Du benutzt doch für erste Teste ein Terminalprogramm und schickst
händisch Zeichen rüber und machst das nicht gleich von einem VB Programm
aus?
Markus B. schrieb:> Meine uart_get_line-Funktion ist noch die selbe
...
Bestimmt nicht.
Quellcode, oder Schläge!
Ach ja: KHB hat zu 99.7% recht, aber laß dich nicht verunsichern.
Natürlich musst du viel lesen, aber selber machen hilft auch weiter.
Beides muß sein.
Karl heinz Buchegger schrieb:> Edit:> Ach ja.> Du benutzt doch für erste Teste ein Terminalprogramm und schickst> händisch Zeichen rüber und machst das nicht gleich von einem VB Programm> aus?
Ich benutze im moment bereits ein eigenes VB-Programm zum testen da ich
so eben meine 7 Zeichen gleichzeitig rüber bekomm und sicher weiß was
dort gesendet wird, wie gesagt, ist ja meine 2te Version und Version 1
geht nach wie vor tadellos ;)
Und ich lass mir dort eben sofort das gesendete "Eing[10]" sofort wieder
zurückschicken, von daher hab ich das eben rausbekommen das ab 8 Zeichen
der Spuck beginnt, da bis 7 alles genau so zurückkommt wie es soll
Klaus Wachtler schrieb:> Übergehe doch einfach das Eintragen von \n in deinen String beim> Empfangen, wenn es dich stört.>> Ein kleines if, und fertig.
Naja ich wollte jetzt meine Funktion umschreiben da ich persönlich mit
dem do-while nicht so zufrieden bin, da ich zB bei einem nicht-empfangen
ein "break" einbaun musste damit meine Funktion nicht blockt. Ich denke
im moment sehr über eine rekursive Funktion nach, hab aber ein bischen
Angst mich total zu überfordern^^ Von daher erst mal nochmal mit ner
For-Schleife oder einer normalen while probieren, wär mir fast lieber
Im Descriptor signalisiert ein USB-Gerät beispielsweise, wieviele
2
Anschlüsse es besitzt und ob es eine externe Stromversorgung enthält.
3
Auf sehr lange Descriptoren reagiert die PS3 allerdings mit
4
Pufferüberläufen, womit sich Code auf den Stack der PS3 schleusen und
5
starten lässt.
6
7
Um das Kopierschutzsystem zu überwinden, geht der simulierte USB-Hub in
8
mehreren Schritten vor und gaukelt der PS3 den Anschluss verschiedener
9
Geräte in einer bestimmten Reihenfolge vor – wobei er mehrere
10
Pufferüberläufe provoziert, um unterschiedliche Daten und Code auf den
11
Stack zu schreiben. Im letzten Schritt wird der gesamte Code ausgeführt.
(Das es sowas noch gibt. Mitlerweile sollte doch nun wirklich jeder
C-Entwickler wissen, dass man externe Kanäle, über die Daten reinkommen
grundsätzlich immer auf potentiellen Pufferüberlauf überwacht)
@KHB: Du malst wieder in den leuchtendsten Farben schwarz!!
So einfach geht es auf einem AVR doch gar nicht, dank
Harvard-Architektur.
Auch wenn ich sie sonst hasse wie die Pest, hier ist sie im Vorteil.
AVR-Frickler schrieb im Beitrag #1841723:
> RTS = REQUEST TO SEND (Hallo ich möchte was senden!)> CTS = CLEAR TO SEND (Jupp schick rüber!)
Das ist zwar schön wörtlich übersetzt, aber nicht korrekt.
RTS ist ein Eingang am sendenden Controller, an den wird der
CTS-Ausgang des empfangenden Controllers angeschlossen.
Eine Anfrage à la "Hallo ich möchte was senden" gibt es nicht, es gibt
nur als Signalausgang die Freigabe für die sendende Gegenstelle.
Klaus Wachtler schrieb:> @KHB: Du malst wieder in den leuchtendsten Farben schwarz!!
LOL
Ich mal überhaupt nicht schwarz.
Das wird tatsächlich gemacht. Damit wird die Sony PSP3 geknackt :-)
> So einfach geht es auf einem AVR doch gar nicht, dank> Harvard-Architektur.> Auch wenn ich sie sonst hasse wie die Pest, hier ist sie im Vorteil.
Du sprichst mir aus der Seele.
OK. Du hast natürlich recht, so einfach ist es auf einem AVR dann auch
wieder nicht. Aber auch auf einem AVR ist ein zerschossener Stack immer
wieder für Überraschungen gut.
Und ich will auch gar nicht wissen, wie lange diese Typen gebraucht
haben um den USB-Descriptor so hinzutricksen, dass sie über den Stack
Code injezieren konnten. Und das über 6 Stufen - mir wird schwindlig :-)
Du hast natürlich Recht damit, daß es ein ernstes Problem ist - das will
ich gar nicht absprechen.
Mit Schwarzmalen hast du hier zumindest auf jeden Fall recht, keine
Frage.
Nur das Beispiel, so Code unterzuschieben, passt nicht zum AVR.
Karl heinz Buchegger schrieb:> wobei er mehrere> Pufferüberläufe provoziert, um unterschiedliche Daten und Code auf den> Stack zu schreiben. Im letzten Schritt wird der gesamte Code ausgeführt.
Ich hätte jetzt eigentlich erwartet, daß moderne CPU ne Memory
Protection Unit haben und dann das OS die Rechte klar vorgibt:
- hier ist Code, den darfst Du nur ausführen, aber nicht lesen oder
schreiben
- hier sind konstante Daten, die sind nur lesbar
- hier sind Daten, die sind lesbar und schreibbar, aber nicht
ausführbar.
Der Ausführungsversuch im Stack sollte also nur ne Exception
verursachen.
Peter
Peter Dannegger schrieb:> Ich hätte jetzt eigentlich erwartet, daß moderne CPU ne Memory> Protection Unit haben und dann das OS die Rechte klar vorgibt:> - hier ist Code, den darfst Du nur ausführen, aber nicht lesen oder> schreiben> - hier sind konstante Daten, die sind nur lesbar> - hier sind Daten, die sind lesbar und schreibbar, aber nicht> ausführbar.
Das gab es eigentlich schon beim 286er. Man definiert dort für Code,
Daten und Stack separate Segmente, und dann gibt es entsprechende
Berechtigungen. Aber alle modernen Betriebssysteme legen diese 3
Segmente deckungsgleich übereinander, so daß dieses Berechtigungssystem
nicht funktioniert. Das ist der Preis, den man gezahlt hat, um jedem
Prozess einen einzelnen linearen Adressraum zur Verfügung stellen zu
können.
Auf modernen Prozessoren gibt's deshalb noch eine weitere Möglichkeit
auf Basis einzelner Speicherseiten. Siehe:
http://de.wikipedia.org/wiki/NX-Bit> Der Ausführungsversuch im Stack sollte also nur ne Exception> verursachen.
Das ist nicht ganz so einfach. Teilweise wird von Compilern Code
generiert, der zur Laufzeit auf den Stack gelegt und dort ausgeführt
werden soll, ganz legitim.
Ich hab an dem Code mal weitergearbeitet und mich dann auch mal
zwangsläufg mit dem "sizeof" bechäftigt und bin dann auf folgendes
gestoßen:
http://www.imb-jena.de/~gmueller/kurse/c_c++/c_sizeof.html
Somit habe ich ja beim aufruf meiner Funktion:
uart_get_line(string, sizeof(string));
die doppelte Anzahl an Zeichen für die länge des Strings übergeben wie
ich brauche, sehe ich das richtig?
Ich wollte mir nämlich die sizeof meines strings IN der Funktion
zurückgeben lassen und habe mich gewundert warum ich immer eine "2"
erhalten habe.
Das wär meine ganze Funktion, ich habe versucht den Fehlerfall eines
überlaufs zu erkennen indem ich überprüfe ob noch Zeichen vorhanden
wären, wenn der String die maximale Länge erreicht hat. Das wird mit
Fehlermeldungen quittiert, die weit jenseits normaler String-Längen
liegen.
Der String gibt mir dann zusätzlich noch die richtige Länge des Strings
an, zumindest theoretisch, denn das funktioniert noch nicht richtig.
Vlt kann mal wer drüber schaun ob er den Fehler findet oder was man
C-Technisch noch verbessern kann/sollte.
mfg Rusticus
1
intuart_get_line(char*string,size_tl_string)
2
{
3
//Variablen deklariern
4
intc;
5
inti=0;
6
7
c=uart_getc();
8
9
if(c&UART_NO_DATA)
10
{
11
/*
12
* no data available from UART
13
*/
14
}
15
else
16
{
17
/*
18
* new data available from UART
19
* check for Frame or Overrun error
20
*/
21
if(c&UART_FRAME_ERROR)
22
{
23
/* Framing Error detected, i.e no stop bit detected */
24
uart_puts_P("UART Frame Error: 300");
25
return300;
26
}
27
if(c&UART_OVERRUN_ERROR)
28
{
29
/*
30
* Overrun, a character already present in the UART UDR register was
31
* not read by the interrupt handler before the next character arrived,
32
* one or more received characters have been dropped
33
*/
34
uart_puts_P("UART Overrun Error: 400");
35
return400;
36
}
37
if(c&UART_BUFFER_OVERFLOW)
38
{
39
/*
40
* We are not reading the receive buffer fast enough,
41
* one or more received character have been dropped
42
*/
43
uart_puts_P("Buffer overflow error: 500");
44
return500;
45
}
46
47
while(c!='\n'&&i<(l_string-1))
48
{
49
string[i]=c;
50
i++;
51
c=uart_getc();
52
53
if(c&UART_FRAME_ERROR)
54
{
55
/* Framing Error detected, i.e no stop bit detected */
56
uart_puts_P("UART Frame Error: 300");
57
return300;
58
}
59
if(c&UART_OVERRUN_ERROR)
60
{
61
/*
62
* Overrun, a character already present in the UART UDR register was
63
* not read by the interrupt handler before the next character arrived,
64
* one or more received characters have been dropped
65
*/
66
uart_puts_P("UART Overrun Error: 400");
67
return400;
68
}
69
if(c&UART_BUFFER_OVERFLOW)
70
{
71
/*
72
* We are not reading the receive buffer fast enough,
73
* one or more received character have been dropped
Markus B. schrieb:> Ich hab an dem Code mal weitergearbeitet und mich dann auch mal> zwangsläufg mit dem "sizeof" bechäftigt und bin dann auf folgendes> gestoßen:> http://www.imb-jena.de/~gmueller/kurse/c_c++/c_sizeof.html>> Somit habe ich ja beim aufruf meiner Funktion:> uart_get_line(string, sizeof(string));> die doppelte Anzahl an Zeichen für die länge des Strings übergeben wie> ich brauche, sehe ich das richtig?
Das siehst du falsch.
Mittels sizeof weist man den Compiler an, den Speicherbedarf einer
Variablen zu ermitteln.
Ein int wird in 2 Bytes abgelegt. Daher ist sizeof(int) gleich 2.
Eine char Variable benötigt nur 1 Byte im Speicher. Daher ist
sizeof(char) gleich 1.
> Ich wollte mir nämlich die sizeof meines strings IN der Funktion> zurückgeben lassen und habe mich gewundert warum ich immer eine "2"> erhalten habe.
weil auf deinem System eine Adresse (also ein Pointer) 2 Bytes belegt,
also ist der sizeof eines Pointers gleich 2.
sizeof ist etwas was vom Compiler ausgewertet wird und nicht zur
Laufzeit! Wenn du zur Laufzeit wissen willst, wie lange ein String
tatsächlich ist, dann musst du strlen benutzen.
Oder aber bei dir: da du in i ja sowieso mitzählst, wieviele Zeichen du
bereits in den String gepackt hast, steht in i schon die Information die
du haben willst. Möglicherweise musst du sie noch um 1 korrigieren, weil
i um 1 zu weit gezählt hat, aber dem Prinzip nach hast du die Länge des
tatsächlichen Strings (also die Anzahl der Zeichen im Array) in i
vorliegen. Du musst da nichts künsteln.
PS: Deine Funktion ist Müll
Lass fürs erste mal die ganze Fehlerbehandlung mit UART_FRAME_ERROR,
UART_OVERRUN_ERROR und UART_BUFFER_OVERFLOW weg. Das zieht dir momentan
den Code noch zu sehr in die Länge, so dass du den Überblick verlierst.
Rück den Code sauber an, sieh zu, dass du den Fall UART_NO_DATA korrekt
behandelst und bring das erst mal zum laufen.
Der Code ist auch ein Musterbeispiel dafür, dass man statt
1
tuwas;
2
while(Bedingung){
3
tuwas;
4
}
besser
1
do{
2
tuwas;
3
}while(Bedingung)
schreibt.
Das bedingte Schleifenkonstrukt, das seinen Körper mindestens einmal
ausführt, ist erfunden. Es gibt keinen Grund, den Körper zweimal
hinzuschreiben und beim zweiten Mal die Schleife 0- oder mehrmals
auszuführen.
Ich versteh nicht ganz warum meine Funktion Müll ist, wenn ich den
Returnwert mal außer Acht lasse, funktioniert die Funktion, ich empfange
einwandfrei meine Zeichen und den String
Ich hab doch dafür die Else anweisung? Ok der Schluss ist nicht optimal
und manche Leute mögen die "do-while-Schleife" bevorzugen, von der ich
extra weg bin, aber funktionieren tuts trotzdem!
Also wieso ist das Müll? Ein bischen detailiertere Angaben würden mir
mehr helfen :)
mfg
weil du zb innerhalb der while Schleife den Fall UART_NO_DATA gar nicht
mehr behandelst. Wenn deien UART nicht mehr mitgekommen ist und auf
Zeichen warten muss, bis dann endlich der erlösende '\n' kommt, hast du
dir den Buffer schon längst mit ungültigen Daten vollgemüllt.
Wie gesagt: Wirf alle Fehlerbehandlungen ausser UART_NO_DATA raus und
sieh erst mal zu, dass du in diesem Fall eine saubere Behandlung
bekommst. Dann wirst du auch solche Unlogikeiten sehen. Die Funktion ist
noch zu lang als dass du sie überblicken könntest. Du hast dich zu
schnell auf Details wie Fehlerbehandlung geworfen.
Eine gute Strategie ist es bei mir immer:
Zuerst mach ich die Funktion ohne mich groß um Fehlerfälle zu kümmern.
Das hat den Vorteil, dass ich mich nur auf den zugrundeliegenden
Algorithmus konzentrieren kann und mich nicht mit Nebenschauplätzen
aufhalten muss.
Wenn ich das fertig habe, dann geh ich den Code durch und schau mir an:
Was kann mir wo an Fehlerfällen auftauchen. Und dann bau ich diese
Behandlung noch zusätzlich ein.
Aber zuerst steht das nackte Verfahren, bei dem angenommen wird: es gibt
keine gravierenden Fehler.
Beim uart_getc musst du immer damit rechnen, dass es UART_NO_DATA
liefert. Das ist in diesem Sinne nicht wirklich ein Fehler. Den Fall
berücksichtige ich daher sofort in der Erstversion. Die anderen
Returncodes sind aber echte Fehler. Die lass ich in der Erstversion erst
mal links liegen.
OK dann bau ich mal meine funktion um und versuche das so zu machen
Die Variante ohne Fehlerbehanldung und co hatte ich bereits schon und
ging ja, deshalb hab ich diese Version probiert
Markus B. schrieb:> OK dann bau ich mal meine funktion um und versuche das so zu machen
Lösch einfach die Benannten Fehlerbehandlungsteile raus. Dann müsste man
schon sehen, wovon ich spreche.
Nicht einfach auskommentieren. Ziel ist es den Code der ganzen Funktion
auf eine Bildschirmseite zu bringen.
Sauber einrücken!