fbpx

Pamiętam gdy pierwszy raz chciałem użyć EEPROMU w STM32. Wszystko szło pięknie do momentu w którym chciałem faktycznie użyć tej pamięci. Okazało się, że… nie wszystkie STMki mają EEPROM! Pozbawione tego ficzera są zwłaszcza serie F. Co zobić w takiej sytuacji? Można użyć zewnętrznego układu na I2C/SPI lub… zaemulować EEPROM na wbudowanej pamięci FLASH. Chodź, pokażę Ci jak to zrobić.

Jak działa pamięć EEPROM?

Myślę, że w pierwszej kolejności należy wiedzieć czym jest pamięć EEPROM. Jest to pamięć nieulotna, czyli jej zawartość nie jest tracona po odłączeniu zasilania. Można z niej oczywiście czytać i nie ma to żadnego wpływu na dane zawarte w pamięci oraz ich żywotność. Możliwe jest też zapisywanie i to już od pojedynczego bajtu. Zaletą EEPROM’u jest również to, że można szybko zapisywać zarówno “zera” jak i “jedynki” co nie jest takie oczywiste w wypadku pamięci FLASH. Dostęp do każdego bajtu zarówno dla odczytu jak i zapisu jest swobodny.

W tym wpisie zajmuję się emulacją EEPROM na wewnętrznej pamięci FLASH mikrokontrolera. Jak działa pamięć FLASH? Działanie jest bardzo podobne do EEPROM’u z jedną “małą” różnicą. Kasowanie pamięci (czyli najczęściej zapis 0xFF w komórce) nie jest taki swobodny swobodny. Wykonuje się go większymi obszarami, które nazywane są sektorami lub stronami w zależności od literatury. Jest to tak zwane czyszczenie FLASH. Sektory te mogą mieć różne rozmiary jak np. 1 kB czy nawet 128 kB. Zapis we flash to tak naprawdę ustawienie samych “zer” pośród płotu “jedynek”.

Z tego zwględu, że pamięć FLASH wymaga specyficznego kasowania nie jest możliwe emulowanie zachowania EEPROM 1:1. Nie można przeznaczyć kilku bajtów pamięci FLASH na kilka zmiennych ponieważ nie jesteśmy w stanie “zapisać” jedynek. Przykład: napisanie 0xF0 wartością 0xFF w pamięci FLASH nie powiedzie się.

Jak wygląda emulacja EEPROM na pamięci FLASH?

Na szczęście ST dostarcza obszerną dokumentację jak emulacja EEPROM na pamięci FLASH powinna wyglądać. Dzisiaj omówię to na przykładzie STM32F103C8T6 znanego miedzy inymi z płytek BluePill oraz na STM32F401RE z zestawu Nucleo

Dla tych dwóch mikrokontrolerów ST przygotowało odpowiednie dokumentacje emulacji EEPROM:

Zasada jest taka sama dla obydwu rodzin. Omówię w skrócie działanie emulacji.

Reprezentacja danych

Dane we FLASH przechowywane są w 32-bitowych komórkach. Komórki te dzielone są na dwie części. Jedna to 16-bitowy wirtualny adres zmiennej w EEPROM. Druga część to wartość tej zmiennej. Dlaczego tak? Ponieważ każda aktualizacja zmiennej wpisuje jej nową wartość w kolejną, wolną komórkę FLASH. Nie nadpisujemy starej wartości.

Po co nam dwie strony pamięci?

Jeżeli każdą nową wartość zmiennej wpisujesz w nową komórkę, to kiedyś te komórki się skończą. Gdy do tego dojdzie, stara strona wyznaczona zostaje do transferu danych na nową stronę. Nie przenosimy wszystkich danych. Nie interesują nas dane historyczne zmiennych dlatego skanujemy od końca pełną stronę i przenosimy najnowsze wartości zmiennych do nowej strony. Po przeniesieniu się na nową stronę zostaje ona oznaczona jako aktywna, a stara będzie skasowana w całości (kasowanie całej strony FLASH).

Odczyt z emulowanego EEPROM

