Forum: Compiler & IDEs AVR GCC Software PWM


von Rudolf Bremer (Gast)


Lesenswert?

Hallo an alle.

Bei der suche nach einer PWM Lüftersteuerung im netz, bin ich auf diese 
seite gestossen: 
http://www.mikrocontroller.net/articles/Soft-PWM#Intelligenter_L.C3.B6sungsansatz 
. Ich habe mich ganz besonderes auf die 8 Kanäle gefreut und dachte mir, 
"ok, so schwer kann es nicht sein, einwenig haste ja druff". Ich habe 
den Code für ein Atmega16 compiliert ohne etwas zu verändern aber, es 
geht nicht. Ich schreibe ab und zu selber kleine programchen, das ABC 
habe ich drauf, im AVR Studio bleibt der Zeiger nach click auf pause 
immer bei
1
  // auf Sync warten
2
 
3
      pwm_sync=0;             // Sync wird im Interrupt gesetzt
4
  ->  while(pwm_sync==0);
und das gibt mir zu denken, dass das interrupt nicht ausgelöst wird.

Für jede hilfe bin ich dankbar, und so werde ich auch etwas neues 
lernen.

MFG

von OliverSo (Gast)


Lesenswert?

>und das gibt mir zu denken, dass das interrupt nicht ausgelöst wird.

Wie lange hast du denn gewartet? Schliesslich muß der Interrupt nicht 
nur einmal aufgerufen werden, bis deine while Bedingung erfüllt wird, 
und im Studio vergeht die "Echtzeit" arg langsam. Um zu prüfen, ob der 
Interrupt ausgelöst wird, empfiehlt es sich, mal auf dessen erste Zeile 
eine breakpoint zu setzen. Und dann warten...

Oliver

von Rudolf Bremer (Gast)


Lesenswert?

Hallo, danke für die Antwort erst mall.

Ich habe ein JTAG ICE und da leuft ja alles so ziemlich in echtzeit, 
also keine simulation.
Ich habe gemacht was du vor geschlagen hast und, leider zind meine 
vermutungen falsch gewesen, nach jedem RUN befehl stoppt das program bei 
der ISR.

Hmm....Jemand noch eine idee?

von Rudolf Bremer (Gast)


Lesenswert?

Ich habe gerade festgestelld, dass die Definition die #Define für 
PWM_PORT und PWM_DDR nicht funktionieren. Im AVR Studio ist zu sehen das 
die Port richtungs register nicht richtig gesetzt werden. Was könte die 
ursache dfür sein?

MFG

von Karl H. (kbuchegg)


Lesenswert?

Rudolf Bremer wrote:

> vermutungen falsch gewesen, nach jedem RUN befehl stoppt das program bei
> der ISR.

Das sieht doch nicht schlecht aus.
Wenn du einen Breakpoint am Begin der ISR hast und sich der
Debugger nach einem Run am Begin der ISR wieder meldet, dann
ist doch alles so wie es sein soll.

Ab dort gehst du dann mit Einzelschrittbefehlen weiter durch
die ISR.

Ist die ISR fertig abgearbeitet, dann müsste nach dem Rücksprung
aus der ISR die while-Schleife verlassen werden.

Ich denke auch, dass hier erst mal ein Bedienungsproblem
des Debuggers vorliegt und kein Programmfehler.

von Karl H. (kbuchegg)


Lesenswert?

Rudolf Bremer wrote:
> Ich habe gerade festgestelld, dass die Definition die #Define für
> PWM_PORT und PWM_DDR nicht funktionieren. Im AVR Studio ist zu sehen das
> die Port richtungs register nicht richtig gesetzt werden. Was könte die
> ursache dfür sein?

Ev. hast du im Simulator einen anderen Prozessor eingestellt
als der für den du compiliert hast.

von Rudolf Bremer (Gast)


Lesenswert?

DAs ging aber schnell... Im Studio ist der richtige procesor 
eingestellt, die testplatine leuft wunderbar, schon zig sachen dmit 
getestet, da ist alles in ordnung... könte mein makefile irgendwie 
schuld daran sein?
Nach dem in main PWM_DDR = 0xFF; aufgerufen wird wird R24 0xFF. R24 ist 
aber kein portregister, oder? Nach dem nächsten schritt wir R24 0x01 und 
nach dem dritten wird 0x10

von Jojo S. (Gast)


Lesenswert?

fehlt in der pwm_snyc deklaration das Zauberwort 'volatile' ?

von Jojo S. (Gast)


Lesenswert?

ok, im angegebenen Link steht es auf jedenfall drin.

von Rudolf Bremer (Gast)


Lesenswert?

Nein, ist dabei. Hmmmm...

von Rudolf Bremer (Gast)


Lesenswert?

Ich poste mal den code hier, könte sein das sich da ein fehler 
eingeschlichen hat:
1
// Defines an den Controller und die Anwendung anpassen
2
  
3
 //#define F_CPU 8000000L                  // Systemtakt in Hz
4
#define F_PWM 100L                      // PWM-Frequenz in Hz
5
#define PWM_STEPS 256                   // PWM-Schritte pro Zyklus(1..256)
6
#define PWM_PORT PORTB                 // Port für PWM
7
#define PWM_DDR DDRB                    // Datenrichtungsregister für PWM
8
 
9
// ab hier nichts ändern, wird alles berechnet
10
 
