mikrocontroller.net

Forum: Compiler & IDEs AVR GCC Software PWM


Autor: Rudolf Bremer (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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#I... 
. 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
  // auf Sync warten
 
      pwm_sync=0;             // Sync wird im Interrupt gesetzt
  ->  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

Autor: OliverSo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Rudolf Bremer (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Rudolf Bremer (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rudolf Bremer (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Jojo S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
fehlt in der pwm_snyc deklaration das Zauberwort 'volatile' ?

Autor: Jojo S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok, im angegebenen Link steht es auf jedenfall drin.

Autor: Rudolf Bremer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nein, ist dabei. Hmmmm...

Autor: Rudolf Bremer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich poste mal den code hier, könte sein das sich da ein fehler 
eingeschlichen hat:
// Defines an den Controller und die Anwendung anpassen
  
 //#define F_CPU 8000000L                  // Systemtakt in Hz
#define F_PWM 100L                      // PWM-Frequenz in Hz
#define PWM_STEPS 256                   // PWM-Schritte pro Zyklus(1..256)
#define PWM_PORT PORTB                 // Port für PWM
#define PWM_DDR DDRB                    // Datenrichtungsregister für PWM
 
// ab hier nichts ändern, wird alles berechnet
 
#define T_PWM (F_CPU/(F_PWM*PWM_STEPS)) // Systemtakte pro PWM-Takt
//#define T_PWM 1   // für TESTs
 
#if (T_PWM<(93+5))
    #error T_PWM zu klein, F_CPU muss vergrösst werden oder F_PWM oder PWM_STEPS verkleinert werden
#endif
 
// includes

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

// globale Variablen
 
uint8_t pwm_setting[8];                 // Einstellungen für die einzelnen PWM-Kanäle
uint16_t pwm_timing[9];                 // Zeitdifferenzen der PWM Werte
uint8_t pwm_mask[9];                    // Bitmaske für PWM Bits, welche gelöscht werden sollen
uint8_t pwm_cnt_max;                    // Zählergrenze
volatile uint8_t pwm_sync;              // Update jetzt möglich
 
// PWM Update, berechnet aus den PWM Einstellungen
// die neuen Werte für die Interruptroutine
 
void pwm_update(void) {
    uint8_t i, j, tmp, min;
    uint8_t index=0;
 
    uint8_t pwm_setting_tmp[8];     // Einstellungen der PWM Werte, sortiert
    uint16_t pwm_timing_tmp[9];     // Zeitdifferenzen der PWM Werte
    uint8_t pwm_mask_tmp[9];        // Bitmaske für PWM Bits, welche gelöscht werden sollen
 
    // PWM setting (0) berechnen
    // gleichzeitig die Bitmasken generieren und PWM settings kopieren
 
    tmp=0;
    j = 1;
    for(i=0; i<8; i++) {
        pwm_mask_tmp[i]=~j;                         // Maske zum Löschen der PWM Ausgänge
        pwm_setting_tmp[i] = pwm_setting[i];
        if (pwm_setting[i]!=0) tmp |= j;
        j <<= 1;
    }
    pwm_mask_tmp[8]=tmp;
    
    // PWM settings sortieren; Einfügesortieren
 
    for(i=0; i<8; i++) {
        min=255;
        for(j=i; j<8; j++) {
            if (pwm_setting_tmp[j]<min) {
                index =j;                   // Index und PWM-setting merken
                min = pwm_setting_tmp[j];
            }
        }
        if (index!=i) {
            // ermitteltes Minimum mit aktueller Sortiertstelle tauschen
            tmp = pwm_setting_tmp[index];
            pwm_setting_tmp[index] = pwm_setting_tmp[i];
            pwm_setting_tmp[i] = tmp;
            tmp = pwm_mask_tmp[index];
            pwm_mask_tmp[index] = pwm_mask_tmp[i];
            pwm_mask_tmp[i] = tmp;
        }
    }
 
    // Gleiche PWM-Werte vereinigen, ebenso den PWM-Wert 0 löschen falls vorhanden
 
    tmp=8;          // maximal 8 Datensätze
    min=8;          // genau 8 Operationen, Löschen oder behalten
    i=0;            // Startindex
 
    while(min>0) {
        while ( ((pwm_setting_tmp[i]==pwm_setting_tmp[i+1]) || (pwm_setting_tmp[i]==0))  && (tmp>1) ) {
            // aufeinanderfolgende Werte sind gleich und können vereinigt werden
            // oder PWM Setting ist Null
            if (pwm_setting_tmp[i]!=0) pwm_mask_tmp[i+1] &= pwm_mask_tmp[i];        // Masken vereinigen
            tmp--;
            min--;
            // Datensatz entfernen
            for(j=i; j<tmp; j++) {
                pwm_setting_tmp[j] = pwm_setting_tmp[j+1];
                pwm_mask_tmp[j] = pwm_mask_tmp[j+1];
            }
            pwm_setting_tmp[j] = 0;
        }
        i++;
        min--;
    }
 
    // Zeitdifferenzen berechnen
 
    j=pwm_setting_tmp[0];
    for (i=0; i<(tmp-1); i++) {
        pwm_timing_tmp[i]=(uint16_t)T_PWM*(pwm_setting_tmp[i+1]-j);
        j=pwm_setting_tmp[i+1];
    }
    pwm_timing_tmp[i]=(uint16_t)T_PWM*(256-j);
    i++;
    pwm_timing_tmp[i]=(uint16_t)T_PWM*pwm_setting_tmp[0];
    pwm_mask_tmp[i]=pwm_mask_tmp[8];
 
    if (pwm_setting_tmp[0]==0) pwm_timing_tmp[i]=(uint16_t)T_PWM;   // Sonderfall, wenn alle Kanäle 0 sind
 
    // auf Sync warten
 
    pwm_sync=0;             // Sync wird im Interrupt gesetzt
    while(pwm_sync==0);
 
    // neue PWM-Daten kopieren mit maximaler Geschwindigkeit
    cli();
    pwm_timing[8]=pwm_timing_tmp[8];
    pwm_mask[8]=pwm_mask_tmp[8];
    pwm_timing[7]=pwm_timing_tmp[7];
    pwm_mask[7]=pwm_mask_tmp[7];
    pwm_timing[6]=pwm_timing_tmp[6];
    pwm_mask[6]=pwm_mask_tmp[6];
    pwm_timing[5]=pwm_timing_tmp[5];
    pwm_mask[5]=pwm_mask_tmp[5];
    pwm_timing[4]=pwm_timing_tmp[4];
    pwm_mask[4]=pwm_mask_tmp[4];
    pwm_timing[3]=pwm_timing_tmp[3];
    pwm_mask[3]=pwm_mask_tmp[3];
    pwm_timing[2]=pwm_timing_tmp[2];
    pwm_mask[2]=pwm_mask_tmp[2];
    pwm_timing[1]=pwm_timing_tmp[1];
    pwm_mask[1]=pwm_mask_tmp[1];
    pwm_timing[0]=pwm_timing_tmp[0];
    pwm_mask[0]=pwm_mask_tmp[0];
    pwm_cnt_max = tmp;
    sei();
}
 
// Timer 1 Output COMPARE A Interrupt
 
ISR(TIMER1_COMPA_vect) {
    static uint8_t pwm_cnt=0;
    uint8_t tmp;
 
    OCR1A += pwm_timing[pwm_cnt];
    tmp    = pwm_mask[pwm_cnt];
    
    if (pwm_cnt == pwm_cnt_max) {
        PWM_PORT = tmp;                         // Ports setzen zu Begin der PWM
        pwm_cnt  = 0;
    }
    else {
        PWM_PORT &= tmp;                        // Ports löschen
        pwm_cnt++;
        if (pwm_cnt == pwm_cnt_max) pwm_sync = 1;   // Update jetzt möglich
    }
    
}
 
int main(void) {
 
    // PWM einstellen
    
    PWM_DDR = 0xFF;         // Port als Ausgang
    
    // Timer 1 OCRA1, als variablem Timer nutzen
 
    TCCR1B = 1;             // Timer läuft mit vollem Systemtakt
    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]={27, 40, 31, 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]={65, 75, 20, 255, 128, 80, 90, 20};
const uint8_t t5[8]={0, 56, 78, 23, 45, 80, 50, 9};
const uint8_t t6[8]={33, 33, 33, 33, 33, 33, 33, 33};
 
// Messung der Interruptdauer
    tmp =0;
    tmp =0;
    tmp =0;
 
// 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();
/******************************************************************/
 
    return 0;
}

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rudolf Bremer (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Rudolf Bremer (Gast)
Datum:

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: OliverSo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Rudolf Bremer (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rudolf Bremer (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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(); )
  sei();
    pwm_sync=0;             // Sync wird im Interrupt gesetzt
   --<- while(pwm_sync==0);
   |
   |    // neue PWM-Daten kopieren mit maximaler Geschwindigkeit
   |->   cli();
        pwm_timing[8]=pwm_timing_tmp[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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rudolf Bremer (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
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

Autor: Rudolf Bremer (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: OliverSo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Falk Brunner (falk)
Datum:

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

volatile uint8_t pwm_cnt_max=1;                    // Zählergrenze

Volatile muss nicht sein, erscheint mir aber sicherer.

MFG
Falk

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>pwm_cnt_max wird nur von pwm_update() geschrieben.

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

setzt pwm_cnt_max am Ende von pwm_update()auf 8. Soweit, sogut.
Das darauffolgende
memcpy(pwm_setting, t2, 8);
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
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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Einen hab ich trotzdem noch (sorry)

In
uint8_t i, j, k, min;
....
 while(min>0)
    {
        while ( ((pwm_setting_tmp[i]==pwm_setting_tmp[i+1]) || (pwm_setting_tmp[i]==0))  && (k>1) )
        {
            // aufeinanderfolgende Werte sind gleich und können vereinigt werden
            // oder PWM Setting ist Null
            if (pwm_setting_tmp[i]!=0) pwm_mask_tmp[i+1] &= pwm_mask_tmp[i];        // Masken vereinigen
            k--;
            min--;
            // Datensatz entfernen
            for(j=i; j<k; j++)
            {
                pwm_setting_tmp[j] = pwm_setting_tmp[j+1];
                pwm_mask_tmp[j] = pwm_mask_tmp[j+1];
            }
            pwm_setting_tmp[j] = 0;
        }
        i++;
        min--;
    }

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
// Datensatz entfernen
for(j=i; j<k; j++)
falsch, und müsste
// Datensatz entfernen
for(j=i; j<=k; j++)
heissen.

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

Oliver

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Oliver (Gast)
Datum:

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

k = 8 ist der Startwert.
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
{
   if (pwm_setting_tmp[i]!=0) pwm_mask_tmp[i+1] &= pwm_mask_tmp[i]; 

     k--; // k=7
     min--;
     for(j=i; j<k; j++) // k=7, die Schleife läuft bis 6, j ist danach 7. Das ist ein Durchlauf zu wenig 
     {
        pwm_setting_tmp[j] = pwm_setting_tmp[j+1];
        pwm_mask_tmp[j] = pwm_mask_tmp[j+1];
      }
      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

Autor: OliverSo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vorschlag (aber noch ungetestet):
 while(min>0)
    {
...
    }

etwas umgeschrieben:
  uint8_t i,j, pwm_cnt_max_tmp;
  
  // Gleiche PWM-Werte vereinigen, ebenso den PWM-Wert 0 löschen falls vorhanden

  i = 1;          // Startindex
  pwm_cnt_max_tmp = 8;  // maximal 8 aktive Datensätze

  while (i<=pwm_cnt_max_tmp)
  {
  
    // wenn aufeinanderfolgende Werte gleich sind, können diese vereinigt werden
    // leere Datensätze mit PWM Setting == Null können gelöscht werden
    // 
    if (pwm_setting_tmp[i] == pwm_setting_tmp[i+1] || pwm_setting_tmp[i] == 0)
    {
      // Masken vereinigen
      if (pwm_setting_tmp[i]!=0)
        pwm_mask_tmp[i+1] &= pwm_mask_tmp[i]; 

      // zum löschen des doppelten oder leeren Datensatzes 
      // alle folgenden Datensätze um eine Position nach vorne schieben
      for (j=i;j<pwm_cnt_max_tmp;j++) 
      {
        pwm_setting_tmp[j] = pwm_setting_tmp[j+1];
        pwm_mask_tmp[j] = pwm_mask_tmp[j+1];
      }
      
      // letzten, jetzt überzähligen Datensatz auf 0 setzten
      pwm_setting_tmp[pwm_cnt_max_tmp] = 0;    
      
      // Anzahl der aktiven Datensätze um eins verringern
      --pwm_cnt_max_tmp;            
    }
    i++; 
  }


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

Oliver

Autor: OliverSo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Murks :-)

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

Oliver

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also, jetzt der getestete Vorschlag:

An Stelle von
// Gleiche PWM-Werte vereinigen, ebenso den PWM-Wert 0 löschen falls vorhanden
 
    k=8;            // maximal 8 Datensätze
    min=8;          // genau 8 Operationen, Löschen oder behalten
    i=1;            // Startindex


 while(min>0)
    {
...
    }
   // Zeitdifferenzen berechnen
 
    i=k;
...
    pwm_cnt_max = k;
    sei();
}

die etwas umgeschriebene Variante:
// Gleiche PWM-Werte vereinigen, ebenso den PWM-Wert 0 löschen falls vorhanden

  i = 1;          // Startindex
  pwm_cnt_max_tmp = 8;  // maximal 8 aktive Datensätze

  while (i<=pwm_cnt_max_tmp)
  {
  
    // wenn aufeinanderfolgende Werte gleich sind, können diese vereinigt werden
    // leere Datensätze mit PWM Setting == Null können gelöscht werden
    //
    while ((pwm_setting_tmp[i] == pwm_setting_tmp[i+1] || pwm_setting_tmp[i] == 0) && (pwm_cnt_max_tmp >i))
    {
      // Masken vereinigen
      if (pwm_setting_tmp[i]!=0)
        pwm_mask_tmp[i+1] &= pwm_mask_tmp[i];

      // zum löschen des doppelten oder leeren Datensatzes
      // alle folgenden Datensätze um eine Position nach vorne schieben
      for (j=i;j<pwm_cnt_max_tmp;j++)
      {
        pwm_setting_tmp[j] = pwm_setting_tmp[j+1];
        pwm_mask_tmp[j] = pwm_mask_tmp[j+1];
      }
      
      // letzten, jetzt überzähligen Datensatz auf 0 setzten
      pwm_setting_tmp[pwm_cnt_max_tmp] = 0;    
      
      // Anzahl der aktiven Datensätze um eins verringern
      --pwm_cnt_max_tmp;            
    }
    i++;
  }

    // Zeitdifferenzen berechnen

    i=pwm_cnt_max_tmp;
...

    pwm_cnt_max =pwm_cnt_max_tmp;
    sei();
}


Autor: Falk Brunner (falk)
Datum:

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

MfG
Falk

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
    i=pwm_cnt_max_tmp;
    pwm_timing_tmp[i]=(uint16_t)T_PWM*(PWM_STEPS-pwm_setting_tmp[i]);
    j=pwm_setting_tmp[i];
    i--;
...
   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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du stellst Fragen...

Ok, 255 funktioniert prinzipiell, nur an erster Stelle nicht.
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.
        min=255;
        for(j=i; j<=PWM_CHANNELS; j++) {
            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

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Oliver (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Jetzt wackeln alle Ausgänge wie gewünscht :-)

Oliver

Autor: Rudolf Bremer (Gast)
Datum:

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

MFG

Autor: Christian (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
[PC = $0316, Time =    0.37 ms, {MEM}]: Indexed write to a I/O space register? Address = $0037 (PWM_DDR = 0xFF;)
[PC = $031A, Time =    0.37 ms, {MEM}]: Indexed write to a I/O space register? Address = $004E (TCCR1B = 2;)
[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:
; Micro + software running
; ------------------------------------------------------------
.MICRO "ATmega32"
.TOOLCHAIN "GCC"
.GCCPATH   "C:\WinAVR-20081205"
.GCCMAKE   AUTO
.TARGET    "softpwm.hex"
.SOURCE    "softpwm.c"

.TRACE              ; Activate micro trace

; Following lines are optional; if not included
; exactly these values are taken by default
; ------------------------------------------------------------
.POWER VDD=5 VSS=0  ; Power nodes
.CLOCK 8meg         ; Micro clock
.STORE 250m         ; Trace (micro+signals) storage time

; Micro nodes: RESET, AREF, PA0-PA7, PB0-PB7, PC0-PC7, PD0-PD7, ACO, TIM1OVF
; Define here the hardware around the micro
; ------------------------------------------------------------
R1  PB0 VSS 50K

.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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.