logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[AtTiny13][C]Wybudzanie watchdogiem z trybu PowerDown

kajtaskajtas 02 Wrz 2010 20:16 2807 13
  • #1 8469279
    kajtaskajtas
    Poziom 13  
    Witam,
    Buduję prosty układ, który ma zapalić diodę led na 100ms (krótki błysk) po czym przejść w tryb niskiego poboru prądu. Następnie po ok 1s procesor powinien się obudzić i znowu: błysnąć diodą i zasnąć. I tak w kółko.
    Jednak program nie działa jak powinien, tj:

    Procek startuje, błyska diodą, zasypia, po 1s budzi się i... nic nie robi, pozostaje aktywny. Wiem to, bo mierzę prąd pobierany przez procka - na początku na chwilę jest względnie duży, potem wynosi ok 4-5µA, po ok 1s 0,5-1mA - tak już zostaje.

    Proszę o pomoc, jak powinien wyglądać prawidłowy program oraz o informację, jak procesor zachowuje się po wybudzeniu przez WatchDoga - cały procesor jest resetowany (raczej nie, bo wtedy program by działał) czy startuje zaraz za komendą sleep, czy jeszcze inaczej?

    Poniżej jeden z wariantów kodu, który sprawdzałem:

    #define F_CPU 1200000UL                   // Zegar fabryczny 1,2MHz (9,6 / 8)
    #include <avr/io.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    #include <avr/wdt.h> 
    #include <avr/sleep.h>
    #define LED PB0
    
    int main (void) //:::::::::::: MAIN::::::::::::
    {
    
    for(;;)
    {	
    
    DDRB |= (1 << LED);                       // LED on PB0
    
    wdt_enable(WDTO_1S);       //ustaw pieska na 1s
    
    PORTB |= (1 << LED);       //zapal (stan wysoki)
    _delay_ms(100);            //poczekaj chwile (błysk)
    PORTB &= ~(1 << LED);     //zgaś (stan niski)
    
    set_sleep_mode(SLEEP_MODE_PWR_DOWN); //ustaw tryb spania
    sleep_mode();                        //śpij!
    } 
    
    
    
    }
    
    
    


    pozdrawiam
  • #2 8469435
    tmf
    VIP Zasłużony dla elektroda
    Zdecydowanie cały procesor jest resetowany. Wykorzystanie WD jest zresztą jedyną programową metodą na przywrócenie wartości defaultowych całemu procesorowi. Zobacz jak wygląda wygenerowany kod assemblerowy i mapę pamięci z pliku lss. Przypuszczam, że dla tak małego procesorka, z mizerną ilością SRAM problemem może być sam kompilator.
  • #3 8469494
    kajtaskajtas
    Poziom 13  
    Dzięki za odpowiedź, jednak kompletnie nie znam się na assemblerze, dlatego prosiłbym o jakieś dokładniejsze wskazówki jak zobaczyć wygenerowany kod asm, nie wiem także gdzie znaleźć ten plik lss. Korzystam z avr-gcc w WinAVR.
  • Pomocny post
    #4 8469716
    hotdog
    Poziom 26  
    Witam. Spróbuj wykorzystać watchdoga w trybie przerwania nie resetu.

    W Main'ie błyskasz diodą pierwszy raz, ustawiasz pieska w tryb przerwania, włączasz watchdoga na 1S i usypiasz procesor i następie pętla bez końca i z pustym ciałem.

    W przerwaniu mrugasz diodą, resetujesz watchdoga i idziesz dalej spać.

    Sumarycznie będziesz nawet pobierał mniejszy prąd.

    To taki walk around, ale tak to właśnie się powinno IMO robić.

    Pozdro.
  • #5 8469920
    kajtaskajtas
    Poziom 13  
    Dzięki hotdog, jutro sprawdzę czy w takiej konfiguracji zadziała. Swoją drogą przeczytałem właśnie w pliku wdt.h, że po resecie watchdog pozostaje aktywny, nastawiony na najkrótszy czas (czyli bodajże 15ms). Możliwe, że to przez to po pierwszym resecie układ już nie przechodził więcej w power-down, chociaż wydaje mi się, że taki wariant już sprawdzałem (wyłączanie WDT zaraz po resecie, i powtórne ustawienie go po mrygnięciu) ale jeszcze raz jutro sprawdzę.

    Pozdrawiam
  • #6 8469984
    gaskoin
    Poziom 38  
    kajtaskajtas napisał:
    Możliwe, że to przez to po pierwszym resecie układ już nie przechodził więcej w power-down, chociaż wydaje mi się, że taki wariant już sprawdzałem (wyłączanie WDT zaraz po resecie, i powtórne ustawienie go po mrygnięciu) ale jeszcze raz jutro sprawdzę.
    Pozdrawiam


    a wyłączałeś watchdoga w startupie czy już w funkcji main ?
  • Pomocny post
    #7 8470007
    mirekk36
    Poziom 42  
    Domyślnie jeśli Watchdog jest włączony cały czas to po resecie ma domyślnie ustawiony czas 16ms (bity WDP2..0 w rejestrze WDTCR są zerami)

    Zatem układ wciąż ci się resetuje po pierwszym przebiegu.

    Żeby się tego pozbyć wklej sobie przed funkcją main() coś takiego

    
    static void __init3(
        void )
        __attribute__ (( section( ".init3" ), naked, used ));
    static void __init3(
        void )
    {
    	wdt_reset();
    	MCUSR &= ~(1<<WDRF);
    	WDTCR |= (1<<WDCE) | (1<<WDE);
    	WDTCR = 0x00;
    }


    Spowoduje to zawsze wyłączenie Watchdoga po resecie dzięki czemu zdąży się ładnie wykonać twój program mignięcia diodą po czym znowu go włączysz i uśpisz procka.
  • #8 8472441
    kajtaskajtas
    Poziom 13  
    Baardzo dziękuję wszystkim za pomoc, problemem rzeczywiście okazał się być WDT ustawiony automatycznie na 16ms po resecie. Po małej modyfikacji programu zwykłe "wdt_disable();" na samym początku programu rozwiązało problem, i układ działa tak jak powinien. Ewentualnie spróbuję jeszcze jak będzie wyglądało użycie wdt jako przerwania, ale z tym sobie już powinienem poradzić.

    Poniżej zamieszczam jeszcze działający kod w razie, gdyby ktoś w przyszłości miał podobny problem ;)

    #define F_CPU 1200000UL                   // Zegar fabryczny 1,2MHz (9,6 / 8)
    #include <avr/io.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    #include <avr/wdt.h>
    #include <avr/sleep.h>
    #define LED PB0
    
    int main (void) //:::::::::::: MAIN::::::::::::
    {
    
    for(;;)
    {   
    wdt_disable();
    DDRB |= (1 << LED);                       // LED on PB0
    
    wdt_enable(WDTO_1S);       //ustaw pieska na 1s
    
    PORTB |= (1 << LED);       //zapal (stan wysoki)
    _delay_ms(100);            //poczekaj chwile (błysk)
    PORTB &= ~(1 << LED);     //zgaś (stan niski)
    
    wdt_reset();
    
    set_sleep_mode(SLEEP_MODE_PWR_DOWN); //ustaw tryb spania
    sleep_mode();                        //śpij!
    }
    }
    


    Pozdrawiam!
  • #9 8473659
    gaskoin
    Poziom 38  
    lepiej jednak umieścić funkcję podobną do tej zaprezentowanej przez mirka (z małą zmianą - wyzerować MCUSR i wywołać watchdog_disable()), umieszczoną w sekcji init3, ponieważ funkcja ta jest wykonywana wtedy jeszcze zanim program przejdzie do funkcji main - w tzw startupie. Czasami może się zdarzyć, że procesor nie zdąży wejść do funkcji main i zablokować watchdoga, a watchdog już zresetuje procesor :)
  • #10 8478532
    kajtaskajtas
    Poziom 13  
    Mam jeszcze jedno pytanie - czy tak ma wyglądać kod z włączoną obsługą Watchdog'a jako przerwanie, a nie reset? Chodzi mi głównie o dane wpisywane do rejestrów WDTCR i MCUSR:
    #define F_CPU 1200000UL                   // Zegar fabryczny 1,2MHz (9,6 / 8)
    #include <avr/io.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    #include <avr/wdt.h>
    #include <avr/sleep.h>
    #define LED PB0
    
    ISR(WDT_vect)
    {
    PORTB |= (1 << LED);       //zapal (stan wysoki)
    _delay_ms(100);            //poczekaj chwile (błysk)
    PORTB &= ~(1 << LED);     //zgaś (stan niski)
    
    wdt_reset();
    
    set_sleep_mode(SLEEP_MODE_PWR_DOWN); //ustaw tryb spania
    sleep_mode();                        //śpij!
    }
    
    int main (void) //:::::::::::: MAIN::::::::::::
    {
       
    //wdt_disable();
    DDRB |= (1 << LED);                       // LED on PB0
    
    PORTB |= (1 << LED);       //zapal (stan wysoki)
    _delay_ms(100);            //poczekaj chwile (błysk)
    PORTB &= ~(1 << LED);     //zgaś (stan niski)
    
    //wdt_enable(WDTO_2S);       //ustaw pieska na 2s
    MCUSR &= ~(1<<WDRF);
    WDTCR |= (1 << WDCE);
    WDTCR |= (0 << WDE) | (1 << WDTIE) | (0 << WDP3) | (1 << WDP2) | (1 << WDP1) | (0 << WDP0);
    
    set_sleep_mode(SLEEP_MODE_PWR_DOWN); //ustaw tryb spania
    sleep_mode();                        //śpij!
    
    for(;;){}
    }
    

    pozdrawiam
  • #11 8478812
    gaskoin
    Poziom 38  
    Teraz masz watchdoga ustawionego tak, że po wykonaniu obsługi przerwania nastąpi reset. To tak po przejrzeniu kodu z grubsza :)
  • #12 8478938
    kajtaskajtas
    Poziom 13  
    Hmm, a czemu? WDE = 0 i WDTIE = 1 włącza przecież Interrupt Mode. Podejrzewam, że muszę jeszcze wyzerować/ustawić jakiś bit, o którym nie mam pojęcia? :P Sprawdziłem ten program (dopisałem oczywiście jeszcze sei(); w mainie) i dzieje się jeszcze inaczej, bo dioda mrygnęła dwa razy (main + 1x przerwanie), potem procek zasypia i już się nie budzi (pobór prądu 3-4uA)
  • Pomocny post
    #13 8478987
    gaskoin
    Poziom 38  
    nie widziałem, że tam są zera, co jest bez sensu, bo a | 0 = a

    bo procesor zasypia w przerwaniu, z którego nic już nie jest w stanie go obudzić ...

    Cytat:
    Chodzi mi głównie o dane wpisywane do rejestrów WDTCR i MCUSR


    jak zwykle problemy są zupełnie gdzieś indziej ;d
  • #14 8479027
    kajtaskajtas
    Poziom 13  
    Z tym przerwaniem masz rację, chyba za bardzo zasugerowałem się postem hotdoga. Przeniosłem zasypianie do pętli nieskończonej i już jest dobrze. Mam małe doświadczenie z przerwaniami i dlatego trochę się w tym jeszcze motam.

    A te zera są dla przejrzystości, szczególnie się to przydaje np tu przy WDP3..0 - jak kiedyś będę modyfikował kod lub korzystał z niego przy pisaniu innego programu, to wygodniej zmienia się preskaler itp. Także to jest ze względów czysto "pomocniczych".

    Dziękuję za szybką pomoc ;)
    pozdrawiam
REKLAMA