LCD Progress Bar (Balkenanzeige)

OP #3838417
Lesenswert?

Ich möchte gerne den Wert eines ADC als Balkengrafik auf einem 16x2 LCD 
darstellen.
Dafür habe ich mir schon mal einen selbst definierten Zeichensatz 
erstellt.
Den ersten Schritt dazu habe ich bereits gemacht.
Indem ich mal einfach den ADC Wert 1-5 als Zeichen ausgebe.

Jetzt müßte ich jedoch nach jedem dieser Schritte auf die nächste 
Cursorposition stellen und den Zähler zurücksetzen.

Hat jemand einen Tipp wie man hier am besten ran geht. Oder wie würdet 
ihr das machen?

Ein funktionierendes Beispiel dazu hätte ich hier zwar gefunden: 
https://www.electronicsblog.net/arduino-lcd-horizontal-progress-bar-using-custom-characters/

Dies bläht den Speicher des ATmega 8 allerdings wieder dermaßen auf, da 
hier mit Kommastellen gerechnet wird.

Aber das müßte doch Platzsparender gehen, oder?
Gast #3838436
Lesenswert?

Lokus Pokus schrieb:
> Hat jemand einen Tipp wie man hier am besten ran geht. Oder wie würdet
> ihr das machen?
Ganze Blöcke ausgeben und das Wandlungsergebnis runterzählen, bis der 
Rest kleiner als 5 oder 6 ist und dann das passende Sonderzeichen mit 
1..4 Elementen.

> Dies bläht den Speicher des ATmega 8 allerdings wieder dermaßen auf, da
> hier mit Kommastellen gerechnet wird.

Wozu das. Dein Display hat bestenfalls 80 oder 96 Elemente. Da reicht 
Festkommaarithmetik von der Dynamik völlig aus. Ein Komma kannst du 
trotzdem in der Anzeige ausgeben.

Was Flash-Speicher kostet, ist die Float Bibliothek.
OP #3838494
Lesenswert?

16 Zeichen mit jeweils 5 Reihen. Also genau 80 Werte wären das.