11
#define T_PWM (F_CPU/(F_PWM*PWM_STEPS)) // Systemtakte pro PWM-Takt
12
//#define T_PWM 1   // für TESTs
13
 
14
#if (T_PWM<(93+5))
15
    #error T_PWM zu klein, F_CPU muss vergrösst werden oder F_PWM oder PWM_STEPS verkleinert werden
16
#endif
17
 
18
// includes
19
20
 
21
#include <stdint.h>
22
#include <string.h>
23
#include <avr/io.h>
24
#include <avr/interrupt.h>
25
 
26
27
// globale Variablen
28
 
29
uint8_t pwm_setting[8];                 // Einstellungen für die einzelnen PWM-Kanäle
30
uint16_t pwm_timing[9];                 // Zeitdifferenzen der PWM Werte
31
uint8_t pwm_mask[9];                    // Bitmaske für PWM Bits, welche gelöscht werden sollen
32
uint8_t pwm_cnt_max;                    // Zählergrenze
33
volatile uint8_t pwm_sync;              // Update jetzt möglich
34
 
35
// PWM Update, berechnet aus den PWM Einstellungen
36
// die neuen Werte für die Interruptroutine
37
 
38
void pwm_update(void) {
39
    uint8_t i, j, tmp, min;
40
    uint8_t index=0;
41
 
42
    uint8_t pwm_setting_tmp[8];     // Einstellungen der PWM Werte, sortiert
43
    uint16_t pwm_timing_tmp[9];     // Zeitdifferenzen der PWM Werte
44
    uint8_t pwm_mask_tmp[9];        // Bitmaske für PWM Bits, welche gelöscht werden sollen
45
 
46
    // PWM setting (0) berechnen
47
    // gleichzeitig die Bitmasken generieren und PWM settings kopieren
48
 
49
    tmp=0;
50
    j = 1;
51
    for(i=0; i<8; i++) {
52
        pwm_mask_tmp[i]=~j;                         // Maske zum Löschen der PWM Ausgänge
53
        pwm_setting_tmp[i] = pwm_setting[i];
54
        if (pwm_setting[i]!=0) tmp |= j;
55
        j <<= 1;
56
    }
57
    pwm_mask_tmp[8]=tmp;
58
    
59
    // PWM settings sortieren; Einfügesortieren
60
 
61
    for(i=0; i<8; i++) {
62
        min=255;
63
        for(j=i; j<8; j++) {
64
            if (pwm_setting_tmp[j]<min) {
65
                index =j;                   // Index und PWM-setting merken
66
                min = pwm_setting_tmp[j];
67
            }
68
        }
69
        if (index!=i) {
70
            // ermitteltes Minimum mit aktueller Sortiertstelle tauschen
71
            tmp = pwm_setting_tmp[index];
72
            pwm_setting_tmp[index] = pwm_setting_tmp[i];
73
            pwm_setting_tmp[i] = tmp;
74
            tmp = pwm_mask_tmp[index];
75
            pwm_mask_tmp[index] = pwm_mask_tmp[i];
76
            pwm_mask_tmp[i] = tmp;
77
        }
78
    }
79
 
80
    // Gleiche PWM-Werte vereinigen, ebenso den PWM-Wert 0 löschen falls vorhanden
81
 
82
    tmp=8;          // maximal 8 Datensätze
83
    min=8;          // genau 8 Operationen, Löschen oder behalten
84
    i=0;            // Startindex
85
 
86
    while(min>0) {
87
        while ( ((pwm_setting_tmp[i]==pwm_setting_tmp[i+1]) || (pwm_setting_tmp[i]==0))  && (tmp>1) ) {
88
            // aufeinanderfolgende Werte sind gleich und können vereinigt werden
89
            // oder PWM Setting ist Null
90
            if (pwm_setting_tmp[i]!=0) pwm_mask_tmp[i+1] &= pwm_mask_tmp[i];        // Masken vereinigen
91
            tmp--;
92
            min--;
93
            // Datensatz entfernen
94
            for(j=i; j<tmp; j++) {
95
                pwm_setting_tmp[j] = pwm_setting_tmp[j+1];
96
                pwm_mask_tmp[j] = pwm_mask_tmp[j+1];
97
            }
98
            pwm_setting_tmp[j] = 0;
99
        }
100
        i++;
101
        min--;
102
    }
103
 
104
    // Zeitdifferenzen berechnen
105
 
106
    j=pwm_setting_tmp[0];
107
    for (i=0; i<(tmp-1); i++) {
108
        pwm_timing_tmp[i]=(uint16_t)T_PWM*(pwm_setting_tmp[i+1]-j);
109
        j=pwm_setting_tmp[i+1];
110
    }
111
    pwm_timing_tmp[i]=(uint16_t)T_PWM*(256-j);
112
    i++;
113
    pwm_timing_tmp[i]=(uint16_t)T_PWM*pwm_setting_tmp[0];
114
    pwm_mask_tmp[i]=pwm_mask_tmp[8];
115
 
116
    if (pwm_setting_tmp[0]==0) pwm_timing_tmp[i]=(uint16_t)T_PWM;   // Sonderfall, wenn alle Kanäle 0 sind
117
 
118
    // auf Sync warten
119
 
120
    pwm_sync=0;             // Sync wird im Interrupt gesetzt
121
    while(pwm_sync==0);
122
 
123
    // neue PWM-Daten kopieren mit maximaler Geschwindigkeit
124
    cli();
125
    pwm_timing[8]=pwm_timing_tmp[8];
126
    pwm_mask[8]=pwm_mask_tmp[8];
127
    pwm_timing[7]=pwm_timing_tmp[7];
128
    pwm_mask[7]=pwm_mask_tmp[7];
129
    pwm_timing[6]=pwm_timing_tmp[6];
130
    pwm_mask[6]=pwm_mask_tmp[6];
131
    pwm_timing[5]=pwm_timing_tmp[5];
132
    pwm_mask[5]=pwm_mask_tmp[5];
133
    pwm_timing[4]=pwm_timing_tmp[4];
134
    pwm_mask[4]=pwm_mask_tmp[4];
135
    pwm_timing[3]=pwm_timing_tmp[3];
136
    pwm_mask[3]=pwm_mask_tmp[3];
137
    pwm_timing[2]=pwm_timing_tmp[2];
138
    pwm_mask[2]=pwm_mask_tmp[2];
139
    pwm_timing[1]=pwm_timing_tmp[1];
140
    pwm_mask[1]=pwm_mask_tmp[1];
141
    pwm_timing[0]=pwm_timing_tmp[0];
142
    pwm_mask[0]=pwm_mask_tmp[0];
143
    pwm_cnt_max = tmp;
144
    sei();
145
}
146
 