Aby odczytać zmienną o odpowiednim adresie wirtualnym trzeba przeglądnąć całą dotychczas zapisaną stronę. Za każdym razem, gdy spotkana zostanie zmienna o wymaganym przez nas adresie wirtualnym, zostaje zapamiętana jej wartość. Kolejne napotkanie tej samej zmiennej aktualizuje pamiętaną tymczasową wartość. Poszukiwania trwają dopóki nie natrafimy na skasowaną komórkę, czyli taką z adresem wirtualnym 0xFFFF. Stąd też zabronione jest użycie tego adresu wirtualnego. Ostatnia zapamiętana wartość jest zwracana jako ta, którą aktualnie przechowuje nasz EEPROM.

Zapis do emulowanego EEPROM

Zapis poszukuje pierwszej wolnej komórki w stronie pamięci. Jeżeli ją znajdzie to wpisuje podany adres wirtualny oraz wartość którą mu podamy. Jeżeli nie ma wolnych komórek na stronie, to rozpozczyna się procedura przeniesienia najnowszych kopii zmiennych z unikalnymi adresami wirtualnymi. Dopiero po tym wpisana zostanie nasza zmienna w pierwszą wolną komórkę już na nowej stronie. Stara będzie wykasowana. Strony wykorzystywane są w ten sposób naprzemiennie.

Przebieg aktualizacji danych

Zobrazowany cały proces wpisywania przykładowych danych oraz przenoszenia ich na nową stronę znajdziesz w dokumentacjach dla F1 (str. 8) i F4 (str. 11).

Biblioteka emulacji EEPROM

Czytanie dokumentacji i wymyślanie biblioteki dla poprawnego działania emulacji EEPROM może trochę zawrócić w głowie. Na szczęście ST przygotowało odpowiednią bibliotekę. Na dodatek działa ona od strzała z naszym ulubionym HAL’em. Cała biblioteka sprowadza się tylko do trzech funkcji co czyni ją niezamowicie prostą w użyciu. Pogimnastykować się natomiast można z jej konfiguracją w pliku eeprom.h. To tam znajduje się kompletna konfiguracja emulatora EEPROM. Pozwól, że najpierw omówię tę konfigurację. Definicje które nas interesują to:

  • #define PAGE_SIZE – definiuje rozmiar stron które zostaną użyte. Informacje tę znajdziesz w Reference Manual mokrokontrolera lub w bibliotekach HAL.
  • #define EEPROM_START_ADDRESS – od którego adresu w pamięci będzie znajdowała się emulacja EEPROM. Zalecam używać końcówki FLASH’a, aby nie kolidować z programem głównym. Adres ten będzie adresem pierwszej użytej strony FLASH.
  • #define PAGE0_BASE_ADDRESS 
    #define PAGE0_END_ADDRESS 
    #define PAGE0_ID – adresy początkowe i końcowe strony zerowej emulacji oraz ID pierwszej strony we FLASH. To samo jest dla PAGE1 EEPROM’u.
  • #define PAGE0
    #define PAGE1 – opis w nagłówku jest mylący i mówi o tym ile stron FLASH jest użyty dla stron EEPROM. Tak na prawdę ważna jest definicja PAGE1 która mówi o tym ile stron dalej znajduje się ona w stosunku do początku PAGE0. Chcąc użyć po 2 strony FLASH na jedną EEPROM, wpiszesz w PAGE1 liczbę 0x2.
  • #define NB_OF_VAR – liczba unikalnych zmiennych które chcemy przechowywać w emulowanej pamięci EEPROM. Jest to ważne z tego względu, że biblioeka potrzebuje tablicy adresów wirtualnych i trzeba ją wcześniej utworzyć w programie.

Funkcje biblioteczne