Der ADC liefert mir auf der Anzeige die Werte von 0 bis 100 (%)
Somit nur Ganzzahlenwerte, keine Nachkommastellen.
1
int main(void)
2
{
3
  init();
4
  char buffer[4];
5
  int stelle = 0;
6

7
  lcd_puts_P("Schwellwert    %\n");        // Text von Flash auf LCD ausgeben
8

9
//  lcd_puts_P("Helligkeit     %\n");        // Text von Flash auf LCD ausgeben
10

11
  for (;;)
12
  {
13
    ADCvalue1 = ADCsingleREAD(1)*100L/1023L;  // ADC Wert auslesen
14
    my_itoa(ADCvalue1, buffer);          // ADC Wert in Text konvertieren
15

16
    lcd_gotoxy(12,0);              // Cursor auf Position 12, Zeile 0
17
    lcd_puts(buffer);              // konvertierten Text auf LCD ausgeben
18
    lcd_gotoxy(stelle,1);              // Cursor auf Position 0, Zeile 1
19

20
    switch (ADCvalue1)
21
    {
22
      case 0:
23
      lcd_puts_P(" ");
24
      break;
25

26
      case 1:
27
      lcd_putc(0);
28
      break;
29

30
      case 2:
31
      lcd_putc(1);
32
      break;
33

34
      case 3:
35
      lcd_putc(2);
36
      break;
37

38
      case 4:
39
      lcd_putc(3);
40
      break;
41

42
      case 5:
43
      lcd_putc(4);
44
      break;
45
    }
46
}
Gast #3838511
Lesenswert?

Lokus Pokus schrieb:
> 16 Zeichen mit jeweils 5 Reihen. Also genau 80 Werte wären das.
>
> Der ADC liefert mir auf der Anzeige die Werte von 0 bis 100 (%)
> Somit nur Ganzzahlenwerte, keine Nachkommastellen.

Dann multiplizierst du deinen ADC-Wert mit 10 und teilst ihn durch 128 
(ganzzahlig). Dann bekommst du die Anzahl der Segmente. Die teilst du 
solange durch 5 und gibst jeweils immer einen 5er-Block aus, bis das 
Ergebnis kleiner als 5 ist. Den Rest gibst du als Zeichen mit der Breite 
des Rests aus (1..4).
Gast #3838527
Lesenswert?

Falk Brunner schrieb:
> Festkommaarithmetik
>
> Immer noch nicht verstanden? Hmmm.

Falls du mich meinst, ich habe mich da nur falsch ausgedrückt.

npn schrieb:
> bis das
> Ergebnis kleiner als 5 ist. Den Rest gibst du als Zeichen mit der Breite
> des Rests aus (1..4).

Soll heißen:
"Dieses Ergebnis gibst du als Zeichen mit der Breite
des Zahlenwertes aus (1..4)."

Der Begriff "Rest" war vielleicht irreführend, weil er klingt, als 
wollte man Kommastellen benutzen und auswerten.
(Firma: Vescomp) #3838531
Lesenswert?

Lokus Pokus schrieb:
> Jetzt müßte ich jedoch nach jedem dieser Schritte auf die nächste
> Cursorposition stellen und den Zähler zurücksetzen.
>
> Hat jemand einen Tipp wie man hier am besten ran geht. Oder wie würdet
> ihr das machen?

 ADC Wert von 0-100
1
  zeichen = Wert % 5;
2
  position = Wert / 5;
3
// Wenn position > 0, einfach in einer Schleife von 0 bis position - 1
4
// Zeichen 5 ausgeben, in position zeichen einschreiben.
 EDIT:
npn schrieb:
> Dann multiplizierst du deinen ADC-Wert mit 10 und teilst ihn durch 128
> (ganzzahlig). Dann bekommst du die Anzahl der Segmente. Die teilst du
 Ja, das solltest du vorher machen, nur mit 100 multiplizieren.
#3838547
Lesenswert?

Hallo Lokus Pokus,

willst Du eine eigene Software implementieren oder ein vorhandenes 
Beispiel bei Dir einbauen?
Wenn Du es selbst implementieren möchtest, gehe ich mangels besserer 
Infos von folgenden Bedingungen aus:

a) Du willst alle 16 Zeichen in einer Reihe verwenden
b) Der Bargraph wächst horizontal von links nach rechts
c) Dein ADC ist Masse bezogen und gibt also Spannungswerte von 0..x aus
d) 12Bit ADC -> Spannung: 0 ... 4095

a+b: Zur Display-Initialisierung müssen 6 Zeichen im  cgram des Displays 
angelegt werden, wobei jedes Zeichen aus 8 Byte besteht, z.B. so:
1
#define MAX_GC_CHARS 6
2
const U8 gc_data[MAX_GC_CHARS][8]={ 
3
{ 0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55 }, // 0/5 [     ]
4
{ 0xba,0x55,0xba,0x55,0xba,0x55,0xba,0x55 }, // 1/5 [#    ]
5
{ 0xba,0x5d,0xba,0x5d,0xba,0x5d,0xba,0x5d }, // 2/5 [##   ]
6
{ 0xbe,0x5d,0xbe,0x5d,0xbe,0x5d,0xbe,0x5d }, // 3/5 [###  ]
7
{ 0xbe,0x5f,0xbe,0x5f,0xbe,0x5f,0xbe,0x5d }, // 4/5 [#### ]
8
{ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }  // 5/5 [#####]
9
};
Das Resultat sieht dann aus, wie in meinem Anhang. In meinem Foto werden 
jedoch statt 16 nur 6 Zeichen auf dem LCD verwendet - das Prinzip ist 
natürlich gleich.

Jetzt nimmst Du Deine 16 Zeichen und entscheidest, welche zu 100% oder 
zu 0% ausgefüllt sind, die bekommen das entsprechende Zeichen. Eines der 
16 LCD-Zeichen ist zum teil gefüllt, hier musst Du dann etwas Code 
bemühen:
4095 ADC-Werte / 5x16 = 51 --> also 1 LCD-Bit innerhalb eines 
LCD-Zeichens stellt 51 Digits Deines ADC dar.

Viel Spaß beim Basteln
Angehängte Dateien:
Gast #3838550
Lesenswert?

