1.8 Kurs „Arduino Łopatologicznie” Jak zapisywać w Pamięci EEPROM Arduino?

Jak zdążyliśmy już zauważyć wartości przypisywane do zmiennych w trakcie wykonywania się programu po odłączeniu zasilania zostają utracone. Jeśli mamy z góry ustalone jakieś wartości możemy zapisać je do stałych i nie ma problemu, co jednak gdybyśmy chcieli zapamiętywać wprowadzone przez użytkownika hasło, lub najwyższy wynik w naszej grze?

Z pomocą przychodzi nam pamięć EEPROM, układ ATMega328 będący sercem naszego Arduino posiada kilobajt takiej pamięci i jest ona na tyle trwała, że jej zawartość powinna dać się odczytać przez wiele lat.

Oficjalne sposoby obsługi tej pamięci są bardzo nieintuicyjne i umożliwiają jednoczesny odczyt lub zapis tylko pojedynczego bajta pamięci.

Dzisiaj zaprezentuję wam jak w przy pomocy biblioteki AVR EEPROM zapisywać różne typy danych.

pamięć EEPROM

Skąd pobrać bibliotekę AVR EEPROM?

Biblioteka ta jest dołączona do Arduino IDE więc nie ma konieczności jej instalowania, wystarczy dodać ją na górze naszego szkicu poleceniem #include <avr/eeprom.h>

Napiszmy teraz kod zapisujący zmienną int do pamięci i potem wytłumaczę wam jak to dokładnie działa:

#include <avr/eeprom.h>


void setup() {
Serial.begin(9600);
int zmienna1 = 10;
int zmienna2 = 20;
int zmienna3 = 50;

eeprom_write_block(&zmienna1,0,2);
eeprom_write_block(&zmienna2,2,2);
eeprom_write_block(&zmienna3,4,2);

int wypisz = 0;

Serial.println("Wypisanie zmiennej: ");
Serial.println(wypisz);

Serial.println("Wypisanie zmienna1: ");
eeprom_read_block(&wypisz,0,2);
Serial.println(wypisz);

Serial.println("Wypisanie zmienna2: ");
eeprom_read_block(&wypisz,2,2);
Serial.println(wypisz);

Serial.println("Wypisanie zmienna3: ");
eeprom_read_block(&wypisz,4,2);
Serial.println(wypisz);

}

void loop() {
  // put your main code here, to run repeatedly:

}

Polecenie, które zapisuje zmienną int do pamięci EEPROM to eeprom_write_block(zmienna1, 0 , 2);  w nawiasie pierwszy argument to &zmienna1 – funkcja jako pierwszy argument oczekuje odwołania do adresu pamięci gdzie przechowujemy naszą zmienną, znak spełnia rolę wskaźnika pamięci i odwołuje się do miejsca gdzie została zapisana zmienna1. Kolejny argument w tym wypadku 0 to początkowy bajt pamięci w którym rozpoczniemy zapis, a liczba 2 oznacza, że zmienna zajmie nam 2 bajty pamięci. Podsumowując z 1024 bajtów zajęliśmy już miejsca 0 oraz 1, aby umieścić kolejny int w naszym przypadku zmienna2 musimy zagospodarować inne komórki pamięci. Zapisujemy kolejną zmienną w pamięci na miejscu 2 oraz 3. Gdybyśmy napisali eeprom_write_block(&zmienna2, 0 , 2); nadpisalibyśmy zmienna1

Aby odczytać coś z pamięci korzystamy z bliźniaczego polecenia:

eeprom_read_block(&wypisz, 0 , 2); tutaj wskazujemy na miejsce w pamięci gdzie będziemy zapisywać odczytaną z pamięci zmienną, w naszym przypadku wartość przypisujemy zmiennej wypisz.

Sprawdź we własnym zakresie co się stanie jeśli odczytasz bajty pamięci po nadpisaniu zmiennych oraz odczytaj i wypisz wartości nieparzystych bajtów.

Zapisywanie do EEPROM wartości float.

Aby zapisać do pamięci EEPROM zmienne typu float musimy zmienić ilość nadpisywanych bajtów pamięci z 2 do 4 ponieważ ten typ danych potrzebuje więcej przestrzeni.

