2016-10-05 9 views
0

Ich arbeite an einem kleinen HTTP-Server. Ich baue einen Router und da es einige Routen geben könnte, wollte ich sie in den Flash-Speicher legen, damit ich nicht den wertvollen SRAM verwenden muss. Aber entweder verstehe ich etwas nicht richtig oder etwas Seltsames passiert, da ich nicht in der Lage bin, meine gespeicherten Daten aus dem Flash zu lesen.Arduino Progmem liest verzerrte Daten

Ich habe eine Struktur, die einen Funktionszeiger und einen Char-Zeiger enthält. Ich möchte ein Array dieser Strukturen in Flash speichern und sie zurücklesen. Bei einem kleinen Debug-Druck kann ich jedoch sehen, dass ich den Char-Zeiger nicht korrekt zurücklesen kann. Es druckt Trash auf den seriellen Port.

Hier ist ein kleines Beispiel.

#include <avr/pgmspace.h> 

typedef struct { 
    void (*func)(); 
    const char *URI; 
} Route; 

void test1() { 
    Serial.println("Executed testfunc1"); 
} 

void test2() { 
    Serial.println("Executed testfunc2"); 
} 

const char route1URI[] PROGMEM = "/route1"; 
const Route route1 PROGMEM = { 
    test1, 
    route1URI 
}; 

const char route2URI[] PROGMEM = "/route2"; 
const Route route2 PROGMEM = { 
    test2, 
    route2URI 
}; 

const Route routingTable[] PROGMEM = { 
    route1, 
    route2 
}; 

void (*getRoute(char *URI))() { 
    Route *r = (Route *)pgm_read_word(routingTable + 0); 
    char *f = (char *)pgm_read_word(r->URI); 

    Serial.println(f); 

    return r->func; 
} 
void setup() { 
    Serial.begin(9600); 
    while (!Serial) { } 

    Serial.println("started setup"); 
    void (*fn)() = getRoute("sometest"); 
    // will cause errors if called 
    //fn(); 
    Serial.println("ended setup"); 
} 

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

} 

Antwort

1

Das PROGMEM ist nicht so einfach zu bedienen. Und es kann etwas vereinfacht werden:

#include <avr/pgmspace.h> 

struct Route { 
    void (*func)(); 
    const char *URI; 
}; 

void test1() { 
    Serial.println(F("Executed testfunc1")); // if you are using progmem, why not for string literals? 
} 

void test2() { 
    Serial.println(F("Executed testfunc2")); 
} 

const char route1URI[] PROGMEM = "/route1"; 
const char route2URI[] PROGMEM = "/route2"; 

const Route routingTable[] PROGMEM = { 
    {test1,route1URI}, 
    {test2,route2URI} 
}; 

void (*getRoute(char *URI))() { 
    Route r; 
    memcpy_P((void*)&r, routingTable, sizeof(r)); // read flash memory into the r space. (can be done by constructor too) 

    Serial.println((__FlashStringHelper*)r.URI); // it'll use progmem based print 
    // for comparing use: strcmp_P(URI, r.URI) 

    return r.func; // r.func is already pointer to the function 
} 

void setup() { 
    Serial.begin(57600); 
    while (!Serial) { } 

    Serial.println("started setup"); 
    void (*fn)() = getRoute("sometest"); 
    // will cause errors if called 
    //fn(); 
    Serial.print((uint16_t)test1, HEX); Serial.print(' '); 
    Serial.print((uint16_t)test2, HEX); Serial.print(' '); 
    Serial.println((uint16_t)fn, HEX); 

    Serial.println("ended setup"); 
} 

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

} 

Ich nehme an route1 und route2 könnten all die Probleme verursachen, da es für die Kopie in die routingTable verwendet wurde. Wenn Sie Elemente von routingTable wie ich initialisieren, funktioniert es viel besser. Und auch getRoute war sehr kaputt.

Wie auch immer, wenn Sie Flash-String haben, können Sie auch String str {(__FlashStringHelper*)r.URI}; verwenden und dann Operator vergleichen: str == URI:

#include <avr/pgmspace.h> 

// get size of array[] 
template<typename T, int size> int GetArrLength(T(&)[size]){return size;} 

struct Route { 
    void (*func)(); 
    const char *URI; 
}; 

void test1() { 
    Serial.println(F("Executed testfunc1")); // if you are using progmem, why not for string literals? 
} 

