home
erste Version am 16.03.2018
letzte Änderung am 04.04.2018

ATtiny85 und Drehgeber als LED-Dimmer


Es soll eine Art Schreibtisch-Lampe entstehen, bei der die Helligkeit eines LED-Streifens mittels eines Drehgebers geregelt wird. Dazu soll ein ATtiny85 den Drehgeber abfragen und einen Schalttransistor über einen PWM-Pin entsprechend ansteuern.
Der Lampen-Körper wird primär 3D-gedruckt. Zusätzlich braucht es nur ein paar M4-Schrauben und Muttern.


Im Einschalt-Moment sollen die LEDs mit maximaler Helligkeit leuchten. Der Drehgeber wird keine Skala bekommen. Hochdrehen führt solange zu helleren LEDs, bis die maximale Helligkeit erreicht ist. Beim Runterdrehen wird bis zu einer minimalen Helligkeit gedimmt.
Programm-Kode zur Abfrage eines Drehgeber habe ich bereits vor über einem Jahr gebaut und hier dokumentiert.
Ebenso habe ich schon geübt, wie der ATtiny85 programmiert wird. Hier stehts.

Die Schwierigkeit ist nun, einen komfortablen Entwicklungs-Workflow zu finden.
Mit dem Programmiergerät kann ich den ATtiny85 flashen. Zum Testen müsste ich ihn jedes mal in ein Breadboard umstecken. Und sowas wie Serial.print() fürs Debuggen stünde nicht zur Verfügung.
Wahrscheinlich wäre es das Einfachste, die primäre Entwicklung auf einem ArduinoUNO durchzuführen und diese final auf den ATtiny85 zu portieren.
Berücksichtigt man frühzeitig die Einschränkungen des ATtiny85 bezogen auf den ATmega328, sollte das machbar sein.

Eine solche Einschränkung ist beispielsweise, dass der ATtiny85 laut Doku nur einen "external interrupt" verarbeiten kann.
Im Kapitel 9.2 heißt es aber auch:
The External Interrupts are triggered by the INT0 pin or any of the PCINT[5:0] pins. Observe that, if enabled, the interrupts will trigger even if the INT0 or PCINT[5:0] pins are configured as outputs. This feature provides a way of generating a software interrupt. Pin change interrupts PCI will trigger if any enabled PCINT[5:0] pin toggles.
Für meinen Drehgeber-Kode sollte der PinChangeInterrupt reichen. Im Kode werden zwar zwei Interrupts aufgesetzt, jedoch landen beide im selben ISR-Kode.

Jetzt habe ich hier mal ein bischen gespickt und meine Drehgeber-ISR (minimal modifiziert) dazukopiert....und....läuft.

#include <avr/io.h>
#include <avr/interrupt.h>

#define DR1 1 // Pin6 am Chip
#define DR2 2 // Pin7 am Chip
#define PWM1 4 // Pin3 am Chip

// 0=00 (Schritt), 1=01 (rückwärts), 2=10 (vorwärts), 3=11 (Grundstellung)
volatile unsigned char dr_t=0;
volatile unsigned char dr_counter=255;

void setup() {
pinMode(DR1, INPUT_PULLUP);
pinMode(DR2, INPUT_PULLUP);
pinMode(PWM1, OUTPUT);

PCMSK|=(1<<PCINT1)|(1<<PCINT2);
GIMSK|=(1<<PCIE); // Bit 5 setzen
sei();
}

ISR(PCINT0_vect) {
static unsigned char p;
switch(p=digitalRead(DR1) | digitalRead(DR2)<<1) {
case 0:
if(dr_t==1) {
dr_t=p;
if(dr_counter>0) dr_counter--;
} else if(dr_t==2) {
dr_t=p;
if(dr_counter<255) dr_counter++;
}
break;
case 1:
case 2:
if(dr_t==3) dr_t=p;
break;
case 3:
dr_t=p;
}
}

void loop() {
analogWrite(PWM1, dr_counter);
delay(10);
}


Der Drehgeber steckt auf den Pins 6 und 7, am Pin 3 hängt ein Ozzi.
Ergebnis: mit dem Drehgeber kann man die Pulsweite des Signals zwischen Null und Maximum regeln.

