Forum: Mikrocontroller und Digitale Elektronik [C] Probleme mit Software-PWM + Fading bei Attiny861A


von Daniel (Gast)


Lesenswert?

Hallo,

ich möchte gerne per Software-PWM LED-Fading durchführen. Als Basis 
dient ein Attiny861A, an welchem eine LED zwischen PA5 und PA6 hängt.

Als Basis habe ich zunächst die erste Variante der Software-PWM aus dem 
Artikel Soft-PWM verwendet. Die Software-PWM an sich funktioniert 
soweit auch. Nun wollte ich einen ersten Ansatz zum Fading der LED 
ausprobieren, indem ich alle x Millisekunden das Tastverhältnis ändere. 
Leider scheint hier irgendwas zu haken, wenn ich z.B. einstelle, dass 
die Helligkeit sich alle 50ms erhöhen soll, so bleibt die LED etliche 
Sekunden auf jede  Helligkeitsstufe stehen. Erst wenn ich "sleep(1)" 
setze geht es etwas schneller, ein vollständiger Durchlauf benötigt aber 
immer noch ~20 Sekunden.

Kann mir jemand sagen, wo mein Fehler ist? Ich vermute, dass irgendwo 
die ISR dazwischen funkt, aber ich habe noch nicht so viel Erfahrung und 
nachdem ich nun zwei Stunden alles mögliche probiert habe weiß ich 
wirklich nicht mehr weiter.

Hier noch der Code, den ich verwende:
1
#define F_CPU 8000000UL
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
6
#define F_PWM 100                  
7
#define PWM_STEPS 255                 
8
#define T_PWM (F_CPU / (F_PWM * PWM_STEPS))
9
10
#if (T_PWM < (152 + 5))
11
  #error T_PWM zu klein!
12
#endif
13
14
#if PWM_STEPS > 255
15
  #error PWM_STEP zu gross!
16
#endif
17
  
18
volatile uint8_t dutyCycle = 16;
19
20
void sleep(uint16_t ms) {
21
  for(; ms > 0; ms--) {
22
    _delay_ms(1);
23
  }    
24
}
25
26
ISR(TIMER1_COMPA_vect) {
27
  static uint8_t pwmCnt = 0;
28
29
  OCR1A += (uint16_t)T_PWM;
30
  
31
  if(dutyCycle > pwmCnt) {
32
    // LED an
33
    PORTA &= ~(1<<PA6);
34
  } else {
35
    // LED aus
36
    PORTA |= (1<<PA6);
37
  }
38
  
39
  if(pwmCnt == (uint8_t)(PWM_STEPS-1)) {
40
    pwmCnt = 0;
41
  } else {
42
    pwmCnt++;
43
  }
44
}
45
46
int main(void)
47
{
48
  int i;
49
  
50
  // PA5 & PA6 als Ausgang, HIGH
51
  DDRA = (1<<DDA6)|(1<<DDA5);
52
  PORTA = (1<<PA6)|(1<<PA5);
53
    
54
  // Timer1 Takt = CPU Takt
55
  TCCR1B = 1;
56
  // Timer1 Output Compare Interrupt aktivieren
57
  TIMSK |= (1<<OCIE1A);
58
  // Interrupts aktivieren
59
  sei();
60
  
61
  while(1) {
62
    // Tastverhältnis ändern
63
    for(i = 0; i <= 255; i++) {
64
      dutyCycle = i;
65
      // warte bis zur nächsten Stufe - HIER HAKT ES SCHEINBAR
66
      sleep(1); 
67
    }
68
  }
69
}

Gruß
Daniel

von Stefan E. (sternst)


Lesenswert?

Daniel schrieb:
> Kann mir jemand sagen, wo mein Fehler ist? Ich vermute, dass irgendwo
> die ISR dazwischen funkt

Die Delay-Funktionen können nicht wissen, dass und wie viel Zeit 
zusätzlich in Interrupts verbracht wird, also addieren sich die Zeiten 
einfach. Wenn du z.B. 50% der Zeit in Interrupts verbringst, dann ist 
die Delay-Zeit doppelt so lang wie angedacht. Sind es 90%, dann ist die 
Zeit 10 mal so lang, usw.

Mir scheint allerdings, dass das alleine hier nicht für das beobachtete 
Verhalten reicht, also kommt da vermutlich noch was dazu, z.B. dass der 
Controller in Wirklichkeit nur mit 1Mhz läuft und nicht mit 8MHz (CKDIV8 
Fuse?).

von Rolf Magnus (Gast)


Lesenswert?

Wie ist denn die Optimierung eingestellt? Bei ausgeschalteter 
Optimierung brauchen die Delay-Funktionen erheblich länger.

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.