Forum: Mikrocontroller und Digitale Elektronik LCD 0 anzeigen uint16_t


von Elias B. (bouni)


Lesenswert?

Hallo Allerseits,

Ich habe folgendes Problem und nach langer Suche im Forum bin ich immer 
noch nicht hinter mein Problem gekommen.

Ich habe ein Grafik LCD von Pollin:
http://www.pollin.de/shop/suchergebnis.html?S_TEXT=+DG-16080-11&S_WGRUPPE=default

Das funktioniert auch wunderbar.
Da dieses Teil einen Touchscreen hat les ich den auch aus, was ebenfalls 
funktioniert.

Ich bin leider gerade auf Arbeit und hab deswegen den Code nicht hier, 
aber ich versuche es so gut es geht zu beschreiben:

Ich lasse die X und Y Werte der AD Wandlungen auf dem Display anzeigen.
Diese speichere ich heirzu in einem uint16_t und wandle sie mit hilfe 
von itoa() in einen char. (char string[10])
Dieses lass ich auf dem Display ausgeben wenn der Touch gedrückt ist, 
was auch geht, wenn nicht habe Ich eine funktion die eine AD Wandlung 
macht und wenn der Wert > 1000 ist (bei nichtberühren ist er ca. 1020) 
die uint16_t Variable mit 0 beschreibt.

Wenn ich die Variable aber in diesem Fall ausgeben lasse, dann erhalte 
ich eine solche Anzeige _0_12: die unterstriche sind leerzeichen, die 12 
eine zahl die ab und zu schwankt.

Ich habe schon alles mögliche probiert, komme aber nicht hinter das 
Problem :/

Ich vermute das es etwas mit danit zu tun hat das die Variable eine 
16bit breite ist.
hab auch schon versucht mit 0x0000 zu beschreiben, aber ohne erfolg :(

Wenn wer ne lösung hat, ich bin für jede Hilfe dankbar!!

Gruss an alle

Elias

von Stefan E. (sternst)


Lesenswert?

Das ist sehr wahrscheinlich ein Problem der Ausgabefunktion, die einfach 
eine feste Anzahl Zeichen ausgibt, statt sich am Stringende zu 
orientieren.

von Klaus W. (mfgkw)


Lesenswert?

Und ich habe eben versucht, in deinem Quelltext den Fehler zu
finden, leider auch ohne Erfolg.

von Elias B. (bouni)


Lesenswert?

Hallo,

erst mal Dnake für die Antworten.

Ich sitze wie gesagt auf Arbeit und habe den Quelltext nicht hier.
Werd Ihn heute Abend posten sobald ich daheim bin.

Ich habe mir auch schon den Wert der 16bit Variable mittels itoa(a,b,2)
binär wandeln lassen und den ausgegeben, und auch dort steht nicht 0 
drin.

Ich denke der Fehler liegt irgendwo auf der Strecke von 0 in die 
Variable schreiben bis zum ausgeben aufs Display.

von Karl H. (kbuchegg)


Lesenswert?

Elias B. schrieb:

> Ich habe mir auch schon den Wert der 16bit Variable mittels itoa(a,b,2)
> binär wandeln lassen und den ausgegeben, und auch dort steht nicht 0
> drin.

Vorsicht.
Bei einer binären Ausgabe einer 16-Bit Zahl, kann der entstehende String 
logischerweise auch 16 Zeichen lang werden. Du brauchst dann klarerweise 
ein

  char string[17];

10 ist dann zuwenig.

Und noch ein Fehler (aber der erklärt jetzt nicht dein Problem).

itoa:   man beachte das i am Anfang. Das steht für I wie INT
        (also signed int)

Du hast aber keinen signed int. Du hast einen unsigned int. Dafür 
brauchst du die Funktion utoa. u wie 'unsigned'

Ich denke auch, dass die wahrscheinlichste Stelle für einen Fehler die 
Ausgabefunktion ist.
Es könnte auch sein (auch ein beliebter Fehler), dass du übersehen hast, 
dass eine neue Ausgabe die alte überschreibt. Ist die neue Ausgabe 
kürzer als die alte, dann bleibt klarerweise von der alten Ausgabe etwas 
übrig.
Wenn man die Zahl 1018 mit 99 überschreibt, dann steht am Display 9918 
und das kann schnell mal zu Konfusion führen.

von Elias B. (bouni)


Lesenswert?

Hallo,

Habs wohl ein wenig dürftig beschrieben.
Der cah string[20] war 20 Zeichen lang für den versuch mit dem Binär :)

