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

AVR-GCC - Zapis i aktualizacja bloku danych w EEPROM?

robiw 24 Sie 2016 19:00 1350 21
  • #1 24 Sie 2016 19:00
    robiw
    Poziom 26  

    Witam Kolegów,
    W swoim projekcie opartym na ATmega644 stosuję wear leveling i zapis struktury 42 bajtów danych do pamięci EEPROM korzystając z wbudowanych funkcji eeprom_write_block i eeprom_update_block. Według dokumentacji zapis bajtu/strony (8 bajtów) danych do pamięci EEPROM zajmuje minimum 3.3ms (Table 6-2.). Zmierzyłem (z użyciem Timera1) czasy zapisu i otrzymałem następujące wartości:

    1. użycie eeprom_write_block i zapis 42 bajtów danych (całej struktury): 16.4 ms,
    2. użycie eeprom_update_block i zapis 42 bajtów danych (całej struktury), przy czym nie został zmieniony jakikolwiek bajt tejże struktury: 2 ms,
    3. użycie eeprom_update_block i zapis 42 bajtów danych (całej struktury), przy czym zmieniono wartość pierwszych 20 bajtów danych tejże struktury: 4.2 ms.

    Skąd takie, krótsze czasy zapisu? Rozumiem, że 42 bajty danych to w sumie 6 stron pamięci EEPROM (5*8 + 2), w związku z czym wartości dla poszczególnych przypadków powinny wynosić:

    1. 6 stron *3.3 ms = 19.8 ms,
    2. brak zmiany - czas potrzebny wyłącznie na odczyt i porównanie,
    3. 3 strony (2*8 + 4) *3.3 ms = 9.9 ms.

    Popełniam jakiś błąd myślowy? Rozumiem, że wystąpienie w tym czasie przerwań (i ich obsługa) może wydłużyć jeszcze te czasy lub nawet uniemożliwić poprawny zapis do EEPROM'a zgodnie z zapisem w dokumentacji (str.22, EEPE: EEPROM Programming Enable):

    Cytat:
    Caution: An interrupt between step 5 and step 6 will make the write cycle fail, since the EEPROM Master Write Enable will time-out. If an interrupt routine accessing the EEPROM is interrupting another EEPROM access, the EEAR or EEDR Register will be modified, causing the interrupted EEPROM access to fail. It is recommended to have the Global Interrupt Flag cleared during all the steps to avoid these problems.


    Z góry dziękuję za podpowiedzi... robiw

    0 21
  • #2 24 Sie 2016 19:27
    atom1477
    Poziom 43  

    Ja w datasheecie widzę co innego:

    Cytat:
    3.4 ms Erase and Write in one operation (Atomic Operation)
    1.8 ms Erase Only
    1.8 ms Write Only

    I są to czasy typowe a nie minimalne.

    Być może funkcja eeprom_write_block sprawdza czy wymagane jest kasowanie i jak nie to nie kasuje a jedynie zapisuje (gdy zmieniane są bity tylko z 1 na 0, np. z wartości 0xF1 na 0xF0).

    0
  • #3 24 Sie 2016 19:34
    robiw
    Poziom 26  

    atom1477 napisał:
    I są to czasy typowe a nie minimalne.


    Rozumiem, w takim razie, że zawsze warto używać _update, zamiast _write? Skąd, więc, takie wynikowe czasy? A jak się ma do tego wystąpienie innych przerwań? robiw

    Dodano po 1 [minuty]:

    atom1477 napisał:
    Być może funkcja eeprom_write_block sprawdza czy wymagane jest kasowanie i jak nie to nie kasuje a jedynie zapisuje (gdy zmieniane są bity tylko z 1 na 0, np. z wartości 0xF1 na 0xF0).


    eeprom_write_block czy eeprom_update_block? To musi jednoznacznie wynikać z jej konstrukcji...robiw

    0
  • #4 24 Sie 2016 19:34
    atom1477
    Poziom 43  

    Podejrzyj kod assemblerowy po kompilacji.
    Przerwania nie zwiększą czasu zapisu bo zapis przebiega sprzętowo. Natomiast mogą zwiększyć czas poza samym zapisem (programowe sprawdzanie flag zapisu).

    0
  • #5 24 Sie 2016 19:36
    excray
    Poziom 39  

    robiw napisał:
    Rozumiem, w takim razie, że zawsze warto używać _update, zamiast _write?

    To zależy co chcesz zrobić. Jak zainicjować EEPROM domyślnymi wartościami to obowiązkowo write. Jak zapisać blok danych uprzednio odczytanych z eeprom to zdecydowanie update.

    0
  • #6 24 Sie 2016 19:41
    robiw
    Poziom 26  

    Zastanawiam się także, w takim razie, czy przed zapisem warto sprawdzać możliwość zapisu za pomocą funkcji eeprom_is_ready()? robiw

    Dodano po 2 [minuty]:

    excray napisał:
    To zależy co chcesz zrobić. Jak zainicjować EEPROM domyślnymi wartościami to obowiązkowo write. Jak zapisać blok danych uprzednio odczytanych z eeprom to zdecydowanie update.


    Chcę zapisać strukturę danych w EEPROM minimalizując czas zapisu, czyli czas "zatrzymania się" programu (pooling, stosowany przez te funckcje)? robiw

    0
  • #7 24 Sie 2016 20:45
    tmf
    Moderator Mikrokontrolery Projektowanie

    excray napisał:
    robiw napisał:
    Rozumiem, w takim razie, że zawsze warto używać _update, zamiast _write?

    To zależy co chcesz zrobić. Jak zainicjować EEPROM domyślnymi wartościami to obowiązkowo write. Jak zapisać blok danych uprzednio odczytanych z eeprom to zdecydowanie update.


    Zawsze można dać _update. Jedyna różnica pomiędzy _write i _update polega na tym, że _update sprawdza, czy wskazana komórka EEPROM nie ma takiej samej wartości jak nowozapisywana i jeśli porównanie wypadnie pozytywnie to odstępuje od zapisu.

    Dodano po 7 [minuty]:

    robiw napisał:

    Popełniam jakiś błąd myślowy? Rozumiem, że wystąpienie w tym czasie przerwań (i ich obsługa) może wydłużyć jeszcze te czasy lub nawet uniemożliwić poprawny zapis do EEPROM'a zgodnie z zapisem w dokumentacji (str.22, EEPE: EEPROM Programming Enable):

    Cytat:
    Caution: An interrupt between step 5 and step 6 will make the write cycle fail, since the EEPROM Master Write Enable will time-out. If an interrupt routine accessing the EEPROM is interrupting another EEPROM access, the EEAR or EEDR Register will be modified, causing the interrupted EEPROM access to fail. It is recommended to have the Global Interrupt Flag cleared during all the steps to avoid these problems.


    Z góry dziękuję za podpowiedzi... robiw


    Czasy zapisu do EEPROM nie są deterministyczne. Tak jak kolega atom napisał, są to czasy typowe i traktuj je jako szacunek.
    Źle też zrozumiałeś notę w kwestii zapisu. Chodzi o to, że na zapis do EEPROM trzeba zezwolić. Robi się to ustawiając odpowiedni bit w rejestrze kontrolnym. To zezwolenie jest ważne przez 4 takty zegara, po czym bit ten jest automatycznie zerowany. Jeśli więc pomiędzy zezwoleniem na zapis, a zapisem obsłużone zostanie przerwanie, to z pewnością miną więcej niż 4 takty i zapis się nie powiedzie. Stąd też w ATMega taką sekcję należy zamykać w sekcję wykonywaną z zablokowanymi przerwaniami (cli/sei). W XMEGA to poprawiono i zapisanie rejestru zezwolenia automatycznie powoduje, że do kolejnej instrukcji procesor nie obsłuży przerwania, w efekcie zapis się powiedzie i nie trzeba marnować pamięci na tworzenie sekcji krytycznych. Dalsza część cytowanej noty odnosi się do sytuacji w której w ISR zapisujesz do EEPROM. Ponieważ zapis EEPROM wiąże się z modyfikacją rejestru adresowego i danych, to jeśli ISR przerwało zapis do EEPROM w aplikacji to zapis ten się skaszani. Jest to typowa sytuacja w której dwa wątki konkurują o dostęp do tego samego zasobu. Znowu najprostszym rozwiązaniem jest zamknięcie takiej operacji w sekcji krytycznej.

    0
  • #8 24 Sie 2016 21:04
    robiw
    Poziom 26  

    Po dogłębnym przejrzeniu tematu w dokumentacji tak właśnie myślałem...jednak korzystając z gotowych funkcji nie możesz zamknąć części funkcji w ATOMIC i tutaj...wszystko kładzie się...robiw

    0
  • #9 24 Sie 2016 21:09
    tmf
    Moderator Mikrokontrolery Projektowanie

    robiw napisał:
    Po dogłębnym przejrzeniu tematu w dokumentacji tak właśnie myślałem...jednak korzystając z gotowych funkcji nie możesz zamknąć części funkcji w ATOMIC i tutaj...wszystko kładzie się...robiw


    Części nie możesz, ale całą funkcję możesz. A jeśli z jakiegoś powodu ci to nie odpowiada to musisz zrobić własną implementację funkcji obsługi EEPROM.

    0
  • #10 25 Sie 2016 07:40
    robiw
    Poziom 26  

    tmf napisał:
    A jeśli z jakiegoś powodu ci to nie odpowiada to musisz zrobić własną implementację funkcji obsługi EEPROM.


    Nie mogę zamknąć całej funkcji, gdyż ona może trwać nawet kilkanaście milisekund, a projekt wykorzystuje 8 funkcji ISR i na 100%, któraś z nich wystąpi w trakcie tych kilkunastu milisekund. Ech...myślałem, że rozwiążę problem "na szybko" korzystając z wbudowanych funkcji. Z drugiej strony, napisanie funkcji typu _update_eeprom nie jest jakoś skomplikowane...

    Zastanawiam się także nad działaniem wbudowanej funkcji _update... ona musi analizować zmianę bajtu stronami anie bajtami, bo gdyby analizowała bajtami i zapisywała każdy zmieniony bajt osobno to zapis takiej struktury z użyciem _update trwałby dłużej, aniżeli z użyciem _write. Dobrze myślę? A strona "zaczyna" się w dowolnym miejscu przestrzeni adresowej pamięci EEPROM, tylko kwestia, że musi obejmować 8 bajtów (dla m644), tak?

    Inne pytanie, to czy jeśli mamy deklarację np. char Tab[30] EEMEM to np. adres 30. elementu do rejestru adresowego przekazuję po prostu tak: EEAR = &Tab[29] ???

    robiw

    Dodano po 39 [minuty]:

    Z tymi stronami to napisałem coś bez sensu, bo przecież do rejestru adresowego przekazuję jeden adres... muszę poczytać w dokumentacji. Swoją drogą musiałbym się zastanowić, jak położenie poszczególnych np. składników struktury podzielone sana strony? robiw

    PS.
    Źródła funkcji _update_block pewnie rozjaśniłyby problem

    0
  • #11 25 Sie 2016 08:04
    tmf
    Moderator Mikrokontrolery Projektowanie

    Zapis nie musi zajmować dużo czasu, jeśli przed wywołaniem zapisu sprawdzisz, czy żaden inny zapis do EEPROM się nie odbywa. Jeśli nie, to wywołanie _write lub _update dla bajtu trwa chwilę, bo funkcja natychmiast wraca i nie czeka na koniec zapisu. Więc tu blokowanie przerwań będzie bezpieczne. Co innego w przypadku zapisu blokowego, ale przecież nie musisz z niego korzystać. W ATMega koncepcja stron pamięci EEPROM jest mglista, bo jak zauważyłeś zapis odbywa się bajtami, więc nie ma żadnego znaczenie czy zapisujesz w ramach strony. To ma jakieś znaczenie przy programowaniu EEPROM po ISP. Strony mają realne znaczenie w XMEGA, gdzie jest 32-bajtowy bufor dla EEPROM i zapis 32-bajtowej strony trwa tyle samo co zapis jednego bajtu.

    0
  • #12 25 Sie 2016 10:38
    robiw
    Poziom 26  

    tmf napisał:
    Co innego w przypadku zapisu blokowego, ale przecież nie musisz z niego korzystać.


    W ten sposób wygodnie zapisać mi strukturę, bo ona ma pola różnej długości i teraz zapisywanie poszczególnych jej pól oddzielnie (o różnej długości) znacznie skomplikuje ten proces. Oczywiście strukturę mogę zrobić elementem unii i indeksować łatwo poszczególne bajty, jednak w ogólnym wypadku musi się zmienić koncepcja programu w tym zakresie. Powiedzmy, że ustawiam flagę konieczności zapisu struktury i wtedy w pętli głównej, w każdym jej obiegu sprawdzam czy czy żaden inny zapis do EEPROM się nie odbywa, i jeśli się nie odbywa to zapisuję kolejny element struktury blokując przerwania na ten drobny czas...i tak aż do końca struktury, po czym kasuję flagę konieczności zapisu i po temacie, zwłaszcza, że na zapis całej tej struktury mam 1s (gdyż ona ulega aktualizacji co sekundę). Dobra koncepcja? robiw

    0
  • #13 25 Sie 2016 11:20
    excray
    Poziom 39  

    Możesz zawsze zapisywać tylko w momencie wyłączania urządzenia. Musisz tylko dorobić detekcję zaniku napięcia. Będzie to ładnie wpływało na Twój "wear leveling".

    0
  • #14 25 Sie 2016 11:21
    tmf
    Moderator Mikrokontrolery Projektowanie

    Może być. A nie prościej wykorzystać przerwania EEPROM? Dodajesz funkcję zapisującą strukturę, która tak naprawdę tylko kopiuje dane do bufora, któy wygodnie zapisujesz w kolejnych przerwaniach EEPROM. Jest to najszybsze rozwiązanie, w dodatku nie wymaga blokowania przerwań.

    0
  • #15 25 Sie 2016 11:59
    robiw
    Poziom 26  

    excray napisał:
    Możesz zawsze zapisywać tylko w momencie wyłączania urządzenia. Musisz tylko dorobić detekcję zaniku napięcia. Będzie to ładnie wpływało na Twój "wear leveling".


    Nie mogę, bo port ADC mam zajęty dla TFT, zaś inne wykorzystanie ADC (pomiar przez AREF) ma również miejsce w tym układzie.

    tmf napisał:
    A nie prościej wykorzystać przerwania EEPROM?

    Inicjować je flagą potrzeby zapisu struktury? A w przerwaniu "lecieć" po kolejnych elementach struktury? robiw

    0
  • #16 25 Sie 2016 11:59
    excray
    Poziom 39  

    robiw napisał:
    Nie mogę, bo port ADC mam zajęty dla TFT, zaś inne wykorzystanie ADC (pomiar przez AREF) ma również miejsce w tym układzie.

    Takie rzeczy robi się na przerwaniu INT lub PCINT.

    0
  • #17 25 Sie 2016 12:13
    robiw
    Poziom 26  

    Spokojnie można też robić na przerwaniu ADC, AC czy innym. U mnie zajęte są wszystkie INT'y oraz cztery PCINT'y...r

    0
  • Pomocny post
    #18 25 Sie 2016 13:41
    tmf
    Moderator Mikrokontrolery Projektowanie

    robiw napisał:

    tmf napisał:
    A nie prościej wykorzystać przerwania EEPROM?

    Inicjować je flagą potrzeby zapisu struktury? A w przerwaniu "lecieć" po kolejnych elementach struktury? robiw


    Można tak, albo dodać kopiowanie struktury do bufora tymczasowego z którego odbędzie się zapis. Ma to sens jeśli w czasie zapisu pola struktóry moga się zmienić - dojdzie wtedy do uszkodzenia danych. Buforowanie rozwiązuje problem. Dodatkowo ma taką zaletę, że możesz zrobić zapis do EEPROM z każdego miejsca w programie, także z ISR.

    0
  • #19 25 Sie 2016 14:33
    robiw
    Poziom 26  

    Zrobię to jednak w przerwaniu EERDY, bo tak będzie dla mnie nawet prościej. Dzięki za sugestię. Buforować nie będę, bo na zapis 42 bajtów danych struktury mam całą sekundę, zaś sam zapis (co dla tego nie ma akurat znaczenia) odbywał się będzie co minutę lub co 30 sekund. Niestety jest miejsce w aplikacji, gdzie w menu konfiguracyjnym odczytywane/zapisywane są dane do innej struktury w EEPROM i te procesy muszę objąć w blok ATOMIC, bo przecież ten zapis/odczyt zakłócony mógłby być przez ISR EEPROMa. Zastanawiam się, czy w ISR sprawdzać przed zapisem, czy komórka nie ma już takiej wartości (musiałbym stosować pooling) czy "olać" sprawę i po prostu dokonywać zapisu. Inna sprawa, że zapis tych 42 bajtów zająć może 42*3.3 ms tak naprawdę...no, ale mamy całą sekundę. Oczywiście nie mowa o czasie wstrzymywania pracy aplikacji, tylko o czasie przeprowadzenia całego procesu... robiw

    0
  • #21 25 Sie 2016 20:09
    robiw
    Poziom 26  

    Zastanawiam się tylko, czy w przerwaniu EEPROM READY mogę bezkarnie używać wbudowanych funkcji eeprom_write_byte i eeprom_read_byte, bo potrzebuję a nie chcę komplikować maszyny stanów przerwania? Inna sprawa, że podstawienie

    np. EEAR = &Tablica[29] zgłasza mi warning, że dokonuję niejawnej promocji wskaźnika do typu int...robiw

    0