Touchlight

Aus Hobbyelektronik.org
Wechseln zu: Navigation, Suche
AVR
Typ ATtiny13
Takt 9,6 MHz
Fuses
High 0xFF
Low 0x7A
Engbedded com logo.png Details


Nach der etwas unfreiwilligen Umräumaktion in meinem Zimmer brauchte ich eine neue Nachttischlampe. Problem: ich hab am neuen Platz keine geeignete Stellfläche.

An der Kopfseite vom Bett ist aufgrund der Fußleiste ein Abstand von knapp 2cm zur Wand. Dieser lädt irgendwie dazu ein, eine indirekte Beleuchtung einzubauen. Die erste Idee war, Leuchtstoffröhren bzw. CCFLs dort zu verstecken. Das Pfeifen der meist billigen Inverter, die Wärmeentwicklung und auch die Spannung/HF waren für den Einsatzzweck doch nicht allzu vertrauenserweckend.

Leuchtmittel

Zum Glück sind LEDs mittlerweile hell genug, dass sie sich für soetwas lohnen, allerdings sind die weißen doch noch ein bisschen teuer. Vor ein paar Wochen bin ich dann bei eBay über ein Angebot gestoßen: 100 "ultrahelle weiße LEDs" für 9 Euro inkl. Versand aus Thailand - also 9ct pro LED. Für den Preis steht bei Conrad noch nicht einmal jemand auf, um eine solche zu holen. Nach ein paar Tagen waren die LEDs dann tatsächlich hier. Zwar haben sie eine gewisse Farb- und Helligkeitsstreuung, dafür kann man sie bei der Menge problemlos selektieren.

Damit die Beleuchtung etwas angenehmer wird, soll sie dimmbar werden. Das soll ein kleiner Atmel Tiny13 übernehmen, der über zwei Hardware-PWMs verfügt. Um diese auch nutzen zu können, wird die LED-Leiste in zwei Stränge unterteilt. Die Anzahl der LEDs wird einzig durch das Netzteil beschränkt (ein austemustertes SonyEricsson-Ladegerät). Da dieses etwas über 400mA zur Verfügung stellt, kommen 22 Dioden (á 20mA) auf die Acrylplatten.

Bedienung

Da sich der kleine AVR nur mit PWM langweilt, soll statt konventionellen Tastern etwas moderneres zum Einsatz kommen: berührungsempfindliche Sensoren.

Problem: Die Controller für Touchpads sind entweder teuer oder schwer zu beschafen. Es gibt zwar haufenweise Code für AVRs, mit denen man das Ganze in Software machen kann. Bis jetzt ist mir aber noch keine über den Weg gelaufen, die bei mir richtig funktioniert hat. Ok, eine (von Zabex) hat funktioniert, diese braucht allerdings je Sensor zwei Pins, was bei dem AVR mit aktiviertem Reset-Pin (schließlich soll das Teil weiterhin mit ISP programmiert werden können) etwas schwierig wird.

Theorie

Ladekurve am Kondensator
Stromlaufplan der Schaltung

Aber erst einmal zur Theorie, wie ein einfacher Sensor funktionieren soll.

Die Spannung an einem Kondensator verhält sich beim Laden wie folgt:

\[U\left(t\right) = U_{0} \cdot \left(1-e^{\frac{-t}{R \cdot C}}\right)\]

