Hauptseite Projekte Impressum Datenschutz

Dekorative Hexagon-Lampe mit Touch-Steuerung


Meine Wohnung ist in der Regel, insbesondere in den Abendstunden, relativ dunkel.

Dies ist ein Problem, vor allem beim Lesen von Büchern, da die Augen so schnell müde werden.

Um dies zumindest zum Teil zu beheben hatte ich letztens die Idee, eine dekorative Lampe mit Touch-Steuerung zu bauen.

Die Idee kam daher, dass ich unbedingt etwas mit den "neuen" 6-Pin-ATTiny-Chips machen wollte (ich meine, ein ganzer Mikrocontroller in nur 6 Pins, wie cool ist das denn?).


Hardware

Die Architektur umfaßt einen ATTiny10. Mit etwas Arbeit konnten auch sämtliche Features auch für den ATTINY5 zugänglich gemacht werden. Im Vergleich mit dem ATTINY10 hat dieser nämlich nur die Hälfte, also 512 Bytes, an Programmspeicher.

Der ATTiny ist mit einem Touch-Button verbunden, der die LEDs steuert. über drei Touch-"Befehle" kann das aktuelle Hexagon entweder "getoggled" werden, oder es können sämtliche Hexagons der Lampe entweder An oder Aus geschaltet werden.

Da der ATTiny nicht genug Strom für 72 LEDs treiben kann, als auch um die Notwendigkeit von strombegrenzenden Widerständen zu eliminieren, steuert der ATTiny einen AO3400 N-Kanal-FET, der jeweils vier LEDs in Reihe mit Masse verbindet.

Mit einem angenommenen Spannungsabfall von 0,6V über den MOSFET kommen an den vier LEDs in Reihe immer noch jeweils 2,85V an, mehr als genug und mit noch ein wenig Spielraum in beide Richtungen für Inkonsistenzen beim Netzteil.

An jeder Seite des Hexagons befinden sich zudem drei Kontakte, VDD (12V), GND sowie ein Kommunikationspin, über den die Funktionalität läuft, sämtliche Hexagons an- bzw. auszuschalten.


Einzelnes Hexagon-Modul

Für die Stromversorgung wurde eine weitere Version der Platine erstellt, die über einen USB-C-Port verfügt und über die USB-PD-Schnittstelle vom zugehörigen Ladegerät eine Spannung von 12V auskommuniziert.

An sich unterscheidet sich die Platine nur darin, dass ein IP2721-USB-C-PD-Trigger-IC dafür verwendet wird. Hierbei handelt es sich um eine einfache Möglichkeit, ohne viele zusätzliche Komponenten 12V aus einem USB-C-Port zu bekommen.

Unangenehmerweise sind diese Chips einzeln ziemlich teuer, als fertige USB-C-Adaptermodule jedoch günstig auf den typischen Websites wie Aliexpress zu erstehen.


Fertige Hexagon-Lampe

Code

Die Programmierung des ATTiny erfolgt über die TPI (Tiny Programming Interface)-Schnittstelle. Diese ist bei jedem Atmel ICE vorhanden und wird von Microchip Studio automatisch ausgewählt.



    void init(){
        CCP = 0xD8; //Unlock protected registers
        CLKPSR = 0x00;	//Set clock prescaler to 1
        CLKMSR = 0x00;  
        //Pin Configuration: PB0: Comm Port, PB1: Touch Input, PB2: FET Gate 
        DDRB = GATE_MASK; //Only set FET Gate to Output
    }
	

In der Init-Funktion wird zunächst die richtige Taktfrequenz gewählt. Dies passiert, indem zunächst die besonders wichtigen Register entsperrt werden.

Im Datenblatt findet sich dabei das nötige Kürzel, in diesem Fall 0xD8.

Im Folgenden wird der Clock Prescaler deaktiviert. Dieser ist ein Eingangsteiler, der den Prozeßortakt herunterteilt. Um die maximal mögliche Geschwindigkeit von 8MHz zu erreichen, wird dieser deaktiviert.

Abschließend wird im Data Direction Register B der Ausgang für das Gate des FETs als solcher initialisiert.

