sleep cpu & pcint

Da will man bei einem alten Projekt mal eben den Code ein wenig aufpeppen, drückt nach einiger Weile den Uploadknopf und zack... nix geht mehr :confused: kennt ihr das?

Im Detail geht es um eines meiner ersten Projekte, eine Fernbedienung, die verschiedene Geräte ansteuern kann. Da die Tasten nicht sehr zuverlässig funktionierten und nicht all zu schnell nacheinander gedrückt werden konnten, wollte ich mal das pollen und debouncen überarbeiten und die Möglichkeit hinzufügen, eine Taste gedrückt zu halten, um einen Befehl schnell wiederholt zu senden.

Problem:
Nach jedem Tastendruck geht die Fernbedienung in den pwr down modus, um die Batterien zu schonen. Dies hat im alten Sketch tip top funktioniert, im neuen funktionierts nun nicht mehr, obwohl die Schlaffunktion und die ISR praktisch identisch sind. Drücke ich eine Taste, wacht er manchmal gar nicht und manchmal stark verspätet auf. Deaktiviere ich die Funktion, die den uC in den Schlaf schickt, funktioniert alles wunderbar. Komme nicht drauf, was der Fehler ist.

Hier der neue Code:
Tab 1

#include <IRremote.h>
#include <avr/sleep.h>
#include <PinChangeInt.h>


int Tasten[9] = {6, 7, A4, 5, 8, A3, A5, A2};
int unused[7] = {A0, A1, A6, A7, 2, 4};
byte reading = B11111111;
byte laststate = B11111111;
byte buttonvalues = B11111111;
byte lastbuttonvalues = B11111111;
int buttonevent;

unsigned long lastDebounce = 0;
unsigned long repeattimer = 0;
unsigned long sleeptimer = 0;

unsigned long deviceArray1[8] = {0x80C, 0x83C, 0x810, 0x811, 0x838, 0x80D, 0x82B};
unsigned long deviceArray1_2[8] = {0xC, 0x3C, 0x10, 0x11, 0x38, 0xD, 0x2B};
unsigned long deviceArray2[8] = {0x800FF, 0x838C7, 0x88877, 0x8A857, 0x848B7, 0x828D7, 0x8B847};
unsigned long deviceArray3[8] = {0x77E17A1E, 0x77E1401E, 0x77E1D01E, 0x77E1B01E, 0x77E1101E, 0x77E1E01E, 0x77E1BA1E};
unsigned long deviceArray4[8] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};

int rc5state = 0;
// IR Led hängt an Pin 3

int Leds[5] = {9, 10, 11, 12};

int device = 0;
unsigned long loopCount = 0;

IRsend irsend;

void setup() {
  for (int i = 0; i < 8; i++) {
    pinMode(Tasten[i], INPUT);
    digitalWrite(Tasten[i], HIGH);
  }
    for (int i = 0; i < 4; i++) {
    pinMode(unused[i], INPUT);
    digitalWrite(unused[i], HIGH);
  }
  
  for (int i=0; i<4; i++) {
    pinMode(Leds[i], OUTPUT); 
  }
  pinMode(3, OUTPUT);
}

void loop() {

  poll(20);
  if(klickCheck(7) == 0) {
      switchDevice();
    }

  
  for (int i=0; i<7; i++) {
    if (klickCheck(i) == 0) {
      sendIR(i);
      repeattimer = millis();
    }
    else if (klickCheck(i) == 1 && millis() - repeattimer > 150) {
       sendIR(i);
     repeattimer = millis();
    }
  }
 
 goToSleep();
} // Loop Ende

Tab 2

//----------------------------------------------------------
// goToSleep: attached pci's und legt den uC schlafen
//----------------------------------------------------------

 void goToSleep () {
   set_sleep_mode(SLEEP_MODE_PWR_DOWN);
   sleep_enable();
   delay(30);
   for (int i = 0; i < 8; i++) {
     PCintPort::attachInterrupt(Tasten[i], awake, FALLING);
   }
   sleep_mode();
   // hier startet er beim Erwachen wieder
   sleep_disable();
}

//----------------------------------------------------------
// awake: ISR zum Wecken des uC und Detachen der Interrupts
//----------------------------------------------------------

void awake () {
 sleep_disable();
 for (int i = 0; i < 8; i++) {
   PCintPort::detachInterrupt(Tasten[i]);
 }
}

//----------------------------------------------------------
// Poll: liest alle Sensoren aus und speichert die Werte in Variablen
//----------------------------------------------------------