ok, das mit dem utoa leuchtet ein :) -> werd ich auf jeden Fall mal 
ändern.

Mal sehen was der Abend bringt.

Wenn ich einen
1
uint16_t test;
mit
1
test = 0;

Mit nul beschreibe müsste er doch komplett null sein oder? Ich meine das 
so nicht nur die untern 8 bit oder so beschrieben werden!?

von Karl H. (kbuchegg)


Lesenswert?

Elias B. schrieb:

> Wenn ich einen
>
1
uint16_t test;
> mit
>
1
test = 0;
>
> Mit nul beschreibe müsste er doch komplett null sein oder? Ich meine das
> so nicht nur die untern 8 bit oder so beschrieben werden!?

Das passt schon so.

von Elias B. (bouni)


Lesenswert?

Hallo zusammen,

Hier der Code mit dem ich den Touch auswerte:
1
// touch.c
2
3
#include "touch.h"
4
#include "adc.h"
5
6
uint8_t touchispressed()
7
  {
8
  uint16_t   adc_value = 0;  
9
  DDRA |=  (1 << PA0) | (1 << PA1); // PA0 auf Ausgang Y1 und PA2 auf Ausgang Y2
10
  
11
  DDRA &= ~(1 << PA2); // PA1 auf Eingang X1
12
  DDRA &= ~(1 << PA3); // PA1 auf Eingang X1
13
14
  PORTA |=  (1 << PA0); // PA0 HIGH Y1
15
  PORTA &= ~(1 << PA1); // PA2 LOW  Y2
16
17
  _delay_us(10);
18
19
  adc_value = readADC(PA2); // PA1 ADC lesen
20
  
21
  if(adc_value > 1000)
22
    {
23
    return 0; // Touch ist NICHT gedrückt
24
    }
25
  else
26
    {
27
    return 1; // Touch ist gedrückt
28
    }
29
  }
30
31
uint16_t readTouchX() 
32
  {
33
  uint16_t   adc_value = 0;  
34
  if(touchispressed())
35
    {
36
    DDRA |=  (1 << PA0) | (1 << PA1); // PA0 auf Ausgang Y1 und PA1 auf Ausgang Y2
37
  
38
    DDRA &= ~(1 << PA2); // PA2 auf Eingang X1
39
    DDRA &= ~(1 << PA3); // PA3 auf Eingang X2
40
41
    PORTA |=  (1 << PA0); // PA0 HIGH Y1
42
    PORTA &= ~(1 << PA1); // PA2 LOW  Y2
43
44
    _delay_us(10);
45
46
    adc_value = readADC(PA2); // PA2 X1 ADC lesen
47
48
    return adc_value; // Touch Wert zurück geben
49
    }
50
  else
51
    {
52
    return 0; // 0 Zurückgeben    
53
    }  
54
  }
55
56
uint16_t readTouchY() 
57
  {
58
  uint16_t   adc_value = 0;  
59
  if(touchispressed())
60
    {
61
    DDRA |=  (1 << PA2) | (1 << PA3); // PA2 auf Ausgang X1 und PA3 auf Ausgang X2
62
  
63
    DDRA &= ~(1 << PA0); // PA0 auf Eingang Y1
64
    DDRA &= ~(1 << PA1); // PA1 auf Eingang Y2
65
66
    PORTA |=  (1 << PA2); // PA0 HIGH X1
67
    PORTA &= ~(1 << PA3); // PA2 LOW  X2
68
69
    _delay_us(10);
70
71
    adc_value = readADC(PA0); // PA0 Y1 ADC lesen
72
73
    return adc_value; // Touch Wert zurück geben
74
    }
75
  else
76
    {
77
    return 0; // 0 Zurückgeben        
78
    }  
79
  }