Jak już wspomniałem, do dyspozycji uzytkownika są 3 proste funkcje. Oto one:

  • uint16_t EE_Init(void) – inicjalizacja emulatora EEPROM. Sprawdza ona czy strony były wcześniej zapisane(po nagłówku) oraz czy trzeba je wykasować. Przywraca użyteczność po zaniku zasilania.
  • uint16_t EE_ReadVariable(uint16_t VirtAddress, uint16_t* Data) – Odczytanie zmiennej z EEPROM. Zmienna spod wirtualnego adresu VirtAddress trafi do pamięci spod wskaźnika Data.
  • uint16_t EE_WriteVariable(uint16_t VirtAddress, uint16_t Data) – Zapis do EEPROM. Zmienna pod wirtualnym adresem VirtAddress zostanie zapisana wartością Data.

Wszystkie te funkcje zwracają kody statusowe zgodne z HAL’em.

Prawda, że wyglądają na proste w użyciu? Czas podziałać z prawdziwymi układami.

Emulacja EEPROM na STM32F103C8T6

W pierwszej kolejności wziąłem popularnego BluePilla. Pozwól, że tym razem pominę konfigurację Cube’a i schemat. Nie są one istotne w tym ćwiczeniu. Skonfigurowałem sobie jedynie wbudowaną diodę oraz UART2 do przesyłania sobie zawartości emulowanego EEPROM na terminal.

Konfiguracja

Do poprawnej konfiguracji potrzebować będę Reference Manuala z mapą pamięci FLASH mikrokontrolera.

Interesuje nas Main memory. Mamy do dyspozycji 128 stron po 1 kB każda. Do testów mogę wykorzystać dwie ostatnie. Na ogół zalecałbym korzystanie właśnie z końcowych stron. 

W pliku nagłówkowym eeprom.h możesz zauważyć, że adresy każdej strony są już podefiniowane. Możnaby nawet nie patrzeć w dokumentacje, aby poprawnie skonfigurować lecz lepiej jest się upewnić, że nikt nie popełnił błędu w kodzie.

Rozmiar strony jest już zdefiniowany w bibliotece HAL. Można się jedynie upewnić czy ma ok 1 kB, czyli 0x400.

Chcemy wykorzystać dwie ostatnie strony, więc EEPROM_START_ADDRESS ustawiam na przedostatnią stronę, czyli ADDR_FLASH_PAGE_126.

Teraz adresy dwóch stron emulacji EEPROM oraz ilość wykorzystywanych stron. Można posłużyć się wcześniej podefiniowanymi adresami lub pobawić się ofsetami.

#define PAGE0_BASE_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + 0x0000))
#define PAGE0_END_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + (PAGE_SIZE - 1)))
#define PAGE0_ID ADDR_FLASH_PAGE_126

#define PAGE1_BASE_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + 0x400))
#define PAGE1_END_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + 0x400 + PAGE_SIZE - 1))
#define PAGE1_ID ADDR_FLASH_PAGE_127

/* Used Flash pages for EEPROM emulation */
#define PAGE0 ((uint16_t)0x0000)
#define PAGE1 ((uint16_t)0x0001)

Oraz ilość przechowywanych zmiennych.

#define NB_OF_VAR ((uint8_t)27)

Kod programu

Do działania biblioteki wymagane jest powołanie tablicy adresów wirtualnych.

uint16_t VirtAddVarTab[NB_OF_VAR];

Później tę tablicę wypełniam kolejnymi wartościami.

// Fill EEPROM variables addresses
for(VarIndex = 1; VarIndex <= NB_OF_VAR; VarIndex++)
{
  VirtAddVarTab[VarIndex-1] = VarIndex;
}

Bardzo ważne jest, aby odblokować pamięć FLASH z poziomu programu zanim użyjemy funkcji inicjującej bibliotekę.

HAL_FLASH_Unlock();

Teraz można zainicjalizować EEPROM. 

if( EE_Init() != HAL_OK)
{
  Error_Handler();
}

Od tej pory można używać EEPROM. W programie stworzyłem tablicę z przykładowymi danymi którą będę wpisywał do EEPROM. Zawiera ona napis “Mateusz Salamon msalamon.pl”. Składa się ona z 27 znaków, co uwględniłem wcześniej w konfiguracji.

