Forum: Mikrocontroller und Digitale Elektronik Soft-PWM flackert


von Marcel H. (multiholle)


Lesenswert?

Ich habe nach dem Tutorial hier im Forum ein Soft-PWM für meine 4 LEDs 
geschrieben. Das funktioniert jetzt einigermaßen, allerdings noch nicht 
ganz perfekt: Die LEDs flackern beim Dimmen. Ich habe für die PWM-Werte 
jeweils 2er-Arrays als Dubblebuffer verwendet. cur gibt immer den 
aktuellen Wert für die ISR an.

Ist evtl. die ISR zu rechenaufwendig, oder wo liegt das Problem?

main.c
1
/*** INCLUDE ******************************************************************/
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <avr/pgmspace.h>
5
#include <util/delay.h>
6
#include <inttypes.h>
7
#include "leds.h"
8
#include "speaker.h"
9
10
/*** DEFINE *******************************************************************/
11
#define F_CPU         20000000L
12
#define F_PWM         100L
13
#define PWM_PRESCALER 1
14
#define PWM_CHANNELS  8
15
#define PWM_STEPS     256
16
#define T_PWM         (F_CPU / (PWM_PRESCALER * F_PWM * PWM_STEPS))
17
18
/** VARS/CONSTS ***************************************************************/
19
volatile uint8_t sync;
20
uint8_t cur = 0;
21
uint8_t pwm_count[2];
22
uint16_t pwm_timing[PWM_CHANNELS + 1][2];
23
uint8_t pwm_mask[PWM_CHANNELS + 1][2];
24
25
uint16_t pwm_table[32]  PROGMEM = {0, 1, 2, 2, 2, 3, 3, 4, 
26
                                   5, 6, 7, 8, 10, 11, 13, 16, 
27
                                   19, 23, 27, 32, 38, 45, 54, 64, 
28
                                   76, 91, 108, 128, 152, 181, 215, 255};
29
30
31
32
/** FUNCTIONS *****************************************************************/
33
ISR(TIMER1_COMPA_vect) {
34
  static uint8_t pwm = 0;
35
  OCR1A +=  pwm_timing[pwm][cur ^ 1];
36
  if (pwm == 0)
37
    leds_set(pwm_mask[pwm][cur ^ 1]);
38
  else
39
    leds_off(pwm_mask[pwm][cur ^ 1]);
40
  pwm++;
41
  if (pwm == pwm_count[cur ^ 1] + 1) {
42
    sync = 1;   
43
    pwm = 0;
44
  }
45
}
46
47
void leds_pwm_set(uint8_t pwm_setting_unsorted[]) {
48
  uint8_t i, j, k;
49
  uint8_t pwm_setting[PWM_CHANNELS];
50
51
  // mask
52
  pwm_mask[0][cur] = 0;
53
  for (i = 0; i < PWM_CHANNELS; i++) {
54
    pwm_setting[i] = pgm_read_word(pwm_table + pwm_setting_unsorted[i]);
55
    if (pwm_setting[i] != 0) 
56
      pwm_mask[0][cur] |= (1 << i);
57
    pwm_mask[i + 1][cur] = (1 << i);
58
  }
59
60
  // sort pwm_settings (bubblesort)
61
  for (i = PWM_CHANNELS - 1; i > 0; i--) {
62
    for (j = 0; j < i; j++) {
63
      // swap
64
      if (pwm_setting[j] > pwm_setting[j + 1]) {
65
        // swap pwm_settings
66
        uint8_t tmp_pwm_setting = pwm_setting[j];
67
        pwm_setting[j] = pwm_setting[j + 1];
68
        pwm_setting[j + 1] = tmp_pwm_setting;
69
        // swap masks
70
        uint8_t tmp_mask = pwm_mask[j + 1][cur];
71
        pwm_mask[j + 1][cur] = pwm_mask[j + 2][cur];
72
        pwm_mask[j + 2][cur] = tmp_mask;
73
      }
74
    }
75
  }
76
77
  // join same values, remove zero values
78
  pwm_count[cur] = PWM_CHANNELS;
79
  i = 0;
80
  while (pwm_count[cur] > i) {
81
    while (pwm_count[cur] > i && (pwm_setting[i] == pwm_setting[i + 1] || pwm_setting[i] == 0)) {
82
      if (pwm_setting[i] != 0)
83
        pwm_mask[i + 2][cur] |= pwm_mask[i + 1][cur];
84
      for (k = i; k < pwm_count[cur] + 1; k++) {
85
        pwm_setting[k] = pwm_setting[k + 1];
86
        pwm_mask[k + 1][cur] = pwm_mask[k + 2][cur];
87
      }
88
      pwm_count[cur]--;
89
    }
90
    i++;
91
  }
92
  if (pwm_setting[i] == 0)
93
    pwm_count[cur]--;
94
95
  // calc timings
96
  pwm_timing[0][cur] = T_PWM * pwm_setting[0];
97
  for (i = 1; i < pwm_count[cur]; i++) {
98
    pwm_timing[i][cur] = T_PWM * (pwm_setting[i] - pwm_setting[i - 1]);
99
  }
100
  pwm_timing[cur][pwm_count[cur]] = T_PWM * (PWM_STEPS - 1 - pwm_timing[cur][pwm_count[cur] - 1]);
101
102
  // swap
103
  sync = 0;
104
  while (sync == 0);
105
106
  cli();
107
  cur ^= 1;
108
  sei();
109
}
110
111
112
int main(void) {
113
  leds_init();
114
  
115
  // set timer prescaler to F_CPU/8
116
    TCCR1B |= (1 << CS00);
117
  // enable output compare interrupt
118
    TIMSK1 |= (1 << OCIE1A);
119
  // enable global interrupts
120
  sei();
121
122
  uint8_t vals[] = {31, 31, 0, 0, 0, 0, 0, 0};
123
  uint8_t i = 0;
124
  while (1) {
125
    vals[i]--;
126
    vals[(i + 2) % 4]++;
127
    
128
    if (vals[i] == 0)
129
      i = (i + 1) % 4;    
130
131
    leds_pwm_set(vals);
132
    _delay_ms(20);
133
  }
134
}

leds.c
1
#include <avr/io.h>
2
#include "leds.h"
3
4
void leds_init(void) {
5
    // set leds outputs
6
    DDRC |= 1 << PC4;
7
    DDRD |= 1 << PD3 | 1 << PD6 | 1 << PD7;
8
    
9
    // turn all leds off
10
    PORTC &= ~(1 << PC4);
11
    PORTD &= ~(1 << PD3 | 1 << PD6 | 1 << PD7);
12
}
13
14
void leds_set(uint8_t state) {
15
    // led 1
16
  if (state & (1 << 0))
17
        PORTC |= 1 << PC4;
18
  else
19
        PORTC &= ~(1 << PC4);
20
21
    // led 2
22
    if (state & (1 << 1))
23
        PORTD |= 1 << PD3;
24
    else
25
        PORTD &= ~(1 << PD3);
26
27
    // led 3
28
    if (state & (1 << 2))
29
        PORTD |= 1 << PD6;
30
    else
31
        PORTD &= ~(1 << PD6);
32
33
    // led 4
34
    if (state & (1 << 3))
35
        PORTD |= 1 << PD7;
36
    else
37
        PORTD &= ~(1 << PD7);
38
}
39
40
void leds_on(uint8_t mask) {
41
    // led 1
42
  if (mask & (1 << 0))
43
        PORTC |= 1 << PC4;
44
45
    // led 2
46
    if (mask & (1 << 1))
47
        PORTD |= 1 << PD3;
48
49
    // led 3
50
    if (mask & (1 << 2))
51
        PORTD |= 1 << PD6;
52
53
    // led 4
54
    if (mask & (1 << 3))
55
        PORTD |= 1 << PD7;
56
}
57
58
void leds_off(uint8_t mask) {
59
    // led 1
60
  if (mask & (1 << 0))
61
        PORTC &= ~(1 << PC4);
62
63
    // led 2
64
    if (mask & (1 << 1))
65
        PORTD &= ~(1 << PD3);
66
67
    // led 3
68
    if (mask & (1 << 2))
69
        PORTD &= ~(1 << PD6);
70
71
    // led 4
72
    if (mask & (1 << 3))
73
        PORTD &= ~(1 << PD7);
74
}

von Marcel H. (multiholle)


Lesenswert?

Das flackern der LEDs tritt nicht auf, wenn ich nur einen PWM-Wert 
anzeige. Wenn ich aber eine Animation erstelle, flackern die LEDs. 
Irgendwas stimmt da mit der synchronisation nicht, aber was?

von Marcel H. (multiholle)


Lesenswert?

Keiner eine Idee?

von Sam .. (sam1994)


Lesenswert?

Sollte dein Code fehlerfrei sein, dann ist dein SOFT-PWM zu langsam.

von R2D2 (Gast)


Lesenswert?

Den Code hab ich mir jetzt nicht genau angeschaut, da bräuchte ich zu 
viel Zeit. Hier mal eine Idee was sein könnte: Wenn du den neuen Wert 
zum falschen Zeitpunkt schreibst gibt es evtl. einen Timer-Zyklus in dem 
nicht richtig geschalten wird.

Beispiel:
Alter Wert: 100
Neuer Wert: 50
Aktueller Timerwert: 75

Der Timer ist schon am neuen Wert vorbei, hat aber noch nicht 
geschalten, weil der alte Wert ja größer war. Deshalb dauert es erst mal 
einen Timerüberlauf lang bis die PWM wieder richtig arbeitet.

Ich übernehme neue Werte deshalb immer nur im Timer-Overflow-IRQ.

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.