Mit dem gebe Ich das ganze auf's Display aus:
1
void lcd_writechar(unsigned char b)
2
3
{  unsigned char i,j,c;
4
5
  if (lcd_xpos+5>=160)
6
7
  {  lcd_xpos=0;
8
9
    if (lcd_ypos+7>=80)
10
11
      lcd_ypos=0;
12
13
  }
14
15
  for (i=0; i<8; i++)
16
17
  {  c=pgm_read_byte(&font[ b ][ i ]);
18
19
    for (j=0; j<6; j++)
20
21
    {  if (c&(32>>j))
22
23
        lcd_setpixel(lcd_xpos+j,lcd_ypos+i);
24
25
      else
26
27
        lcd_clrpixel(lcd_xpos+j,lcd_ypos+i);
28
29
    }
30
31
  }
32
33
  lcd_xpos+=6;
34
35
}
36
37
void lcd_writestring(const char *string)
38
{
39
    do
40
    {
41
        lcd_writechar(*string);
42
    }
43
    while (*string++);
44
}

Und hier noch die main.c wo ich das ganze aufrufe:
1
#define F_CPU 3686400UL
2
3
#include <avr/io.h>
4
5
#include <avr/interrupt.h>
6
7
#include <avr/pgmspace.h>
8
9
#include <util/delay.h>
10
#include <stdlib.h>
11
12
#include <portbits.h>
13
14
15
#include "lc7981.h"
16
#include "adc.h"
17
#include "touch.h"
18
19
20
int main(void)
21
22
{
23
24
  lcd_init();  // LCD Controller initialisieren
25
26
  uint16_t x_value; // Varaiable für den X Wert
27
  uint16_t y_value; // Varaiable für den Y Wert
28
  char string_x[8]; // Varaiable für den X Wert gewandelt in char
29
  char string_y[8]; // Varaiable für den Y Wert gewandelt in char
30
  for(;;)
31
  {
32
    x_value = readTouchX();     // X Touch Wert lesen
33
    utoa(x_value, string_x, 10);     // INT in STRING wandeln
34
    lcd_gotoxy(1,50);      // Auf LCD Position springen
35
    lcd_writestring(string_x);    // X Wert schreiben
36
    lcd_gotoxy(32,50);      // Auf LCD Position springen
37
    lcd_writestringP(PSTR(": X Wert"));  // String dahinter schreiben
38
    
39
    y_value = readTouchY();      // X Touch Wert lesen
40
    utoa(y_value, string_y, 10);     // INT in STRING wandeln
41
    lcd_gotoxy(1,60);      // Auf LCD Position springen
42
    lcd_writestring(string_y);    // X Wert schreiben
43
    lcd_gotoxy(32,60);      // Auf LCD Position springen
44
    lcd_writestringP(PSTR(": Y Wert"));  // String dahinter schreiben
45
46
  }
47
48
}

Ich hoffe das hilft weiter.

Den Fehler habe Ich immer noch nicht gefunden :/

Mfg Elias

von Stefan E. (sternst)


Lesenswert?

Also ist es das hier:

Karl heinz Buchegger schrieb:
> Es könnte auch sein (auch ein beliebter Fehler), dass du übersehen hast,
> dass eine neue Ausgabe die alte überschreibt. Ist die neue Ausgabe
> kürzer als die alte, dann bleibt klarerweise von der alten Ausgabe etwas
> übrig.
> Wenn man die Zahl 1018 mit 99 überschreibt, dann steht am Display 9918
> und das kann schnell mal zu Konfusion führen.

Ich sehe nämlich nur lauter lcd_gotoxy/lcd_writestring-Kombinationen, 
ohne dass mal irgendwo irgendetwas auf dem LCD gelöscht würde.

Übrigens:
Dein lcd_writestring gibt auch immer die Null-Terminierung mit aus.

von Elias B. (bouni)


Lesenswert?

Hallo Karl Heinz,

Der Tipp mit dem LCD löschen hat mich schon entscheidend weitergebracht 
:)

Die 0 funktioniert jetzt.
Aber dafür flackert das Display :/

Hab aber n Work around :) Nicht schön aber selten :D
1
  for(;;)
