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

[ATMega128L][WinAVR]Dziwne zachowanie wewnętrznego EEPROMu

08 Sie 2008 13:28 4474 26
  • #1 5420292
    Konto nie istnieje
    Konto nie istnieje  
  • #2 5422505
    Dr.Vee
    VIP Zasłużony dla elektroda
    Witam,

    Żabek napisał:

    Jeszcze przy okazji niejako, może mi ktoś wyjaśni, bo spotkałem się z dwiema metodami zapisu i chciałbym zrozumieć, czym różni się taki zapis
    eeprom_write_byte(&zmienna, wartosc);
    od zapisu takiego
    eeprom_write_byte((uint8_t *)zmienna, wartosc);
    zakładając, że uint8_t to unsigned char?


    Jeśli używasz &zmienna, to zapisujesz pod adres zmiennej.
    Jeśli uzywasz (typ *)zmienna, to zapisujesz pod adres, który jest przekazany w zmiennej.

    Żabek napisał:

    Eksperymentalnie doszedłem do następującego wniosku - odczytana z EEPROMu wartość jest różna w zależności od miejsca wywołania funkcji odczytującej.
    ...
    Do komórek zapisano odpowiednio 1 2 3 4 i ta sama funkcja w zupełnie innym miejscu odczytuje je poprawnie!


    Sprawdź:
    * jaki jest adres zmiennych - printf("0x%04x", &zmienna), czy wszedzie taki sam?
    * czy zmiana rodzaju optymalizacji coś daje?
    * czy winny jest printf, czy eeprom_read_byte - rozdziel odczyt od wypisywania i przetestuj.

    Pozdrawiam,
    Dr.Vee
  • #3 5422591
    lelekx
    Poziom 30  
    Sprawdź, czy obydwa wywołania procedury odczytu są w tej samej połówce pamięci (górnej/dolnej). Jeżeli nie, to sugeruje błąd w bibliotece obsługi EEPROMu, która nie radzi sobie prawidłowo z dużą pamięcią programu (>32kW/64kB) - odczyt stałych z ROM?
    Awaryjnie zawsze możesz sobie napisać własne procedury dostępu do EEPROM.
  • #4 5423177
    Konto nie istnieje
    Konto nie istnieje  
  • #5 5424397
    Dr.Vee
    VIP Zasłużony dla elektroda
    Witam,

    Żabek napisał:
    Dr.Vee napisał:

    Sprawdź:
    * jaki jest adres zmiennych - printf("0x%04x", &zmienna), czy wszedzie taki sam?

    Sprawdziłem, niestety są różne. Raz jest to 0x4C8, raz 0x01, a odczyt prawidłowy otrzymuję spod 0x4D5.


    No to coś jest nieźle "pokićkane". Zobacz sobie wynik kompilacji programu z pliku program.lss:
    
    avr-objdump -h -S program.elf > program.lss
    

    Funkcje do zapisu eepromu sa rozwijane w miejscu wykonania - poszukaj, jakie dostają argumenty i kto jest tutaj winny... :)

    Żabek napisał:

    Czy 'eleganckim' byłby taki sposób, że definiuję sobie tak:
    #define CONFIGMEM_STATIC_IPADDR_MSB 0x4D5

    a następnie przy zapisie czy odczycie z EEPROMu stosowałbym bezpośrednio adres
    val = eeprom_read_byte(CONFIGMEM_STATIC_IPADDR_MSB);
    eeprom_write_byte(CONFIGMEM_STATIC_IPADDR_MSB, val2);
    ??
    Tylko jak wtedy zapisać 4 tablice, które są w EEPROMie?


    Eleganckie to na pewno nie będzie, poza tym zaraz o tym zapomnisz i któraś inna zmienna w eepromie z adresem przydzielonym automatycznie przez kompilator wyląduje Ci w tym samym miejscu.

    Ale jeśli Ci się "pali pod nogami", to tablicę możesz zapisać/odczytać tak:
    
    eeprom_write_block((void*)adres_w_eeprom, tablica_uint8_t, 4);
    eeprom_read_block(tablica_uint8_t, (void*)adres_w_eeprom, 4);

    Czyli 4 bajty pod rząd.

    Pozdrawiam,
    Dr.Vee
  • #6 5424513
    Konto nie istnieje
    Konto nie istnieje  
  • #7 5425073
    Dr.Vee
    VIP Zasłużony dla elektroda
    Witam,

    Żabek napisał:

    Te pierwsze trzy wywołania odczytu z EEPROMu mają jako argument podany adres w postaci &zmienna. Niby kompilator coś wylicza ale okazuje się, że adres jest zły. Ostatnie wywołanie jest z jawnym podaniem adresu. To jest dobrze i w efekcie daje poprawny odczyt...
    Przejrzałem ten plik dalej i w 'tłumaczeniu' innej funkcji te adresy są wyliczone już dobrze. :|


    No to rzeczywiście coś z generacją kodu jest nie tak... Kończą mi się pomysły ;) Kod jest jak widać wielki i raczej pokazanie go jest nierealne/niewiele pomoże...

    Żabek napisał:

    Dopiero teraz zwróciłem uwagę a ostrzeżenia kompilatora:
    W przypadku jawnego podania adresu wyskakuje
    parsing argument 1 of 'eeprom_read_byte' makes pointer from integer without a cast

    natomiast wariant z '&' daje ostrzeżenie
    parsing argument 1 of 'eeprom_read_byte' discards qualifiers from pointer target type

    Nie bardzo rozumiem co to i dlaczego tak się dzieje...


    W pierwszym przypadku oznacza to, że zapomniałeś rzutowania:
    
    eeprom_read_byte((const uint8_t *) adres);
    


    W drugim przypadku kompilator niegroźnie przypomina, że przekazujesz adres bez "const", bo wyrażenie (&zmienna) jest typu (uint8_t *), a funkcja przyjmuje (const uint8_t *).

    Pozdrawiam,
    Dr.Vee
  • #8 5425117
    Konto nie istnieje
    Konto nie istnieje  
  • #9 5425149
    szeryf.rm
    Poziom 22  
    Kolego, czy ty testujesz to na symulatorze czy w układzie, bo jakoś nie doczytałem, a to ważne. Może będę w stanie ci pomóc.
  • #10 5425548
    Konto nie istnieje
    Konto nie istnieje  
  • #11 5425728
    szeryf.rm
    Poziom 22  
    Doświadczalnie powiem ci, że jeśli chcesz żeby wszystko na 110% działało zmień układ i zastosuj zewnętrzny eeprom. Niestety wewnętrzny nie zawsze działa i różnie z nim bywa. Poprawienie jego sprawności to ustawienie "fuse low byte" 10xxxxxx dla 3.3V i 00xxxxxx dla 5V. Jeśli to nie pomoże to znaczy że masz błąd w programie.
  • #12 5425769
    Konto nie istnieje
    Konto nie istnieje  
  • #13 5425888
    szeryf.rm
    Poziom 22  
    bez optymalizacji też nie działa (-O0)?
    Jeśli nie, to weź spreparuj kod do minimum, tak żeby pokazywał sedno sprawy. Usuń dosłownie wszystko co tylko można, ale tak, żeby problem występował. Uprość nawet odczyt i zapis, żebyśmy mogli prześledzić co robisz. Bo jeśli z O0 nie będzie ci chodzić i fusy masz dobrze ustawione, to tylko kod może więcej wyjaśnić.

    Dodano po 31 [minuty]:

    Kolego, napisz następnym razem coś prostszego do testów. To zazwyczaj wiele wyjaśnia. Bo widzę duży "bałagan" w twoich działaniach. Próbujesz wszystkiego, ale zacznij od podstaw. Jak mi coś nie chodzi w praktyce i na symulatorze to tworzę nowy czysty projekt i wrzucam tam tylko to co nie chodzi od wersji minimalnej do wersji takiej jak mam w programie. W twoim przypadku to przetestuj sobie kod:

    
    #include <avr/io.h>
    
    #include <avr/eeprom.h> 
    //#define 	EEMEM   __attribute__((section(".eeprom")))
    
    static uint32_t  EEMEM Q1;
    static uint32_t  EEMEM Q2;
    static uint32_t  EEMEM Q3;
    static uint32_t  EEMEM Q4;
    static uint8_t EEMEM zmx;
    static uint8_t EEMEM zmy;
    static uint8_t EEMEM zmz;
    
    int main()
    {
    	//próba prawidłowego zapisu
    	eeprom_write_byte(&zmx, 10);
    	eeprom_write_byte(&zmy, 20);
    	eeprom_write_byte(&zmz, 30);
    
    	//ogólna podpowiedź, która już wiele wyjaśnia
    	uint16_t a = zmx;
    	uint16_t b = &zmx;
    
    	uint16_t a2 = zmy;
    	uint16_t b2 = &zmy;
    
    	uint16_t a3 = zmz;
    	uint16_t b3 = &zmz;
    
    	//próba prawidłowego odczytu
    	uint8_t pobrane_zmx = eeprom_read_byte(&zmx);
    	uint8_t pobrane_zmy = eeprom_read_byte(&zmy);
    	uint8_t pobrane_zmz = eeprom_read_byte(&zmz);
    
    	//próba prawidłowego zapisu
    	eeprom_write_byte(&zmx, 100);
    	eeprom_write_byte(&zmy, 101);
    	eeprom_write_byte(&zmz, 102);
    
    	//próba ponownego odczytu
    	pobrane_zmx = eeprom_read_byte(&zmx);
    	pobrane_zmy = eeprom_read_byte(&zmy);
    	pobrane_zmz = eeprom_read_byte(&zmz);
    
    	
    	return 0;
    }
    


    U mnie on chodzi bez problemu. GCC 3.ileś, ale na codzień pracuję na wersji gcc 4.ileś i prawdopodobnie też ci pójdzie.
  • Pomocny post
    #14 5426347
    Dr.Vee
    VIP Zasłużony dla elektroda
    Witam,

    Ogólnie zgadzam się z podejściem szeryfa.fm - ale Żabek doszedł już do tego, że problem leży w generowaniu kodu binarnego i jest dość złożony (tak podejrzewam, kodu źródłowego nie widziałem). W tak prostym przykładzie może się nie powtórzyć.

    Poza tym powinno być:
    /* ... */
    uint16_t a = (uint16_t) &zmx;
    uint16_t b = (uint16_t) &zmy;
    uint16_t c = (uint16_t) &zmz;
    /* ... */
    
    Pozdrawiam,
    Dr.Vee
  • #15 5426471
    szeryf.rm
    Poziom 22  
    Dr.Vee wierz mi na słowo, że to co dopisałeś to jedynie nie wyrzuci ostrzeżeń. Natomiast w praktyce jest to poprawne dla avr.

    Poza tym zaproponowałem, żeby kolega Żabek spróbował kompilacji z -O0. Tam jak już coś nie chodzi to zazwyczaj błąd leży po stronie programisty.

    A jeśli chodzi o "tak prosty przykład" to wydaje mi się, że też się musi powtórzyć przy -O0 :), bo tam nie ma co być źle :P. Kod z -O0 można czytać jak książkę. No i mam nadzieję, że zakładamy, że kod jest pisany tylko w C a nie w C i asm.

    Dodano po 12 [minuty]:

    a tak na marginesie. Żabek mógłby uprościć nieco kod i zamieścić go tutaj. Nie ma w tym nic skomplikowanego. Przecież można usunąć wiele z kodu i dać go do obejrzenia. Nikt niepełnego i nie działającego kodu i tak przecież nie wykorzysta.
  • #16 5426534
    MarasK
    Poziom 18  
    hmm może walnę totalną głupotę, ale próbowałeś dodać opóźnienia?
    
    unsigned char EEMEM zapychacz[4]; //zabezpieczenie przed zapisem na poczatku eeprom (moze byc wadliwe)
    unsigned int EEMEM zmienne[5];		//obszar zmiennych w eeprom
    
    [...]
    
    	for (i=0;i<5;i++)		//odczyt danych z eeprom do tablicy dane
    	{
    		eeprom_busy_wait();
    		dane[i]=eeprom_read_word(&zmienne[i]);
    	}
    
    		for(i=0;i<4;i++)					//nadpisz je domyslnymi (10,10,10,10,50)
    		{eeprom_busy_wait();
    		eeprom_write_word(&zmienne[i],10);
    		}
    		eeprom_busy_wait();
    		eeprom_write_word(&zmienne[i],50);
    
    


    W mojej atmedze8 początek EEPROMu był uszkodzony - wysypywało losowe wartości, z innych adresów działało bez problemu. Jak kolega wyżej napisał pamiętaj o włączeniu BOD (odpowiednio do napięcia).

    Po uzyciu busy_wait wszystkie moje problemy zniknęły
  • #17 5426560
    szeryf.rm
    Poziom 22  
    A i jeszcze jedno. Jak się korzysta z Os i innych optymalizacji to trzeba pamiętać, że niektóre zmienne muszą być volatile.

    MarasK, twoje rozwiązanie z "zapychaczem" jest błędne, pomijając to, że nie o to tutaj chodzi. Jak nie dodasz static to zmienne w programie mające static wyprzedzą twoją tablicę. Po prostu static jest umieszczany przed innymi zmiennymi.
  • #18 5426562
    Konto nie istnieje
    Konto nie istnieje  
  • #19 5426567
    MarasK
    Poziom 18  
    Nie używam innych obszarów eepromu - tylko te dwie tablice. Żadnych zmiennych ze static.
  • #20 5426591
    szeryf.rm
    Poziom 22  
    Żabek, moim zdaniem coś robisz źle na BANK. Co jak co, ale adresy zmiennych i to globalnych są adresami statycznymi, które na prawdę można zamienić na #define. Jest zapewne mało ludzi, którym uda się znaleźć błąd w statycznie kompilowanych adresach. Po prostu jest to tak mało prawdopodobne, że graniczy z cudem. To tak jakby kompilator się pomylił licząc "2+2-4/10". Tylko cud spowoduje, że się pomyli, bo to działanie nie będzie liczone w twoim programie tylko zostanie policzone na etapie kompilacji. Tak samo z tym co ty przedstawiłeś.

    Może jak masz wiele plików i kompilujesz je osobno to masz skopane zmienne, bo nie korzystasz z extern?

    Co do -O0 to pozbądź się delaya i będzie chodzić z -O0. To że ci się nie kompiluje z nie twojej winy, to nie znaczy że tego nie można obejść. Jak wywalisz delaya na próbę to się skompiluje.

    Pozdrawiam i nie poddawaj się bez ustalenia przyczyny, bo być może masz gdzieś jakiś krytyczny błąd, który wyjdzie po 2 miesiącach i po sprzedaniu 1000 urządzeń :). Poddanie się bez ustalenia przyczyny nie jest podejściem profesjonalnym.

    MarasK teraz nie używasz, ale jak zechcesz to będziesz miał problem :). To co podałem to jedynie informacja. Poza tym to offtop.
  • #21 5427104
    MarasK
    Poziom 18  
    szeryf.rm - rozumiem o co chodzi ze static :) Mimo wszystko namawiam autora, aby spróbował dodać instrukcję eeprom_wait_busy przed każdą operacją zapisu/odczytu i sprawdził czy to pomoże.
  • #22 5427117
    szeryf.rm
    Poziom 22  
    Autor i tak już ponoć dał sobie spokój. To co piszesz jest raczej nie do tego problemu, ale jest to jak najbardziej słuszne jeśli taki problem wystąpi. Uzycie "busy_wait" to obowiązek i to pewnie zrobił. Wiele by powiedziało, gdyby kolega zmienił na -O0. W zasadzie to wszystko powie, bo jak z -O0 chodzić nie chce to już nie ma o czym gadać i trzeba szukać gdzie się popełniło błąd.
  • #23 5427220
    Konto nie istnieje
    Konto nie istnieje  
  • Pomocny post
    #24 5427321
    szeryf.rm
    Poziom 22  
    Żabek to nie wstawiaj komentarzy. Utwórz kopię pliku delay.h a potem napisz na szybkiego swoją wersję z zwartością TYLKO taką i nic więcej:
    
    void _delay_ms(double __ms) {}
    void _delay_us(double __us) {}
    


    I po tym -O0 powinno śmigać.
    Jest to niezbyt ładne, ale skuteczne i trwa moment oraz nie wymaga szerszej wiedzy.

    delay.h mam nadzieję że wiesz gdzie jest :).


    ps. a jak uznasz że nie jesteś w stanie dojść, to wyślij te źródła na pw do mnie i Dr.Vee (jeśli Dr.Vee nie ma nic przeciwko). Obejrzymy i znajdziemy ci błąd. Przecież nikt ci tego nie udostępni jak sobie tego nie życzysz.

    Dodano po 2 [godziny] 17 [minuty]:

    Autor wątku udostępnił kod na PW. Po kilku minutach przeglądania i poprawce tak żeby na gcc 4.3 się skompilowało znalazłem błąd. Z moich założeń wynika, że ten błąd będzie bezpośrednią przyczyną problemu, choć nie sprawdziłem tego w bezpośredni sposób, a jedynie utworzyłem analogiczny projekt, w którym popełniłem analogiczny błąd. Skoro w moim projekcie nie działało to znaczy, że tutaj też jest błąd.

    W załączniku zamieszczam 2 wersje projektu w avr-studio. Jak łatwo zauważyć obie wersje są bliźniaczo podobne :), z tym że tylko jedna działa.

    Błąd wynika niestety z nieznajomości składni języka na dostatecznie dużym poziomie. Z doświadczenia wiem, że nie warto zakładać że kompilator się myli, jeśli nie sprawdzi się wszystkiego od A do Z. Już wielokrotnie udowadniałem, że kompilator myli się rzadziej niż ci, którzy go oto oskarżają. Tutaj jest podobnie :). Na szczęście wszystko jest jasne i jest kupa do poprawiania :).

    Z racji tego, że rozwiązanie już zapisałem wcześniej, to za łopatologiczne wytłumaczenie ustaliłem liczbę punktów.

    I pamiętajcie, że kompilator się myli rzadziej niż jest o to oskarżany :).

    Pozdrawiam i liczę, że kolega Żabek kliknie wszędzie "pomógł" :P:P:)
    Załączniki:
  • #25 5428604
    Konto nie istnieje
    Konto nie istnieje  
  • #26 5429376
    szeryf.rm
    Poziom 22  
    Jak najbardziej żabek masz rację. To rozwiązanie, które ja przedstawiłem nie miało na celu pokazania, że tak MA być i nie inaczej. Konsekwentnie można zrobić to dokładnie tak samo ale logiczniej ułożonyć w plikach i nadal będzie działać. Ba, nawet includy da się zrobić :).

    Patrz na to:
    config.c:
    
    uint32_t zmienna1;
    uint32_t zmienna2;
    uint8_t EEMEM zmienna3;
    


    config.h
    
    extern uint32_t zmienna1;
    extern uint32_t zmienna2;
    extern uint8_t EEMEM zmienna3;
    


    Przy takim podejściu dołączać możesz config.h w dowolnym miejscu kodu, bo zmienne zmienna1, zmienna2 i zmienna3 będą w pliku config.c

    Składnia to jedno, porządek w kodzie to drugie :)
    pozdrawiam.
  • #27 5431569
    Konto nie istnieje
    Konto nie istnieje  
REKLAMA