W programie wpisuje i odczytuję te dane z EEPROM według schematu:

  1. Wpisanie danych do EEPROM. Każdy znak pod swój unikalny adres wirtualny.
  2. Odczyt i wypisanie na terminal
  3. Wpisane danych do EEPROM w odwrotnej kolejności.
  4. Odczyt i wypisanie na terminal. Napis powinien być w odwrotnej kolejności.
  5. Wpisanie danych do EEPROM. 
  6. Odczyt i wypisanie na terminal. Efekt taki sam jak w pkt. 2

Efekty z terminala widać na screenie.

Natomiast co się dzieje z pamięcią FLASH? Czego się spodziewam? Według dokumentacji emulacji EEPROM napis powinien trafić pod zmienne z adresami wirtualnymi 1-27 najpierw w normalnej kolejności, później wspak i na końcu znowu normalnie. Rezultat poniżej.

Dane oznaczone 1 są to dane z pierwszego zapisu. 2 to zapis odwrotny. 3 to ponowny zapis w normalnej kolejności. Przeanalizuj proszę te dane. Pierwsza komórka u góry z lewej to nagłówek strony. Jest on w stanie ERASED, więc można pisać do tej strony.

Dalej są dane. Pierwsze 16 bitów (4 cyfry) to wirtualny adres każdej ze zmiennych. Drugie 16 bitów do wartość, czyli znaki z napisu.

Emulacja EEPROM na STM32F401RE

Organizacja pamięci w mikroprocesorach może być różna. Z tego względu należy zawsze sprawdzać dokumentacje. W serii F4 możemy się lekko zaskoczyć gdyż pamięć FLASH wygląda tak:

Mamy tylko 8 sektorów z czego nie są one równe wielkościowo. Pamiętaj, że kasowanie FLASH odbywa się po całym sektorze. Smutno byłoby uzyć 256 kB FLASH’a dla kilku zmiennych EEPROM. Dlatego wykorzystam sektory 2 i 3. Konfiguracja:

/* Define the size of the sectors to be used */
#define PAGE_SIZE (uint32_t)0x4000 /* Page size = 16KByte */

/* Device voltage range supposed to be [2.7V to 3.6V], the operation will
be done by word */
#define VOLTAGE_RANGE (uint8_t)VOLTAGE_RANGE_3

/* EEPROM start address in Flash */
#define EEPROM_START_ADDRESS ((uint32_t)0x08008000) /* EEPROM emulation start address:
from sector2 : after 16KByte of used
Flash memory */

/* Pages 0 and 1 base and end addresses */
#define PAGE0_BASE_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + 0x0000))
#define PAGE0_END_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + (PAGE_SIZE - 1)))
#define PAGE0_ID FLASH_SECTOR_2

#define PAGE1_BASE_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + 0x4000))
#define PAGE1_END_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + (2 * PAGE_SIZE - 1)))
#define PAGE1_ID FLASH_SECTOR_3

/* Used Flash pages for EEPROM emulation */
#define PAGE0 ((uint16_t)0x0000)
#define PAGE1 ((uint16_t)0x0001) /* Page nb between PAGE0_BASE_ADDRESS & PAGE1_BASE_ADDRESS*/

Ilość zmiennych tak samo jak dla F1 ustawiam na

/* Variables' number */

#define NB_OF_VAR ((uint8_t)27)

Kod w pliku main.c jest taki sam. Rezultat na terminalu jest identyczny jak dla F1, a w pamięci FLASH wygląda to podobnie z tym, że pod innym adresem.

Pokusiłem się o sprawdzenie ile czasu zajmuje zapisanie i odczytanie mojej tablicy znaków.

173 ms dla zapisu i 3,24 ms dla odczytu. Co ciekawe wartości te nie zmieniają się znacząco wraz ze wzrostem zapełnienia FLASH. Jest jeden moment, kiedy czas ten będzie bardzo duży. Jest to przeniesienie danych na nową stronę EEPROM oraz kasowanie sektorów FLASH.

Emulacja EEPROM przed programem głównym

Jak pewnie zauważyłeś zdefiniowałem emulację EEPROM w połowie FLASHA F401RE. Niechętnie chciałbym wykorzystywać połowę dostępnej pamięci na EEPROM gdybym definiował ją na dwóch ostatnich sektorach.