2
  {
3
    x_value = readTouchX();       // X Touch Wert lesen
4
    if(x_value > 0)
5
      {
6
      utoa(x_value, string_x, 10);     // INT in STRING wandeln
7
      lcd_gotoxy(1,50);      // Auf LCD Position springen
8
      lcd_writestring(string_x);    // X Wert schreiben
9
      }
10
    else
11
      {
12
      lcd_gotoxy(1,50);      // Auf LCD Position springen
13
      lcd_writestring("0   ");    // X Wert schreiben
14
      lcd_gotoxy(32,50);      // Auf LCD Position springen
15
      lcd_writestringP(PSTR(": X Wert"));  // String dahinter schreiben
16
      }    
17
    y_value = readTouchY();        // X Touch Wert lesen
18
    if(y_value > 0)
19
      {
20
      utoa(y_value, string_y, 10);     // INT in STRING wandeln
21
      lcd_gotoxy(1,60);      // Auf LCD Position springen
22
      lcd_writestring(string_y);    // X Wert schreiben
23
      }
24
    else
25
      {
26
      lcd_gotoxy(1,60);      // Auf LCD Position springen
27
      lcd_writestring("0   ");    // X Wert schreiben
28
      lcd_gotoxy(32,60);      // Auf LCD Position springen
29
      lcd_writestringP(PSTR(": Y Wert"));  // String dahinter schreiben
30
      }
31
32
  }

Du hast ausserdem geschrieben das:
"Übrigens:
Dein lcd_writestring gibt auch immer die Null-Terminierung mit aus."

Wie kann ich das umgehen ?


Gruss Elias

von Karl H. (kbuchegg)


Lesenswert?

Elias B. schrieb:
> Hallo Karl Heinz,
>
> Der Tipp mit dem LCD löschen hat mich schon entscheidend weitergebracht
> :)
>
> Die 0 funktioniert jetzt.
> Aber dafür flackert das Display :/

Das wundert mich nicht gerade.
Deine Routinen werden ziemlich langsam sein, wenn du keine Optimierung 
machst sondern ständig alles auf Pixelebene runterbrichst. Aber das soll 
jetzt (noch) nicht das Problem sein :-)

> Hab aber n Work around :) Nicht schön aber selten :D

Und nicht wirklich zielführend :-)

Aber eins nach dem anderen

> Du hast ausserdem geschrieben das:
> "Übrigens:
> Dein lcd_writestring gibt auch immer die Null-Terminierung mit aus."
>
> Wie kann ich das umgehen ?

Indem du die writeString Funktion so veränderst, dass sie das 
abschliessende '\0' Zeichen eines jeden Strings nicht mit ausgibt.
1
void lcd_writestring(const char *string)
2
{
3
  while (*string)
4
    lcd_writechar(*string++);
5
}

Zu deinem Problem.
Was ist denn eigentlich das Problem?

Auf dem LCD steht zb 1023
Jetzt gehst mit dem gotoxy wieder auf den Anfang an dieser Stelle und 
schreibst 78 drüber.
Was steht dann am LCD? Nein da steht nicht 78. Da steht 7823. Dies 
deshalb, weil ja niemand dafür gesorgt hat, dass auch die 23 von 1023 
überschrieben werden.

-> Die einfachste Lösung ist es, für Zahlenausgaben eine Feldbreite 
einzuführen. Also: Die Zahl wird immer in einem Feld der Breite 4 
ausgegeben. Ist die Textdarstellung einer Zahl kleiner als dieses Feld, 
dann wird mit Leerzeichen aufgefüllt. Man kann links auffüllen oder auch 
rechts auffüllen. Füllt man links auf, dann hat man den Vorteil, dass 
die Einerstelle der Zahl immer an der gleichen Position an der 
Ausgabestelle erscheint. Das ist insofern gut, als es beim Lesen hilft, 
wenn die Zahl nicht konstant ist und sich ständig ein wenig ändert. Die 
Zahl springt dann nicht am Display hin und her, wenn sie zb ständig 
zwischen 102 und 99 pendelt.

Wie kannst du auffüllen?
Ganz einfach: Stell die Stringlänge fest und gib die fehlende Anzahl an 
Leerzeichen vorher aus.

Und damit sind wir beim nächsten Punkt:
Eine Zahlenausgabe ist in einem Progamm meistens eine zentrale 
Operation. So wie es auch eine Stringausgabe ist. Es ist daher 
vernünftig sich dafür eine Funktion zu schreiben und nicht den Code 
dafür über das komplette Program x-mal zu verstreuen
1
void lcd_writeuint( uint16_t Number, uint8_t fieldWidth )
2
{
3
  char Buffer[10];
4
  int8_t Missing, i;
5
6
  utoa( Number, Buffer, 10 );
7
8
  Missing = fieldWidth - strlen( Buffer );
9
  for( i = 0; i < Missing; ++i )
10
    lcd_writechar( ' ' );
11
12
  lcd_writestring( Buffer );
13
}