Marc Vesely schrieb:
> EDIT:
> npn schrieb:
>> Dann multiplizierst du deinen ADC-Wert mit 10 und teilst ihn durch 128
>> (ganzzahlig). Dann bekommst du die Anzahl der Segmente. Die teilst du
>  Ja, das solltest du vorher machen, nur mit 100 multiplizieren.

Sorry, war mein Fehler. Weil der TO von ADC geschrieben hatte, ging ich 
von einem Maximal-ADC-Wert von 1023 aus, deshalb die 10. Dann klappt der 
Rechenweg auch, wie ich ihn geschrieben hatte. Einverstanden? :-)
Gast #3838558
Lesenswert?

Auf jeden Fall löse dich von der Annahme, Fließkomma Zahlen zu 
benötigen.

Der ADC des AVR Mikrocontrollers meldet (z.B.) 4,99 Volt als 1023. Na 
klingelt es? Der ADC braucht dazu auch keine Fließkommazahlen.

Mal ein ganz simples Beispiel: Angenommen, du wilst 50 Klötzchen bei 5 
Volt anzeigen. Dann teilst Du einfach den Meßwert des ADC durch 20 
(benzugnehmend auf obiges Beispiel). 1024/20=50 und ganz ohne Fließkomma 
Zahlen.

Oder anders gesagt: 1,5 Meter kann man auch als 150 Zentimeter 
bezeichnen - und schon kommt man ohne Fließkommazahlen aus.

Dieses Prinzip kannst Du sicher auch auf Deine Anwendung übertragen.
#3838568
Lesenswert?

Lokus Pokus schrieb:
> Wieso 125, wieso 128? Eure Rechnungen gehen mir nicht ganz ein.
> Ich habe 16 Zeichen. Pro Zeichen 5 Segmente, dass wären 80 Segmente im
> gesammten.
> Der Maximalwert der ADC ist 100. (oder 1023 wenn man ihn direkt
> ausliest)

Was willst du denn jetzt anzeigen?
ADC-Wert bis 80% oder soll der Wert vorher auf die 80 skaliert werden? 
Also ganzer Balken = 100%.

mfg.
#3838570
Lesenswert?

@ Lokus Pokus (derschatten)

>Wieso 125, wieso 128? Eure Rechnungen gehen mir nicht ganz ein.
>Ich habe 16 Zeichen. Pro Zeichen 5 Segmente, dass wären 80 Segmente im
>gesammten.
>Der Maximalwert der ADC ist 100. (oder 1023 wenn man ihn direkt
>ausliest)

Na dann lies du mal den Artikel Festkommaarithmetik und besorg die 
Prozentrechnung, das ist Mathematik Klasse 7 oder so.
Gast #3838580
Lesenswert?

Lokus Pokus schrieb:
> Wieso 125, wieso 128? Eure Rechnungen gehen mir nicht ganz ein.
> Ich habe 16 Zeichen. Pro Zeichen 5 Segmente, dass wären 80 Segmente im
> gesammten.
> Der Maximalwert der ADC ist 100. (oder 1023 wenn man ihn direkt
> ausliest)

1023 x 10 / 128 = 79,92 (gerundet 80)

Alles klar? :-)
#3838607
Lesenswert?

Bei einem Endwert von 1023 kann man es sich vielleicht auch so deutlich 
machen:

Hier sei jedes des 16 LCD-Zeichen durch 5 Character dargestellt. Immer 
nach diesen 5 Charactern (also immer zwischen jedem LCD-Zeichen) steht 
der ADC-Wert auf dieser Anzeige-Skala:

 [#####] [#####] [#####] [##___] [_____] [_____]  ...  [_____]
0      64      128     192     256     320     384    960     1024
Gast #3838615
Lesenswert?

Lokus Pokus schrieb:
> ok, nun weiß ich aber immer noch nicht wie ich nach 5 Segmenten
> zum
> nächsten Zeichen springe.

Du gibst so viele vollständig gefüllte Kästchen aus, wie die Division 
durch 5 ganzzahlig ergibt. Beispiel 76 durch 5 ergibt 15. Also gibst du 
15 vollständig gefüllte Kästchen aus. Dann kommst der Rest. Weil 15 mal 
5 gleich 75 ist und der Ausgangswert aber 76 ist, bleibt ein Rest von 1. 
Also gibst du nach den 15 gefüllten Kästchen noch ein Zeichen aus, was 
ein Segment darstellt. Bei 77 wäre das dann das Zeichen, was 2 Segmente 
darstellt und so weiter. Jetzt klar? :-)
(Firma: Vescomp) #3838634
Lesenswert?

