// Treppenlichtautomat - Prinzip // für Tiny 25/45/85 // Testaufbau: // PB0 schaltet Spannungsteiler kurz aktiv, PB1 (kurz Aktiv nach Ablauf oder Aktiv während Verzögerungszeit) // Taster an PB2 (INT0) // ADC zur Zeiteinstellung an PB4 = ADC2 und PB3 = ADC3 // // Stromaufnahme im Schlafzustand: ca. 0.5µA @ 5V bzw. ca. 0.1µA bei 3V (Tiny85), mit BrownOut: ca. 20µA @ 5V // mögliche Erweiterung: beide Pulsvarianten auf Ausgang geben, dann muss aber RES (PB5) verwendet werden // -> HV-Programmierung // /* VCC VCC VCC + + + | | | | 100n | | o--||--. .-. .-. | | 10k | | | | 10k | === | | | | | GND '-' '-' .---------o-----------. | | | | | | | | Zeit Pot 1 Zeit Pot2 | o-----o Res | Puls am Ende ___|_____________ .___ | | | PB1 o------- | |_| | --- | | Start | ---100n| | | | | | oder Puls ___| Zeit Pot1 _________ | === | | |_____________| | GND | Tiny25/45/85 | | | | .-----)----------o PB0 | | | | | .-. | | | P1 | |<---)----------o PB4 ADC2 | 10k | | | | | '-' | | | | | | | GND o----------o PB2 INT | | | | | | | | Poti2 o PB3 ADC3 | | o wie P1| | Taste |=|> '----------o----------' | o | | === === GND GND - Verzögerung mit Poti1, Pulsdauereinstellung mit Poti2 - Poti1 /Poti2 ist nur zum Messen bestromt - PB1 für kurze Zeit (entsprechend Poti2) nach Ablauf aktiv - Poti2 genauso an PB0 und GND anschließen - Potibereiche mit Widerständen auf beiden Seiten in HW einschränkbar, allerdings dabei Reduzierung der Auflösung der Zeiteinstellung - mit Poti2 kann bei Power-Up oder Reset die Betriebsart (Treppenlicht / Futterautomat) gewählt werden. Wenn Poti2 auf Min. gestellt, dann ist nach dem Reset Mode Treppenlicht aktiv. */ #define F_CPU 1000000 // DIV8-Fuse aktiv #include // fuer int-typen #include #include #include #include #include // wegen Makros für 16/32-Bit Zugriffe #include #define HIGH_ACTIVE // Ausgang PB1 auf HIGH-Active stellen // Timing Range einstellen für internen Oszillator mit CKDIV8 // 2^MULT ist Faktor für den gelesenen ADC-Wert, max. MULT=22 (10Bit ADC in uint32_t), // je nach Wahl von MULT ergibt sich: // Max Bereich (MULT=22): ~ 305h ... 40 Jahre // Min Bereich (MULT= 0): ~ 260ms ... 300s // Einstellauflösung entsprechend 260ms .... 305h // weitere Variation möglich durch andere Teilerwahl in TCCR0B // ACHTUNG: Werte mit internem Oszillator gemessen bei 3,3V, Temperaturschwankungen beachten! // Werte können je nach Baustein abweichen! //#define MULT 0 // sehr kurz: gemessen ca 260ms ... 4:30min //#define MULT 1 //#define MULT 2 // kurze Zeiten; gemessen ca. 1s ... ca. 18 Minuten #define MULT 3 // kurze Zeiten; gemessen ca. 2s ... ca. 36 Minuten //#define MULT 4 // ca 4s ... 70min //#define MULT 5 // ca 8s .... 2:20h //#define MULT 8 // ca 68s ... 18,5h //#define MULT 10 // ca 4,5min ... 74,5h //#define MULT 22 // ca 310h ... 35 Jahre #define DIV 4 // Divisor für die PB1-Pulsdauer (4: 0,25 ... 16s) ADC-Wert / 2^DIV // globale Definition volatile uint32_t l_wcount; volatile uint8_t wcount_zero; // uint8_t Variable, um Atomic-Probleme mit l_wcount zu umgehen. // Zeigt nur Null-/NichtNullzustand von l_wcount an ISR (TIMER0_OVF_vect) { l_wcount--; if (l_wcount==0) wcount_zero=0; else wcount_zero=1; } EMPTY_INTERRUPT (INT0_vect); // leer, benötigt für Install ISR INT0 zum Wecken per Taste /****************************/ /* ADC Einzelmessung */ uint16_t ADC_Read( void ) { uint16_t adc_res; ADCSRA |= (1< abschalten (80µA von 800µA) ADCSRA &= ~(1 << ADEN); // ADC ausschalten PRR |= (1 << PRADC); // Power ADC ausschalten TIMSK &= ~(1 << TOIE0); // Timer IRQ ausschalten l_wcount = 1L; // = 1 vermeidet nach Reset den Durchlauf von Block 'Timer abgelaufen' sei(); // Interrupts aktivieren set_sleep_mode(SLEEP_MODE_PWR_DOWN); // INT0 weckt wieder auf, benötigt 0.1µA sleep_mode(); // erst mal Schlafen while (1) { if (wcount_zero == 0x0) // Timer abgelaufen { if (!mode) // Treppenlicht { // AUS #ifdef HIGH_ACTIVE PORTB &= ~(1 << PB1); #else PORTB |= (1 << PB1); // PB1 HIGH #endif } else // Futterautomat, mode=1 { // EIN #ifdef HIGH_ACTIVE PORTB |= (1 << PB1); #else PORTB &= ~(1 << PB1); // PB1 LOW #endif ATOMIC_BLOCK(ATOMIC_RESTORESTATE) // GCC-Makro: Zwischenfunken von IRQs vermeiden { l_wcount = (uint32_t) adc_val_PB3; // Timer für Ende-Puls setzen wcount_zero=1; } TCNT0 = 0x0; // Timerzähler neu setzen, damit volle Zyklen durchlaufen werden while (wcount_zero > 0 ) // Pulsdauer für PB1 mit Timer, mit Taster unterbrechbar (anstatt _delay_ms()) { keyin = PINB & (1<> DIV) + 1; // Division mit 2^DIV zur Zeiteinschränkung } wcount_zero=1; ADCSRA &= ~(1 << ADEN); // ADC ausschalten - zum Strom sparen (ca. 300µA@5V) PRR |= (1 << PRADC); // Power ADC ausschalten (nochmal 25µA) } PORTB &= ~(1 << PB0); // ADC-Poti wieder abschalten --> Power Save } // Taste gedrückt } // Ende while(1) } // Ende main