2016-10-05 138 views
0

我正在一个小型的HTTP服务器上工作。我正在建立一个路由器,因为可能有很多路由,所以我想把它们放到闪存中,这样我就不必使用有价值的SRAM。但是,无论我是否正确地理解某些内容或发生了一些奇怪的事情,因为我似乎无法从闪存中读回存储的数据。Arduino progmem读取乱码数据

我有一个结构,其中包含一个函数指针和一个字符指针。我想将这些结构的数组存储到闪存中并将其读回。但是,通过一个小的调试打印,我可以看到我无法正确读回字符指针。它将垃圾打印到串口。

这是一个小例子。

#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: 

} 

回答

1

PROGMEM并不是那么容易使用。它可以是有点简单:

#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: 

} 

我想route1route2,因为它是用于复制到routingTable可能导致所有的烦恼。如果您像我一样初始化routingTable的元素,则效果会更好。还有getRoute被打破了很多。

无论如何,如果你有闪光的字符串,你也可以使用String str {(__FlashStringHelper*)r.URI};,然后使用比较操作: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是一个指向PROGMEM字符数组,但Serial.println不知道!它最终试图从RAM中读取字符串,而不是。

Arduino Serial库似乎不支持PROGMEM中的字符串。您需要遍历一次打印一个字符的字符串,使用另一个库或将字符串存储在RAM中。

0

正如指出的@KIIV,最好是直接指定RouteroutingTable的声明中。作为替代方案,你可以重新定义了结构Route

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

这样,阅读从闪存都URIfunction地址可以由单一的通话进行到memcpy_P。完整的代码:

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

目前我的URI支持多达254个字符,所以这真的是一个倒退。此外,这会浪费任何少于15个字符的网址的内存。 –

+0

你没有在问题中提到你的URI会很长。你是正确的,这种方法会浪费内存,但如果顺序是几个字节,我认为它是可以接受的。在你的情况下,@KIIV答案是更好的解决方案。 – putu