Forum: Mikrocontroller und Digitale Elektronik PWM Duty in Prozent


von ich (Gast)


Lesenswert?

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
float pwm=0, prozent=0;
2
unsigned int wert=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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Lukas K. (carrotindustries)


Lesenswert?

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.

von ich (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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

von Smarty (Gast)


Lesenswert?

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?"
;-)

von ich (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Martin (Gast)


Lesenswert?

Kannst du nicht den Timer nur bis 49999 laufen lassen, dann ist die 
umrechnerei etwas leichter.

von ich (Gast)


Lesenswert?

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ß..

von Eneloop (Gast)


Lesenswert?

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.

von ich (Gast)


Lesenswert?

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.

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.