Forum: Mikrocontroller und Digitale Elektronik AVR Atmega8 UART an LCD in C


von Lars R. (lino1973)


Lesenswert?

Hallo,
ich bitte um Hilfe, ich möchte ein 2Byte Datenpaket in Hex von einem 
externen Temperatursensor auf Rx Empfangen und als Dezimal 
Temperaturwert auf dem myAVR LCD Add-On anzeigen, komme aber auf kein 
grünen Zweig. Kann mir vielleicht jemand ein Beispiel zeigen oder mir 
sonst wie weiter helfen?

Grüße Lino

von Falk B. (falk)


Lesenswert?


von Mystik (Gast)


Lesenswert?

Wie Falk schon mit dem Link zeigt, solltest Du dich erstmal mit den 
Grundlagen beschäftigen, denn Dein Vorhaben gehört  zu den elementarsten 
Dingen im Umgang mit Mikrocontrollern....

Wenn Du Hilfe erwartest, dann zeig mal Deine bisherigen Versuche und 
Ergebnisse, bzw. sag wo es genau hängt...

Einen Hexwert in ASCII zu wandeln und auf das Display zu schicken sollte 
man mit ein wenig Nachdenken auch als Anfänger(?) hinbekommen ;-)

von Julius (Gast)


Lesenswert?

hallo...

das hier: http://www.avr-cpp.de/doku.php?id=ein_kleines_projekt

macht etwas in die richtung die du suchst... durch die Basics musst du 
dich natürlich erstmal durcharbeiten dass läßt sich kaum abkürzen :-/

Gruß J.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Hier ist eine kleine Routine dafür:
1
#include <stdlib.h>
2
#include <string.h>
3
// output a number in decimal style
4
static void printnum(int16_t number) {
5
char dectable[16] = "0000000000000000";
6
char a;
7
uint8_t n,i;
8
  a = itoa(number,dectable,10);
9
  i = strlen(dectable);
10
  for (n=0;n < i;n++) {
11
    lcd_putc(dectable[n]); // Peter Fleurys Library
12
  }
13
}
Funktioniert gut, nur warum kriege ich immer 'Warning: makes Pointer 
from integer without a cast' in der itoa Zeile? Wäre nett, wenn mir das 
mal jemand erklärt.

von Karl H. (kbuchegg)


Lesenswert?

Matthias Sch. schrieb:

> Funktioniert gut, nur warum kriege ich immer 'Warning: makes Pointer
> from integer without a cast' in der itoa Zeile? Wäre nett, wenn mir das
> mal jemand erklärt.

weil itoa nun mal keinen char zurückliefert.

Wenn du mit einem Returnwert nichts machst, so wie hier bei dir, dann 
musst du ihn auch nicht auffangen.


Die Ausgabeschleife ist zwar in einem gewissen Sinne naheliegend aber 
unnötig kompliziert. strlen macht ja auch nichts anderes, als nach dem 
'\0' Zeichen im String zu suchen und mitzuzählen wieviele Zeichen es bis 
zum '\0' vorgefunden hat. Genau dasselbe kannst du aber auch machen, nur 
mit dem einen Unterschied, dass du die Zeichen nicht zählst, sondern 
einzeln ausgibst.
1
static void printnum(int16_t number)
2
{
3
  char numAsString[16] = "0000000000000000";
4
  char* strPtr = numAsString;
5
  
6
  itoa( number, numAsString, 10 );
7
  while( *strPtr != '\0' )
8
    lcd_putc( *strPtr++ );
9
}

oder wenn dir Array-Schreibweise erst mal lieber ist als Pointer
1
static void printnum(int16_t number)
2
{
3
  char numAsString[16] = "0000000000000000";
4
  int i = 0;
5
  
6
  itoa( number, numAsString, 10 );
7
  while( numAsString[i] != '\0' )
8
    lcd_putc( numAsString[i++] );
9
}

Es gibt keinen Grund warum man den String 2-mal durchgehen muss: Einmal 
um seine Länge festzustellen und das andere mal um dann genau diese 
Zeichen auszugeben. Einmal durchgehen reicht völlig.