Z tego względu wpadłem na pomysł, aby EEPROM zrobić na pierwszych dwóch sektorach, a program główny przesunąć na sektor nr 2. Niestety wykonanie tego zabiegu powoduje jakiś błąd w działaniu programu i mikrokontroler się wywala… Pogrzebałem trochę w kodzie i znalazłem pewien problem przy funkcji weryfikującej czy strona jest wykasowana. Użyta jest ona w inicjalizacji na której wywala się program. Jest nawet założony wątek na forum ST odnośnie tego błędu.

Niestety moje próby naprawienia tego fragmentu nie pomogły. Być może problem jeszcze jest gdzieś indziej lub po prostu nie da się wykonać zamiany EEPROM z programem głównym.

Jedyne co mi przychodzi do głowy podczas pisania tego artykułu to rozdział projektu na bootloader i program główny. Bootloader byłby uruchamiany z adresu początkowego i wykonywałby skok do aplikacji głównej, która mieści się za dwoma kolejnymi, pustymi sektorami. Wtedy kawałek FLASH między botloaderem a programem głównym mógły być wykorzystany na emulację EEPROM 🙂

Podsumowanie

Czy warto uzywać emulacji EEPROM? Sądząc po tym, że kod od wielu lat ma łatwe do znalezienia błędy w kodzie to obstawiam, że mało kto go wykorzystuje. Działanie emulacji EEPROMU też nie jest idealne. Zabiera co najmniej dwa sektory (strony) pamięci FLASH.

Jak widziałeś w mikrokontrolerach serii F4 emulacja EEPROM może być bardzo uciążliwa ze względu na różne rozmiary sektorów pamięci.

Jest jeszcze jedna kwestia której nie poruszyłem. Żywotność tego rozwiązania. Pamieć FLASH nie jest demonem żywotności. Jedna komórka pamięci może wytrzymać tylko ok 10 tys. cykli zapisu. Nie jest to zbyt duża ilość zwłaszcza porównując ją z żywotnością EEPROM na poziomie około miliona cykli (pamięci AT24C32 od Atmel).

Powstaje więc pytanie – czy warto emulować EEPROM? Czasem warto, czasem nie 🙂 Na to należy sobie odpowiedzieć indywidualnie. Zależy to od projektu. Można skorzystać z takiego STM32, który ma już EEPROM w swojej strukturze. Można uzyć zewnętrznego układu. Rozwiązań jest co najmniej kilka. 

Warto jednak wiedzieć, że w sytuacjach ekstremalnych można skorzystać z pamięci FLASH jako emulowanego EEPROM. Działa to poprawnie więc dla rzadko zmienianych danych może być to całkiem niezły sposób.

Kody testowe wraz z bibliotekami znajdziesz na moim GitHUB’ie: F103C8, F401RE

Uważasz, że popełniłem gdzieś błąd? Masz jakiś ciekawy pomysł co można ulepszyć? Podziel się tym w komentarzu! Pamiętaj, że dyskusja ma być kulturalna i zgodna z zasadami języka polskiego.

4.3/5 - (11 votes)

kurs stm32

Wyniki konkursu

Ostatnio ogłosiłem mały konkurs. Sposród zgłoszeń wybrałem troje zwycięzców. Są nimi:

  1. Leoneq ;3  z komentarzem 
    “Hmm, ja bym najchętniej chciał zobaczyć artykuł o generowaniu obrazu VGA. Na arduino jest jakaś biblioteka, ale 120×60 pikseli to słabo, nie mówiąc o dwóch zajętych timerach. Na stm generowanie obrazu nie powinno być tak zajmujące, a dałoby baaardzo dużo możliwości dla nowych projektów ? Btw, obserwuję Twojego bloga od dłuższego czasu, i muszę powiedzieć że utrzymuje poziom. Ładnie napisane, konkretnie i ciekawie. Pozdrawiam ?
  2. Mruczek  z komentarzem
    “Komunikacja! Ethernet – ten na dużych Nucleo, wykorzystanie ESP8266 jako WiFi, NRF24L01+, który jest fajny do zastosowań energooszczędnych z komunikacją bezprzewodową.”
  3. Mateusz z komentarzem
    “Witam ? jestem uszczęśliwiony, że na blogu są już tematy, które mnie interesowały m.in. właśnie o wyświetlaczach czy alcelerometrze. Ale sam byłbym zainteresowany bardziej wykorzystaniem zarówno FPU jak i opcji ADC w „bardziej zaawansowanym” przetwarzaniu sygnałów. W temacie można by zawrzeć także proste procedury przetwarzania takie jak chociażby FFT do zmiany dziedziny czasu w dziedzinę częstotliwości, czy tez proste/bardziej złożone układy kondycjonowani sygnałów na takie przyjazne dla procesora, pomiar względem masy pozornej itp. Myśle, ze pomimo wszechobecnej cyfryzacji taki temat mógłby się przydać ?

