#if defined(RAMPZ) #define PROGMEM_UPPER _attribute_ ((section (".textupper"))) #define SET_RAMPZ(value) {RAMPZ = value;} #define pgm_read_byte_inc(addr) \ (__extension__({ \ uint8_t __result; \ _asm_ \ ( \ "elpm %0, Z+" "\n\t" \ : "=r" (__result), \ "=z" (addr) \ : "1" (addr) \ ); \ __result; \ })) #else #define PROGMEM_UPPER PROGMEM #define SET_RAMPZ(value) #define pgm_read_byte_inc(addr) \ (__extension__({ \ uint8_t __result; \ _asm_ \ ( \ "lpm %0, Z+" "\n\t" \ : "=r" (__result), \ "=z" (addr) \ : "1" (addr) \ ); \ __result; \ })) hi, ich arbeite mich hier gerade in einen code ein, und hier wird diese funktion verwendet - ich frage mich was deren genaue funktion ist. so wie es scheint wird ihr ein pointer vom typ const char * übergeben, und sie gibt dann ein byte zurück, ist das so richtig? dann müsste ich sie in c doch in etwa so deklarieren, oder!? unsigned char pgm_read_byte_inc (const char *) oder!? zusätzlich wird dann "dem namen nach" noch der pointer um 1 erhöht...sehe ich das richtig? damit könnte ich also ein array auslesen.. meine zweite frage, warum macht man das an der stelle so? zeiterparnis? oder hat es was mit dem internen/externen ram/flash zu tun? ich würde diese funktion ertsmal ganz gern einfach in c schreiben, um damit ein lokales const unsigned char array auslesen zu lönnen. gruß, w.
Prinzipell ist es die gleiche Funktionalität wie
1 | pgm_read_byte (addr++); |
Das Problem damit ist jedoch, daß pgm_read_byte (ebenfalls ein Inline-Makro) immer nur ein LPM erzeugt, und das ++ nicht mit dem LPM kombiniert werden kann sondern zB mit adiw r30,1 separat erfolgt. Das kostet Zeit und Platz. Die Makros von oben gehen übrigens nicht für alle AVRs; denn einige ATtiny kennen nur einen LPM, der implizit nach R0 lädt, und kein LPM Rn, Z. Eine inline Funktion wird übrigens anderen Code geben. Entgegen der oft zu findenden Aussage, Inline-Funktionen lieferten gleichen oder gleichschnellen Code wie entsprechende Makros, ist das nicht der Fall. Grund dafür ist zB die Returnwert-Promotion einer geinlinten Funktion, so daß result immer auf 16 Bit expandiert wird -- im Gegensatz zum Makro. (avr-gcc ohne -mint8)
>ich würde diese funktion ertsmal ganz gern einfach in c schreiben Hätte der Autor dieses Codestücks vermutlich auch getan, wenn es denn möglich gewesen wäre. Das geht aber nicht. Und ja, das hat was mit dem flash zu tun. Der gcc (um den es hier vermutlich geht) kann mit der Harvard-Architektur des AVR's (um den es hier vermutlich geht) nix anfangen, und kennt keinen Unterschied zwischen Programm- und Data-Speicher. Der wird durch nichts und niemanden dazu bewegen zu sein, den Befehl "lpm" zu verwenden, um auf den Programmspeicher zuzugreifen. Das geht nur in Assembler. Oliver
ok ok, nochmal langsam: ich will diese funktion ersetzen, mit ihr wird ein datensatz geladen, ein ganzer haufen bytes die dann weiterverarbeitet werden. ich lege diesen datensatz einfach global an: const unsigend char datensatz[]={0xff,....,0xab}; und will die oben genannten funktionen so in c ausprägen das ich (wie mit der funktion oben dies schon geschehen ist) die einzelnen werte aus dem datensatz[] auslesen. ich kenne den avr asm nicht, leider - deswegen verstehe ich auch nicht genau was dort passiert und wo das problem liegt... was ist ein LPM - tut mir leid, ich kenn mich da noch nicht so tiefgehend aus, in meiner welt gibts ram zellen, intern und extern (optional, können evtl. gemappt werden) am prozessor, stack, heap, und programmspeicher flash, am rande noch etwas eeprom speicher - egal - ich verstehe das problem bei der sache noch nicht. w.
ein datensatz mit der alten funktion wurde so angelegt: uint8_t _attribute_ ((progmem)) datensatz[] = { 0x04,...,0xff}; (keine ahnung was da passiert) im code wird sie so verwendet: unsigned short var; var+=pgm_read_byte_inc(datensatz); var=pgm_read_byte_inc(datensatz); var-=pgm_read_byte_inc(datensatz); folgend in schleifen usw... es wird ihr immer die adresse von datensatz übergeben.... es gibt auch noch die funkion: pgm_read_byte(datensatz); deren deklaration ich noch nicht gefunden habe, sie wird ebenfalls zwichendurch aufgerufen, ich schätze sie inkrementiert nur den "datenpointer" im gegensatz zu pgm_read_byte_inc(datensatz); nicht. 1) warum macht man das so? was bezweckt es genau? 2) wie kann ich es so umschreiben das ich auf mein kleines globales array zugreifen kann, ohne die gesamten funktionen die diese zugriffsfunktion nutzen zu ändern? ich hoffe ich konnte es so etwas besser darstellen.... gruß, w.
ich versuche es zz mit diesen defines... #define pgm_read_byte_inc(d) *d;d++ #define pgm_read_byte(d) *d w.
LPM liest ein byte aus dem Programm Memory in ein Register- also dem Flash. Der gcc kennt aber keine getrennten Speicher, ergo kommt der gar nicht auf die Idee, LPM zu verwenden. Ohne große Umbauten am Compiler und der avr-libc gibt es keine Möglichkeit, "ganz normal" auf ein im Flash liegendes Array zuzugreifen. Das geht nur über die jeweilgen pgm_read-Funktionen, und damit nur für jeden Wert einzeln. Oliver
...ich glaube wir reden aneinader vorei :-)... ich kann doch schreiben: const unsigend char datensatz[]={0xff,0xff,0xab}; void main (void) { unsigned short x; x=datensatz[0]; x+=datensatz[1]; x+=datensatz[2]; } oder ich schreibe: uint8_t attribute ((progmem)) datensatz[] = {0xff,0xff,0xab}; void main (void) { unsigned short x; x=pgm_read_byte_inc(datensatz); x+=pgm_read_byte_inc(datensatz); x+=pgm_read_byte_inc(datensatz); } ein unterschied ist, so wie ich es verstanden habe, das das array oben, nach dem laden wohl im ram liegt, während das im unteren bsp im flash liegt und da der compiler hier nicht unterscheiden kann, zwischen flash und ram, benötigt man diese read funktionen um ihm eben genau dies "aufzuzwingen". was passiert also im oberen fall, es ist immerhin ein const array, wird jedoch demnach trotzdem im ram angelegt... nebenbei, der inhalt des arrays MUSS jedoch irgentwann ja mal im programmcode gelegen haben, da das programm ja auch mal in den controller geflasht wurde - jedoch der compiler macht einen anderen zugriff daraus!?au mai.. dies ist mir jedoch egal - ich will von der hardware weg - ich will das untere main überall nutzen können, auf jedem prozessor, das datenarray muss NICHT im flash liegen! was also genau macht diese read funktion mit dem array, gibt sie einfach nur einen nach dem anderen wert des arrays zurück!? bei jedem aufruf den nächsten!? w.
Wenn es wirklich nur darum geht, ein möglichst einfach zu verstehendes und portierbares C-Programm zu bekommen, dann kannst Du ganz darauf verzichten. Dazu nimmst Du wie bereits erwähnt die Abbildungen
1 | #if defined (__AVR__) && defined (__GNUC__) // avr-gcc
|
2 | # include <avr/pgmspace.h>
|
3 | # define pgm_read_byte_inc(d) pgm_read_byte(((const unsigned char *) (d))++)
|
4 | #else // kein avr-gcc
|
5 | # define pgm_read_byte(d) (*(d))
|
6 | # define pgm_read_byte_inc(d) (*((const unsigned char *) (d))++)
|
7 | # define PROGMEM
|
8 | #endif // avr-gcc
|
Falls d etwa ein Pointer auf long ist, erhälst Du sonst mit
1 | #define pgm_read_byte_inc(d) (*(d)++)
|
ein anderes Ergebnis, weil ++ immer um sizeof (*d) erhöht. Ohne PROGMEM funktioniert das Programm genauso, nur daß mehr RAM verbraucht wird, weil die Konstanten nicht in FLASH leben wie mit PROGMEM, sondern im RAM. avr-gcc hat wie gesagt keine Vorstellung davon, was LPM macht. Diese Instruktion wird nur in ein paar tablejumps textuell ausgegeben, zB in manchen Sprungtabellen für switch/case. Leider gibt es in GCC 4 immer noch nicht die nötige Infrastruktur, um Harvard-Architekturen besser im Backend unterstützen zu können. Die Umsetzung von pgm_read* etc ist über Attribute implementiert, was aber die Aufgabe nicht adäquat löst. An der Stelle wären nämlich eigene Qualifier nötig (ähnlich wie const, volatile, unsigned, signed), die dann auch Ausdrücke wie
1 | progmem char * foo1; // foo1 zeigt ins Flash und steht im RAM/GPR |
2 | char * progmem foo2; // foo2 zeigt ins RAM und steht im FLASH |
3 | void *a = foo1; // lädt foo1 per LDS/LDD (oder MOV falls schon im GPR) |
4 | void *b = foo2; // lädt foo2 per LPM |
erlaubten.
Das PROGMEM-Zeig dient wie gesagt nur dazu, um RAM zu sparen. Die const-Daten liegen so oder so im Flash, aber in einem Fall wird beim Lesen ins Flash gegriffen (avr-gcc + pgm_*) und im anderen Fall (Standard-C) kopiert der Startup-Code das Gerüffel ins RAM oder legt es in eine read-only Section je nach Compiler/µC und greift nach dem Startup nicht mehr auf diese Daten im Flash zu, sondern nur noch auf die Kopien.
>ein unterschied ist, so wie ich es verstanden habe, das das array oben, >nach dem laden wohl im ram liegt, während das im unteren bsp im flash >liegt und da der compiler hier nicht unterscheiden kann, zwischen flash >und ram, benötigt man diese read funktionen um ihm eben genau dies >"aufzuzwingen". So ist es. >was passiert also im oberen fall, es ist immerhin ein const array, wird >jedoch demnach trotzdem im ram angelegt... So ist es beim gcc auf dem AVR. Bei anderen Compilern kann das anders sein. >nebenbei, der inhalt des arrays MUSS jedoch irgentwann ja mal im >programmcode gelegen haben, da das programm ja auch mal in den >controller geflasht wurde - jedoch der compiler macht einen anderen >zugriff daraus!?au mai.. Jein. Im Ram sind das initialisierte globale Variable. Diese werden beim Start des Programms, noch bevor main() aufgerufen wird, mit Werten gefüllt. Die Initialisierungswerte dazu stehen natürlich im Flash, und werden von dort ins Ram kopiert. In allen Programmteilen, die du programierst, greift der Compiler dann nur auf das Ram zu. >dies ist mir jedoch egal - ich will von der hardware weg - ich will das >untere main überall nutzen können, auf jedem prozessor, das datenarray >muss NICHT im flash liegen! Dann wirst du aber für jede Architektur eine passende Implementierung von pgm_read_byte_inc() erstellen müssen. >was also genau macht diese read funktion mit dem array, gibt sie einfach >nur einen nach dem anderen wert des arrays zurück!? bei jedem aufruf den >nächsten!? Im Prinzip schon. Die Funktion gibt den Wert, auf den addr zeigt, zurück, und erhöht danach addr dann um eins. Oliver
@Johann und Oliver, danke für die erklärungen und hintergrundinfos - damit komme ich zurecht. schade das wenn man das schlüsselwort const verwendet dies nicht auch einfluss auf den ursprung der variablen nimmt sondern in diesem fall nur die variable vor dem zugriff schützt. const heißt ja nun das die werte nicht verändert werden können, sollen müssen. aber das ist sicher das was du damit sagen wolltest: >>Leider gibt es in GCC 4 immer noch nicht die nötige Infrastruktur, um >>Harvard-Architekturen besser im Backend unterstützen zu können. Die >>Umsetzung von pgm_read* etc ist über Attribute implementiert, was aber >>die Aufgabe nicht adäquat löst. An der Stelle wären nämlich eigene >>Qualifier nötig (ähnlich wie const, volatile, unsigned, signed)... ...was mich etwas wundert ist, warum der compiler davon nichts weiß, dies ist dann auch der grund warum die werte aus dem flash vorm main ins ram kopiert werden, obwohl sie als const deklariert sind, demnach also eh nicht verändert werden, theoretisch müsste beim einsatz von const ja NUR eben dieser spezielle befehl verwendet werden um den zugriff ins flash an dieser stelle zu sichern, denn letztendlich werden die zellen ja sicher adressen haben, da dies aber alles viel komplizierter sein muss, wird es dann wohl so gemacht (ich meine, wer kopiert schon gern konstante werte ins ram) - schade. ich werds testen (also die defines) @oli klar muss ich diese funktion dann immer neu implementieren, aber solange zumindest eine definierte softwareschnittstelle vorhanden ist, kann man das ja auch machen :-)... vieles ist ja so wild, das es kein anfang und kein ende hat... gruß, w.
whitenoise wrote: > @Johann und Oliver, > > schade das wenn man das schlüsselwort const verwendet dies nicht auch > einfluss auf den ursprung der variablen nimmt sondern in diesem fall nur > die variable vor dem zugriff schützt. Das ist auch Sinn und Zweck von const. Wie wolltest Du mit const unterscheiden, ob ein Wert ins FLASH oder ins EEPROM lokatiert werden soll? Schon daran sieht man, daß const die falsche Baustelle dafür ist. Kompexere µC haben evtl noch mehr Möglichkeiten, konstante Daten abzulegen. > const heißt ja nun das die werte nicht verändert werden können, sollen > müssen. Nein, das heisst es nicht. Es gibt ja auch schöne Konstrukte wie
1 | const volatile ... |
womit mancher Zeitgenosse gerne ausdrückt, daß man den Wert nicht verändern darf, er sich aber ändern kann -- zB ein read-only SFR. Und schliesslich: In einem Komposit können einzelne Komponenten const sein und andere nicht... wie wolltest Du so eine Struct/Union lokatieren? >>>Leider gibt es in GCC 4 immer noch nicht die nötige Infrastruktur, um >>>Harvard-Architekturen besser im Backend unterstützen zu können. Die >>>Umsetzung von pgm_read* etc ist über Attribute implementiert, was aber >>>die Aufgabe nicht adäquat löst. An der Stelle wären nämlich eigene >>>Qualifier nötig (ähnlich wie const, volatile, unsigned, signed)... > > ...was mich etwas wundert ist, warum der compiler davon nichts weiß, > dies ist dann auch der grund warum die werte aus dem flash vorm main ins > ram kopiert werden, obwohl sie als const deklariert sind, demnach also > eh nicht verändert werden, theoretisch müsste beim einsatz von const ja > NUR eben dieser spezielle befehl verwendet werden um den zugriff ins > flash an dieser stelle zu sichern, denn letztendlich werden die zellen > ja sicher adressen haben, da dies aber alles viel komplizierter sein > muss, wird es dann wohl so gemacht (ich meine, wer kopiert schon gern > konstante werte ins ram) - schade. Zunächst würde die Einführung neuer Qualifier eine Erweiterung des C-Standards entsprechen, was nicht so prickeln ist. huge, far, near etc sind Beispiele dafür. Mit der momentanen Strategie bleibt der Compiler hier "sauber", und lastet es dem Anwender auf, der über inline Asm und Attribute den Code patchen muss. Der eigentliche Grund ist aber, daß GCC intern nur einen einzigen Pointer-Mode kennt, der per Makro definiert wird. Intern bräuchte man aber mehrer Pointer-Modi, die dann bei der asm-Ausgabe entsprechend umgesetzt werden könnten und vom Parser korrekt initialisiert würden. Problem ist der Mittelteil, wo zig Transformationen und Optimierungen geschehen. Die tausenden von Stellen im GCC dahingehend zu erweitern und zu testen ist ne echte Strafe und ein eigenes, auf mindestens 1 MJahr anzulegendes Projekt. Und wenn man damit fertig wäre, müsste man das in die dann aktuelle GCC-Version nachführen. > @oli > klar muss ich diese funktion dann immer neu implementieren, aber solange > zumindest eine definierte softwareschnittstelle vorhanden ist, kann man > das ja auch machen :-)... vieles ist ja so wild, das es kein anfang und > kein ende hat... Is ja nur ein *p++, falls man nicht den pathologischen Fall hat, durch einen nicht-char-Zeiger auf chars zuzugreifen. Dann muss eben noch der Cast dazu. Johann
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.