147
// Timer 1 Output COMPARE A Interrupt
148
 
149
ISR(TIMER1_COMPA_vect) {
150
    static uint8_t pwm_cnt=0;
151
    uint8_t tmp;
152
 
153
    OCR1A += pwm_timing[pwm_cnt];
154
    tmp    = pwm_mask[pwm_cnt];
155
    
156
    if (pwm_cnt == pwm_cnt_max) {
157
        PWM_PORT = tmp;                         // Ports setzen zu Begin der PWM
158
        pwm_cnt  = 0;
159
    }
160
    else {
161
        PWM_PORT &= tmp;                        // Ports löschen
162
        pwm_cnt++;
163
        if (pwm_cnt == pwm_cnt_max) pwm_sync = 1;   // Update jetzt möglich
164
    }
165
    
166
}
167
 
168
int main(void) {
169
 
170
    // PWM einstellen
171
    
172
    PWM_DDR = 0xFF;         // Port als Ausgang
173
    
174
    // Timer 1 OCRA1, als variablem Timer nutzen
175
 
176
    TCCR1B = 1;             // Timer läuft mit vollem Systemtakt
177
    TIMSK |= (1<<OCIE1A);   // Interrupt freischalten
178
 
179
    sei();                  // Interrupts gloabl einschalten
180
 
181
 
182
/******************************************************************/
183
// nur zum testen, in der Anwendung entfernen
184
 
185
// Test values
186
volatile uint8_t tmp;
187
const uint8_t t1[8]={27, 40, 31, 17, 150, 99, 5, 9};
188
const uint8_t t2[8]={27, 40, 3, 0, 150, 99, 5, 9};
189
const uint8_t t3[8]={27, 40, 3, 17, 3, 99, 3, 0};
190
const uint8_t t4[8]={65, 75, 20, 255, 128, 80, 90, 20};
191
const uint8_t t5[8]={0, 56, 78, 23, 45, 80, 50, 9};
192
const uint8_t t6[8]={33, 33, 33, 33, 33, 33, 33, 33};
193
 
194
// Messung der Interruptdauer
195
    tmp =0;
196
    tmp =0;
197
    tmp =0;
198
 
199
// Debug 
200
 
201
    memcpy(pwm_setting, t1, 8);
202
    pwm_update();
203
 
204
    memcpy(pwm_setting, t2, 8);
205
    pwm_update();
206
 
207
    memcpy(pwm_setting, t3, 8);
208
    pwm_update();
209
 
210
    memcpy(pwm_setting, t4, 8);
211
    pwm_update();
212
 
213
    memcpy(pwm_setting, t5, 8);
214
    pwm_update();
215
    
216
    memcpy(pwm_setting, t6, 8);
217
    pwm_update();
218
/******************************************************************/
219
 
220
    return 0;
221
}

von Karl H. (kbuchegg)


Lesenswert?

Rudolf Bremer wrote:

> Nach dem in main PWM_DDR = 0xFF; aufgerufen wird wird R24 0xFF. R24 ist
> aber kein portregister, oder? Nach dem nächsten schritt wir R24 0x01 und
> nach dem dritten wird 0x10

R24 interessiert keinen.
Was passiert im I/O View mit dem Port-Register?
Nur das ist interessant.

von Rudolf Bremer (Gast)


Lesenswert?

Ich muss mich veguckt haben, der Portrichtungsregister wird richtig 
gesetzt im I/O View wird der DDRB auf 0xFF gesetzt. Also werden die 
Register richtig definiert.

HEULLL

von Rudolf Bremer (Gast)


Lesenswert?

Ich habe gerade im Studio
1
 while(pwm_sync==0);