Wykonamy to modyfikując nasze polecenie podając jako ostatni argument funkcji, który oznacza ilość zajmowanego miejsca 4 bajty zamiast 2, będzie to wyglądać następująco:

float zmiennaPrzecinkowa = 15.70;

eeprom_write_block(&zmiennaPrzecinkowa, 0 , 4);

Podobnie zmodyfikujemy jej odczyt:

float wypisz = 0.0;

eeprom_read_block(&wypisz, 0 , 4);

Przechowywanie w pamięci EEPROM łańcucha znaków – tekstu.

Dzięki temu, że znasz już mechanizm działania tablicy omówiony na poprzedniej lekcji łatwiej będzie zrozumieć jak działa zapis do pamięci znaków tekstowych. Należy mieć na uwadze, że działamy na bajtach, zatem jeden bajt będzie odpowiadał jednej literze. Aby wpisać tekst do pamięci przyporządkujemy jednemu znakowi jedną komórkę naszej tablicy i ładujemy do pamięci EEPROM po jedynm znaku zapisanym jako typ char czyli element tablicy ASCII, o typie zmiennej char wspominaliśmy na lekcji 2 jeśli chcesz wiedzieć jaką wartość przyjmuje ta zmienna cofnij się do tej lekcji klikając w link -> KLIK

Aby łatwiej było wytłumaczyć jak przebiega proces zapisu znaków do pamięci pokażę kod, który to realizuje a potem krok po kroku omówimy co z czym się je.

#include <avr/eeprom.h>

//tworzymy stałą wyznaczającą maksymalną długość hasła
const int maxDlugoscHasla = 10;

//tworzymy tablice do przechowywania znaków o maksymalnej dlugosci 10
char hasloTablica [maxDlugoscHasla];

void setup() {
Serial.begin(9600);
//odczytujemy i przypisujemy aktualne hasło z pamięci do tablicy
eeprom_read_block(&hasloTablica, 0 , maxDlugoscHasla);


}

void loop() {

Serial.println("Aktualne hasło: ");
Serial.println(hasloTablica);
delay(1500);
Serial.println("Wpisz nowe hasło: ");
//czekamy aż bufor będzie gotowy otrzymać znaki
while (!Serial.available() ) { };

//sprawdzamy jak długie jest hasło
int iloscZnakow = Serial.readBytesUntil('\n' , hasloTablica, maxDlugoscHasla);

hasloTablica[iloscZnakow] = '\0';
//zapisujemy nowe hasło w miejsce pamięci gdzie było poprzednie
eeprom_write_block(hasloTablica, 0 , maxDlugoscHasla);
Serial.println("Nowe zapisane hasło to:");
Serial.println(hasloTablica);

}

Tworzymy stałą typu int maxDlugoscHasla która będzie odpowiadała maksymalnej liczbie zajętych bajtów. Kolejno tworzymy tablicę dla typu danych char o nazwie hasloTablica , która przechowuje każdy znak naszego hasła w osobnym indeksie.

Kolejno odczytujemy aktualnie zapisane dane z pamięci od 0 do 19 i wypisujemy je w serial monitorze.

Wyświetlamy zachętę do podania nowego hasła i pętlą while, która wykonuje się pusta w nieskończoność, aż warunek w nawiasie zwróci logiczną wartość false. W naszym przypadku sprawdzamy czy w buforze czekają na nas dane, negacja wykrzyknikiem oznacza tyle co: wykonuj kod (w pustych nawiasach klamrowych czyli nic nie rób) póki !Serial.available() czyli bufor jest pusty. Jeśli zapełnimy bufor jakimiś znakami pęlta się skończy i zaczniemy liczyć wpisane znaki. Łańcuchy znaków mają to do siebie, że ich koniec zawsze oznaczony jest specjalnym znakiem '\0′ jest to znak zarezerwowany specjalnie do oznaczenia zakończenia tablicy znaków i odczytujemy funkcją Serial.readBytesUntil(character, buffer, length), która podaje liczbę odczytanych znaków aż napotka znak przekazany za pomocą parametru character w naszym przypadku '\0′, z tablicy znaków o nazwie hasloTablica, o maksymalnej długości maxDlugoscHasla.