Gratuluję zwycięzcom! Odezwijcie się do mnie na maila: mateusz@msalamon.pl w celu finalizacji dostarczenia nagród 🙂

Podobne artykuły

.

24 komentarze

Gustlik · 17/02/2023 o 22:04

A może obsługa wbudowanego EEPROM? Bo jakoś szukam i nie mogę znaleźć..;) chyba tylko taki na i2c pozostaje.

Felícito · 17/12/2022 o 06:14

Hi, I want to thank you for sharing this post and for all the comments, specially to Chris comments about the miracles that also happened to me. All the information was incredible usefull. Sorry to write in english, but my native tongue is spanish and didn’t want to use translator. Kind regards from El Salvador.

Atmele · 14/09/2022 o 15:47

dleczego europa nie u8żywa tych mikroprocesorówi innych Atmeli? KIlka , a może 10 lat temu królowały na świecie PICe.

BHornicki · 31/01/2022 o 03:15

Cześć,
W artykule wkradł się błąd. STM32F103C8T6 posiada 64kB FLASH, w 1kB stronach, czyli łącznie 64 strony! Mapa pamięci FLASH z reference manuala podaje 128 stron, ponieważ dotyczy ona również modelu posiadającego 128kB.
Zamiast stosować w tym mikrokontrolerze strony ADDR_FLASH_PAGE_126 i ADDR_FLASH_PAGE_127, należałoby wykorzystać ADDR_FLASH_PAGE_62 i ADDR_FLASH_PAGE_63.

    Mateusz Salamon · 31/01/2022 o 09:16

    Pytanie czy to jest rpawdziwy układ, czy z BluePilla. Te z Chin na ogół mają “odblokowane” 128k po jakimś spadzie 😉 Oczywiście nie warto korzystać z tych górnych 64k, bo są niepewne.

Enkidu · 24/04/2021 o 19:18

Właśnie musiałem sobie przypomnieć co nieco w temacie emulacji EEPROM. Żeby nie bawić się w bootloader, a mieć emulację na 3x16kB w F401 należy przeedytować skrypt linkera. W sekcji “MEMORY” u mnie wygląda to tak:
MEMORY
{
ISR (rx) : ORIGIN = 0x08000000, LENGTH = 16K
EEPROM (xrw) : ORIGIN = 0x08004000, LENGTH = 48K
FLASH (rx) : ORIGIN = 0x08010000, LENGTH = 448K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 96K
}

następnie wektor przerwań przerzucamy do pierwszego obszaru, czyli w poniższym bloku zmieniamy FLASH na ISR (można też zupełnie świadomie przenieść tam inne elementy, ale to już zależy od aplikacji, ja to pominąłem):
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >ISR

    Mateusz Salamon · 17/05/2021 o 13:16

    Dzięki! Przyda się.

tataOlka · 14/03/2021 o 00:39