übersprungen und, ab da geht es.Also die ISR :
1
    else {
2
        PWM_PORT &= tmp;                        // Ports löschen
3
        pwm_cnt++;
4
        if (pwm_cnt == pwm_cnt_max) pwm_sync = 1;   // Update jetzt möglich
 kommt garnicht dazu pwm_sync auf 1 zu setzen.

von Karl H. (kbuchegg)


Lesenswert?

Grundsatzfrage:
Wird die ISR jetzt angesprungen oder wird sie es nicht?

Man kann in eine ISR genauso einen Breakpoint setzen, wie
in jede andere Funktion auch. Wird die ISR angesprungen,
dann läuft sie auch auf den Breakpoint auf.
Ab dort musst du dann mit Singlesteps rausfinden was da abgeht.

Im Moment hab ich das Gefühl du spekulierst ...
> kommt garnicht dazu pwm_sync auf 1 zu setzen.
... und Spekulation ist kein guter Ratgeber beim Debuggen.

von OliverSo (Gast)


Lesenswert?

Auch wenn ein JTAG was feines ist, am allerschönsten für solche 
Fehlersuchen ist VMLAB. Damit sieht man dann direkt, was abgeht.

Ansonsten setzt mal Breakpoints auf jede der folgend Zeilen der ISR:

    if (pwm_cnt == pwm_cnt_max) {
    pwm_cnt++;
    if (pwm_cnt == pwm_cnt_max) pwm_sync = 1;

und dann noch einen auf deine while-Bedingung,

und stepp dann jedes mal im Einzelschrittbetrieb durch die ISR. Dann 
siehst du, ob pwm_cnt inkremetiert wird, wenn nicht, warum nicht, und 
wenn ja, wann pwm_sync zu 1 wird.

Oliver

von Rudolf Bremer (Gast)


Lesenswert?

Die ISR wird angesprungen, ich habe die Braekpoints markiert aber, es 
ist so, dass nur der erste IF angesprungen wird (in der ISR)und 
pwm_cnt/pwm_cnt_max immer 0 sind. Ist es richtig, dass pwm_cnt in der 
ISR mit 0 init. wird? Was passiert damit wenn die ISR aufgerufen wird, 
ich meine, wird sie jedesmall neu init.?

von Karl H. (kbuchegg)


Lesenswert?

Rudolf Bremer wrote:
> Die ISR wird angesprungen, ich habe die Braekpoints markiert aber, es
> ist so, dass nur der erste IF angesprungen wird (in der ISR)und
> pwm_cnt/pwm_cnt_max immer 0 sind.

Nein.

pwm_cnt ist static, wird also nur beim ersten Betreten der ISR
initialisiert.
pwm_cnt_max wird in pwm_update berechnet.
Bevor also diese Funktion nicht aufgerufen wird, hat es keinen
Sinn die ISR zu Debuggen.

Daher ist auch das vorzeitige Freigeben des Interrupts mit
sei() nicht unbedingt toll. So wie ich das sehe, macht das
die pwm_update() Funktion ja sowieso -> sollte aus der main
herausgenommen werden.

Was mich stutzig macht: Im main() gibt es keine Hauptschleife.

von Rudolf Bremer (Gast)


Lesenswert?

ammm, ind der pwm_update wird sei(); erst nach while(pwm_sync==0); und 
da pwm_sync erst in der ISR bearbeitet wird muss, meiner meinung nach, 
sei(); in main sein...oder nicht?!? Was die hauptschleife angeht... wie 
weiter oben gesagt, wenn ich Studio anhalte, steht der Zeiger auf: 
while(pwm_sync==0); und, wenn ich dann zum nächsten befehl springe ( 
cli(); )
1
  sei();
2
    pwm_sync=0;             // Sync wird im Interrupt gesetzt
3
   --<- while(pwm_sync==0);
4
   |
5
   |    // neue PWM-Daten kopieren mit maximaler Geschwindigkeit
6
   |->   cli();
7
        pwm_timing[8]=pwm_timing_tmp[8];
8
         pwm_mask[8]=pwm_mask_tmp[8];
 Funktioniert es, also 8 verschiedene PW werte am den port pins und, der 
Zeiger, nach dem ich Studio angehalten habe, ist ganz am ende der main. 
Anscheinend reicht ab dem pwm_update(); die ISR aus.

MFG

von Karl H. (kbuchegg)


Lesenswert?

Rudolf Bremer wrote:
> ammm, ind der pwm_update wird sei(); erst nach while(pwm_sync==0); und
> da pwm_sync erst in der ISR bearbeitet wird muss, meiner meinung nach,
> sei(); in main sein...oder nicht?!?

Ooops.
Mein Fehler. Da hast du absoult recht, hab ich übersehen.

von Rudolf Bremer (Gast)


Lesenswert?

Hat jemand im forum schon mall das erwehnte Program benutzt oder, weiss 
jemand wer das geschrieben hat? Das Program ist zwar etwas gross aber, 
laut Entwikler braucht es nur 0,1 bis 1% cpu leistung für 8 Kanäle, ist 
schon was, da bleibt noch eine menge leistung für LCD, tastenentprelung 
und weiss der geier was noch... Es ist das beste Soft PWM das ich bis 
jetzt gesehen habe und, es wäre traurig wenn es nicht geht. Hat denn 
jemand von euch die Möglichkeit es zu testen? Am besten gleich mit ein 
Atmega16. Ich habe es nicht so drauf mit Programieren und, des wegen 
Schreie ich hier noch mall nach Hilfe.

P.S: Es wird denke ich, auch anderen Anfänger helfen mit der materie 
klar zu kommen. Ich würde natürlich auch gerne wissen warum das nicht 
geht, ich zerbreche mir damit den kopf seit etwa 1 Woche, ich kriege 
langsam ´ne krise.

Für jede hilfe bin ich dankbar. MFG

von Oliver (Gast)


Lesenswert?

Also, ich habs mal in vmlab getestet.

So, wie das das steht, ist das Programm noch nie gelaufen. Neben deinem 
while-Problem, das daran liegt, daß pwm_cnt_max zu Beginn nirgends 
initialisiert wird, und deshalb das erste
1
if (pwm_cnt == pwm_cnt_max)
 in der ISR immer wahr bleibt, ist da noch mehr im argen. pwm_cnt wird 
größer als pwn_cnt_max, und damit gehen alle folgende array-Zugrife in 
die Hose. pwm_cnt_max wird zunächst auf 8 gesetzt, wenn dann pwn_cnt bis 
8 hochgezählt hat, wird pwm_cnt_max auf 7 gesetzt (warum auch immer) - 
ohne pwm_cnt zurückzusetzen, das immer noch auf 8 steht, und welches im 
folgenden fröhlich weiter erhöht wird, weil pwm_cnt == pwm_cnt_max erst 
wieder wahr wird, wenn der Zähler einmal "rum" ist.

An der Stelle habe ich dann aufgehört. Die Grundidee des Codes mag ja 
brauchbar sein, aber was da steht, ist Schrott. Da musst du noch einiges 
an Gehirnschmalz reinstecken, und direkt selbsterklärend ist der Code 
auch nicht.

Viel Spaß (!)

Oliver

von Rudolf Bremer (Gast)


Lesenswert?

@ Oliver
Danke für die mühe die du dir gemacht hast. Schade eigentlich. Es war 
wieder "zu gut um war zu sein :D". Ich werde noch ein wenig damit 
rumprobieren (wie gesagt, wen ich pwm_cnt mit anderen wert als 0 
initialisiere, geht es). Und wen es nicht so geht, nehme ich ein 2ten 
contr. die kosten ja nix, die 3,50 für´n Atmega16 sind ein Witz. Und 
trotz dem, bin ich der meinung, dass wen so ein artikel veröfentlicht 
wird, dann müste auch, das was da drin steht funktionieren, denke ich.

MFG

von OliverSo (Gast)


Lesenswert?

>Und trotz dem, bin ich der meinung, dass wen so ein artikel veröfentlicht
>wird, dann müste auch, das was da drin steht funktionieren, denke ich.

Denke ich auch. Tut es aber nicht. So what.

>(wie gesagt, wen ich pwm_cnt mit anderen wert als 0
>initialisiere, geht es).
Nun ja, irgendwie zappeln die Ausgänge, aber ob das das gewollte Ergenis 
ist, bezweifele ich mal.

Ganz aufgeben würde ich das Thema aber nicht - die Fehler im Programm 
lassen sich sicherlich beheben.

Oliver

von Falk B. (falk)


Lesenswert?

@  Rudolf Bremer (Gast)

>Hat jemand im forum schon mall das erwehnte Program benutzt oder, weiss
>jemand wer das geschrieben hat? Das Program ist zwar etwas gross aber,

Ich kenn den Autor. Seh ich jede Tag im Spiegel ;-)

>jetzt gesehen habe und, es wäre traurig wenn es nicht geht. Hat denn

Nun, ich hab sie nur im AVR-Studio getestet, kann aber nciht wirklich 
nachvollziehen, warum die nicht gehen soll. Was aber möglicherweise das 
problem ist, dass pwm_cnt == pwm_cnt_max nie rreicht wird, wenn nach dem 
Programmstart bei de Variablen auf Null stehen. Also pwm_cnt_max einfach 
mal auf 1 setzen.

@ Oliver (Gast)

>So, wie das das steht, ist das Programm noch nie gelaufen.

Nur im AVR-Studio Simulator.

>while-Problem, das daran liegt, daß pwm_cnt_max zu Beginn nirgends
>initialisiert wird, und deshalb das erste

Doch sind globale/statische Variablen. Aber man sollte pwm_cnt_max 
tatsächlich anders initialisieren ;-)

>die Hose. pwm_cnt_max wird zunächst auf 8 gesetzt, wenn dann pwn_cnt bis

Wo?

>8 hochgezählt hat, wird pwm_cnt_max auf 7 gesetzt (warum auch immer) -
>ohne pwm_cnt zurückzusetzen, das immer noch auf 8 steht, und welches im
>folgenden fröhlich weiter erhöht wird, weil pwm_cnt == pwm_cnt_max erst
>wieder wahr wird, wenn der Zähler einmal "rum" ist.

Nein. pwm_cnt_max wird nur von pwm_update() geschrieben.

>brauchbar sein, aber was da steht, ist Schrott. Da musst du noch einiges

Nanana, das hab ich mal jetzt nicht gehört.

>an Gehirnschmalz reinstecken, und direkt selbsterklärend ist der Code
>auch nicht.

Soll er auch gar nicht bzw. ist wohl ein wenig viel verlangt.

MfG
Falk

von Falk B. (falk)


Lesenswert?

Soo, es ist wie vorhergesagt. Einfach in der Definition von pwm_cnt_max 
eine =1 eintragen und schon läuft alles bestens.

1
volatile uint8_t pwm_cnt_max=1;                    // Zählergrenze

Volatile muss nicht sein, erscheint mir aber sicherer.

MFG
Falk

von Oliver (Gast)


Lesenswert?

>So, wie das das steht, ist das Programm noch nie gelaufen.

>Nur im AVR-Studio Simulator.