void poll(unsigned long debouncedelay) {
  for (int i = 0; i<8; i++) {
    bitWrite(reading, i, digitalRead(Tasten[i]));
  }
  if (reading != laststate) {
    lastDebounce = millis();
  }
  if (reading != buttonvalues && millis() - lastDebounce > debouncedelay) {
    buttonvalues = reading;
  }
  laststate = reading;
}

//----------------------------------------------------------
// klickCheck: 0 = frisch geklickt, 1 = gehalten, 2 = losgelassen, 3 = nicht gedrückt
//----------------------------------------------------------

int klickCheck (int button) { //7=switch
    if (bitRead(buttonvalues, button) == 0 && bitRead(lastbuttonvalues, button) == 1) {
      bitWrite(lastbuttonvalues, button, 0);
      buttonevent = 0;
    }
    
    else if (bitRead(buttonvalues, button) == 0 && bitRead(lastbuttonvalues, button) == 0) {
      buttonevent = 1;
    } 
    
    else if (bitRead(buttonvalues, button) == 1 && bitRead(lastbuttonvalues, button) == 0) {
      bitWrite(lastbuttonvalues, button, 1);
      buttonevent = 2;
    }
    else if (bitRead(buttonvalues, button) == 1 && bitRead(lastbuttonvalues, button) == 1) {
      buttonevent = 3;
    }
    return buttonevent;
}

//----------------------------------------------------------
// sendIR: sendet code
//----------------------------------------------------------

void sendIR (int taste) {

  switch(device) {

  case 0:
    if(rc5state == 0) {
      irsend.sendRC5(deviceArray1[taste], 12);
      rc5state = 1;
    }
    else {
      irsend.sendRC5(deviceArray1_2[taste], 12);
      rc5state = 0;
    }
    break;

  case 1:
      irsend.sendNEC(deviceArray2[taste], 32);
    break;

  case 2:
      irsend.sendNEC(deviceArray3[taste], 32);
    break;

  case 3:
    irsend.sendRC5(deviceArray4[taste], 12);
    break;
  }
}

//----------------------------------------------------------
// switch: Gerätumschaltfunktion
//----------------------------------------------------------

void switchDevice() {
    for(int i = 0; i<4; i++) {
        digitalWrite(Leds[i], LOW);
    }
    digitalWrite(Leds[device], HIGH);
    loopCount = millis();
    
    while(millis() - loopCount < 1000) {
      poll(20);
      if(klickCheck(7) == 0) {
        device++;
        if(device > 3) {
          device = 0; 
        }
        for(int i = 0; i<4; i++) {
          digitalWrite(Leds[i], LOW);
        }
        digitalWrite(Leds[device], HIGH);
        loopCount = millis();
      }
    }
    for(int i = 0; i<4; i++) {
      digitalWrite(Leds[i], LOW);
    }
}

Mit sleep_mode_idle gehts... aber da ist mir die Energieersparnis eigentlich zu gering :confused:

Lustigerweise funktioniert es mit pwr_down tadellos, wenn ich den alten sketch aufspiele

Hitsuji:
Mit sleep_mode_idle gehts... aber da ist mir die Energieersparnis eigentlich zu gering :confused:

Lustigerweise funktioniert es mit pwr_down tadellos, wenn ich den alten sketch aufspiele

Verwendest Du ein Standard Arduino-Board oder irgendwas selbstgebruzzeltes als Hardware?

Wenn es Dir auf den Energieverbrauch ankommt, dann sicher das selbstgebruzzelte?
Wie wird denn da der Systemtakt bereitgestellt: intern, extern, Quarz oder Resonator?

Soweit mir bekannt ist, müssen die Fuses für Powerdown-Sleepmodes je nach Takterzeugung passend gesetzt sein, sonst startet der Controller nicht mehr richtig. Im Zweifelsfall die Fuses für die gewählte Takterzeugung immer so wähle, dass die maximale "PowerDown StartUp-Time" gewählt wird. Das wäre im AVR-Fuse Calculator immer der Eintrag mit den "+65ms" zu der gewählten Takterzeugung.

Jawoll, selbst gebastelt mit einem Atmega168p, interner Oszillator. Wie ich die Fuses genau gesetzt habe, weiss ich nicht mehr genau, hab die IDE zwischenzeitlich neu installiert und mir dabei meine modifizierte Boards.txt Datei gelöscht. Müsste da gegebenenfalls mal den avrdude bemühen.

Die Fernbedienung war mit dem alten Sketch aber schon seit einiger Zeit in Betrieb und nutzte den pwr_down mode. Funktionierte eigentlich nicht schlecht und es funktioniert auch jederzeit wieder, wenn ich den alten Sketch aufspiele, was mich nun eben etwas stutzig macht.