Do Krzysiek (ale nie tylko),
W pliku linkera (u mnie to STM32F407VETX_FLASH.ld, bo mam czarną, chińską płytkę z STM32F407) zmieniłem wpis dotyczący wektora przerwania od resetu:
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(65536);
} >FLASH
Skutek jest taki, że na wektor ten rezerwowane są sektory 0,1, 2 i 3 (pierwsze 64KB pamięci flash, choć tak naprawdę wektor zajmuje tylko 4 pierwsze bajty). ‘EEPROM’ mam ustawiony na sektory 2 i 3 (w sumie 32KB), reszta, czyli 448KB (512KB – 64KB, od adresu 0x08010000) to miejsce na program (sekcja “.text” i inne “drobiazgi”). Całkiem sporo moim zdaniem. Trochę szkoda tych pierwszych 32KB – tylko 4B są wykorzystane, reszta się marnuje. Może ktoś wie, jak poprawić ten wpis w pliku linkera, ja się na tym nie znam.

    tataOlka · 14/03/2021 o 23:30

    Drobna, porządkowa korekta. Dopiero podejrzałem zawartość pamięci flash i na jej początku siedzi nie jeden wektor, ale cała tablica wektorów przerwań (u mnie 392 bajty). Marnuje się więc nieco mniej pamięci 🙂

amilo_pa · 27/02/2021 o 00:21

Mam F103C8, i chciałbym zapisać tablicę składającą się z 7988 Bajtów ( niecałe 8kB – czyli muszę wykorzystać 8 stron) tylko jeden raz, podczas flashowania procka. Następnie w kodzie chciałbym ją odczytywać i np wysyłać na UARTa. W związku z tym adres startowy muszę ustawić na: #define EEPROM_START_ADDRESS ADDR_FLASH_PAGE_119, gdzie ADDR_FLASH_PAGE_119 ((uint32_t)0x0801DC00).
I teraz zakładam, że musze zmultiplikować makra: PAGEx_BASE_ADDRESS, PAGEx_END_ADDRESS, PAGEx, aż do 8, a także zmodyfikować funkcje w eeprom.c do odczytu i zapisu tych wszystkich stron? -> dobrze zakładam ?

Krzysiek · 06/02/2021 o 16:09

Obsługa emulacji EEPROM na FLASH na Nucleo-F411RE sprawiła mi wiele kłopotów.
Po dodaniu plików eeprom.c i eeprom.h do istniejącego projektu i włączeniu emulacji na sektorze 2 i 3 zaczęły dziać się cuda.
Przy wyświetlaniu tekstów przez uart2 zaczynały pojawiać się krzaczki, program się zawieszał, resetował RTC, przestawał działać guzik od resetu.
Zauważyłem prawidłowość, wyłączenie EE_Init() powoduje, że program działa prawidłowo.
Przejrzałem kod, nie zauważyłem podejrzanych fragmentów. Zauważyłem, że funkcja EE_Init() zawsze formatuje drugi sektor. Zacząłem podejrzewać, że problem jest w zasilaniu z USB, które daje może za mały prąd do wyczyszczenia sektora. Przełączyłem na zasilanie zewnętrzne. Poprawy nie było żadnej.
Po wielu godzinach znalazłem rozwiązanie.
Mój program wykorzystuje: GPIO, DMA, USART1, USART2, ADC, TIM10, TIM11, SPI2 i RTC.
Przestrzeń FLASH z sektorów 2 i 3 jest przestrzenią przeznaczoną również na kod programu.
Jeśli program jest większy niż 32KB to funkcja EE_Init niszczy kod programu.
Dobrze to widać, gdy wyłączymy korzystanie z emulacji eeprom, załadujemy program i za pomocą ST-Link’a odczytamy drugi sektor (od adresu 0x08008000). Zwykle mamy w kodzie jakieś wyświetlane teksty i pięknie je tam widać.
Przełączenie na korzystanie z sektorów 6 i 7 powoduje, że emulacja eeprom przestała niszczyć kod programu. Program zaczął działać prawidłowo.
Wnioski:
– Włączenie emulacji eeprom na sektorach 2 i 3 skutkuje, że nasz program nie może być większy od 32KB.

– Włączenie emulacji eeprom na sektorach 6 i 7 skutkuje, że nasz program nie może być większy od 256KB.
– Na emulację eeprom wykorzystamy wówczas 256KB pamięci flash