void test2() { 
    Serial.println(F("Executed testfunc2")); 
} 
void test3() { 
    Serial.println(F("Executed testfunc3")); 
} 

const char route1URI[] PROGMEM = "/route1"; 
const char route2URI[] PROGMEM = "/route2"; 
const char route3URI[] PROGMEM = "/route3"; 

const Route routingTable[] PROGMEM = { 
    {test1,route1URI}, 
    {test2,route2URI}, 
    {test3,route3URI} 
}; 

void (*getRoute(char *URI))() { 
    for (int8_t i = 0; i < GetArrLength(routingTable); ++i) { 
    Route r; 
    memcpy_P((void*)&r, routingTable+i, sizeof(r)); // read flash memory into the r space. (can be done by constructor too) 

    String uri {(__FlashStringHelper*)r.URI}; 
    if (uri == URI) { 
     return r.func; // r.func is already pointer to the function 
    } 
    } 

    return nullptr; 
} 

void setup() { 
    Serial.begin(57600); 
    while (!Serial) { } 

    Serial.println("started setup"); 
    void (*fn)() = getRoute("/route3"); 
    // will cause errors if called 
    //fn(); 
    Serial.print((uint16_t)test1, HEX); Serial.print(' '); 
    Serial.print((uint16_t)test2, HEX); Serial.print(' '); 
    Serial.print((uint16_t)test3, HEX); Serial.print(' '); 
    Serial.println((uint16_t)fn, HEX); 

    Serial.println("ended setup"); 
} 
0
char *f = (char *)pgm_read_word(r->URI); 
Serial.println(f); 

f ist ein Zeiger auf ein Zeichen-Array in PROGMEM, aber Serial.println das nicht wissen! Am Ende versucht es, die Zeichenfolge aus dem RAM zu lesen, wo es nicht ist.

Die Arduino Serial-Bibliothek scheint keine Zeichenfolgen in PROGMEM zu unterstützen. Sie müssen die Zeichenfolge, die jeweils ein Zeichen druckt, schrittweise durchlaufen, eine andere Bibliothek verwenden oder die Zeichenfolge im RAM speichern.

0

Wie @KIIV zeigte, ist es besser Route direkt in der Deklaration von routingTable angeben. Als alternative Lösung, könnten Sie die Struktur Route zu

typedef struct { 
    void (*func)(); 
    char URI[16]; //adjust the size to your need 
} Route; 

Auf diese Weise neu definiert, sowohl URI und function Adresse aus dem Flash-Lesen kann durch einzigen Aufruf memcpy_P erfolgen. Die vollständigen Codes:

typedef struct { 
    void (*func)(); 
    char URI[16]; //adjust the size to your need 
} Route; 

void test1() { 
    Serial.println("Executed testfunc1"); 
} 

void test2() { 
    Serial.println("Executed testfunc2"); 
} 

const Route routingTable[] PROGMEM = { 
    {test1, "/route1"}, 
    {test2, "/route2"} 
}; 

void (*getRoute(char *URI, int idx))() { 
    Route r; 
    memcpy_P(&r, &routingTable[idx], sizeof(Route)); 

    Serial.print(idx); Serial.println(". -----------------------------"); 
    Serial.print("Route: "); Serial.println(r.URI); 
    Serial.print("fn address: "); Serial.println((uint16_t)r.func, HEX); 
    Serial.print("test1 address: "); Serial.println((uint16_t)test1, HEX); 
    Serial.print("test2 address: "); Serial.println((uint16_t)test2, HEX); 

    return r.func; 
} 

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

    Serial.println("started setup"); 
    void (*fn)(); 

    const int n = sizeof(routingTable)/sizeof(Route); 
    for (int i = 0; i < n; i++) { 
     fn = getRoute("sometest", i); 
     fn(); 
    } 
    Serial.println("ended setup"); 
} 

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

derzeit unterstützt meine URI bis zu 254 Zeichen, also wäre dies wirklich ein Schritt zurück. Außerdem würde dies Speicher für jede URL, die weniger als 15 Zeichen lang ist, verschwenden. –

+0

Sie haben es in der Frage nicht erwähnt, dass Ihr URI so lange sein wird. Sie sind richtig, dass dieser Ansatz Speicher verschwendet, aber wenn die Reihenfolge mehrere Bytes ist, denke ich, dass es akzeptabel ist. In deinem Fall ist @KIIV Antwort die bessere Lösung. – putu

Verwandte Themen