Hallo noch mall, ich habe schon wieder ein problem, und zwar:
1
...
2
uint16_tpwm1;
3
uint16_tadcw1;
4
uint16_tadcw2;
5
...
6
if(adcw1>=800)&&(adcw1<=900)){
7
8
adcw2=adcw1-800;
9
pwm1=((adcw2/100)*12024);//pwm1 = x% von adcw2
10
pwm_setting[1]=pwm1;
11
pwm_update();
12
13
}
das Prolem ist, dass adcw2 die richtigen Werte zugewiesen bekommt, aber
die berechnung von pwm1 in die hose geht.
Für eure hilfe danke ich im vorraus.
P.S: Ich glaube, ich brauche nicht zu sagen, dass ich so gut wie keine
ahnung von programieren habe. :)
MFG
> pwm1 = ((adcw2 / 100)*12024); //pwm1 = x% von adcw2
Mal 12024? Bist Du sicher?
Abgesehen davon: Ich hoffe, Du weißt, dass eine Integer-Division erstens
immer eine ganze Zahl als Ergebnis liefert und dass sie zweitens nicht
rundet...
@ Rudolf Bremer (Gast)
Die Berechnung wird mit Integer durchgeführt. D.h. alle Nachkommastellen
sind für die Katz. 800/100 = 8, 899/8 =8 etc.
Siehe Festkommaartihmetik.
So gehts besser. Die Rechung erfolgt in 32 Bit, ausserdem erst
Multiplikation, dann Division.
1
pwm1=(uint32_t)adcw2*12024/100;//pwm1 = x% von adcw2
ups, nein, natürlich ist 1024 gemeint :) Aha, ich wuste es aber nicht,
die Nachkommastellen sind egall, so genau will ich es nicht haben. Und
wie könte ich das am besten machen?
>pwm1=(uint32_t)adcw2*12024/100;//pwm1 = x% von adcw2
2
>
Hmmm, also ich ganz persönlich würde da ein paar Klammern spendieren.
1
pwm1=((uint32_t)adcw2*12024)/100;//pwm1 = x% von adcw2
Sieht doch besser aus, gelle? Und der Compiler kommt auch nicht auf die
Idee, 12024/100 als Konstante zu berechnen.
Aber ich denke nach wie vor, dass der OP eher 1024 meint und nicht
12024...
@ Johannes M. (johnny-m)
>Sieht doch besser aus, gelle? Und der Compiler kommt auch nicht auf die>Idee, 12024/100 als Konstante zu berechnen.
Das darf er nicht, und demzufolge tut er es auch nicht. Weil ne Variable
im Spiel ist.
@ Rudolf Bremer (Gast)
>die Nachkommastellen sind egall, so genau will ich es nicht haben. Und
Schon, aber in deinen Zwischenergebnissen werden sie weggeschmissen!
-> massiver Rundungsfehler.
MfG
Falk
Machst Du ne 10-Bit-PWM? Dann überleg mal, was passiert, wenn Du in
adcw2 100% vorgibst... Ich weiß jetzt zwar nicht, wie Du die Werte
bearbeitest, bevor Du sie an irgendein Compare-Register weitergibst,
aber das dürfte zu einem interessanten Nebeneffekt führen. Kleiner Tipp:
Ein 10-Bit-Wert kann maximal 1023 sein...
ALso es ist 1024 gemeint, nicht 12024, 8 bit PWM. Es handelt sich um die
Software PWM von Falk Brunner:
http://www.mikrocontroller.net/articles/Soft-PWM#Intelligenter_L.C3.B6sungsansatz
Ich frage mich gerade selber warum ich 1024 benutze und nicht 255?!? Wie
sollte denn die "Formel" aussehen, um die PWM mit werte 0-100, also mit
Prozente zu steuern?
@ Rudolf Bremer (Gast)
>Ich frage mich gerade selber warum ich 1024 benutze und nicht 255?!? Wie
Weil dein ADC 10 Bit Werte ausspuckt?
>sollte denn die "Formel" aussehen, um die PWM mit werte 0-100, also mit>Prozente zu steuern?
Man kann auch mit 0..255 prozentual steuern. Halt /255 statt /100
rechnen.
In deinem Beispiel geht das einfach mit
1
pwm1=adcw2/4;
Aber warum einfach, wenns auch umständlich geht ;-)
MFG
Falk
@Falk:
So wie ich das verstanden habe (siehe OP) möchte er den Wertebereich von
800...900 auf 0...100% abbilden (warum auch immer). Dementsprechend
steht in adcw2 eine Zahl von 0 bis 100.
@ Johannes M. (johnny-m)
>So wie ich das verstanden habe (siehe OP) möchte er den Wertebereich von>800...900 auf 0...100% abbilden (warum auch immer). Dementsprechend>steht in adcw2 eine Zahl von 0 bis 100.
Und wozu dann die Multiplikation/Division?
MfG
Falk
Falk Brunner wrote:
> Und wozu dann die Multiplikation/Division?
Um von einem Prozentwert (0...100) auf einen Wert für die PWM (0...1023)
zu kommen. Oder wie würdest Du einen Wert aus einem bestimmten Bereich
auf einen anderen Wertebereich skalieren? Ist im Prinzip nichts anderes
als ne Multiplikation mit 10,24 (wobei 10,23 richtiger wäre...).
Dass er nur eine 8-Bit-PWM hat und dementsprechend die PWM-Werte nur
0...255 werden können, hat er ja mittlerweile gemerkt.
Am besten ist es, ganz auf divisionen zu verzichten. Als einer der
ersten µCs habe die ARM Cortex-M3 ne Divisionseinheit drin, bzw. nen
Befehl, der dies in einer Hand voll Takten realisiert.
Shifts sind da viel effektiver, da sie schneller abgearbeitet werden
können (bei ARM z.B. brauchen alle shifts (0...31) nur einen Takt
braucht). Wie schnell der AVR das schafft, weiss ich nicht, aber
sicherlich schneller als ne Division.
"In deinem Beispiel geht das einfach mit"
1
pwm1=adcw2>>2;
Bleibt man in seiner software auf werten zur basis 2 (also z.B. 0...1023
adc, 0...255 pwm etc) kann man sehr effektiv mit shifts arbeiten.
Für ein User Interface kann man dann ja die prozente einbauen ...
Greetz,
/r.
Hab noch was nettes:
1
voidled(charvalue);
2
3
led_out(1<<(i=++i<8?i:0));
4
5
led_out((0xff>>(0x08-adc/0x7f))&0xff);
led() nimmt 8bit werte entgegen und shiftet die an pos. 8 eines 32Bit
GPIOs. Was machen die funktion in den klammern? ;-)
Bei der zweiten ist absichtlich kein (>>7) drin, da ich sonst nicht an
den Endbereich rankomme (siehe beitrag oben) für die Anzeige. adc sind
10 bit.
Random ... wrote:
>> "In deinem Beispiel geht das einfach mit">
1
>pwm1=adcw2>>2;
2
>
>
Du kannst davon ausgehen, dass die Compilerbauer auch keine
Vollidioten sind und eine Division durch 4 durch einen Shift
ersetzen, so er denn auf der Zielhardware schneller abläuft
als eine Division.
> Bleibt man in seiner software auf werten zur basis 2 (also z.B. 0...1023> adc, 0...255 pwm etc) kann man sehr effektiv mit shifts arbeiten.
Oh, man kann auch mit anderen Werten und Operationen sehr effektiv
arbeiten. Zb. kann der gcc für eine Reihe von Multiplikationen
sehr effektiv ein paar Shifts und Additionen einsetzen um sich
eine Multiplikation zu ersparen. zb. eine Multiplikation mit 3
oder mit 10 oder ...
Wie gesagt: Compilerbauer sind keine Vollidioten und kennen meistens
den Prozessor sehr viel besser als die meisten Anwendungsprogrammierer.
Für den Programmierer aber gilt:
Schreib den Code so, wie er am klarsten ist. Diese Low-Level
Pfriemeleien überlasse dem Compiler. Der kann das nämlich besser.
> Hab noch was nettes:> led_out(1<<(i=++i<8?i:0));
undefined bahaviour und damit illegales C
@Random:
Man kann alles übertreiben! Eine Division durch eine Zweierpotenz hat
der Compiler automatisch durch entsprechende Alternativen zu ersetzen,
oder er taugt nichts. Der Anwender sollte sich nur im Klaren sein, dass
eine Division durch 2^n (bzw. ein %2^n) i.d.R. wesentlich einfacher
durch andere Operationen ersetzt werden kann und dass er deshalb
versucht, durch geschickte Kombinationen mit Multiplikationen dafür
sorgt, dass es möglichst mit /2^n geht.
Aber es geht nunmal nicht immer und dementsprechend kann man Divisionen
durch andere Zahlen nicht immer vermeiden. Deshalb sollte man erstens
solche Divisionen nicht generell verteufeln und außerdem nicht mit
praktisch nicht mehr lesbarem Code rumprotzen, den man auch so schreiben
kann, dass man ihn ohne Papier und Stift entziffern kann.
Der Compiler ist u.a. dazu da, dem Programmierer zumindest einen Teil
der Drecksarbeit abzunehmen.
random wrote:
> oha, das gab haue *duck*>>>> Hab noch was nettes:>>> led_out(1<<(i=++i<8?i:0));>>>undefined bahaviour und damit illegales C>> warum?
Weil du zwischen 2 Sequence Points (in diesem Fall ist ein
Sequence Point: von einem ; zum nächsten ;) nur einmal
schreibend auf eine Variable zugegriffen werden darf.
In dem Moment in dem in einer Expression links vom =
eine Variable vorkommt, die rechts noch mal mit einem ++
steht -> undefined behaviour.
Aus dem selben Grund:
printf( "%d %d", ++i, ++i ); <-- undefined behaviour
i = ++i; <-- undefined bahaviour
foo( ++i, ++i ); <-- undefined behaviour
das sind so die häufigsten Fälle.
Frage. Erkennen die Compiler sowas und schmeissen wenigstens ne Warnung?
Das ist einer der Gründe, warum ich mich für C nicht wirklich begeistern
kann. "Aktive" Anweisungen in Abfragen und Funktionsaufrufen. Lang lebe
Pascal! Naja, eher R.I.P. :-(
MFG
Falk
Falk Brunner wrote:
> Frage. Erkennen die Compiler sowas und schmeissen wenigstens ne Warnung?
Ich kenn keinen.
Wohl auch deshalb nicht, weil sowas nicht in allen Fällen
detektierbar ist. Ich denke da an Übergabe in Funktionen
hinein, in der dann 2 verschiedene Argumente letztendlich
bei ein und derselben Variablen landen.
Grob skizziert
void foo( int* a, int* b )
{
*a = (*b)++;
}
void bar()
{
int i;
foo( &i, &i );
}
es gibt aber doch eine reihenfolge, in der operatoren abgearbeitet
werden. Und in ASM werden doch eh einzelne operationen draus, je nach
architektur.
> printf( "%d %d", ++i, ++i ); <-- undefined behaviour> foo( ++i, ++i ); <-- undefined behaviour
Bei den beiden ist es klar, weil glaub ich nciht festgelegt ist, welcher
der beiden parameter zuerst übergeben wird. So was macht man ja auch
nicht :-)
Für mein Beispiel sollte aber klar geregelt sein, dass erst das ++i
erfolgt (i++ hat natürlich eine andere wirkung), und danach die andere
Zuweisung, egal, wie man es dreht.
> led_out(1<<(i=++i<8?i:0));
also:
i++;
if(i>8) i=0;
led_out(i);
auch die entscheidung, ob i<8 wird vorher getroffen, da ++i ja
ausdrücklich sagt, dass i erst erhöht wird, bevor es gelesen wird für
weitere auswertung...
Ist zwar jetzt OT, aber es interessiert mich trotzdem :-)
Random ... wrote:
> es gibt aber doch eine reihenfolge, in der operatoren abgearbeitet> werden.
Das hat nichts damit zu tun, in welcher Reihenfolge die
Operatoren dann tatsächlich ausgeführt werden.
Bei einem ++ gibt es nur eine Zusage:
Das Inkrement wird irgendwann vor dem nächsten Sequence Point
durchgeführt. Irgendwann. Das kann auch die allerletzte Aktion
vor dem ';' (dem nächsten Sequence Point) sein.
Der Compiler hat alle Freiheiten die Aktion so umzustellen,
wie ihm das in den Kram passt. Der eigentliche Inkrement ist
ein Nebeneffekt und von dem wird nur gefordert, dass er
beim nächsten Sequence Point abgeschlossen sein muss.
> Und in ASM werden doch eh einzelne operationen draus, je nach> architektur.
Spielt keine Rolle. C definiert sich nicht dadurch welche
Assembler Befehle da raus kommen.
>>> printf( "%d %d", ++i, ++i ); <-- undefined behaviour>> foo( ++i, ++i ); <-- undefined behaviour>> Bei den beiden ist es klar, weil glaub ich nciht festgelegt ist, welcher> der beiden parameter zuerst übergeben wird.
Im C Jargon heist das:
Die Reihenfolge der Auswertung von Funktionsargumenten ist
undefiniert. Mit der Übergabe hat das erst mal nichts zu tun.
Vor dem eigentlichen Funktionsaufruf sitzt ein Sequence Point.
Wenn die Funktion dann aufgerufen wird, müssen alle Nebeneffekte
abgeschlossen sein.
> So was macht man ja auch> nicht :-)
:-) Geh mal nach comp.lang.c-c++
Da kommt sowas öfter vor als man denkt.
>> Für mein Beispiel sollte aber klar geregelt sein, dass erst das ++i> erfolgt (i++ hat natürlich eine andere wirkung), und danach die andere> Zuweisung, egal, wie man es dreht.
Ist es nicht.
> i++;> if(i>8) i=0;> led_out(i);
Du verwechselst Operatorenreihung mit Auswertereihenfolge.
An ersterem kann der Compiler nichts drehen, die ist definiert.
Aber an letzterem kann der Compiler rumdrehen wie er lustig
ist. Wenn der Compiler beschliest, den inkrement erst ganz
zum Schluss zu machen (weil ihm dann die Register besser in
den Kram passen) dann ist das zulässig. Auch sagt kein Mensch,
dass nach dem Inkrement die Variable i sofort zurückgeschrieben
werden muss. Das Ergebnis von i++ (also der neue, erhöhte Wert)
kann auch nach dem led_out(i) an die Variable i im Speicher
zugewiesen werden. Oder irgendwo dazwischen.
Es ist daher völlig zulässig, dass Folgendes passiert.
i habe den Wert 8
i++ i wird erhöht, das Zwischenergebnis, 9,
wird in einem Register gehalten und der
Compiler verschiebt den Update der Variablen
nach hinten
if( i > 8 ) i = 0 i, bzw. das gecachte Zwischenergebnis 9
ist größer als 8, daher wird i zu 0.
Der Compiler erfüllt auch gleich seine
Pflicht und schreibt die 0 in den
Speicher zurück.
led_out(i) 0 wird ausgegeben
und jetzt ist noch die Rückspeicherung des Inkrements,
nämlich der 9 ausständig. Also macht der Compiler das
mal.
Neuer Wert für i: 9
Autsch!
Moral von der Geschichte:
Nicht zuviel in ein Statement packen. Das führt sehr schnell
zu unliebsamen Effekten.
Auf eine Variable nur einmel schreibend zugreifen. Wenn das
geschieht, darf das dann auch nur der einzige Zugriff auf die
Variable sein.
Selbst ein
j = i + i++;
hat schon undefiniertes Verhalten.
Jetzt bin ich etwas durcheinander....Ich bin nich soooo helle in Mathe
und in Programieren schon garnicht.
Was ich machen will ist: An ein ADC ist ein NTC angeschlossen, die Werte
die ich benötige sind zw. 100 und 175. Also, müßten die Werte 0-75
0-100% representieren und, diese wieder rumm 0-255. Jetzt ist es etwas
anderes, ich benutze den ADC in 8 bit ( (1<<ADLAR) )also müsste das
ganze etwas einfacher sein, aber ich komme nicht drauf.
MFG
Aber du musst da nicht mit Gleitkomma Operationen rumschmeissen.
Das ganze ist eine Variante der in der Schule beliebten
Apfel-Rechnerei:
10 Äpfel kosten 12 Mark, wieviel kosten 7 Äpfel?
10 ....... 12
7 ....... x
---------------
( Zahl über x, mal Stumpf durch Spitz )
12 * 7
x = --------
10
Der klassische Dreisatz
Den ADC Wert im Bereich von 100 bis 175 hast du durch die
Subtraktion von 100 schon mal in den Bereich 0 bis 75 gebracht.
Also:
Ein Eingangswert von 75 soll einem Ausgangswert von 255 entsprechen.
Welcher Ausgangswert ergibt sich daher für einen Eingangswert
von ADC
75 ..... 255
ADC ..... x
-------------------
255 * ADC
x = -----------
75
Michael Wilhelm wrote:
> nur zur Information:> meine Lösung 724 Takte
...und die Einbindung der kompletten float-Library (vorausgesetzt, die
wird im restlichen Programm nirgends gebraucht), also u.U. ein paar KiB
mehr Code...
Außerdem geht das ganze nicht wie Du schreibst in unsigned char ...