Lokus Pokus schrieb:

Marc Vesely schrieb:
> ADC_Wert von 0-100

  Kor_Wert = ADC_Wert * 100 / 125
1
  zeichen = ADC_Wert % 5;
2
  position = Kor_Wert / 5;
3
// Wenn position > 0, einfach in einer Schleife von 0 bis position - 1
4
// Zeichen 5 ausgeben, in position zeichen einschreiben.
> ok, nun weiß ich aber immer noch nicht wie ich nach 5 Segmenten zum
> nächsten Zeichen springe.
 Du springst nirgendwo hin, höchstens der Cursor, aber auch das
 ist nicht nötig, weil du zuerst die Zeichen mit allen 5 Segmenten
 reinschreibst. Und an letzter Stelle kommt 'zeichen'.
 Ein Zeichen hat 5 Segmente (0-5), Display hat 16 Stellen (0-15).
Gast #3838642
Lesenswert?

Thomas Eckmann schrieb:
> In diesem Sinne:
>
> unsigned char GK = ADC_Wert / 5;
> for(unsigned char i = 0; i < GK; i++)
> {
>   SchreibGanzeKaestchen();
> }
> SchreibRestKaestchen(ADC_Wert % 5);
>
> mfg.

Genau richtig. Wenn man die "Restkaestchen" als Zeichenadresse 1..4 im 
LCD definiert und das LCD 20 Stellen hat, passt das. Wir haben hier aber 
16 Stellen.
Wenn man als maximalen ADC-Wert 100 nimmt, muß die Berechnung lauten:
1
GK = ADC x 100 / 125 / 5
Da kommt maximal 16 raus, die maximale Anzahl der vollständigen 
Kästchen.
GK muß aber uint16_t sein, char reicht hier nicht.
Gast #3838659
Lesenswert?

Mike schrieb:
> Was ist denn hier los?
>
> Nach gefühlten 30 Beiträgen wird es doch wohl möglich sein, per simplem
> Dreisatz von 1023 auf 80 umzuskalieren.
> Das ist nun wirklich Schulmathematik der 7. Klasse ...

Es geht hier nicht vorrangig um Skalierung, sondern wie man mit selbst 
definierten Zeichen im LCD-Display einen Balken darstellt mit der 
Auflösung von einem fünftel Zeichenbreite. Das ist ein klein wenig mehr. 
:-)
OP #3838664
Lesenswert?

in etwa so:
1
  for (;;)
2
  {
3
    ADCvalue1 = ADCsingleREAD(1)*100L/1023L;  // ADC Wert auslesen
4
    my_itoa(ADCvalue1, buffer);          // ADC Wert in Text konvertieren
5

6
    lcd_gotoxy(12,0);              // Cursor auf Position 12, Zeile 0
7
    lcd_puts(buffer);              // konvertierten Text auf LCD ausgeben
8
    lcd_gotoxy(0,1);              // Cursor auf Position 0, Zeile 1
9

10

11
    unsigned char GK = ADCvalue1 * 100 / 125 /5;
12

13
    int peace = ADCvalue1 % 5;
14

15
    for(unsigned char i=0; i < GK; i++)
16
    {
17
      lcd_putc(4);
18
    }
19

20
    switch (peace)
21
    {
22
      case 0:
23
      break;
24

25
      case 1:
26
      lcd_putc(0);
27
      break;
28

29
      case 2:
30
      lcd_putc(1);
31
      break;
32

33
      case 3:
34
      lcd_putc(2);
35
      break;
36

37
      case 4:
38
      lcd_putc(3);
39
      break;
40
    }
41
}

Wie lösch ich nun die Zeichen aber wieder?
#3838668
Lesenswert?

Lokus Pokus schrieb:
> was meinst du mit vollständig gefüllte Kästchen? Ein Zeichen mit 5x7
> Pixel?

Ich hoffe so wird klar, was mit den Begriffen gemeint ist ...

