II. ADC (Analog Digital Converter)JControl-basierte Geräte verfügen über mehrere ADC-Eingänge, mit denen (analoge) Eingangsspannungen gemessen werden können. Dieses Tutorial zeigt, wie sich mit Hilfe der Klasse | |
Bild 1 zeigt ein Beispiel, wie analoge Werte angezeigt werden können. In diesem Fall wurden Komponenten aus der graphischen Bibliothek Vole verwendet. Wie diese Komponten benutzt werden, erfahren sie in dem entsprechenden Tutorial. Das in diesem Tutorial behandelte Beispiel beschränkt sich auf die einfache Akquirierung von Analogwerten. Sie werden am Ende des Tutorials als reine Zahlenwerte auf dem LC-Display des JControl-Moduls ausgegeben.
Vorraussetzungen: Für dieses Tutorial wird eine Installation der JControl-Tools sowie ein JControl-Modul mit LC-Display (SmartDisplay, Sticker oder PLUI) benötigt.
Download: http://www.jcontrol.org/examples/ADCExample.zip Dieses Tutorial mit allen Quelltexten und Ressourcen als JControl/IDE-Projekt (ZIP-Archiv).
Viele JControl-Geräte sind mit einem 8-Bit Analog-Digital-Konverter mit mehreren Eingangskanälen (ADC-Kanäle) ausgestattet. Diese können zum Messen anliegender Spannungen verwendet werden, die z.B. durch einen Temperatur-Fühler erzeugt werden. Die genaue Pinbelegung kann der API Dokumentation http://www.jcontrol.org/current/docs/api/jcontrol/io/GPIO.html von GPIO
oder den Produkt-Datenblättern http://www.domologic.com/support/ch1/index_de.html entnommen werden.
Die Klasse ADC
stellt lediglich eine Funktion zur Verfügung, die statische Methode getValue(int channel)
. Ihr Aufruf liefert einen 8-Bit Wert zwischen 0 und 255 zurück. Dieser Wert entspricht dem aktuellen Messwert des durch channel
ausgewählten ADC-Kanals mit Bezug auf die an Pin VDDA anliegende Referenzspannung (standardmäßig die Versorgungsspannung), wobei 255 aussagt, dass die anliegende Spannung gleich oder größer der Referenzspannung ist.
Achtung: Die zu messende Eingangsspannung an den ADC-Kanälen darf die Versorgungsspannung keinesfalls um mehr als 0.3V überschreiten und nicht kleiner als -0.3V sein.
In dem folgenden Beispiel wird gezeigt, wie die Multithreading-Fähigkeiten von JControl verwendet werden können, um in einer Applikation kontinuierlich die Messwerte der ADC-Kanäle aufzunehmen, ohne den normalen Programmablauf dadurch zu beeinträchtigen. So kann im Vordergrund beispielsweise eine grafische Benutzerschnittstelle angezeigt werden, während im Hintergrund automatisch auf Änderungen von Messwerten reagiert wird.
Unsere Applikation soll zunächst herausfinden, wie viele ADC-Kanäle auf dem System verfügbar sind und dann entsprechend viele Threads starten, die die Messwerte kontinuierlich einlesen. Der folgende Quelltext zeigt, wie diese Aufgabe mit JControl realisiert werden kann.
1 | import jcontrol.io.ADC; |
2 | import jcontrol.io.Display; |
3 | import jcontrol.lang.ThreadExt; |
4 | import jcontrol.system.Management; |
5 | |
6 | /** |
7 | * This example shows how to read out ADC values on JControl. |
8 | * Furthermore, the usage of multiple threads in one application |
9 | * is demonstrated. |
10 | * |
11 | * <p>(C) DOMOLOGIC Home Automation GmbH 2003</p> |
12 | */ |
13 | public class ADCExample { |
14 | /** array to store ADCReader instances in */ |
15 | ADCReader adcreaders[]; |
16 | |
17 | |
18 | /** |
19 | * Inner class <code>ADCReader</code> realizes a thread |
20 | * that continuously reads and stores ADC values. |
21 | */ |
22 | class ADCReader extends Thread { |
23 | /** adc channel */ |
24 | int channel; |
25 | /** last read value */ |
26 | int value = 0; |
27 | /** thread instance */ |
28 | Thread instance = null; |
29 | |
30 | /** |
31 | * Constructs an <code>ADCReader</code> thread. |
32 | * @param channel the ADC input channel to read values from |
33 | */ |
34 | public ADCReader(int channel) { |
35 | instance = this; |
36 | this.channel = channel; |
37 | this.start(); |
38 | } |
39 | |
40 | /** |
41 | * This method is invoked when the thread execution starts. |
42 | * @see java.lang.Runnable#run() |
43 | */ |
44 | public void run() { |
45 | while (instance == this) { |
46 | // read current adc value |
47 | value = ADC.getValue(channel); |
48 | |
49 | // sleep for 100 millis |
50 | try { |
51 | ThreadExt.sleep(100); |
52 | } catch (InterruptedException e) {} |
53 | } |
54 | } |
55 | |
56 | /** |
57 | * Stop thread execution. |
58 | */ |
59 | public void quit() { |
60 | instance = null; |
61 | } |
62 | |
63 | /** |
64 | * Retrieve current adc value |
65 | */ |
66 | public int getValue() { |
67 | return value; |
68 | } |
69 | } |
70 | |
71 | |
72 | /** |
73 | * Constructs an <code>ADCExample</code> instance. |
74 | * We start as many <code>ADCReader</code> threads as |
75 | * adc input channels exist. |
76 | */ |
77 | public ADCExample() { |
78 | String s = Management.getProperty("io.adcchannels"); |
79 | int numchannels = Integer.parseInt(s); |
80 | |
81 | adcreaders = new ADCReader[numchannels]; |
82 | |
83 | // create ADCReader threads and store instances into an array |
84 | for (int i=0; i<numchannels; i++) |
85 | adcreaders[i] = new ADCReader(i); |
86 | |
87 | // draw adc status continuously on lcd |
88 | drawStatus(); |
89 | } |
90 | |
91 | |
92 | /** |
93 | * Draw ADC values (read by ADCReader threads) on lcd |
94 | */ |
95 | void drawStatus() { |
96 | Display lcd = new Display(); |
97 | |
98 | while (true) { |
99 | lcd.clearDisplay(); |
100 | |
101 | // show adc values on lcd |
102 | for (int i=0; i<adcreaders.length; i++) { |
103 | lcd.drawString("ADC channel ".concat( |
104 | String.valueOf(i)).concat(" value: ").concat( |
105 | String.valueOf(adcreaders[i].getValue())), |
106 | 0, 8*i); |
107 | } |
108 | |
109 | // sleep for 500 millis |
110 | try { |
111 | ThreadExt.sleep(500); |
112 | } catch (InterruptedException e) {} |
113 | } |
114 | } |
115 | |
116 | /** |
117 | * Main method. Program execution starts here. |
118 | */ |
119 | public static void main(String[] args) { |
120 | new ADCExample(); |
121 | } |
122 | } |
Die Hauptklasse ADCExample
enthält eine innere Klasse ADCReader
, welche die Eigenschaften eines Thread-Objekts erfüllt. Wenn der Thread durch einen Aufruf der Methode start
gestartet wird, (hier im Konstruktor public ADCReader(int channel)
), folgt automatisch ein Aufruf der Methode run()
. Diese liest in einer Endlosschleife alle 100ms den aktuellen Wert des im Konstruktor spezifizierten ADC-Kanals ein. Dazu wird die getValue
-Methode aus der ADC
-Klasse verwendet.
Die Aufgabe der Hauptklasse ADCExample
ist einfach: Es müssen lediglich genau so viele ADCReader
-Instanzen erzeugt werden wie ADC-Kanäle im System verfügbar sind. Die Anzahl letzterer kann durch einen Aufruf der Methode Management.getProperty("io.adcchannels")
auf jedem JControl-System erfragt werden. Wurden die ADCReader
-Instanzen erzeugt, läuft die kontinuierliche Messwerterfassung ohne weiteres Zutun der Hauptklasse automatisch im Hintergrund.
Um die im Beispiel gemessenen Werte der A/D-Wandler visuell kontrollieren zu können, soll das ADCExample
nun um eine Funktion zur Darstellung der Messwerte auf dem LCD erweitert werden. Diese soll parallel und unabhängig zur Messwerterfassung laufen.
Da die Hauptklasse nach der Instantiierung der ADCReader
-Threads nichts weiter zu tun hat, bietet es sich an, den Programmthread selbst für die Darstellung der Messwerte zu benutzen. Die folgende Methode drawStatus()
erledigt dies, indem sie alle 500ms die aktuellen Messwerte aller ADCReader
-Threads einliest und auf dem LCD als Strings ausgibt.
94 | /** |
95 | * Draw ADC values (read by ADCReader threads) on lcd |
96 | */ |
97 | void drawStatus() { |
98 | Display lcd = new Display(); |
99 | |
100 | while (true) { |
101 | lcd.clearDisplay(); |
102 | |
103 | // show adc values on lcd |
104 | for (int i=0; i<adcreaders.length; i++) { |
105 | lcd.drawString("ADC channel ".concat( |
106 | String.valueOf(i)).concat(" value: ").concat( |
107 | String.valueOf(adcreaders[i].getValue())), |
108 | 0, 8*i); |
109 | } |
110 | |
111 | // sleep for 500 millis |
112 | try { |
113 | ThreadExt.sleep(500); |
114 | } catch (InterruptedException e) {} |
115 | } |
116 | } |
Fügt man dem ADCExample
-Konstruktor nun noch einen Aufruf drawStatus()
hinzu, ist das ADCExample
vollständig. Statt die Messwerte lediglich anzuzeigen, können auf die in diesem Tutorial beschriebene Art und Weise auch komplexe Steuer- und Regelsysteme implementiert werden. Die Implementierung der Messwerterfassung in Form von Threads garantiert, dass die Messwerte völlig unabhängig von der Applikationslogik kontinuierlich aktualisiert werden.
Der Test: Probieren Sie nun das ADCExample
auf Ihrem JControl-System aus! Laden Sie den Quelltext zum ADCExample.java herunter und fügen Sie ihn einem leeren JControl/IDE-Projekt hinzu. Nach dem Hochladen auf Ihr JControl-Modul werden Sie eine Darstellung ähnlich dem folgenden Bild 2 sehen.