Forum: Mikrocontroller und Digitale Elektronik SoftPWM grenzwertig


von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Hallo allerseits,

bei folgender Aufgabe stoße ich momentan an meine grenzen, oder sehe nur 
wieder mal den Wald vor lauter Bäumen nicht:

ATmega328P mit 16 MHz (vorgegeben, nicht änderbar)

Mit Timer 0 müsste ich zwei unabhängige PWM-Signale erzeugen, allerdings 
mit 25 kHz. Auf einen anderen Timer auszuweichen ist nicht möglich 
(alles schon belegt)

Hardware-PWM wäre zwar super, aber mit 16MHz und den wenigen Prescalern 
komme ich nicht in die Gegend von 25kHz

Derzeitige Lösung: Timer 0 läuft mit Prescaler 8 (also 2MHz), die 25kHz 
erreiche ich ziemlich genau indem ich bis 80 zähle. Also je ein 
Interrupt auf Output Compare A und B, im Interrupt setze/lösche ich den 
Ausgang, und erhöhe OCR0A/B um die Low/High Zeit (also z.B. bei 20% Duty 
um 16 bzw. 64)

Funktioniert eigentlich perfekt, nur in den "Randbereichen" mit sehr 
kleiner Low- oder High-Zeit verhaspelt er sich. Ich gehe davon aus dass 
sich der Timer selbst überholt, bzw. der Counter-Wert schon weiter 
gestiegen ist als mein Inkrement des OCR.

Ursache ist mit ziemlicher Sicherheit eine (wenn auch geringe) last 
anderer Interrupts, und ein paar Routinen (Software-SPI) die mit 
gesperrten Interrupts laufen (wenn auch nur sehr kurz).

Vermutlich wird der OC-Int ausgelöst, ISR nicht gleich angesprungen, 
TCNT zählt brav weiter, sobald er in die ISR kommt erhöhe ich zwar brav 
den OCR-Wert, aber TCNT hat schon drüberweggezählt. bei einem PWM-Wert 
von 5 und Prescaler 8 sind das 40 takte, die können schon mal 
zusammenkommen...


Timer:Setup:
1
    TCCR0A = (0 << COM0A1) | (0 << COM0A0) | (0 << COM0B1) | (0 << COM0B0) | (0 << WGM01) | (0 << WGM00);  // normal operation
2
    TCCR0B = (0 << FOC0A) | (0 << FOC0B) | (0 << WGM02) | (0 << CS02) | (1 << CS01) | (0 << CS00);  // Prescaler 8
3
    OCR0A = 80;      // PWM 1 = Fan#1
4
    OCR0B = 80;      // PWM 2 = Fan#2
5
    TIMSK0 = (1 << OCIE0B) | (1 << OCIE0A) | (0 << TOIE0);  // Output Compare Match A & B Interrupt Enable;

Eine der ISR sieht so aus (die zweite ganz analog)
1
// Timer 0 compare match A handler (Fan #1)
2
ISR(TIMER0_COMPA_vect)
3
{
4
    uint8_t pwm = Fan1_PWM;  // cache volatile
5
6
    if (pwm == 0) {
7
        PORTD &= ~_BV(6);    // always off
8
        OCR0A += 80;    // setup next event
9
    } else if (pwm >= 80) {
10
        PORTD |= _BV(6);    // always on
11
        OCR0A += 80;    // setup next event
12
    } else {
13
        if (PIND & _BV(6)) {
14
            // currently on => turn off
15
            PORTD &= ~_BV(6);
16
            OCR0A += (80 - pwm);  // setup next event
17
        } else {
18
            // currently off => turn on
19
            PORTD |= _BV(6);
20
            OCR0A += pwm;  // setup next event
21
        }
22
    }
23
}

"Knistern" tuts so im bereich 10% / 90%, bei 5% ists ganz vorbei, da 
zieht er "Ehrenrunden".

Eine pragmatische Lösung wäre, den Arbeitsbereich auf 20%..80% zu 
beschränken, <20% als "ganz aus" und >80% als "ganz ein" zu behandeln, 
wobei man in der Praxis vermutlich nicht mal einen Unterschied bemerken 
würde.

Da ich notorisch neugierig und unzufrieden bin, hätte mich interessiert 
obs auch eine saubere Lösung gäbe?

Eine Variante wie mir einfiele wäre statt OCR zu inkrementieren, OCR auf 
TCNT+Offset zu setzen, der Preis dafür wäre eine nicht mehr stimmende 
PWM-Frequenz.

Andere Ideen?

Wie gesagt, Timer 0 und 16 MHz sind gesetzt.


Danke, Michi

von Peter D. (peda)


Lesenswert?

Michael Reinelt schrieb:
> Auf einen anderen Timer auszuweichen ist nicht möglich
> (alles schon belegt)

Wirklich?
Da ist doch bestimmt noch Luft.
Zeig mal her.

Auch als Timer geeignet:
- ADC free runnning
- UART senden

von robin (Gast)


Lesenswert?

Sind die Signale komplett unabhängig? Oder fangen beide zur gleichen 
Zeit an und sind nur unterschiedlich lange high?

Letzteres wäre nicht schlecht, dadurch könnte man die Werte vorrechnen 
uns nur noch eine Maske in den Interrupts ausgeben.