Jedes der 16 LCD-Zeichen besteht aus 5x8 Punkten. Die 8 Punkte liegen in 
einer Reihe übereinander - davon wiederum gibt es 5 Reihen 
nebeneinander. Darum kann ein Segment aus 5 einzelnen "Schritten" 
bestehen, z.B. 3/5 sind "an" wie im angehängten Bild (mit Legende).
Angehängte Dateien:
#3838723
Lesenswert?

Lokus Pokus schrieb:

1
unsigned char GK = ADCvalue1 * 100 / 125 /5;
2

3
int peace = ADCvalue1 % 5;

Das passt noch nicht.

Was ist eigentlich der besondere Sinn von * 100 / 125 ?

1
unsigned char ADC80 = ADCvalue1 * 8 / 10;
2
unsigned char GK = ADC80 / 5;
3
unsigned char peace = ADC80 % 5;
4

5
...

Kümmere dich nicht darum, dass es jetzt eine Variable mehr ist. Der 
Compiler wird das optimeren.

Lokus Pokus schrieb:
> Muß nicht schattig sein. Reicht eine Leerstelle.

Mach vor der Ausgabe eine komplette Leerzeile. Bring das erstmal damit 
zum Laufen. Damit optimeren, dass nur ab dem peace-Zeichen gelöscht 
wird, kannst du immer noch.

mfg.
OP #3838739
Lesenswert?

Ich glaube ich habe es fast:
1
  for (;;)
2
  {
3
    ADCvalue1 = ADCsingleREAD(1)*100L/1023L;  // ADC Wert auslesen und in % umwandeln
4
    my_itoa(ADCvalue1, buffer);          // ADC Wert in Text konvertieren
5

6
    lcd_gotoxy(12,0);              // Cursor auf Position 12, Zeile 0
7
    lcd_puts(buffer);              // konvertierten Text auf LCD ausgeben
8
    lcd_gotoxy(0,1);              // Cursor auf Position 0, Zeile 1
9

10

11
    unsigned char ADC80 = ADCvalue1*8/10;
12
    unsigned char GK = ADC80 / 5;
13
    unsigned char peace = ADC80 % 5;
14

15
    for(unsigned char i=0; i<GK; i++)
16
    {
17
      lcd_putc(4);
18
    }
19

20
    switch (peace)
21
    {
22
      case 0:
23
      break;
24

25
      case 1:
26
      lcd_putc(0);
27
      break;
28

29
      case 2:
30
      lcd_putc(1);
31
      break;
32

33
      case 3:
34
      lcd_putc(2);
35
      break;
36

37
      case 4:
38
      lcd_putc(3);
39
      break;
40
    }
41

42
      for(unsigned char i=0; i<GK; i++)
43
      {
44
      lcd_puts_P(" ");
45
      }
46
}

Beim löschen bleibt mir jetzt nur noch ein Strich übrig, den ich weg 
bekommen muß.
#3838749
Lesenswert?

@Lokus Pokus (derschatten)
1
  for (;;)
2
  {
3
    ADCvalue1 = ADCsingleREAD(1)*100L/1023L;  // ADC Wert auslesen und in % umwandeln
4
    my_itoa(ADCvalue1, buffer);          // ADC Wert in Text konvertieren
5

6
    lcd_gotoxy(12,0);              // Cursor auf Position 12, Zeile 0
7
    lcd_puts(buffer);              // konvertierten Text auf LCD ausgeben
8
    lcd_gotoxy(0,1);              // Cursor auf Position 0, Zeile 1
9

10

11
    unsigned char ADC80 = ADCvalue1*8/10;
12
    unsigned char GK = ADC80 / 5;
13
    unsigned char peace = ADC80 % 5;
14

15
    // ganze Kaestchen
16
    for(unsigned char i=0; i<GK; i++)
17
    {
18
      lcd_putc(4);
19
    }
20
    
21
    // Teilkaestchen
22
    if (peace>0) lcd_putc(peace-1); // Frieden?
23

24
    // Rest leere Kaestchen
25
    for(; i<16; i++)
26
    {
27
      lcd_putc(' ');
28
    }
29
}

P S Du meinst mit peace wohll eher piece, was aber hier im 
technisch/mathematischen Bereich eher als fraction bezeichnet wird ;-)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren