Forum: Compiler & IDEs Fehlermeldung: near initialization for


von Andreas (ah3112)


Lesenswert?

Hallo,

benötige leider mal wieder Eure Hilfe, da ich selber nicht weiterkomme 
und mir googeln auch nichts gebracht hat.

Ich habe eine Software für den Atmega162 geschrieben. An dem Controller 
hängt ein LCD-Display und externer Ram. Das LCD-Display wird im 
memory-mapped betrieben. Bis gestern lief auch alles wunderbar.

Als Compiler hatte ich den avr-gcc.exe (GCC) 8.5.1 20241123.

Heute habe ich noch eine weitere Datei erstellt. Das Compilieren klappt 
einwandfrei und mir wurde kein Fehler gemeldet.

Wenn ich das Programm auf den Controller übertrage, stürzt das ab. Am 
Anfang des Programms habe ich zur Kontrolle 2 LEDs geschaltet. Die 
kommen auch noch. Mein Verdacht war, wenn auf den RAM zugegriffen wird, 
dass es dabei hakt.

Vorab zur Information. Auf derselben Hardware läuft eine Software, die 
erheblich mehr Ram benötigt. Auch das LCD-Display funktioniert. An der 
Hardware liegt es nicht.

Um das Problem näher einzugrenzen, habe ich sämtliche Dateien nach Atmel 
Studio kopiert. Dort lassen die sich nicht compilieren. Ich erhalte die 
im Betreff angegebene Fehlermeldung. Exakt ist die Fehlermeldung: "Error 
2 (near initialization for 'eingabenmenu[0].text')". Allerdings auch für 
alle anderen Elemente von eingabenmenu. Dazu noch "Error 1 initializer 
element is not constant" für alle Zeilen. Ich vermute, dass mit meiner 
Initialsierung was nicht stimmt.

Hier mal, wie das Ganze aussieht.
1
typedef struct display
2
{
3
  const char *text_zeile[LCD_DISP_LENGTH];
4
} t_display;
5
6
typedef struct {
7
  t_display text;
8
  const int8_t previous;
9
  const int8_t next;
10
  int8_t ( *fp )( void );
11
} t_menu;
12
13
const t_display eingabe_datum_display = {
14
    {"   Eingabe Datum",
15
     "Bestätigen mit Enter",
16
     "Weiter mit Down",
17
     "Zurück mit Up"},
18
};
19
20
const t_display eingabe_zeit_display = {
21
    {"    Eingabe Zeit",
22
     "Bestätigen mit Enter",
23
     "Weiter mit Down",
24
     "Zurück mit Up"},
25
};
26
27
static const t_menu eingabenmenu[] = {
28
   {eingabe_datum_display ,-1 ,1 ,eingabe_datum},
29
   {eingabe_zeit_display ,1 ,2 ,eingabe_zeit},
30
};

Jetzt meine Fragen:

1. Was mache ich falsch bei der Initialsierung? Ich nehme mal an, dass 
es was mit den Arrays bzw. Strukturen zu tun hat und mir das Programm 
deshalb abstürzt.


2. Warum hat mir der Compiler von Atmel Studio diesen Fehler gemeldet, 
aber nicht der 8.5.1? Bzw. welche Einstellung muss ich angeben, damit 
der mir das auch als Fehler meldet?


3. Ich habe in einer Datei "#include <time.h>" stehen. Der Compiler von 
Atmel Studio findet die nicht. Beim 8.5.1 kein Problem. Was müsste ich 
angeben, damit der Fehler bei Atmel Studio nicht kommt? Dies ist aber 
zweitrangig, da Atmel Studio von mir eigentlich nicht verwendet wird.

Vorab schon mal vielen Dank.


Viele Grüße
Andreas

von Oliver S. (oliverso)


Lesenswert?

Andreas schrieb:
> Als Compiler hatte ich den avr-gcc.exe (GCC) 8.5.1 20241123.

Und wie/womit rufst du den auf?
Zeig doch mal den Aufruf.

time.h ist Teil der avrlibc. Das sollte auch beim Studio dabei sein.
Atmel Studio klingt allerdings deprecated. Welche Version ist das?

Oliver

von Harald K. (kirnbichler)


Lesenswert?

Statt
1
  t_display text;

würde ich
1
  t_display *text;

in t_menu verwenden, und in der Initialisierung den Adressoperator & 
verwenden.

Also statt
1
    {eingabe_datum_display ,-1 ,1 ,eingabe_datum},

das hier:
1
    {&eingabe_datum_display ,-1 ,1 ,eingabe_datum},

Andreas schrieb:
> Wenn ich das Programm auf den Controller übertrage, stürzt das ab.

Auch wenn hier im Forum manche Zeitgenossen unterwegs sind, die meinen, 
daß man einen Debugger nie brauchen würde, hier hilft er.

Lass' das Programm im Debugger laufen und Du siehst, was schiefläuft.

Debuginterfaces sind auch für AVRs kein überteuertes Hexenwerk mehr.

von Andreas (ah3112)


Lesenswert?

Oliver S. schrieb:
> time.h ist Teil der avrlibc. Das sollte auch beim Studio dabei sein.
> Atmel Studio klingt allerdings deprecated. Welche Version ist das?
Das ist 6.2. Die aktuelle Version läuft bei mir nicht.

Harald K. schrieb:
> Statt  t_display text;
>
> würde ich  t_display *text;

Danke, werde ich alles morgen ausprobieren und berichten.

Harald K. schrieb:
> Auch wenn hier im Forum manche Zeitgenossen unterwegs sind, die meinen,
> daß man einen Debugger nie brauchen würde, hier hilft er.

Das war meine Überlegung, das im Simulator von Atmel Studio laufen zu 
lassen.
Das Problem ist aber, wenn ich das im Debugger laufen lasse, dann sehe 
ich zwar, dass er bei dem Zugriff auf die Variablen abstürzt. Aber mir 
ist dann immer noch nicht klar, wie ich die korrekt initialisieren muss.

Erstmal danke

von Yalu X. (yalu) (Moderator)


Lesenswert?

Ich gehe mal davon aus dass du in C (und nicht in C++) programmierst.

Vorab: Warum dein Programm crasht, weiß ich auch nicht. aber für die
Fehlermeldung, die nur in bestimmten GCC-Versionen erscheint, gibt es
eine Erklärung:

Du benutzt Variablen (eingabe_datum_display und eingabe_zeit_display)
als constant expressions, was durch den C-Standard nicht abgedeckt ist
und deswegen bei älteren GCCs zu Fehlermeldungen führt (dass die
Variablen als const deklariert sind, ändert nichts daran, dass es
Variablen sind).

Offensichtlich betrachten neuere GCCs const-deklarierte Variablen
dennoch als constant expressions. Lt. Standard ist das eine legale
Spracherweiterung:

"An implementation may accept other forms of constant expressions."

Dein Code ist insofern nicht falsch, aber auch nicht gut, da die Daten
in eingabe_datum_display und eingabe_zeit_display zweimal im Speicher
auftreten, nämlich einmal in den Variablen selbst und zusätzlich als
Kopie in eingabenmenu.

Besser ist es, die entsprechenden Daten nicht in eigene Variablen,
sondern direkt in den Initialisierer von eingabenmenu zu schreiben.

Du kannst auch Haralds Vorschlag folgen, dann werden werden in
eingabenmenu statt der Kopien von eingabe_datum_display und
eingabe_zeit_display nur jeweils ein Pointer (2 Bytes) angelegt. Aber
auch 2×2 Bytes sind etwas verschenkter Speicherplatz. Zudem werden durch
die Indirektion über die Pointer die Zugriffe auf die Daten aufwendiger
(sowohl was die Codegröße als auch die Ausführungszeit betrifft).

Das alles sollte aber das Programm nicht crashen lassen, es sei denn,
dass durch die doppelte Speicherbelegung der RAM-Platz am Ende doch
etwas knapp wird.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Andreas schrieb:
> 2. Warum hat mir der Compiler von Atmel Studio diesen Fehler gemeldet,
> aber nicht der 8.5.1?

Weil du im Atmel Studio mit dem C-Compiler kompilierst, vorher aber 
offenbar mit C++.

Andreas schrieb:
> static const t_menu eingabenmenu[] = {
>    {eingabe_datum_display ,-1 ,1 ,eingabe_datum},

Hier kopierst du ja eingabe_datum_display nach eingabenmenu hinein. In C 
kann man structs nicht so direkt zuweisen, in C++ schon.

: Bearbeitet durch User
von Andreas (ah3112)


Lesenswert?

Yalu X. schrieb:
> Besser ist es, die entsprechenden Daten nicht in eigene Variablen,
> sondern direkt in den Initialisierer von eingabenmenu zu schreiben.

Wie würde das dann aussehen? So:
1
static const t_menu eingabenmenu[] = {
2
   {{"   Eingabe Datum",
3
     "Bestätigen mit Enter",
4
     "Weiter mit Down",
5
     "Zurück mit Up"}, -1 ,1 ,eingabe_datum},
6
   {{"    Eingabe Zeit",
7
     "Bestätigen mit Enter",
8
     "Weiter mit Down",
9
     "Zurück mit Up"} ,1 ,2 ,eingabe_zeit},
10
};

Yalu X. schrieb:
> Das alles sollte aber das Programm nicht crashen lassen, es sei denn,
> dass durch die doppelte Speicherbelegung der RAM-Platz am Ende doch
> etwas knapp wird.
Das war definitiv nicht. Es wurde mir angezeigt, dass 110% Ram belegt 
sind. Dies war aber bezogen auf den internen SRAM des Atmega162. Bei dem 
anderen Programm, wass erheblich mehr Ram benötigte, wurde mir 500% 
angezeigt. Als Extenen RAM hatte ich 32K. Ich habe auch einen Ramtest 
über den gesamten Rambereich laufen lassen, es wurde mir kein Fehler 
angezeigt.

Niklas G. schrieb:
> Weil du im Atmel Studio mit dem C-Compiler kompilierst, vorher aber
> offenbar mit C++.
Nein. Es war beides definitiv der C-Compiler.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Andreas schrieb:
> Es wurde mir angezeigt, dass 110% Ram belegt sind.

Das ist schlecht und sollte keinesfalls ignoriert werden.

> Dies war aber bezogen auf den internen SRAM des Atmega162. […] Als
> Extenen RAM hatte ich 32K.

Das solltest du den Linker aber auch wissen lassen. Sonst legt er dir
den Stack mitten in den Datenbereich. Wenn dadurch bspw. einer der
Funktionszeiger in eingabenmenu überschrieben wird, ist der Crash schon
sicher.

Ändere also das Linkerskript entsprechend, dann wird als netter
Nebeneffekt auch die prozentuale RAM-Belegung korrekt angezeigt.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Andreas schrieb:
> Nein. Es war beides definitiv der C-Compiler.

Ah, habs gefunden:

https://stackoverflow.com/questions/54135942/why-initializer-element-is-not-a-constant-is-not-working-anymore

Beim Initialisieren ein struct zu kopieren ist kein standardkonformes C, 
aber der GCC kann es ab Version 8 als eine Spracherweiterung. Dein Atmel 
Studio wird wohl eine ältere Version verwenden.

von Andreas (ah3112)


Lesenswert?

Yalu X. schrieb:
> Das solltest du den Linker aber auch wissen lassen. Sonst legt er dir
> den Stack mitten in den Datenbereich. Wenn dadurch bspw. einer der
> Funktionszeiger in eingabenmenu überschrieben wird, ist der Crash schon
> sicher.
Meinst Du das hier:
1
-Wl,-Tdata=0x800500 -Wl,--defsym,__DATA_REGION_ORIGIN__=0x800500 -Wl,--defsym=__DATA_REGION_LENGTH__=32k
Das ist drin.

Niklas G. schrieb:
> Beim Initialisieren ein struct zu kopieren ist kein standardkonformes C,
> aber der GCC kann es ab Version 8 als eine Spracherweiterung. Dein Atmel
> Studio wird wohl eine ältere Version verwenden.
Danke für die Info.

Ich habe es gerade so:
1
static const t_menu eingabenmenu[] = {
2
   {{"   Eingabe Datum",
3
     "Bestätigen mit Enter",
4
     "Weiter mit Down",
5
     "Zurück mit Up"}, -1 ,1 ,eingabe_datum},
6
7
   {{"    Eingabe Zeit",
8
     "Bestätigen mit Enter",
9
     "Weiter mit Down",
10
     "Zurück mit Up"} ,1 ,2 ,eingabe_zeit},
11
};
ausprobiert mit dem gcc 8.5.1. Zuerst kam die Warnung, dass um den Text 
noch geschweifte Klammern fehlen. Als ich die drum gemacht hatte, liess 
sich das Programm einwandfrei compilieren.

Kommentiere ich die Funktionen, in denen auf diese structs zugegriffen 
wird, aus, dann startet mein Programm. Sobald ich die Kommentierung 
rausnehme, läuft die Software erst gar nicht an.

Ich werde es morgen mal ausprobieren, wie Harald geschrieben hat, aber 
ich glaube im Moment nicht, dass das mein Problem löst.

Ich habe diese Mimik mit den structs noch in einem anderen Programmteil. 
Da funktionierte das bis gestern einwandfrei. Erst als ich heute noch 
eine weitere C-Datei hinzugefügt hatte, die auch diese structs, nur mit 
anderem Text und Funktionen verwendet, hat es nicht mehr funktioniert. 
Ich bin dam Verzweifeln.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Andreas schrieb:
> Meinst Du das hier:
> -Wl,-Tdata=0x800500 -Wl,--defsym,__DATA_REGION_ORIGIN__=0x800500 
-Wl,--defsym=__DATA_REGION_LENGTH__=32k
> Das ist drin.

Ja, damit sollte es eigentlich funktionieren (wenn ich nicht etwas
übersehen habe). Der Stack liegt damit zwar unverändert am oberen Ende
des internen RAM, da die Daten nun aber komplett im externen RAM liegen,
sollte es keine Konflikte geben. Ganz im Gegenteil, denn der Stack darf
jetzt auf bis zu 1 KiB (die Größe des internen RAM) anwachsen, ohne dass
er mit irgendetwas kollidiert.

: Bearbeitet durch Moderator
von Harald K. (kirnbichler)


Lesenswert?

Andreas schrieb:
> Das war meine Überlegung, das im Simulator von Atmel Studio laufen zu
> lassen.

Debugger, nicht Simulator.

> Das Problem ist aber, wenn ich das im Debugger laufen lasse, dann sehe
> ich zwar, dass er bei dem Zugriff auf die Variablen abstürzt.

Das hat sich ja wohl mittlerweile geklärt.

von Oliver S. (oliverso)


Lesenswert?

Harald K. schrieb:
> Andreas schrieb:
>> Das war meine Überlegung, das im Simulator von Atmel Studio laufen zu
>> lassen.
>
> Debugger, nicht Simulator.

Laufen tut es da im Simulator. Mit dem Debugger kann man das dann 
debuggen.

Oliver

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Andreas schrieb:
1
> -Wl,-Tdata=0x800500 -Wl,--defsym,__DATA_REGION_ORIGIN__=0x800500 
2
> -Wl,--defsym=__DATA_REGION_LENGTH__=32k
> Das ist drin.

Schau mal im MAP File, ob das auch Effekt hat.  Ob die Symbole verwendet 
werden, hängt nämlich an der Version der Binutils.  Das ld Script muss 
dazu so beginnen:
1
OUTPUT_FORMAT(...)
2
OUTPUT_ARCH(...)
3
...
4
__DATA_REGION_ORIGIN__ = DEFINED(__DATA_REGION_ORIGIN__) ? __DATA_REGION_ORIGIN__ : ...;
5
__DATA_REGION_LENGTH__ = DEFINED(__DATA_REGION_LENGTH__) ? __DATA_REGION_LENGTH__ : ...;
6
...
7
MEMORY
8
{
9
  ...
10
  data   (rw!x) : ORIGIN = __DATA_REGION_ORIGIN__, LENGTH = __DATA_REGION_LENGTH__
11
  ...
12
}
13
...

von Andreas (ah3112)


Angehängte Dateien:

Lesenswert?

Ich habe das jetzt mal so abgeändert, wie Harald vorgeschlagen hat, dann 
läuft es auch nicht. Zusätzlich bekomme ich folgende Meldung:
1
initialization discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
2
    {&eingabe_datum_display ,-1 ,1 ,eingabe_datum},

Johann L. schrieb:
> Schau mal im MAP File, ob das auch Effekt hat.  Ob die Symbole verwendet
> werden, hängt nämlich an der Version der Binutils.  Das ld Script muss
> dazu so beginnen:
Das finde ich da überhaupt nicht drin. Ich habe folgendes darin 
gefunden:
1
Name             Origin             Length             Attributes
2
text             0x00000000         0x00004000         xr
3
data             0x00800500         0x00008000         rw!x
4
eeprom           0x00810000         0x00000200         rw!x
5
fuse             0x00820000         0x00000003         rw!x
6
lock             0x00830000         0x00000400         rw!x
7
signature        0x00840000         0x00000400         rw!x
8
user_signatures  0x00850000         0x00000400         rw!x
9
*default*        0x00000000         0xffffffff
Das Map-File habe ich mal angehangen. Allerdings ist es nicht die 
Version, in der ich das so gemacht habe, wie Harald vorgeschlagen hat 
sondern, so
1
static const t_menu eingabenmenu[] = {
2
   {{"   Eingabe Datum",
3
     "Bestätigen mit Enter",
4
     "Weiter mit Down",
5
     "Zurück mit Up"}, -1 ,1 ,eingabe_datum},
6
   {{"    Eingabe Zeit",
7
     "Bestätigen mit Enter",
8
     "Weiter mit Down",
9
     "Zurück mit Up"} ,1 ,2 ,eingabe_zeit},
10
};

Harald K. schrieb:
> Debugger, nicht Simulator.
Das mit dem Debugger habe ich noch nicht verstanden. Zum Debuggen müsste 
ich doch die Fuses für JTAG setzen. Da ich den Controller aber im 
memory-mapped betreibe, würde das doch nicht funktionieren.

Ich tendiere im Moment dazu den ganzen Kram in den Flash zu verlagern, 
in der Hoffnung, dass ich dann die Probleme nicht habe.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Andreas schrieb:
> Ich tendiere im Moment dazu den ganzen Kram in den Flash zu verlagern,
> in der Hoffnung, dass ich dann die Probleme nicht habe.

Würde ich an deiner Stelle auch machen. Welchen Sinn sollte es haben, 
konstante Strings im RAM vorzuhalten, die man eh nie ändern möchte? 
Dafür ist der RAM eines AVR einfach zu teuer (im Vergleich zum Flash).

von Harald K. (kirnbichler)


Lesenswert?

Oliver S. schrieb:
> Laufen tut es da im Simulator. Mit dem Debugger kann man das dann
> debuggen.

Das ist schön für den Simulator, nur ist es hilfreicher, den Code auf 
dem eigentlichen Controller laufen zu lassen. Der sieht nämlich dann 
auch die realen In- und Outputs, auch wenn das bei diesem 
Initialisierungsproblem nicht wirklich relevant ist.

Der Simulator ist ein Relikt aus der Zeit, als man nichts anderes hatte 
bzw. man mit sündhaft teuren ICEs hantieren musste. Sowas ist Schnee von 
gestern.

Andreas schrieb:
> Zum Debuggen müsste
> ich doch die Fuses für JTAG setzen.

Oh, damit schießt man sich natürlich in den Fuß, insbesondere, wenn das 
auch noch ein µC ist, der nichts anderes kann. Neuere AVRs können u.a. 
debugWIRE, was nur einen Pin belegt (der, da sonst /RESET, sonst auch 
nicht oft verwendet wird). UPDI, das andere AVRs haben, braucht auch nur 
einen Pin.

Nun gut; wenn Du drauf angewiesen bist, diesen alten µC verwenden zu 
müssen, und das JTAG-Interface nicht nutzen kannst, dann bist Du auf so 
etwas wie den Simulator oder vollständige Intuition angewiesen.

von Andreas (ah3112)


Lesenswert?

Jörg W. schrieb:
> Welchen Sinn sollte es haben, konstante Strings im RAM vorzuhalten, die
> man eh nie ändern möchte?
Der ursprüngliche Gedanke war, da ich ausreichend RAM zur Verfügung 
habe, aber der Flash eventuell zu klein werden könnte, das direkt in den 
RAM zu legen. Da ich mich aber in den letzten Tagen dazu entschlossen 
habe einen anderen Controller zu nehmen, der ausreichend Flash hat, kann 
das jetzt auch dahin.

Harald K. schrieb:
> Nun gut; wenn Du drauf angewiesen bist, diesen alten µC verwenden zu
> müssen,
Angewiesen bin ich nicht. Aber das ist einer derjenigen, mit denen ich 
seit Jahren spiele, den also kenne.

von Oliver S. (oliverso)


Lesenswert?

Harald K. schrieb:
> Neuere AVRs können u.a. debugWIRE

Was genau hast du an

Andreas schrieb:
> Atmega162

nicht verstanden?

Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Andreas schrieb:
> Der ursprüngliche Gedanke war, da ich ausreichend RAM zur Verfügung
> habe, aber der Flash eventuell zu klein werden könnte, das direkt in den
> RAM zu legen.

Trugschluss. Alle Konstanten im RAM brauchen eh gleich viel Flash noch 
dazu – woher sollte denn sonst der RAM beim Start initialisiert werden?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Vermutlich hat der Crash überhaupt nichts mit der Initialisierung der
Menüdatenstruktur zu tun. Du hattest diese ja nur deswegen im Verdacht,
weil der alte GCC des Atmel Studios dort wegen der Nutzung einer neueren
Spracherweiterung einen Fehler gemeldet hat.

Andreas schrieb:
> Ich tendiere im Moment dazu den ganzen Kram in den Flash zu verlagern,
> in der Hoffnung, dass ich dann die Probleme nicht habe.

Das ist sicher sinnvoll, aber ich würde damit noch ein wenig warten, bis
der eigentliche Fehler gefunden ist. Sonst läufst du Gefahr, den Fehler
zu kaschieren, ohne ihn behoben zu haben. Irgendwann wird er dann erneut
zuschlagen.

Ich würde eher hier ansetzen:

Andreas schrieb:
> Heute habe ich noch eine weitere Datei erstellt.

Möglicherweise hast du den Fehler damit eingeschleppt. Sicher ist das
aber nicht, denn der Fehler könnte auch schon vorher existiert haben,
ist aber erst durch die Einführung des neuen Moduls (und der damit
verbundenen Verschiebung von Code- und Datenfragmenten) zu Tage
getreten.

Andreas schrieb:
> Zum Debuggen müsste
> ich doch die Fuses für JTAG setzen. Da ich den Controller aber im
> memory-mapped betreibe, würde das doch nicht funktionieren.

Dann versuch es mit LED-Debugging (was tu ja teilweise bereits getan
hast) und/oder Printf-Debugging.

von Harald K. (kirnbichler)


Lesenswert?

Yalu X. schrieb:
> Dann versuch es mit LED-Debugging (was tu ja teilweise bereits getan
> hast) und/oder Printf-Debugging.

Das ist Debugging nach Gehör. Da ist der Simulator doch besser, bei dem 
kann man Breakpoints setzen, Adressen ansehen etc.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Andreas schrieb:
> Ich tendiere im Moment dazu den ganzen Kram in den Flash zu verlagern,
> in der Hoffnung, dass ich dann die Probleme nicht habe.

Irgendwie passen die Typen doch nicht zusammen.  Das mit __flash zu 
dekorieren ändert doch nix daran.

> Johann L. schrieb:
>> Schau mal im MAP File, ob das auch Effekt hat.  Ob die Symbole verwendet
>> werden, hängt nämlich an der Version der Binutils.  Das ld Script muss
>> dazu so beginnen:
> Das finde ich da überhaupt nicht drin.

Das Map-File enthält auch kein ld Skript, sondern Werte von Symbolen, 
deren Lokatierung etc.  Default ld Skripte werden zu Referenz 
installiert in $prefix/avr/lib/ldscripts.

> Ich habe folgendes darin gefunden:
> [c]
> Name             Origin             Length             Attributes
> text             0x00000000         0x00004000         xr
> data             0x00800500         0x00008000         rw!x

Sieht doch gut aus.

Wie wird eigentlich SP initialisiert?  Default für __stack ist RAMEND 
aus <avr/io.h>, und das wird durch -Tdata oder __DATA_REGION_ORIGIN__ 
nicht beeinflusst.  Evtl. ist also noch Symbol __stack zu definieren 
damit der Startup-Code SP entsprechend setzt.

... SP scheint auch zu passen. Interner RAM ovn ATmega162 geht von 0x100 
bis 0x4ff.  Brauch XRAM extra Wait-States?

von Andreas (ah3112)


Lesenswert?

Jörg W. schrieb:
> Trugschluss. Alle Konstanten im RAM brauchen eh gleich viel Flash noch
> dazu – woher sollte denn sonst der RAM beim Start initialisiert werden?
Ok, das war mir nicht bekannt. Dann hätte ich das direkt machen sollen.

Yalu X. schrieb:
> Das ist sicher sinnvoll, aber ich würde damit noch ein wenig warten, bis
> der eigentliche Fehler gefunden ist. Sonst läufst du Gefahr, den Fehler
> zu kaschieren, ohne ihn behoben zu haben. Irgendwann wird er dann erneut
> zuschlagen.
Das ist auch meine Befürchtung. Insbesondere, da hier jetzt mehrfach 
angedeutet wurde, dass die Initialisierung korrekt war.

Yalu X. schrieb:
> Möglicherweise hast du den Fehler damit eingeschleppt.
Glaube ich nicht. Es gibt zwei Dateien, die exakt das identische machen. 
Nur mit anderen Variablen, aber für die Anzeige dieselben Funktionen 
aufrufen. Bis die zweite Datei dazugekommen ist, hat es "funktioniert".

Ich hatte gestern schon die Vermutung:

Yalu X. schrieb:
> denn der Fehler könnte auch schon vorher existiert haben,
> ist aber erst durch die Einführung des neuen Moduls (und der damit
> verbundenen Verschiebung von Code- und Datenfragmenten) zu Tage
> getreten.

Ich zeige hier mal die Funktionen, die auf die entsprechenden Strings 
und Arrays zugreifen. Vielleicht sieht ja jemand den Fehler. Aber der 
Compiler läuft ohne Fehlermeldung durch:
1
void show_all_display_lines(t_display lcdtextzeile) {
2
3
  lcd_clrscr();
4
  for(int i = 0; i < LCD_LINES; i++) {
5
    show_one_display_line(0,i, lcdtextzeile.text_zeile[i]);
6
    for(int j= 0; j < strlen(lcdtextzeile.text_zeile[i]); j++) {
7
      if((lcdtextzeile.text_zeile[i][j] == KEY_UP_CODE)   ||
8
          (lcdtextzeile.text_zeile[i][j] == KEY_DOWN_CODE)  ||
9
          (lcdtextzeile.text_zeile[i][j] == KEY_ENTER_CODE))
10
      {
11
          lcd_gotoxy(j, i);
12
        switch(lcdtextzeile.text_zeile[i][j]) {
13
          case KEY_UP_CODE: {
14
              lcd_data(KEY_UP);
15
            break;
16
          };
17
          case KEY_DOWN_CODE: {
18
              lcd_data(KEY_DOWN);
19
            break;
20
          };
21
          case KEY_ENTER_CODE: {
22
              lcd_data(KEY_ENTER);
23
            break;
24
          };
25
        } // switch(lcdtextzeile.text_zeile[i][j]) {
26
      } // if(lcdtextzeile.text_zeile[i][j] ==
27
    } // for(int j= 0; j < strlen(lcdtextzeile.text_zeile[i]); j++) {
28
  } // for(int i = 0; i < LCD_LINES; i++) {
29
}
30
31
void show_one_display_line(const uint8_t x, const uint8_t y, const char* text) {
32
33
  lcd_gotoxy(x,y);
34
  lcd_puts(text);
35
}

Kurz zur Erklärung: KEY_UP_CODE, KEY_DOWN_CODE sind von mir im RAM des 
LCD-Display abgelegte Zeichen, wo ich Pfeil-hoch bzw. runter darstelle. 
Hier ist im String ein okatler Wert vorhanden. Damit es nicht zu 
verwirrend wird, habe ich die Strings gestern ein wenig abgeändert. 
Tatsächlich sieht das dann so aus:
1
#define KEY_UP_CODE  0x7D
2
#define KEY_DOWN_CODE  0x7E
3
#define KEY_ENTER_CODE  0x7F
4
5
const t_display eingabe_datum_display = {
6
     // 12345678901234567890
7
    {"   Eingabe Datum",
8
     "Best\341tigen mit \177",
9
     "Weiter mit \176",
10
     "Zur\365ck mit \175"},
11
};
Mit "\341" stelle ich die Umlaute im String dar. Wenn ich direkt 'ä' 
schreibe, stellt das Display irgendwelche merkwürdigen Zeichen dar.

Yalu X. schrieb:
> Dann versuch es mit LED-Debugging (was tu ja teilweise bereits getan
> hast) und/oder Printf-Debugging.
Nach dem Schalten der LEDS befindet sich ein printf("gestartet\n\r");. 
Selbst das sehe ich schon nicht mehr. Zu diesem Zeitpunkt ist der 
Controller aber noch nicht im memory-mapped geschaltet. Das LCD-Display 
also noch gar nicht initialisiert.

Ich habe auch gerade versucht das so umzuschreiben, dass die Texte und 
Strukturen im Flash angelegt werden. Aber da bin ich bisher an meiner 
eigenen Unfähigkeit gescheitert. Liegt daran, dass ich das bisher noch 
nie gemacht hatte und daher damit zu kämpfen habe. Allerdings habe ich 
damit auch sowieso momentan Bauchschmerzen, da ich schon den Verdacht 
hatte, dass der Fehler waonders liegt.

Johann L. schrieb:
> Brauch XRAM extra Wait-States?
Es steht das: "MCUCR = (1<<SRE) | (1<<SRW10);" drin. Das wurde erst bei 
der Initialsierung des LCDs gemacht. Das habe ich gerade mal direkt am 
Anfang des Programms gesetzt. Leider ändert aber nichts.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wie sieht's denn mit Debugging / Simulation aus?

Soweit ich sehe ist der Code ja recht simpel bzw. braucht keine Hardware 
außer dem LCD (das man einfach simulieren kann).  Zumindest bei AVRtest 
könnte ich dich da unterstützen.

https://github.com/sprintersb/atest

von Andreas (ah3112)


Lesenswert?

Johann L. schrieb:
> Wie sieht's denn mit Debugging / Simulation aus?
> Soweit ich sehe ist der Code ja recht simpel bzw. braucht keine Hardware
> außer dem LCD (das man einfach simulieren kann).  Zumindest bei AVRtest
> könnte ich dich da unterstützen.
> https://github.com/sprintersb/atest
Danke. Ich sehe mir das später mal an und versuche das ans laufen zu 
bringen. Muss jetzt erstmal weg.

von Andreas (ah3112)


Lesenswert?

Johann L. schrieb:
> https://github.com/sprintersb/atest
Ich habe das gerade mal von der console aufgerufen mit
1
avrtest -s 0x4000 -d -mmcu=avr5 lcd.elf
Da tut sich gar nichts. Das Programm wird zwar aufgerufen, aber bleibt 
hängen. Es wird auch nichts ausgegeben/angezeigt.

Sind meine Parameter falsch? Oder was muss ich eingeben, damit mir auch 
irgendwas angezeigt wird. Damit ich mir etwas Tipperei erparen kann, 
habe ich die elf Datei nur in "LCD.elf" umbenannt. Aber das wird es ja 
wohl nicht sein.

Ich bin jetzt auch erstmal weg und komme erst am Abend dazu wieder 
reinzusehen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Andreas schrieb:
> Johann L. schrieb:
>> https://github.com/sprintersb/atest
> Ich habe das gerade mal von der console aufgerufen mit
>
1
> avrtest -s 0x4000 -d -mmcu=avr5 lcd.elf
2
>
> Da tut sich gar nichts. Das Programm wird zwar aufgerufen, aber bleibt
> hängen.

Ist ja auch klar.  AVRtest simuliert keine I/O -- und selbst wenn, würde 
kein LCD simuliert werden und die Kommunikation nicht funktionieren.

1) Zunächst wird ein exit-atmega162.o generiert. Dazu in den avrtest 
Ordner gehen und
1
make exit-atmega162.o
ausführen.

Das exit-atmega162.o Modul definiert Kommunikation mit dem Simulator so 
dass printf() etc. funktioniert wie auf einem normalen PC.  Es wird bei 
jedem Build mit hinzugelinkt.

2) Dann wird der Code so angepasst, dass er auch auf einem PC laufen 
würde (keine Ports einlesen, keine ISRs, etc).  Ports schreiben (wie zum 
Beispiel Wait-States setzen oder LED blinken) können drin bleiben, haben 
aber außer dem Setzen des SFRs keinen Effekt.

Der Code wird mit -DAVRTEST_H übersetzt.  Oder mit -include 
.../avrtest.h was AVRTEST_H ebenfalls setzt.  avrtest.h wird nur 
gebraucht wenn Syscalls direkt genutzt werden sollen.

2a) LCD-Initialisierung wird nicht gebraucht, also zum Beispiel
1
#ifndef AVRTEST_H
2
    lcd_init();
3
#endif
oder
1
void lcd_init (void)
2
{
3
#ifndef AVRTEST_H
4
    // LCD init.
5
#endif
6
}


Die LCD-Routinen werden so erweitert:
1
void lcd_gotoxy (int x, int y)
2
{
3
#ifdef AVRTEST_H
4
    printf ("lcd_gotoxy(%d,%d)\n", x, y);
5
#else
6
    // normaler lcd_gotoxy code
7
#endif
8
}
9
10
void lcd_puts (const char *text)
11
{
12
#ifdef AVRTEST_H
13
    printf ("lcd_puts(\"%s\")", text); // falls text im RAM
14
    printf ("lcd_puts(\"%S\")", text); // falls text im PROGMEM / __flash
15
#else
16
    // normaler lcd_puts code
17
#endif
18
}

AVRtest ist wie gesagt low-level; Debugging ist damit nicht möglich. 
Man kann sich aber die simulierten Instruktionen anzeigen lassen, etwa:
1
avrtest_log -mmcu=avr5 lcd.elf -m 10000 -v

-m begrenzt die Anzahl simulierter Instruktionen.  Hilfreich wenn sich 
ein Programm aufhängt.

-v zeigt welche ELF Program Header geladen werden.

-d ist nicht notwendig.  RAM wird durch den Startup-Code initialisiert.

Beispiel:
1
#include <stdio.h>
2
3
int x = 1234;
4
5
int main (void)
6
{
7
    printf ("Hallo x=%d\n", x);
8
    return 0;
9
}
1
$ (cd /someplace/avrtest; make exit-atmega162.o)
2
$ avr-gcc -mmcu=atmega162 -Os -o main.elf main.c /someplace/avrtest/exit-atmega162.o
3
$ avrtest_log -mmcu=avr5 main.elf -m 10000 -v
4
>>> Load NOTE .note.gnu.avr.deviceinfo: mcu="atmega162": Flash 0x0 -- 0x4000-1 = 16 KiB
5
>>> strtab[3045] 461 entries, 61 usable, 16 functions, 45 other, 230 bad, 27 unused vectors
6
>>> Load PHDR 0x000000 -- 0x00071b (vaddr = 0x000000) "rx"  text
7
>>> Load PHDR 0x00071c -- 0x000745 (vaddr = 0x800100) "rw"  data
8
0000: JMP     
9
10
+++[0] __vectors --> __init 
11
0072: CLR     (R1)<-00 (SREG)->'' (SREG)<-'Z' 
12
0074: OUT     (R1)->00 (SREG)<-'' 
13
0076: LDI     (R28)<-ff 
14
0078: LDI     (R29)<-04 
15
007a: OUT     (R29)->04 (SPH)<-04 
16
007c: OUT     (R28)->ff (SPL)<-ff 
17
18
+++[0] __init --> __do_copy_data 
19
007e: LDI     (R17)<-01 
20
0080: LDI     (R26)<-00 
21
...
22
+++[1] main --> exit 
23
00f2: STS     (R24)->00 (012a)<-00 
24
00f6: STS     (R25)->00 (012b)<-00 
25
00fa: JMP     
26
27
+++[1] exit --> _exit 
28
070c: LDS     (012a)->00 (R24)<-00 
29
0710: LDS     (012b)->00 (R25)<-00 
30
0714: *** SYSCALL #30: exit 0: (R24)->0000 
31
32
 exit status: EXIT
33
      reason: exit 0 function called
34
     program: main.elf
35
exit address: 000718
36
total cycles: 2613
37
total instr.: 1704

: Bearbeitet durch User
von Andreas (ah3112)


Lesenswert?

Danke für Deine Erklärungen.

Johann L. schrieb:
> 1) Zunächst wird ein exit-atmega162.o generiert. Dazu in den avrtest
> Ordner gehen und
make exit-atmega162.o
> ausführen.
Das hat schon mal funktioniert.

Mit dem Rest werde ich mich jetzt beschäftigen und mich wieder melden.

: Bearbeitet durch User
von Andreas (ah3112)


Lesenswert?

Nach einigen leichten Schwierigkeiten, die aber an mir lagen, ging das 
Ganze  erstaunlich gut. Danke Dir nochmal.

Ich bekomme immer den Exit Status: Timeout. Aufgerufen habe ich das mit:
1
avrtest_log -mmcu=avr5 main.elf -m 10000 -v >inh

