Partage
  • Partager sur Facebook
  • Partager sur Twitter

Base de temps sur attiny45, comportement étrange

    20 janvier 2015 à 21:10:22

    Bonjour,

    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();
        }
    }



    -
    Edité par pemace 20 janvier 2015 à 21:14:12

    • Partager sur Facebook
    • Partager sur Twitter
      21 janvier 2015 à 0:00:22

      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.

      -
      Edité par remace 21 janvier 2015 à 0:01:26

      • Partager sur Facebook
      • Partager sur Twitter

      oui. non. enfin je regarde et je te dis.

        21 janvier 2015 à 12:35:46

        Salut remace,

        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?

        -
        Edité par pemace 21 janvier 2015 à 12:43:57

        • Partager sur Facebook
        • Partager sur Twitter
          21 janvier 2015 à 19:17:11

          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...

          • Partager sur Facebook
          • Partager sur Twitter

          oui. non. enfin je regarde et je te dis.

            21 janvier 2015 à 21:17:40

            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.

            Le code devient alors :

            unsigned char i;
            unsigned char j
            
            for( i = 0 ; i <= mode ; i++ ) {
            
                PORTB |= 1<<PIN_SORTIE;
            
                for ( j = 0 ; j < 5 ; j++ ) {
                    wdt_reset();
                    _delay_ms(100);
                }
            
                PORTB&=~(1<<PIN_SORTIE);
            
                for ( j = 0 ; j < 5 ; j++ ) {
                    wdt_reset();
                    _delay_ms(100);
                }
            
                PORTB|=1<<PIN_SORTIE;
            
            }

            5 delay de 100ms, c'est pareil que un delay de 500ms tout en permettant de faire des wdt_reset() de temps en temps.

            -
            Edité par lorrio 21 janvier 2015 à 21:20:39

            • Partager sur Facebook
            • Partager sur Twitter
              22 janvier 2015 à 16:45:22

              Merci remace et lorrio.

              • Partager sur Facebook
              • Partager sur Twitter

              Base de temps sur attiny45, comportement étrange

              × 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.
              • Editeur
              • Markdown