PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Software PWM



Johannes G.
14.02.2007, 15:15
Hallo,

mal eine Frage zum Software PWM:
Braucht man da pro PWM einen Timer?
Oder kann ein Timer mehrere PWM Signale erzeugen?
Auflösung sollte 10bit sein, wenn aber nur 8bit gehen ist es auch egal...


Viele Grüße,
Johannes

SprinterSB
14.02.2007, 15:36
Wenn man keinen Bock schiesst sollte ein Timer reichen, so kompliziert ist es ja nicht.


Beachte, daß
PWM-Frequemz = Timer-Frequenz / Auflösung

Mit einer Soft-PWM sind also nicht so hohe Frequenzen erreichbar etc.

Johannes G.
14.02.2007, 17:21
Hallo,
welche Timerfrequenz würdest du für LEDs empfehlen?

Viele Grüße,
Johannes

SprinterSB
14.02.2007, 18:34
Für eine LED mit 11 Helligkeitsstufen (0%, 10%...100%) reicht meiner Erfahrung nach eine PWM-Auflösung von 100, wobei man die duty-cycles quadratisch verteilt, also 0/100, 1/100, 4/100, ...

Da LEDs praktisch unträge sind und es nicht flimmern sollte (auch nicht, wenn die Anzeige im Blickfeld wandert), solltes es schon allermindestens eine 100Hz-PWM sein.

Wenn wir von einer Timer-IRQ-Rate von 20kHz ausgehen hast du noch Luft. Die Auflösung könntest du zB auf 200 erhöhen (ca. 15 Helligkeiten) oder alternativ die PWM-Frequenz auf 200Hz steigern.

:idea: Das Flimmern kann man auch einfach dadurch reduzieren, daß man nicht mit einer PWM ansteuert.

Beispiel: Nehmen wir mal an, wir hätten eine PWM mit einer Auflösung von 5 und wollen 2/5 duty. Eine PWM würde dann liefern
11000110001100011000...
Die selbe Helligkeit und kann man aber auch erreichen mit
10100101001010010100...
Das hat viel kürzere Intervalle gleichen Pegels, was das Flimmern reduziert.

Vom algorithmischen Aufwand ist das genauso einfach wie ne Soft-PWM, formal aber eine Überlagerung von phasenverschobenen PWMs, hier von 2 Stück.

Johannes G.
14.02.2007, 19:57
Hallo,

danke für die Erklärung..
Du hast nicht zufällig einen kleinen Beispielcode in C (oder ASM), oder?
Weil ich weiß nicht wie ich mit einem Timer mehrere PWMs erzeugen soll...

Viele Grüße,
Johannes

Hanni
14.02.2007, 20:29
Generell kann man sagen, das je höher die Auflösung und Wiederholrate der PWM sein soll, desto mehr Gedanken muss man sich im Vorfeld machen und desto optimaler muss der Code aussehen.

Reicht für es 1 oder 2 Kanäle noch aus jeden einzelnen Kanal einzeln zu vergleichen und separat auszugeben, so muss man bei 8 Kanälen in 8 Bit Auflösung für eine annehmbare Wiederholrate (ab 250 Hz) schon etwas tiefer in die Codekiste greifen.
Hannes Lux schlug auf www.mikrocontroller.net z.B. das folgende Code Konstrukt vor:


cp pwz,soll0 ;Sollwert0 erreicht?
ror wl ;Ergebnis (Carry) sichern
cp pwz,soll1 ;Sollwert1 erreicht?
ror wl ;Ergebnis (Carry) sichern
cp pwz,soll2 ;Sollwert2 erreicht?
ror wl ;Ergebnis (Carry) sichern
cp pwz,soll3 ;Sollwert3 erreicht?
ror wl ;Ergebnis (Carry) sichern
cp pwz,soll4 ;Sollwert4 erreicht?
ror wl ;Ergebnis (Carry) sichern
cp pwz,soll5 ;Sollwert5 erreicht?
ror wl ;Ergebnis (Carry) sichern
cp pwz,soll6 ;Sollwert6 erreicht?
ror wl ;Ergebnis (Carry) sichern
cp pwz,soll7 ;Sollwert7 erreicht?
ror wl ;Ergebnis (Carry) sichern
com wl ;invertieren wegen L-aktiven Ausgängen
out aus,wl ;Ergebnisse ausgeben

Wie meine Vorredner schon erwähnten läuft dieser Code in einer Timer Interupt Routine.

Vielleicht reicht das ja als Denkanstoß aus.


Für eine 10 Bit Software PWM dürfte ein wesentlich höherer Aufwand von Nöten sein.
Aber dazu mehr, wenn ich meine Theorien in dieser Richtung in die Praxis umgesetzt habe.

Grüße,
Hanni

SprinterSB
14.02.2007, 22:15
Mein Beispielcode aus dem Wiki (Artikel PWM) umgesetzt nach C, hier für 8 Kanäle, sie sämtilch an einem 8-Bit-Port ausgegeben werden, uns zwar pwm[0] an Pin0, etc


typedef struct
{
uint8_t tick;
uint8_t duty;
} pwm_t;

pwm_t pwm[8];

void job_pwm (uint8_t max)
{
uint8_t i;
//uint8_t max = 100;
uint8_t port = 0;

uint8_t * ppwm = (uint8_t*) & pwm[0];

for (i= sizeof (pwm) / sizeof (pwm[0]); i > 0; i--)
{
uint8_t tick = * ppwm + 1;

if (tick >= max)
tick = 0;

* ppwm++ = tick;

port >>= 1;

if (tick < * ppwm++)
port |= 0x80;
}

PORTB = port;
}

Für Assembler kommt direkt von avr-gcc und kann als Vorlage dienen:

.global job_pwm
job_pwm:
ldi r20,lo8(0) ; port,
ldi r30,lo8(pwm) ; ppwm,
ldi r31,hi8(pwm) ; ppwm,
ldi r21,lo8(8) ; i,
.L26:
ld r19,Z ; tick,* ppwm
subi r19,lo8(-(1)) ; tick,
cp r19,r24 ; tick, max
brlo .L24 ; ,
ldi r19,lo8(0) ; tick,
.L24:
st Z+,r19 ; , tick
lsr r20 ; port
ld r18,Z+ ; tmp50,
cp r19,r18 ; tick, tmp50
brsh .L23 ; ,
ori r20,lo8(-128) ; port,
.L23:
subi r21,lo8(-(-1)) ; i,
brne .L26 ; ,
out 56-0x20,r20 ; , port
ret


Die Bits werden in "port" aufgesammelt und gemeinsam ausgegeben.