2014
 |
Rheinturmuhr mit Arduino auf TFT-Display
Die Seite "Rheintumuhr mit Arduino" scheint oft aufgerufen zu werden. Das Projekt aus 2010 wird nun ergänzt mit der TFT-Variante, da diese Displays inzwischen für einstellige Eurobeträge erhältlich sind. Dadurch sind 39 Leuchtdioden zu Darstellung fast zu teuer. Ist auch noch ein SD-Karten-Slot vorhanden, so kann die Uhr sogar als animiertes Foto erscheinen.
|
Mit dem SainSans 1.8-Zoll-TFT-Display für unter € 10 werden keine LED mehr benötigt, da quasi 20480 Leuchtdioden in Form von Pixel verfügbar sind. Auf diesen 128x160 Pixel sollte ein Rheinturm seinen Platz finden. Benötigt wird:
- Arduino Uno mit Programmierumgebung
- SainSans-TFT-Display 128x160 Pixel
- SD-Karte (optional)
|
Die Lampen sind dann wohl sehr klein, aber auch fein. In Wirklichkeit sind sie hier ganze zwei Pixel breit, so dass die Uhr kurz unter dem Restaurant aufhört. Die Zwischenräume wurden so gewählt, dass die Antenne auch noch abgebildet werden kann und so der gesamte Turm Platz findet.
Die Details zum Anschluss dieses Displays werden im Netz schon ausführlich gezeigt. Darum nur der Hinweis auf die Seite Tweaking4All, die sehr ausführlich zeigt, wie zu verfahren ist, um das Display zu testen. Auch die Unterschiede zum ebenfalls weit verbreiteten Adafruit-TFT sind dort sehr gut und deutlich - ja fast unübersehbar - dokumentiert.
|
Bibliotheken Software-Vorbereitung
|
| Außer einer Spannungsversorgung (Vcc und Masse) werden für die Display-Ansteuerung nur fünf Verbindungen benötigt. Durch die serielle Datenübertragung ist Geschwindigkeit etwas gedrosselt, aber die Vorteile der wenig aufwendigen Verdrahtung dürften überwiegen. Außerdem werden in diesem Projekt nur jede Sekunde Änderungen in der Anzeige notig. Nach dem Auspacken der Hardware und dem Bereitlegen der sieben Verbindungsstücke muss zunächst die entsprechende Bibliothek verfügbar sein. Auch dies wird auf Tweaking4All, erläutert. Es handelt sich um Adafruit_GFX.zip und Adafruit_ST37775.zip. Später wird noch die SD.zip benötigt, um die SD-Karte anzusprechen. Die Bibliotheken werden entpackt und nach z.B. unter Windows "C:\Users\....\arduino-1.0.6\libraries" kopiert. Beim nächsten Start der IDE sollten diese Bibliotheken nun auch mit Beispiele auswählbar sein. |
Verdrahtung Hardware-Vorbereitung
|
Tweaking4All zeigt wie das 1.8"-Display mit dem Arduino verbunden wird. Einmal als Tabelle und auch als Bild. Dort werden auch die beiden Varianten für Low-Speed und High-Speed getrennt aufgeführt. Dieses Projekt benutzt nur die High-Speed-Variante, damit alles flüssig läuft. Betrachtet man die Anzeige von hinten und nummeriert die Anschlüsse beginnend mit der Spannungsversorgung, ergibt sich folgende Tabelle
|
Display
|
Arduino
|
01 VCC
02 GND
03 SCL
04 SDA
05 RS/DC
06 RES
07 CS
08 MSO
09 SCLK
10 MOSI
11 CS
|
VCC
GND
13 SCL
11 SDA
09 RS/DC
08 RES
10 CS
|
Zu beachten sind die Verbindungen 8 und 9 beim Arduino. Diese sind in der Adafruit-Variante vertauscht, wodurch die Beispiele aus der Bibliothek ohne diese Änderung nicht funktionieren. Wenn alles richtig aufgebaut ist, können die Beispiele aufgerufen und getestet werden.
Somit wäre die Initialisierung des Displays wie folgt im Sketch:
#include <SPI.h>
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#define TFT_CS 10
#define TFT_RST 8
#define TFT_DC 9
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
Zum Schluss wird ein TFT-Display-Objekt mit dem Namen 'tft' angelegt.
|
Turmbau Dreiecke und Rechtecke
|
| Mit zwei Dreiecken und zwei Rechtecken entsteht ein grafisches Kunstwerk - 'The Towerart' - welches den Turm darstellt. Das obere Dreieck stilisiert die Plattform und das untere Dreieck den eigentlichen Mast vom Boden bis zur Spitze. Die Rechtecke symbolisieren die Antennen-Ebenen oberhalb der Plattform. Außer den Grafik-Routinen zum Zeichnen stellt die Bibliothek auch eine RGB-Methode bereit. Mit ihr können Farben auf die vertraute Rot-Grün-Blau-Art gesetzt werden, auch wenn das Display nicht alles darstellen kann. RGB(255,0,0) wäre dann z. B. volles Rot. Die Methode nennt sich Color565(red,green,blue) und wurde im Sketch hier mit einem 'define' kurz zu 'rgb' umdefiniert. Im Anschluss folgen die Definitionen der zu verwendenden Grafik-Farben:
#define rgb Color565
#define BACK rgb(0,0,255)
#define TOWER rgb(63,63,63)
#define LAMP_ON ST7735_YELLOW
#define LAMP_OFF rgb(127,127,127)
#define LAMP_POS rgb(255,0,0)
#define DIGITS LAMP_ON
...
In der setup()-Routine erfolgt das einmalige Zeichnen des Turms. Als Hilfsvariablen werden die halbe Bildschirmbreite w2 und die Bildschirmhöhe h benutzt. Der Bildschirmhintergrund wird mit der Farbe BACK gezeichnet, der Turm erhält die Farbe TOWER.
...
int w2=tft.width()/2;
int h=tft.height();
tft.fillScreen(BACK);
tft.fillTriangle(w2-15,50,w2+15,50,w2+1,75,TOWER);//V
tft.fillTriangle(w2+1,5,w2-6,h,w2+8,h,TOWER);// ^
tft.fillRect(w2-14,43,30,3,TOWER);//=
tft.fillRect(w2-11,36,24,3,TOWER);//-
...
|
Lampen 2x1 Pixel
|
D ie Uhr setzt sich aus insgesamt 39 Lampen zusammen. Diese Darstellung ist im Netz ausführlich beschrieben. Damit die Anzeige erkennbar bleibt, wurde die Größe der Lampen zu 2x1 Pixel festgelegt. Mit der Methode 'Versuch und Irrtum' erfolgte die Ermittlung für dieses TFT-Display experimentell. Gezeichnet wird mit der schnellen drawFastHLine-Routine und nicht mit zweimaligem Aufruf von drawPixel. Die x-Koordinate wird auf die Bildschirmmitte gesetzt, die Startposition der y-Koordinate (unterste Lampe) auf die Bildschirmhöhe minus 8 Pixel, also etwas über dem Boden. Die Variablen i enthält Nummer der Lampe, die aktuell verarbeitet und positioniert werden soll. Die do-Schleife arbeitet alle 39 Lampen ab. Im Dekoder (weiter unten) wird entschieden, ob die Variable 'is' den Wert true oder false hat, die aktuelle Lampe die Farbe LAMP_ON oder LAMP_OFF bekommt. Die y-Koordinate wird um 2 Pixel geändert für die nächste Lampe und als Zwischenraum der Lampenblöcke (9, 5, 9, 5, 9, 2) nochmals 4 Pixel. Zwischen die Blöcke werden die 'Positionsleuchten' aus Jux als Kreise mit dem Radius 1 gezeichnet mit der Farbe LAMP_POS. Auf dem Display erscheinen dann Kreuze aufgrund der Auflösung.
x=tft.width()>>1;
y=tft.height()-8;
i=1; //1-39 Lamps
do //The Clock
{... //Decoder
tft.drawFastHLine(x, y, 2, is ? LAMP_ON : LAMP_OFF);
y-=2;
if(i==9 || i==9+5 || i==9+5+9 || i==9+5+9+5 || i==9+5+9+5+9)
{tft.fillCircle(x-2, y-1, 1, LAMP_POS);
tft.fillCircle(x+2, y-1, 1, LAMP_POS);
y-=4;
}
}while(++i<=39); //all done
|
Dekoder Fallunterscheidung
|
| Die Fallunterscheidung switch mit den vielen case in der C-Syntax ist sehr schreibintensiv, trotzdem übersichtlicher als endlose if-then-else-Verschachtelungen. Für jede Lampe i wird überprüft, ob sie an oder aus ist. Die logische Variable 'is' enthält diese Information in der Form true/false und wird anschließend benutzt, um die Farbe für die aktuelle Lampe festzulegen. Die 23. Lampe ist demnach an, wenn die Einer-Stelle der Minuten m01 größer als die Differenz zwischen der aktuellen Lampe (23) und der ersten Lampe (15) in ihrem Block ist. An dieser Stelle möge sich der fortgeschrittene Programmierer mit eleganterem Kode schmücken.
switch (i) //Decode Time To Lamp
{case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8:
case 9: is=s01>(i-1); break;
case 10: case 11: case 12: case 13: case 14: is=s10>(i-10); break;
case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22:
case 23: is=m01>(i-15); break;
case 24: case 25: case 26: case 27: case 28: is=m10>(i-24); break;
case 29: case 30: case 31: case 32: case 33: case 34: case 35: case 36:
case 37: is=h01>(i-29); break;
case 38: case 39: is=h10>(i-38); break;//Hours
}
Die eigentliche Uhr wurde aus der LED-Variante der Rheinturmuhr übernommen. Mit Hilfe der Arduino-Routine millis() wird der Sekundentakt abgeleitet. Für eine Demonstration ist das sicher ausreichend. Funk-, GPS-, TC/IP und RTC-Uhr-Module gibt es für einen genaueren Lauf. In dieser Variante läuft die Uhr 10x schneller, wenn der Analogeingang A0 auf Masse gelegt wird. Dies wurde für Demonstrationszwecke eingebaut und könnte ein Ansatz zum Stellen der Uhr sein.
//Text Time Display
tft.setCursor(5, tft.height()-12);
sprintf(s,"%0d%0d:%0d%0d:%0d%0d",h10,h01,m10,m01,s10,s01);
tft.print(s);
while(TIMEREAD()<(t+interval)); //waiting ...
1000 ms or 100 ms
s01++; //s++;
if(s01>=10){s01=0;s10++;}
if(s10>=6){s10=0;m01++;} //m++;
if(m01>=10){m01=0;m10++;}
if(m10>=6){m10=0;h01++;} //h++;
if(h01>=10){h01=0;h10++;}
if(h10>=2 && h01>=4){h10=0;h01=0;}
interval=analogRead(A0)==0?100:1000;
|
Quelltext RhineTowerArt gesamt
|
| Die Darstellung des Ergebnisses als Foto ist relativ blass. In Wirklichkeit ist das Display subjektiv sehr brillant und hell. Auch die eine oder andere Pixelkoordinate wurde noch feinjustiert.
Hier nun der gesamte Quelltext:
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#define TFT_CS 10
#define TFT_RST 8
#define TFT_DC 9
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
int s10=0,s01=0; // ONESECOND TENSECOND BLOCK
int m10=5,m01=9; // Start 23:59:00 Uhr
int h10=2,h01=3; //
unsigned long t,t0,interval=1000; //TIMEINIT
unsigned long TIMEREAD(void){return millis()-t0;}
//Colors
#define rgb Color565
#define BACK rgb(0,0,255)
#define TOWER rgb(63,63,63)
#define LAMP_ON ST7735_YELLOW
#define LAMP_OFF rgb(127,127,127)
#define LAMP_POS rgb(255,0,0)
#define DIGITS LAMP_ON
void setup(void) {
Serial.begin(9600);
Serial.print("Rheinturmuhr auf 1.8 TFT");
tft.initR(INITR_BLACKTAB); // initialize like a ST7735S chip, black tab
// The Tower Art
int w2=tft.width()/2;
int h=tft.height();
tft.fillScreen(BACK);
tft.fillTriangle(w2-15,50,w2+15,50,w2+1,75,TOWER);//V
tft.fillTriangle(w2+1,5,w2-6,h,w2+8,h,TOWER);// ^
tft.fillRect(w2-14,43,30,3,TOWER);//=
tft.fillRect(w2-11,36,24,3,TOWER);//-
tft.setTextSize(1);
tft.setTextColor(DIGITS,BACK);
t0=millis();
}
void loop()
{char s[80]; int i,x,y,is;
t=TIMEREAD();
x=tft.width()>>1;
y=tft.height()-8;
i=1; //1-39 Lamps
do //The Clock
{switch (i) //Decode Time To Lamp
{case 1: case 2: case 3: case 4:
case 5: case 6: case 7: case 8: case 9: is=s01>(i-1); break;
case 10: case 11: case 12: case 13: case 14: is=s10>(i-10); break;
case 15: case 16: case 17: case 18: case 19:
case 20: case 21: case 22: case 23: is=m01>(i-15);break;
case 24: case 25: case 26: case 27: case 28: is=m10>(i-24); break;
case 29: case 30: case 31: case 32: case 33:
case 34: case 35: case 36: case 37: is=h01>(i-29);break;
case 38: case 39: is=h10>(i-38); break; //Hours
}
tft.drawFastHLine(x, y, 2, is ? LAMP_ON : LAMP_OFF);
y-=2;
if(i==9 || i==9+5 || i==9+5+9 || i==9+5+9+5 || i==9+5+9+5+9)
{tft.fillCircle(x-2, y-1, 1, LAMP_POS);
tft.fillCircle(x+2, y-1, 1, LAMP_POS);
y-=4;
}
}while(++i<=39); //all done
//Text Time
tft.setCursor(5, tft.height()-12);
sprintf(s,"%0d%0d:%0d%0d:%0d%0d",h10,h01,m10,m01,s10,s01);
tft.print(s);
//waiting ...
while(TIMEREAD()<(t+interval));
//one second
s01++; //s++;
if(s01>=10){s01=0;s10++;}
if(s10>=6){s10=0;m01++;} //m++;
if(m01>=10){m01=0;m10++;}
if(m10>=6){m10=0;h01++;} //h++;
if(h01>=10){h01=0;h10++;}
if(h10>=2 && h01>=4){h10=0;h01=0;}
interval=analogRead(A0)==0?100:1000;
}
und hier als Arduino-ino-Datei ab IDE 1.0
|
Foto von der SD-Karte
|
| Das Display hat auf der Rückseite ein SD-Kartenslot mit einem Klappmechanismus. Falls ein Schnappmechanismus erwartet wird, kann das zu Problemen mechanischer Art führen ...
Fortsetzung folgt ...
|
|
|
|
Weitere Software

|