Noch besser ist es allerdings, die Aufgaben zu trennen. Denn in dieser 
Funktion stecken eigentlich 2 Funktionalitäten. Die eine soll eine Zahl 
ausgeben und das macht sie indem sie für die Zahl eine String 
Repräsentierung erzeugt und dann den String ausgibt. Aber einen String 
ausgeben ist eine nützliche Funktionalität für sich selber. Daher wird 
das vernünftig sein, die Dinge zu trennen
1
static void printString( const char* str )
2
{
3
  while( *str )
4
    lcd_putc( *str++ );
5
}
6
7
static void printnum(int16_t number)
8
{
9
  char numAsString[16] = "0000000000000000";
10
  
11
  itoa( number, numAsString, 10 );
12
  printString( numAsString );
13
}

Jetzt kenn ich aber zufällig die Fleury Lib und weiß, dass die eine 
Funktion hat, um einen String auszugeben. D.h. die Funktion printString 
ist für die Katze, denn die Funktion gibt es bereits. Sie heißt 
lcd_puts.

Also magert dein Code ab zu
1
static void printnum(int16_t number)
2
{
3
  char numAsString[16] = "0000000000000000";
4
  
5
  itoa( number, numAsString, 10 );
6
  lcd_puts( numAsString );
7
}
Und da kann man doch jetzt nicht mehr meckern.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Karl Heinz Buchegger schrieb:
> weil itoa nun mal keinen char zurückliefert

Ach, ich Dummerchen zack, ich hab den Fehler immer in den Argumenten 
gesucht.
Hier nochmal die korrigierte Version:
1
#include <stdlib.h>
2
#include <string.h>
3
// output a number in decimal style
4
static void printnum(int16_t number) {
5
char dectable[16] = "0000000000000000";
6
char *a;
7
uint8_t n,i;
8
  a = itoa(number,dectable,10);
9
  i = strlen(dectable);
10
  for (n=0;n < i;n++) {
11
    lcd_putc(dectable[n]); // Peter Fleurys Library
12
  }
13
}
Karl Heinz, ich danke dir!

Karl Heinz Buchegger schrieb:
> Jetzt kenn ich aber zufällig die Fleury Lib und weiß, dass die eine
> Funktion hat, um einen String auszugeben.

Da hat aber nochmal jemand editiert. Das mit der Stringausgabe ist mir 
bekannt, aber die Routine ist deswegen so gehalten, damit ich auch 
schnell mal eine UART oder ein anderes Characterdevice anquäken kann.
Das Aufteilen in 2 Routinen schau ich mir mal in Ruhe an.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:

> Also magert dein Code ab zu
>
1
> static void printnum(int16_t number)
2
> {
3
>   char numAsString[16] = "0000000000000000";
4
> 
5
>   itoa( number, numAsString, 10 );
6
>   lcd_puts( numAsString );
7
> }
8
>
> Und da kann man doch jetzt nicht mehr meckern.

Doch, kann man schon. Denn die anderen Funktionen, die etwas mit dem LCD 
machen oder ausgeben, heißen alle lcd_irgendwas. Nur deine heißt 
'printnum'. Abgesehen davon, dass diese Benamung schon mal deswegen 
schlecht ist, weil printnum ja eigentlich 2 Wörter sind, die ich aber 
auf Anhieb nicht erkennen kann (sieh dir einfach mal an um wieviel 
leichter printString zu lesen ist, die Grossschreibung vom S macht einen 
gewissen Unterschied in der Lesbarkeit), ist es auch so, dass aus 
printnum nicht hervorgeht, wo denn eigentlich die Ausgabe erfolgt. Bei 
lcd_puts weiß ich das sofort. Das lcd_ verrät es mir. Bei printnum weiß 
ich erst mal gar nichts. Das könnte genausogut die UART sein, oder eine 
Ausgabe per SPI oder sonst irgendwie.

Wenn du ein Benamungsschema schon vorgegeben hast, dann bist du gut 
beraten, wenn du dich daran hältst. Auch wenn es nicht von dir stammt. 
Wenn alle LCD Funktion einen Präfic lcd_ haben, dann kannst du ruhig 
diese 'Tradition' fortführen. Der Lesbarkeit, Verstehbarkeit und 
Konsistenz deines Code kann sowas nur zu Gute kommen
1
static void lcd_int16(int16_t number)
2
{
3
  char numAsString[16] = "0000000000000000";
4
  
5
  itoa( number, numAsString, 10 );
6
  lcd_puts( numAsString );
7
}

