Hallo!
Ich habe da ein dickes Problem im GCC AVR gefunden.
Ich will einen String nach einer Reihe von Schlüsselwörtern
durchsuchen. Wenn die Schlüsselwörtern im Ram stehen und
unter verwendung von strcasecmp geht das auch wunderbar.
Kommen die Schlüsselwörter aus dem Flash mit strcasecmp_P
geht das nur wenn der index in Strings[] eine konstante ist.
Kennt da jemand einen workaround?
#include <avr/pgmspace.h>
#include <stdio.h>
#include <avr/iom128.h>
#include <avr/interrupt.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int16_t indx;
char cliline_ram[] = "WRITE";
const char PROGMEM Str1[] = "HELP";
const char PROGMEM Str2[] = "INFO";
const char PROGMEM Str3[] = "WRITE";
const char PROGMEM Str4[] = "READ";
const char PROGMEM Str5[] = "POLL";
const char PROGMEM Str6[] = "DEFINE";
const char PROGMEM Str7[] = "SCAN";
const char PROGMEM Str8[] = "IPADDR";
const char PROGMEM Str9[] = "IPMASK";
const char PROGMEM Str10[] = "GATEWAY";
const char PROGMEM Str11[] = "PORT";
const char* PROGMEM Strings[] = { Str1, Str2,Str3 ,Str4 ,Str5 ,Str6
,Str7 ,Str8,Str9,Str10,Str11 };
int16_t gr;
uint8_t i;
int16_t main(void)
{
indx =strcasecmp_P((const char*)&cliline_ram,Strings[0]); //nicht
gefunden
indx =strcasecmp_P((const char*)&cliline_ram,Strings[1]); //nicht
gefunden
indx =strcasecmp_P((const char*)&cliline_ram,Strings[2]); //gefunden
ok
i=2;
indx =strcasecmp_P((const char*)&cliline_ram,Strings[i]); //geht nicht
gr = sizeof(Strings) / 2;
for (i=0;i<gr; i++)
{
indx =strcasecmp_P((const char*)&cliline_ram,Strings[i]); //geht
nicht
indx =strcasecmp_P((const char*)&cliline_ram,Strings[2]); //geht
}
return 0;
}
hbloed schrieb:> indx =strcasecmp_P((const char*)&cliline_ram,Strings[0]); //nicht> gefunden
Hier liegt der Fehler.
Du kannst nicht einfach Strings[0] benutzen.
Denn Strings liegt je seinerseits selbst wieder im Flash. Zugriffe
darauf müssen daher über die pgm_read.... Funktionen geführt werden.
Ist der Index eine Konstante, dann rettet dich der Optimizer, der den
ZUgriff auf Strings[0] durch den Inhalt, nämlich der Adresse von Str1,
ersetzt, die er kennt. Bei Strings[i] geht das dann klarerweise nicht
mehr.
> Ich habe da ein dickes Problem im GCC AVR gefunden.
Wider nix. War doch nur ein Programmierfehler.
(Das Umcasten von cliline_ram auf const char* kannst du dir sparen. Das
const ist an dieser Stelle die Zusicherung der Funktion, dass sie den
String nicht verändern wird. Wenn eine Funktion diese Zusicherung macht,
kann man daher der Funktion sowohl konstante Texte als auch nicht
konstante Texte übergeben. Das weiß auch der Compiler)
So einfach ist es nicht,
nicht gefunden ist in dem Fall i.o. weil der String ja nicht
übereinstimmt.
die Zeilen wo ght nicht steht sind das Problem:
indx =strcasecmp_P((const char*)&cliline_ram,Strings[i]); //geht nicht
indx =strcasecmp_P((const char*)&cliline_ram,Strings[2]); //geht
Wenn ich über i zugreife geht es nicht, bei zugriff über [2] ist es ok.
Das mit dem pgm_read bring nix, hab ich auch schon probiert.
hbloed schrieb:> Das mit dem pgm_read bring nix, hab ich auch schon probiert.
Dann probier's beim nächsten Mal richtig, dann geht's.
Ob es allerdings wirklich lohnt, die paar Byte an Zeigern für den
Stress mit dem notwendigen pgm_read_word() tatsächlich noch in den
Flash zu verlagern, musst du selbst wissen. Wenn du dein PROGMEM
in der Deklaration von Strings[] einfach streichst, funktioniert der
ganze Krempel auf Anhieb.
hbloed schrieb:> So einfach ist es nicht,
Doch. Ist es.
> Wenn ich über i zugreife geht es nicht, bei zugriff über [2] ist es ok.
Und ich hab (zumindest versucht) dir erklärt, warum das so ist: Das
Verhalten ist deswegen so, weil dir der Optimizer einen eigentlich
falschen Code durch die Optimierung ungewollt so zurechtbiegt, dass er
wieder funktioniert.
> Das mit dem pgm_read bring nix, hab ich auch schon probiert.
Dann hast du das falsch eingebaut.
Da liegt beim Code im Eröffnungsposting der Knackpunkt. Machs richtig
und es wird in allen Fällen funktionieren.
Wenn du mittels PROGMEM etwas ins Flash verlagerst, dann geht der
Zugriff auf den Inhalt immer über eine der pgm_read Funktionen, der man
die Adresse im Flash übergibt, an der die gewünschten Daten liegen.
int PROGMEM i = 5;
i liegt im Flash, also führt der Zugriff zum Auslesen von i über die
pgm_read Funktionen
const char* Strings[] PROGMEM = { ....
Strings liegt im Flash, also führt der Zugriff zum Auslesen des Inhalts
eines Array Elements über die pgm_read Funktionen.
Du willst hier den Inhalt des i-ten Array elements von Strings benutzen
strcasecmp_P((const char*)&cliline_ram,Strings[i]);
also musst du pgm_read benutzen und dieser Funktion die Adresse des
i-ten Arrayelements in Strings übergeben.