Die Formel ist (dadurch, dass ich hier kein TeX hab') etwas unschön.

U(t) ist die Spannung am Kondensator abhängig von der Zeit, U0 die Ladespannung, e die Eulersche Zahl, t die der Zeitpunkt nach Ladebeginn, R der Ladewiderstand und C die Kapazität des Kondensators.

Die Portpins an Mikrocontrollern haben gegenüber GND eine nicht allzu hohe Kapazität. Legt man einen großen Widerstand an einen Portpin kann man die Zeit, bis der Spannungspegel als High erkannt wird, jedoch relativ gut messen. Dieser Pegel liegt bei den meisten AVRs bei 5V Betriebsspannung bei etwa 1,9V (im Datenblatt nach "Pin Thresholds and Hysteresis" suchen).

Schließt man jetzt an den Port-Pin eine Metallplatte an, verändert sich zunächst nicht viel an der Kapazität, bringt man allerdings einen größeren (leitenden) Gegenstand in die Nähe bzw. berührt sie damit, verändert sich die Kapazität am Port-Pin - es dauert länger, bis der Pin geladen ist.

Achtung: der Pin, an dem gemessen wird, ist anfälliger für ESD-Schäden! Aus diesem Grund sollte ein Widerstand (~1k) in Reihe zur Metallplatte angebracht werden! Weiter sollte die Metallplatte mit einer Folie bzw. Lack isoliert werden. Dadurch sinkt zwar die Empfindlichkeit, allerdings kann man die Andruckstärke/-fläche besser messen.

Bei Berührung der Metallplatte dauert es etwa 60µs, bis der Pin bei 1MOhm-Ladewiderstand die Schwellenspannung erreicht hat. Ohne dauert es etwas länger (genaue Zahlen kann ich momentan nicht sagen), wenn kein Finger an der Schaltung anliegt.

Praxis

Um es noch ein wenig anschaulicher zu machen, habe ich das Oszilloskop auf die Schaltung angesetzt.

Dazu wurde die Schaltung auf dem Breadboard aufgebaut. Die Ladewiderstände entsprechen wie im Schaltplan 1,1MOhm. Die Sensorfläche mit 28x28mm (7,8 cm²) und 1,5mm Dicke (Dielektrikum: Hartpapier) ist mit 5cm Leitung direkt (ohne Angstwiderstand) mit dem AVR verbunden.

Aufgrund der nicht zu verachtenden Kapazität des Breadboards un des Tastkopfes (Impedanz weiß ich gerade nicht auswendig) dürften die Messwerte deutlich verzerrt sein!

Die Bilder und Erklärungen dienen daher höchstens zum Veranschaulichen des vorherigen Abschnitts!

Im ersten Bild ist der Spannungsverlauf zu sehen, bei dem die Sensorfläche nicht berührt wurde. Entgegen der Datenblattangabe erkennt der AVR den High-Pegel bei 2,58V, wobei es 26,6µs dauert, bis dieser Wert erreicht wird.

Wird die komplette Sensorfläche mit der Hand abgedeckt, steigt die Dauer bis zum Schwellenwert auf 41µs, also etwa das 1,5-fache der Zeit der unberührten Sensorfläche. Bei einem CPU-Takt von 9,6MHz sind dies 394 Taktzyklen (im Vergleich zu 256 Taktzyklen vorher), die man recht entspannt messen kann.

In den letzten beiden Bildern ist ein kompletter "Abfrageburst" (5 Einzelabfragen) zu sehen. Auch wenn es auf den ersten Blick irreführend ist, die Bildunterschriften sind korrekt. Bei den beiden Fotos habe ich (dummerweise) eine andere Zeitbasis gewählt. Auch bei den Bursts hat das Zeitverhältnis zwischen unberührt und berührt den Faktor 1,5.

Wobei ich dazu sagen muss: Traue keiner Statistik, die du nicht selbst gefälscht hast! Die gemessenen Dauern schwanken insbesondere beim Berühren der Sensorfläche immens. Hauptursache dürfte wahrscheinlich der Elektrosmog sein, der die 50 bzw. 100Hz (Halbwellen) aus dem Stromnetz beisteuert...

Die Software

Bei der Software muss man außer warten eigentlich nicht viel machen.

Vor der Messung muss der Pin lediglich komplett entladen sein. Das erreicht man, indem man den Pin als Ausgang schaltet und den Pegel auf Low setzt. Zum Starten der Messung muss nun lediglich der Pin auf Eingang umgestellt werden. Der interne Pull-Up muss aus bleiben! In diesem Zustand kann nun der externe Pull-Up den Port langsam laden - nun kann mit einer einfachen Schleife die Zeit gemessen werden:

<geshi lang=c> uint8_t getcap(uint8_t pin, uint8_t sampletime, uint8_t waittime) { KEYDDR &= ~pin; //Pin als Eingang -> laden lassen while(waittime--) { nop(); } //etwas Zeit verstreichen while(!(KEYPIN & pin) && --sampletime); //Zeit messen KEYDDR |= pin; //Pin als Ausgang -> entladen return sampletime; } </geshi>

Das ist im Prinzip auch schon alles.

Bei einer Sensorfläche von 4cm² war (bei 9,6MHz Takt des Tiny13) sampletime 50 und waittime 40. Waittime sollte so gesetzt werden, dass sich der Rückgabewert ohne Berührung des Pads nicht verändert. Sampletime sollte so gewählt werden, dass bei vollflächiger Berührung der Wert 0 erreicht wird. So kann man die Fläche und Andruckstärke der Berührung relativ gut ermitteln.

Mit zwei einfachen ifs kann man nun einen simplen Sensortaster bauen:

<geshi lang=c> ... i = 0; n = 6; while(--n) { i += getcap(1<<PB3, 50, 40); } i /= 5; if(i < 42) { PORTB |= (1<<PB2); } else if(i > 45) { PORTB &= ~(1<<PB2); } ... </geshi>

Man beachte die Hysterese zwischen An- und Ausschaltwert. Ohne diesen kleinen Abstand flackert die LED, wenn das Sensorfläche nur zu einem bestimmten Grad berührt wird. Die Kapazität wird in diesem Beispiel 5 mal gemessen, um Ausreißer zu vermeiden.

Soweit lieb und nett, aber wer möchte seinen Finger auf einem Schalter halten, um Licht zu bekommen? Über einen zweiten Sensor kann man zwar einen Ein- und Ausschalter implementieren, aber warum mit wenig zufriedengeben?

Dimmer

Helligkeitsverlauf

Nachdem der Tiny13, wie bereits erwähnt, zwei PWMs sein eigen nennt, ist ein Dimmer eigentlich sehr schnell gebaut.

Über die Register OCR0A und OCR0B kann direkt das On-Off-Verhältnis an den Ausgängen eingestellt werden. Die Sensoren werden als Up-Down-Taster umfunktioniert und schon kann man durch Handauflegen hell und dunkel machen.

Allerdings ist der Helligkeitsverlauf für das menschliche Auge etwas unangenehm. Da menschliche Wahrnehmungen diesbezüglich logarithmisch ist, darf die Helligkeit nicht linear verändert werden, sondern muss einer Exponentialfunktion folgen. Also in der Tabellenkalkulation Pi mal Daumen eine E-Funktion ausgeben lassen und markante Punkte markiert.

Mit Hilfe der X- und Y-Koordinaten kann nun im Programm die Helligkeit bestimmt werden. Die Punkte wurden so gewählt, dass der Verlauf zwischen ihnen möglichst linear ist - somit können die Zwischenwerte ermittelt werden, ohne dass es wirklich auffällt.

Bleibt nur noch ein kleines Problem: Man sieht besonders bei den niedrigen Helligkeiten Stufen beim Dimmen, das ist etwas störend. Abhilfe schafft der zweite PWM. Da die LED-Leisten sowieso in zwei Stränge aufgeteilt sind, können weitere Helligkeitswerte simuliert werden. Die Verteilung der Helligkeit auf zwei Kanäle ist recht einfach:

<geshi lang=c> uint16_t tmp = GetPwm((uint16_t)brightness); uint16_t ba = tmp / 2; uint16_t bb = tmp - ba; </geshi>

Ist zwar nicht sonderlich schön, funktioniert dafür aber recht gut.

Download

Datei:Touch 1.0.zip

Enthält sowohl den simplen Touchsensor als auch die Version mit serieller Ausgabe.

CPU-Takt muss jeweils 9,6MHz sein (Bei Fabrikneuen Tiny13ern muss die CKDIV8-Fuse entfernt werden, damit das Teil bei vollem Takt läuft), der Rest nach eigenem Ermessen. Stromlaufpläne ist bei beiden Sourcen gleich, bei TouchSense ist zusätzlich der (nicht-invertierte) UART auf PBO. Dort werden die Messwerte der beiden Sensoren Tab-getrennt ausgegeben. die Konstante MINDELAY für den UART-Pause in suart.c muss wahrscheinlich angepasst werden.