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

Problem z kompilatorem/debugerem w avr-studio.

neo73 15 Maj 2010 22:51 1281 7
  • #1 8080186
    neo73
    Poziom 10  
    Witam.
    Poniżej przedstawiam dość specyficzny problem.
    Mamy np zmienną k zadeklarowaną następująco:
    volatile int k;

    Jest ona zadeklarowana nie w głównej funkcji main tylko w funkcji bitin();
    Na początku jest ona zerowana.
    I tu zaczyna się ciekawie.
    W programowej symulacji działa wszystko ok. Natomiast gdy odpalam na rzeczywistym układzie zmienna nie jest zerowana.
    Idźmy dalej.
    Przeanalizujmy kod asemblera który odpowiada za czynność zerowania tej zmiennej:
    55:       	k = 0;
    +00000060:   821B        STD       Y+3,R1         Store indirect with displacement
    +00000061:   821A        STD       Y+2,R1         Store indirect with displacement
    


    Jak widzimy zawartość rejestru R1 (równa 0) jest zapisywana do pamięci pod adresem Y+2 i Y+3.
    Y w podanym przykładzie ma wartość wskazującą na komórki pod koniec pamięci danych (0x10F4). Niestety przy sprzętowej symulacji nie jest zmieniana wartość komórek Y+2 i Y+3.
    Co leprze region ten jest wypełniony, nie wiedząc dlaczego, wartościami 0x18 i nie można go w żaden sposób zmienić.

    Jakieś pomysły??

    Pozdrawiam.
  • #2 8080197
    _Robak_
    Poziom 33  
    Wyłącz optymalizację i pewnie wszystko będzie się wydawało ok;)
  • #3 8080310
    neo73
    Poziom 10  
    Identyczne zachowanie.
  • #4 8080472
    tmf
    VIP Zasłużony dla elektroda
    Pokaż cały kod w którym jest problem, bo na podstawie wycinka nic nie da się powiedzieć. Jedno jest raczej pewne - to ty gdzieś popełniasz błąd.
  • #5 8080650
    neo73
    Poziom 10  
    Zawartość pliku main.c:
    // OS headers
    #include <avr/eeprom.h>
    #include <avr/interrupt.h>
    #include <avr/io.h>
    #include <avr/pgmspace.h>
    #include <avr/delay.h>
    // General purpose include files
    #include "main.h"
    #include "StdDefines.h"
    
    
    volatile char jjj;
    
    extern int bitin(void);
    
    int main(void)
    {
    	PORTB = 0x00;							// Initial state is everything off
    	DDRB  = 0x04;							// Data direction register for port B
     	PORTB |= 0x80;				//podciągnięcie linii RX do 5V
    
    	while(TRUE)
    	{
    	jjj = bitin();
    
    	if(jjj == 1)
    		jjj = 222;
    	else
    		jjj = 111;
    	}
    return 0;
    }
    
    int bitin(void)
    {  					//function to read a bit
    static char oldstate;  				//oldstate retained between runs of this function
    volatile char currentstate;
    volatile int k;
    	k = 0;
    
    //	for (k=0;k<492;k++){ 		//this loop allows 838 us to go by.  If no state change, bit is 1
    	while (k<492)
    	{
    		_delay_us(1); 		 	// move to halfway thru the next bit
    		currentstate = PINB;
    		currentstate &= 0x80;
       		if (currentstate != oldstate)//if state has changed
    		{    	
    			oldstate = currentstate;  	//update oldstate
                            PORTB ^= 4;
    			_delay_us(430); 		 	// move to halfway thru the next bit
                            PORTB ^= 4;
    			_delay_us(100); 		 	// move to halfway thru the next bit
    			return 0;  			//return 0 if state changed
      		}//end of if
       		k++;
    	}//end of for
    	return 1;  					//return 1 if state did not change
    }//end of bitin()
    


    Zawartość pliku nagłówkowego main.h:
    #define 	_UTIL_DELAY_H_   1
    #define 	F_CPU   14745600UL
    #define	RXSIZE	(50)
    


    Zawartość pliku StdDefines.h:
    #	define	TRUE			(1)
    #	define	FALSE			(0)
    
    #	define	ON				(1)
    #	define	OFF				(0)
    
    #	define	HI				(1)
    #	define	LO				(0)
    
    #	define	PASS			(1)
    #	define	FAIL			(0)
    
    #	define	OK				(1)
    #	define	ERROR			(0)
    



    Dodam że kod jest kompilowany pod Atmega64.
    Programowa symulacja przebiega prawidłowo.
    Na rzeczywistym procku - kicha.
  • #6 8080832
    zumek
    Poziom 39  
    neo73 napisał:
    ...Jakieś pomysły??

    Pozdrawiam.


    Mój pomysł to M103C=1.

    Pozdrawiam.
  • #7 8080847
    mirekk36
    Poziom 42  
    To nie żaden problem z kompilatorem/debugerem tylko niestety problem ze zrozumieniem co i jak powinno działać.

    Po pierwsze nie wyłączaj żadnej optymalizacji bo po co??? Chyba, że tobisz ten program, żeby działał po wsze czasy tylko w symulatorze to wtedy możesz się bawić w takie tam.

    Analizując od góry, po co ci:

    volatile char jjj; 


    tzn po co to volatile ???

    extern int bitin(void); 


    co to za deklaracja funkcji ??? bez tego extern wystarczy, extern stosuje się do zmiennych jeśli są zadeklarowane w innych plikach nagłówkowych.

    Oczywiście jeśli jjj będzie zadeklarowana bez volatile jak wyżej mówiłem to oczywistym jest też, że kompilator przy normalnej włączonej optymalizacji -Os wyrzuci taki zbędny fragment:

       if(jjj == 1) 
          jjj = 222; 
       else 
          jjj = 111; 


    bo jest on nikomu i do nieczego nie potrzebny ;) - jak sobie z tym poradzić - za chwilę

    Po kiszeczkę zdeklarowałeś te zmienne automatyczne w funkcji jako volatile???? to już jest lekki dziwoląg

    volatile char currentstate; 
    volatile int k; 


    nie mówiąc już o tym, że skoro zmienna k nie przyjmuje wartości ujemnych to można by było dla porządku użyć dla niej typu uint16_t, a z kolei dla porządku dla zmiennej currentstate typu uint8_t (to oczywiście znowu nie przeszkadza w programie - ale chyba warto, żeby on bardziej przejrzyście wyglądał)

    Poza tym po co funkcja zwraca typ int skoro wartość wyjściowa przyjmuje tylko 0 lub 1. Znowu wystarczyłby typ uint8_t ..... a już po takich zmianach lepiej by ci się analizowała kod w asemblerze.

    A na koniec taka refeleksja. Jeśli do analizowania tak prostych programów w AVRkach, ktoś musi już korzystać z super debugerów, symulatorów itp to wg mnie lekka masakra. Czyż nie lepiej poradzić sobie w czystym żywym procku i sprawdzić to wstawiając własne proste jak drut pułapki programowe typu choćby dioda LED, wyświetlacz LCD, czy jakiś OUTPUT na RS232 w ostateczności??? Ale tu zwykła dioda LED by wystarczyła.

    Wystarczy po tym fragmencie:

       if(jjj == 1) 
          jjj = 222; 
       else 
          jjj = 111; 


    użyć w jakimś prostym warunku IF zapalania bądź gaszenia dowolnej diody LED podłączonej do jakiegoś dowolnego pinu, np:

    if(jjj==222) PORTB |= (1<<PB7);
    else PORTB &= ~(1<<PB7);


    co oczywiście można by było zastąpić:

    if( bitin() ) PORTB |= (1<<PB7);
    else PORTB &= ~(1<<PB7);


    już ten mały dodatek spowoduje, że kompilator pomimo optymalizacji -Os nie pominie tego fragmentu z tym twoim dosyć dziwnym warunkiem (if(jjj==1) bo zmienna jjj będzie z kolei potrzebna do warunku w którym zapalana bądź gaszona jest dioda LED. Obserując diodę będziesz widział czy zmieniał się stan na pinie w tej twojej funkcji czy nie.

    Daj też sobie spokój z takimi dywagacjami

    neo73 napisał:

    Jak widzimy zawartość rejestru R1 (równa 0) jest zapisywana do pamięci pod adresem Y+2 i Y+3.
    Y w podanym przykładzie ma wartość wskazującą na komórki pod koniec pamięci danych (0x10F4). Niestety przy sprzętowej symulacji nie jest zmieniana wartość komórek Y+2 i Y+3.
    Co leprze region ten jest wypełniony, nie wiedząc dlaczego, wartościami 0x18 i nie można go w żaden sposób zmienić.

    Jakieś pomysły??


    ponieważ zmienne, które tworzysz w funkcji (poza tą static) są tworzone dynamicznie i na stosie a nie w obszarze pamięci danych - zmiennych globalnych, dlatego wszystko ci się miesza.

    Wywal te volatile przy tych zmiennych, daj prostsze typy tak jak pisałem i wtedy popatrz na asembler - gdzie zmienna k będzie wrzucona do jednego z rejestrów i będzie ładnie w nim wyzerowana a potem porównywana do wartości z while(k<xxx) oczywiście będzie w jednym rejestrze jeśli sobie dla uproszczenia i chęci lepszego zaobserwowania co się dzieje w asemblerze - zdeklarujesz ją jako uint8_t i dasz tam wartość 255 zamiast 492 tak dla testu. Później jak załapiesz dasz już typ uint16_t i wartość 492 - wtedy w asemblrze zobaczysz, że wartość zmiennej k będzie przechowywana w 2 rejestrach.

    Dzięki temu że bez volatile będzie to działało szybciej - bo operacje porównywania i zwiększania zmiennej k++ będą odbywały się na rejestrach a nie w pamięci komórek na stosie.
  • #8 8082724
    neo73
    Poziom 10  
    mirekk36 - oczywiście masz racje we wszystkim co piszesz.
    specyfikator volatile został tylko dodany aby zobaczyć bez optymalizacji jak te zmienne się zachowują.
    Nie ma to nic wspólnego z docelowym programem. To wynik testów.
    Tak samo jak
    if(jjj == 1)
          jjj = 222;
       else
          jjj = 111; 


    został dodany tylko dlatego żeby kompilator na drodze optymalizacji nie wywalił mi tego fragmentu. Oczywiście ani to ani specyfikator volatile nie jest tutaj potrzebny.
    Tak samo jak w przypadku k, czy currentstate.
    Co do zwracanych wartości przez funkcję bitin(), to oczywiście wystarczy tylko typ uint16_t.

    Tak jak pisałem, ten kod był tylko dla zdiagnozowania błędu który okazał się w hardware.

    Okazało się że fabrycznie Atmega64 ma ustawiony fusebit odnośnie kompatybilności z ATmega103. Przy tym ustawieniu pamięć danych ma pojemność nie 4096 a 4000.
    AVR studio tego nie uwzględnia i traktuje pojemność pamięci jako 4096.
    Debuger skakał do pamięci która nie istniała fizycznie.
    Po wyłączeniu tego fusebita wszystko zaczęło działać tak jak powinno.

    Serdecznie dziękuje za uwagi

    Pozdrawiam.
    Marcin.
REKLAMA