Le but de ce programme est d'allumer une led 5 secondes toutes les 5 minutes, 30 minutes ou 60 minutes.
Le changement de mode se fait sur la pin 2 grâce à l'interruption INT0. La base de temps est donnée par le watchdog qui déclenche une interruption toutes les secondes.
Sauf que...le programme allume bien la led mais l'éteint un 1/10 de seconde avant de se rallumer etc.
Voici le programme sous atmel studio
#define F_CPU 1000000L
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>
#define PIN_SORTIE PB3
#define TEMPS_SORTIE_SECONDES 5
#define NOMBRE_MODES 3
unsigned char temps_cycle_minutes[]={5,30,60};
unsigned char mode;
unsigned char compteur_secondes;
unsigned char compteur_minutes;
//Action à effectuer toutes les secondes
ISR(WDT_vect)
{
wdt_reset();
if(compteur_secondes==59)
{
compteur_secondes=0;
if(compteur_minutes==temps_cycle_minutes[mode]-1)
{
compteur_minutes=0;
PORTB|=1<<PIN_SORTIE;
} else compteur_minutes++;
} else compteur_secondes++;
if(compteur_secondes==TEMPS_SORTIE_SECONDES) PORTB&=~(1<<PIN_SORTIE);
}
ISR(INT0_vect)
{
//Anti-rebond
_delay_ms(50);
if((PINB&(1<<PB2))==0)
{
if(mode==NOMBRE_MODES-1) mode=0;
else mode++;
unsigned char i;
for(i=0; i<=mode; i++)
{
PORTB|=1<<PIN_SORTIE;
_delay_ms(500);
PORTB&=~(1<<PIN_SORTIE);
_delay_ms(500);
}
PORTB|=1<<PIN_SORTIE;
compteur_secondes=0;
compteur_minutes=0;
wdt_reset();
}
}
void setup()
{
cli();
mode=0;
compteur_secondes=0;
compteur_minutes=0;
//PIN_SORTIE comme sortie
DDRB|=1<<PIN_SORTIE;
//PIN_SORTIE à l'état haut, les autres pins avec résistances de pull up
PORTB=0xFF;
//Mode de veille le plus économique
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
//Paramétrage du watchdog (cf datasheet p44-47)
//Interruption au déclenchement du watchdog
WDTCR|=(1<<WDE)|(1<<WDIE);
//Prescaller sur 65536 tours d'horloge
WDTCR&=~(1<<WDP0);
WDTCR|=(1<<WDP1)|(1<<WDP2);
//Paramétrage de l'interruption INT0 (cf p51-54 de la datasheet)
//INT0 sur front descendant
MCUCR&=~(1<<ISC00);
MCUCR|=1<<ISC01;
//On active l'interruption sur INT0
GIMSK|=(1<<INT0);
sei(); //On active les interruptions
}
void loop()
{
sleep_enable();
sleep_cpu();
sleep_disable();
}
int main(void)
{
setup();
while(1)
{
loop();
}
}
le watchdog redémarre le microcontroleur à chaque fois qu'il fait un overflow. du coup ton programme recommence du même point toutes les secondes (mettons la premiere instruction de setup, pour fixer...).
du coup... si tu veux juste compter un temps, bah 'faut pas se servir du watchdog.
Merci beaucoup. J'ai relu un peu la doc, du coup il disent que si WDE est à 1, WDIE est mis à 0 à la prochaine interruption. Ca redémarre donc bien. Je serais entre mettre WDIE à 1 à chaque interruption, ou mettre WDE à 0 dans le setup.
Tu as une alternative pour compter le temps en mode veille?
bah si tu coupe pas les timers, tu dois pouvoir t'en servir, mais désactive le watchdog.
en fait, sur certains vieux microcontroleurs, le watchdog c'était un timer "normal", pour lequel on pouvait activer une option qui redémarre le micro à chaque overflow.
dans tous les cas, tes timers, à priori actifs même en mode veille, parce que tu peux faire réveiller le microcontroleur sur une interruption sur un timer, du coup... mais attention, un timer c'est un compteur, qui compte des oscillations d'une horloge, donc vérifie que ton horloge est pas coupée par le mode veille, et tout se passera bien, normalement...
En effet, tout est écrit dans ce que tu as entouré
Le bit WDIE à '1' permet d'activer l'interruption du watchdog tout en empêchant que le processeur passe en RESET.
Cependant, il est automatiquement remis à '0' de façon matérielle lorsque l'interruption se produit.
Du coup, au prochain overflow du watchdog, il n'y aura pas d'interruption mais un RESET.
Si tu ne veux pas de RESET, la première chose à faire dans l'interruption est de remettre WDIE à '1'.
A noter que wdt_reset(); permet de remettre le compteur du watchdog à 0 et non de mettre ce bit à WDIE à '1'.
Du coup, ton microprocesseur passe ne mode RESET au deuxième coup du watchdog.
Ton code devrait donc plutôt être :
//Action à effectuer toutes les secondes
ISR(WDT_vect)
{
// Reparamétrage du watchdog pour ne pas qu'il nous fasse un reset au prochain coup
WDTCR|=(1<<WDE)|(1<<WDIE);
if(compteur_secondes==59)
{
compteur_secondes=0;
if(compteur_minutes==temps_cycle_minutes[mode]-1)
{
compteur_minutes=0;
PORTB|=1<<PIN_SORTIE;
} else compteur_minutes++;
} else compteur_secondes++;
if(compteur_secondes==TEMPS_SORTIE_SECONDES) PORTB&=~(1<<PIN_SORTIE);
}
A mon avis, tu ne vas pas tarder à te rendre compte que tu auras un problème avec ton interruption sur le bouton.
En effet, le processeur ne peux pas gérer des interruptions imbriquées donc pendant qu'il est dans l'interruption du bouton, il ne fait pas l'interruption du watchdog
Or, dans l'interruption du bouton, tu fais de gros delay qui peuvent amener le watchdog à se redéclencher et donc faire un RESET le processeur car WDIE n'aura pas été remis à '1'.
Tu ferais mieux de faire de petit delay avec des wdt_reset pour remettre le compteur à 0 avant qu'il ne soit trop tard.
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
oui. non. enfin je regarde et je te dis.
oui. non. enfin je regarde et je te dis.