Da das ganze aber für Lüfter(PC?) zu sein scheint, würde ich das Signal 
einfach begrenzen und gut.

Eine andere nicht ganz so schöne Lösung wäre eine kleine waitschleife in 
der isr für die kleinen Werte.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Peter Dannegger schrieb:
> Wirklich?
> Da ist doch bestimmt noch Luft.
> Zeig mal her.
ich habe insgesamt 5xPWM, einen Timer für ADC und diverse System Clocks, 
also alle drei Tinmer belegt, einzig der OCR2B von Timer2 wäre noch 
verfügbar, aber Timer2 läuft schon mit Fullspeed Hardware-PWM für eine 
Heizung, hier wird das PWM-Signal über Drossel+Diode geglättet (also 
eigentlihc ein Step-Down-Converter) deswegen brauch ich die 62.5kHz

> Auch als Timer geeignet:
> - ADC free runnning
> - UART senden
Ich liebe deine Ideen :-)
UART ist aber in verwendung, und ADC auch.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

robin schrieb:
> Sind die Signale komplett unabhängig? Oder fangen beide zur gleichen
> Zeit an und sind nur unterschiedlich lange high?

Oje, ich muss mich korrigieren: von meinen drei Lüftern (die die 25kHz 
brauchen) liegt eh nur einer auf Timer 0, die andern beiden auf timer 1 
(wo ich keine Probleme hab, da geht alles "in hardware")

Das zweite PWM-Signal auf Timer 0 ist "nur" das LCD-Backlight, da ist 
die Frequenz egal, das kann aihc also auch rein in Hardware machen. 
Damit hab ich schon mal etwas weniger Interrupt-last.

> Da das ganze aber für Lüfter(PC?) zu sein scheint, würde ich das Signal
> einfach begrenzen und gut.
Denke ich auch, aber man ist halt Perfektionist :-) Außerdem kann ich 
dabei was lernen, irgendwann brauch ichs sicher mal.

> Eine andere nicht ganz so schöne Lösung wäre eine kleine waitschleife in
> der isr für die kleinen Werte.
Ja, das wäre aber wirklich ganz ganz unschön...

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

So, ich hab das umgebaut, meine System Clocks auf Timer 0 verschoben, 
und das Backlight vom LCD auf "hardware-PWM" umgestellt.

Aber irgendwie tut das nicht...

Kann es sein dass man "Fast-PWM" und normale OCR-Interrupts nicht 
mischen kann?

Im Datenblatt des ATmega328P auf Seite 107 gibt es eine Tabelle 15-8 
"Waveform Generation Mode Bit Description"

Da gibts eine Spalte "Update of OCRx at.." und da steht bei FastPWM 
"BOTTOM". Was wollen die mir damit sagen?

ich versuche folgendes: Timer 0 läuft mit Prescaler 8, OCR0B ist 
Fast-PWM (funktioniert), mit OCR0A rufe ich einen Interrupt auf, der 
dann OCR0A mit pwm oder 80-pwm weiterzählt. Nur funktioniert das 
überhaupt nicht, es sieht so aus als ob er grundsätzlich im Kreis rennen 
würde.

aktuell Timer-Initialisierung:
1
    TCCR0A = (0 << COM0A1) | (0 << COM0A0) | (1 << COM0B1) | (0 << COM0B0) | (1 << WGM01) | (1 << WGM00);  // Mode 3: OC0A disconnected, Fast PWM on OC0B
2
    TCCR0B = (0 << FOC0A) | (0 << FOC0B) | (0 << WGM02) | (0 << CS02) | (1 << CS01) | (0 << CS00);  // Prescaler 8
3
    OCR0A = 80;      // PWM 1 = Fan#1
4
    OCR0B = 255;    // PWM 2 = Backlight
5
    TIMSK0 = (0 << OCIE0B) | (1 << OCIE0A) | (1 << TOIE0);  // Overflow and Output Compare Match A Interrupt Enable;

und die ISR dazu:
1
// Timer 0 compare match A handler (Fan #1)
2
ISR(TIMER0_COMPA_vect)
3
{
4
    uint8_t pwm = Fan1_PWM;  // cache volatile
5
6
    LED_PORT ^= LED_BIT;
7
8
    if (pwm == 0) {
9
  FAN1_OFF();    // always off
10
  OCR0A += 80;    // setup next event
11
    } else if (pwm >= 80) {
12
  FAN1_ON();    // always on
13
  OCR0A += 80;    // setup next event
14
    } else {
15
  if (FAN1_READ()) {
16
      // currently on => turn off
17
      FAN1_OFF();
18
      OCR0A += (80 - pwm);  // setup next event
19
  } else {
20
      // currently off => turn on
21
      FAN1_ON();
22
      OCR0A += pwm;  // setup next event
23
  }
24
    }
25
}
 ich hab eine LED mitgetriggert, und sehe schon dort total unregelmäßige 
Signale...

Wo ist mein Denkfehler?

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Lösung gefudnen: ich darf keinen PWM Mode verwenden, denn in diesem Fall 
werden beide Output Compare Register gepuffert und erst bei BOTTOM 
durchgeladen.

PWM und Nicht-PWM zu mischen scheint schwierig...

ich hab aber noch ein anderes problem, dazu aber einen eigenen Thread.

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.