Hallo,
ich kämpfe gerade mit dem SDCC (fuer STM8S).
Ich möchte an eine Funktion eine struct - Variable übergeben und
scheitere kläglich.
Warum funktioniert folgendes Konstrukt unter GCC (egal ob für PC, AVR
oder ARM) anstandslos:
1
structcolvalue
2
{
3
uint8_tr;
4
uint8_tg;
5
uint8_tb;
6
};
7
8
voidrgbtoega(structcolvaluecol,uint8_tnr)
9
{
10
}
11
12
voidp_rgbfromega(structcolvalue*col,uint8_tnr)
13
{
14
}
15
16
structcolvaluergbfromega(uint8_tnr)
17
{
18
structcolvaluetest;
19
20
test.r=12;
21
test.g=24;
22
test.b=48;
23
24
returntest;
25
}
aber mit dem SDCC (3.5.0) keine einzige der 3 "Konstrukten" ?
Die Fehlermeldung des SDCC ist immer der Art:
SDCC cannot pass structure col as ....
Liegt das an mir oder an SDCC und wie kann ich das mit SDCC realisieren?
(Natürlich hab ich jetzt schon eine Menge "gespielt", in der Art: Zeiger
auf struct, nur den Zeiger uebergeben und über den Offset des Zeigers
den struct manipulieren .... manches geht, anderes auch da nicht und
schön ist anderst !
Ralph S. schrieb:> SDCC cannot pass structure col as ....
Und was steht da anstelle der Punkte?
Vor C89 konnten keine Strukturen als Werte übergeben werden (und damit
auch nicht als Rückgabewert von Funktionen verwendet werden), auch
direkte Zuweisungen waren nicht möglich:
1
structblafusel;
2
inta;
3
4
intfunction1(structblaparameter);
5
intfunction2(structbla*parameter);
6
structblafunction3(void);
7
8
a=function1(fusel);
9
a=function2(&fusel);
10
fusel=function3();
Vor C89 war hiervon ausschließlich function2 verwendbar.
Vielleicht ist Dein SDCC etwas ... historisch konfiguriert?
Ein Grund, auf diese Behandlung von Strukturen zu verzichten, ist der
teilweise erhebliche Stackbedarf - hat der von Dir verwendete µC
überhaupt einen ausreichendend großen Stack?
(Du hast geschrieben STM8S; mit dem hab' ich noch nie zu tun gehabt)
Wenn ich mich recht erinnere, unterstützt SDCC auch µCs, die praktisch
gar keinen Stack (oder einen nur wenige Bytes großen) haben.
so, noch einmal überprüft:
Struct nach function2 funktioniert (und ich habe nicht bemerkt, dass die
Fehlermeldung nur von den beiden anderen gekommen ist).
Mir sind nicht mehr, als die 3 Möglichkeiten "irgendetwas" mit dem SDCC
mit struct in der Funktion hinzubekommen als die obigen 3.
Funktion 2 war für mich allerdings nur diejenige, die als allerletztes
Mittel zum Einsatz kommen sollte.
Funktion 1 hat folgende Fehlermeldung:
SDCC cannot pass structure "_col" as function argument
Funktion 3 hat Fehlermeldung:
Function cannot return aggregate. Func body ignored
---------------------------------------------------------------
Rufus Τ. F. schrieb:
> hat der von Dir verwendete> überhaupt einen ausreichendend großen Stack?
Der STM8S103F3P6 hat 1 kByte RAM, hiervon grundsätzlich 513 Bytes für
den Stack (für das Vorhaben mit nur 3 Byte großer Struktur ist der Stack
glaube ich nicht der Fehler)
---------------------------------------------------------------
Rufus Τ. F. schrieb:
> Vielleicht ist Dein SDCC etwas ... historisch konfiguriert?
... ich dachte, dass das der entscheidende Hinweis ist, weil sich in
meinem Makefile ein zusätzliches -c eingeschlichen hat
CLFLAGS =-c --std=c99 --opt-code-size
Das veranlasst den Compiler, den Schalter --std-c99 einfach zu
"überlesen".
Wird das -c entfernt (weil es vor der zu übersetzenden Datei noch einmal
vorkommt) übersetzt SDCC grundsätzlich ohne --std=c89 oder "besser".
Mit --std=c99 übersetzt er Funktion2 klaglos.
Der SDCC ist in sich "irgendwie historisch", auch in der Version 3.6.0
Ich verwende den SDCC deshalb, weil es der einzige freie Compiler für
STM8 ist den ich kenne (und deshalb bin ich sehr sehr froh, dass es ihn
gibt), ansonsten verwende ich arm-none-eabi-gcc für stm32 und lpc, sowie
avr-gcc für avr
--------------------------------------------------------------
Recherche im SDCC Wiki ergibt:
1
SO C11
2
3
Use --std-c11 to compile in this mode.
4
5
Not yet implemented in sdcc:
6
7
- Data type double, long double.
8
- Structures and unions can not be assigned, passed as function
9
parameters or return values.
10
-Intermingled declarations and code (variable declaration no longer
11
restricted to file scope or the start of a compound statement
12
(block)).
13
- Compound literals.
Ich werde also mit den Einschränkungen leben müssen, aber herzlichen
Dank an Rufus, der mir wenigstens Funktion2 ermöglicht hat
... jetzt bin ich halt hergegangen und habe eine Struktur deklariert und
davon eine globale Variable abgeleitet die dann in den den Funktionen
verwendet wird.
Allerdings ungern !
Ralph S. schrieb:> und habe eine Struktur deklariert und davon eine globale Variable> abgeleitet die dann in den den Funktionen verwendet wird.
Das wäre ja nun nicht nötig gewesen.
Die Beispiele function1 und function3 lassen sich problemlos mit
function2 abbilden.
Da sind keine globalen Variablen erforderlich.
Wieso meinst Du, so etwas zu benötigen?
... na ja, weil ich glaube, dass andere mit einer globalen Variablen
besser zurechtkommen als dann mit dem Zeiger zu hantieren.
Im Moment schreib ich Header und Code .... und Doku ... und bin noch am
Überlegen wie es denn besser ist !
Schön wäre halt etwas in der Art gewesen:
struct colvalue rgbfromvalue(uint8_t r, uint8_t g, uint8_t b)
und dann:
void ws_setarray(uint8_t *ledarray, struct colvalue col)
dann hätte man bspw. schreiben können:
ws_setarray(&ledbuffer[0], rgbfromvalue(12,0,100));
So, wie ich das für mich für die STM32 auch gemacht habe. Auch wenn ich
froh bin dass es den SDCC gibt, an solchen Dingen merkt man, dass der
GCC einfach eine Ecke besser ist.
Schaun ma mal ...
Nur das hier geht nicht auf diese Art und weise:
> ws_setarray(&ledbuffer[0], rgbfromvalue(12,0,100));
Das muss man in Teilschritte aufteilen:
1
structcolvaluefarbe;
2
3
rgbfromvalue(12,0,10,&farbe);
4
ws_setarray(ledbuffer,&farbe);
Oder man baut sich eine Variante von ws_setarray, der gleich die
RGB-Werte übergeben werden.
Jedenfalls braucht es für keinen dieser Schritte globale Variablen.
Rufus Τ. F. schrieb:>> void ws_setarray(uint8_t *ledarray, struct colvalue col)> void ws_setarray(uint8_t *ledarray, struct colvalue *p);
hmmm, das weiß ich wohl, dass ich hier auch call by reference machen
kann. Mir gings jetzt nur um die Handhabung (für "Unbedarfte").
Hier definiere ich mir ja "nur" eine (oder mehrere) lokale Variable.
Rufus Τ. F. schrieb:> Das muss man in Teilschritte aufteilen:>> struct colvalue farbe;>> rgbfromvalue(12, 0, 10, &farbe);> ws_setarray(ledbuffer, &farbe);>> Oder man baut sich eine Variante von ws_setarray, der gleich die> RGB-Werte übergeben werden.
... da... bin ich gerade dran am basteln... (um die globale Variable zu
vermeiden)
ein
ws_setarray(uint8_t *ledarray, uint8_t nr, uint8_t r, uint8_t g, uint8_t
b)
hatte ich ja schon.... da war mir allerdings das Aufrufen der Funktion
für jeweils getrennte rgb-Werte zu lange (deswegen ja der "verwerfliche"
Gedanke mit der globalen)
hmmmmmm.... die Variante mit dem Zeiger auf den struct .... ist 180 Byte
länger und bedeutet deutlich mehr Schreibarbeit beim Programmieren.
Dafür ... kann man sich (wie ursprünglich gewollt) relativ gut einzelne
Farben zusammenbasteln und diese dann nur noch per Namen benutzen ...
Das ganze wird eine Software + Header (noch nicht der einen einzigen
Datei entnommen) für WS2812b LEDs in Verwendung mit STM8. :-) irgendwie
bin ich froh, das noch nicht voreilig eingestellt zu haben und ich weiß
noch nicht, ob mir das mit dem Zeiger auf den struct so gefällt. Muß ich
mal eine Nacht drüber schlafen.
Erstmal herzlichen Dank Rufus
PS: anbei der Code wie er momentan ausschaut... Die Aufrufe mit dem
Zeiger auf struct sind schon irgendwie ... gewöhnungsbedürftig !
Ralph S. schrieb:> Die Aufrufe mit dem Zeiger auf struct sind schon irgendwie ...> gewöhnungsbedürftig !
Du machst noch nicht sonderlich lange irgendwas mit C, kann das sein?
Das ist nicht gewöhnungsbedürftig, das ist vollkommen normal - und es
ist in diesem Fall auch deutlich performanter (was mit der Strukturgröße
skaliert).
Es ist erheblich weniger Aufwand, nur die Adresse einer Struktur zu
übergeben, als die komplette Struktur auf den Stack zu kopieren.
Deine Farbstruktur ist nur ein paar Bytes groß, da fällt das noch nicht
sonderlich ins Gewicht, aber Strukturen können beliebig groß werden, und
da wird es sehr, sehr ineffizient, sie per "call-by-value" zu übergeben
oder als Rückgabewert einer Funktion zu nutzen.
Rufus Τ. F. schrieb:> Du machst noch nicht sonderlich lange irgendwas mit C, kann das sein?> Das ist nicht gewöhnungsbedürftig, das ist vollkommen normal - und es> ist in diesem Fall auch deutlich performanter (was mit der Strukturgröße> skaliert).
ich mach schon relativ lange etwas mit C (und auch C++). Mir ist das
schon sehr sehr klar, dass das performanter ist und dass in C ohne
Zeiger nix geht. Es geht schlicht darum, das auch für meine
Auszubildenden handhabbar zu haben (wurden heute interviewt und ich war
überrascht, dass ihnen das schon sehr klar war wie das funktioniert).
Rufus Τ. F. schrieb:> Deine Farbstruktur ist nur ein paar Bytes groß, da fällt das noch nicht> sonderlich ins Gewicht, aber Strukturen können beliebig groß werden, und> da wird es sehr, sehr ineffizient, sie per "call-by-value" zu übergeben> oder als Rückgabewert einer Funktion zu nutzen.
schmunzeln muß, ich würde niemals ein Bild oder in der Art etwas OHNE
Zeiger machen.
Meine Überlegungen gehen in die Richtung, WANN man von etwas abweichen
kann, wann es doch einfacher ist, globale Variable zu verwenden und wann
nicht.
Smile, ich werde es mit Zeiger auf struct realisieren (wie oben im
Anhang).
Es ging mir nicht darum, wie der Code ausschaut, sondern mir ging es
darum, wie der Programmierfluß mit dem Softwaremodul ist. Heute haben
sie damit Farbverläufe programmiert und das hat gut geklappt.
Egal mit welcher Sprache: Irgendwie neige ich dazu, Funktionen ewig
viele Parameter mitzugeben.
C++ ist da "etwas schönes" weil ich dann für vieles eine eigene Methode
machen kann.
Hmmm.. lach, meine Freundin wartet... ich könnte ewig schreiben...
Vielen Dank Rufus
Hallo,
dass man eine Ergebnis-Struktur als Zeiger-Parameter übergibt, ist ganz
normal. Aber man kann auch diesen Zeiger als Ergebnis parallel
zurückliefern. Dann kann man auch Verschachteln - ohne einen Sack voller
globaler Hilfsvariablen.
Funktionsmuster (sicher mit Schreibfehlern):
Ralph S. schrieb:> Recherche im SDCC Wiki ergibt:> SO C11>> Use --std-c11 to compile in this mode.>> Not yet implemented in sdcc:>> - Data type double, long double.> - Structures and unions can not be assigned, passed as function> parameters or return values.> -Intermingled declarations and code (variable declaration no longer> restricted to file scope or the start of a compound statement> (block)).> - Compound literals.>> Ich werde also mit den Einschränkungen leben müssen, aber herzlichen> Dank an Rufus, der mir wenigstens Funktion2 ermöglicht hat
Dabei ist SDCC von der Standardunterstützung her noch einer der besseren
Compiler für STM8:
http://www.colecovision.eu/stm8/compilers.shtml
C89 wird allerdings von IAR besser unterstützt.
Philipp
Ralph S. schrieb:> Ich möchte an eine Funktion eine struct - Variable übergeben und> scheitere kläglich.Ralph S. schrieb:> struct colvalue> {> uint8_t r;> uint8_t g;> uint8_t b;> };
UND???
Warum willst du mit Gewalt mit dem Kopf durch die Wand?
Du hättest für deinen rgb-Farbtopf einen simplen long nehmen können
und fertig ist die Laube.
Für sowas unbedingt einen struct benutzen zu wollen - insbesondere wenn
es der Compiler einem nachdrücklich um die Ohren haut - ist ein mir
unverständlicher Mutwillen. Wozu versuchst du das eigentlich erst?
Mal ganz generell: struct's als Values anstatt als Referenz (sprich
Pointer in C) an Funktionen übergeben zu wollen, ist auf einem heutigen
PC zwar albern, aber dank riesigen RAM's kein Problem. Aber wir sind
hier in µC-Gefilden und da ist das ein Problem.
Noch elender wird es mit dem Vorhaben, Funktionen einen struct
zurückgeben lassen zu wollen. Versuche doch bloß einmal, dir
vorzustellen, WIE eine Funktion ein Gebilde von prinzipiell
unbeschränkter Größe an einen Aufrufer zurückgeben soll.
Bei allen gewöhnlichen Datentypen wird das in einem oder mehreren
Registern getan - aber bei einem Struct? In C gibt es ja nicht einmal
Strings und folglich keinerlei definierten Mechanismus zum Returnieren
eines Strings aus einer Funktion. Bei Pascal wurde das früher auf dem
Stack gemacht, aber das war zur Zeit, als dort Strings maximal 255
Zeichen lang sein konnten. War überschaubar.
Heutzutage geht das bei Pascal und C auf dem PC ganz anders, da wird ein
String oder ein Struct im Heap aufgebaut und übergeben und sowohl
Argumente als auch Returnwerte sind Zeiger dorthin, die nach Aufruf vom
aufrufenden Programm verwertet und dann wieder abgebaut werden müssen.
Aber sowas auf einem µC implementieren zu wollen, ist ne Art Größenwahn
- und das alles nur dazu, daß der C-Programmierer mal bloß nicht über
mikrocontroller-geeignete Verfahrensweisen nachzudenken braucht?
Nee.
Fehlanzeige.
In solchen Fällen erwartet man vom Programmierer, daß er sich Gedanken
darüber macht, was auf der Zielplattform angemessen ist. Und das wäre
im vorliegenden Falle ein simpler long.
W.S.
W.S. schrieb:> Warum willst du mit Gewalt mit dem Kopf durch die Wand?>> Du hättest für deinen rgb-Farbtopf einen simplen long nehmen können> und fertig ist die Laube.
Und dann bei jedem Zugriff mit Bitschieberei und Maskiererei die
einzelnen Werte da rein und raus fummeln? Strukturen sind genau für das,
was der TE hier machen will, da. Er hat drei Werte, die er benennen und
zu einer "Gruppe" zusammenfassen will.
> Für sowas unbedingt einen struct benutzen zu wollen - insbesondere wenn> es der Compiler einem nachdrücklich um die Ohren haut - ist ein mir> unverständlicher Mutwillen. Wozu versuchst du das eigentlich erst?
Weil es sinnvoll ist. Dass der Compiler es nicht annimmt, ist ein
Problem des Compilers.
> Mal ganz generell: struct's als Values anstatt als Referenz (sprich> Pointer in C) an Funktionen übergeben zu wollen, ist auf einem heutigen> PC zwar albern, aber dank riesigen RAM's kein Problem. Aber wir sind> hier in µC-Gefilden und da ist das ein Problem.
Dir ist aber klar, dass dein vorgeschlagener long dort vermutlich
größer ist als diese Struct?
> Noch elender wird es mit dem Vorhaben, Funktionen einen struct> zurückgeben lassen zu wollen. Versuche doch bloß einmal, dir> vorzustellen, WIE eine Funktion ein Gebilde von prinzipiell> unbeschränkter Größe an einen Aufrufer zurückgeben soll.
Jede struct hat eine genau definierte Größe. In diesem Fall 3 Bytes.
[irgendwas über Strings und Heap]
Was faselst du von Strings? Um die geht's doch gar nicht. Was haben die
mit den Beschränkungen des Compilers beim Umgang mit struct zu tun?
Genau gar nichts!
> In solchen Fällen erwartet man vom Programmierer, daß er sich Gedanken> darüber macht, was auf der Zielplattform angemessen ist. Und das wäre> im vorliegenden Falle ein simpler long.
Angemessen wäre ein Compiler, der richtig funktioniert. Wenn so einer
nicht zur Verfügung steht, muss man sich einen Work-Around dafür
einfallen lassen. Das wäre es, was dein long ist: Ein Work-Around.
W.S. ist so richtig "nett", ich weiß sehr wohl was ich mache.
Mein struct ist 3 Byte groß und würde (so der Compiler es zulassen
würde) den Stack mit 3 Byte belasten.
Ein long ist 4 Byte groß !
W.S. schrieb:> Noch elender wird es mit dem Vorhaben, Funktionen einen struct> zurückgeben lassen zu wollen. Versuche doch bloß einmal, dir> vorzustellen, WIE eine Funktion ein Gebilde von prinzipiell> unbeschränkter Größe an einen Aufrufer zurückgeben soll
Hat hier irgendwer etwas von "unbeschränkter Größe" geschrieben, es sind
einfach nur 3 Byte und ich weiß sehr wohl dass es nur 3 Byte sind. Der
Stack eines STM8S103F3P6 ist 513 Byte groß. Wenn man weiß, dass der
Compiler grundsätzlich vor einem Aufruf das Z-Register (16 Bit) und den
Akku (8 Bit) auf den Stack legt, dann von hinten nach vorne die
Parameter, dann ist der Stackbedarf einer 3 Byte struct nicht mehr die
Welt. Vor allen Dingen dann nicht, wenn eine Referenz die als Parameter
mit übergeben wird hier 2 Byte belegt.
Rolf M. schrieb:> Was faselst du von Strings? Um die geht's doch gar nicht. Was haben die> mit den Beschränkungen des Compilers beim Umgang mit struct zu tun?> Genau gar nichts!
Full ACK
Rolf M. schrieb:> Angemessen wäre ein Compiler, der richtig funktioniert. Wenn so einer> nicht zur Verfügung steht, muss man sich einen Work-Around dafür> einfallen lassen. Das wäre es, was dein long ist: Ein Work-Around.
nochmal Full ACK
Nur gefällt mir der long noch weniger als eine Referenz auf einen struct
zu übergeben, deshalb:
es wurde jetzt so realisiert, dass die Funktion eine Referenz auf die
struct als Parameter bekommt und eben Rückgabelos ist.
Nachtrag:
W.S. schrieb:> In solchen Fällen erwartet man vom Programmierer, daß er sich Gedanken> darüber macht, was auf der Zielplattform angemessen ist. Und das wäre> im vorliegenden Falle ein simpler long.
Einen "simplen" long bei einem 8 Bit MCU gibt es nicht !!!
Dieser long wird genauso auf dem Stack gespeichert (und nicht in
Registern), ganz sicherlich dieses bei einem STM8, der außer Akku, Z, Y,
PC und SP keine weiteren Register besitzt.
Ich werde einfach für mich untersuchen, ob der Compiler eine long
Variable auf den Stack legt (wovon ich ausgehe) oder dieses anderst
realisiert (denn das weiß ich im Moment wirklich nicht so genau).
W.S. schrieb:> In solchen Fällen erwartet man vom Programmierer, daß er sich Gedanken> darüber macht, was auf der Zielplattform angemessen ist.
... im übrigen gehe ich her und fange bei mir unbekannten
Microcontrollern an, das Datenblatt zu lesen, deren Struktur und
Speicherorganisation UND, um den Chip oder die Chipfamilie kennen zu
lernen mache erste Versuche in Maschinensprache. DANACH lege ich die
Maschinensprache weg und mache dann C.... C++ mache ich auf dem PC ...
und ich bin am überlegen dieses vllt. auf den größeren STM32 zu tun
(entgegen meiner Überzeugung, C++ auf MCU's nicht machen zu wollen).
Manchmal wissen Leute eben doch was sie tun, auch wenn es nicht den
Anschein hat.
Ralph S. schrieb:>> Einen "simplen" long bei einem 8 Bit MCU gibt es nicht !!!>> Dieser long wird genauso auf dem Stack gespeichert (und nicht in> Registern), ganz sicherlich dieses bei einem STM8, der außer Akku, Z, Y,> PC und SP keine weiteren Register besitzt.>> Ich werde einfach für mich untersuchen, ob der Compiler eine long> Variable auf den Stack legt (wovon ich ausgehe) oder dieses anderst> realisiert (denn das weiß ich im Moment wirklich nicht so genau).
Für SDCC und STM8:
Parameter werden immer auf dem Stack übergeben. Rückgabewerte (mit
Ausnahme von long long) in Registern.
Wo eine Variable sonst liegt entscheidet der Registerallokator. Der hat
bei einem long allerdings deutlich mehr Freiheiten als bei einer struct.
Philipp