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

cykliczny odczyt komórki eepromu

kli 11 Gru 2008 09:50 3462 35
  • #1 5844881
    kli
    Poziom 13  
    Mam mniej więcej taki program(nie wklejam całego kodu bo jest dość długi a problem chyba tkwi tutaj):

    
    
    ...
    unsigned char EEPROM_read(unsigned char Address) 
    { 
    //while(EECR & (1<<EEWE)); 
    EEAR = Address; 
    EECR |= (1<<EERE); 
    return EEDR; 
    }
    
    ...
    
    while(1)
        {	
    
            if (k == EEPROM_read(0x00) ) k=0;
            ...
            delayms(500);
            k++;
            ...
        }
    


    W eepromie pod adresem 0x00 zapisana jest liczba 0x0B. Niestety, program nie działa jak powinien, restartuje się pętla lub ciągle siedzi w jednej iteracji, trudno stwierdzić.
    No i pytanie moje jest takie, czy może nie można odczytywać tego samego adresu eeprom w pętli(mimo że następuje to aż co 0,5 sekundy)?
    A może potrzebna jest wcześniej jakaś"inicjalizacja" eepromu?
    W nocie nie znalazłem takich informacji.
  • Pomocny post
    #2 5845031
    chudybyk
    Poziom 31  
    Moim zdaniem powinno działać.
    Jak zadeklarowana jest zmienna k? Spróbuj dodać do deklaracji atrybut "volatile".
  • Pomocny post
    #4 5845329
    chudybyk
    Poziom 31  
    Odczyt eepromu może się nie powieść tylko w przypadku, gdy nie zakończyła się poprzednia operacja zapisu. Wtedy wystarczyłoby Ci odblokować warunek, który masz skomentowany. Jeśli jesteś pewny, że nic nie zapisujesz do eepromu, to cudów nie ma - gdzieś indziej tkwi błąd. Może coś innego modyfikuje Ci zmienną k, może pętla czasowa jest źle policzona (czy taktujesz procek tak jak w ustawieniach przy kompilacji?)
    Jaki dokładnie jest objaw złego działania?
  • #5 5846934
    kli
    Poziom 13  
    Nic nie zapisuje do eepromu, tylko odczytuje dlatego zakomentowalem
    
    //while(EECR & (1<<EEWE));
    

    Zmienna k nie jest modyfikowana nigdzie indziej,
    Wstawienie:
     
    volatile unsigned char k; 
    

    niestety nie pomoglo.

    Wczesniej, jak mialem to napisane tak:
    
    while(1) 
        {    
    
            if(k == pgm_read_byte_near(PROGRAM_TABLE[0]) k=0;
            ... 
            delayms(500);
            PORTA ^= (1<<PA3); //dioda
            k++; 
            ... 
        }
    
    

    to wszystko bylo ok. , dioda migala, zaczelo się sypać po zapisaniu tablicy PROGRAM_TABLE w eepromie i wstawieniu linijki z odczytem zerowego adresu eepromu
    
    if (k == EEPROM_read(0x00) ) k=0;
    


    A objawy złego działania są takie, że dioda nie miga, czyli jakby zawieszała się główna pętla programu, bo obsługa przerwań
    (timer1, INT0) nadal działa.

    Dodano po 2 [godziny] 22 [minuty]:

    W ogole zauważyłem, że taki kod działa:
    
    //k =0x0B;
    if(EEPROM_read(0x00) == 0x0B)  PORTA |= (1<<PA3);
    else PORTA &= ~(1<<PA3);
    


    Natomiast taki już powoduje zawieszenie się programu:
    
    k = 0x0B;
    if(EEPROM_read(0x00) == k)  PORTA |= (1<<PA3);
    else PORTA &= ~(1<<PA3);
    

    Tak jakby nie można było przyrównywać zawartości komórki eepromu do zmiennej?
  • Pomocny post
    #6 5847826
    BoskiDialer
    Poziom 34  
    Pierwsze moje wrażenie po przeczytaniu pierwszego postu, to odczyt jest zbyt szybki: po ustawieniu EERE wstawił bym jednego lub profilaktycznie dwa nop'y
    asm volatile("nop"::);
    .
    Jeśli to nie to, to spróbuj zamienić funkcję odczytu komórki na zwracanie stałej, żeby się przekonać w którym miejscu jest problem: przy odczycie, czy dalej.
  • #7 5847947
    kli
    Poziom 13  
    Pokombinowałem troche z tymi "nopami" i okazało się że kod działa w takim przypadku:

    
    k = 0x0B;
    asm volatile("nop"::);
    if(EEPROM_read(0x00) == k) PORTA |= (1<<PA3);
    else PORTA &= ~(1<<PA3);
    


    Nie mam pojęcia czemu tak jest o_0.

    /------------------------------------------------------------------------

    Niestety po następujących modyfikacjach, program dalej się zawiesza:

    
    ...
    volatile unsigneg char k = 0;
    ...
    unsigned char EEPROM_read(unsigned char Address) 
    { 
    while(EECR & (1<<EEWE)); 
    EEAR = Address; 
    EECR |= (1<<EERE);
    asm volatile("nop"::);
    asm volatile("nop"::);
    return EEDR; 
    }
    
    ...
    main()
    {
     ....
    
         while(1)
         {
             asm volatile("nop"::);
             if(k == EEPROM_read(0x00) ) k = 0;
             asm volatile("nop"::);
    
             ...
             waitms(1000);
             k++;
         }
    }
    
  • Pomocny post
    #8 5848002
    BoskiDialer
    Poziom 34  
    Bardziej chodziło mi o wstawienie nop'a tak:
    unsigned char EEPROM_read(unsigned char Address) 
    { 
    //while(EECR & (1<<EEWE)); 
    EEAR = Address; 
    EECR |= (1<<EERE); 
    asm volatile("nop"::);
    return EEDR; 
    }

    Jeśli teraz działa, ale nie wiadomo co było nie tak, to nie można tego traktować jako usunięcie błędu. W którymś momencie może się coś wysypać i nie będzie wiadomo co jest nie tak i co zrobić żeby działało. Może lepiej wklej cały kod, bo takie fragmenty się źle analizuje bez kontekstu.

    Dodano po 9 [minuty]:

    W jaki sposób wykorzystujesz zmienną k?

    Nop'y przed i po if'ie są zbędne. Nadal to są tylko fragmenty. Jeśli nie wiesz gdzie jest błąd, to nie usuwaj fragmentów ze względu na to, że wydaje Ci się, że tam błędów nie ma. Błąd może czaić się gdziekolwiek i mieć wpływ na pracę całego układu - wiem z własnego doświadczenia...
  • #9 5848399
    kli
    Poziom 13  
    OK, wstawiam cały kod:
    
    //---------------------------------------------------------------------------------------------------
    //Naglowki
    //---------------------------------------------------------------------------------------------------
    #include <avr/io.h>
    #include <avr/interrupt.h>
    //#include <avr/signal.h>
    #include <avr/pgmspace.h>
    //#include <avr/sleep.h>
    #include <avr/eeprom.h>
    
    //---------------------------------------------------------------------------------------------------
    //Zmienne
    //---------------------------------------------------------------------------------------------------
    unsigned char PROGRAM_NUMBER;
    volatile unsigned char k;
    unsigned int delay_counter;
    unsigned char STILL;
    unsigned char CYCLE;
    unsigned char DELAY;
    unsigned char SWITCH_ON;
    
    const unsigned char BUTTON = 0b01000000;
    
    unsigned char EEMEM TABLE[19]=
    {
    	10,
        255,   0,   0,    44,    0,     7,   
        128,   0,   0,    14,    0,    12,
    	0,     0,   0,    0,     0,     0, 
    };
    
    //---------------------------------------------------------------------------------------------------
    //Funkcja odczytujaca dana z pamiecie EEPROM
    //---------------------------------------------------------------------------------------------------
    
    unsigned char EEPROM_read(unsigned char Address) 
    { 
    while(EECR & (1<<EEWE)); 
    EEAR = Address; 
    EECR |= (1<<EERE);
    asm volatile("nop"::);
    asm volatile("nop"::);
    return EEDR; 
    }
    
    //---------------------------------------------------------------------------------------------------
    //Funkcja reset
    //---------------------------------------------------------------------------------------------------
    void reset(void)
    {
         //--------------------------------------
    	//USTAWIAENIA PORTOW
    	//--------------------------------------
        DDRB = 0x00;//caly port B jako wejscie	
    	PORTB = 0xFF;//podciaganie  
        DDRA = 0xFF;//caly port A jako wyjscie  
        PORTA = 0xFF;//podciaganie
        //--------------------------------------
    	//USTAWIENIA LICZNIKOW
    	//--------------------------------------
        TCCR1B |= (1 << CS10); 
        //--------------------------------------
    	//zerowanie zmiennych
    	PROGRAM_NUMBER = 0;
        SWITCH_ON = 0;					   
    	STILL = 0;
    	k = 0;
    	//--------------------------------------
    	//USTAWIENIA PRZERWAN
    	//--------------------------------------
        TIMSK |= (1 << TOIE1);//odblokowanie przerwania od przepelnienia licznika timer1
    	GIMSK |= (1 << INT0);//odblokowanie przerwania zewnetrznego INT0 (przycisk)
    }
    
    //---------------------------------------------------------------------------------------------------
    //MAIN
    //---------------------------------------------------------------------------------------------------
    int main()
    {
        
        reset();
    	// globalne odblokowanie przerwań
    	sei();
        
        //--------------------------------------
    	//GLOWNA PETLA PROGRAMU
    	//--------------------------------------
        while(1)
        {	
    		 
    		 //if(k == 10) //Jak odznacze tą linijke zamiast ponizszej to program dziala!
    		 if(k == EEPROM_read(TABLE))
    		 {
    		     k=0;
    			 PORTA ^= (1<<PA4);
             }
             
    		 PORTA ^= (1<<PA3);
             		 		
    		 k++;
    		 		 			 
             do
    		 {   		    		 		 							 	
    			 DELAY = 1;
    			 while(DELAY);
    			 	   
    	         STILL++;
    			             
                 if (SWITCH_ON == 1)
    		     {					  
    			     if(!(PINB & BUTTON))
    				 {
    			          delay_counter++;
    				      if(delay_counter>200)
    				      {	
    						  cli();
    
    						  PORTA = 0xFF;
                               
                              //oczekiwanie na zwolnienie przycisku
    					      while(!(PINB & BUTTON));
    
    
    						  //opoznienie
    						  delay_counter = 0;
    					      while(--delay_counter);					     
    
    						  GIMSK |= (1 << INT0);
                              
    						  //uspienie
    						  MCUCR |=(1<<SM1)|(1<<SE);//set_sleep_mode(SLEEP_MODE_PWR_DOWN);//sleep_enable();
    						  sei();						 
    						  asm volatile("sleep");//sleep_cpu();
    						  MCUCR &=~(1<<SE);//sleep_disable();
    					 
    					      //oczekiwanie na zwolnienie przycisku
    					      while(!(PINB & BUTTON));
    
    						  //opoznienie przy wlaczaniu
                              delay_counter = 0;
    					      while(--delay_counter);
    
    						  reset();											  
    				      }
        		     }
     
     				 else
    				 {	
    				 	  delay_counter = 0;
    					  while(--delay_counter);
    	
    					 					 														 						
    			          SWITCH_ON = 0;  			       
    				      STILL = 0;
    				      CYCLE = 0;
    					  k = 0;
    
    					  GIMSK |= (1 << INT0);
    				 }
    		         
    		     }
    
    
             }
    		 while(STILL);		 
    		 	      
        }
    
    return 0;
    }
    
    
    
    //---------------------------------------------------------------------------------------------------
    // Przepelnienie licznika timer1
    //---------------------------------------------------------------------------------------------------
    SIGNAL (SIG_OVERFLOW1)
    {
    
        CYCLE++;
        //if(CYCLE==1) CYCLE=0;
        if(CYCLE ==0)
    	{
            if(DELAY != 0) DELAY = 0;   
    	}
    
    }
    
    
    
    //---------------------------------------------------------------------------------------------------
    // Przerwanie INT0(przycisk)
    //---------------------------------------------------------------------------------------------------
    ISR(INT0_vect)
    {
        //wylaczenie przerwan od INT0
    	GIMSK &= ~(1 <<INT0);
    	SWITCH_ON = 1; 
    }
    
  • Pomocny post
    #11 5848640
    chudybyk
    Poziom 31  
    Chyba już wiem o co biega.
    Porcedurka EEPROM_read() powinna wyć podobna jak w pliku eeprom.h:

    
    /** \ingroup avr_eeprom
        Read one byte from EEPROM address \a __p.
     */
    __ATTR_PURE__ static __inline__ uint8_t eeprom_read_byte (const uint8_t *__p)
    {
        do {} while (!eeprom_is_ready ());
    #if	E2END <= 0xFF
        EEARL = (unsigned)__p;
    #else
        EEAR = (unsigned)__p;
    #endif
        EECR |= (1 << EERE);
        return EEDR;
    }
    


    Swoją drogą, może być prościej stosować właśnie "eeprom_read_byte".
    Wtedy wywołanie wyglądałoby:
    (k == eeprom_read_byte(&TABLE[0]))


    Freddie Chopin, nie trzeba używać osobnego pliku dla umieszczenia kilku zmiennych w eepromie.
  • #13 5848685
    chudybyk
    Poziom 31  
    Miałem na myśli, że plik .eep sam się stworzy w trakcie kompilacji. Nie trzeba dodatkowych zabiegów.
  • #15 5849563
    kli
    Poziom 13  
    Cytat:

    a tak w ogole to ty programujesz ten EEPROM? do tego celu jest osobny plik (rozszerzenie *.eep), ktory trzeba wgrac do procka


    Tak, programuje eeprom i ta tablica rzeczywiscie tam siedzi bo jak juz napisalem odczyt eepromu poza pętlą while działa, dzięki czemu mogłem to sprawdzić.

    Cytat:


    Swoją drogą, może być prościej stosować właśnie "eeprom_read_byte".
    Wtedy wywołanie wyglądałoby:
    (k == eeprom_read_byte(&TABLE[0]))



    Użyłem powyższej funkcji, ale objawy są te same. Zauważyłem, że program się jednak nie zawiesza, tak jak myślałem wcześniej tylko zawiesza się sam PORTA(tzn diody nie migaja jak powinny).
  • #16 5851079
    piotr_go
    Konstruktor DIY elektronika
    Sprawdź listing assemblerowy jaki jest generowany.
    Też miałem niedawno dziwny problem z GCC.
    Niby taki banalny kawałek kodu:
    dana = ADCW;
    dana *= 31940;

    a okazało się że "dana" mnożona jest ale przez puste rejestry :(
    Musiałem przed mnożeniem dać wstawki:
    asm("ldi r18, 0xC4");
    asm("ldi r19, 0x7C");
    asm("ldi r20, 0");
    asm("ldi r21, 0");

    żeby zadziałało.
  • #18 5851254
    piotr_go
    Konstruktor DIY elektronika
    	unsigned long dana;
    	ADCSRA |= _BV(ADSC);
    ...
    ...
    ...
    while((ADCSRA & _BV(ADSC))>0){}
    	dana = ADCW;
    	asm("ldi r18, 0xC4");
    	asm("ldi r19, 0x7C");
    	asm("ldi r20, 0");
    	asm("ldi r21, 0");
    	dana *= 31940;
    	dana >>= 16;
    	dana += 25;


    i kawałek jaki wygenerował kompilator kiedy nie użyłem wstawek w asm :
    		while((ADCSRA & _BV(ADSC))>0){}
     474:	36 99       	sbic	0x06, 6	; 6
     476:	fe cf       	rjmp	.-4      	; 0x474 <__stack+0x15>
    		dana = ADCW;
     478:	64 b1       	in	r22, 0x04	; 4
     47a:	75 b1       	in	r23, 0x05	; 5
     47c:	80 e0       	ldi	r24, 0x00	; 0
     47e:	90 e0       	ldi	r25, 0x00	; 0
    		dana *= 31940;
     480:	a9 d1       	rcall	.+850    	; 0x7d4 <__mulsi3>
    		dana >>= 16;
     482:	7c 01       	movw	r14, r24
     484:	00 27       	eor	r16, r16
     486:	11 27       	eor	r17, r17
    		dana += 25;
     488:	89 e1       	ldi	r24, 0x19	; 25
     48a:	90 e0       	ldi	r25, 0x00	; 0
     48c:	a0 e0       	ldi	r26, 0x00	; 0
     48e:	b0 e0       	ldi	r27, 0x00	; 0
     490:	e8 0e       	add	r14, r24
     492:	f9 1e       	adc	r15, r25
     494:	0a 1f       	adc	r16, r26
     496:	1b 1f       	adc	r17, r27


    a tu sprawny kawałek ze wstawkami:
    		while((ADCSRA & _BV(ADSC))>0){}
     474:	36 99       	sbic	0x06, 6	; 6
     476:	fe cf       	rjmp	.-4      	; 0x474 <__stack+0x15>
    		dana = ADCW;
     478:	64 b1       	in	r22, 0x04	; 4
     47a:	75 b1       	in	r23, 0x05	; 5
     47c:	80 e0       	ldi	r24, 0x00	; 0
     47e:	90 e0       	ldi	r25, 0x00	; 0
    		asm("ldi r18, 0xC4");
     480:	24 ec       	ldi	r18, 0xC4	; 196
    		asm("ldi r19, 0x7C");
     482:	3c e7       	ldi	r19, 0x7C	; 124
    		asm("ldi r20, 0");
     484:	40 e0       	ldi	r20, 0x00	; 0
    		asm("ldi r21, 0");
     486:	50 e0       	ldi	r21, 0x00	; 0
    		dana *= 31940;
     488:	a9 d1       	rcall	.+850    	; 0x7dc <__mulsi3>
    		dana >>= 16;
     48a:	7c 01       	movw	r14, r24
     48c:	00 27       	eor	r16, r16
     48e:	11 27       	eor	r17, r17
    		dana += 25;
     490:	89 e1       	ldi	r24, 0x19	; 25
     492:	90 e0       	ldi	r25, 0x00	; 0
     494:	a0 e0       	ldi	r26, 0x00	; 0
     496:	b0 e0       	ldi	r27, 0x00	; 0
     498:	e8 0e       	add	r14, r24
     49a:	f9 1e       	adc	r15, r25
     49c:	0a 1f       	adc	r16, r26
     49e:	1b 1f       	adc	r17, r27
  • #20 5851457
    kli
    Poziom 13  
    Cytat:

    Kod:
    unsigned char DELAY;


    Kod:
    DELAY = 1;
    while(DELAY);


    Reszty kodu nie chce mi się analizować.


    BoskiDialer DELAY jest zerowany w przerwaniu od timera1, wtedy kod rusza dalej. Jak Ci się nie chce to nie pomagaj.
  • Pomocny post
    #21 5851841
    BoskiDialer
    Poziom 34  
    Tak, jest zerowane w przerwaniu, ale zmienna nie jest oznaczona jako volatile - w takim przypadku kod zostanie zoptymalizowany do jednego sprawdzenia i pętli nieskończonej.

    Dodano po 6 [minuty]:

    unsigned char DELAY;
    void myfunc()
    {
    	DELAY = 1; 
    	while(DELAY);
    }
    kompiluje się do:
    myfunc:
    	ldi r24,lo8(1)
    	sts DELAY,r24 /* ustawienie zmiennej */
    .L6:
    	rjmp .L6 /* <-- pętla nieskończona */
  • #22 5852221
    kli
    Poziom 13  
    U mnie kompilator rozpisuje to tak:

    
    215:DELAY = 1;
    +00000279:   E081        LDI        R24,0x01       Load immediate
    +0000027A:   9380008C    STS        0x008C,R24   Store direct to data space
    216:while(DELAY);
    +0000027C:   9180008C    LDS       R24,0x008C   Load direct from data space
    +0000027E:   2388        TST       R24               Test for Zero or Minus
    +0000027F:   F7E1        BRNE      PC-0x03         Branch if not equal
    
    


    Tak więc nie ma pętli nieskończonej, aczkolwiek dzięki za uwagę, przyda się na przyszłość.

    Czyli nie to jest problemem, zresztą pisałem że program się nie wiesza.
  • Pomocny post
    #26 5853544
    BoskiDialer
    Poziom 34  
    kli: Sprawdziłem na kompilatorze, który posiadam (avr-gcc 4.3.0) - kiedy zostanie wygenerowany taki kod, jaki wkleiłeś - na wszystkich poziomach optymalizacji oprócz -O0 jest generowany kod, który ja wkleiłem, tylko przy -O0 taki jak twój. Używanie "volatile" nie jest czymś opcjonalnym ("dzięki za uwagę, przyda się na przyszłość"), używając przerwań musisz być świadomy tego, co to zmienia. Jeśli nie jesteś pewien, to zadeklaruj wszystkie zmienne globalne(z jakich korzystasz w przerwaniach) jako volatile. Poczytaj o "volatile"

    Podobnie na wyższych poziomach optymalizacji, pętle opóźniające z "delay_counter" zostają usunięte - już przy -O1 znikają. Poczyta o "_delay_ms"

    Analizując kod nie umiem do niczego dojść, kod nie jest opisany. Sprawdź może dokładnie w którym miejscu kod się zawiesza/w jakim przypadku zawartość zmiennych przestaje się zmieniać lub opisz dokładne objawy. Sprawdź też, gdy przy odczycie zamiast "EECR |= (1<<EERE);" dasz "EECR = (1<<EERE);".
  • #28 5853765
    kli
    Poziom 13  
    BoskiDialer : Masz rację, wcześniej nie sprawdzałem dla innych poziomów opytmalizacji, teraz widzę że rzeczywiście tworzy nieskończoną pętlę. Użyłem volatile do wszystkich krytycznych zmiennych.

    Ale dopiero, kiedy zmieniłem funkcję odzytu eepromu tak jak sugerowałeś to zadziałało!!!
    
    unsigned char EEPROM_read(unsigned char Address) 
    { 
    while(EECR & (1<<EEWE)); 
    EEAR = Address; 
    EECR = (1<<EERE); //tutaj jest tak, zamiast EECR |= (1<<EERE);
    asm volatile("nop"::);
    return EEDR; 
    }
    
    

    Możesz mi powiedzieć czym się to teraz różni??
  • #29 5854005
    BoskiDialer
    Poziom 34  
    Nie potrafię tego do końca wytłumaczyć, gdyż w dokumentacji nie ma (nie znalazłem) żadnej wzmianki o tym, żeby wpisywać do EECR wartość (1<<EERE) zamiast ustawiać tylko wybrany bit. Ustawianie bitu przez "|=" wymusza odczyt rejestru, aktualizację kopii a następnie wpisanie, przy "=" występuje samo wpisanie. Jeśli bity inne niż EERE były by ustawione, to mogło by to doprowadzić do wpisywania jedynki również w innych pozycjach, co jest tutaj nie dozwolone, jakkolwiek bit EEMWE jest kasowany po 4 cyklach, EEWE jest z założenia (pętla) skasowany, EERIE mógł by powodować resetowanie procesora przez skok do nieobsłużonego wektora przerwania (tu jest potencjalny błąd, chociaż nigdzie nie widzę ustawienia tego bitu, bootloader na tym procesorze nawet gdyby był, to by nie korzystał z przerwań). Napisałem o tym, gdyż przy przeanalizowaniu kodu denerwowały mnie nagminnie stosowane operacje na bitach, podczas gdy nie zawsze są one potrzebne.
  • #30 5854160
    Freddie Chopin
    Specjalista - Mikrokontrolery
    w AVRach jest bug, który powoduje wywolanie przerwania EERDY jesli do zapisu ktoregos tam bitu w EECR uzyje sie instrukcji ST lub STS. moze tu lezy problem?

    w ktorejstam wersji gcc byl bug, ktory tego nie uwzglednial.

    Cytat:

    5. Reading EEPROM by using ST or STS to set EERE bit triggers unexpected interrupt
    request.
    Reading EEPROM by using the ST or STS command to set the EERE bit in the EECR register
    triggers an unexpected EEPROM interrupt request.
    Problem Fix / Workaround
    Always use OUT or SBI to set EERE in EECR.


    4\/3!!
REKLAMA