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

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

robiw 24 Aug 2016 19:00 2097 21
  • #1
    robiw
    Level 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):

    Quote:
    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
  • #2
    Anonymous
    Level 1  
  • #3
    robiw
    Level 26  
    atom1477 wrote:
    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 wrote:
    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
  • #4
    Anonymous
    Level 1  
  • #5
    excray
    Level 40  
    robiw wrote:
    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.
  • #6
    robiw
    Level 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 wrote:
    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
  • #7
    tmf
    Moderator of Microcontroller designs
    excray wrote:
    robiw wrote:
    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 wrote:

    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):

    Quote:
    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.
  • #8
    robiw
    Level 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
  • #9
    tmf
    Moderator of Microcontroller designs
    robiw wrote:
    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.
  • #10
    robiw
    Level 26  
    tmf wrote:
    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
  • #11
    tmf
    Moderator of Microcontroller designs
    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.
  • #12
    robiw
    Level 26  
    tmf wrote:
    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
  • #13
    excray
    Level 40  
    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".
  • #14
    tmf
    Moderator of Microcontroller designs
    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ń.
  • #15
    robiw
    Level 26  
    excray wrote:
    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 wrote:
    A nie prościej wykorzystać przerwania EEPROM?

    Inicjować je flagą potrzeby zapisu struktury? A w przerwaniu "lecieć" po kolejnych elementach struktury? robiw
  • #16
    excray
    Level 40  
    robiw wrote:
    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.
  • #17
    robiw
    Level 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
  • Helpful post
    #18
    tmf
    Moderator of Microcontroller designs
    robiw wrote:

    tmf wrote:
    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.
  • #19
    robiw
    Level 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
  • #20
    tmf
    Moderator of Microcontroller designs
    Zawsze warto sprawdzać czy zapis jest konieczny. Oszczędza to pamięć, a czasowo w porównaniu do zapisu praktycznie nic nie kosztuje.
  • #21
    robiw
    Level 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
  • #22
    tmf
    Moderator of Microcontroller designs
    Tak, możesz w przerwaniu korzystać z tych funkcji. Co do ostrzeżenia - to zrób jawną konwersję typów. Przypisanie jest poprawne.