Nun muss statt des Ozzis lediglich die Basis eines Transitors rangeklemmt werden und schon lässt sich damit die Helligkeit der LEDs regeln.
Naja, einen 5V-Regler braucht es auch noch, weil der ATtiny85 nur zwischen 2.7V und 5.5V betrieben werden will.
Also etwa so (Schaltplan anklicken für volle Größe):
Schaltplan für ATtiny85 mit Drehgeber als LED-Dimmer   Beschaltung MJE3055   Beschaltung LF50CV

Eine mögliche Erweiterung könnte vielleicht sein, dass man mit dem Taster des Drehgebers die aktuelle Helligkeit im EEPROM speichern kann - um diese Helligkeit beim nächsten Kaltstart als Einschalt-Helligkeit zu nutzen. Die eingestellte Helligkeit sollte dazu über einem gewissen Mindestwert liegen - damit man nicht den Zustand AUS als Einschalt-Helligkeit speichern kann. Oder die Bedingung if(dr_counter>0) dr_counter--; wird einfach auf if(dr_counter>10) dr_counter--; geändert. Schließlich soll man die Lampe gefälligst primär (also vor dem 12V-Netzteil) abschalten, wenn man ihr Licht gerade nicht braucht...



Mittlerweile leuchtet die erste Lampe.
Die Firmware auf dem ATtiny85 ist derzeit folgende:
//#include <avr/io.h>
//#include <avr/interrupt.h>

#define DR1 1 // Pin6 am Chip
#define DR2 2 // Pin7 am Chip
#define PWM1 4 // Pin3 am Chip

// 0=00 (Schritt), 1=01 (rückwärts), 2=10 (vorwärts), 3=11 (Grundstellung)
volatile unsigned char dr_t=0;
volatile unsigned char dr_counter=255;

void setup() {
pinMode(DR1, INPUT_PULLUP);
pinMode(DR2, INPUT_PULLUP);
pinMode(PWM1, OUTPUT);

PCMSK|=(1<<PCINT1)|(1<<PCINT2);
GIMSK|=(1<<PCIE); // Bit 5 setzen
sei();
}

ISR(PCINT0_vect) {
static unsigned char p;
switch(p=digitalRead(DR1) | digitalRead(DR2)<<1) {
case 0:
if(dr_t==1) {
dr_t=p;
if(dr_counter<=127) dr_counter=dr_counter*2+1;
else dr_counter=255;
} else if(dr_t==2) {
dr_t=p;
if(dr_counter>1) dr_counter/=2;
}
break;
case 1:
case 2:
if(dr_t==3) dr_t=p;
break;
case 3:
dr_t=p;
}
}

void loop() {
analogWrite(PWM1, dr_counter);
delay(100);
}

Bei der Schaltung habe ich zwei zusätzliche 100µ-Elkos spendiert, weil sich der ATtiny85 sporadisch aufgehängt hat.
Allerdings hat weder der Elko vor dem Regler noch der dahinter irgendwas an diesem Verhalten geändert.
Die Änderung trat erst ein, nachdem ich den delay(10) in loop() auf delay(100) geändert hatte. Warum auch immer er bei 100 analogWrites pro Sekunde gelegentlich durcheinander kommt - zehn Änderungen pro Sekunde sind allemal genug.
Jedenfalls sind die zwei Elkos wahrscheinlich eher nicht nötig.



Heute habe ich die zweite Lampe mit Dimmer gebaut und dabei auf die beiden Kondensatoren verzichtet.
Somit entspricht die neue Schaltung exakt obigem Schaltplan - und sie funktioniert perfekt.
Allerdings ist dabei zu bemerken, dass diese Lampe ihre 12V aus einem ATX-Netzteil mit 500W bezieht.
Will heißen: zu jedem Zeitpunkt steht garantiert genug Saft zur Verfügung.

Andersrum wäre es denkbar, dass der ATtiny85 bei Einsatz eines dieser 5€-Mini-Billig-Stecker-Netzteile nicht mehr genug Strom abbekommt, wenn die LEDs gerade maximal Strom ziehen. In diesem Fall könnte ein Elko hinter dem Regler durchaus Sinn machen.

BTW: um die Richtung des Drehgebers (bzgl. heller/dunkler) umzukehren, kann man die Kontakte umlöten. Man kann aber auch einfach in der Firmware die entsprechenden Bedingungen tauschen. Also "if(dr_t==1) {" wird zu "if(dr_t==2) {" und "} else if(dr_t==2) {" wird zu "} else if(dr_t==1) {".