>Einfach in der Definition von pwm_cnt_max
>eine =1 eintragen und schon läuft alles bestens.

:-)

Oliver

von Oliver (Gast)


Lesenswert?

>pwm_cnt_max wird nur von pwm_update() geschrieben.

Richtig.
1
memcpy(pwm_setting, t1, 8);
2
pwm_update();

setzt pwm_cnt_max am Ende von pwm_update()auf 8. Soweit, sogut.
Das darauffolgende
1
memcpy(pwm_setting, t2, 8);
2
pwm_update();
setzt pwm_cnt_max am Ende von pwm_update()auf 7, wahrscheinlich, weil in 
t2  ein Wert 0 ist. Da pwm_cnt aber noch auf 8 steht, zählt das jetzt 
einmal rum. Das mag zwar in Echtzeit gar nicht auffallen, ist aber so.
Über
1
OCR1A += pwm_timing[pwm_cnt];
 wird OCR1A x-mal mit Speicherschrott geladen (der allerdings die meiste 
Zeit aus Nullen besteht), und die ISR entsprechend erratisch aufgerufen.

Ist pwm_cnt einmal rum, gehts wieder richtig weiter.

Ok, das mit dem (Voll-)Schrott nehme ich zurück, aber das solltest du 
dir nochmals ansehen.

Oliver

von Falk B. (falk)


Lesenswert?

@ Oliver (Gast)

>Ok, das mit dem (Voll-)Schrott nehme ich zurück, aber das solltest du
>dir nochmals ansehen.

Akzeptiert. Danke für die Kritik und Fehlersuche.

MfG
Falk

von Falk B. (falk)


Lesenswert?

OK, der Quelltext im Artikel Soft-PWM ist korrigiert. Er läuft jetzt 
sauber an und schaltet auch sauber zwischen verschiedenen Einstellungen 
um.
In realer Hardware getestet.

MFG
Falk

von Oliver (Gast)


Lesenswert?

Einen hab ich trotzdem noch (sorry)

In
1
uint8_t i, j, k, min;
2
....
3
 while(min>0)
4
    {
5
        while ( ((pwm_setting_tmp[i]==pwm_setting_tmp[i+1]) || (pwm_setting_tmp[i]==0))  && (k>1) )
6
        {
7
            // aufeinanderfolgende Werte sind gleich und können vereinigt werden
8
            // oder PWM Setting ist Null
9
            if (pwm_setting_tmp[i]!=0) pwm_mask_tmp[i+1] &= pwm_mask_tmp[i];        // Masken vereinigen
10
            k--;
11
            min--;
12
            // Datensatz entfernen
13
            for(j=i; j<k; j++)
14
            {
15
                pwm_setting_tmp[j] = pwm_setting_tmp[j+1];
16
                pwm_mask_tmp[j] = pwm_mask_tmp[j+1];
17
            }
18
            pwm_setting_tmp[j] = 0;
19
        }
20
        i++;
21
        min--;
22
    }

geht irgendwas schief.

min wird in der inneren while-Schleife mehrfach dekrementiert, und da es 
ein unsigned ist, geht es da leider durch Null nach 255. Das fällt 
wieder beim zweiten Datensatz auf.

Ich habs zwar nicht 100% verstanden, aber vermutlich ist
1
// Datensatz entfernen
2
for(j=i; j<k; j++)
falsch, und müsste
1
// Datensatz entfernen
2
for(j=i; j<=k; j++)
heissen.

Mit der Änderung läuft es auch in vmlab ohne Fehler.

Oliver

von Falk B. (falk)


Lesenswert?

@ Oliver (Gast)

>min wird in der inneren while-Schleife mehrfach dekrementiert, und da es

Das soll auch so sein. Mit welchem Testdatensatz hast du das 
festgestellt?

>Ich habs zwar nicht 100% verstanden, aber vermutlich ist

>// Datensatz entfernen
>for(j=i; j<k; j++)

>falsch, und müsste

Nein, das ist OK. k startet mit 8. Wenn der erste Datensatz entfernt 
wird, werden die Indizes 2..8 auf 1..7 kopiert, also j<k. Danach wird 
Index 8 gelöscht. min sollte immer genau acht mal decrementiert werden. 
Hast du den Code komplett neu übernommen? Das musst du, da ich einiges 
geändert habe.

MfG
Falk

von Oliver (Gast)


Lesenswert?

Im zweiten Datensatz. Der Wert 0 steht nach der Sortierung ganz vorne.

