Forum: Mikrocontroller und Digitale Elektronik Problem mit der Soft-PWM


von Andi (Gast)


Lesenswert?

Hallo ihr lieben,

hab da mal ein kleines Problem und zwar wollte ich an meinem ATMega 32
16Hz extern getaktet eine PWM ausprobieren.
Leider blinken die 8 an PortD angeschlossenen LED's nur einmal kurz auf
und dann habe ich eine konstant leuchtende an Pin D0. Seh da nicht so
ganz durch hab den Code über das GCC Tutorial im Mikrocontroller.net 
gefunden und dann an meinen Controller angepasst. Würde halt gerne diese 
Software PWM benutzen da man durch diese mit einem Controller 32 Kanäle 
ansprechen kann.

Kann mir jemand sagen was ich falsch mache, wollte eine art soft fading
effekt haben das die LED's sich langsam an und ab dimmen.



/*
    Eine 8-kanalige PWM mit intelligentem Lösungsansatz

    ATmega32 @ 8 MHz

*/

// Defines an den Controller und die Anwendung anpassen

#define F_CPU         16000000L           // Systemtakt in Hz
#define F_PWM         100L               // PWM-Frequenz in Hz
#define PWM_PRESCALER 8                  // Vorteiler für den Timer
#define PWM_STEPS     256                // PWM-Schritte pro
Zyklus(1..256)
#define PWM_PORT      PORTD              // Port für PWM
#define PWM_DDR       DDRD               // Datenrichtungsregister für
PWM
#define PWM_CHANNELS  8                  // Anzahl der PWM-Kanäle

// ab hier nichts ändern, wird alles berechnet

#define T_PWM (F_CPU/(PWM_PRESCALER*F_PWM*PWM_STEPS)) // Systemtakte pro
PWM-Takt
//#define T_PWM 1   //TEST

#if ((T_PWM*PWM_PRESCALER)<(111+5))
    #error T_PWM zu klein, F_CPU muss vergrösst werden oder F_PWM oder
PWM_STEPS verkleinert werden
#endif

#if ((T_PWM*PWM_STEPS)>65535)
    #error Periodendauer der PWM zu gross! F_PWM oder PWM_PRESCALER
erhöhen.
#endif
// includes

#include <stdint.h>
#include <string.h>
#include <avr/io.h>
#include <avr/interrupt.h>

// globale Variablen

uint16_t pwm_timing[PWM_CHANNELS+1];          // Zeitdifferenzen der PWM
Werte
uint16_t pwm_timing_tmp[PWM_CHANNELS+1];

uint8_t  pwm_mask[PWM_CHANNELS+1];            // Bitmaske für PWM Bits,
welche gelöscht werden sollen
uint8_t  pwm_mask_tmp[PWM_CHANNELS+1];        // ändern uint16_t oder
uint32_t für mehr Kanäle

uint8_t  pwm_setting[PWM_CHANNELS];           // Einstellungen für die
einzelnen PWM-Kanäle
uint8_t  pwm_setting_tmp[PWM_CHANNELS+1];     // Einstellungen der PWM
Werte, sortiert
                                              // ändern auf uint16_t für
mehr als 8 Bit Auflösung

volatile uint8_t pwm_cnt_max=1;               // Zählergrenze,
Initialisierung mit 1 ist wichtig!
volatile uint8_t pwm_sync;                    // Update jetzt möglich

// Pointer für wechselseitigen Datenzugriff

uint16_t *isr_ptr_time  = pwm_timing;
uint16_t *main_ptr_time = pwm_timing_tmp;

uint8_t *isr_ptr_mask  = pwm_mask;              // Bitmasken fuer
PWM-Kanäle
uint8_t *main_ptr_mask = pwm_mask_tmp;          // ändern uint16_t oder
uint32_t für mehr Kanäle

// Zeiger austauschen
// das muss in einem Unterprogramm erfolgen,
// um eine Zwischenspeicherung durch den Compiler zu verhindern

void tausche_zeiger(void) {
    uint16_t *tmp_ptr16;
    uint8_t *tmp_ptr8;                          // ändern uint16_t oder
uint32_t für mehr Kanäle

    tmp_ptr16 = isr_ptr_time;
    isr_ptr_time = main_ptr_time;
    main_ptr_time = tmp_ptr16;
    tmp_ptr8 = isr_ptr_mask;
    isr_ptr_mask = main_ptr_mask;
    main_ptr_mask = tmp_ptr8;
}

// PWM Update, berechnet aus den PWM Einstellungen
// die neuen Werte für die Interruptroutine

void pwm_update(void) {

    uint8_t i, j, k;
    uint8_t m1, m2, tmp_mask;                   // ändern uint16_t oder
uint32_t für mehr Kanäle
    uint8_t min, tmp_set;                       // ändern auf uint16_t
für mehr als 8 Bit Auflösung

    // PWM Maske für Start berechnen
    // gleichzeitig die Bitmasken generieren und PWM Werte kopieren

    m1 = 1;
    m2 = 0;
    for(i=1; i<=(PWM_CHANNELS); i++) {
        main_ptr_mask[i]=~m1;                       // Maske zum Löschen
der PWM Ausgänge
        pwm_setting_tmp[i] = pwm_setting[i-1];
        if (pwm_setting_tmp[i]!=0) m2 |= m1;        // Maske zum setzen
der IOs am PWM Start
        m1 <<= 1;
    }
    main_ptr_mask[0]=m2;                            // PWM Start Daten

    // PWM settings sortieren; Einfügesortieren

    for(i=1; i<=PWM_CHANNELS; i++) {
        min=PWM_STEPS-1;
        k=i;
        for(j=i; j<=PWM_CHANNELS; j++) {
            if (pwm_setting_tmp[j]<min) {
                k=j;                                // Index und
PWM-setting merken
                min = pwm_setting_tmp[j];
            }
        }
        if (k!=i) {
            // ermitteltes Minimum mit aktueller Sortiertstelle tauschen
            tmp_set = pwm_setting_tmp[k];
            pwm_setting_tmp[k] = pwm_setting_tmp[i];
            pwm_setting_tmp[i] = tmp_set;
            tmp_mask = main_ptr_mask[k];
            main_ptr_mask[k] = main_ptr_mask[i];
            main_ptr_mask[i] = tmp_mask;
        }
    }

    // Gleiche PWM-Werte vereinigen, ebenso den PWM-Wert 0 löschen falls
vorhanden

    k=PWM_CHANNELS;             // PWM_CHANNELS Datensätze
    i=1;                        // Startindex

    while(k>i) {
        while ( ((pwm_setting_tmp[i]==pwm_setting_tmp[i+1]) ||
(pwm_setting_tmp[i]==0))  && (k>i) ) {

            // aufeinanderfolgende Werte sind gleich und können
vereinigt werden
            // oder PWM Wert ist Null
            if (pwm_setting_tmp[i]!=0)
                main_ptr_mask[i+1] &= main_ptr_mask[i];        // Masken
vereinigen

            // Datensatz entfernen,
            // Nachfolger alle eine Stufe hochschieben
            for(j=i; j<k; j++) {
                pwm_setting_tmp[j] = pwm_setting_tmp[j+1];
                main_ptr_mask[j] = main_ptr_mask[j+1];
            }
            k--;
        }
        i++;
    }

    // letzten Datensatz extra behandeln
    // Vergleich mit dem Nachfolger nicht möglich, nur löschen
    // gilt nur im Sonderfall, wenn alle Kanäle 0 sind
    if (pwm_setting_tmp[i]==0) k--;

    // Zeitdifferenzen berechnen

    if (k==0) { // Sonderfall, wenn alle Kanäle 0 sind
        main_ptr_time[0]=(uint16_t)T_PWM*PWM_STEPS/2;
        main_ptr_time[1]=(uint16_t)T_PWM*PWM_STEPS/2;
        k=1;
    }
    else {
        i=k;
        main_ptr_time[i]=(uint16_t)T_PWM*(PWM_STEPS-pwm_setting_tmp[i]);
        tmp_set=pwm_setting_tmp[i];
        i--;
        for (; i>0; i--) {
            main_ptr_time[i]=(uint16_t)T_PWM*(tmp_set-pwm_setting_tmp[i]);
            tmp_set=pwm_setting_tmp[i];
        }
        main_ptr_time[0]=(uint16_t)T_PWM*tmp_set;
    }

    // auf Sync warten

    pwm_sync=0;             // Sync wird im Interrupt gesetzt
    while(pwm_sync==0);

    // Zeiger tauschen
    cli();
    tausche_zeiger();
    pwm_cnt_max = k;
    sei();
}

// Timer 1 Output COMPARE A Interrupt

ISR(TIMER1_COMPA_vect) {
    static uint8_t pwm_cnt;                     // ändern auf uint16_t
für mehr als 8 Bit Auflösung
    uint8_t tmp;                                // ändern uint16_t oder
uint32_t für mehr Kanäle

    OCR1A += isr_ptr_time[pwm_cnt];
    tmp    = isr_ptr_mask[pwm_cnt];

    if (pwm_cnt == 0) {
        PWM_PORT = tmp;                         // Ports setzen zu Begin
der PWM
                                                // zusätzliche PWM-Ports
hier setzen
        pwm_cnt++;
    }
    else {
        PWM_PORT &= tmp;                        // Ports löschen
                                                // zusätzliche PWM-Ports
hier setzen
        if (pwm_cnt == pwm_cnt_max) {
            pwm_sync = 1;                       // Update jetzt möglich
            pwm_cnt  = 0;
        }
        else pwm_cnt++;
    }
}

int main(void) {

    // PWM Port einstellen

    PWM_DDR = 0xff;         // Port als Ausgang
    // zusätzliche PWM-Ports hier setzen

    // Timer 1 OCRA1, als variablen Timer nutzen

    TCCR1B = 2;             // Timer läuft mit Prescaler 8
    TIMSK |= (1<<OCIE1A);   // Interrupt freischalten

    sei();                  // Interrupts gloabl einschalten


/******************************************************************/
// nur zum testen, in der Anwendung entfernen

// Test values
volatile uint8_t tmp;
const uint8_t t1[8]={255, 40, 3, 17, 150, 99, 5, 9};
const uint8_t t2[8]={27, 40, 3, 0, 150, 99, 5, 9};
const uint8_t t3[8]={27, 40, 3, 17, 3, 99, 3, 0};
const uint8_t t4[8]={0, 0, 0, 0, 0, 0, 0, 0};
const uint8_t t5[8]={9, 1, 1, 1, 1, 1, 1, 1};
const uint8_t t6[8]={33, 33, 33, 33, 33, 33, 33, 33};
const uint8_t t7[8]={0, 0, 0, 0, 0, 0, 0, 88};


// Messung der Interruptdauer
    tmp =1;
    tmp =2;
    tmp =3;

// Debug

    memcpy(pwm_setting, t1, 8);
    pwm_update();

    memcpy(pwm_setting, t2, 8);
    pwm_update();

    memcpy(pwm_setting, t3, 8);
    pwm_update();

    memcpy(pwm_setting, t4, 8);
    pwm_update();

    memcpy(pwm_setting, t5, 8);
    pwm_update();

    memcpy(pwm_setting, t6, 8);
    pwm_update();

    memcpy(pwm_setting, t7, 8);
    pwm_update();


/******************************************************************/

    while(1);
    return 0;
}



Hab hier auch mal den Link:
http://www.mikrocontroller.net/articles/Soft-PWM

wäre super wenn mir jemand helfen könnte

Gruß Andi

von Floh (Gast)


Lesenswert?

Andi schrieb:
> ATmega32 @ 8 MHz
> #define F_CPU         16000000L           // Systemtakt in Hz
Was jetzt?

längeren Code bitte als Anhang, mit dieser Zeilenlängenbegrenzung will 
das hier keiner lesen. :-)

von Andi (Gast)


Lesenswert?

Sorry kenn mich in diesem Forum noch nicht so wirklich aus. Ich versuche 
es jetzt mal so:
1
/*
2
    Eine 8-kanalige PWM mit intelligentem Lösungsansatz
3
4
    ATmega32 @ 8 MHz
5
6
*/
7
8
// Defines an den Controller und die Anwendung anpassen
9
10
#define F_CPU         16000000L           // Systemtakt in Hz
11
#define F_PWM         100L               // PWM-Frequenz in Hz
12
#define PWM_PRESCALER 8                  // Vorteiler für den Timer
13
#define PWM_STEPS     256                // PWM-Schritte pro
14
Zyklus(1..256)
15
#define PWM_PORT      PORTD              // Port für PWM
16
#define PWM_DDR       DDRD               // Datenrichtungsregister für
17
PWM
18
#define PWM_CHANNELS  8                  // Anzahl der PWM-Kanäle
19
20
// ab hier nichts ändern, wird alles berechnet
21
22
#define T_PWM (F_CPU/(PWM_PRESCALER*F_PWM*PWM_STEPS)) // Systemtakte pro
23
PWM-Takt
24
//#define T_PWM 1   //TEST
25
26
#if ((T_PWM*PWM_PRESCALER)<(111+5))
27
    #error T_PWM zu klein, F_CPU muss vergrösst werden oder F_PWM oder
28
PWM_STEPS verkleinert werden
29
#endif
30
31
#if ((T_PWM*PWM_STEPS)>65535)
32
    #error Periodendauer der PWM zu gross! F_PWM oder PWM_PRESCALER
33
erhöhen.
34
#endif
35
// includes
36
37
#include <stdint.h>
38
#include <string.h>
39
#include <avr/io.h>
40
#include <avr/interrupt.h>
41
42
// globale Variablen
43
44
uint16_t pwm_timing[PWM_CHANNELS+1];          // Zeitdifferenzen der PWM
45
Werte
46
uint16_t pwm_timing_tmp[PWM_CHANNELS+1];
47
48
uint8_t  pwm_mask[PWM_CHANNELS+1];            // Bitmaske für PWM Bits,
49
welche gelöscht werden sollen
50
uint8_t  pwm_mask_tmp[PWM_CHANNELS+1];        // ändern uint16_t oder
51
uint32_t für mehr Kanäle
52
53
uint8_t  pwm_setting[PWM_CHANNELS];           // Einstellungen für die
54
einzelnen PWM-Kanäle
55
uint8_t  pwm_setting_tmp[PWM_CHANNELS+1];     // Einstellungen der PWM
56
Werte, sortiert
57
                                              // ändern auf uint16_t für
58
mehr als 8 Bit Auflösung
59
60
volatile uint8_t pwm_cnt_max=1;               // Zählergrenze,
61
Initialisierung mit 1 ist wichtig!
62
volatile uint8_t pwm_sync;                    // Update jetzt möglich
63
64
// Pointer für wechselseitigen Datenzugriff
65
66
uint16_t *isr_ptr_time  = pwm_timing;
67
uint16_t *main_ptr_time = pwm_timing_tmp;
68
69
uint8_t *isr_ptr_mask  = pwm_mask;              // Bitmasken fuer
70
PWM-Kanäle
71
uint8_t *main_ptr_mask = pwm_mask_tmp;          // ändern uint16_t oder
72
uint32_t für mehr Kanäle
73
74
// Zeiger austauschen
75
// das muss in einem Unterprogramm erfolgen,
76
// um eine Zwischenspeicherung durch den Compiler zu verhindern
77
78
void tausche_zeiger(void) {
79
    uint16_t *tmp_ptr16;
80
    uint8_t *tmp_ptr8;                          // ändern uint16_t oder
81
uint32_t für mehr Kanäle
82
83
    tmp_ptr16 = isr_ptr_time;
84
    isr_ptr_time = main_ptr_time;
85
    main_ptr_time = tmp_ptr16;
86
    tmp_ptr8 = isr_ptr_mask;
87
    isr_ptr_mask = main_ptr_mask;
88
    main_ptr_mask = tmp_ptr8;
89
}
90
91
// PWM Update, berechnet aus den PWM Einstellungen
92
// die neuen Werte für die Interruptroutine
93
94
void pwm_update(void) {
95
96
    uint8_t i, j, k;
97
    uint8_t m1, m2, tmp_mask;                   // ändern uint16_t oder
98
uint32_t für mehr Kanäle
99
    uint8_t min, tmp_set;                       // ändern auf uint16_t
100
für mehr als 8 Bit Auflösung
101
102
    // PWM Maske für Start berechnen
103
    // gleichzeitig die Bitmasken generieren und PWM Werte kopieren
104
105
    m1 = 1;
106
    m2 = 0;
107
    for(i=1; i<=(PWM_CHANNELS); i++) {
108
        main_ptr_mask[i]=~m1;                       // Maske zum Löschen
109
der PWM Ausgänge
110
        pwm_setting_tmp[i] = pwm_setting[i-1];
111
        if (pwm_setting_tmp[i]!=0) m2 |= m1;        // Maske zum setzen
112
der IOs am PWM Start
113
        m1 <<= 1;
114
    }
115
    main_ptr_mask[0]=m2;                            // PWM Start Daten
116
117
    // PWM settings sortieren; Einfügesortieren
118
119
    for(i=1; i<=PWM_CHANNELS; i++) {
120
        min=PWM_STEPS-1;
121
        k=i;
122
        for(j=i; j<=PWM_CHANNELS; j++) {
123
            if (pwm_setting_tmp[j]<min) {
124
                k=j;                                // Index und
125
PWM-setting merken
126
                min = pwm_setting_tmp[j];
127
            }
128
        }
129
        if (k!=i) {
130
            // ermitteltes Minimum mit aktueller Sortiertstelle tauschen
131
            tmp_set = pwm_setting_tmp[k];
132
            pwm_setting_tmp[k] = pwm_setting_tmp[i];
133
            pwm_setting_tmp[i] = tmp_set;
134
            tmp_mask = main_ptr_mask[k];
135
            main_ptr_mask[k] = main_ptr_mask[i];
136
            main_ptr_mask[i] = tmp_mask;
137
        }
138
    }
139
140
    // Gleiche PWM-Werte vereinigen, ebenso den PWM-Wert 0 löschen falls
141
vorhanden
142
143
    k=PWM_CHANNELS;             // PWM_CHANNELS Datensätze
144
    i=1;                        // Startindex
145
146
    while(k>i) {
147
        while ( ((pwm_setting_tmp[i]==pwm_setting_tmp[i+1]) ||
148
(pwm_setting_tmp[i]==0))  && (k>i) ) {
149
150
            // aufeinanderfolgende Werte sind gleich und können
151
vereinigt werden
152
            // oder PWM Wert ist Null
153
            if (pwm_setting_tmp[i]!=0)
154
                main_ptr_mask[i+1] &= main_ptr_mask[i];        // Masken
155
vereinigen
156
157
            // Datensatz entfernen,
158
            // Nachfolger alle eine Stufe hochschieben
159
            for(j=i; j<k; j++) {
160
                pwm_setting_tmp[j] = pwm_setting_tmp[j+1];
161
                main_ptr_mask[j] = main_ptr_mask[j+1];
162
            }
163
            k--;
164
        }
165
        i++;
166
    }
167
168
    // letzten Datensatz extra behandeln
169
    // Vergleich mit dem Nachfolger nicht möglich, nur löschen
170
    // gilt nur im Sonderfall, wenn alle Kanäle 0 sind
171
    if (pwm_setting_tmp[i]==0) k--;
172
173
    // Zeitdifferenzen berechnen
174
175
    if (k==0) { // Sonderfall, wenn alle Kanäle 0 sind
176
        main_ptr_time[0]=(uint16_t)T_PWM*PWM_STEPS/2;
177
        main_ptr_time[1]=(uint16_t)T_PWM*PWM_STEPS/2;
178
        k=1;
179
    }
180
    else {
181
        i=k;
182
        main_ptr_time[i]=(uint16_t)T_PWM*(PWM_STEPS-pwm_setting_tmp[i]);
183
        tmp_set=pwm_setting_tmp[i];
184
        i--;
185
        for (; i>0; i--) {
186
            main_ptr_time[i]=(uint16_t)T_PWM*(tmp_set-pwm_setting_tmp[i]);
187
            tmp_set=pwm_setting_tmp[i];
188
        }
189
        main_ptr_time[0]=(uint16_t)T_PWM*tmp_set;
190
    }
191
192
    // auf Sync warten
193
194
    pwm_sync=0;             // Sync wird im Interrupt gesetzt
195
    while(pwm_sync==0);
196
197
    // Zeiger tauschen
198
    cli();
199
    tausche_zeiger();
200
    pwm_cnt_max = k;
201
    sei();
202
}
203
204
// Timer 1 Output COMPARE A Interrupt
205
206
ISR(TIMER1_COMPA_vect) {
207
    static uint8_t pwm_cnt;                     // ändern auf uint16_t
208
für mehr als 8 Bit Auflösung
209
    uint8_t tmp;                                // ändern uint16_t oder
210
uint32_t für mehr Kanäle
211
212
    OCR1A += isr_ptr_time[pwm_cnt];
213
    tmp    = isr_ptr_mask[pwm_cnt];
214
215
    if (pwm_cnt == 0) {
216
        PWM_PORT = tmp;                         // Ports setzen zu Begin
217
der PWM
218
                                                // zusätzliche PWM-Ports
219
hier setzen
220
        pwm_cnt++;
221
    }
222
    else {
223
        PWM_PORT &= tmp;                        // Ports löschen
224
                                                // zusätzliche PWM-Ports
225
hier setzen
226
        if (pwm_cnt == pwm_cnt_max) {
227
            pwm_sync = 1;                       // Update jetzt möglich
228
            pwm_cnt  = 0;
229
        }
230
        else pwm_cnt++;
231
    }
232
}
233
234
int main(void) {
235
236
    // PWM Port einstellen
237
238
    PWM_DDR = 0xff;         // Port als Ausgang
239
    // zusätzliche PWM-Ports hier setzen
240
241
    // Timer 1 OCRA1, als variablen Timer nutzen
242
243
    TCCR1B = 2;             // Timer läuft mit Prescaler 8
244
    TIMSK |= (1<<OCIE1A);   // Interrupt freischalten
245
246
    sei();                  // Interrupts gloabl einschalten
247
248
249
/******************************************************************/
250
// nur zum testen, in der Anwendung entfernen
251
252
// Test values
253
volatile uint8_t tmp;
254
const uint8_t t1[8]={255, 40, 3, 17, 150, 99, 5, 9};
255
const uint8_t t2[8]={27, 40, 3, 0, 150, 99, 5, 9};
256
const uint8_t t3[8]={27, 40, 3, 17, 3, 99, 3, 0};
257
const uint8_t t4[8]={0, 0, 0, 0, 0, 0, 0, 0};
258
const uint8_t t5[8]={9, 1, 1, 1, 1, 1, 1, 1};
259
const uint8_t t6[8]={33, 33, 33, 33, 33, 33, 33, 33};
260
const uint8_t t7[8]={0, 0, 0, 0, 0, 0, 0, 88};
261
262
263
// Messung der Interruptdauer
264
    tmp =1;
265
    tmp =2;
266
    tmp =3;
267
268
// Debug
269
270
    memcpy(pwm_setting, t1, 8);
271
    pwm_update();
272
273
    memcpy(pwm_setting, t2, 8);
274
    pwm_update();
275
276
    memcpy(pwm_setting, t3, 8);
277
    pwm_update();
278
279
    memcpy(pwm_setting, t4, 8);
280
    pwm_update();
281
282
    memcpy(pwm_setting, t5, 8);
283
    pwm_update();
284
285
    memcpy(pwm_setting, t6, 8);
286
    pwm_update();
287
288
    memcpy(pwm_setting, t7, 8);
289
    pwm_update();
290
291
292
/******************************************************************/
293
294
    while(1);
295
    return 0;
296
}

von Rainer (Gast)


Lesenswert?

Wer soll denn das lesen. Bitte hänge doch den Quellcode als Datei über 
Dateianhang dran. (Taste /Durchsuchen.../ unter Betreff).

Zu deinem Problem:
Läuft denn im Debugger alles sauber durch?
D.h. sind die vorberechneten Werte wie erwartet, passiert im IRQ das 
richtige und stimmen beim Port die DDR und PORT-Werte?

Gruß
Rainer

von Andi (Gast)


Angehängte Dateien:

Lesenswert?

Ok ich versuche es nochmal :)

Zu deinen Fragen:
- Der Debugger bringt keinerlei Fehlermeldungen oder Warnungen
- Die Ports sind auch korrekt PortD als Ausgang und dieser auch gesetzt

Ehrlich gesagt kann ich dir mit der IRQ Frage nicht folgen, was ist ein 
IRQ?

Hab jetzt mal alles angehängt:)

von Rainer (Gast)


Lesenswert?

Mit dem Anhang sieht das doch viel besser aus. Anscheinend kommt das 
Forum nur mit den Projektdateien nicht zurecht, keine Ahnung wieso. In 
solch einem Fall hilft das Verpacken in eine ZIP-Datei. Aber egal.

> - Der Debugger bringt keinerlei Fehlermeldungen oder Warnungen

Du meinst wahrscheinlich nicht Debugger sondern Compiler und 
Linker oder?

Mit dem Debugger von AVR-Studio kannst du dem Prozessor in der 
Simulation des Programmablaufes Schritt für Schritt auf die Finger 
gucken und verfolgen was passiert. Dafür mußt du natürlich erstmal 
wissen, was passieren soll. Aber dazu ist ja in dem Artikel Soft-PWM 
einiges geschrieben. Den Debugger würde ich erstmal mit einem weniger 
komplexen Programm ausprobieren.

> Ehrlich gesagt kann ich dir mit der IRQ Frage nicht folgen, was ist
> ein IRQ?

IRQ ist die Abkürzung von Interrupt, d.h. eine Unterbrechung des gerade 
laufenden Programmteils.

Gruß Rainer

von Andi (Gast)


Lesenswert?

Erstmal danke Rainer, werde jetzt noch mal den Bericht in aller Ruhe 2-3 
mal lesen und hoffen das ich irgendwas versteh und nebenbei das ganze 
mit dem Debugger versuchen, dann ist der debugger also das 
Simulationstool :)

Gruß Andi

von Rainer (Gast)


Lesenswert?

Hallo Andi,

beim Simulieren wirst du sehen, dass im Hauptprogramm gleich nachdem 
Port, Timer und Interruptflag (OCIE1A) gesetzt sind, über sei() die 
Interruptbearbeitung freigegeben wird. Die Steuerung fängt also an zu 
laufen, bevor die Daten zum Test und debuggen geladen und vorbereitet 
worden sind. Das geht auch mit dem Programm nicht anders, weil die 
Routine pwm_update(), die das Vorbereiten übernimmt, über das Flag 
pwm_sync mit der InterruptServiceRoutine ISR(TIMER1_COMPA_vect) 
zusammenarbeitet. Das stört aber nicht weiter. Das Hauptprogramm macht 
dann nach dem Start eigentlich gar nichts mehr und kreist in "while 
(1);" (ganz am Ende) nur um sich selbst. In der Simulation sieht man das 
dann gar nicht.

Rainer

P.S. IRQ heißt richtig Interrupt Request, d.h. das ist die 
Unterbrechungsanforderung, die dann zum Aufruf der 
InterruptServiceRoutine führt.

von Andi (Gast)


Angehängte Dateien:

Lesenswert?

So,
hab jetzt mal den kleineren Code aus dem Beitrag zerstückelt und so weit 
ich es verstanden hab kommentiert. Ab dem Moment wo jedoch die 
Interrupts ins Spiel kommen steig ich aus. Hab jetzt mal gegoogelt und 
verstehe auch das Prinzip des ganzen. Ich denke das mit den Interrupts 
ist auch nicht das Problem warum meine 8 LED's an Port D nicht leuchten 
:(

Ich frage mich nur ob ich im main nicht irgendwo die Ausgänge aktivieren 
muss ich setze dort ja nur das Datenrichtungsregister.

Was auch ganz unverständlich ist, was dieser Programmteil hier macht:

    memcpy(pwm_setting, t1, 8);

    memcpy(pwm_setting, t2, 8);

    memcpy(pwm_setting, t3, 8);

    memcpy(pwm_setting, t4, 8);

    memcpy(pwm_setting, t5, 8);

    memcpy(pwm_setting, t6, 8);

Hab im Anhang mal den kommentierten Code, was mit ????? gekennzeichnet 
ist ist eine Vermutung und ich hab keine Ahnung ob das so passt.

von Rainer (Gast)


Lesenswert?

Hallo Andi,

um die Fragezeichen würde ich mich später kümmer. Verstehe erstmal die 
8-Kanal Version. Der ATmega32 mit 16 MHz schafft die auf jeden Fall.

Mit der Definition
   #define PWM_DDR DDRD    // Datenrichtungsregister für PWM
und der Zeile
   PWM_DDR = 0xFF;         // Port als Ausgang
werden alle 8 Anschlüsse vom Port D als Ausgang programmiert und daran 
wird während des Programmlaufs auch nicht mehr gerüttelt.

Die Ausgangswerte, d.h. das Ein- und Ausschalten der LEDs passiert dann 
in der IRS-Routine mit
  "PWM_PORT = tmp;"  bzw. "PWM_PORT &= tmp;"

Während der Programmteil mit den memcpy Aufrufen läuft, müßte sich 
dauern die Helligkeit ändern und dann bleiben sie auf einem Endwert 
stehen, den du siehst. Die Zeit, die die Steuerung auf jeder einzelnen 
Helligkeitsstufe läuft, kannst du dir mit dem Debugger ansehen, wenn du 
auf die Zeilen mit den memcpy-Aufrufen jeweils einen Breakpoint legst, 
so dass das Programm dort anhält, und du dir dann links in dem Fenster 
"Prozessor" die unter "Stop-Watch" angezeigte Zeit ansiehst.

In den Konstanten-Arrays T1 bis T7 stehen jeweils komplette Sätze von 
neuen Helligkeitswerten, die während der laufenden PWM-Ausgabe mit 
memcpy nach pwm_setting kopiert werden und dann mit pwm_update 
aufbereitet (so wie in dem Artikel beschrieben) und am Ende mit 
tausche_zeiger an die Interruptgesteuerte PWM-Erzeugung übergeben 
werden. Da kommt die im Artikel beschrieben Pufferung ins Spiel, die 
erlaubt, dass die PWM weiterläuft, während mit pwm_update schon die 
neuen Steuerwerte erzeugt werden.

Damit die einzelnen Helligkeitsstufen länger zu sehen sind, könntest du
vor den Aufrufen von memcpy jeweils die Verzögerungsfunktion
    _delay_ms (1000);  // 1000 ms warten
einbauen und müßtest dann mehr sehen können.

Damit der Compiler die Funktion _delay_ms finden kann muß dazu die
passende Bibliothek mit der Zeile
#include <util/delay.h>
mit eingebunden werden, z.B. ziemlich oben nach
"#include <avr/interrupt.h>".

Die Funktionsbeschreibung von memcpy findest du z.B. in
  http://de.wikibooks.org/wiki/C-Programmierung
unter Referenzen  Standardbibliotheken  string.h beschrieben.

Der ganze Knackpunkt an dem Algorithmus ist die Art, wie in pwm_update 
aus den Helligkeitswerten die Zeiten für den Timer und die Bitmuster für 
den Ausgang erzeugt werden. Da steckt schon einiges dahinter. Vielleicht 
fängst du nicht mit der kompliziertesten Version an, sondern probierst 
die einfachern Versionen aus dem Artikel mal der Reihe nach mit 
Verstehen (!)durch. Dann ist der Schritt nicht gleich so riesengroß.

Viel Spaß

Rainer

von Andi (Gast)


Lesenswert?

Ja hab mir jetzt auch mal die erste Version vorgeknüpft. Viellen Dank 
für deine umfassende Hilfe bin dir wirklich zu tiefem Dank verpflichtet. 
So werd mich jetzt mal dran machen dies nachvollziehen zu können :) Wenn 
es was neues gibt melde ich mich.

Gruß Andi

von Andi (Gast)


Lesenswert?

So lieber Rainer,

dein Tip mit der delay war Gold wert, hab auf diese Weise das meißte 
verstehen können und die PWM läuft jetzt auch, auch konnte ich sie 
inzwischen meinen Anforderungen nach anpassen.
Jetzt habe ich nur noch ein Problem, ich lege Spannungsversorgung an den 
Controller und er dimmt die LED's entsprechend der 7 voreingestellten 
Stufen. Hat er Stufe 7 erreicht tut sich nichts mehr die LED's leuchten 
nun mit dem für Schritt 7 eingestellten Wert. Ich denke das soll alles 
ja auch so, aber wie bekomm ich jetzt zum Beispiel das ganze erneut 
gestartet? Normalerweise würde ja eine While Schleife ausreichen um die 
LED's wieder von vorne dimmen zu lassen. Aber selbst wenn ich über den 
Gesamten Inhalt des "mains" ne Schleife lege und die while vor dem 
return Befehl am Ende des Programms raushaue bleibt der Controller in 
Stufe 7 stehen :(
Kanns aber auch nicht nachvollziehen :(

Gruß Andi

von Rainer (Gast)


Lesenswert?

Hi Andi,

na klasse.

Dann wird das mit der Schleife auch noch hinzukriegen sein.

Eigentlich sollte das mit der While-Schleife ok sein, am besten nicht 
über das ganze main, sondern nur über den Bereich wo die 
Helligkeitsvektoren geladen werden, da die Initialierung ja erledigt ist 
und sich nur die Helligkeitswerte wiederholen sollen, also:

// Debug
#define ZEITms  200  // Zeitdauer der PWM-Muster T1..T7

    while (1) {
      memcpy(pwm_setting, t1, 8);
      pwm_update();
      _delay_ms(ZEITms);
...
      memcpy(pwm_setting, t7, 8);
      pwm_update();
    _delay_ms(ZEITms);
    }

Dann muß das eigenlich funktionieren. Probier mal...

Das bisherige while(1); am Ende ist egal, weil das Programm da sowieso 
nicht mehr hin kommt, wenn es in der neuen Schleife festhängt.

Gruß Rainer

von Andi (Gast)


Lesenswert?

So hab jetzt mal das ganze ausprobiert, klappt jetzt auch mit der 
Schleife. Was mich jedoch sehr verwundert hat es funktioniert nur wenn 
ich die andere While(1); und den return Befehl rausschmeiße.

Jetzt bin ich grad an der Auflösung dran bekomme das aber noch nicht 
wirklich hin. Wollte die Auflösung von 8 auf 16 ändern um mehr Stufen 
zum dimmen zu erhalten. Der Schreiber des Codes hat die Stellen ja 
kommentiert die man ändern soll, leider bringt er mir Warnhinweise 
sobald ich in der Liste in der "main" weitere Zeilen einfüge.
Die Warnhinweise lauten wie folgt:

unused variable 't8' usw.

Vielleicht weiß ja jemand die Lösung...

Gruß Andi

von Falk B. (falk)


Lesenswert?

@  Andi (Gast)

>Jetzt bin ich grad an der Auflösung dran bekomme das aber noch nicht
>wirklich hin. Wollte die Auflösung von 8 auf 16 ändern um mehr Stufen
>zum dimmen zu erhalten.

Lesen und Verstehen scheinen nicht zu deinen Kernkompetenzen zugehören.
Die Angabe bezieht sich nicht auf die STUFEN, sondern BITBREITE!
8 Bit = 256 Stufen. Mehr als 10 Bit =1024 Stufen sind nicht drin.

MFG
Falk

von Andi (Gast)


Lesenswert?

Ne sind wirklich nicht so meins, bin noch Frischling in C und in 
Mikrocontrollern und versuch grad durch lerning by doing mir das Ganze 
anzueignen. Ich lerne auf diese Weise wohl am schnellsten :)

Klappt jetzt auf jeden Fall, hatte ganz vergessen das ich unterhalb der 
Liste die Variablen erst einmal aufrufen muss.

Doch wenn ich dich jetzt richtig verstanden habe kann ich mit 8-Bit 
meine Dimmung über 1024 Stufen laufen lassen???

Hab jetzt ein Long für die Auflösung verwendet falls das so richtig ist, 
weil ich dachte ich könne nur auf diese Weise 32 Stufen zum dimmen 
erhalten.

Gruß Andi

von Falk B. (falk)


Lesenswert?

OMG!

von Rainer (Gast)


Lesenswert?

Andi schrieb:
> Doch wenn ich dich jetzt richtig verstanden habe kann ich mit 8-Bit
> meine Dimmung über 1024 Stufen laufen lassen???

8-Bit Helligkeitswerte und 1024 Stufen paßt irgendwie nicht zusammen, 
oder?
Da würd' ich nochmal nachzählen, was in dem Programm die Amplituden- und 
was die Zeitauflösung bestimmt.

Rainer

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.