Dlaczego nie mogę używać wskaźników zamiast tablicy w PROGMEM?

11

Obecnie zmieniam niektóre biblioteki, aby używały pamięci flash zamiast pamięci RAM do przechowywania ciągów, aby nie zabrakło SRAM w projekcie.

Niektóre ciągi w bibliotece są zadeklarowane w następujący sposób:

const char *testStringA = "ABC";

Różni się to od tego, jak zwykle to robię:

const char testStringB[] = "DEF";

Myślę jednak, że te dwa są równoważne, gdy zostaną zadeklarowane jako const i zainicjowane w deklaracji. Oba działają dobrze w kodzie.

Próbowałem przenieść je do flashowania:

const prog_char *testStringC PROGMEM = "GHI";

Potem odkryłem, że to nie działa. Po wydrukowaniu produkował gobbledegook.

Jednak zgodnie z bardziej typowym wzorem:

const prog_char testStringD[] PROGMEM = "JKL";

działa w porządku.

Widzę w demontażu:

00000068 <testStringC>:
  68:   04 01                                               ..

0000006a <_ZL11testStringD>:
  6a:   4a 4b 4c 00                                         JKL.

Jest więc jasne, że wskaźnik i PROGMEM powoduje, że łańcuch / tablica nie jest inicjowany.

Dlaczego to?

Przykładowy kod:

#include <avr/pgmspace.h>

const int BUFFER_LEN = 20;

const char *testStringA = "ABC";
const char testStringB[] = "DEF";
const prog_char *testStringC PROGMEM = "GHI";
const prog_char testStringD[] PROGMEM = "JKL";

void setup()
{
    Serial.begin(9600);
}

void loop()
{
    char buffer[BUFFER_LEN];

    Serial.println(testStringA);
    Serial.println(testStringB);

    strncpy_P(buffer, testStringC, BUFFER_LEN);
    Serial.println(buffer);

    strncpy_P(buffer, testStringD, BUFFER_LEN);
    Serial.println(buffer);

    delay(1000);

}
Cybergibbons
źródło

Odpowiedzi:

6

Cóż, na to pytanie mądrze odpowiedziano w odpowiedzi na pytanie C dotyczące przepełnienia stosu : różnice między wskaźnikiem char a tablicą .

Zasadniczo z czym deklarujesz się jako PROGMEM,

const prog_char testStringD[] PROGMEM = "JKL";

jest zarówno tablicą, jak i pamięcią, na którą wskazuje, to znaczy elementami tablicy, oba na stosie zakresu prądu. Natomiast z:

const prog_char* testStringC PROGMEM = "GHI";

deklarujesz wskaźnik PROGMEM do stałego ciągu, który może pozostać w innym miejscu w pamięci, ale nie zostanie zadeklarowany jako ciąg PROGMEM.

Chociaż tego nie testowałem, ale powinieneś spróbować zadeklarować:

const prog_char* testStringC PROGMEM = F("GHI");

aby faktycznie przydzielić wskazany ciąg w przestrzeni PROGMEM. Wydaje mi się, że powinno działać, używając F()makra Arduino , które dodaje dużo kodu typu „plateplate”, aby uzyskać taki sam wynik jak deklaracja tablicowa.

Jak powiedziano w komentarzach, jeśli nie w kontekście globalnym, PSTR()można użyć F()makra zamiast makra.

Prostsze jest lepsze: użyj deklaracji tablicowej, a nie wskaźnikowej!

Jeśli chodzi o inną odpowiedź , __flashkwalifikator to trzecie rozwiązanie ;-)

zmo
źródło
Całkowicie się zgadzam, że „prostsze jest lepsze” - tablica jest znacznie wyraźniejsza. Zawsze się interesuję, gdy coś nie jest od razu oczywiste.
Cybergibbons,
F () zwraca FlashStringHelper, który jest zasadniczo taki sam, ale użycie PSTR () działa dobrze (o ile wprowadzasz consts do funkcji).
Cybergibbons,
faktycznie, naprawdę zasugerowałem najpierw PSTR()makro, ale zmieniłem na F()przed przesłaniem, ponieważ twoje stałe są globalne w twoim Q, więc wolałem trzymać się tego, który powinien działać w obu kontekstach.
zmo
3

Co ta linia:

const prog_char *testStringC PROGMEM = "GHI";

robi to napisać kod prologu, aby skopiować znaki z łańcucha do SRAM, a następnie inicjuje wskaźnik zapisany we flashu do tej lokalizacji SRAM. Musisz załadować wskaźnik w normalny sposób, a następnie wyrejestrować wskaźnik jak zwykle.

const char *str = pgm_read_word(&testStringC);
Serial.println(str);

Ta linia:

const prog_char testStringD[] PROGMEM = "JKL";

tworzy tablicę znaków we flashu, umożliwiając dostęp do niej zgodnie z oczekiwaniami.

Ignacio Vazquez-Abrams
źródło