Das selbe Problem taucht nun bei meinem aktuellen Projekt auch auf. Schlaf aktivieren gelingt problemlos, allerdings lässt er sich nicht mehr aufwecken.

Scheinbar passiert bei sleepmode idle gar nichts, denn da kann man nach wie vor alles machen, auch wenn gar keine interrupts ausgelöst werden. Dies zeigt sich nun langsam auch beim alten Projekt, denn da sind die Batterien nun einiges schneller entladen als berechnet.

Ich denke, das Problem ist bei beiden Projekten das selbe, deshalb erspare ich euch Einzelheiten zum aktuellen Projekt. Der Code zum Schlafenlegen und Wecken ist praktisch gleich, Systemtakt diesmal 20MHz.

Habe auch nochmals alle Beispiele durchgekuckt, die ich finden konnte, aber finde den Fehler einfach nicht heraus. Wer kann helfen?

Ich nehme an du hast das gelesen?

Da steht das:

 set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  sleep_enable();

  // Do not interrupt before we go to sleep, or the
  // ISR will detach interrupts and we won't wake.
  noInterrupts ();
  
  // will be called when pin D2 goes low  
  attachInterrupt (0, wake, LOW);

....

interrupts ();  // one cycle
 sleep_cpu ();   // one cycle

Im Beispiel mit PinChange Interrupts ganz unten ist es ähnlich:

void goToSleep ()
  {
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);
  ADCSRA = 0;            // turn off ADC
  power_all_disable ();  // power off ADC, Timer 0 and 1, serial interface
  noInterrupts ();       // timed sequence coming up
  resetWatchdog ();      // get watchdog ready
  sleep_enable ();       // ready to sleep
  interrupts ();         // interrupts are required now
  sleep_cpu ();          // sleep                
  sleep_disable ();      // precaution
  power_all_enable ();   // power everything back on
  }  // end of goToSleep

Jep, hab ich gelesen.
Die Interrupts attache ich erst direkt vor dem Schlafenlegen und in der ISR werden sie detached (funktioniert mit dem alten Code prima). Aber auch interrupts() und nointerrupts() hilft nicht weiter.

Ich vermute, dass sich irgendwie das pollen und die klickcheck Funktion in die Haare kriegen. Hab in der ISR mal eine LED einschalten lassen. Klappte eigentlich einwandfrei. Also wird der Interrupt ausgelöst. Wenn ich das Pollen auch in die ISR nehme, kann ich die Funktionen mit zweimaligem Klicken auslösen, jedoch funktionieren dann die Funktionen mit gehaltener Taste nicht...

okay, Problem im alten Gerät gelöst. Meine Poll- und Klickcheck Funktionen benötigen mehrere Durchgänge durch den Loop, um etwas Gescheites zu produzieren. Aber die goToSleep Funktion am Ende des Loops legt den uC nach jeder Runde schon schlafen. Die Funktion also mit einem Timer versehen, der sich bei Tastendruck resetet und bei Untätigkeit nach 2 Sekunden den Schlaf veranlässt.

Allerdings muss in diesem Fall das Problem beim neueren Gerät woanders liegen, denn da ist die Schlaffunktion schon jetzt so gebaut, dass sie erst nach 60s den Schlaf einleitet... :confused:
Speziell am Aufbau der Schaltung ist, dass jeder Taster über einen Multiplexer abgefragt wird. Habe jedoch extra den zentralen Taster an die Adresse 0 gehängt und die Adresseingänge mit Pulldown Widerständen versehen, sodass die Adresse 0 eingestellt ist, wenn vom uC nichts mehr kommt. Ist das ein Fehler?

Voilà! Problem im neuen Gerät auch erkannt, allerdings etwas unbefriedigend gelöst.

Das Problem war, dass ich einen Atmega324p verwende und dieser in der library nicht bei den "ifdef"s aufgelistet ist. Zum funktionieren brachte ichs, indem ich die "if" Bedingungen für den Atmega644 und 644p und das "else" auskommentierte und somit so tat, als wär ich einer von denen :slight_smile:
Allerdings könnte das nun Probleme bereiten, wenn ich wieder einen anderen Controler verwenden will...

Habe auch probiert, das Folgende in die Definezeile einzubauen: || defined(AVR_ATmega324P), hat aber nicht funktioniert. Woher kommen diese def's? Bzw. wo müsste ich meinen Controler dazufügen, damit ich ihn so in die ifdef Reihe einfügen kann?