Vor einigen Jahren habe ich eine Programm erstellt, dass regelmäßig eine LED blinken lässt. Die Schaltung hängt am Garagenfenster und läuft seitdem mit dem ersten Satz an Batterien (schon ca. 4Jahre). Der Stromverbrauch ist also nur minimal – vermutlich leicht über der Selbstentladung der Batterien.
Das ganze läuft mit einem Attiny13. Software- & Hardwareseitig wurden diverse Tricks zum Energiesparen umgesetzt. Z.B.:
#define F_CPU 128000 #define DEBUG 0 #define LED_PULS 90 //ms #define LED_PAUSE 10 //s #define ADC_THRESHOLD 757 //3,7V #define ADC_INTERVAL 60*30 //s #define ADC_PIN_PREENABLE_TIME 1 //s #define ADC_CALIBRATE_TIME 15000L //ms #include <avr/io.h> #include <avr/interrupt.h> #include <avr/wdt.h> #include <util/delay.h> #include <avr/sleep.h> //Fuses: // High: 0xFF // Low: 0x73 //Ggf. ISP-Takt runtersetzen -> 25kHz bwz. falls versehentlich CHKDIV8 aktiv -> 2kHz //PB0 => PIN5 => LED //PB1 => PIN6 => Spannungsteiler Enabled //PB2 => PIN7 => Spannungsteiler unsigned int adc_measure(int ch) { //ADC aktivieren ADCSRA |= (1 << ADEN); //ADC Channel wählen ADMUX = ch; //Messung starten ADCSRA |= (1 << ADSC); //Warten bis Messung fertig while(!(ADCSRA & (1<<ADIF))); //Messung fertig Flag resetten (durch schreiben einer 1) ADCSRA |= (1<<ADIF); //ADC deaktivieren ADCSRA &= ~(1 << ADEN); //ADC-Wert zurückgeben return ADC; } int main(void) { //LED-Pin als Ausgang setzen DDRB |= (1 << 0); //AD-Komperator deaktivieren ACSR |= ACD; //Spannungsteiler-Pin als Ausgang setzen DDRB |= (1 << 1); //ADC konfigurieren ADCSRA |= (1<<ADPS1) | (1<<ADPS0); //ADC-Prescale=8 ADMUX &= ~(1<<REFS0); //Vcc = Ref ADCSRA |= (1 << ADEN); //ADC aktivieren //ADC=Eingang & Pullup DISABLED DDRB &= ~(1 << 2); PORTB &= ~(1 << 2); //Lichtschwelle am Poti einstellen, nach Programmstart uint16_t j=0; PORTB |= (1 << 1); _delay_ms(1000); while(j<ADC_CALIBRATE_TIME) { uint16_t measure_value=0; measure_value=adc_measure(1); if(measure_value>ADC_THRESHOLD) { PORTB |= (1 << 0); } else { PORTB &= ~(1 << 0); } j++; _delay_ms(1); } PORTB &= ~(1 << 1); //Watchdog Interval Timer WDTCR |= (1<<WDCE) | (1<<WDE); //Enable Reset MCUSR &= ~(1<<WDRF); //Clear WDRF in MCUSR WDTCR |= (1<<WDCE); //Watchdog ChangeEnable WDTCR = 0b1000110; //Enable Interrupt, Disable Reset, Set Prescaler 1s //Interrupts aktivieren sei(); //Sleep aktivieren und auf Powerdown stellen sleep_enable(); set_sleep_mode(SLEEP_MODE_PWR_DOWN); while(1) { //CPU schlafen legen sleep_cpu(); } } volatile uint16_t i1=0; volatile uint16_t i2=0; volatile uint16_t blink_enabled=1; //wird sekündlich aufgerufen ISR(WDT_vect) { i1++; i2++; if(i1==LED_PAUSE) { if(blink_enabled==1) { PORTB |= (1 << 0); _delay_ms(LED_PULS); PORTB &= ~(1 << 0); } i1=0; } if(i2==(ADC_INTERVAL-ADC_PIN_PREENABLE_TIME)) { #if DEBUG == 0 PORTB |= (1 << 1); #endif } if(i2==ADC_INTERVAL) { uint16_t measure_value=0; measure_value=adc_measure(1); #if DEBUG == 0 PORTB &= ~(1 << 1); #endif #if DEBUG == 1 _delay_ms(1); uint16_t x=0; while (x<measure_value) { PORTB |= (1 << 1); x++; _delay_us(100); } PORTB &= ~(1 << 1); #endif if(measure_value>ADC_THRESHOLD) { blink_enabled=1; } else { blink_enabled=0; } i2=0; } }
Der Prozessor läuft bei mit 128khz. Deswegen muss man beim Programmieren die ISP-Frequenz heruntersetzen. Nach dem Programmstart werden erstmal alle I/Os und der ADC initialisiert. Dann folgt ein Zeitfenster, bei dem man den Poti auf eine richtige Position einstellen kann, damit die Schaltung Tag & Nacht korrekt unterscheiden kann. Anschließend wird der Watchdog und der Sleepmode konfiguriert und der AVR schlafen gelegt.
Aufgeweckt wird der AVR sekündlich. Es wird geprüft, ob eine Aktion folgen muss, sonst legt der AVR sich gleich wieder schlafen. Wenn die definierte Zeit verstrichen ist, dann blinkt die LED kurz auf (sofern es dunkel ist). Der Code für das Überprüfen der Helligkeit wird auch regelmäßig aufgerufen (bei mir halbstündlich). Bevor der ADC die Helligkeit misst, wird der Spannungsteiler eingeschaltet, damit der genug Zeit hat, einen korrekten Wert auszugeben.
Die LED ist bei mir alle 10s für 90ms an. Das reicht, um dies als deutliches Aufblitzen zu erkennen. Habe allerdings auch eine klare, rote 5mm High-Power-LED. Bei schwächeren/low Current-LEDs kann das anders sein.
Ich hatte ursprünglich leider keine Zeichnung angefertigt, weshalb ich diese eben noch per Kopf nachgebaut habe. Die Werte der Widerstände/des Potis weiß ich nicht mehr genau, aber das dürfte ja kein Problem sein…
Versorgt wird das ganze mit 3x AA-Batterien, also mit 4,5V bei vollen Batterien.