Fischertechnik
AVR
Raspberry Pi
Elektronik
Netzwerk
Sonstiges


















Impressum

Stromsparen mit ATtiny25

Schaltplan Blinkschaltung mit ATtiny25
Diese Schaltung läuft mit einem alten 65mAh-LiPo aus einem alten Spielzeug mehr als sechs Monate und erzeugt unerwartet helle Lichtblitze.
Der Trick ist die Nutzung der "sleep"-Funktion des ATtiny, die den Strombedarf drastisch reduziert. Das Programm erzeugt alle 15 Sekunden einen Lichtblitz mit einer Dauer von 3 Millisekunden. Die weiße LED wird ohne Vorwiderstand betrieben, überlebt das aber problemlos; sie hat ja genügend Zeit zum Abkühlen.
Der Stromverbrauch im Sleep-Mode/Watchdog-Enabled liegt bei ca. 6μA.

Mit dieser Technik habe ich auch einen Temperatur-Logger auf Basis ATmega328P aufgebaut, der alle drei Minuten die Temperatur misst und auf einer μSD-Karte abspeichert. Dieser Logger läuft mit einem 420mAh-LiPo mehr als 4 Wochen.

Quelltext

/* 2011 Februar CN
   ATTINY 25/45/85 fuer Blinkschaltung
   Stromversorgung: 1 Zelle LiPo
   Beschaltung: Pin 1 offen (Reset)
                Pin 2 Ausgang fuer LED Port B3
                Pin 3 
                Pin 4 Ground, Masse
                Pin 5 
                Pin 6 
                Pin 7
                Pin 8 Vcc, +3,7V
*/
#define SET_B3      PORTB |=  (1<< PB3)       // LED 
#define CLR_B3      PORTB &= ~(1<< PB3)
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
/* ------------------------------------------------------------------------- */
#include <inttypes.h>
#include <avr/io.h>
#include <stdlib.h>
#include <string.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
//-----------------------------------------------------------------------------
// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
void setup_watchdog(uint8_t ii) {
  uint8_t bb;
  if (ii > 9 ) ii=9;    // begrenze auf erlaubte Werte
  bb=(ii+0x18)&0x27;    // schiebe bit3 in bit 5
  bb|= (1<<WDCE);       // ist ggf. ueber .... ?????
  MCUSR &= ~(1<<WDRF);
  WDTCR |= (1<<WDCE) | (1<<WDE); // nur so geht der Enable vom Watchdog!!
  WDTCR = bb;          // set new watchdog timeout value
  WDTCR |= _BV(WDIE);  // Watchdog Interrupt Enable
}
//-----------------------------------------------------------------------------
// set system into the sleep state; system wakes up when watchdog is timed out
void system_sleep() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_mode();    // gute nacht, schlaf schoen...
}
//-----------------------------------------------------------------------------
void delay_seconds_by_watchdog(uint16_t sec){
  uint16_t t1;
  uint8_t  t2; //t2 ist die tatsaechliche Watchdog-Zeit in Sekunden
  uint8_t  t3; //t3 ist der zugehoerige Parameter hierzu
  t1=sec;
  while(t1>0){
    if(t1>7){t3=9;t2=8;}
    else if(t1>3){t3=8;t2=4;}
         else if(t1>1){t3=7;t2=2;}
              else {t3=6;t2=1;};
    setup_watchdog(t3);
    system_sleep();
    t1-=t2;
  };
}
/* ------------------------------------------------------------------------- */
void wait(uint16_t dauer){
  uint16_t j;
  uint8_t i;
  for(j=0;j<dauer;j++){
    for(i=100;i>0;i--)asm volatile ("nop\nnop\nnop\nnop\nnop\nnop\nnop");
  };
}
/* ------------------------------------------------------------------------- */
// ISR fuer Watchdog muss selber nichts tun; wichtig ist, dass sleep endet
ISR(WDT_vect) {
}
/* ------------------------------------------------------------------------- */
void main(void) {
  uint8_t minutes=0;
  DDRB = (1<<PB3);   // Setze LED-Anschluss als Ausgang
  sei();             // erlaube prinzipiell Interrupts
  for(;;){
    SET_B3;          // LED an
    wait(3);         // ca. 3 ms
    CLR_B3;          // LED aus
    DDRB = 0;        // LED Ausgang als Eingang definieren, spart Strom...
    delay_seconds_by_watchdog(15);
    DDRB = (1<<PB3); // LED-Ausgang wieder richtig als Ausgang
  };
}
/* ------------------------------------------------------------------------- */

Makefile

avrdude    = avrdude -v -v
DEVICE     = attiny25
CLOCK      = 1000000
PROGRAMMER =  -c avrispmkII -P usb 
OBJECTS    = blink25.o
# Fuses: internal oscilator 8 MHz, 1:8 prescale, BOD 2.7V
FUSES      =  -U hfuse:w:0xDD:m -U lfuse:w:0x62:m

AVRDUDE = $(avrdude) $(PROGRAMMER) -p $(DEVICE)
OBJDUMP = avr-objdump
COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE) -mint8

# symbolic targets:
all:	blink25.hex blink25.lss

.c.o:
	$(COMPILE) -c $< -o $@

.S.o:
	$(COMPILE) -x assembler-with-cpp -c $< -o $@
asm:
	$(COMPILE) -S -o blink25.asm blink25.c
.c.s:
	$(COMPILE) -S $< -o $@

flash:	all
	$(AVRDUDE) -U flash:w:blink25.hex:i

fuse:
	$(AVRDUDE) $(FUSES)

install: flash fuse

clean:
	rm -f blink25.hex blink25.elf $(OBJECTS)

blink25.elf: $(OBJECTS)
	$(COMPILE) -o blink25.elf $(OBJECTS)

blink25.hex: blink25.elf
	rm -f blink25.hex
	avr-objcopy -j .text -j .data -O ihex blink25.elf blink25.hex
disasm:	blink25.elf
	avr-objdump -Z -d blink25.elf

%.lss: %.elf
	@echo
	$(OBJDUMP) -h -S $< > $@

cpp:
	$(COMPILE) -E blink25.c