Elektroda.pl
Elektroda.pl
X
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

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

stachu_15 13 Aug 2011 19:41 4598 11
Automation24.pl
  • #1
    stachu_15
    Level 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
    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
    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
    	[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.
  • Automation24.pl
  • #2
    dondu
    Moderator on vacation ...
    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ę.
  • #3
    stachu_15
    Level 11  
    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.
  • Automation24.pl
  • #5
    stachu_15
    Level 11  
    dondu wrote:
    Zrób to:

    dondu wrote:
    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ę...
  • #7
    dondu
    Moderator on vacation ...
    stachu_15 wrote:
    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.
  • #8
    stachu_15
    Level 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.
  • #9
    dondu
    Moderator on vacation ...
    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]
  • #10
    Anonymous
    Anonymous  
  • #12
    stachu_15
    Level 11  
    albertb wrote:
    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 wrote:

    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?!
    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...)
    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!