Hallo zusammen. Ich möchte einen kleinen PWM-Generator bauen. Also über
Taster oder Drehimpulsgeber soll einmal die Frequenz und einmal das
Tastverhältnis in Prozent (0,5% Schritte) eingestellt werden. Die
Frequenz ist erstmal nicht das Problem. Das Problem ist folgendes. Ein
16-Bit PWM-Modul in einem PIC hat bei 0% den Wert 0 und bei 100% den
Wert 65.535. Wenn ich jetzt z.B. 43,5% einstellen will, würde ich es wie
folgt ausrechnen:
(65535/100)*43,5% = 28507,725 -> 28508 ist der Wert, den ich in den
Speicher schreibe. Welchen Datentyp nehme ich jetzt aber für welche
Variable??
Für den aktuellen Prozentwert kann ich ja den Float nehmen, oder? Kann
ich dass dann auch für den PWM-Wert nehmen? Ich hab mir das so
vorgestellt:
1
floatpwm=0,prozent=0;
2
unsignedintwert=0;
3
4
/* Init */
5
6
while(1){
7
if(/* Button up */&&prozent<100){
8
prozent+=0.5;
9
}
10
if(/* Button down */&&prozent>0){
11
prozent-=0.5;
12
}
13
pwm=(65535/100)*prozent;
14
wert=floor(pwm);
15
/* Neuen Prozentwert anzeigen */
16
/* wert in Speicher schreiben */
17
}
Ich habe jetzt erstmal nur das, worum sich meine Frage dreht,
hingeschrieben.
Sollte es ansich so funktionieren, habe ich noch 2 Fragen.
1. Statt (65535/100) kann ich doch eigentlich auch gleich 655.35
hinschreiben, oder? Hat es einen Vorteil?
2. Ich habe in meinem C-Compiler die Funktionen der c_math lib
aufgelistet. Dort findet man floor zum immer abrunden und ceil zum immer
aufrunden. Das kenne ich schon von PHP, doch dort gibt es auch die
Funktion round();, die dann so rundet, wie ich es haben will (erst ab ,5
aufrunden). Die Funktion markiert er aber dann als nicht
gültig/definiert. Gibt es so eine Funktion nicht?
Danke für jede Antwort schonmal.
ich schrieb:> Sollte es ansich so funktionieren, habe ich noch 2 Fragen.> 1. Statt (65535/100) kann ich doch eigentlich auch gleich 655.35> hinschreiben, oder? Hat es einen Vorteil?
Du meinst, ausser dass der Wert dann genau ist?
Denn 65535/100 ergibt nicht 655.35 sondern 655
65535 ist ein integer, 100 ist ein integer. Also wird auch in integer
dividiert. Und eine integer Division liefert nun mal keine
Nachkommastellen.
> 2. Ich habe in meinem C-Compiler die Funktionen der c_math lib> aufgelistet. Dort findet man floor zum immer abrunden und ceil zum immer> aufrunden. Das kenne ich schon von PHP, doch dort gibt es auch die> Funktion round();, die dann so rundet, wie ich es haben will (erst ab ,5> aufrunden). Die Funktion markiert er aber dann als nicht> gültig/definiert. Gibt es so eine Funktion nicht?
Ist aber leicht gemacht.
Einfach 0.5 dazuzählen und das ganz einem int zuweisen.
Das Verhalten bei der Zuweisung eines Float an einen int ist genau
definiert: Kommsatellen werden abgeschnitten.
Zählst du vor dem Abschneiden 0.5 dazu, dann hast du genau das
kaufmännische Runden, das du haben möchtest.
Siehe Festkommaarithmetik
Mach es anders: Ein Dreh' erhöht pwm um 328 bzw. 327. Dann teilst du
deinen PWM-Wert*1000 durch durch 65535 und setzt das Komma an der
richtigen Stelle für's Display. Dann bekommst du auch das angezeigt, was
ausgegeben wird und nicht, das was du eingestellt hast.
Karl Heinz Buchegger schrieb:> Denn 65535/100 ergibt nicht 655.35 sondern 655
Achso, ich dachte weil die Variable, in die es geschrieben wird, ein
Float ist, geht das so. Naja. Ginge es denn theoretisch, wenn man
65535.0/100 schreibt? Oder ich habe auch was gelesen, dass es so geht
"((float)65535)/100". Aber ich werde die 655.35 hinschreiben, ich frage
nur aus Interesse.
Karl Heinz Buchegger schrieb:> Einfach 0.5 dazuzählen und das ganz einem int zuweisen.> Das Verhalten bei der Zuweisung eines Float an einen int ist genau> definiert: Kommsatellen werden abgeschnitten.> Zählst du vor dem Abschneiden 0.5 dazu, dann hast du genau das> kaufmännische Runden, das du haben möchtest.
Danke für den Tip, so werde ichs machen.
Luk4s K. schrieb:> Mach es anders: Ein Dreh' erhöht pwm um 328 bzw. 327. Dann teilst du> deinen PWM-Wert*1000 durch durch 65535 und setzt das Komma an der> richtigen Stelle für's Display. Dann bekommst du auch das angezeigt, was> ausgegeben wird und nicht, das was du eingestellt hast.
Da hast du warscheinlich recht, doch wann ich 328 und wann 327 nehme,
müsste ich noch überlegen. Außerdem wird das ganze dann
komplizierter/zeitaufwändiger, als ich es brauche/will. Soll ansich
gestern schon ferig sein. Ich habe auch schon überlegt, ob ich nicht ein
PIC nehme, der ein 10-bit PWM-Modul drinne hat. Nur mit einem PIC mit
16-bit PWM-Modul kann ich auch 10 Bit einstellen und damit eine höhere
Frequenz fahren. Bei 10 Bit wäre es ja einfach, da kann ich mir auch
vorstellen, dass 100% nicht 1024 sondern 1000 sind. Eine kleine
Abweichung zwar, aber die ist nicht so groß. Dann wäre 0,5% ein Wert von
5.
ich schrieb:> Karl Heinz Buchegger schrieb:>> Denn 65535/100 ergibt nicht 655.35 sondern 655>> Achso, ich dachte weil die Variable, in die es geschrieben wird, ein> Float ist, geht das so.
Das ist völlig wurscht.
Wo und wie du ein Ergebnis abspeicherst, beeinflusst nicht mit welcher
Operation das Ergebnis berechnet wird.
Wenn der Compiler entscheiden soll, ob er das '/' mittels einer
Integer-Division oder einer Floating-Point-Division berechnen muss, dann
kennt er nur 2 Dinge: Den Ausdruck links vom '/' und den Ausdruck rechts
vom '/'. Und einzig und alleine deren Datentypen entscheiden darüber,
welche Operation genommen wird.
> Naja. Ginge es denn theoretisch, wenn man> 65535.0/100 schreibt?
Jetzt steht links vom '/' ein Floating Point Datentyp und rechts vom '/'
steht ein Integer Datentype. Die Regeln sagen: Das wird als Flaoting
Point Division gerechnet.
Es gewinnt immer der 'höherwertige' Datentyp.
int
long
double/float
> Oder ich habe auch was gelesen, dass es so geht> "((float)65535)/100".
kommt aufs gleiche raus.
uch hier steht jetzt links vom '/' eine Floating Point Zahl. Das diese
Flaoting Point Zahl dadurch gewonnen wurde, dass ein Integer auf
Floating Point umgecastet wurde spielt keine Rolle. Entscheidend ist,
dass der Ausdruck links vom '/' ein Floating Point Datentyp ist. Wie der
zustande kommt, ist uninteressant.
> Aber ich werde die 655.35 hinschreiben
Ist in diesem Fall die bessere, da einfachere, Lösung
Gibt es eigentlich irgendeinen Grund, soetwas mit Float zu behandelt,
außer dass sonst der Programmspeicher nicht vollzukriegen ist und der
Prozessor Langeweile kriegt?
Tips dazu siehe
Beitrag "Float vermeiden, ADC-Ergebnis umrechnen?"
;-)
Ok, dann habe ich es verstanden. Vielen Dank für die Hilfestellung.
Ich hätte nebenbei noch ein paar Fragen zum gleichen Projekt, allerdings
ein anderes Thema. Ich hoffe dass ihr mir trotzdem helfen könnt.
Das PWM-Signal kommt mit 0-5V aus einem PIC raus. Dieses Signal will ich
in 3 verschiedenen Spannungen über die Stiftleiste K1 (links) ausgeben
können. Dazu hab ich mir die angehängte Schaltung überlegt.
Im ersten Fall (Schalterstellung links) soll an Pin3 5V zur Verfügung
stehen (um eine kleine angehängte Platine oder einen Servo zu
versorgen), an Pin2 soll das PWM-Signal als 0-5V rauskommen und Pin1 ist
GND.
Im zweiten Fall (Schalterstellung mitte) soll es genauso sein, wie im
ersten Fall, nur soll 3.3V ausgegeben werden und das PWM Signal soll
auch von 0 bis 3.3V gehen.
Im dritten Fall (Schalterstellung rechts) will ich das so haben, dass an
Pin3 eine Spannung angelegt werden kann (z.B. 12V) und das PWM-Signal
soll mit dieser Spannung ausgegeben werden.
1) Würde das so wie ich es angehängt habe funktionieren oder habe ich
einen Denkfehler?
2) Sollte T1 und/oder T2 lieber ein N-Channel MOSFET sein?
Ansich sollte ein Uce-sat von 0.2V ausreichen um a) T1 voll sperren
zu lassen und b) das PWM-Signal auf 0.2V zu ziehen, was in der
TTL-Norm ja noch als Low gilt.
3) Wenn 0.2V ausreichen, könnte ich doch dann für beide Transistoren
einen BC547 nehmen, oder?
Denn T2 muss ja nur T1 steuern und T1 zieht das Signal nur auf Masse.
Wenn der PWM-Ausgang stark belastet wird, muss ja nur der R1 genug
Watt aushalten, der T1 ist davon ja aber nicht umbedingt betroffen.
Martin schrieb:> Kannst du nicht den Timer nur bis 49999 laufen lassen, dann ist die> umrechnerei etwas leichter.
Stimmt, das sollte auch gehen. Ich habe jetzt einen anderen PIC gewählt,
der ein bisschen kleiner ist und der hat ein 10bit PWM-Modul. Dann
stelle ich das PR register auf 249, denn die Formel für das Verhältnis
ist:
PWM duty cycle ratio = (CCPRxL:CCPxCON<5:4>)/(4*(PRx + 1))
Also die 10bit geteilt durch 1000. Damit kann man gut umgehen.
Rechnerisch kommt eine Auflösung von 9,966 raus. Wie das auch immer
gehen soll, aber hauptsache, er kann das Verhältnis ausgeben.
Die 3 Fragen in meinem letzten Beitrag wären trotzdem interessant, also
falls da jemand was weiß..
ich schrieb:> stelle ich das PR register auf 249,
Stell es auf 200.
Die extra-Auflösung brauchst du ja nicht, und die Frequenz steigt (=>
leichter zu filtern).
Und die Umrechnung von ½ % auf PWM-Wert ist trivial, und geht ganz ohne
float.
Eneloop schrieb:> Stell es auf 200.> Die extra-Auflösung brauchst du ja nicht, und die Frequenz steigt (=>> leichter zu filtern).
Da will ich 2 Stufen machen. ~10bit und ~8bit, vielleicht auch ~7bit.
Wird dann immer ungenauer bzw. werde ich da dann größere Schritte
machen, wie z.B. 8bit=1%schritte, 7bit = 1 oder 2%.. Muss ich nochmal
gucken. Mir ging es im moment nur um die Berechnung.
Und jetzt noch um die Schaltung.