Die gesamte Datei, wo ich mir die Ausgabe umgelenkt habe, ist jetzt der 
Einfachheit halber nicht beigefügt.
1
>>> Load NOTE .note.gnu.avr.deviceinfo: mcu="atmega162": Flash 0x0 -- 0x4000-1 = 16 KiB
2
>>> strtab[4361] 887 entries, 80 usable, 40 functions, 40 other, 566 bad, 23 unused vectors
3
>>> Load PHDR 0x000000 -- 0x0010ff (vaddr = 0x000000) "rx"  text
4
>>> Load PHDR 0x001100 -- 0x001581 (vaddr = 0x800500) "rw"  data
5
0000: RJMP    
6
7
+++[0] __vectors --> __init 
8
0070: CLR     (R1)<-00 (SREG)->'' (SREG)<-'Z' 
9
0072: OUT     (R1)->00 (SREG)<-'' 
10
0074: LDI     (R28)<-ff 
11
0076: LDI     (R29)<-04 
12
0078: OUT     (R29)->04 (SPH)<-04 
13
007a: OUT     (R28)->ff (SPL)<-ff 
14
15
+++[0] __init --> __do_copy_data 
16
007c: LDI     (R17)<-09 
17
007e: LDI     (R26)<-00 
18
0080: LDI     (R27)<-05 
19
0082: LDI     (R30)<-00 
20
0084: LDI     (R31)<-11 
21
0086: RJMP    
22
008c: CPI     (###)->82 (R26)->00 (SREG)->'' (SREG)<-'CH' 
23
008e: CPC     (R27)->05 (R17)->09 (SREG)->'CH' (SREG)<-'CNSH' 
24
0090: BRNE    (SREG)->'CNSH'  Z->0
25
26
................................................................
27
Das hier zwischen habe ich jetzt mal weggelassen.
28
................................................................
29
30
092c: SUBI    (###)->01 (R18)->fd (R18)<-fc (SREG)->'I' (SREG)<-'NSI' 
31
092e: SBCI    (###)->00 (R24)->a9 (R24)<-a9 (SREG)->'NSI' (SREG)<-'NSI' 
32
0930: SBCI    (###)->00 (R25)->14 (R25)<-14 (SREG)->'NSI' (SREG)<-'I' 
33
0932: BRNE    (SREG)->'I'  Z->0
34
35
36
 exit status: TIMEOUT
37
      reason: instruction count limit reached
38
     program: LCD_Display_Gaerbox.elf
39
exit address: 00092c
40
total cycles: 1256848
41
total instr.: 1000000

Meine erste Vermutung war, dass die Anzahl der "-m 10000" zu klein war. 
Daraufhin habe ich das um Faktor 10 erhöht. Dasselbe Ergebnis. Nochmal 
um Faktor 10 erhöht. Ebenfalls dasselbe Ergebnis.

In dem Programm warte ich auch auf Tasten. Aber daran liegt der Timeout 
nicht. Ich bin das ganze Programm durchgegangen. Dort, wo auf Tasten 
gewartet wird, habe ich es entweder auskommentiert oder einen festen 
Wert zurückgegeben:
1
#ifdef AVRTEST_H
2
3
  keys = (1 << BUTTON_PIN_DOWN);
4
  printf ("keys in wait_key: %d\n", keys);
5
  return keys;
6
#else
7
......
8
#endif

Ich bekomme von den ganzen printf im Programm auch nichts angezeigt. 
Folgendes habe ich z.B. abgeändert:
1
#ifndef AVRTEST_H
2
    lcd_init(LCD_DISP_ON);
3
#else
4
    printf("Statt lcd_init\n\r");
5
#endif
So, wie ich das verstanden habe, müsste ich das doch sehen. Daraufhin 
habe ich in der main.c "#include "avrtest.h" eingefügt. Damit war aber 
das Problem, dass in den anderen Dateien dann "AVRTEST_H" nicht mehr 
bekannt war. Daher habe ich in jeder Datei das "#include "avrtest.h"" 
eingefügt. Es ist aber trotzdem nichts zu sehen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Andreas schrieb:
> Ich bekomme von den ganzen printf im Programm auch nichts angezeigt.

Kann mehrere Gründe haben:

- exit-atmega162.o wurde nicht zugelinkt.
- AVRTEST_H is nicht #define'd.
- Mit aktiviertem Logging werden Strings von printf nicht als Ganzes
  ausgegeben, sondern von "Hallo" zuerst ein H, dann hundert Befehle
  später ein a, dann hundert Befehle später ein l, ...

printf() Ausgaben sieht man besser mit -no-log, also Logging 
deaktiviert.

> Meine erste Vermutung war, dass die Anzahl der "-m 10000" zu klein war.
> Daraufhin habe ich das um Faktor 10 erhöht. Dasselbe Ergebnis. Nochmal
> um Faktor 10 erhöht. Ebenfalls dasselbe Ergebnis.

Das Programm IST vermutlich in eine Endlosschleife wie alle Embedded 
Anwendungen :-)

Rufe einfach mal exit(wert) auf oder return wert in main() an einer 
Stelle wo die Ausführung aus jeden Fall hinkommen soll(te),

Falls das Programm in einer unerwarteten oo-Schleife ist, dann siehtst 
du am Ende des Logs wo das Programm so rumwuselt.

: Bearbeitet durch User
von Andreas (ah3112)


Lesenswert?

Johann L. schrieb:
> Das Programm IST vermutlich in eine Endlosschleife wie alle Embedded
> Anwendungen :-)
Das stimmt natürlich.

Johann L. schrieb:
> Rufe einfach mal exit(wert) auf oder return wert in main() an einer
> Stelle wo die Ausführung aus jeden Fall hinkommen soll(te),
Mache ich morgen und gebe Bescheid.

von Andreas (ah3112)


Lesenswert?

Ich habe mich doch noch dran gesetzt und das getestet.

Um zu gucken, ob das Programm in einer Endlosschleife läuft, habe ich an 
diversen stellen returns eingegeben. Das funktionierte auch soweit.

Direkt in der main wird eine Funktion (lcd_generatechar_for_buttons();) 
aufgerufen, bei der Zeichen im Ram des LCD-Displays erzeugt werden. Wenn 
ich die Funktion nicht aufrufe, sondern so wie unten auskommentiere
1
    // Enter/Up und Down-Taste als Character im CG-RAM des LCDs speichern
2
    //lcd_generatechar_for_buttons();
3
    return 5;
4
    // loop forever
5
    index = 0;
6
    for (;;) {
7
        for (;;) {
8
          show_all_display_lines(mainmenu[index].text);
9
          new_index = mainmenu[index].fp();
10
          if(new_index == 0)
11
            index = mainmenu[index].previous;
12
          else
13
            index = mainmenu[index].next;
14
        }
15
    }

bekomme ich beim Aufruf von
1
avrtest_log -mmcu=avr5 LCD_Display_Gaerbox.elf -m 10000 -v -no-log >inh

folgenden Text ausgegeben:
1
>>> Load NOTE .note.gnu.avr.deviceinfo: mcu="atmega162": Flash 0x0 -- 0x4000-1 = 16 KiB
2
>>> strtab[3760] 600 entries, 63 usable, 23 functions, 40 other, 322 bad, 22 unused vectors
3
>>> Load PHDR 0x000000 -- 0x0008e9 (vaddr = 0x000000) "rx"  text
4
>>> Load PHDR 0x0008ea -- 0x000945 (vaddr = 0x800500) "rw"  data
5
Timerinitialsierung deaktiviert
6
gestartet
7
8
Statt lcd_init
9
10
11
 exit status: ABORTED
12
      reason: exit 5 function called
13
     program: LCD_Display_Gaerbox.elf
14
exit address: 0008e6
15
total cycles: 5573
16
total instr.: 3373
Es werden jetzt auch die printfs angezeigt.

Nehme ich die Funktion wieder rein, dann habe ich folgende Ausgabe:
1
>>> Load NOTE .note.gnu.avr.deviceinfo: mcu="atmega162": Flash 0x0 -- 0x4000-1 = 16 KiB
2
>>> strtab[3839] 632 entries, 66 usable, 26 functions, 40 other, 338 bad, 22 unused vectors
3
>>> Load PHDR 0x000000 -- 0x000953 (vaddr = 0x000000) "rx"  text
4
>>> Load PHDR 0x000954 -- 0x0009ed (vaddr = 0x800500) "rw"  data
5
Timerinitialsierung deaktiviert
6
gestartet
7
8
Statt lcd_init
9
10
lcd_command(64)
11
lcd_data(4)
12
lcd_data(14)
13
lcd_data(21)
14
lcd_data(36)
15
lcd_data(
16
 exit status: TIMEOUT
17
      reason: instruction count limit reached
18
     program: LCD_Display_Gaerbox.elf
19
exit address: 000472
20
total cycles: 15968
21
total instr.: 10000
Es wird bei "lcd_data(" schon abgebrochen.

Ich hatte mich schon gefreut und das Programm ohne die object-datei für 
avrtest wieder auf den Controller geladen, dabei diese Funktion 
auskommentiert. Es ist aber dasselbe Ergebnis. Die Software läuft nicht.
Ich kann in der Funktion aber auch keinen Fehler sehen. Hier mal, wie 
die Funktion aussieht.
1
// Anzahl der Buttons
2
#define CTRL_CODES  3
3
4
#define KEY_UP    0
5
#define KEY_DOWN  1
6
#define KEY_ENTER  2
7
8
uint8_t ctrl_key[][8] = {
9
  // Taste hoch
10
  { 0b00000100,   //    X
11
    0b00001110,    //   XXX
12
    0b00010101,    //  X X X
13
    0b00100100,    //    X
14
    0b00000100,    //    X
15
    0b00000100,    //    X
16
    0b00000100,
17
    0b00000100},
18
  // Taste runter
19
  { 0b00000100,
20
    0b00000100,
21
    0b00100100,    //    X
22
    0b00000100,    //    X
23
    0b00000100,    //    X
24
    0b00010101,    //  X X X
25
    0b00001110,   //   XXX
26
    0b00000100},    //    X
27
  // Enter-Taste, Möglichkeit 1
28
  { 0b00000001,   //      X
29
    0b00000101,   //      X
30
    0b00001001,    //    1 X
31
    0b00010001,    //   1  X
32
    0b00011111,    //   XXXX
33
    0b00010000,    //   1  X
34
    0b00001000,    //    1 X
35
    0b00000100},   //      X
36
  // Enter-Taste, Möglichkeit 2
37
  { 0b00000001,   //      X
38
    0b00000001,   //      X
39
    0b00000101,    //    1 X
40
    0b00001001,    //   1  X
41
    0b00011111,    //   XXXX
42
    0b00001000,    //   1  X
43
    0b00000100,    //    1 X
44
    0b00000000},
45
};
46
47
void lcd_generatechar_for_buttons()
48
{
49
    // Startposition des 1. Zeichens (KEY_UP) einstellen
50
  lcd_command(1<<LCD_CGRAM);      // set CG RAM start address 0
51
  for(int i = 0;i < CTRL_CODES;i++)
52
    for (uint8_t j=0; j<8; j++)
53
      lcd_data(ctrl_key[i][j]);  // Bitmuster übertragen
54
}
An lcd_data und lcd_command kann es nicht liegen, die sehen momentan so 
aus:
1
void lcd_command(uint8_t cmd)
2
{
3
#ifdef AVRTEST_H
4
5
    printf ("lcd_command(%d)\n", cmd);
6
7
#else
8
9
    lcd_waitbusy();
10
    lcd_write(cmd,0);
11
#endif
12
}
13
14
void lcd_data(uint8_t data)
15
{
16
#ifdef AVRTEST_H
17
18
    printf ("lcd_data(%d)\n", data);
19
20
#else
21
22
    lcd_waitbusy();
23
    lcd_write(data,1);
24
#endif
25
}
Um auszuschliessen, dass es am Atmega liegt, habe ich einen anderen 
Atmega162 genommen. Dasselbe Ergebnis.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Andreas schrieb:
> lcd_data(36)
> lcd_data(
>  exit status: TIMEOUT
>       reason: instruction count limit reached
>      program: LCD_Display_Gaerbox.elf
> exit address: 000472
> total cycles: 15968
> total instr.: 10000[/c]
> Es wird bei "lcd_data(" schon abgebrochen.

Offenbar weil -m 10000 Instruktionen erreicht wurden.  Also -m erhöhen 
oder weglassen (-m 0 simuliert ohne Obergrenze an Instruktionen).

printf ist ja sehr aufwändig.  Alternativ kann man die LOG Funktionen 
aus avrtest.h verwenden, die fast ohne Overhead Daten an den PC ausgeben 
können.

Und oben habe ich AVRtest erwähnt weil ich dich da unterstützen kann bei 
der Verwendung; das war aber nicht als Empfehlung zu verstehen AVRtest 
zu verwenden.  Wie du siehst ist das recht mühsam.

Keiner kennt dein Programm so gut wie du, und bevor du zu AVRtest 
greifst würde ich erst mal Standard-Techniken verwenden:

Du hast vermutlich schon eine recht konkrete Vorstellung wo es hakt im 
Programm, also

- Nochmals den Code checken.  Insbesondere Warnings aktivieren und 
beheben.

- Checken dasss der erzeugte Code das tut was die C-Quelle ausdrückt
  (bzw. was du ausdrücken willst). Etwa: -save-temps -fverbose-asm -g0
  und dann s-Files checken.

- Code debuggen.  Debugger gibt's ja einige, etwa Simulavr mit
  gdbserver Interface oder in XYZ Studio.

AVRtest ist da eher last resort und eigentlich dafür konzipiert, 
(Regression)Tests möglichts schnall abzuarbeiten.  Mit AVRtest zu 
"debuggen" ist mühsam, und ohne Vorstellung wo es ungefähr hakt ist's 
noch 10x mühsamer...

von Andreas (ah3112)


Lesenswert?

Mir ist heute eine andere Idee gekommen. Ich bin mir allerdings nicht 
sicher, ob mein Gedankengang richtig ist.

Als ich beim Linker noch nicht angegeben hatte, dass der RAM extern ist, 
hat mir avr-size irgendwas an RAM von 110% angezeigt bezogen auf den 
Atmega162.

Sämtliche Texte habe ich noch im RAM. Ich gehe mal davon aus, dass die 
in den externen RAM geladen werden, bevor mein Programm überhaupt 
anfängt zu laufen. Wenn der Compiler die beim Programmstart in den 
externen RAM laden will, ist der in meinem Programm noch gar nicht 
initialisiert. Soll heißen der Controller kann den externen RAM noch gar 
nicht ansprechen.

Kann das was mit dem Problem zu tun haben?

von Harald K. (kirnbichler)


Lesenswert?

Andreas schrieb:
> Ich gehe mal davon aus, dass die
> in den externen RAM geladen werden, bevor mein Programm überhaupt
> anfängt zu laufen.

Ja, das macht der Startupcode, der zu Deinem Programm dazugelinkt wird. 
Der wird vor Aufruf der Funktion main() ausgeführt.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Andreas schrieb:
> Sämtliche Texte habe ich noch im RAM. Ich gehe mal davon aus, dass die
> in den externen RAM geladen werden, bevor mein Programm überhaupt
> anfängt zu laufen. Wenn der Compiler die beim Programmstart in den
> externen RAM laden will, ist der in meinem Programm noch gar nicht
> initialisiert. Soll heißen der Controller kann den externen RAM noch gar
> nicht ansprechen.
>
> Kann das was mit dem Problem zu tun haben?

Falls sowas wie Wait-States o.ä. gesetzt werden müssen, dann ja.

libgcc kopiert .data LMA nach .data VMA in .init4; XRAM muss also davor 
konfiguriert / ansprechbar gemacht weden:
1
__attribute__((__used__,__unused__,__naked__,__section__(".init3")))
2
static void init_xram (void) // Niemals von Hand aufrufen!
3
{
4
    // Code
5
}

Strikt nach GCC darf in naked-Funktionen nur Inline Asm stehen, aber 
wenn der Code einfach genug ist geht auch C.

Für init-Sections siehe: 
https://avrdudes.github.io/avr-libc/avr-libc-user-manual/mem_sections.html#sec_dot_init

von Andreas (ah3112)


Lesenswert?

Harald K. schrieb:
> Ja, das macht der Startupcode, der zu Deinem Programm dazugelinkt wird.
> Der wird vor Aufruf der Funktion main() ausgeführt.

Johann L. schrieb:
> Falls sowas wie Wait-States o.ä. gesetzt werden müssen, dann ja.

Eure beiden Hinweise haben mir zur Lösung des Problems weitergeholfen. 
Ich habe jetzt folgendes in die Software eingefügt.
1
__attribute__((__used__,__unused__,__naked__,__section__(".init3")))
2
3
static void init_xram (void) // Niemals von Hand aufrufen!
4
{
5
    MCUCR = (1<<SRE) | (1<<SRW10);
6
    //EMCUCR |= (1 << SRW01) | (1 << SRW00) | (1 << SRW11);
7
}

Jetzt läuft es. Die Zeile mit EMCUCR ist nicht erforderlich. Das ganze 
ist auch reproduzierbar. Kommentiere ich das wieder aus, dann tut sich 
gar nichts mehr.

Ganz lieben Dank nochmal an alle, die sich hier Gedanken und mir 
Vorschläge gemacht haben.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Andreas schrieb:
> Johann L. schrieb:
>> Falls sowas wie Wait-States o.ä. gesetzt werden müssen, dann ja.
>
> Eure beiden Hinweise haben mir zur Lösung des Problems weitergeholfen.

Womit sich folgendes bewahrheitet:

> Andreas schrieb:
>> Ich tendiere im Moment dazu den ganzen Kram in den Flash zu verlagern,
>> in der Hoffnung, dass ich dann die Probleme nicht habe.
>
> Das ist sicher sinnvoll, aber ich würde damit noch ein wenig warten, bis
> der eigentliche Fehler gefunden ist. Sonst läufst du Gefahr, den Fehler
> zu kaschieren, ohne ihn behoben zu haben.

Mit Strings im Flash wäre das Problem erstmal weg, aber sobald andere 
Variablen im Static Storage wären käme das Problem wieder zurück ... an 
anderer Stelle und mit anderen Artefakten.

von Andreas (ah3112)


Lesenswert?

Johann L. schrieb:
> Mit Strings im Flash wäre das Problem erstmal weg, aber sobald andere
> Variablen im Static Storage wären käme das Problem wieder zurück ... an
> anderer Stelle und mit anderen Artefakten.
Vollkommen richtig.

Nachdem ich das untenstehende gelesen habe,

Jörg W. schrieb:
> Trugschluss. Alle Konstanten im RAM brauchen eh gleich viel Flash noch
> dazu – woher sollte denn sonst der RAM beim Start initialisiert werden?

habe ich mich auch geärgert. Allerdings nicht über den Hinweis von Jörg 
sondern über meine Blödheit. Wenn ich vorher mal mein Gehirn 
eingeschaltet hätte, hätte mir eigentlich selber klar sein müssen, dass 
mein Gedankengang, das in den RAM zu legen um Flash zu sparen, 
vollkommen Banane war, da die sowieso erstmal im Flash vorhanden sein 
müssen.

Ich lasse die Initialisierung über xram auch sicherheitshalber drin.

Ich werde das in den nächsten Tagen ändern und das nicht mehr in den RAM 
legen. Hier werde ich mich dann nochmal melden, da ich das bisher noch 
nie gemacht hatte.

: Bearbeitet durch User
von Andreas (ah3112)


Lesenswert?

Um die Strings in den Ram zu legen, habe ich mich weitestgehend hier an 
das Tutorial gehalten. Aber so richtig funktioniert es nicht. Ich hätte 
mal die Bitte, dass mir jemand einen Tipp gibt, was ich falsch mache 
bzw. wie ich es anders machen soll.

Wie ich es befürchtet habe, breche ich mir daran einen ab, die Strings 
sauber in den flash zu legen und auch korrekt anzuzeigen. Ich sitze seit 
gestern daran und habe es heute morgen erst geschafft, das ins Ram zu 
legen. Aber ich komme nicht weiter.

Hier mal, wie es bisher war, als das ganze noch im Ram war.
1
typedef struct display
2
{
3
  const char *text_zeile[LCD_DISP_LENGTH];
4
} t_display;
5
6
typedef struct {
7
  t_display text;
8
  const int8_t previous;
9
  const int8_t next;
10
  int8_t ( *fp )( void );
11
} t_menu;
12
13
14
void show_all_display_lines(t_display lcdtextzeile) {
15
16
  lcd_clrscr();
17
  for(int i = 0; i < LCD_LINES; i++) {
18
    show_one_display_line(0,i, lcdtextzeile.text_zeile[i]);
19
  } // for(int i = 0; i < LCD_LINES; i++) {
20
}
21
22
void show_one_display_line(const uint8_t x, const uint8_t y, const char* text) {
23
24
  lcd_gotoxy(x,y);
25
  lcd_puts(text);
26
}

Ich konnte in der Funktion show_all_display_lines mit 
lcdtextzeile.text_zeile[i] die Strings ansprechen und auch anzeigen.

So ist der neue Stand im Moment:
1
typedef struct display
2
{
3
  const char __flash *text_zeile[LCD_DISP_LENGTH];
4
} t_display;
5
6
typedef struct menu {
7
  const t_display text;
8
  const int8_t previous;
9
  const int8_t next;
10
  int8_t ( *fp )( void );
11
} t_menu;
12
13
#define FSTR(X) ((const __flash char[]) { X } )
14
15
const t_menu main_menu[] __attribute__((progmem)) = {
16
   {{FSTR(" 0 Testzeile 1"),
17
   FSTR(" 0 Testzeile 2"),
18
   FSTR(" 0 Testzeile 3"),
19
   FSTR(" 0 Testzeile 4")} ,0 ,1 ,anzeige_version},
20
   {{FSTR(" 1 Testzeile 3"),
21
   FSTR(" 1 Testzeile 3"),
22
   FSTR(" 1 Testzeile 3"),
23
   FSTR(" 1 Testzeile 4")} ,1 ,1 ,eingabe_betrieb},
24
};

Ich kann die Strings mit
1
show_display_lines(main_menu[index].text.text_zeile);
aufrufen und anzeigen.
show_display_lines macht folgendes:
1
void show_display_lines(const __flash char * const* lcd) {
2
3
  lcd_clrscr();
4
  for(int i = 0; i < LCD_LINES; i++) {
5
    lcd_gotoxy(0,i);
6
         lcd_puts_p((PGM_P)pgm_read_word(lcd++));
7
    }
8
}
Die Texte werden auch sauber auf dem LCD-Display angezeigt. Da die Texte 
in einem Array sind, muss ich die über den Pointer "lcd++" hochzählen. 
Hier bin ich mir nicht sicher, ob das jetzt nur zufällig funktioniert 
und ich Glück habe. Oder ich mir möglicherweise irgendwann ein Problem 
hole, weil ich hier voraussetze, dass die Zeiger auf die Strings alle 
hintereinander liegen.

Am liebsten wäre mir, wenn ich die weiterhin als array über den index 
ansprechen könnte. Sofern ich mir mit dem Hochzählen des Pointers keine 
neuen Probleme einhole, könnte ich damit auch leben.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Andreas schrieb:
1
> void show_display_lines(const __flash char * const* lcd) {
2
> 
3
>   lcd_clrscr();
4
>   for(int i = 0; i < LCD_LINES; i++) {
5
>     lcd_gotoxy(0,i);
6
>          lcd_puts_p((PGM_P)pgm_read_word(lcd++));
7
>     }
8
> }

Das kann nich sein. lcd ist entweder ein Typ oder ein Objekt.

PGM_P ist veraltet, wirf das raus.

pgm_read_word braucht es mit __flash auch nicht mehr. Einfach:
1
lcd_puts_p (lcd++);

Und dann natürlich mit -Waddr-space-convert.

von Andreas (ah3112)


Lesenswert?

Johann L. schrieb:
> Das kann nich sein. lcd ist entweder ein Typ oder ein Objekt.
Ich verstehe nicht was Du meinst. Habe jetzt die ganze Zeit rumprobiert. 
Die einzige Lösung, die funktioniert ist die:
1
void show_display_lines(const t_display* lcd) {
2
3
  lcd_clrscr();
4
  for(int i = 0; i < LCD_LINES; i++) {
5
    lcd_gotoxy(0,i);
6
    lcd_puts_p((PGM_P)pgm_read_word(&lcd->text_zeile[i]));
7
    }
8
}
9
10
    show_display_lines(&main_menu[index].text);

Aber ich muss (PGM_P) und pgm_read_word nehmen, ansonsten bekomme ich 
entweder Fehler beim compilieren oder es werden nur Hieroglyphen 
angezeigt.

Beitrag #7863370 wurde von einem Moderator gelöscht.
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Andreas schrieb:
> Johann L. schrieb:
>> Das kann nich sein. lcd ist entweder ein Typ oder ein Objekt.
> Ich verstehe nicht was Du meinst.

Vergiss es war'n Glitch in meinem Brain v0.9beta.

> Aber ich muss (PGM_P) und pgm_read_word nehmen, ansonsten bekomme ich
> entweder Fehler beim compilieren

Dann fehlen irgendwo __flash Qualifier. -Waddr-space-convert sollte 
sagen,. wo.

Eigentlich sollte doch
1
void lcd_puts_p (const __flash char*);
2
...
3
lcd_puts_p (lcd->text_zeile[i]);

(ohne &) oder nicht? Oben war doch
1
> void show_display_lines(const __flash char * const* lcd)
sollte das nicht so?
1
void show_display_lines(const __flash char * const __flash *lcd)

von Andreas (ah3112)


Lesenswert?

Ich komme nicht weiter, glaube aber auch, dass ich mittlerweile den Wald 
vor lauter Bäumen nicht mehr sehe.

Es wird mir zwar die Fehlermeldung angezeigt, aber das sagt mir  nichts.
1
./test_lcd.c:182:10: warning: conversion from address space 'generic' to address space '__flash' [-Waddr-space-convert]
2
          show_all_display_lines(mainmenu[index].text.text_zeile);
3
          ^~~~~~~~~~~~~~~~~~~~~~

Ist meine definition der Arrays und Textstrings überhaupt richtig?
1
typedef struct display
2
{
3
  const __flash char *text_zeile[LCD_DISP_LENGTH];
4
} t_display;
5
6
typedef struct menu {
7
  const t_display text;
8
  const int8_t previous;
9
  const int8_t next;
10
  int8_t ( *fp )( void );
11
} t_menu;
Wenn das schon falsch ist, dann dürfte der Rest auch nicht 
funktionieren.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Kannst du mal ein minimales compilierbares Beispiel zusammenkopieren?

von Andreas (ah3112)


Angehängte Dateien:

Lesenswert?

Jörg W. schrieb:
> Kannst du mal ein minimales compilierbares Beispiel
> zusammenkopieren?
Hatte etwas gedauert. Meine eigentliche Planung war heute das Ganze neu 
aufzusetzen. Daher hatte ich meine ganzen Versuche gelöscht und auch 
nicht gesichert.

Damit das einfacher ist, habe ich nur eine Datei daraus gemacht und 
sämtliche überflüssigen Dateien weggelassen. Die Funktionen für das LCD 
habe ich abgeändert und nur einzelne Funktionen eingefügt. Das Programm 
läuft natürlich nicht auf der realen Hardware. Aber ich habe über 
mehrere Arten versucht was vernünftiges hinzubekommen. Bei sämtlichen 
Funktionen habe ich als Kommentar die Auswirkungen in der tatsächlichen 
Hardware hingeschrieben und welche Warnungen ich beim Compilieren 
bekomme.

Am Anfang der Datei ist auch als Kommentar eingefügt, mit welchen 
Parametern Compiler und Linker aufgerufen werden.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Gut, danke. Mal ein paar Anmerkungen:
1
#define lcd_write(d,rs) if (rs) //…

Gefährlich, das so als Makro zu schreiben. Das verhält sich hinter einem 
"if" nicht mehr sinnvoll wie eine Anweisung. Der übliche Trick, das zu 
umgehen, ist der auf den ersten Blick etwas schräg anmutende Konstrukt
1
 do {  } while(0)

Aber: das würde ich an deiner Stelle eh nicht als Makro schreiben, 
sondern als inline-Funktion:
1
static void inline lcd_write(char d, bool rs) {
2
  if (rs)
3
    *(volatile uint8_t*)(LCD_IO_DATA) = d;
4
  else
5
    *(volatile uint8_t*)(LCD_IO_FUNCTION) = d;
6
}
(Das "inline" wird es nicht brauchen, das erkennt der Compiler allein, 
solange es "static" ist.)

Selbst als Makro sollte man das durch Zeilenumbrüche übersichtlicher 
schreiben:
1
#define lcd_write(d,rs)                          \
2
  do {                                           \
3
    if (rs)                                      \
4
      *(volatile uint8_t*)(LCD_IO_DATA) = d;     \
5
    else                                         \
6
      *(volatile uint8_t*)(LCD_IO_FUNCTION) = d; \
7
  } while (0)
1
#define FSTR(X) ((const __flash char[]) { X } )
2
3
const t_menu main_menu[] __attribute__((progmem)) = {

Wenn du __flash benutzt, dann doch überall, und nicht plötzlich wieder 
mit Attributen mischen.
1
void lcd_puts_p(const char *progmem_s)
2
// print string from program memory on lcd (no auto linefeed)
3
{
4
    register char c;
5
6
    while ( (c = pgm_read_byte(progmem_s++)) ) {
7
        lcd_putc(c);
8
    }
9
}

Davon abgesehen, dass "register" ein unsinniges Schlüsselwort von vor 50 
Jahren ist, bleib doch auch hier bei __flash:
1
void lcd_puts_p(const __flash char *s)
2
{
3
   char c;
4
5
   while ((c = *s++) != '\0')
6
     lcd_putc(c);
7
}
… dann erübrigen sich auch solche Typecasts wie:
1
        lcd_puts_p((PGM_P)lcd++);
OK, das sind jetzt nur meine Anmerkungen vom ersten Draufgucken. 
Bestimmt hat Johann noch mehr …

: Bearbeitet durch Moderator
Beitrag #7863696 wurde vom Autor gelöscht.
von Andreas (ah3112)


Lesenswert?

Danke erstmal. Das mit dem write habe ich jetzt nur aus der lib von 
Peter Fleury zur LCD-Ansteuerung kopiert, damit ich einigermaßen zeigen 
konnte, wie der Code ist.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Das hier:
1
#define LCD_LINES         4
2
#define LCD_DISP_LENGTH      20
3

4
typedef struct display
5
{
6
  const char __flash *text_zeile[LCD_DISP_LENGTH];
7
} t_display;
ist mir auch nicht klar.

Erstens: warum willst du da jeweils zwanzig Zeiger drin haben? Belegen 
tust du davon dann vier. Müsste also LCD_LINES heißen. Zweitens, wofür 
braucht das noch eine eigene struct?

Probier mal, das alles auf __flash umzubauen und schau, ob das Compilat 
dann Sinn hat. Gern auch mal die erzeugte Assemblerdatei ansehen.

von Andreas (ah3112)


Lesenswert?

Jörg W. schrieb:
> Das hier:#define LCD_LINES         4
> #define LCD_DISP_LENGTH      20
> …
> typedef struct display
> {
>   const char __flash *text_zeile[LCD_DISP_LENGTH];
> } t_display;
> ist mir auch nicht klar.

Stimmt. Jetzt wo Du es schreibst, fällt es mir auch auf. Gedacht war 
immer LCD_Lines. Warum ich da LCD_DISP_LENGTH reingeschrieben habe, 
weiss ich auch nicht. Bei der gesamten Programmierung habe ich in den 
Schleifen auch immer LCD_LINES genommen.

Jörg W. schrieb:
> Zweitens, wofür
> braucht das noch eine eigene struct?

Meine ersten Versuche waren, dass ich da jeweils Zeile1, Zeile2 usw. 
drin stehen hatten. Nachdem das funktioniert hatte, wollte ich das etwas 
verbessern und habe daraus ein Array gemacht und dabei ist es geblieben. 
Wenn ich mir da jetzt Gedanken drüber mache, ist das natürlich Quatsch 
und ich kann das Array direkt hierein:
1
typedef struct menu {
2
  const t_display text;
3
  const int8_t previous;
4
  const int8_t next;
5
  int8_t ( *fp )( void );
6
} t_menu;
packen.

Heute oder morgen werde ich das ändern und auch mit LCD-Display testen 
und wieder berichten.

Danke Dir erstmal für die Hinweise.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

Habs mal etwas aufgeräumt.
1
#define FLIT(X) ((const __flash char[]) { X } ) // AVR-LibC v2.3
2
3
typedef struct menu
4
{
5
    const __flash char *texte[LCD_LINES];
6
    const int8_t previous;
7
    const int8_t next;
8
    int8_t (*fp) (void);
9
} t_menu;
10
11
const __flash t_menu main_menu[] =
12
{
13
    {
14
        {
15
            FLIT(" 0 Testzeile 1"),
16
            FLIT(" 0 Testzeile 2"),
17
            FLIT(" 0 Testzeile 3"),
18
            FLIT(" 0 Testzeile 4")
19
        }, 0, 1, anzeige_version
20
    },
21
    {
22
        {
23
            FLIT(" 1 Testzeile 1"),
24
            FLIT(" 1 Testzeile 2"),
25
            FLIT(" 1 Testzeile 3"),
26
            FLIT(" 1 Testzeile 4")
27
        }, 1, 1, eingabe_betrieb
28
    }
29
};

Ausgabe ist dann einfach so:
1
void show_display_lines (const __flash t_menu *menu)
2
{
3
    lcd_clrscr();
4
    
5
    const __flash char *const __flash *texte = & menu->texte[0];
6
    for (int i = 0; i < LCD_LINES; i++)
7
    {
8
        lcd_gotoxy (0, i);
9
        lcd_puts_p (*texte++);
10
    }
11
}
12
13
int main(void)
14
{
15
    int index, new_index;
16
    ...
17
    printf_P (PSTR("gestartet\n"));
18
19
    index = 0;
20
    for (;;)
21
    {
22
        for (;;)
23
        {
24
            const __flash t_menu *menu = &main_menu[index];
25
            show_display_lines (menu);
26
            new_index = menu->fp();
27
            if (new_index == 0)
28
                index = menu->previous;
29
            else
30
                index = menu->next;
31
32
#ifdef AVRTEST_H
33
            static int count;
34
            if (++count > 4)
35
                return 0;
36
#endif
37
        }
38
    }
39
}
1
$ avr-gcc -Werror -mmcu=atmega162 -Os -o test_lcd.elf -DF_CPU=1000000 -Wall -Waddr-space-convert -include $AVRTEST/avrtest.h test_lcd.c $AVRTEST/exit-atmega162.o -Wl,-Map,test_lcd.map
2
$ avr-objdump -h -d -S -r -j .text -j .data -j .rodata -j .eeprom -j .boot test_lcd.elf > test_lcd.lst
3
$ avr-objdump -P mem-usage test_lcd.elf
4
5
test_lcd.elf:     file format elf32-avr
6
AVR Memory Usage
7
----------------
8
Device: atmega162
9
10
Program:    2512 bytes (15.3% Full)
11
(.text + .data + .rodata + .bootloader)
12
13
Data:         38 bytes (3.7% Full)
14
(.data + .bss + .noinit)
15
16
$ avrtest -mmcu=avr5 test_lcd.elf
17
gestartet
18
19
lcd_clrscr()
20
@LCD(0,0): 0 Testzeile 1
21
@LCD(0,1): 0 Testzeile 2
22
@LCD(0,2): 0 Testzeile 3
23
@LCD(0,3): 0 Testzeile 4anzeige_version()
24
lcd_clrscr()
25
@LCD(0,0): 1 Testzeile 1
26
@LCD(0,1): 1 Testzeile 2
27
@LCD(0,2): 1 Testzeile 3
28
@LCD(0,3): 1 Testzeile 4eingabe_betrieb()
29
lcd_clrscr()
30
@LCD(0,0): 1 Testzeile 1
31
@LCD(0,1): 1 Testzeile 2
32
@LCD(0,2): 1 Testzeile 3
33
@LCD(0,3): 1 Testzeile 4eingabe_betrieb()
34
lcd_clrscr()
35
@LCD(0,0): 1 Testzeile 1
36
@LCD(0,1): 1 Testzeile 2
37
@LCD(0,2): 1 Testzeile 3
38
@LCD(0,3): 1 Testzeile 4eingabe_betrieb()
39
lcd_clrscr()
40
@LCD(0,0): 1 Testzeile 1
41
@LCD(0,1): 1 Testzeile 2
42
@LCD(0,2): 1 Testzeile 3
43
@LCD(0,3): 1 Testzeile 4eingabe_betrieb()
44
 exit status: EXIT
45
      reason: exit 0 function called
46
     program: test_lcd.elf
47
exit address: 0009b0
48
total cycles: 10015462
49
total instr.: 8009073

Die vielen Cycles kommen vom wait.

von Andreas (ah3112)


Lesenswert?

Johann L. schrieb:
> Habs mal etwas aufgeräumt.
Wahnsinn. Läuft alles einwandfrei. Das hätte ich nie und nimmer 
hinbekommen. Ganz lieben Dank und schöne Ostern.

von Andreas (ah3112)


Lesenswert?

Jörg W. schrieb:
> Gefährlich, das so als Makro zu schreiben. Das verhält sich hinter einem
> "if" nicht mehr sinnvoll wie eine Anweisung. Der übliche Trick, das zu
> umgehen, ist der auf den ersten Blick etwas schräg anmutende Konstrukt
Hierzu habe ich noch zwei Fragen.

In der Library von Peter Fleury stand auch
1
#define lcd_read(rs) (rs) ? *(volatile uint8_t*)(LCD_IO_DATA) : *(volatile uint8_t*)(LCD_IO_FUNCTION)

Das habe ich jetzt auch geändert in
1
static inline uint8_t lcd_read (bool rs)
2
{
3
    uint8_t d;
4
5
    if (rs)
6
        d = *(volatile uint8_t*)(LCD_IO_DATA);
7
    else
8
        d = *(volatile uint8_t*)(LCD_IO_FUNCTION);
9
    return d;
10
}
1. Ist diese Änderung aus dem gleichen Grund angebracht, wie Jörg 
geschrieben hat?

2. Kann ich mir dadurch irgendwas reinholen? Das Display funktiniert 
damit aber.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Andreas schrieb:
> Ist diese Änderung aus dem gleichen Grund angebracht, wie Jörg
> geschrieben hat?

Ja.

> Kann ich mir dadurch irgendwas reinholen? Das Display funktiniert damit
> aber.

Mehr Klarheit im Code. :-)

Denk dran, sowas wie die Peter-Fleury-Bibliotheken stammt noch aus dem 
vorigen Jahrtausend. Da war gerade die Unterstützung eher kleiner 
Architekturen im GCC noch lange nicht so ausgefeilt wie heute. Da konnte 
man mit einem Makro schon mal etwas „Mikro-Optimierung“ machen – 
heutzutage ist das aufgrund des schlechter wartbaren Sourcecodes eher 
hinderlich.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Andreas schrieb:
> static inline uint8_t lcd_read (bool rs)
> {
>     uint8_t d;
>     if (rs)
>         d = *(volatile uint8_t*)(LCD_IO_DATA);
>     else
>         d = *(volatile uint8_t*)(LCD_IO_FUNCTION);
>     return d;
> }

Das ist grundsätzlich korrekt, aber mir gefällt dieser Stil nicht so 
gut: Das if-else macht nichts anderes als Daten raus zu liefern. Die 
werden aber erst umständlich auf die "d" Variable zugewiesen, also als 
Seiteneffekt. Außerdem ist die Variable am Anfang uninitialisiert, d.h. 
es steht irgendwas zufälliges drin. Klar könnte man erstmal mit "=0;" 
initialisieren, aber das macht's nicht besser weil das kein sinnvoller 
Wert ist.

Man könnte versehentlich die Variable auslesen bevor dann später ein 
Wert zugewiesen wird. Bei so einem einfachen Beispiel ist das zwar nicht 
so wahrscheinlich, aber man sollte sich angewöhnen so etwas 
grundsätzlich "sauber" umzusetzen (also: jede lokale Variable hat immer 
einen sinnvollen Wert), damit man das bei komplexeren Situationen 
reflexhaft richtig macht.

In anderen Sprachen hat if-else einen Rückgabewert, und das letzte 
Statement einer Funktion ist automatisch der Rückgabewert der Funktion, 
dort kann man das prinzipiell so in der Art umsetzen:
1
uint8_t lcd_read (bool rs)
2
{
3
    if (rs)
4
        *(volatile uint8_t*)(LCD_IO_DATA);
5
    else
6
        *(volatile uint8_t*)(LCD_IO_FUNCTION);
7
}

Dort gibt es keine uninitialisierten Variablen, und der Datenfluss ist 
direkt ersichtlich. Da C aber eine byte-Verarbeitungssprache aus den 
1970ern ist, geht das hier leider nicht. Man kann sich mit dem ternären 
?: Operator behelfen, es also letztendlich genau wie im Makro machen:
1
static inline uint8_t lcd_read (bool rs)
2
{
3
    return rs ?
4
        *(volatile uint8_t*) LCD_IO_DATA
5
      :
6
        *(volatile uint8_t*) LCD_IO_FUNCTION;
7
}

Das sieht zwar nicht ganz so schön aus, aber dafür vermeidet man 
ebenfalls die uninitialisierten Variablen und braucht keine 
Seiteneffekte, sondern hat den direkten Datenfluss.

von Andreas (ah3112)


Lesenswert?

Jörg W. schrieb:
> Mehr Klarheit im Code. :-)
Ok, Hauptsache keine Nachteile. :-)

Niklas G. schrieb:
> In anderen Sprachen hat if-else einen Rückgabewert, und das letzte
> Statement einer Funktion ist automatisch der Rückgabewert der Funktion,
> dort kann man das prinzipiell so in der Art umsetzen:
> uint8_t lcd_read (bool rs)
> {
>     if (rs)
>         *(volatile uint8_t*)(LCD_IO_DATA);
>     else
>         *(volatile uint8_t*)(LCD_IO_FUNCTION);
> }

Das so zu machen, war meine erste Idee. Eine innere Eingebung hat mir 
aber gesagt, dass mit meiner Version zu machen und daher hatte ich das 
gar nicht ausprobiert. Habe Deine Version mal gerade getestet. Da kommt:
1
warning: control reaches end of non-void function [-Wreturn-type]

Habe es gerade so geändert.
Niklas G. schrieb:
> static inline uint8_t lcd_read (bool rs)
> {
>     return rs ?
>         *(volatile uint8_t*) LCD_IO_DATA
>       :
>         *(volatile uint8_t*) LCD_IO_FUNCTION;
> }

Compiler und Software arbeiten einwandfrei. Danke

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Niklas G. schrieb:
1
> static inline uint8_t lcd_read (bool rs)
2
> {
3
>     return rs ?
4
>         *(volatile uint8_t*) LCD_IO_DATA
5
>       :
6
>         *(volatile uint8_t*) LCD_IO_FUNCTION;
7
> }

Verwende ich auch so, allerdings mit anderer Formatierung:
1
static inline uint8_t lcd_read (bool rs)
2
{
3
    return rs
4
        ? *(volatile uint8_t*) LCD_IO_DATA
5
        : *(volatile uint8_t*) LCD_IO_FUNCTION;
6
}

Bei mehr als 2 Alternativen dann auch (hier im GCC):
1
// Return the scalar int mode for a modesize of 1, 2, 3, 4 or 8 bytes.
2
static machine_mode size_to_mode (int size)
3
{
4
  return select<machine_mode>()
5
    : size == 1 ? QImode
6
    : size == 2 ? HImode
7
    : size == 3 ? PSImode
8
    : size == 4 ? SImode
9
    : size == 8 ? DImode
10
    : bad_case<machine_mode> ();
11
}

Soweit ganz schon (nur die 2 Makros sind hässlich)
1
template<typename T>
2
T bad_case ()
3
{
4
  gcc_unreachable ();
5
}
6
7
#define select false ? bad_case

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Niklas G. schrieb:

> Außerdem ist die Variable am Anfang uninitialisiert, d.h.
> es steht irgendwas zufälliges drin.

Praktisch existiert sie in diesem Moment noch gar nicht.

> Klar könnte man erstmal mit "=0;"
> initialisieren, aber das macht's nicht besser weil das kein sinnvoller
> Wert ist.

Richtig, daher bin ich auch kein großer Freund dieser "schreib überall 
einen Initializer hin"-Praxis (wie sie bspw. MISRA fordert). Sie 
verhindert nämlich zielsicher, dass einen der Compiler warnt, wenn man 
irgendwo dann vergessen hat, einen sinnvollen Wert zuzuweisen – und 
diese Warnungen beherrschen Compiler mittlerweile sehr sicher, so man 
sie warnen lässt (und nicht etwa die Codeflussanalyse durch -O0 
unterdrückt). Die Warnung hat mir schon längeres Debugging erspart.

> Da C aber eine byte-Verarbeitungssprache aus den
> 1970ern ist, geht das hier leider nicht. Man kann sich mit dem ternären
> ?: Operator behelfen, es also letztendlich genau wie im Makro machen:

Nö.

Natürlich kannst du in der inline-Funktion mehrere return haben. Ist 
übersichtlicher als der ternäre Operator.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Jörg W. schrieb:
> Ist übersichtlicher als der ternäre Operator.

Finde ich überhaupt nicht. Und die vielen Leute die funktionale 
Programmiersprachen nutzen auch nicht.

Jörg W. schrieb:
> Praktisch existiert sie in diesem Moment noch gar nicht.

Spielt für die Überlegungen keine Rolle.

Andreas schrieb:
> Habe Deine Version mal gerade getestet. Da kommt:

Ja so geht es in C ja nicht, wie gesagt..

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Niklas G. schrieb:
>> Ist übersichtlicher als der ternäre Operator.
>
> Finde ich überhaupt nicht.

Gut, Geschmackssache dann. Darüber kann man sich streiten. ;-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Niklas G. schrieb:
> Ja so geht es in C ja nicht, wie gesagt..

GCC könnte aus einem brace block einen Rückkehrwert bilden, aber das ist 
a) compilerspezifisch und b) werden sich andere C-Programmierer fragen, 
was das soll, wenn sie den Code später lesen müssen. Da ist die 
inline-Funktion der einfachere Weg, ob nun mit ternärem Operator oder 
mit zwei return.

