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

[Atmega32+Atmega8][DS18B20][C]Reset AVR po odczycie z DS18B20

stachu_15 13 Sie 2011 19:41 4226 11
  • #1 13 Sie 2011 19:41
    stachu_15
    Poziom 11  

    Witam, mam dziwny problem, pomoże mi ktoś go rozwiązać?
    Chodzi o to, że buduję urządzenie, które na żądanie będzie mierzyło temperaturę układem DS18B20. Żądanie odbywa się poprzez naciśnięcie odpowiedniego przycisku na pilocie RC5. Całe moje urządzenie to dwa AVR:
    Atmega8 (jako dekoder RC5, który wysyła odebrany adres i komendę poprzez USART do drugiego uC)
    Atmega32 (jako właściwy procesor) - tu odbywa się odbiór USART, sprawdzenie warunków na komendę (funkcja switch).
    Do Atmegi32 mam też podłączone diody, które się włączają po wciśnięciu odpowiednich klawiszy, oraz fotorezystor w układzie dzielnika napięcia, po wciśnięciu klawisza na pilocie istnieje możliwość pomiaru napięcia (ADC) na tym czujniku oświetlenia. Te elementy działają bezproblemowo. Jednak podczas żądania pomiaru temperatury dzieją się dziwne rzeczy.
    Urządzenie mierzy temperaturę, wysyła ją przez UART (FT232 > USB) do komputera, po czym się resetuje. Nie zaobserwowałem takiego zjawiska podczas ciągłego pomiaru w nieskończonej pętli main()... while(1)...
    Niezależnie jakie dam opóźnienie po odczycie temperatury, nawet 10s, to po tym czasie procesorek się resetuje. Wiem to, ponieważ po resecie wysyłane jest powitanie na UART. Podłączony mam też na przerwaniu INT0 zegarek DS1307. On działa dobrze. A dlaczego poruszam sprawę jego istnienia - bo zauważyłem, że Atmega nie resetuje się całkiem. Po resecie w odstępie ok. 100ms rysują się na LCD (HD44780) krateczki - taki jakby pasek postępu. Zauważyłem, że po tym śmiesznym resecie po pomiarze temperatury układ przerwanie INT0 dalej jest aktywne bo zegarek je inicjuje - co przerywa proces rysowania ładowania paska. Dlaczego ten program się tak dziwnie zachowuje, że w zależności gdzie i kiedy wywołam funkcję odczytu temperatury z DS18B20 to albo układ się resetuje albo nie. Poniżej zamieszczam parę funkcji. Funkcje do obsługi czujnika DS18B20 zapożyczyłem z XYZ Hobby Robot - kurs AVR
    gettemp - funkcja odczytu temperatury

    Code:
    void gettemp(void)
    
    {
       double temperature = 0;
       //unsigned char ds18b20_pad[9];
       uint8_t   ds18b20_pad[9];
       if(ds18b20_ConvertT())
        {
          _delay_ms(750);
             
          ds18b20_Read(ds18b20_pad);     
          temperature = ((ds18b20_pad[1] << 8) + ds18b20_pad[0]) / 16.0 ;
          dtostrf(temperature,3,2,temperaturenapis);
         USART_Write("POMIAR TEMPERATURY\r\n");
       }
    }

    funkcja przerwania od odbioru USART oraz funkcja rozp - rozpoznająca otrzymany komunikat




    Code:
    ISR(USART_RXC_vect)
    
    {
       //gettemp();
       cli();
       i++;
       odbior[i] = USART_Receive();
       if (odbior[i] == 13)
       {            
          rozp(odbior);
          i = 0;
       }
       sei();
    }


    void rozp(unsigned char* odbior)
    {
       char tekst[32];
       adres = ((odbior[2]-48)*10+odbior[3]-48);
       komenda = ((odbior[6]-48)*10+odbior[7]-48);
       USART_Write("--> ");
        for (j = 1; j <= i; j ++) USART_Transmit(odbior[j]);
       sprintf(tekst,"Adres: %d, Komenda: %d\r", adres, komenda);
       USART_Write(tekst);      
       if (adres == 0)
       {
          switch (komenda)
          {
             case 1:
                PORTC ^= 1 << PC7;
                USART_Write("Zmieniono wyjście 1\r\n");
                break;
             case 2:
                PORTC ^= 1 << PC6;
                USART_Write("Zmieniono wyjście 2\r\n");
                break;
             case 3:
                PORTC ^= 1 << PC5;
                USART_Write("Zmieniono wyjście 3\r\n");
                break;
             case 4:
                PORTC ^= 1 << PC4;
                USART_Write("Zmieniono wyjście 4\r\n");
                break;
             case 5:
                PORTC ^= 1 << PC3;
                USART_Write("Zmieniono wyjście 5\r\n");
                break;
             case 6:
                PORTC ^= 1 << PC2;
                USART_Write("Zmieniono wyjście 6\r\n");
                break;
              case 11:
                 USART_Write("Resetowanie AVR...\r\n");
                 _delay_ms(10);
                 Reset_AVR();
                 break;
             case 54:
                ac_read();
                USART_Write("Aktualne oświetlenie: ");
                USART_Write(ac);
                USART_Write("V\r\n");
                break;
             case 60:
                gettemp();
                USART_Write("Aktualna temperatura: ");
                USART_Write(temperaturenapis);
                USART_Write("°C\r\n");
                break;
             case 63:
                gettemp();
                _delay_ms(10000);
                break;
             default:
                USART_Write("Komenda nierozpoznana! \r\n");
          }
       } else USART_Write("Adres nierozpoznany! \r\n");
    }


    A oto co dostaję na komputerze w terminalu
    Code:
       [RESET]
    
    --> A00,K01
    Adres: 0, Komenda: 1
    Zmieniono wyjście 1
    --> A00,K60
    Adres: 0, Komenda: 60
    POMIAR TEMPERATURY
    Aktualna temperatura: 25.06°C
       [RESET]

    Początkowo wciśnięto klawisz 1, by zaświecić jedną z diodek LED, później klawisz pomiary temperatury.

    0 11
  • Mitronik
  • #2 13 Sie 2011 21:59
    dondu
    Moderator Mikrokontrolery Projektowanie

    Coś mam wrażenie że Atmega wcale się nie resetuje. Aby to stwierdzić wykorzystaj rejestr MCUCSR.
    Dopiero wtedy można być pewnym, że reset następuje oraz określić który to przypadek.

    Poza tym, nie pokazałeś funkcji main().

    Osobna sprawa to schemat - przydałby się.

    0
  • #3 14 Sie 2011 12:32
    stachu_15
    Poziom 11  

    Code:
    int main(void)
    
    {
       JTAG_off();
       USART_Init();
       LCD_Initalize();LCD_GoTo(1,2);LCD_WriteData(0xFF);_delay_ms(100);
       I2CInit();LCD_WriteData(0xFF);_delay_ms(100);
       DS1307_Init();LCD_WriteData(0xFF);_delay_ms(100);
       ac_init();LCD_WriteData(0xFF);_delay_ms(100);
       
       PORTB |= (1 << PB2);LCD_WriteData(0xFF);_delay_ms(100); //pull-up INT2
       
       PORTD |= (1 << PD3);LCD_WriteData(0xFF);_delay_ms(100); //pull-up INT1
       
       PORTD |= (1 << PD2);LCD_WriteData(0xFF);_delay_ms(100); //pull-up INT0
       LCD_WriteData(0xFF);_delay_ms(100);
       LCD_WriteData(0xFF);_delay_ms(100);
       LCD_WriteData(0xFF);_delay_ms(100);      
       LCD_WriteData(0xFF);_delay_ms(100);
       LCD_WriteData(0xFF);_delay_ms(100);
       LCD_WriteData(0xFF);_delay_ms(100);
       LCD_WriteData(0xFF);_delay_ms(100);
       LCD_WriteData(0xFF);_delay_ms(100);
       LCD_WriteData(0xFF);_delay_ms(100);
       LCD_WriteData(0xFF);_delay_ms(100);
       LCD_WriteData(0xFF);_delay_ms(100);
       GICR |= (1 << INT1)/* | (1 << INT2)*/;LCD_WriteData(0xFF);_delay_ms(100);// | (1 << INT0);
       MCUCR |= (1 << ISC11);LCD_WriteData(0xFF);_delay_ms(100);
       DDRC |= (1 << PC2)|(1 << PC3)|(1 << PC4)|(1 << PC5)|(1 << PC6)|(1 << PC7);LCD_WriteData(0xFF);_delay_ms(100);

       sei();
       while(1)
       {

       }
    }

    Tak wygląda main() - nic specjalnego. Ze schematem będzie cieżko, bo to wszystko jest na uniwersalu, aczkolwiek część na gotowej wykonanej przeze mnie płytce testowej. Jeśli chodzi o rezystory pullupowe to pamiętam o nich. Wszystko zgodne z dokumentacją.
    Byłbym zapomniał - funkcja USART_Init(); na koniec wysyła rzekome "[RESET]" - po tym resecie po żądaniu pomiaru temperatury obserwuję "zamazanie/nadpisanie" wyświetlania godziny i daty a zastąpienie tego właśnie tymi kwadracikami, w terminalu na kompie mam napis reset. Jak będę miał chwilę nagram filmik pokazujący jak zachowuje się układ.

    0
  • Mitronik
  • #4 14 Sie 2011 13:34
    dondu
    Moderator Mikrokontrolery Projektowanie

    Zrób to:

    dondu napisał:
    Coś mam wrażenie że Atmega wcale się nie resetuje. Aby to stwierdzić wykorzystaj rejestr MCUCSR.

    0
  • #5 14 Sie 2011 16:37
    stachu_15
    Poziom 11  

    dondu napisał:
    Zrób to:

    dondu napisał:
    Coś mam wrażenie że Atmega wcale się nie resetuje. Aby to stwierdzić wykorzystaj rejestr MCUCSR.

    Co ma się przez to rozumieć? Jak mam to sprawdzać i w jakim momencie? W tej chwili zapis bitowy tego rejestru wygląda następująco 0b10000011. Nie bardzo wiem na co konkretnie mam zwrócić uwagę...

    0
  • #6 14 Sie 2011 16:40
    xury
    Poziom 39  

    Dzięki temu rejestrowi możesz sprawdzić co wywołało reset.

    0
  • #7 14 Sie 2011 16:46
    dondu
    Moderator Mikrokontrolery Projektowanie

    stachu_15 napisał:
    Co ma się przez to rozumieć? Jak mam to sprawdzać i w jakim momencie? W tej chwili zapis bitowy tego rejestru wygląda następująco 0b10000011. Nie bardzo wiem na co konkretnie mam zwrócić uwagę...


    Datasheet :)

    [Atmega32+Atmega8][DS18B20][C]Reset AVR po odczycie z DS18B20

    Jak to wykorzystać?
    1. W pierwszej linii kodu funkcji main() odczytujesz zawartość tego rejestru i zapisujesz do zmiennej, a później
    2. następnie od razu zerujesz dolne 4 bity.
    3. analizujesz powód resetu w oparciu o dane ze zmiennej i odpowiednio reagujesz

    W ten sposób, będziesz miał pewność, że reset wystąpił i z jakiego powodu.

    0
  • #8 14 Sie 2011 20:42
    stachu_15
    Poziom 11  

    OK sprawdziłem, po tym dziwnym resecie... odczytuję wartość 195 (0b11000011), po normalnym 3 (0b00000011). Bit 6 (ISC2) odpowiada za ... ? no właśnie bo nie czaję tego... :/ Ja w ogóle nie używam INT2... zatem dlaczego jeszcze reset to uruchamia:/ Bit 7 (JTD) sam ustawiam chwilę później - aby mieć możliwość z korzystania z pinów, które normalnie są używane przez JTAGa.

    0
  • #9 14 Sie 2011 20:45
    dondu
    Moderator Mikrokontrolery Projektowanie

    Pokaż ten main() w którym kod sprawdzania MCUCSR wpisałeś.

    Przyglądnij screenowi z tabelką poniżej dla Atmega32:

    [description_area=916,658:3067681200_1313348352.gif][description=38,471,389,96,152266712,12]To Ciebie interesuje[/description][/description_area]

    0
  • #10 14 Sie 2011 20:54
    94075
    Użytkownik usunął konto  
  • #11 14 Sie 2011 21:05
    dondu
    Moderator Mikrokontrolery Projektowanie

    Pokaż ten main() w którym kod sprawdzania MCUCSR wpisałeś.
    Pokaż schemat w szczególności ten fragment z ATmega32.

    0
  • #12 14 Sie 2011 21:45
    stachu_15
    Poziom 11  

    albertb napisał:
    Jak przewalczysz temat będziesz wiedział, czemu wszyscy piszą, że przerwania mają być krótkie ;-)

    Tak tylko jakieś pomysły jak to rozwiązać w inny sposób?
    Wpadłem na pomysł aby zapalać jakąś flagę... np. odebrano coś na uart > zapal flagę... w main()...while(1)... sprawdź co to i zgaś flagę ?! czy to tak będzie działać?
    albertb napisał:

    Jeśli odbiornik zgubi LF to zmienna i może urosnąć do większej wartości niż przewidywana. Wtedy piszesz poza pamięcia zmiennej odbiór.

    Akurat to mi się też wydaje najbardziej prawdopodobne... świadczyłby o tym fakt, że wywołując żądanie pomiaru temperatury w innym miejscu programu nie obserwuję dziwnego resetu...
    main() wrzucę za chwilę, jeszcze coś testuję

    ...
    Oto main(); - nie mam pojęcia czy dobrze odczytuję ten MCUCSR?!
    Code:
    int reset_source = 0;
    
    int main(void)
    {
       reset_source = MCUCSR;
       MCUCSR |= 11100000;
       JTAG_off();
       USART_Init();
       sprintf(tekst, "\n=====>%d\r\n\n", reset_source);
       USART_Write(tekst);
       
       LCD_Initalize();LCD_GoTo(1,2);LCD_WriteData(0xFF);_delay_ms(100);
       I2CInit();LCD_WriteData(0xFF);_delay_ms(100);
       DS1307_Init();LCD_WriteData(0xFF);_delay_ms(100);
       ac_init();LCD_WriteData(0xFF);_delay_ms(100);
       //PORTB |= (1 << PB2);LCD_WriteData(0xFF);_delay_ms(100); //pull-up INT2
       PORTD |= (1 << PD3);LCD_WriteData(0xFF);_delay_ms(100); //pull-up INT1
       PORTD |= (1 << PD2);LCD_WriteData(0xFF);_delay_ms(100); //pull-up INT0
       LCD_WriteData(0xFF);_delay_ms(100);
       LCD_WriteData(0xFF);_delay_ms(100);
       LCD_WriteData(0xFF);_delay_ms(100);      
       LCD_WriteData(0xFF);_delay_ms(100);
       LCD_WriteData(0xFF);_delay_ms(100);
       LCD_WriteData(0xFF);_delay_ms(100);
       LCD_WriteData(0xFF);_delay_ms(100);
       LCD_WriteData(0xFF);_delay_ms(100);
       LCD_WriteData(0xFF);_delay_ms(100);
       LCD_WriteData(0xFF);_delay_ms(100);
       LCD_WriteData(0xFF);_delay_ms(100);
       GICR |= (1 << INT1)/* | (1 << INT2)*/;LCD_WriteData(0xFF);_delay_ms(100);// | (1 << INT0);
       MCUCR |= (1 << ISC11);LCD_WriteData(0xFF);_delay_ms(100);
       DDRC |= (1 << PC2)|(1 << PC3)|(1 << PC4)|(1 << PC5)|(1 << PC6)|(1 << PC7);LCD_WriteData(0xFF);_delay_ms(100);
       sei();
       while(1)
       {
          
       }
    }



    EDIT..

    Ok dotarłem... teraz poprawnie resetowałem MCUCSR (tzn czyściłem o odczycie...)
    Code:
    int main(void)
    
    {
       int reset_source = 0;
       reset_source = MCUCSR;
       MCUCSR &= ~_BV(0) & ~_BV(1) & ~_BV(2) & ~_BV(3) & ~_BV(4);
       JTAG_off();
       USART_Init();
       sprintf(tekst, "\n=====>%d\r\n\n", reset_source);
       USART_Write(tekst);
    ...

    Otrzymałem wynik następujący... normalny reset "przyciskiem" zwraca 2, ten dziwny reset zwraca 128... czyli zapalony bit wyłączenia JTAGa, ale brak źródła resetu... zatem muszę pokombinować z tą nadpisywaną pamięcią adresową... Jak dotrę do jakiegoś rozwiązania to się odezwę, jeśli napotkam kolejne problemy też się odezwę :-)
    Póki co dzięki wielkie!

    0