Ich habe nach langer Zeit endlich mal wieder WinAVR angefasst, da mein
Grundlayout erstmal fertig ist und ich ein Menü über ein VFD-Display
steuern will. Folgendes Problem:
Irgendwann, wenn mein Menü groß genug ist stürzt der ATmega16 einfach
nach Start ab, weil der Speicher gefüllt ist. Mein Menü liegt dabei in
einem Stringarray. Folgende Lösungsansätze gab es:
1. ATmega32 verwenden (Pinkompatibel) - nur wegen des SRAMs ?!
2. Array im Flash ablegen und zugreifen
Punkt 2 wäre natürliche die optimalste Lösung. Aber wie soll das
funktionieren, dass ich da auch noch zugreifen kann? Wenn es einmal im
Flash ist, müsste ich ja mit den verbiegen von Adressen direkt darauf
zugreifen - sofern der ATmega mich lässt. Nur wie bekomme ich das Array
da rein und wie greife ich bestmöglich darauf zurück?
Ich hatte das damals mal mit einer Funktion gemacht, die ich einfach in
den Hauptspeicher kopiert habe, um den Flash wärend des betriebes neu
zu beschreiben. Das war aber auch kein Atmel und der Flash war ein
externer Speicher. Nur hier ist ja erst einmal die Schwierigkeit das in
den Flash zu bekommen. Die 16K sind ja locker ausreichend.
Vielen Dank! Ich glaube das war der nötige Anstoß, den ich brauchte. Ich
kann zwar mitd en Funktionen selbst nichts anfangen, da ich ja keine
flexiblen Daten irgendwo speichere aber mein erster Test mit einer
CRC-Tabelle hat schonmal massig Speicher gespart:
const unsigned char crc8_data[] = { x, y, .. };
umgewandelt in
const unsigned char crc8_data[] PROGMEM = { x, y, .. };
Ich hoffe das klappt dann auch so und ich kann dann mein Menü in den
Flash schieben. Muss ich demnächst mal probieren.
Gesegnete Osterfeiertage wünsche ich noch.
Ich habe mir das mal angeschaut. Die optimalste Lösung ist das ja leider
nicht. Ich verstehe garnicht, wieso "const" nicht sowieso in den
Festspeicher gelegt werden. Aber für die notwendigsten Sachen werde ich
es doch machen. Für einzelne Bytetabellen lohnt das ja kaum, da man die
Werte ja immer mit pgm_read_byte aulesen muss.
Aber warum kriegt der das hier nicht auf die Reihe? Bin ich blind? So
ein paar Ausschnitte:
const char menu_tab_temp_a[] PROGMEM = "Temperatur Sensor %d\r\n%d
\370C";
const char menu_tab_temp_b[] PROGMEM = "Sensor an/aus\r\n%s";
const char menu_tab_type[] = {
MENU_TYPE_HEAD, MENU_TYPE_SUB, MENU_TYPE_SUB, MENU_TYPE_SUB,
MENU_TYPE_SUB, MENU_TYPE_SUB, MENU_TYPE_SUB, MENU_TYPE_ENDSUB,
MENU_TYPE_HEAD, MENU_TYPE_SUB, MENU_TYPE_SUB, MENU_TYPE_SUB,
MENU_TYPE_SUB, MENU_TYPE_SUB, MENU_TYPE_SUB, MENU_TYPE_ENDSUB,
PGM_P menu_tab_text[] PROGMEM = {
menu_tab_temp_a, menu_tab_temp_b, menu_tab_temp_c, menu_tab_temp_d,
menu_tab_temp_e, menu_tab_temp_f, menu_tab_temp_g, 0,
menu_tab_temp_a, menu_tab_temp_b, menu_tab_temp_c, menu_tab_temp_d,
menu_tab_temp_e, menu_tab_temp_f, menu_tab_temp_g, 0,
char menu_text[50];
char menubuf[50];
unsigned char menupos = 0;
menupos = 0;
strcpy_P(menu_text, menu_tab_text[menupos]);
switch (menu_tab_type[menupos]) {
case MENU_TYPE_HEAD:
sprintf(menubuf, menu_text, 1, 25);
break;
case MENU_TYPE_SUB:
sprintf(menubuf, menu_text, 41);
break;
default:
menubuf[0] = 'A';
menubuf[1] = 0;
}
vfd_print(menubuf);
Der gibt da immeer Müll als Text aus. Oder kurz: Sieht aus, als ob
menupos nicht 0 ist.
> Ich verstehe garnicht, wieso "const" nicht sowieso in den> Festspeicher gelegt werden.
Weil das absolut nicht der Sinn des `const' qualifiers ist (zumindest
nicht in C -- in C++ ist das was anderes).
Insbesondere aber, weil C ganz offenbar nicht so recht für
Harvard-Architekturen konzipiert worden ist. Hatten wir hier schon
ein paarmal. Ein string literal (das ja implizit sowas wie `const'
ist) ist ein gültiger Parameter für die str*() Standard-Funktionen.
Mit separatem ROM-Segment funktioniert das aber nicht mehr (*), wenn
die string literals automatisch im ROM landen.
(*) Ausnahme: nur über objektorientierte Ansätze wie die `generic
pointers' beim Keil C51 Compiler. Das wiederum geht zu Lasten von
Speicherplatz (jede Funktion ist zwangsweise dreimal da, da erst zur
Laufzeit entschieden wird, was wirklich benötigt wird) und Laufzeit.
> Für einzelne Bytetabellen lohnt das ja kaum, da man die Werte ja> immer mit pgm_read_byte aulesen muss.
Der Prozessor muß das ohnehin, egal, ob Dir der Compiler das abnimmt
(wie beim IAR) oder ob Du das explizit hinschreiben mußt (wie beim
GCC).
> PGM_P menu_tab_text[] PROGMEM
Das sieht mir sehr nach doppelt gemoppelt aus.
const char *menu_tab_text[] PROGMEM = ...
müßte meiner Meinung nach OK sein.
menu_tab_text[] wandert durch `PROGMEM' in den ROM. Du mußt dann
jeden der Pointer mit pgm_read_word() erstmal auslesen, bevor Du
den Inhalt nach strcpy_P() schicken darfst.
> Der Prozessor muß das ohnehin, egal, ob Dir der Compiler das abnimmt> (wie beim IAR) oder ob Du das explizit hinschreiben mußt (wie beim> GCC).
Nagut, wenn das nacher funktioniert und ich immernoch zu enig Speicher
habe, kann ich es ja auch umbauen.
>> PGM_P menu_tab_text[] PROGMEM> Das sieht mir sehr nach doppelt gemoppelt aus.> const char *menu_tab_text[] PROGMEM = ...> müßte meiner Meinung nach OK sein.
Dachte ich auch erst. Aber laut FAQ
(http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_rom_array)
soll das so gemacht werden. Letztendlich steht da sicher auch nur für
PGM_P const char * irgendwas nehme ich mal an.
> menu_tab_text[] wandert durch `PROGMEM' in den ROM. Du mußt dann> jeden der Pointer mit pgm_read_word() erstmal auslesen, bevor Du> den Inhalt nach strcpy_P() schicken darfst.
Auch hier wird das nach FAQ so gemnacht. Dafür denke ich ist ja die
Funktion strcpy_P da, die in pgmspace.h definiert wurde.
Nachtrag: Wenn ich anstatt "menupos" eine 0 eintrage geht das. Aber
warum?
> Auch hier wird das nach FAQ so gemnacht. Dafür denke ich ist ja die> Funktion strcpy_P da, die in pgmspace.h definiert wurde.
Jein. Du hast beides im ROM stehen: menu_tab_text[] selbst, sowie die
Zeiger auf die Strings, die in dieser Tabelle enthalten sind.
strcpy_P() extrahiert die Strings sauber aus dem ROM, aber Du
extrahierst den Inhalt von menu_tab_text[] selbst nicht aus dem ROM
(sondern aus dem RAM). Da fehlt ein pgm_read_word().
> Nachtrag: Wenn ich anstatt "menupos" eine 0 eintrage geht das.
Aber
> warum?
Weil der Compiler dann bereits die dereferenzierte ROM-Adresse an
strcpy_P() übergibt, d. h. menu_tab_text[0] wird gar nicht zur
Laufzeit ermittelt, sondern bereits zur Compilezeit.
Genau dieses Symptom ist ein typischer Fall für pgm_read_word()
vergessen. ;-)