: Bearbeitet durch Moderator
von Andreas (ah3112)


Lesenswert?

Da es noch ein weiteres array gibt, was auch nie verändert wird, wollte 
ich dies auch noch in den flash legen und nicht in den Ram.

Bisher sah die Definition so aus:
1
uint8_t ctrl_key[][8] = {

Heute habe ich dann versucht in den Flash zu legen mit:
1
static const __flash uint8_t ctrl_key[][8] = {
2
  // Taste hoch
3
  { 0b00000100,   //    X
4
    0b00001110,    //   XXX
5
    0b00010101,    //  X X X
6
    0b00100100,    //    X
7
    0b00000100,    //    X
8
    0b00000100,    //    X
9
    0b00000100,
10
    0b00000100},
11
  // Taste runter
12
  { 0b00000100,
13
    0b00000100,
14
    0b00100100,    //    X
15
    0b00000100,    //    X
16
    0b00000100,    //    X
17
    0b00010101,    //  X X X
18
    0b00001110,   //   XXX
19
    0b00000100},    //    X
20
  // Enter-Taste, Möglichkeit 1
21
  { 0b00000001,   //      X
22
    0b00000101,   //      X
23
    0b00001001,    //    1 X
24
    0b00010001,    //   1  X
25
    0b00011111,    //   XXXX
26
    0b00010000,    //   1  X
27
    0b00001000,    //    1 X
28
    0b00000100},   //      X
29
  // Enter-Taste, Möglichkeit 2
30
  { 0b00000001,   //      X
31
    0b00000001,   //      X
32
    0b00000101,    //    1 X
33
    0b00001001,    //   1  X
34
    0b00011111,    //   XXXX
35
    0b00001000,    //   1  X
36
    0b00000100,    //    1 X
37
    0b00000000},
38
};

Aufgerufen wird das dann folgendermassen:
1
void lcd_data(uint8_t data);
2
3
void lcd_generate_char_for_buttons()
4
{
5
    // Startposition des 1. Zeichens (KEY_UP) einstellen
6
  lcd_command(1<<LCD_CGRAM);      // CG RAM start address 0
7
  for(int i = 0;i < CTRL_CODES;i++)
8
    for (uint8_t j=0; j<8; j++)
9
      lcd_data(ctrl_key[i][j]);  // Bitmuster übertragen
10
}

Es ist auch vollkommen egal, ob ich das auf eine der untenstehenden 
Möglichkeiten mache:
1
static const __flash uint8_t ctrl_key[][8] = {
2
3
static const uint8_t __flash ctrl_key[][8] = {

Das Ganze funktioniert auch einwandfrei. Eigentlich hatte ich erwartet, 
dass der Compiler mindestens eine Warnung raus gibt. Aber nichts 
dergleichen. Meine Frage ist jetzt, liegt das tatsächlich im Flash oder 
ist das immer noch im Ram?

Dann habe ich noch zwei weitere Fragen.
1. Wo finde ich eine vernünftige Erklärung, wo der Unterschied zwischen 
__flash und PROGMEM ist? PROGMEM habe ich bisher auch noch nie genutzt, 
aber interessieren würde mich das schon.

2. Wo finde ich eine gute Erklärung, wie genau die Syntax für __flash 
bei der Definition ist? Ich fand/finde die Unterstützung hier zwar 
superklasse, aber ich würde das gerne mal verstehen, damit ich beim 
nächsten Mal (hoffentlich) keinen neuen Thread aufmachen muss.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Andreas schrieb:
> Das Ganze funktioniert auch einwandfrei. Eigentlich hatte ich erwartet,
> dass der Compiler mindestens eine Warnung raus gibt. Aber nichts
> dergleichen.

Falls du -Waddr-space-convert erwartest: Das ist aus per default und 
nicht in -Wall, muss also händisch angeschaltet werden.

> Meine Frage ist jetzt, liegt das tatsächlich im Flash oder
> ist das immer noch im Ram?

Blick ins Map-File.  Wie man das erzeugen lässt siehst du bei den 
Befehlen, die ich oben mit angegeben hatte.

> Dann habe ich noch zwei weitere Fragen.
> 1. Wo finde ich eine vernünftige Erklärung, wo der Unterschied zwischen
> __flash und PROGMEM ist?

Flash ist ein Qualifier gemäß ISO/IEC TR 18037 "Embedded C".

"progmem" ist nur ein Attribut, ist also nicht Teil einer Adresse. Es 
wirkt i.W wie Attribut "section" mit paar extra Checks.  Zum Lesen wird 
Inline Asm gebraucht (pgm_read_xxx) während __flash mit normalem C-Code 
(bzw. GNU-C) zugegriffen werden kann.

> 2. Wo finde ich eine gute Erklärung, wie genau die Syntax für __flash
> bei der Definition ist?

Wie gesagt syntaktisch analog zu const bzw. volatile, allerdings nur 
erlaubt für const (bei Zeigern / Adressen) bzw. const + Static Storage 
(bei Objekt-Definition und -Deklaration).

> Ich fand/finde die Unterstützung hier zwar
> superklasse, aber ich würde das gerne mal verstehen, damit ich beim
> nächsten Mal (hoffentlich) keinen neuen Thread aufmachen muss.

Einfach mal schauen, was avr-gcc fürn Code erzeugt:
1
int fun (const __flash int * const * const __flash *p)
2
{
3
    return ***p;
4
}

von Andreas (ah3112)


Lesenswert?

Danke für Deine Erklärung. Map-File lasse ich sowieso immer erstellen. 
Habe gerade mal reingesehen. Dort steht:
1
 .progmem.data.ctrl_key
2
                0x000001a6       0x20 ./display_functions.o

Ich verstehe das so, dass das im Flash ist. Von der Länge würde es 
passen.

Johann L. schrieb:
> Falls du -Waddr-space-convert erwartest:

Das habe ich eingestellt, als Du mir das hier geschrieben hattest. 
Trotzdem gibt es keine warnings.

Johann L. schrieb:
> Einfach mal schauen, was avr-gcc fürn Code erzeugt:
> int fun (const __flash int  const  const __flash *p)
> {
>     return ***p;
> }

Hab ich. Aber so richtig hilft mir das jetzt nicht weiter.
1
.section  .text.fun,"ax",@progbits
2
.global  fun
3
  .type  fun, @function
4
fun:
5
/* prologue: function */
6
/* frame size = 0 */
7
/* stack size = 0 */
8
.L__stack_usage = 0
9
 ;  ../test_lcd.c:72:     return ***p;
10
  movw r30,r24   ; , p
11
  lpm r26,Z+   ; , *p_4(D),
12
  lpm r27,Z+   ; , *p_4(D),
13
 ;  ../test_lcd.c:72:     return ***p;
14
  ld r30,X+   ;  *_1
15
  ld r31,X   ;  *_1
16
 ;  ../test_lcd.c:74: }
17
  lpm r24,Z+   ; ,,
18
  lpm r25,Z+   ; ,,
19
/* epilogue start */
20
  ret

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Andreas schrieb:
> Aber so richtig hilft mir das jetzt nicht weiter.

Dreifache Dereferenzierung halt, wobei die Adresse jedesmal als 
Flash-Adresse bewertet wird, der dann mit LPM gelesen wird.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg W. schrieb:
> Dreifache Dereferenzierung halt, wobei die Adresse jedesmal als
> Flash-Adresse bewertet wird, der dann mit LPM gelesen wird.

Nur die 1te und 3te Dereferenzierung.  Die mttlere liest per LD vom RAM 
weil da kein __flash steht.  3x flash wäre:
1
int fun (const __flash int * const __flash * const __flash *p) ...

: Bearbeitet durch User
von Andreas (ah3112)


Lesenswert?

Jörg W. schrieb:
> der dann mit LPM gelesen wird
Daran, dass dort LPM steht, hatte ich mir schon gedacht, dass auf den 
Flash zugegriffen wird.

Johann L. schrieb:
> 3x flash wäre:
> int fun (const __flash int  const __flash  const __flash *p) ...
Das heisst, vor jedem, was ich im Flash haben will, schreibe ich einfach 
"__flash"?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Andreas schrieb:
> Das heisst, vor jedem, was ich im Flash haben will, schreibe ich
> einfach "__flash"?

Prinzipiell ja, kann aber etwas komplizierter werden wie zum Beispiel
1
const __flash char *p = "Hallo";
Hier ist p ein Flash-Zeiger, aber "Hallo" steht gemäß TR18037 im Generic 
Address-Space.  Man braucht also Verrekungen wie FLIT von oben:
1
const __flash char *p = FLIT("Hallo");

Und dann geht es nicht nur um Lokatierung, sondern auch um Zeiger wie 
z.B. in Funcktionsargumenten.  Da muss __flash (so wie const und 
volatile auch) an der richtigen Seite vom "*" stehen:
1
// Zeiger auf RAM-Position, die Zeiger auf __flash enthält.
2
const __flash char * [const] * p;
3
4
// Zeiger auf Flash-Position, die Zeiger auf RAM enthält.
5
[const] char * const __flash * p;
6
7
// Zeiger auf Flash-Position, die Zeiger auf Flash enthält.
8
const __flash char * const __flash * p;

: Bearbeitet durch User
von Andreas (ah3112)


Lesenswert?

Danke, ich werde mir das mal ausdrucken und gut weglegen

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Johann L. schrieb:
> Jörg W. schrieb:
>> Dreifache Dereferenzierung halt, wobei die Adresse jedesmal als
>> Flash-Adresse bewertet wird, der dann mit LPM gelesen wird.
>
> Nur die 1te und 3te Dereferenzierung.  Die mttlere liest per LD vom RAM
> weil da kein __flash steht.

Stimmt.

Wobei es sicher nur selten sinnvoll ist, Zeiger auf den RAM im Flash zu 
haben, andersrum dagegen schon eher.

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
Noch kein Account? Hier anmelden.