Im Folgenden werde ich meinen Code kurz erklären. Die Touch-Eingaben werden durch die exzellente TinyTouchLib vom GitHub-Nutzer cpldcpu gehandlet. Einen Link zu dem ganzen Projekt gibt es unten.


    void fadeLEDs(u8 dir){ //DIR: 0: Down, 1: Up
        for(u8 i = 0; i < 255; i++){
            for(u8 j = 0; j < 255; j++){
                if((dir && (j > i)) || (!dir && (j <= i))){
                    PORTB &= ~GATE_MASK; //Turn off
                } else {
                    PORTB |= GATE_MASK; //Turn on for the rest
                }
            }
        }
    }
	

Die FadeLEDs-Funktion ist dafür zuständig, die LEDs bei einem Knopfdruck nicht gleich von "0 auf 100" anzuschalten, sondern einen "langsamen" übergang darzustellen.

Ich habe die Funktion so geschrieben, um die Anzahl der Instruktionen zu minimieren, um möglichst die ganze Software auf einen ATTINY5 zu quetschen.

Alles was die Funktion tut, ist über zwei Schleifen hochzuzählen (um so zwei uint8_t zu verwenden und nicht int8_t's absteigen zu müßen) un abhängig von der gewünschten Richtung den Output entweder auf 1 (an) oder 0 (aus) zu setzen.

	int main(void){
		u8 ledState = 0; 
		
		/*Initialize the touch library and initialize ports*/
		init();
		tinytouch_init();
		
		while (1){
			
			//The touch button was pushed
			if(tinytouch_sense() == tt_push){ 
				
				//Toggle LED state and set the output	
				ledState = ~ledState;
				fadeLEDs(ledState);
				
				/*This code is there so that with a double preß all the other leds are toggled*/
				
				//Initialize a counter variable to keep track of the time.
				//The goal is to have a window after the first push in which a second push triggers the COMM port to toggle all other modules.
				u8 counter = 0; 
				
				//Wait for a potential second push of the button. The inner things of the while loop take around 5ms, so the total window is around a second.
				while((tinytouch_sense() != tt_push) && (counter <= 200)){
					counter++; 
				}
				
				//If the button has been pressed, the while loop breaks with the counter staying under 200.
				//Send a message on the COMM port.
				if(counter < 200){
					DDRB = GATE_MASK | COMM_MASK;
					//Set COMM to low to signal other modules
					PORTB &= ~COMM_MASK;
					_delay_ms(100);
				
					//Set COMM to tri state
					PORTB |= COMM_MASK;
					DDRB = GATE_MASK;
				}
			}
			
			//look for a message on the COMM port.
			if(!((PINB & COMM_MASK) == COMM_MASK)){
				
				//Toggle the LED state.
				ledState = ~ledState;
				fadeLEDs(ledState);
			}
		}
	}
	

Die Main-Funktion erfüllt die eigentlichen Verwaltungsaufgaben des Programms. Sobald eine Touch-Eingabe erkannt wurde, wird der Wert der ledState-Variable umgeschaltet.

Die Variable kann entweder den Wert 0 oder 255 annehmen. Dies spart Instruktionen, da nur eine Inversion der Bits und somit auch in der fadeLEDs-Funktion nur eine "größer 0"-Abfrage notwendig ist.

Um auch die anderen Module umzuschalten, ist es notwendig, kurz nach dem ersten Druck des Sensors diesen noch ein zweites Mal zu betätigen.

Hierfür wird eine Variable eingefügt, die mit jedem Durchlauf der Schleife inkrementiert wird. Da die Ausführung dieser etwa 5ms benötigt (einschließlich ADC-Messung), gibt es also in etwa ein 1s-Fenster,

in dem eine zweite Eingabe auch die anderen LEDs ansteuert. Bricht die Schleife mit einem Zählwert unter 200 ab, so wird für 100ms der Kommunikationsausgang auf eine logische '0' gezogen.

Da es sich bei dem Kommunikations-"Bus" um ein Open-Drain-Signal handelt, können so auch mehrere Module den Ausgang auf '0' ziehen, ohne Schaden anzurichten.

Der letzte Teil der Hauptschleife ist dafür da, um am Kommunikationspin zu erkennen, ob dieser auf '0' gezogen wurde.

Sollte dies der Fall sein, wird wieder die ledState-Variable umgeschaltet und die LEDs entsprechend angesteuert.


Falls es Fragen gibt, twittert mich an, unter dem Handle @ItsLSchlegel.