Wtedy poznamy ile znaków liczy nasze nowe hasło. Do tablicy znaków przechowującej nasze hasło na ostatnią pozycję dopisujemy znak końca tablicy hasloTablica[iloscZnakow] = '\0′; i w takiej postaci zapisujemy na nowo do naszej pamięci.

Chodź na pierwszy rzut oka cały proces może się wydawać lekko zagmatwany to wierzę, że jeśli na spokojnie pare razy przeanalizujesz powyższy kod wszystko stanie się jasne.

Kasowanie pamięci EEPROM

Należy pamietać, że EEPROM pozwala nam na zapis około 100 000 razy. Dla przeciętnego użytkownika taka liczba wydaję się wystarczająca lecz jeśli ktoś bardzo intensywnie nadpisuje dane w dłuższym okresie może wyeksploatować pamięć w swoim Arduino.

Jeśli dużo zapisujemy na EEPROM przy kolejnych projektach mogą pojawić się śmieci, których nie chcemy, bądź mamy zapisane tam dane, które nie chcielibyśmy aby wyciekły jak nasze hasło.

Wtedy najlepiej wyczyścić całą pamięć. Można to zrobić w prosty sposób nadpisując każdy bajt wartością 0.

Aby to zrobić należy wykonać następujący kod:


#include <avr/eeprom.h>

void setup() {
Serial.begin(9600);
Serial.println("Wymazywanie EEPROM rozpoczęte");
for (int i=0; i <= 1023; i++){
  int a = 0;
  eeprom_write_block(&a,i,1);
  }
Serial.println("Wymazywanie EEPROM zakończone");
 
}

void loop() {
  // put your main code here, to run repeatedly:

}

Jeśli pojawią się jakieś wątpliwości zachęcam do zadawania pytań w komentarzach pod wpisem.

Pamiętaj aby polubić nasz profil na facebooku aby być na bieżąco a kolejnymi lekcjami.

kliknij aby przejść do naszego profilu na Facebooku

1 Comments

Zostaw komentarz

  1. Michał
    Odpowiedz

    Witam.
    U mnie nie działa i kompletnie nie mam pomysłu dlaczego.
    tylko ze chciałem żeby program czekał na wpisanie czegoś w serial monitorze.
    macie pomysł czemu nie działa prawidłowo?

    int wzrost; // deklaruje zmienną do przechowania wartosci wzrostu
    int waga; // deklaruje zmienną do przechowania wartosci wagi

    float wynik; //Zmienna do wyliczenia BMI
    String imie; //deklaruje zmienna do zapamietania imienia

    String powitanie = „Witam Cie serdecznie, napisz prosze jak masz na imie”; //zmienna do skrocenia kodu

    //char coto;
    void setup()
    {
    Serial.begin(9600);

    }

    void loop()
    {
    Serial.println(powitanie); // wyswietla powitanie
    while(Serial.available() == 0) {} //czeka w pętli na wprowadzenie danych z konsoli
    imie=Serial.readString(); //zapisuje wprowadozne dane do pamięci do zmiennej imie Jak dobrze rozumiem to po jego zapisaniu czyści bufor ?
    Serial.print(imie);
    Serial.println(” podaj prosze swoja wage”);

    while(Serial.available() == 0) {} // czeka na dane
    waga=Serial.parseInt();
    Serial.print(imie);
    Serial.println(” podaj prosze swoj wzrost”);
    Serial.flush(); // niby ma czyścić bufor a nie czyści
    while(Serial.available() == 0) {} // ma czekac na dane ale po wprowadzeniu wagi automatycznie przypisuje wartosc 0 dla wzrostu
    wzrost=Serial.parseInt();

    float wynik=((waga/(wzrost*wzrost))*100); // nie wiem czemu ale nie wylicza mi tej awrtosci wyrzuca mi zawsze 0.00

    Serial.println(” twoja waga to”);
    Serial.println(waga);
    Serial.println(„przy wzroscie”);
    Serial.println(wzrost);
    Serial.print(„daje BMI =: „);
    Serial.println(wynik);

    delay(1000);

Odpowiedz

Twój adres e-mail nie zostanie opublikowany