oder wenn du in der Tradition vom Fleury bleiben willst, bei dem

  lcd_putc       ein Zeichen ausgeben
  lcd_puts       einen String ausgeben

bedeutet, dann nenn die Funktion halt lcd_puti (i wie int).

von Karl H. (kbuchegg)


Lesenswert?

Matthias Sch. schrieb

> Da hat aber nochmal jemand editiert. Das mit der Stringausgabe ist mir
> bekannt, aber die Routine ist deswegen so gehalten, damit ich auch
> schnell mal eine UART oder ein anderes Characterdevice anquäken kann.
> Das Aufteilen in 2 Routinen schau ich mir mal in Ruhe an.

Ach.
Und eine Funktion
1
static void uart_int16(int16_t number)
2
{
3
  char numAsString[16] = "0000000000000000";
4
  
5
  itoa( number, numAsString, 10 );
6
  uart_puts( numAsString );
7
}

wär dir zu kompliziert?

Du kannst es drehen wie du willst. Ausgabefunktionen haben eine gewisse 
Hierarchie. Praktisch immer.

ganz zu unterste steht eine Funktion, die 1 Zeichen ausgibt.
Darauf aufbauend gibt es eine Funktion, die einen String ausgibt
Und auf dieser Stringausgabe wiederrum bauen dann Funktionen auf, die 
ihre Ausgabe dergestalt erledigen, dass sie zunächst einen String 
erzeugen und mit der Stringausgabe die eigentliche Ausgabe erledigen.

Und das ist immer so!
Willst du auf eine anderes Device ausgeben, dann benutzt du eben eine 
andere String Ausgabe. Aber es gibt keinen Grund warum du in all diesen 
Funktion die Aufdröselung in einen String selbst vornehmen musst. Denn 
eine String Ausgabe ist auf allen Ausgabegeräten immer eine sinnvolle 
Funktionalität für sich selber.
Zumal es ja nicht bei einer einzigen Funktion bleibt. Da gibt es 16-Bit 
int mit und ohne Vorzeichen. Da gibt es 32 Bit long mit und ohne 
Vorzeichen. Da gibt es zum Beispiel ein Datum, welches aus 
Tag-Monat-Jahr besteht. Da gibt es .... Willst du in all diesen 
Funktionen die Abarbeitung eines Strings immer wieder neu 
ausprogrammieren?

Eine weitere 'Kunst' in der Programmierung besteht nicht darin, 
möglichst viele Lines of Code zu produzieren, sondern sie besteht darin, 
sich einen Baukasten zu machen, in dem die Einzelteile ineinandergreifen 
und die universell benutz- und kombinierbar sind. Das ganze Shell 
Konzept von Unix beruht darauf, einfache Tools zur Verfügung zu haben, 
die aber so gestaltet sind, dass sie wie Bauklötze zusammengesteckt 
werden können um sich mit einfachen Basisfunktionalitäten komplexere 
Funktionalitäten zu erzeugen. Und die sind dann wieder kombinierbar, 
etc. etc.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

So Karl Heinz, du kannst dich wieder beruhigen. Ich hatte lediglich eine 
kleine Routine gepostet, um dem TE zu helfen und keineswegs vor, eine 
Grundsatzdiskussion um Programmierstile zu entfachen. Da du anscheinend 
genau das vorhast, klinke ich mich hiermit aus. Viel Spass noch.

von Karl H. (kbuchegg)


Lesenswert?

Matthias Sch. schrieb:
> So Karl Heinz, du kannst dich wieder beruhigen.

Hab ich kein Problem damit :-)

Ich seh ja meine 'Aufgabe' hier nicht nur darin, Code zu korrigieren, 
sondern auch Techniken und Verfahren den Leuten näher zu bringen, die 
unter dem Stichwort 'gute Coding-Praxis' zusammengefasst werden können.
Und das beginnt durchaus ja auch schon im kleinen.

> Ich hatte lediglich eine
> kleine Routine gepostet, um dem TE zu helfen

Ooops. Ich hatte gar nicht gemerkt, dass du nicht der TE bist. Sorry 
dafür. Aber enn der TE davon etwas für seine eigene Praxis mitnehmen 
kann, dann hab ich das Ziel ja erreicht.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.