[Réglé]Soucis avec le Watchdog

Bonjour,
J'essaie d'optimiser la consommation électrique de mon horloge et pour cela je souhaite utiliser le Watchdog de l'AVR.
Je suis parti d'un exemple qui fonctionne mais j'ai des soucis pour l'intégrer à mon code.
J'appelle la fonction goToSleep() depuis ma fonction loop() selon certaines conditions. J'ai la trace attendue, mais le sketch ne s'arrête pas.
Il boucle encore un certain nombre de fois, puis l'ensemble se fige. Faut un reset pour le débloquer.

L'idée c'est d'endormir l'AVR et de le faire réveiller par le Watchdog ou une interruption matérielle.
On ne peut l'endormir que pendant 8s maximum alors quand l'horloge est en veille, elle doit se réveiller toutes les 8s, puis se rendormir.

Voici un extrait de code, avec la fonction qui endort l'AVR et qui configure l'ISR utilisée par le Watchdog .

Je ne maitrise pas encore toutes les subtilités des registres de l'AVR (mais je suis familier du dév de plus ou moins bas niveau depuis quelques décenies).
J'imagine que le problème est dans la configuration du watchdog et dans le type de sleep mode. Je souhaiterais utiliser le sleep mode qui consomme le moins.

/*
* Endort l'AVR
*/
void goToSleep(){
#ifdef DEBUG_SERIAL
	Serial.println("dodo!");
#endif	
	// clear various "reset" flags
  MCUSR = 0;     
  // allow changes, disable reset
  WDTCSR = _BV (WDCE) | _BV (WDE);
  // set interrupt mode and an interval 
  WDTCSR = _BV (WDIE) | _BV (WDP3) | _BV (WDP0);    // set WDIE, and 8 seconds delay
  wdt_reset();  // pat the dog

  
	// Choose our preferred sleep mode:
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 
    // Set sleep enable (SE) bit:
    sleep_enable();
 
    // Put the device to sleep:
    sleep_cpu();
 
    // Upon waking up, sketch continues from this point.
    sleep_disable();
}

// watchdog interrupt
ISR (WDT_vect) 
{
  wdt_disable();  // disable watchdog  
  Serial.println("Réveillé par le watchdog");
}

Si quelqu'un a une piste ou des liens vers des exemples (car je trouve qu'ils se font rares!).

J'aurais bien essayé mais je ne sais pas où récupérer les fonctions wdt)() et sleep_().
Elles ne sont pas dans les libs de base.

barbudor:
J'aurais bien essayé mais je ne sais pas où récupérer les fonctions wdt)() et sleep_().
Elles ne sont pas dans les libs de base.

En effet, elles sont ici:

//includes pour sleep mode
#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/wdt.h>

Voici l'exemple duquel je suis parti: http://www.engblaze.com/hush-little-microprocessor-avr-and-arduino-sleep-mode-basics/

Pour info j'utilise un Duemillanove (et bientôt un ATMega328 avec le minimum autour).

Bonjour

De mon côté ce n'est encore qu'un sujet prévu sur lequel je n'ai (pour AVR) que des marques pages
dans mon cas je dois me pencher sur la veille profonde et le reveil par interruption externe (PCF8583 programmé)
Sujet assez peu traité (les Duemilanove et Uno ne sont de toutes façons pas concues pour les très faibles consommations)
La bonne adresse est je pense JeeLabs qui fait comme toujours de manière plûtôt exhaustive !!
Pas moins de 75 articles depuis 2009 marqués 'low power'... il a testé tour à tour tous les niveaux de veille, toutes les solutions pour reveiller le µC sans parler des régulateurs et solutions d'alimentations.Mesures et courbes à l'appui...
http://jeelabs.org/tag/lowpower/

Sinon , sans avoir lu ton code , donc sous toutes réserves je dirai que :

Il boucle encore un certain nombre de fois, puis l'ensemble se fige. Faut un reset pour le débloquer.

