Hallo,
animiert durch eine Diskussion zum Thema CGRam, und als Dankeschön, hier
mal ein CodeSchnipsel mit etwas anderem Ansatz.
Vorwort:
Obwohl mittlerweile grafische und farbige Displays den Text-LCDs den
Rang ablaufen haben diese durchaus noch eine Daseinsberechtigung durch
Ihre einfache Ansteuerung und Anschaffung. Mit etwas Geduld hat man ja
heute innerhalb von 4 Wochen für einen guten Euro ein 16x2 Modell im
Briefkasten.
Und für kleinere µC-Projekte muss es ja nicht immer Grafik sein... aber
ganz ohne ist irgendwie auch doof... daher haben alle HD44780 und
kompatible einen Speicherbereich für 8 eigenen Zeichen (5*8), die man
beliebig belegen kann, und - da es sich um Ram handelt - auch beliebig
oft.
Beim "Standardansatz" werden nun diese Zeichen vorbelegt und der Bargraf
daraus zusammengesetzt.
Das bedeutet man benötigt mindestens 6 Zeichen für einen horizontalen
und mindestens 9 Zeichen für einen vertikalen Bargraf. Hier kann man
sich zur Not dann z.B. mit Leerzeichen und Vollzeichen(0xFF) behelfen
wenn man nicht genügend freie eigene Zeichen zur Verfügung hat, was
jedoch die Designs wiederum stark einschränkt.
Die Auflösung dieser Bargrafen liegt dann auch in einem überschaubaren
Bereich:
Horizontal: Spalten*5 (z.B. 16x5=80 Werte)
Vertikal: Zeilen*8 (z.B. 4x8=32 Werte)
Erweiterte Herangehensweise:
Die eigenen Zeichen werden erst dann belegt oder geändert, wenn der
anzuzeigende Wert bekannt ist.
Dieser Ansatz erlaubt eine dtl. höhere Auflösung und benötigt weniger
eigene Zeichen pro Bargraf (hier 3) auch verfügt der Bargraf über einen
Rand, um dem User zu vermitteln wo dieser endet. Ob horizontal oder
Vertikal ist hierbei wurscht, das folgende Beispiel ist horizontal, was
bei den meisten Displays wohl mehr Sinn macht.
(Mit leichter Anpassung im Code wären bis zu 6 solcher Bargrafen in
horizontal oder vertikal gleichzeitig auf einem LCD möglich, in der
vorliegenden Variante sind 2 gleichzeitig erlaubt, was für die meisten
LCDs ausreichen sollte.)
Das Bild im Anhang zeigt die Anwendung beispielhaft auf einem 20x4 LCD,
die Auflösung beträgt hier 400 Werte. Verzichtet man auf den Rand wären
800 möglich (bei 20 Spalten). Dargestellt wird dies durch den letzten
Balken, der -wertabhängig- nur anteilig eingeblendet wird. Benötigt man
diese Auflösung nicht, hat man mit dem Ansatz zumindest eigene Zeichen
gespart:
Code kann frei verwendet oder angepasst werden,
von der Anwendung sind 3 Funktionen bereitzustellen:
1 | void LCD_setCGRamAddress(uint8_t address);
|
2 | void LCD_goto(column, row); //DDRAM
|
3 | LCD_writeCharacter(uin8_t data); //unsigned char
|
Viel Spaß damit...
1 | #define BAR_LINE_0 0b00000000
|
2 | #define BAR_LINE_5 0b00011111
|
3 | #define BAR_LINES 4 //3 or 4 allowed
|
4 | #define BAR_PIXEL_PER_CHAR (BAR_LINES*5ul)
|
5 |
|
6 | void LCD_drawBarGraphHorizontal(uint8_t column, uint8_t row, uint8_t width, uint16_t value, uint16_t maxValue, uint8_t uCharOffset)
|
7 | {
|
8 | uint16_t scaledValue=((uint32_t) value)*(((uint32_t) (width)))*BAR_PIXEL_PER_CHAR/((uint32_t) maxValue);
|
9 | uint8_t scaledValueCharPosition=scaledValue/BAR_PIXEL_PER_CHAR;
|
10 | uint8_t scaledValueRemainder=scaledValue%BAR_PIXEL_PER_CHAR;
|
11 |
|
12 | LCD_setCGRamAddress(uCharOffset*8);
|
13 | //Full Element UDF 0
|
14 | LCD_writeCharacter(BAR_LINE_5);
|
15 | LCD_writeCharacter(BAR_LINE_0);
|
16 | LCD_writeCharacter(BAR_LINE_5);
|
17 | LCD_writeCharacter(BAR_LINE_5);
|
18 | LCD_writeCharacter(BAR_LINE_5);
|
19 | if(BAR_LINES==3)
|
20 | {
|
21 | LCD_writeCharacter(BAR_LINE_0);
|
22 | LCD_writeCharacter(BAR_LINE_5);
|
23 | LCD_writeCharacter(BAR_LINE_0);
|
24 | }
|
25 | else
|
26 | {
|
27 | LCD_writeCharacter(BAR_LINE_5);
|
28 | LCD_writeCharacter(BAR_LINE_0);
|
29 | LCD_writeCharacter(BAR_LINE_5);
|
30 | }
|
31 | //Scaled Element UDF 1
|
32 | LCD_writeCharacter(BAR_LINE_5);
|
33 | LCD_writeCharacter(BAR_LINE_0);
|
34 |
|
35 | uint8_t line;
|
36 | for(int8_t lineId=(BAR_LINES-1); lineId>=0; lineId--)
|
37 | {
|
38 | line=BAR_LINE_0;
|
39 | for(uint8_t digit=0; digit<5; digit++)
|
40 | {
|
41 | if(scaledValueRemainder>((digit*BAR_LINES)+lineId))
|
42 | {
|
43 | line|=(0b00010000>>digit);
|
44 | }
|
45 | }
|
46 | LCD_writeCharacter(line);
|
47 | }
|
48 | if(BAR_LINES==3)
|
49 | {
|
50 | LCD_writeCharacter(BAR_LINE_0);
|
51 | LCD_writeCharacter(BAR_LINE_5);
|
52 | LCD_writeCharacter(BAR_LINE_0);
|
53 | }
|
54 | else
|
55 | {
|
56 | LCD_writeCharacter(BAR_LINE_0);
|
57 | LCD_writeCharacter(BAR_LINE_5);
|
58 | }
|
59 | //Empty Element UDF 2
|
60 | LCD_writeCharacter(BAR_LINE_5);
|
61 | LCD_writeCharacter(BAR_LINE_0);
|
62 | LCD_writeCharacter(BAR_LINE_0);
|
63 | LCD_writeCharacter(BAR_LINE_0);
|
64 | LCD_writeCharacter(BAR_LINE_0);
|
65 | LCD_writeCharacter(BAR_LINE_0);
|
66 | if(BAR_LINES==3)
|
67 | {
|
68 | LCD_writeCharacter(BAR_LINE_5);
|
69 | LCD_writeCharacter(BAR_LINE_0);
|
70 | }
|
71 | else
|
72 | {
|
73 |
|
74 | LCD_writeCharacter(BAR_LINE_0);
|
75 | LCD_writeCharacter(BAR_LINE_5);
|
76 | }
|
77 |
|
78 | LCD_goto(column, row);
|
79 | for(uint8_t charPosition=0; charPosition<width; charPosition++)
|
80 | {
|
81 | if(charPosition<scaledValueCharPosition)
|
82 | LCD_writeCharacter(0);
|
83 | else if(charPosition==scaledValueCharPosition)
|
84 | LCD_writeCharacter(1);
|
85 | else
|
86 | LCD_writeCharacter(2);
|
87 | }
|
88 | }
|
P.S.: Char 0 und 2 kann man natürlich auch in eine Init-Funktion
auslagern, falls das restliche Programm diese Zeichen nicht nutzt, damit
fallen dann wesentlich weniger LCD-Schreibzyklen an.