Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[atmega8][c] Watchdog - procesor się restartuje zamiast zgłosić przerwanie

MiLupo 16 Lut 2012 09:41 3790 13
  • #1 16 Lut 2012 09:41
    MiLupo
    Poziom 12  

    Witam,
    Zaczynam właśnie zabawę z procesorami atmega i mam problem z watchdog'iem na atmega8. Pod atmega328 działa to super ale tam jest troszkę inaczej (jest WDIE, którego w atmega8 nie ma)

    Skorzystałem z przykładowego kodu z tej strony http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/

    Próbowałem jeszcze z wdt_enable(WTDO_2s) ale niestety procesor się restartuje zamiast wchodzić w stan uśpienia :(

    Czy pomógł by mi ktoś przerobić to tak aby działało na atmega8? Chodzi o to aby co 2 sek zmieniała się flaga f_wdt i raz odpaliła się pętla loop (na jej końcu znowu jest uśpienie procesora)

    Code:

    #include <avr/wdt.h>
    #include <avr/sleep.h>
    #ifndef cbi
    #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
    #endif
    #ifndef sbi
    #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
    #endif

    volatile boolean f_wdt=1;

    void setup(){
      /* ATMEGA8 MCUCR = ATMEGA328 SMCR */
      cbi( SMCR,SE );      // sleep enable, power down mode
      cbi( SMCR,SM0 );     // power down mode
      sbi( SMCR,SM1 );     // power down mode
      cbi( SMCR,SM2 );     // power down mode
      setup_watchdog(7);  // 2s
    }
    void loop(){
      _wdtDisable();
      if (f_wdt==1)
      {
        f_wdt=0;       // reset flag
        /** jakis kod do wykonania **/
    }
      system_sleep();
    }

    /******************************** watchdog **/
    ISR(WDT_vect) {
      f_wdt=1;
    }

    //****************************************************************
    // 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
    // 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
    void setup_watchdog(int ii) {
      byte bb;
      int ww;
      if (ii > 9 ) ii=9;
      bb=ii & 7;
      if (ii > 7) bb|= (1<<5);
      bb|= (1<<WDCE);
      ww=bb;
      MCUSR &= ~(1<<WDRF);
      /* ATMEGA8 WDTCR = ATMEGA328 WDTCSR */
      WDTCSR |= (1<<WDCE) | (1<<WDE);
      WDTCSR = bb;
      WDTCSR |= _BV(WDIE); /* ! Niestety WDIE nie ma w atmega8 */
    }
    void system_sleep() {
      cbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter OFF
      set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
      sleep_enable();
      sleep_mode();                        // System sleeps here
      sleep_disable();                     // System continues execution here when watchdog timed out
      sbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter ON);                   
    }

    0 13
  • #2 16 Lut 2012 10:41
    tmf
    Moderator Mikrokontrolery Projektowanie

    Po pierwsze zmień kurs z którego się uczysz. Te wszystkie cbi/sbi to prehistoria i nie wiem po co autor próbuje to reanimować.
    Druga sprawa - WD służy właśnie do resetowania procesora. Tylko w niektórych mikrokontrolerach AVR ma rozbudowane funkcje i może być użyty jako np. timer, czy generować przerwania. Poczytaj w nocie do M8 jakie w tym kontrolerze ma możliwości.
    Trzecia sprawa - M8 to już też prehistoria, dla własnego dobra zapomnij o tym procesorze.

    0
  • #3 16 Lut 2012 11:31
    MiLupo
    Poziom 12  

    I nie ma możliwości żeby w atmega8 watchdog nie wymuszał restartu? A jakiś timer lub coś podobnego?

    Zdaje sobie sprawę, że m8 to już prehistoria ale dla mnie by w zupełności wystarczył (jeśli tylko była by możliwość uśpienia i "wzbudzenia" po np. 2 sek.). Programik zajmuje dosłownie 5kB i wykorzystuję góra 3-4 piny z portu. Dodatkowo koszt jest śmieszny bo poniżej 5 zł a za m128 (na którym działa ten programik z watchdogiem) kosztuje już 2x więcej. Planuję zrobienie 10 takich urządzeń więc cena procka ma znaczenie.

    0
  • #5 16 Lut 2012 12:47
    MiLupo
    Poziom 12  

    No właśnie też teraz znalazłem podobny problem na forum i też polecano 88. Tam jest to tak samo rozwiązane jak w 328 więc kod powinien działać bez problemu. Sprawdzałem na 328 i działa super.

    0
  • #6 16 Lut 2012 12:54
    tmf
    Moderator Mikrokontrolery Projektowanie

    Ale WD nie służy do usypiania i wybudzania procesora! To, że w nowszych AVR dodano mu różne dziwne funkcjonalności to nie znaczy, że inaczej się nie da. Wybudzić procesor może np. przerwanie timera po dowolnym czasie - warunek, że timer w danym trybie uśpienia ciągle działa. Więc i na M8 da się to zrobić. Natomiast pisząc o M8, że to prehistoria nie miałem na myśli zastąpienia go M128, lecz właśnie M88 lub innymi AVRami z nowszym rdzeniem.

    0
  • #8 16 Lut 2012 13:40
    tmf
    Moderator Mikrokontrolery Projektowanie

    Zapewne, ale bez złośliwości, nie sądzę aby autorowi przy jego obecnej wiedzy robiło różnicę w jakim trybie uśpienia będzie procesor. Te ileśtam uA pewnie i tak traci na błędnym projekcie elektronicznym, czy też błędnej konfiguracji procesora. Pomijając już to, że śmiesznie byłoby walczyć o te kilka uA w M8, gdzie w PicoPower na dzień dobry i bez wysiłku zyskamy 2-10 razy tyle.

    0
  • #10 16 Lut 2012 15:08
    MiLupo
    Poziom 12  

    Projekt jest banalny i nie ma w nim praktycznie żadnej elektroniki oprócz procka. Program ma 1 raz przelecieć pętlę loop w której sprawdzany jest stan na jednym z pinów, wzbudzić radio, przesłać info o stanie pinu, uśpić radio i przejść w stan uśpienia. Cykl ma się powtarzać co 2min.
    Żeby rzadziej korzystać z radia używam jeszcze dodatkowej pętli w której zliczam wybudzenia i np. jeśli licznik przekroczy 10 (~20min) to go resetuję i dopiero wtedy wysyłam przez radio.
    Na 328 chodzi to bez problemu i wszystko się ładnie usypia. Gorzej z tym m8.
    Taki tryb uśpienia wybrałem ponieważ z tego co doczytałem jest najbardziej energooszczędny i zadziałał na 328 za pierwszym razem więc nie kombinowałem z innymi.

    0
  • #11 16 Lut 2012 15:25
    tmf
    Moderator Mikrokontrolery Projektowanie

    Czyli jeśli masz zewnętrzny przycisk to nie ma problemu - konfigurujesz go tak, aby generował przerwanie i już. Nie musisz wybudzać procesor, nic nie musisz sprawdzać. Ktoś wciśnie przycisk -> procesor się wybudza i robi co chcesz. Jeśli energia jest problemem to M88 zużywa jej na dzień dobry 2xmniej przy normalnej pracy.

    0
  • #12 16 Lut 2012 16:03
    MiLupo
    Poziom 12  

    No z tym "zwieraczem" to nie jest do końca tak, ponieważ nie wiem czy przez tydzień nikt go nie "zwarł"... a może coś się uszkodziło, padły baterie albo jeszcze coś innego. A tak np raz na pół godziny wysyłam prosty sygnał w stylu keepalive i wiem że urządzenie i transmisja działa. Po drugie w odpowiedzi na sygnał keepalive mogę przekazywać rozkazy sterujące żeby np. od teraz nie zgłaszał się co 30 min tylko co godzinę. Taki watchdog jest mi więc raczej niezbędny.

    0
  • #14 16 Lut 2012 21:42
    MiLupo
    Poziom 12  

    Zakupiłem właśnie atmega88, zmieniłem kod,wgrałem i działa dokładnie tak jak chciałem :)

    Zmieniony kod:

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

    void setup(){
      delay(100);
      wdt_enable(WDTO_2S);
       _WD_CONTROL_REG = _BV(WDIE); /* generate watchdog interrupts */
       sei(); /* enable interrupts */
      pinMode(7, OUTPUT);
      pinMode(8, OUTPUT);
      digitalWrite(7,LOW);
      digitalWrite(8,LOW);
    }
    int counter=1;
    volatile boolean f_wdt=1;
    void loop(){
      set_sleep_mode(SLEEP_MODE_PWR_DOWN);
      sleep_mode();
      if(f_wdt==1)
      {
        f_wdt=0;
        counter++;
        digitalWrite(8,HIGH);
          delay(100);
          digitalWrite(8,LOW);
          delay(100);
        if(counter>3)
        {
          digitalWrite(7,HIGH);
          delay(100);
          digitalWrite(7,LOW);
          delay(100);
          digitalWrite(7,HIGH);
          delay(100);
          digitalWrite(7,LOW);
          delay(100);
          counter=0;
        }
      }
     
    }

    ISR(WDT_vect, ISR_NAKED){
            f_wdt=1;
       _WD_CONTROL_REG = _BV(WDIE);
       reti();
    }

    0