fait penser à des interruptions imbriquées et une pile qui déborde là ou il ne faut pas... Il n' y a pas beaucoup d'espace RAM sur nos micros, ça se bouscule très vite...
Je ne connais pas encore les circuits d'interruptions sur AVR mais je me demande si dans ta routine ISR le wdt_disable() suffit à se prémunir contre des interruptions imbriquées. Ne faut-il pas explicitement (comme pour d'autres micros) baisser le drapeau de l'interruption en cours de traitement ?

J'ai rien touché a part rajouté quelques LEDs et un peu de délay pour être sur que la liaison série soit purgée et ca marche :

#include <avr/interrupt.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <avr/wdt.h>

#define LED1    13
#define LED2    12

void goToSleep()
{

  Serial.println("dodo!");
  delay( 1000 );
  
	// clear various "reset" flags
  MCUSR = 0;     
  // allow changes, disable reset
  WDTCSR = _BV (WDCE) | _BV (WDE);
  // set interrupt mode and an interval 
  WDTCSR = _BV (WDIE) | _BV (WDP3) | _BV (WDP0);    // set WDIE, and 8 seconds delay
  wdt_reset();  // pat the dog

  
	// Choose our preferred sleep mode:
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
 
    // Set sleep enable (SE) bit:
    sleep_enable();
 
    // Put the device to sleep:
    digitalWrite( LED1, LOW );
    digitalWrite( LED2, LOW );
    sleep_cpu();
    digitalWrite( LED1, HIGH );
 
    // Upon waking up, sketch continues from this point.
    sleep_disable();

    Serial.println("reveil!");
    delay(100);
}

// watchdog interrupt
ISR (WDT_vect) 
{
  digitalWrite( LED2, HIGH );
  wdt_disable();  // disable watchdog  
  Serial.println("Watchdog!");
}

void setup()
{
  Serial.begin(9600);
  
  Serial.println( "Dans setup..." );
 
  digitalWrite( LED1, HIGH );
  pinMode( LED1, OUTPUT );
  digitalWrite( LED2, HIGH );
  pinMode( LED2, OUTPUT );
  
  delay(100);
  
}



void loop()
{
  Serial.println("Loop..." );
  delay(1000);
  goToSleep();  
  Serial.println("...Loop" );
}

Je pensais avoir isolé le problème mais non, ça bloque toujours.
Ca doit être quelque part au niveau des interruptions en effet. Je vais continuer de creuser.

J'ai deux sources d'interruptions matérielles: un détecteur de mouvement (PIR) et un récepteur DCF77.
Je coupe l'alim du récepteur DCF77 quand l'horloge passe en veille.

Pas facile à débugguer!

Je me demande si le fait d'avoir un Serial.println() dans une ISR ne pose pas problème? (j'avais lu quelque part que c'est déconseillé).

personnellement, par prudence (excès de prudence parfois) , j'enlèverai le serial.println() de l'ISR pour que le traitement de cette routine soit la plus courte possible. je préfère ne laisser que ce qui est urgent et indispensable. Quitte à lever un drapeau et faire le traitement peu après hors routine d'interruption. Tout ce qui peut attendre doit attendre. un serial.println() c'est pas si urgent que ça... Tu a en plus deux sources d'interruption matérielles, ça devient délicat !!

l'alim du DCF77 est coupée à la mise en veille. Est-ce que la sortie de veille (interruption watchdog) rétablit immédiatement l'alim DCF77. Si oui, est-ce que le signal DCF77 est propre ? produit-il une interruption lors d cette remise en route ?

C'est réglé!
Alors il y avait deux problèmes:
-Le Serial.println() dans l'ISR était la cause du blocage.
-Et j'avais un bug dans ma gestion des modes de veille qui lors de l'expiration du premier délai de 8s ne ré-enclenchait pas correctement le watchdog.

J'ai deux mode de veille: j’éteins l'affichage au bout d'un certain temps et j'allume alors le récepteur DCF77 (car la réception radio est brouillée par le multiplexage de la matrice de led via le Max 7219).
Quand j'ai reçu une trame DCF77 complète je mets l'horloge dans un second mode de veille où là j'utilise le watchdog.

Elle est actuellement synchronisée et exécute 1 cycle ( loop())toutes les 8s comme attendu.

La sortie du mode sommeil via une interruption matérielle du PIR fonctionne comme attendu.

Je touche au but (le plus dur est de faire la face avant!).