Hallo,
ich benutze eine Funktion, welche HSV-Koordinaten in RGB-Koordinaten
umrechnet. Leider ist diese Funktion 378 Byte groß, was auf einem
ATtiny13 deutlich zu viel ist (damit der Rest des Programms auch noch
Platz hat).
Daher würde ich diese Methode optimieren, finde aber keinen
Optimierungsspielraum mehr:
1
voidsetColorHSV(charhue,charsaturation,charvalue)
2
{
3
unsignedchari,f;
4
unsignedintp,q,t;
5
unsignedcharred=0,green=0,blue=0;
6
7
if(saturation==0)// Farbsättigung ist Null -> Grau
8
{
9
red=green=blue=0;
10
}
11
else// Farbsättigung ist nicht Null -> Einteilen in 60°-Winkel des Farbtons
12
{
13
i=hue/43;
14
f=hue%43;
15
p=(value*(255-saturation))/256;
16
q=(value*((10710-(saturation*f))/42))/256;
17
t=(value*((10710-(saturation*(42-f)))/42))/256;
18
19
switch(i)
20
{
21
case0:red=value;green=t;blue=p;break;
22
case1:red=q;green=value;blue=p;break;
23
case2:red=p;green=value;blue=t;break;
24
case3:red=p;green=q;blue=value;break;
25
case4:red=t;green=p;blue=value;break;
26
case5:red=value;green=p;blue=q;break;
27
}
28
setColorRGB(red,green,blue);
29
}
30
}
P.S.: Die Größe der Funktion habe ich ermittelt indem ich einmal mit
obigem Code kompiliert habe und einmal nachdem ich den gesamten Inhalt
des Funktionsrumpfes auskommentiert hatte.
@Gast:
In der Tat müssen sie keine 16-Bit-Variablen sein. Daher habe ich
1
unsignedintp,q,t;
ersetzt durch
1
unsignedcharp,q,t;
Der benötigte Flash-Speicherplatz reduziert sich jedoch nicht. Ich
vermute, dass es daran liegt, dass während der Berechnung von p,q,t
große Zahlen verwendet werden.
Das teilen durch 256 erscheint mir hier merkwürdig ist das korrekt?
Und ich würde mir da mal das ASM listing ansehen da kannst du dann sehen
wo er wirklich was verbraucht.
Hallo,
du verwendest ja alle 4 Grundrechenarten plus MOD und DIV, da ist das
doch nicht viel, also wird sich auch kaum noch was wegoptimieren lassen.
Ich versuche bei µC oft, alle (!) Divisionen zu vermeiden, weil das die
aufwendigste Operation ist. Also statt / 43 verwende ich * 0,023; das
ist natürlich nur bei Konstanten sinnvoll. Es kann aber auch nach hinten
losgehen, je nach Library und Linker, also ausprobieren.
Gruss Reinhard
AVR Studio + GCC legt im Arbeitsordner einen Ordner "default" an und
darin sollte eine .lss Datei sein. Falls nicht, kannst du dem Compiler
im AVRStudio unter Project->Conf.Options sagen, er solle bitte eins
erstellen.
Gruß
Fabian
>Der benötigte Flash-Speicherplatz reduziert sich jedoch nicht. Ich>vermute, dass es daran liegt, dass während der Berechnung von p,q,t>große Zahlen verwendet werden.
Bei den meisten der Multiplikationen drohen Überläufe.
8 Bit x 8Bit kann größer als 8 Bit werden. Da fehlen
dann noch geeignete casts. Dadurch wird der
Code aber noch größer.
So, ich hab mal ein bißchen rumgespielt:
Casting für die Multiplikationen eingesetzt und ein bißchen
ausgeklammert. Ist dabei von 514 auf 436 Bytes runter gegangen.
Komischerweise schrumpft der Code durch das Casting sogar;)
>Hallo Holger,>>hier hast du eine Vorbelegung
Die ist nicht von mir;)
> if (saturation != 0) // Farbsättigung ist nicht Null -> Einteilen in >60°-Winkel des Farbtons
Ok, ausprobiert, bleibt bei 434 Bytes.
Schmeisst man das komplett raus
if (saturation == 0) // Farbsättigung ist Null -> Grau
Gibts noch mal 6 Bytes geschenkt.
Macht man aus dem switch() einen if(i==x) Spaghetti Code
nochmal 4 Bytes.
Ich glaub das wars dann aber auch. 424 Bytes.
Ich würde es trozdem mal mit 255 anstelle von 256 versuchen... Das
sollte dir auf jedenfall ein 16bit Division erstparen und das Ergebnis
nicht merklich beeinflussen.
Ebenso die gemsichten 42/43 kommen mir komisch vor.
Kompilierst du auch mit Optimierung auf Platz?
>Ich würde es trozdem mal mit 255 anstelle von 256 versuchen... Das>sollte dir auf jedenfall ein 16bit Division erstparen und das Ergebnis>nicht merklich beeinflussen.
Bloß das nicht! Mit 256 zieht er sich nur das Highbyte des
Ergebnisses rein. Passt schon. Bei 255 müsste wirklich
dividiert werden.
>Kompilierst du auch mit Optimierung auf Platz?
Ja, Os.
>Bloß das nicht! Mit 256 zieht er sich nur das Highbyte des>Ergebnisses rein. Passt schon. Bei 255 müsste wirklich>dividiert werden.
So, kurz ausprobiert, 438 Bytes.
Also erst einmal vielen Dank! Das ist in jedem Fall eine verbesserung,
wenn auch eine kleine. Tatsächlich hat sich in der If-Bedingung ein
Programmierfehler eingeschlichen, den aber keiner bemerkt hat:
Das ist der Code mit Fehlerkorrektur und der Behebung des
Programmierfehlers.
Ich bedanke mich für eure Unterstützung, möchte aber gleichzeitig auf
keinen Fall dem Ehrgeiz einiger hier (insbesondere Holgers) einen
Abbruch tun. Wenns noch besser geht, dann gerne! :-)
> eine division durch 256 ist gleichbedeutend mit x>>8 8 bits nach rechts> schieben, falls dein compiler das nicht schon macht ... probiers doch> mal :)
Das is so ziemlich die älteste Optimierung, die ein Compiler von selber
macht...
hallo,
die beiden Zeilen
((10710 - ( (unsigned int)saturation * f) ) / 42) )
((10710 - ((unsigned int)saturation * (unsigned char)(42 - f)) ) / 42)
ergänzen sich zu einer Konstanten:
-> die 2. Zeile ist durch simple Subtraktion der 1. Zeile von einer
Konstanten (255/256) möglich.
matze schrieb:
> @Frank:>> Hat das irgendwelche Nebenwirkungen? Warum macht das der Compiler nicht> selbst? Und: Bei mir spart das genau 8 Byte.
das spart ein wenig am epilog der main-funktion, der ja nicht gebraucht
wird, da ja normalerweise eine endlosschleife in der main werkeln.
aber wieviel es spart hängt glaub ich auch aein wenig vom controller ab,
auf nem tiny13 hat mir das glaub ich 12 Byte gespart. auf nem mega88
warns nur 8B
Wie Vlad sagt, auf einem µC braucht main nichts zurückgeben. Wieviel man
spart, hängt wohl von Compilerversion, Controller etc. ab.
Vielleicht läßt sich auch am makefile noch tunen. Eventuell erlaubt die
Anwendung:
CFLAGS += -mtiny-stack #Stack auf 256 begrenzt (GCC 4.3.0)
Hallo matze,
ich habe mal die Numerik etwas optimiert, den Switch von avr und den
noreturn von Frank übernommen:
1
#include<stdio.h>
2
#include<stdint.h>
3
4
volatileuint8_tdummy;
5
6
voidsetColorRGB(uint8_tr,uint8_tg,uint8_tb){
7
dummy=r;dummy=g;dummy=b;
8
}
9
10
voidsetColorHSV4(uint8_th,uint8_ts,uint8_tv){
11
uint16_tvs=v*s,h6=6*h,f;
12
uint8_ti,p,u,r=v,g=v,b=v;
13
14
p=(v<<8)-vs>>8;
15
i=h6>>8;
16
f=((i|1)<<8)-h6;
17
if(i&1)
18
f=-f;
19
u=((uint32_t)v<<16)-(uint32_t)vs*f>>16;
20
switch(i){
21
case0:g=u;b=p;break;
22
case1:r=u;b=p;break;
23
case2:r=p;b=u;break;
24
case3:r=p;g=u;break;
25
case4:r=u;g=p;break;
26
case5:g=p;b=u;break;
27
}
28
setColorRGB(r,g,b);
29
}
30
31
intmain(void)__attribute__((noreturn));
32
intmain(void){
33
inth=100,s=250,v=255;
34
setColorHSV4(h,s,v);
35
for(;;);
36
}
Man kommt jetzt ohne die Divisionen aus, und die Ergebnisse sind
genauer. Da aber statt der Division nun eine 32-Bit-Multiplikation
benötigt wird, ist der Code-Größe gegenüber der Version von avr kaum
geschrumpft, mit dem GCC 5.3.4 ist er sogar ein ganzes Stück größer
geworden.
Die folgenden Codegrößen beziehen sich jeweils auf das fertig gelinkte
Programm incl. der obigen Dummy-setColorRGB-Funktion, dem obigen Main
und den benötigten Bibliotheksfunktionen. Bei der Originalversion habe
ich den Datentyp der Argumente von char in unsigned char geändert, da
sonst nicht richtig gerechnet wird.
1
------------------------------------
2
AVR-GCC 4.2.4 4.3.4 4.4.2
3
------------------------------------
4
Original 506 476 458
5
Version von avr 438 412 398
6
meine Version 442 454 390
7
------------------------------------
Die Diagramme im Anhang zeigen die Abweichungen der Ergebnisse von
matzes ("deins") und meinem ("meins") Programm zum jeweils exakten Wert,
den ich mit Double-Genauigkeit berechnen lassen habe. Dabei ist s=250,
v=255, h läuft von 0 bis 255.
Ich habe mein Programm nur auf dem PC getestet, dabei aber durch
Einfügen zusätzlicher Casts die 16-Bit-Integer-Rechengenauigkeit des AVR
simuliert. Deswegen vermute ich, dass das Programm auch auf dem AVR
richtig rechnet, aber ganz sicher bin ich mir nicht ;-)
Falls Fehler auftreten, einfach meckern! Vielleicht kann ich dann noch
etwas geradebiegen.