Nasuwa się rozwiązanie: jeśli chcielibyśmy korzystać z sektorów 2 i 3 to podczas linkowania obiektów należałoby “przeskakiwać” ten obszar. Ja tego zrobić niestety nie potrafię.

W repo jest mały błąd:
plik: https://github.com/lamik/EEPROM-emulation-STM32F4-HAL/blob/master/Inc/eeprom.h
jest:
#define PAGE1_END_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + (2 * PAGE_SIZE – 1))-1)
powinno być:
#define PAGE1_END_ADDRESS ((uint32_t)(EEPROM_START_ADDRESS + (2 * PAGE_SIZE – 1)))
W artykule jest poprawnie 🙂

    Mateusz Salamon · 07/02/2021 o 18:49

    Nic dziwnego, że dzieją Ci się cuda, jak chcesz nadpisywać EEPROMem swój własny program 😉
    Trzeba zmienić konfigurację emulacji EEPROmu właśnie przez te defeine’y. Do dobrych wniosków doszedłeś 😉 Albo EEPROM, albo program.

Daniel · 29/10/2020 o 16:07

Hej, a co w przypadku gdybym chciał zapamiętywać tylko 1 zmienną? Czy wtedy też trzeba tworzyć tablicę? Jak to wtedy rozwiązać?

    Mateusz Salamon · 29/10/2020 o 17:38

    Tak, też tak trzeba, bo musisz kasować całą stronę tak czy siak.

SaS · 22/05/2020 o 14:43

Brakuje mi w tym artykule informascji o “wariacjach” w ich HAL-u. Raz ma to byc adres komórki pamięci innym razem adres strony. Cóż, poczżtkujacy polegnie gdy wezmnie przykład z netu (niczym arduinowiec) i spróbuje go użyć (zwłaszcza jak nie użyje debugera).
HAL STM, sprowadza się do bibliotek Arduino. Nie jest tak skopany jak arduino, ale niestety, bywa, że sprawia duże problemy 🙁

    Mateusz Salamon · 28/05/2020 o 12:37

    Heh nie miałem z tym problemu to nie opisałem 😀 W którym dokładnie miejscu jest taka rozbieżność?

Aleksander · 01/04/2020 o 22:23

Dziękuje za wpis z którego skorzystałem.
Mam tylko pytanie dlaczego stworzyłeś tablicę uint16_t VirtAddVarTab[NB_OF_VAR];
nie trzeba z niej korzystać, można
EE_WriteVariable(VarIndex+1, VarDataTab[VarIndex])
i tak
EE_ReadVariable(VarIndex+1, &VarDataTabRead[VarIndex])
u mnie to zadziałało,

    Mateusz Salamon · 02/04/2020 o 20:06

    W sumie to nie pamiętam jaki miałem z nią zamiar, ale tak – można bez niej działać. Taka tablica przyda się jeśli adresy zmiennych będą inne niż takie po kolei 🙂

dambo · 17/08/2019 o 15:05

Wow – 173ms na zapis? Myślałem, że to była zamiana kolejności w poprzednich wartościach – 173ms to kaaaaawał czasu. Dopisane do listy, żeby się tym pobawić jakoś dokładniej – jakbym coś ciekawego się dowiedział to dam znać.

    Mateusz Salamon · 17/08/2019 o 15:19

    Wychodzi ok 6,4 ms na jedną zmienną. Fakt, kawał czasu. Według dokumentacji ST dla F4 czas zapisu jednej zmiennej nie powinien przekroczyć 255 us przy zegarze systemowym 168 MHz. Pewnie jeszcze coś pokombinuję z tym i podzielę się wynikami w aktualizacji 🙂

dambo · 17/08/2019 o 08:52

Odczyt trwa ~5x dłużej niż zapis?

    Mateusz Salamon · 17/08/2019 o 09:23

    O kur! Zerknałemjeszcze raz w kod i miałem oznaczyć “doliny”, a nie “górki” na analizatorze. Już poprawiam 😀 dzięki za czujność! Faktycznie coś mi śmierdziało jak opisywałem wyniki 😛

Dodaj komentarz

Avatar placeholder

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *