Co mogę zrobić, jeśli zabraknie mi pamięci Flash lub SRAM?

28

Zgodnie z dokumentacją Arduino, ATmega328 ma 32 KB pamięci Flash dla bootloadera + przesłanego szkicu i tylko 2 KB SRAM dla danych wykonawczych. ATmega2560 ma znacznie więcej, w sumie odpowiednio 256 KB i 8 KB.

W obu przypadkach limity te wydają się raczej niewielkie, zwłaszcza w porównaniu z urządzeniami konsumenckimi o podobnych rozmiarach, takimi jak smartfony.

Co możesz zrobić, jeśli zabraknie? Np. Jeśli twój szkic jest zbyt duży lub potrzebujesz przetworzyć dużo danych (takich jak łańcuchy) w czasie wykonywania? Czy jest jakiś sposób na rozszerzenie Flasha lub SRAM?

Peter Bloomfield
źródło
Spróbuj użyć optymalizacji zmiennej. Omówiono tutaj
TheDoctor
1
Użyj odpowiednich zakresów zmiennych, jeśli jeszcze tego nie zrobiłeś, kompilator zoptymalizuje użycie pamięci RAM, gdy zmienne nie są używane.
jippie
1
Możesz użyć Teensy 3.2 (z oprogramowaniem Teensyduino), który byłby porównywalny z Arduino. Biorąc pod uwagę, że kończy Ci się pamięć RAM / PROGMEM, oznaczałoby to, że prawdopodobnie próbujesz pchnąć Arduino. Teensy 3.2 nie jest dużo droższy. ale ma: 32-bitowy, 72 MHz (wtf !?) 256KB FLASH i 64KB RAM i 3x UART. Nie więcej pamięci programu jak ATmega2560, ale powinno wystarczyć zwiększenie pamięci RAM i prędkości.
Paul

Odpowiedzi:

30

Optymalizacja
Programowanie niskopoziomowe dla systemów wbudowanych różni się zupełnie od programowania dla urządzeń ogólnego przeznaczenia, takich jak komputery i telefony komórkowe. Wydajność (pod względem prędkości i przestrzeni) jest znacznie ważniejsza, ponieważ zasoby są na wagę złota. Oznacza to, że pierwszą rzeczą do zrobienia, jeśli zabraknie Ci miejsca, jest sprawdzenie, które części kodu możesz zoptymalizować.

Jeśli chodzi o zmniejszenie wykorzystania przestrzeni programowej (Flash), rozmiar kodu może być dość trudny do optymalizacji, jeśli nie masz doświadczenia lub jeśli jesteś bardziej przyzwyczajony do programowania na komputerach stacjonarnych, które zwykle nie potrzebują tej umiejętności. Niestety, nie istnieje podejście oparte na „magicznej kuli”, które działałoby we wszystkich sytuacjach, chociaż pomaga, jeśli poważnie zastanawiasz się, co naprawdę musi mieć szkic . Jeśli funkcja nie jest potrzebna, wyjmij ją.

Czasami pomocne jest również określenie, gdzie wiele części kodu jest takich samych (lub bardzo podobnych). Możesz je skondensować w funkcje wielokrotnego użytku, które można wywoływać z wielu miejsc. Należy jednak pamiętać, że czasami próba uczynienia kodu zbyt wielokrotnego użytku w rzeczywistości powoduje, że jest on bardziej szczegółowy. Jest to trudna równowaga do uderzenia, która zwykle towarzyszy ćwiczeniom. Pomocne może być poświęcenie czasu na sprawdzenie, jak zmiany kodu wpływają na dane wyjściowe kompilatora.

Optymalizacja danych wykonawczych (SRAM) jest nieco łatwiejsza, gdy się do tego przyzwyczaisz. Bardzo częstym problemem dla początkujących programistów jest używanie zbyt dużej ilości danych globalnych. Wszystko zadeklarowane w zasięgu globalnym będzie istniało przez cały okres istnienia szkicu, i to nie zawsze jest konieczne. Jeśli zmienna jest używana tylko w obrębie jednej funkcji i nie musi trwać między wywołaniami, należy uczynić ją zmienną lokalną. Jeśli wartość musi być dzielona między funkcje, zastanów się, czy możesz przekazać ją jako parametr zamiast uczynić ją globalną. W ten sposób będziesz używał SRAM dla tych zmiennych tylko wtedy, gdy ich potrzebujesz.

Kolejnym zabójcą używanym przez SRAM jest przetwarzanie tekstu (np. Używanie Stringklasy). Ogólnie rzecz biorąc, w miarę możliwości należy unikać wykonywania operacji na łańcuchach. To ogromne wieprze pamięci. Na przykład, jeśli wysyłasz dużo tekstu do numeru seryjnego, użyj wielu wywołań Serial.print()zamiast zamiast konkatenacji ciągów. Spróbuj również zmniejszyć liczbę literałów łańcuchowych w kodzie, jeśli to możliwe.

Jeśli to możliwe, unikaj rekurencji. Za każdym razem, gdy wykonuje się wywołanie rekurencyjne, poziom stosu jest wyższy. Zamiast tego przekształć swoje funkcje rekurencyjne na iteracyjne.

Użyj EEPROM
EEPROM służy do długoterminowego przechowywania rzeczy, które zmieniają się tylko sporadycznie. Jeśli potrzebujesz użyć dużych list lub tabel przeglądowych stałych danych, rozważ wcześniejsze zapisanie ich w EEPROM i wyciąganie tylko tego, czego potrzebujesz.

Oczywiście pamięć EEPROM ma jednak dość ograniczony rozmiar i prędkość oraz ma ograniczoną liczbę cykli zapisu. Nie jest to świetne rozwiązanie dla ograniczeń danych, ale może wystarczyć, aby zmniejszyć obciążenie pamięci Flash lub SRAM. Jest również całkiem możliwe połączenie z podobną pamięcią zewnętrzną, taką jak karta SD.

Rozszerzenie
Jeśli wyczerpałeś wszystkie inne opcje, rozszerzenie może być możliwe. Niestety, rozszerzenie pamięci Flash w celu zwiększenia przestrzeni programu nie jest możliwe. Jednakże, to jest możliwe, aby rozwinąć SRAM. Oznacza to, że możesz być w stanie zmienić swój szkic, aby zmniejszyć rozmiar kodu kosztem zwiększenia rozmiaru danych.

Zdobywanie większej ilości pamięci SRAM jest w rzeczywistości dość proste. Jedną z opcji jest użycie jednego lub więcej układów 23K256 . Są one dostępne za pośrednictwem SPI, a biblioteka SpiRAM pomaga ci z nich korzystać. Uważaj tylko, że działają przy napięciu 3,3 V, a nie 5 V.

Jeśli używasz Mega, możesz alternatywnie uzyskać tarcze rozszerzające SRAM od Lagrangian Point lub Rugged Circuits .

Peter Bloomfield
źródło
1
Możesz również przechowywać stałe dane w pamięci programu, a nie w pamięci SRAM, jeśli masz problemy z pamięcią SRAM i wolną pamięcią programu. Zobacz tutaj lub tutaj
Connor Wolf
1
Inną świetną alternatywą dla EEPROM jest karta SD. Zajmuje kilka portów IO, ale jeśli potrzebujesz dużej ilości miejsca, powiedzmy na dane mapy lub podobne, możesz łatwo wymienić i edytować za pomocą niestandardowego programu na komputerze.
Anonimowy pingwin
1
Ludzie nie powinni być zachęcani do korzystania z SPI SRAM lub rozszerzeń pamięci RAM, jeśli brakuje im pamięci. To tylko strata pieniędzy. Wybór większego MCU byłby tańszy. Poza tym wydajność może być bardzo słaba. Najpierw należy dokonać szacunku typu ballpark: jeśli szacowane użycie pamięci RAM jest zbyt bliskie limitu, wówczas wybierasz niewłaściwą płytę / mikrokontroler / platformę programistyczną. Jasne, dobre użycie (przechowywanie ciągów we flashu) i optymalizacja (unikanie korzystania z niektórych bibliotek) mogą być prawdziwymi zmieniaczami gry. Jednak w tym momencie nie widzę korzyści z korzystania z platformy Arduino Software.
następny hack
24

