Hallo
Verwende Winavr und AVRStudio zum Programmieren.
Beschäftige mich in letzter Zeit mit dem Thema LCD. Der C Code vom
Tutorial zur Ansteuerung eins LCD funktioniert bereits. Weiters möchte
ich Variableninhalte auf das Disply ausgeben. Beim Durchforsten des
Internets bin ich auf die Funktion itoa gestoßen und habe mir bereits
itoa im FAQ von mikrocontroller.net zu Gemüte geführt. Nun bin ich dabei
diese Funktion zu erproben und gebe bereits Inhalte am LCD aus.
Alleredings stellen sich mir eine menge von Fragen und hoffe, dass ihr
mir diese beantworten könnt.
Als Grundlage dient der LCD Code vom Tutorial.
Wenn ich den folgenden Code einfüge erscheint am LCD wie gewünscht die
Zahl 95.
1
chara[10];
2
chari=95;
3
4
itoa(i,a,10);
5
lcd_string(a);
Es erscheint nach dem Befehl BULD jedoch diese Fehlermeldung:
>../LCD.c:188: warning: implicit declaration of function 'itoa'
Liegt das ein einem fehlenden Include?
Wenn ich vor der Zahl 95 ein Minus einfüge zeigt das Display -95 nicht
an. Warum?
Danke für eure Beiträge
Michael schrieb:
> Wenn ich den folgenden Code einfüge erscheint am LCD wie gewünscht die> Zahl 95.>>
1
>chara[10];
2
>chari=95;
3
>
4
>itoa(i,a,10);
5
>lcd_string(a);
6
>
>> Es erscheint nach dem Befehl BULD jedoch diese Fehlermeldung:>>../LCD.c:188: warning: implicit declaration of function 'itoa'>> Liegt das ein einem fehlenden Include?
Ja.
"implicit declaration" bedeutet immer, dass der Compiler gezwungen ist
eine Funktion zu verwenden, der er noch nicht vorgestellt wurde.
Eine Funktion macht man mit einem Protoypen bekannt und die Protoypen
für die Standardfunktionen befinden sich in einem Header File, welches
man besser includiert.
> Wenn ich vor der Zahl 95 ein Minus einfüge zeigt das Display -95 nicht> an. Warum?
Weil char der falsche Datentyp ist um einen int zu speichern.
Die Funktion heisst ja nicht umsonst itoa .... Integer TO Ascii
Und Integer ist in C nun mal ein int.
>> Liegt das ein einem fehlenden Include?>> Ja.> "implicit declaration" bedeutet immer, dass der Compiler gezwungen ist> eine Funktion zu verwenden, der er noch nicht vorgestellt wurde.>> Eine Funktion macht man mit einem Protoypen bekannt und die Protoypen> für die Standardfunktionen befinden sich in einem Header File, welches> man besser includiert.
Hab jetzt #include <stdlib.h> eingefügt und siehe da, es kommt keine
Fehlermeldung mehr.
Warum funktionierte auch vorher die Ausgabe mit Fehlermeldung?
Wie kann ich in Zukunft feststellen welches Include für eine
Fehlermeldung benötigt wird?
> Weil char der falsche Datentyp ist um einen int zu speichern.> Die Funktion heisst ja nicht umsonst itoa .... Integer TO Ascii> Und Integer ist in C nun mal ein int.
Dass habe ich mir auch schon gedacht, bevor ich itoa anwendete, aber es
funktionierte. Was mir noch aufgefallen ist, ist, dass ich auch für
char-Werte 200 eigeben kann ohne utoa (wie im Tutorial beschrieben) zu
verwenden. Warum funktioniert dies auch ohne utoa?
Gibt es so eine funktion für char? Für long gibt es ja sprintf, dazu
komme ich aber später zu sprechen.
Michael schrieb:
> Hab jetzt #include <stdlib.h> eingefügt und siehe da, es kommt keine> Fehlermeldung mehr.> Warum funktionierte auch vorher die Ausgabe mit Fehlermeldung?
Weil in dem Fall die C Standardregeln greifen und du Glück hattest, dass
diese Regeln genau das bewirken, was bewirkt werden muss.
> Wie kann ich in Zukunft feststellen welches Include für eine> Fehlermeldung benötigt wird?
Falsche Vorgehensweise.
Du willst eine Funktion benutzen.
Dazu siehst du in der Doku nach, welchen Header File du für diese
Funktion brauchst.
Aber mit der Zeit kennt man die auswendig. Soviele gibt es ja nicht.
>>> Weil char der falsche Datentyp ist um einen int zu speichern.>> Die Funktion heisst ja nicht umsonst itoa .... Integer TO Ascii>> Und Integer ist in C nun mal ein int.>> Dass habe ich mir auch schon gedacht, bevor ich itoa anwendete, aber es> funktionierte.
... offensichtlich nicht. Funktionieren bedeutet bei mir, dass es auch
mit negativen Zahlen kein Problem gibt.
> Was mir noch aufgefallen ist, ist, dass ich auch für> char-Werte 200 eigeben kann ohne utoa (wie im Tutorial beschrieben) zu> verwenden. Warum funktioniert dies auch ohne utoa?
Weil ein char nunmal kein int ist.
> Gibt es so eine funktion für char?
Zunächst mal solltest du von einem "char" Abschied nehmen.
char benutzt man, wenn man das Byte im Sinne eines Zeichens benutzen
will.
Ansonsten benutzt man immer entweder signed char oder unsigned char.
Dann gibt es auch keine Misverständnisse mit dem Vorzeichen.
Anstelle von signed/unsigned char nimmt man aber auch gerne uint8_t bzw.
int8_t. Das bringt dann noch etwas besser zum Ausdruck, dass man es hier
iegentlich mit 'einem kleinen Integer' zu tun hat, der zum Rechnen
gedacht ist. Und da es dann wieder die Unterscheidung in 'mit/ohne
Vorzeichen' gibt, ist auch dann alles weitere klar.
Und nein. Für char gibt es keine Entsprechung. itoa ist schon richtig.
Aber du weißt nie, ob der Compiler einen char als "mit Vorzeichen" oder
"ohne Vorzeichen" auffasst :-)
> Für long gibt es ja sprintf, dazu> komme ich aber später zu sprechen.
sprintf kann man mit allem möglichen verwenden. Aber die Entsprechung
für long wäre ltoa bzw. ultoa
Karl heinz Buchegger schrieb:
> Weil in dem Fall die C Standardregeln greifen und du Glück hattest, dass> diese Regeln genau das bewirken, was bewirkt werden muss.
Könntest du bitte einen Link über die C Standardregeln hierher posten,
damit ich mir diese einmal zu Gemüte führen kann. Beschäftige mich erst
seit einem halben Jahr mit Mikrocontroller und bin mit solchen Dingen
noch nicht sattelfest. Weiters wird mir dies nicht erspart bleiben
tiefer in diese Thematik einzusteigen.
> Falsche Vorgehensweise.> Du willst eine Funktion benutzen.> Dazu siehst du in der Doku nach, welchen Header File du für diese> Funktion brauchst.>> Aber mit der Zeit kennt man die auswendig. Soviele gibt es ja nicht.
Wo befindet sich diese Doku? Ich nehme an, im Winavr-Ordner. Wie gehe
ich dabei vor?
Habe Folgendes ausprobiert:
1
chara[10];
2
unsignedinti=-54;
3
4
itoa(i,a,10);
5
lcd_string(a);
Obwohl ich unsigned int i = -54; schreibe wird die Zahl -54 korrekt am
Display angezeigt. Müsste ich da normalerweise nicht int i = -54;
schreiben?
Wenn ich allerdings diesen Code eingebe,
1
chara[10];
2
unsignedinti=-54;
3
4
utoa(i,a,10);
5
lcd_string(a);
wird irgendeine Zahl ausgegeben.
Warum hängt das von itoa bzw. utoa ab und nicht von dem Datentyp, mit
dem ich i deklariere?
Im FAQ steht, dass das Array nicht zu klein dimensioniert werden soll.
Hierfür habe ich diesen Code probiert.
1
chara[0];
2
unsignedinti=54894;
3
4
utoa(i,a,10);
5
lcd_string(a);
Warum funktioniert dies auch, wenn a[0] ist?
Wie kann ich im Vorhinein feststellen das mein Array später nicht zu
klein ist bzw. was muss ich dabei beachten?
Michael schrieb:
> [...]> Wo befindet sich diese Doku? Ich nehme an, im Winavr-Ordner. Wie gehe> ich dabei vor?
Na, entweder konsultierst du eben die Doku von WinAVR (Ein Shortcut im
Startmenü wird automatisch erzeugt, wenn du das nicht abgewählt hast),
oder du suchst im Internet. Zweiteres ist ganz praktisch, wenn du den
Namen der Funktion bereits kennst. Meistens verweist eines der ersten
Ergebnisse auf cppreference.com, da findest du gute Beschreibungen zu
den Standardfunktionen. Die entsprechenden Headers sind auch aufgeführt.
> Habe Folgendes ausprobiert:>
1
>chara[10];
2
>unsignedinti=-54;
3
>
4
>itoa(i,a,10);
5
>lcd_string(a);
6
>
>> Obwohl ich unsigned int i = -54; schreibe wird die Zahl -54 korrekt am> Display angezeigt. Müsste ich da normalerweise nicht int i = -54;> schreiben?
Wie das übergebene Argument verwendet wird, hängt von der Funktion ab,
nicht vom Datentyp. Wenn du -54 in einen unsigned int speicherst, dann
sollte der Compiler eigentlich eine Warnung ausgeben. Da es für den
Prozessor aber eigentlich keine wirkliche Unterscheidung von
signed/unsigned gibt, funktioniert der Code trotzdem. Ob eine Zahl an
einer Speicherstelle positiv oder negativ ist, ist nur eine Frage der
Interpretation. Bei 16 Bit sieht das beispielsweise so aus:
54 hat das Bitmuster 110110.
Auf 16 bit aufgebohrt ist das 0000 0000 0011 0110.
Weil die Zahl negativ ist, wird davon das Zweierkomplement gebildet,
also alle Bits negieren und 1 dazuaddieren:
1111 1111 1100 1010
Dieses Bitmuster übergibst du jetzt an itoa(). Die Funktion prüft nun,
ob das Argument negativ ist, indem das oberste Bit auf 1 geprüft wird.
Dies ist hier der Fall. Also kommt schonmal ein '-' in den Puffer. Für
die weitere Verwendung wird die Zahl nun in's Positive verdreht (x =
-x;). Um das zu tun, wird wiederum das Zweierkomplement gebildet:
0000 0000 0011 0110
Du siehst, das ist wieder das gleiche Bitmuster wie bei 54. Somit folgt
die Ausgabe "54" mit einem Minuszeichen, also "-54".
> Wenn ich allerdings diesen Code eingebe,>
1
>chara[10];
2
>unsignedinti=-54;
3
>
4
>utoa(i,a,10);
5
>lcd_string(a);
6
>
> wird irgendeine Zahl ausgegeben.> Warum hängt das von itoa bzw. utoa ab und nicht von dem Datentyp, mit> dem ich i deklariere?
Siehe oben. utoa() interpretiert das oberste Bit nicht als Vorzeichen
und gibt somit die Zahl mit dem Bitmuster
1111 1111 1100 1010b (65482) aus.
> Im FAQ steht, dass das Array nicht zu klein dimensioniert werden soll.> Hierfür habe ich diesen Code probiert.>
1
>chara[0];
2
>unsignedinti=54894;
3
>
4
>utoa(i,a,10);
5
>lcd_string(a);
6
>
> Warum funktioniert dies auch, wenn a[0] ist?
Das liegt daran, dass die Grösse von a[] nirgendwo überprüft wird.
utoa() schreibt also einfach über den gültigen Bereich hinaus. Dahinter
ist nun jedoch nicht "nichts", sondern weitere Speicherzellen. Das kann
jedoch dazu führen, dass andere Variablen oder Rücksprungadressen oder
sonstige Werte überschrieben werden. Solche Fehler sind oft schwer zu
finden, daher reservierst du besser immer ausreichend viel Platz.
> Wie kann ich im Vorhinein feststellen das mein Array später nicht zu> klein ist bzw. was muss ich dabei beachten?
Tja, das ist nicht so einfach zu beantworten. Grundsätzlich kannst du
dich an der maximal darstellbaren Zahl orientieren. utoa() stellt einen
int als String dar. Auf einem 8- oder 16-Bit-System ist ein int meistens
16 Bits breit. Somit ist die grösste darstellbare Zahl 65535. Ein String
zur Aufnahme eines dezimal dargestellten ints muss demnach im Extremfall
fünf Ziffern plus die Nullterminierung fassen können. Nun könnte der
Code aber plötzlich auf einem anderen System kompiliert werden, wo ein
int vielleicht 32 Bits umfasst (Z.B. auf einem "normalen" PC), dann
könnte bei einem grösseren Wert durchaus ein Pufferüberlauf passieren.
Das wird am PC aber normalerweise mit einer Schutzverletzung quittiert,
nur solltest du dich nicht darauf verlassen.
Philipp Burch schrieb:
> Ein String> zur Aufnahme eines dezimal dargestellten ints muss demnach im Extremfall> fünf Ziffern plus die Nullterminierung fassen können.
Heißt das, dass in meinem Array die Zahl 5 stehen muss (array[5]), da
die Zahl 65535 fünf Ziffern hat und die Nullterminierung eingerechnet
wird. Insgesamt sind also 6 Stellen zu beachten und da bei einem Array
'null' eingerechnet wird steht also die Zahl fünf im Array. Ist meine
Annahme korrekt?
Mir ist aufgefallen, wenn ich beim obigen Beispiel anstatt von -54 nur
54 eintrage und die binäre Darstellung einstelle, also utoa(i,a,2);,
erscheint am LCD wie erwünscht 110110.
Wäre auch eine Darstellung in dieser Form möglich? 0000000000110110
Mir ist klar, dass 110110 für den Benutzer besser ersichtlich ist.
Michael schrieb:
> Heißt das, dass in meinem Array die Zahl 5 stehen muss (array[5]), da> die Zahl 65535 fünf Ziffern hat und die Nullterminierung eingerechnet> wird. Insgesamt sind also 6 Stellen zu beachten und da bei einem Array> 'null' eingerechnet wird steht also die Zahl fünf im Array. Ist meine> Annahme korrekt?
Nein. Bei der Definition des Arrays ist die Zahl in den Klammern die
Größe, und nicht der größte mögliche Index.
1
chararray[5];
Array hat die Größe 5, erlaubter Index geht von 0 bis 4.
Michael schrieb:
> Karl heinz Buchegger schrieb:>> Weil in dem Fall die C Standardregeln greifen und du Glück hattest, dass>> diese Regeln genau das bewirken, was bewirkt werden muss.>> Könntest du bitte einen Link über die C Standardregeln hierher posten,> damit ich mir diese einmal zu Gemüte führen kann. Beschäftige mich erst> seit einem halben Jahr mit Mikrocontroller und bin mit solchen Dingen> noch nicht sattelfest. Weiters wird mir dies nicht erspart bleiben> tiefer in diese Thematik einzusteigen.
Phillip hat ja schon so ziemlich alles beantwortet.
Alledings zeigen deine Fragestellungen, dass du einen ganz wesentlichen
Kardinalfehler machst, den leider heutzutage viele machen:
Du versuchst die Programmiersprache C in einem "Versuch und Irrtum"
Verfahren zu lernen. Das wird nicht funktionieren! Dir fehlt eine
systematische Einführung in die Programmiersprache und dazu wirst du
Literatur benötigen.
Die C-Standardregeln, aka den C-Standard, vergisst du gleich wieder als
Nachschlagdokument. Da stehst du nämlich davor, wie der Ochs vorm Tor.
Du brauchst ein Buch, welches dir den Standard bereits in aufbereiteter
Form nahe bringt. Und zwar systematisch, vom Einfachen zum Schwierigen.
Inklusive all der Stolpersteine.
Kauf dir zb einen Kernighan&Ritchi Programmieren in C
Da gehts mit den Grundlagen los und das Buch bringt dich von den
absoluten Grundlagen zu all den kleinen Feinheiten, ohne dass etwas
ausgelassen wird.
Michael schrieb:
> Mir ist aufgefallen, wenn ich beim obigen Beispiel anstatt von -54 nur> 54 eintrage und die binäre Darstellung einstelle, also utoa(i,a,2);,> erscheint am LCD wie erwünscht 110110.> Wäre auch eine Darstellung in dieser Form möglich? 0000000000110110
Möglich ist das schon. Nur musst du sich selber drum kümmern, dass
führende 0-en erzeugt werden.
> Mir ist klar, dass 110110 für den Benutzer besser ersichtlich ist.
Ganz im Gegenteil. In Dezimalschreibweise sind führende 0-en oft nicht
sehr sinnvoll. In Binärschreibweise ist aber oft das Weglassen derselben
nicht besonders schlau. Hat man eine Binärzahl vor sich, dann muss man
erst mal die Anzahl der Stellen abzählen, ehe man weiß welchen
Stellenwert die erste 1 hat (als Dezimalzahl aufgefasst).
Bei Binärzahlen ist es daher oft tatsächlich schlau, mit führenden 0-en
zu arbeiten. Dazu muss man aber wissen, auf wieviele Binärziffern man
insgesamt hin möchte. Sollen es 8 sein, oder 16, oder 32 oder ...
Zunächst möchte ich mich bei allen Usern für die Unterstützung bedanken.
Habe weiters folgenden Code ausprobiert:
1
charBuffer[20];
2
inti=25;
3
4
sprintf(Buffer,"%d",i);
5
lcd_string(Buffer);
Hier kommen ebenfalls zwei Warnungen, obwohl ich die stdlib.h
eingebunden habe:
>../LCD.c:195: warning: implicit declaration of function 'sprintf'>../LCD.c:195: warning: incompatible implicit declaration of built-in function
'sprintf'
Welches Include gehört für diese Funktion eingebunden?
Möchte weiters Zahlen mit einem Komma ausgeben. Laut FAQ nur mit sprintf
möglich. Leider funktioniert sprintf mit Kommawerten nicht. Wie kann ich
vorgehen, damit auch float-Werte am lcd erscheinen? Gibt es eine
Funktion ftoa mit der dies erreicht werden kann?
Wenn das mit den float funktioniert, möchte ich als Letztes noch einen
String, der mittels RS 232 übertragen wird und im UDR Register ist, auf
das Display ausgeben. Grund dafür ist, dass ich kontrollieren kann, ob
wirklich alle Zeichen richtig übertragen wurden. Die LEDs am Board
leuchten bereits lt. ASCII Code.
String sieht folgendermaßen aus: ZW 156.49 UA 285.39
Weiß nicht welche Funktion ich für diesen String anwenden soll.
Michael schrieb:
> Hier kommen ebenfalls zwei Warnungen, obwohl ich die stdlib.h> eingebunden habe:>>../LCD.c:195: warning: implicit declaration of function 'sprintf'>>../LCD.c:195: warning: incompatible implicit declaration of built-in function> 'sprintf'> Welches Include gehört für diese Funktion eingebunden?
Das findest du jetzt selber heraus :-)
Geh auf google, gib sprintf ein
Und den ersten Link "sprintf C++ Referenz" nimmst du.
Normalerweise gehört zu einer derartigen Referenz auch, welche Include
Files notwendig sind, das ist dort so leider nicht angegeben, aber im
Beispiel ganz unten ist er drinn.
> Möchte weiters Zahlen mit einem Komma ausgeben. Laut FAQ nur mit sprintf> möglich. Leider funktioniert sprintf mit Kommawerten nicht. Wie kann ich> vorgehen, damit auch float-Werte am lcd erscheinen? Gibt es eine> Funktion ftoa mit der dies erreicht werden kann?
ftoa gibt es.
Du solltest dir angewöhnen, mehr in deine Doku, bzw in deine Literatur
zu schauen.
Allerdings könnte auch das hier für dich interessant sein:
http://www.mikrocontroller.net/articles/FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio> Wenn das mit den float funktioniert, möchte ich als Letztes noch einen> String, der mittels RS 232 übertragen wird und im UDR Register ist,
Ähm. Das kann nicht sein.
Im UDR ist ein Byte. Ein String ist aber eine Abfolge von Bytes. Ein
String kann daher nicht im UDR Register sein. Aber du kannst natürlich
Zeichen für Zeichen, nachdem es übertragen wurde, aus dem UDR holen und
an einen String anhängen, bis der komplett ist (woher weißt du, das der
String komplett ist und nicht noch ein Zeichen kommt?)
> auf> das Display ausgeben. Grund dafür ist, dass ich kontrollieren kann, ob> wirklich alle Zeichen richtig übertragen wurden.
Dazu würde es auch ausreichen, einfach jedes Zeichen, nachdem es
empfangen wurde aufs LCD auszugeben. Dann entsteht am LCD genauso der
übertragene Text.
Die Zahl -2125.12 wird richtig angezeigt. Laut FAQ ist in meinem Bsp.
die Breite 3. Das Komma und die Nachkommastellen zusammengerechent sind
ebenfalls 3. Warum werden die Ziffern vor dem Komma dennoch angezeigt?
Wenn ich für i einen float nehme meldet der Compiler, dass %3.2f nur für
double verwendet werden darf, lt. FAQ aber doch?
Habe im Netz bezüglich ftoa gesucht, aber es ist nichts Brauchbares
dabei das mir weiter hilft. In einem anderen Forum ist sogar gestanden,
dass es die Funktion ftoa nicht gibt bzw. nicht funktioniert.
Dieser Code funktioniert jedenfalls NICHT:
1
chara[0];
2
floati=54.2;
3
4
ftoa(i,a,10);
5
lcd_string(a);
Möchte trotzdem ftoa verwenden, da die Funktion schon einmal
ressourcensparender ist.
Wo muss ich den Code ändern, damit das LCD den richtigen Wert ausgibt?
Im FAQ steht jedenfalls, dass es dies Funktion gibt. Auch du hast die im
vorigen Post bestätigt.
Ich zitize das FAQ:
>Die Namensgebung lehnt sich an das Schema an: Kürzel_für_den_Datentyp to a. Eine >Funktion die einen unsigned int wandelt, heißt dann utoa (oder _utoa), Floating >Point heißt dann ftoa (oder _ftoa), etc.
Leider ist kein entsprechendes Bsp. zu ftoa aufgeführt.
Michael schrieb:
> Die Zahl -2125.12 wird richtig angezeigt. Laut FAQ ist in meinem Bsp.> die Breite 3. Das Komma und die Nachkommastellen zusammengerechent sind> ebenfalls 3. Warum werden die Ziffern vor dem Komma dennoch angezeigt?
Weil printf/sprintf/fprintf die Feldbreite eigenmächtig erweitern, wenn
es notwendig ist.
Die Angabe der Feldbreite ist also so etwas wie eine Minialbreite. Man
benötigt sie zb bei der Ausgabe von Tabellen um sicherzustellen, dass zb
die Einerstellen der Werte sauber untereinander stehen.
> Wenn ich für i einen float nehme meldet der Compiler, dass %3.2f nur für> double verwendet werden darf, lt. FAQ aber doch?
Diese Warnung ist eigentlich unsinnig, da laut C-Standardregeln ein
float an dieser Stelle sowieso immer als double übergeben werden muss.
Für die Funktion sprintf gibt es daher keinen Unterschied zwischen
double und float.
> Habe im Netz bezüglich ftoa gesucht, aber es ist nichts Brauchbares> dabei das mir weiter hilft.
Probier dtostrf()