k = 8 ist der Startwert.
1
while ( ((pwm_setting_tmp[i]==pwm_setting_tmp[i+1]) || (pwm_setting_tmp[i]==0))  && (k>1) ) // ist erfüllt, da pwm_setting_tmp == 0
2
{
3
   if (pwm_setting_tmp[i]!=0) pwm_mask_tmp[i+1] &= pwm_mask_tmp[i]; 
4
5
     k--; // k=7
6
     min--;
7
     for(j=i; j<k; j++) // k=7, die Schleife läuft bis 6, j ist danach 7. Das ist ein Durchlauf zu wenig 
8
     {
9
        pwm_setting_tmp[j] = pwm_setting_tmp[j+1];
10
        pwm_mask_tmp[j] = pwm_mask_tmp[j+1];
11
      }
12
      pwm_setting_tmp[j] = 0; // hier wird jetzt pwm_setting_tmp[7] auf 0 gesetzt, es müsste aber pwm_setting_tmp[8] sein.

Der Underrun von min passiert dann später, wenn i = 7 ist, und 
pwm_setting_tmp[7]==0 wahr ist. Dann läuft die while-Schleife so lange 
durch, bis k=0 ist, und dekrementiert dabei min ein paar mal zu oft.

Oliver

von OliverSo (Gast)


Lesenswert?

Vorschlag (aber noch ungetestet):
1
 while(min>0)
2
    {
3
...
4
    }

etwas umgeschrieben:
1
  uint8_t i,j, pwm_cnt_max_tmp;
2
  
3
  // Gleiche PWM-Werte vereinigen, ebenso den PWM-Wert 0 löschen falls vorhanden
4
5
  i = 1;          // Startindex
6
  pwm_cnt_max_tmp = 8;  // maximal 8 aktive Datensätze
7
8
  while (i<=pwm_cnt_max_tmp)
9
  {
10
  
11
    // wenn aufeinanderfolgende Werte gleich sind, können diese vereinigt werden
12
    // leere Datensätze mit PWM Setting == Null können gelöscht werden
13
    // 
14
    if (pwm_setting_tmp[i] == pwm_setting_tmp[i+1] || pwm_setting_tmp[i] == 0)
15
    {
16
      // Masken vereinigen
17
      if (pwm_setting_tmp[i]!=0)
18
        pwm_mask_tmp[i+1] &= pwm_mask_tmp[i]; 
19
20
      // zum löschen des doppelten oder leeren Datensatzes 
21
      // alle folgenden Datensätze um eine Position nach vorne schieben
22
      for (j=i;j<pwm_cnt_max_tmp;j++) 
23
      {
24
        pwm_setting_tmp[j] = pwm_setting_tmp[j+1];
25
        pwm_mask_tmp[j] = pwm_mask_tmp[j+1];
26
      }
27
      
28
      // letzten, jetzt überzähligen Datensatz auf 0 setzten
29
      pwm_setting_tmp[pwm_cnt_max_tmp] = 0;    
30
      
31
      // Anzahl der aktiven Datensätze um eins verringern
32
      --pwm_cnt_max_tmp;            
33
    }
34
    i++; 
35
  }

und weiter unten
1
pwm_cnt_max = k;
ersetzen durch
1
pwm_cnt_max = pwm_cnt_max_tmp;

Oliver

von OliverSo (Gast)


Lesenswert?

Murks :-)

mein Vorschlag funkioniert nicht für mehr als zweifach vorhandene Werte.

Oliver

von Oliver (Gast)


Lesenswert?

Also, jetzt der getestete Vorschlag:

An Stelle von
1
// Gleiche PWM-Werte vereinigen, ebenso den PWM-Wert 0 löschen falls vorhanden
2
 
3
    k=8;            // maximal 8 Datensätze
4
    min=8;          // genau 8 Operationen, Löschen oder behalten
5
    i=1;            // Startindex
6
7
8
 while(min>0)
9
    {
10
...
11
    }
12
   // Zeitdifferenzen berechnen
13
 
14
    i=k;
15
...
16
    pwm_cnt_max = k;
17
    sei();
18
}

die etwas umgeschriebene Variante:
1
// Gleiche PWM-Werte vereinigen, ebenso den PWM-Wert 0 löschen falls vorhanden
2
3
  i = 1;          // Startindex
4
  pwm_cnt_max_tmp = 8;  // maximal 8 aktive Datensätze
5
6
  while (i<=pwm_cnt_max_tmp)
7
  {
8
  
9
    // wenn aufeinanderfolgende Werte gleich sind, können diese vereinigt werden
10
    // leere Datensätze mit PWM Setting == Null können gelöscht werden
11
    //
12
    while ((pwm_setting_tmp[i] == pwm_setting_tmp[i+1] || pwm_setting_tmp[i] == 0) && (pwm_cnt_max_tmp >i))
13
    {
14
      // Masken vereinigen
15
      if (pwm_setting_tmp[i]!=0)
16
        pwm_mask_tmp[i+1] &= pwm_mask_tmp[i];
17
18
      // zum löschen des doppelten oder leeren Datensatzes
19
      // alle folgenden Datensätze um eine Position nach vorne schieben
20
      for (j=i;j<pwm_cnt_max_tmp;j++)
21
      {
22
        pwm_setting_tmp[j] = pwm_setting_tmp[j+1];
23
        pwm_mask_tmp[j] = pwm_mask_tmp[j+1];
24
      }
25
      
26
      // letzten, jetzt überzähligen Datensatz auf 0 setzten
27
      pwm_setting_tmp[pwm_cnt_max_tmp] = 0;    
28
      
29
      // Anzahl der aktiven Datensätze um eins verringern
30
      --pwm_cnt_max_tmp;            
31
    }
32
    i++;
33
  }
34
35
    // Zeitdifferenzen berechnen
36
37
    i=pwm_cnt_max_tmp;
38
...
39
40
    pwm_cnt_max =pwm_cnt_max_tmp;
41
    sei();
42
}

von Falk B. (falk)


Lesenswert?

Ok, der Code ist debuggt und überarbeitet. Jetzt sollte es mit allen 
möglichen Datensätzen laufen.

MfG
Falk

von Falk B. (falk)


Lesenswert?

@  Oliver (Gast)