Kiedy prześlesz swój kod do Arduino, powiedz na przykład Uno, powie ci ile bajtów zużywa z dostępnych 32K. Tak dużo masz pamięci flash (pomyśl o dysku twardym komputera). Podczas gdy twój program działa, używa tak zwanej SRAM i jest go znacznie mniej.

Czasami zauważysz, że twój program zachowuje się dziwnie w miejscu, którego nawet nie dotykałeś od dłuższego czasu. Możliwe, że ostatnie zmiany spowodowały brak pamięci (SRAM). Oto kilka wskazówek, jak zwolnić część pamięci SRAM.

Przechowywanie ciągów we Flashu zamiast SRAM.

Jedną z najczęstszych rzeczy, które widziałem, jest brak pamięci w układzie, ponieważ jest za dużo długich łańcuchów.

Użyj tej F()funkcji, gdy używasz ciągów znaków, aby były one przechowywane we Flashu zamiast w pamięci SRAM, ponieważ masz ich znacznie więcej.

Serial.println(F("This string will be stored in flash memory"));

Użyj odpowiednich typów danych

Możesz zapisać bajt, przełączając z int(2 bajty) na byte(1 bajt). Bez znaku bajt da ci 0-255, więc jeśli masz liczby, które nie przekraczają 255, zapisz bajt!

Skąd mam wiedzieć, że kończy mi się pamięć?

Zwykle obserwujesz, jak twój program zachowuje się dziwnie i zastanawiasz się, co poszło nie tak ... Nie zmieniłeś niczego w kodzie w pobliżu punktu, w którym się psuje, więc co daje? Brakuje pamięci.

Istnieje kilka funkcji informujących o ilości dostępnej pamięci.

Dostępna pamięć

sachleen
źródło
Czy wiesz, czy F()jest to funkcja specyficzna dla Arduino, czy jest w bibliotekach AVR? Możesz PROGMEM const ...również wspomnieć o tym .
jippie
Możesz również użyć struktur bitowych, aby dodatkowo zmniejszyć przestrzeń używaną przez zmienne 5eg, jeśli masz do czynienia z dużą liczbą boolowską).
jfpoilpret
17

Oprócz tego, co powiedzieli inni (na co się całkowicie zgadzam), radziłbym przeczytać ten artykuł adafruitowy o pamięci; jest dobrze napisany, wyjaśnia wiele rzeczy na temat pamięci i dostarcza wskazówek, jak ją zoptymalizować.

Pod koniec czytania myślę, że uzyskałbyś dość kompletną odpowiedź na swoje pytanie.

Podsumowując, masz 2 możliwe cele optymalizacji (w zależności od tego, gdzie znajdują się problemy z pamięcią):

  • Flash (tj. Pamięć programu); w tym celu możesz:
    • usuń martwy kod (np. dowolny kod, który jest zawarty, ale nieużywany) i nieużywane zmienne (to również pomaga w SRAM)
    • oddzielić zduplikowany kod
    • usuń bootloader całkowicie (możesz zyskać od 0,5 K dla UNO do 2 lub 4K dla innych modeli Arduino); ma to jednak pewne wady
  • SRAM (tj. Dane stosu, sterty i danych statycznych); w tym celu możesz:
    • usuń nieużywane zmienne
    • zoptymalizuj rozmiar każdej zmiennej (np. nie używaj długich -4 bajtów- jeśli potrzebujesz tylko int -2 bajtów)
    • użyj odpowiedniego zakresu dla swoich zmiennych (i preferuj stos zamiast danych statycznych, jeśli to możliwe)
    • zmniejszyć rozmiar buforów do ścisłego minimum
    • przenieś stałe dane do PROGMEM (tzn. twoje dane statyczne pozostaną w pamięci Flash i nie zostaną skopiowane do SRAM na początku programu); dotyczy to również ciągów ciągłych, dla których można użyć F()makra)
    • unikaj alokacji dynamicznej, jeśli nie jest to absolutnie konieczne; unikniesz rozdrobnionego stosu, który może się nie kurczyć nawet po zwolnieniu pamięci