Damit vereinfacht sich deine Hauptschleife zu
1
  for(;;)
2
  {
3
    x_value = readTouchX();       // X Touch Wert lesen
4
    lcd_gotoxy(1,50);      // Auf LCD Position springen
5
    lcd_writeuint(x_value,4);
6
    lcd_gotoxy(32,50);      // Auf LCD Position springen
7
    lcd_writestringP(PSTR(": X Wert"));  // String dahinter schreiben
8
9
    y_value = readTouchY();        // X Touch Wert lesen
10
    lcd_gotoxy(1,60);      // Auf LCD Position springen
11
    lcd_writeuint(y_value,4);
12
    lcd_gotoxy(32,60);      // Auf LCD Position springen
13
    lcd_writestringP(PSTR(": Y Wert"));  // String dahinter schreiben
14
  }

Und noch eine Anmerkung:
Schau dir deine Kommentare noch einmal an.
Und dann beantworte mal die Frage:
Was erzählt mir eigentlich der Kommentar, was ich Quelltext nicht auch 
so sehe?

    lcd_gotoxy(32,50);      // Auf LCD Position springen

Super. Der Funktionsaufruf heisst lcd_gotoxy. Das dieser Funktionsaufruf 
die Ausgabeposition irgendwo hinstellt, weis ich auch so. Was steht im 
Kommentar? Da steht, dass die Ausgapeposition irgendwo hin gestellt 
wird. Was hat mir also der Kommentar erzählt, was ich nicht auch im Code 
sehe? Gar nichts.

    lcd_writestringP(PSTR(": Y Wert"));  // String dahinter schreiben

Selbiges. Der Funktionsaufruf heißt lcd_writestringP. Selbst wenn ich 
nichts über die Funktion wüsste, was würde ich wohl ohne groß 
nachzudenken annehmen was die Funktion macht? Der Name fängt mit lcd_ 
an, es wird sich also um eine Funktion handeln, die irgendwas mit dem 
LCD macht. Dann kommt writestring. Gut da muss ich eine Weile optisch 
suchen um zu erkennen dass es sich um 2 Wörter handelt. writeString wäre 
leichter zu lesen, aber was solls. write ... also eine Ausgabe. 
...String, also ein String. Die Funktion wird als einen String auf das 
LCD schreiben. Das schiesst mir in 2 Zehntelsekunden durch den Kopf wenn 
ich lcd_writeString lese. Und was erzählt mir der Kommentar "String 
dahinter schreiben". Also genau dasselbe!
Wozu dann der Kommentar?
Das einzige was ich mir eventuell nicht beim Lesen des Funktionsaufrufs 
erklären kann ist das P im Funktionsnamen. Aber das erklärt mir auch der 
Kommentar nicht, was es damit auf sich hat.

Erkläre in einem Kommentar nicht das 'Wie'. Erkläre das 'Warum'! Und 
wenn du der Ansicht bist, dass das 'Warum' aus dem Programmflus an 
dieser Stelle selbsterklärend ist, dann lass den Kommentar weg! Denn 
dann kann er auch nicht falsch sein, wie zb hier
1
    PORTA |=  (1 << PA2); // PA0 HIGH X1
2
    PORTA &= ~(1 << PA3); // PA2 LOW  X2

Hier erklärst du wiederrum das 'Wie', aber nicht das 'Warum'. 
Blöderweise ist das 'Wie' auch noch falsch kommentiert.

von Bouni (Gast)


Lesenswert?

Hallo Karl Heinz,

erst einaml Danke für deine sehr aufschlussreiche Antwort!!

Zum Thema Kommentare:
Aller Anfang ist schwer, ich hab so of schon gelesen "Kommentier 
gefälligst deinen Code" und dachte viel hilft viel.
Aber deine Kritik leuchtet ein und ich gelobe besserung :)

Gruss Elias

von Karl H. (kbuchegg)


Lesenswert?

Bouni schrieb:

> Zum Thema Kommentare:
> Aller Anfang ist schwer,

Ich weiß.
Sinnvoll kommentieren ist eines der schwersten Dinge.

> ich hab so of schon gelesen "Kommentier
> gefälligst deinen Code"
... und niemand sagt einem was sinnvoll ist :-)

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.