Deine Version sieht gut aus. Aber was macht sie wenn alle PWM Werte Null 
sind?
Dann wird der letzte Datensatz nicht gelöscht, weil (pwm_cnt_max_tmp >i) 
nicht erfüllt ist.

MFG
Falk

von Oliver (Gast)


Lesenswert?

pwm_cnt_max_tmp >i wird immer erfüllt :-)

Wenn alle Werte 0 sind, bleibt i auf 1, und pwm_cnt_max_tmp zählt runter 
bis 1. Dann endet die while-Schleife.

Dein alter Code
1
    i=pwm_cnt_max_tmp;
2
    pwm_timing_tmp[i]=(uint16_t)T_PWM*(PWM_STEPS-pwm_setting_tmp[i]);
3
    j=pwm_setting_tmp[i];
4
    i--;
5
...
6
   if (pwm_setting_tmp[0]==0) pwm_timing_tmp[0]=(uint16_t)T_PWM;   // Sonderfall, wenn alle Kanäle 0 sind

ist damit wunderbar klar gekommen. Den neuen hab ich nicht probiert.


>#define PWM_PRESCALER 8                  // Vorteiler für den Timer
:-)
Mit der alten Version (mit Precaler =1) gab es ein paar nette Effekte, 
aber so gehts besser.


Eine letzte Kleinigkeit gibts noch: 255 als Wert funktioniert nicht. Da 
kommt die Sortierroutine durcheinander.

Oliver

von Falk B. (falk)


Lesenswert?

@ Oliver (Gast)

>Eine letzte Kleinigkeit gibts noch: 255 als Wert funktioniert nicht. Da
>kommt die Sortierroutine durcheinander.

Kann ich nicht feststellen? Mit welchem Datensatz?

MfG
Falk

von Oliver (Gast)


Lesenswert?

Du stellst Fragen...

Ok, 255 funktioniert prinzipiell, nur an erster Stelle nicht.
1
const uint8_t t1[8]={255, 40, 3, 17, 128, 99, 5, 9};
Ändert man in folgendem Ausschnitt min in ein uint16_t, und setzt das 
auf 256, dann funktioniert alles.
1
        min=255;
2
        for(j=i; j<=PWM_CHANNELS; j++) {
3
            if (pwm_setting_tmp[j]<min)

Aber es ist mit jetzt zu spät, rauszufinden, warum es weiter hinten im 
Datensatz auch ohne die Änderung geht.

Oliver

von Falk B. (falk)


Lesenswert?

@ Oliver (Gast)
Eieiei, das ist wirklich kniffelig. Naja, aber jetzt sollte es OK 
sein. Ich hab auch deine Idee mit der etwas verbesserten Aufräumroutine 
eingebaut. Spart eine lokale Variable uund ist übersichtlicher.

MfG
Falk

von Oliver (Gast)


Angehängte Dateien:

Lesenswert?

Jetzt wackeln alle Ausgänge wie gewünscht :-)

Oliver

von Rudolf Bremer (Gast)


Lesenswert?

Wow, na, hier ist aber wat los :) Ja, hmmmm, ich bin sprachloss... Ich 
freue mich auf jeden fall, dass es jetzt funktioniert.

MFG

von Christian (Gast)


Lesenswert?

Ich bekomm den intelligenten Ansatz einfach nicht zum Laufen. Alles, was 
in VMLAB passiert, ist dass die Pegel alle auf Null gehen, da bleiben 
sie dann aber auch die restliche Zeit. VMLAB gibt folgende Warnungen 
aus, ich glaub
1
[PC = $0316, Time =    0.37 ms, {MEM}]: Indexed write to a I/O space register? Address = $0037 (PWM_DDR = 0xFF;)
2
[PC = $031A, Time =    0.37 ms, {MEM}]: Indexed write to a I/O space register? Address = $004E (TCCR1B = 2;)
3
[PC = $02AD, Time =   65.92 ms, {MEM}]: Indexed read from a I/O space register? Address = $004A (OCR1A += isr_ptr_time[pwm_cnt];)

Hier die Projektdatei dazu:
1
; Micro + software running
2
; ------------------------------------------------------------
3
.MICRO "ATmega32"
4
.TOOLCHAIN "GCC"
5
.GCCPATH   "C:\WinAVR-20081205"
6
.GCCMAKE   AUTO
7
.TARGET    "softpwm.hex"
8
.SOURCE    "softpwm.c"
9
10
.TRACE              ; Activate micro trace
11
12
; Following lines are optional; if not included
13
; exactly these values are taken by default
14
; ------------------------------------------------------------
15
.POWER VDD=5 VSS=0  ; Power nodes
16
.CLOCK 8meg         ; Micro clock
17
.STORE 250m         ; Trace (micro+signals) storage time
18
19
; Micro nodes: RESET, AREF, PA0-PA7, PB0-PB7, PC0-PC7, PD0-PD7, ACO, TIM1OVF
20
; Define here the hardware around the micro
21
; ------------------------------------------------------------
22
R1  PB0 VSS 50K
23
24
.plot v(PB0) v(PB1) v(PB2) v(PB3)

Vielleicht steh ich ja grad gewaltig aufm Schlauch, aber bisher hat 
eigentlich immer alles geklappt, was ich getestet hab.

In AVR Studio will das Programm auch nicht so recht, es ist sau langsam, 
aber immerhin gehen die Ports an und aus. Auf einem echten uC passiert 
dagegen gar nichts :-(

Christian

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.