Opisano także dodatkowe podejście do zmniejszenia zużycia pamięci SRAM (ale rzadko stosowane, ponieważ jest nieco ciężkie podczas kodowania i niezbyt wydajne), polega na użyciu pamięci EEPROM do przechowywania danych zbudowanych przez program, ale nie jest używane później, gdy pewne warunki występują, gdy dane mogą zostać ponownie załadowane z EEPROM.

jfpoilpret
źródło
1
Usuwanie martwego kodu - kompilator naprawdę dobrze radzi sobie z tym za ciebie - nie zrobi to żadnej różnicy, jeśli masz dużo kodu, który nigdy nie jest wywoływany. Jeśli przypadkowo zadzwonisz do kodu, którego nie potrzebujesz, to oczywiście jest inaczej.
dethSwatch
9

W przypadku braku miejsca na dysku są dwie rzeczy do zrobienia:

  • Jakoś „zoptymalizuj” swój kod, aby wymagał mniej miejsca; lub przynajmniej zużywa mniej miejsca, z którego zabrakło Ci miejsca (i zużywa więcej miejsca, które wciąż masz dużo). Lub,
  • Dodaj więcej miejsca.

Istnieje wiele wskazówek online, jak zrobić to pierwsze (a dla zdecydowanej większości rzeczy, które ludzie robią z Arduino, wbudowana pamięć jest więcej niż wystarczająca po „optymalizacji”). Więc skupię się na drugim:

Istnieją 3 rzeczy, które zużywają flash lub SRAM; każdy potrzebuje nieco innego podejścia do dodawania pamięci:

  • zmienne przechowywanie: możliwe jest rozszerzenie SRAM, jak już wskazała sachleen. SRAM, FRAM i NVSRAM są odpowiednie dla szybko zmieniających się zmiennych. (Chociaż w zasadzie można użyć pamięci flash do przechowywania zmiennych, musisz jednak martwić się o zużycie lampy błyskowej). SPI (protokół szeregowy) jest najłatwiejszy do połączenia z Arduino. Biblioteka SpiRAM współpracuje z Microchip 23K256 szeregowym układzie SRAM. Układ szeregowy FRAM Ramtron FM25W256 (obecnie należący do Cypress) również korzysta z SPI. Cypress CY14B101 NVSRAM wykorzystuje również SPI. Itp.

  • ciągłe dane, które muszą znajdować się przy następnym włączeniu zasilania: jest to prawie tak proste, jak rozszerzenie SRAM. Istnieje wiele zewnętrznych urządzeń pamięci EEPROM, FRAM, NVSRAM i FLASH . Obecnie najniższy koszt w przeliczeniu na MB to karty flash SD (dostępne za pośrednictwem SPI). Ramtron FM25W256 (patrz wyżej), Cypress CY14B101 (patrz wyżej) itp. Mogą również przechowywać stałe dane. Wiele osłon rozszerzających zawiera gniazdo kart SD, a kilka bibliotek i samouczków obsługuje odczytywanie i zapisywanie na kartach (flash) SD. (Nie możemy do tego użyć SRAM, ponieważ SRAM zapomina o wszystkim, gdy kończy się zasilanie).

  • kod wykonywalny: Niestety, rozszerzenie pamięci Flash Arduino w celu zwiększenia przestrzeni programowej nie jest możliwe. Jednak programiści zawsze mogą refaktoryzować szkic, aby zmniejszyć rozmiar kodu kosztem zwiększenia rozmiaru danych i spowolnienia jego działania. (Teoretycznie można posunąć się do przetłumaczenia całego szkicu na jakiś interpretowany język, zapisać tę wersję szkicu na karcie SD, a następnie napisać tłumacza dla tego języka, który działa na Arduino, aby pobrać i wykonać instrukcje z Karta SD - dalej na Arduino , tłumacz BASIC, tłumacz Tom Napier Picaro, język specyficzny dla aplikacji itp.).

David Cary
źródło