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

Tytuł: Resetowanie Watchdog Timera w ATtiny13a - liczenie czasu i sterowanie programem

kdgp1491 31 Gru 2022 14:13 609 13
REKLAMA
  • #1 20361148
    kdgp1491
    Poziom 3  
    Próbuję stworzyć układ, który po wykryciu ciemności przez fototranzystor uruchamia program, który ma wyłączyć się po określonym czasie aktywności.

    [syntax]
    WDTCR |= (1<<WDCE) | (1<<WDTIE);
    WDTCR &= ~(1<<WDE);
    WDTCR |= (1<<WDP3 )|(0<<WDP2 )|(0<<WDP1)|(1<<WDP0); // 8s
    sei();
    if(ACSR & (1<<ACO)) {
    if (czas_c < 2) {
    PORTB |= (1<<PROG_WYK); //PROG_WYK - TAK
    }
    else {
    PORTB &= ~(1<<PROG_WYK); // PROG_WYK - NIE
    {

    ISR(WDT_vect) {
    czas_c ++;
    }
    [/syntax]


    Problemem jest to, że watchdog timer prawdopodobnie za każdym razem resetuje się po przerwaniu i czas_c nie jest liczony
  • REKLAMA
  • #2 20361197
    maciej_333
    Poziom 38  
    Trudno zrozumieć co chcesz zrobić. Jednak Watchdog powinien wybudzać mikrokontroler (poprzez RESET) z głębokiego uśpienia. To oczywiście wyzeruje różne zmienne (jeżeli są inicjalizowane). Powinno się po RESET sprawdzać co było źródłem tego RESETu tj. POR czy właśnie Watchdog. Jest rejestr z jakiego można to odczytać. Kolejna kwestia, to chyba w main() brak jest pętli głównej. To kolejny poważny problem.

    Przerwanie Watchdog'a jest tylko do tego, by zerować jego licznik. Przecież zastosowanie Watchdog'a jest właśnie takie, by zabezpieczyć mikrokontroler przed zawieszeniem.

    Reasumując program nie działa właściwie, bo dochodzi do RESET'u mikrokontrolera poprzez źle wykorzystany Watchdog i brak pętli głównej.
  • REKLAMA
  • #3 20361215
    kdgp1491
    Poziom 3  
    Nie ma main bo to wycinek z całości gdzie mam dużo bałaganu, który by tylko zaciemniał sytuację. Wyciąłem problematyczny kawałek.
    W internecie znalazłem info poniżej, z którego wywnioskowałem, że przy WDE - 0 i WDTIE -1 będzie działać w trybie przerwania bez resetu.
    Ogólnie słabo się znam na programowaniu. Chcę uzyskać efekt, że jeśli fototranzystor podłączony do komparatora wykryje zaciemnienie zadziała program, który wyłączy się po określonym czasie i przejdzie wtedy do trybu czuwania.


    DTON(1) WDE WDTIE Tryb Działanie przy zakończeniu okresu zliczania
    1 0 0 Zatrzymany Żadne
    1 0 1 Tryb przerwania Przerwanie
    1 1 0 Tryb resetu systemu Reset
    1 1 1 Tryb przerwania i resetu systemu Przerwanie, następnie przejście w tryb resetu systemu
    0 X X Tryb resetu systemu Reset
  • REKLAMA
  • Pomocny post
    #4 20361266
    maciej_333
    Poziom 38  
    Jeżeli chcesz wykorzystać Watchdog jako rodzaj timera, to w rejestrze WDTCR powinno się ustawić bit WDTIE. Ponadto za pomocą bitów WDP3...WDP0 należy ustawić prescaler Watchdog'a. Z tego wynika co jaki czas będzie przerwanie. Jeżeli np. potrzeba 8 s, to ustawić należy WDP3 i WDP0, czyli powinno być:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Nie wiem po co tyle kombinacji i jeszcze do tego operacja odczyt-modyfikacja-zapis. Wynika to z dokumentacji Attiny13A:
    Tytuł: Resetowanie Watchdog Timera w ATtiny13a - liczenie czasu i sterowanie programem
    Tytuł: Resetowanie Watchdog Timera w ATtiny13a - liczenie czasu i sterowanie programem
    W związku z tym FUSEBIT WDTON musi być ustawiony na 1, czyli niezaprogramowany.

    Cytat:
    Chcę uzyskać efekt, że jeśli fototranzystor podłączony do komparatora wykryje zaciemnienie zadziała program, który wyłączy się po określonym czasie i przejdzie wtedy do trybu czuwania.

    Proponuję zatem wybudzać mikrokontroler tym przerwaniem. Można wprowadzić go w jeden z trybów uśpienia. Kolejno w przerwaniu od komparatora uruchomić jakiś timer. W jego przerwaniu ponownie wywoływać uśpienie. Oczywiście w takim przypadku nie da się zrobić pełnego uśpienia, więc na czas oczekiwania w uśpieniu na przerwanie można ograniczyć częstotliwość zegara (da się to zrobić programowo). W main() powinna być tylko konfiguracja i pętla główna (bez kodu wewnątrz).
  • #5 20362785
    kdgp1491
    Poziom 3  
    Problem z resetowaniem rozwiązany.
    Obecnie nie mogę poradzić sobie ze zwiększaniem czas_c w ISR(WDT_vect).
    Jeśli używam czas_c ++ wszystko działa, gdy próbuję użyć if (stan==1 czas++) już nie zlicza

    [syntax]
    // Libraries
    #include <avr/io.h> // for GPIO
    #include <avr/sleep.h> // for sleep mode
    #include <avr/interrupt.h> // for interrupts
    #include <avr/wdt.h>
    // Pin definitions
    #define POMIAR PB0 // FOTOTRANZYSTOR
    #define NAP_ODN PB1 // NAPIECIE ODNIESIENIA
    #define SPR_WYBUD PB2 // SPRAWDZANIE WYBUDZENIA
    #define EMPTY PB3 // PUSTY
    #define PROG_WYK PB4 // PROGRAM WYKONAWCZY
    volatile uint32_t czas_c = 0; // licznik czasu w ciemnosci
    volatile uint32_t czas_j = 0; // licznik czasu jasność
    volatile uint8_t stan = 0; // 1-ciemny 2-koniec ciemny 3-jasny 4-koniec jasny
    int max_c = 10; // max czas ciemność - 6h ?
    int max_j = 5; // max czas jasność - 1h ?
    void Tryby_pracy(void);
    void program_glowny(void);
    void czuwanie(void);
    int main(void) {
    DIDR0 = (1<<POMIAR) | (1<<EMPTY); // disable digital input on REF and EMPTY
    DDRB = (1<<SPR_WYBUD) | (1<<PROG_WYK); // LED and BUZZER pin as output
    PORTB = (1<<SPR_WYBUD) | (1<<POMIAR) | (1<<NAP_ODN); // LED on, internal pullups for REF and PROBE
    TIMSK0 = (1<<OCIE0A); // enable output compare match A interrupt
    PCMSK = (1<<NAP_ODN); // enable interrupt on PROBE pin
    MCUSR =0;// &= ~(1<<WDRF);
    WDTCR |= (1<<WDCE) | (1<<WDTIE);
    WDTCR &= ~(1<<WDE);
    WDTCR |= (1 << WDP1) | (1 << WDP2) ; // 1s
    sei();
    Tryby_pracy();
    }
    void Tryby_pracy(void) {
    while(1) {
    if(ACSR & (1<<ACO)) { // zaciemnienie
    if (czas_c < max_c) {
    PORTB |= (1<<PROG_WYK); //PROG_WYK - TAK
    stan = 1;
    }
    else {
    stan = 2;
    PORTB &= ~(1<<PROG_WYK);
    // czuwanie(); // w czuwaniu jeśli zak_j sprawdzanie co 10s - możliwe chwilowe zaćmienie
    }
    }
    else {
    if (czas_j < max_j) {
    PORTB &= ~(1<<PROG_WYK); //PROG_WYK - NIE
    stan = 3;
    }
    else {
    stan = 4;
    // czuwanie(); // w czuwaniu jeśli zak_j sprawdzanie co 15 min
    }
    }
    // program_glowny();
    }
    }
    ISR(WDT_vect) {
    //czas_c ++;
    if (stan == 1) czas_c ++;
    if (stan == 2) czas_j ++;
    [/syntax]
  • #6 20362978
    maciej_333
    Poziom 38  
    Robisz masę dziwnych rzeczy na podstawie tego, co jest w Internecie. Nie korzystasz natomiast z dokumentacji. Nie bardzo rozumiem o co tu chodzi.

    Nie wiem też po co zapisywać coś do PCMSK, skoro nie wykorzystujesz przerwań "pin change". Nie jest to użyte, bo brak stosownego ISR i ustawienia bitu PCIE w rejestrze GIMSK. Ustawiłeś też bit OCIE0A w rejestrze TIMSK0. To z kolei jest zezwolenie na przerwanie typu "compare" dla Timera 0. Teoretycznie nie powinno to wystąpić, bo przy takiej konfiguracji jest odłączony zegar. Nie wiem też po co zerujesz MCUSR, skoro nie sprawdzasz źródła resetu. Pokazałem to wcześniej w konkretnym celu, ale tutaj nie jest to potrzebne. Trybu uśpienia też nie ma. Nie rozumiem po co się bawić z komparatorem bez przerwania od niego?

    Z kodu wynikałoby, że zmienne czas_c i czas_j są inkrementowane w przerwaniu od Watchdog'a, kiedy odpowiednio stan==1 i stan==2. Jednak początkowo zmienna stan jest inicjalizowana na 0. Stąd nigdy nie dojdzie do inkrementacji zmiennych czas_c i czas_j. Musi dojść do zmiany stanu na wyjściu komparatora. Jednak, jak tego nie będzie to stan==3. Wywoła to brak zmiany czas_c i czas_j. Dopiero, jak komparator zmieni stan, to stan==1. Nie ma sensu tego dalej analizować, bo kod w pętli głównej nie ma sensu.

    Napisz co dokładnie chcesz osiągnąć. Wtedy będzie można coś sensownego zaproponować.
  • #7 20363127
    kdgp1491
    Poziom 3  
    Praktycznie jest to mój pierwszy program stąd dużo niejasności bo dopiero się uczę. Testowałem projekt https://github.com/wagiminator/ATtiny13-ContinuityTester . Na jego podstawie próbowałem parę rzeczy zaadoptować do mojego "projektu" i te o których wspomniałeś z niego pozostały i prawdopodobnie nie będą wykorzystane
    Zerowanie MUSCR zatrzymało niechciane resetowanie po przerwaniu (przynajmniej tak mi się wydaje bo teraz się nie resetuje)

    Z "internetu" wyczytałem , że przed modyfikacją zmiennej, która będzie użyta w procedurze obsługi przerwania trzeba wstrzymać globalne przerwania . Nie wiem czy dobrze zrobiłem - zmieniłem kod na :
    cli();
    stan=1
    sei();
    i teraz zlicza prawidłowo
    Dołożyłem- po osiągnięciu stan4 zerowany jest czas_c, po osiągnięciu stan1 zerowany jest czas_j.

    Mam parę świeczek led podobnych do tej https://www.navia.sklep.pl/produkty/wklady-elektryczne/swiece/swieca-ruchomym-10ccm i zamierzam je lekko zmodyfikować. Moje świeczki nie mają timerów i świecą jednostajnie.
    Założenia - po zmroku świeczka ma zapalić się na około 6 godzin (stan1) i po tym czasie ma przejść w stan2 tryb SLEEP_MODE_PWR_DOWN - program czuwanie. Aby ponownie ją uruchomić musi minąć określony czas w "jasności" (stan3) żeby wykluczyć chwilowe zmiany światła.
    Dodatkowo mam zamiar wstawić efekt symulacji ruchu płomienia przez losową modulację szerokości impulsu - program główny.
    Na razie testuję załączanie i wyłączanie po czasie - po zmianach, które opisałem wydaje się, że na razie działa prawidłowo. Ale może tylko mi się tak wydaje. Jak masz jakiś lepszy pomysł jak to osiągnąć chętnie spróbuje.
  • #8 20363163
    maciej_333
    Poziom 38  
    kdgp1491 napisał:
    Z "internetu" wyczytałem , że przed modyfikacją zmiennej, która będzie użyta w procedurze obsługi przerwania trzeba wstrzymać globalne przerwania . Nie wiem czy dobrze zrobiłem - zmieniłem kod na :
    cli();
    stan=1
    sei();

    Jest to skrajnie nieprawidłowe. Nie może to pomóc, tylko zaszkodzić. Kiedy występuje przerwanie dochodzi do zapisu PC na stosie i skok do właściwego adresu w wektorze przerwań (zależnego od źródła przerwania). Potem z tego wektora jest skok do właściwego ISR. Jednak jak dojdzie do przerwania, to już bit I w SREG (globalne zezwolenie na przerwania) jest wyzerowany. Zatem w trakcie obsługi danego przerwania inne nie mogą być obsługiwane (jest to prosty i jednopoziomowy system przerwań).

    Stąd wstawienie w ISR cli() nic nie daje. Wstawienie sei() może jednak zaszkodzić. Przecież ISR ma na końcu rozkaz RETI (powrót z przerwania). Więc wstawienie sei() odblokuje przerwania i może to doprowadzić do obsługi kolejnego przerwania przed powrotem z danego ISR. W kolejnym ISR może być ten sam problem, co w końcu doprowadzi do przeciążenia stosu.

    kdgp1491 napisał:
    Zerowanie MUSCR zatrzymało niechciane resetowanie po przerwaniu (przynajmniej tak mi się wydaje bo teraz się nie resetuje)

    Podobnie zerowanie MCUSR w tym programie nie ma sensu i nie może pomóc. Zeruje się go, jeżeli trzeba wyzerować flagi, jakie są w nim zawarte.

    Proponowałbym zrobić jak pisałem wcześniej. Wybudzać mikrokontroler co ok. 8 s resetem poprzez Watchdog. Zatem w main() musi być konfiguracja i potem odczyt MCUSR. Kolejno określa się co wywołało reset, jeśli POR lub BOR itd. trzeba wyzerować licznik (jakaś zmienna globalna - nieinicjalizowana). Z kolei w przypadku, gdy reset był od Watchdog'a należy sprawdzić komparator. Jak jest ciemno, to załączamy lampę i zmieniamy Watchdog na timer, ale bez resetu. Wtedy można odmierzać czas załączenia i jakoś tam sterować lampą. Dalej już komparator będzie obsługiwany w przerwaniu. Kolejny licznik załatwi resztę.
  • #9 20363224
    kdgp1491
    Poziom 3  
    [wyłącznie przerwania nie ma w ISR tylko w Tryby_pracy. CZy takie coś jest dozwolone?
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    dla różnych trybów przerwania z resetem lub bez - będzie ok?

    [syntax]void WDT_RESET(void) {
    wdt_reset();
    MCUSR &= ~(1<<WDRF);
    WDTCR |= (1<<WDCE) | (1<<WDE);
    WDTCR |= (1 << WDP1) | (1 << WDP2) | (1 << WDTIE ) | (1<<WDE); // przerwanie z resetem
    [/syntax]

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Cytat:
    Zatem w main() musi być konfiguracja i potem odczyt MCUSR. Kolejno określa się co wywołało reset, jeśli POR lub BOR itd. trzeba wyzerować licznik (jakaś zmienna globalna - nieinicjalizowana).

    Tego niestety nie rozumiem
  • #10 20363496
    maciej_333
    Poziom 38  
    Włączanie i wyłączanie przerwań w pętli głównej lub jakiejś funkcji, czyli poza ISR może być stosowane w przypadku krytycznego kodu. Kod krytyczny to taki, który musi się wykonać ze ściśle określonymi zależnościami czasowymi. Tym samym wystąpienie przerwania w trakcie wykonywania tego kodu zakłóciłoby te zależności. Tutaj oczywiście nie ma to sensu.

    Cytat:
    dla różnych trybów przerwania z resetem lub bez - będzie ok?


    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    }

    Kod ten nie ma zbytnio sensu, bo najpierw mamy jedną konfigurację, zaś zaraz potem drugą. Żadnych warunków tu nie ma.

    Generalnie najpierw w main() powinno być to:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Właśnie ten kod pozwoli ustalić jak konfigurować mikrokontroler - zależnie od źródła resetu. Niestety nie mam czasu tego dokładniej opisać.
  • #11 20363568
    kdgp1491
    Poziom 3  
    Cytat:
    Kod ten nie ma zbytnio sensu, bo najpierw mamy jedną konfigurację, zaś zaraz potem drugą. Żadnych warunków tu nie ma.


    To są dwie różne funkcje wywoływane w zależności od potrzeby[/quote]
  • #12 20363587
    mpier
    Poziom 29  
    Witam,
    zapomnij o tym co napisałeś i zacznij od nowa. Np. tak:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Pozdrawiam.
  • REKLAMA
  • #13 20363683
    tmf
    VIP Zasłużony dla elektroda
    A tak przy okazji - uwzględniłeś, że ten MCU ma tylko 1 kB FLASH (512 instrukcji) i 64 bajty RAM? Pokazany program kompiluje się do jak dużego kodu wynikowego?
  • #14 20378857
    kdgp1491
    Poziom 3  
    Cytat:

    Witam,
    zapomnij o tym co napisałeś i zacznij od nowa. Np. tak:
    ...
    zgaś_świeczkę();
    if (jest_ciemno()) {
    zapal_świeczkę();
    }


    Widzę, że zaczęły się bardzo "konstruktywne" odpowiedzi, następny zaproponuje "wyjdź na dwór i zapal świeczkę"

Podsumowanie tematu

Dyskusja dotyczy problemu z resetowaniem Watchdog Timera w mikrokontrolerze ATtiny13a, który ma za zadanie uruchomić program po wykryciu ciemności przez fototranzystor i wyłączyć się po określonym czasie. Użytkownik napotkał trudności w zliczaniu czasu w przerwaniu ISR(WDT_vect) z powodu nieprawidłowego użycia zmiennej czas_c. Uczestnicy dyskusji sugerowali poprawki w konfiguracji rejestru WDTCR oraz wskazali na konieczność sprawdzania źródła resetu w rejestrze MCUSR. Wskazano również na błędy w obsłudze przerwań oraz na potrzebę uproszczenia kodu. Ostatecznie użytkownik zdołał rozwiązać problem z resetowaniem, ale nadal borykał się z inkrementacją zmiennej czas_c w ISR.
Podsumowanie wygenerowane przez model językowy.
REKLAMA