Ich habe ein erstes kleines Projekt mit einem ATTINY 261 , Button und einer Digital-Anzeige.
Um das ganze an einer Batterie betreiben zu können versuche ich mich gerade
den ATTINY in den SLEEP Modus zu setzen und alle 100 ms mit einem timer interrupt wieder aufzuwecken.
Leider funktioniert das aufwecken nicht.
void setup()
{
// Setup Timer0
cli(); // Disable interrupts
TCCR0A = 0; // Clear timer control register A
TCCR0B = 0; // Clear timer control register B
TIMSK |= (1 << TOIE0); // Enable timer overflow interrupt
sei(); // Enable interrupts
// Set prescaler to 64
TCCR0B |= (1 << CS01) | (1 << CS00);
// display a 1 in the 1-7 digit display
showNumber(1);
void loop()
{
// Put ATtiny261 to sleep
// set_sleep_mode(SLEEP_MODE_PWR_DOWN);
set_sleep_mode(SLEEP_MODE_IDLE);
sleep_enable();
sleep_cpu();
sleep_disable();
// Check if 100ms has elapsed (approximately)
if (timerCounter >= 25)
{ // Approximately 100ms with prescaler 64
timerCounter = 0; // Reset counter
// Display 2 in the 1-7 Digit display
showNumber(2);
volatile uint8_t timerCounter = 0;
// Increment the counter on timer overflow
ISR(TIM0_OVF_vect) { timerCounter++; }
Der Verbrauch sinkt auf 11 mA (etwa 9mA für die 1 in der Anzeige), leider funktioniert das aufwecken nicht.
Hat mit jemand einen Tipp?
Beim überfliegen des Attiny261 Datenblattes scheinen die Register dieselben eines Attiny85 zu sein...
Darum hier ein grobes Raster welches ich bei einem Attiny85 verwendet habe:
#include <avr/wdt.h>
#include <avr/sleep.h>
// time until the watchdog wakes the mc in seconds
constexpr uint8_t WATCHDOG_TIME {8}; // 1, 2, 4 or 8
// after how many watchdog wakeups we should collect and send the data
constexpr uint8_t WATCHDOG_WAKEUPS_TARGET {7}; // 8 * 7 = 56 seconds between each data collection
// watchdog ISR
ISR(WDT_vect) {
// nothing to do here, just wake up
}
void enableWatchdog() {
cli();
MCUSR &= ~(1 << WDRF); // clear the reset flag
WDTCR |= (1 << WDCE) | (1 << WDE); // set WDCE to be able to change/set WDE
#if WATCHDOG_TIME == 1 // set new watchdog timeout prescaler value
WDTCR = 1 << WDP1 | 1 << WDP2;
#elif WATCHDOG_TIME == 2
WDTCR = 1 << WDP0 | 1 << WDP1 | 1 << WDP2;
#elif WATCHDOG_TIME == 4
WDTCR = 1 << WDP3;
#elif WATCHDOG_TIME == 8
WDTCR = 1 << WDP0 | 1 << WDP3;
#else
// #error WATCHDOG_TIME must be 1, 2, 4 or 8!
#endif
WDTCR |= (1 << WDIE); // enable the WD interrupt to get an interrupt instead of a reset
sei();
}
// function to go to sleep
void enterSleep(void) {
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // EDIT: could also use SLEEP_MODE_PWR_DOWN for lowest power consumption.
sleep_enable();
sleep_mode(); // Now enter sleep mode.
// The program will continue from here after the WDT timeout
sleep_disable(); // First thing to do is disable sleep.
}
void setup() { enableWatchdog(); }
void loop() {
// some code
// sleep
for (size_t i = 0; i < WATCHDOG_WAKEUPS_TARGET; i++) { enterSleep(); }
}
Das stimmt doch mit dem Codebeispiel überein. Und demnach müsste der Watchdog alle 8 Sekunden auslösen. Die Schleife wird sieben mal durchlaufen, also müsste etwa alle 56 Sekunden die loop() einmal abgearbeitet werden.
Zu allem Überfluss habe ich jetzt auch noch mit
clock_prescale_set rumgespielt.
Eigentlich erfolgreich: mit clock_prescale_set(clock_div_8);
hat sich der Verbrauch nochmal halbiert.
Aber nach einem clock_prescale_set(clock_div_256);
scheint das Ding nun für Arduino ISP zu langsam zu sein und ich
kann leider nichts mehr auf den ATTINY hochladen.
Da ich leider auch keinen zweiten ATTINY zur Hand habe,
muss mich also zuerst mal damit beschäftigen wie ich den ATTINY
wieder über ISP ansprechbar mache --> TinyFuses
In meinem Beispiel ist noch ein Fehler. Müsst eigentlich aufgefallen sein. Aber trotzdem.
Die Zeile constexpr uint8_t WATCHDOG_TIME {8}; // 1, 2, 4 or 8 ist falsch.
Es muss #define WATCHDOG_TIME 8 lauten. Ansonsten funktionieren die #if Direktiven in der enableWatchdog() Funktion nicht.
Ja, hätte ich eigentlich sehen können, habe mit einem neuen ATTINY den
nicht gefixten code nochmal laufen lassen und statt sleep macht er ein reboot.
Mein nun funktionierender code sieht so aus.
ISR(WDT_vect) { wdt_disable(); };
void enableWatchdog()
{
// disable ADC
ADCSRA = 0;
// ADCSRA |= (1 << ADEN); // Setze das ADEN-Bit, um den ADC zu aktivieren
// clear various "reset" flags
MCUSR = 0;
// allow changes, disable reset
WDTCR = bit (WDCE) | bit (WDE);
// set interrupt mode and an interval
WDTCR = bit (WDIE) | bit (WDP2) | bit (WDP0); // set WDIE, and 0.5 seconds delay
// WDTCR = bit (WDIE) | bit (WDP2); // set WDIE, and 0.25 seconds delay
wdt_reset(); // pat the dog
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
// set_sleep_mode (SLEEP_MODE_IDLE);
// set_sleep_mode (SLEEP_MODE_STANDBY);
noInterrupts (); // timed sequence follows
sleep_enable();
// turn off brown-out enable in software
// MCUCR = bit (BODS) | bit (BODSE);
// MCUCR = bit (BODS);
interrupts (); // guarantees next instruction executed
sleep_cpu ();
// cancel sleep as a precaution
sleep_disable();
}
void setup()
{
clock_prescale_set(clock_div_8);
for(byte i=0; i<=7; i++)
pinMode(LED[i],OUTPUT);
pinMode(PIN_BTN1,INPUT_PULLUP);
// pinMode(PIN_BUZZER,OUTPUT);
DDRB |= (1 << PIN_BUZZER); // PWM-fähiger Pin für Buzzer
}
Verbrauch liegt wie erwartet bei etwa 5µA bei ausgeschalteten LEDs.
Werde mich nun mit einer Stromversorgung an einer 9V Block Batterie (mit MCP1602) beschäftigen.