Forum: Mikrocontroller und Digitale Elektronik PWM mit Interrupts steuern


von Marius D. (lonestarr102)


Angehängte Dateien:

Lesenswert?

Ich fahre über einen Ausgang eine viertel Sinuswelle als PWM-Signal. 
Dieses Signal wird extern über ein RC-Gleid geschoben, welches mir eine 
schöne sinusförmige Rampe auf meinem Oszilloskop anzeigt.

Ich möchte nun wenn der Sinus oben beim Maximalwert angekommen ist eine 
pause machen. Das heißt das Signal soll auf maximal stehen bleiben. Dies 
funktioniert leider nicht. Sobald  mein Delay startet ist der Ausgang 
auf 0 und die Pause wird gemacht befor die neue Viertel Welle abgefahren 
wird. Also die Pause wird in einem falschen Zustand gemacht. Ich komme 
an diesem Punkt momentan nicht weiter. Vielleicht kann mir ja jemand 
helfen.

Des Weiteren habe ich noch das Problem, dass in dem viertel Sinus 
ungefähr in der Mitte ein unerklärlicher Pick nach unten ist.

Code im Anhang.

benutze ein Attiny44A

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Schreib das
1
            if (i > (0xFF-divider))

mal als
1
            if (i > (255-divider))

(noch besser wäre
1
            if (i > (sizeof(sinewave) - divider))
)


Ich bin mir da auch immer ein bischen unsicher, welchen Datentyp das 
Literal 0xFF eigentlich hat. Da können scheinbar seltsame Dinge 
passieren, wenn der nach int promoted wird. Die genauen Regeln sind da 
nicht unbedingt trivial.
Alledings gibt es an dieser Stelle überhaupt keinen Grund, da jetzt eine 
Hex-Zahl zu benutzen.

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

Marius D. schrieb:

> Ich fahre über einen Ausgang eine viertel Sinuswelle als PWM-Signal.
> Dieses Signal wird extern über ein RC-Gleid geschoben, welches mir eine
> schöne sinusförmige Rampe auf meinem Oszilloskop anzeigt.

Schön. Nur was soll der Sinn einer sinusförmigen Rampe sein?

> Ich möchte nun wenn der Sinus oben beim Maximalwert angekommen ist eine
> pause machen. Das heißt das Signal soll auf maximal stehen bleiben.

Das ist dann schlicht ein übersteuerter Sinus. Kann man ganz leicht 
erreichen, indem man den Wert aus der Sinustabelle mit einem hinreichend 
großen Fakter mittels "sättigender" Arithmetik multipliziert.

Oder anders ausgedrückt: Man schaltet der Multiplikation eine simple 
binäre IF-Verzweigung nach, ungefähr sowas:

if (value > limit) value = limit;

Es ergibt sich genau das, was du willst, ein Sinus mit gekappten 
Spitzen.

> Des Weiteren habe ich noch das Problem, dass in dem viertel Sinus
> ungefähr in der Mitte ein unerklärlicher Pick nach unten ist.

Dann ist der Code oder die Sinustabelle Rotz.

von Thomas E. (thomase)


Lesenswert?

Marius D. schrieb:
> Sobald  mein Delay startet ist der Ausgang
> auf 0 und die Pause wird gemacht befor die neue Viertel Welle abgefahren
> wird.

Erst geht der Ausgang auf 0, dann kommt das Delay.

> TCCR1A |= (1 << COM1B1) | (1 << COM1B0) | (1 << WGM10);

Deine PWM läuft falsch rum.

TCCR1A |= (1 << COM1B1) /*| (1 << COM1B0)*/ | (1 << WGM10);

mfg.

von M. K. (sylaina)


Lesenswert?

c-hater schrieb:
> Es ergibt sich genau das, was du willst, ein Sinus mit gekappten
> Spitzen.

Wie kommst du drauf, dass er eine gekappte Spitze will? Er schrieb er 
will warten bis der Sinus sein Maximum erreicht hat und dann eine 
gewisse Zeit auf dem Maximum bleiben. Das ist was anderes als eine 
gekappte Spitze.

Ich persönlich würde hier auch nicht mit einem delay() arbeiten sondern 
warten bis der Sinus seinen Maximalwert hat und dann einen Timer 
entsprechend anschubsen.

von Marius D. (lonestarr102)


Angehängte Dateien:

Lesenswert?

Karl Heinz schrieb
> noch besser wäre
> if (i > (sizeof(sinewave) - divider))

Hab ich gerade mal mit Dezimalzahl probiert, leider bleibt das Problem 
bestehen.

Thomas E. schrieb:
> Deine PWM läuft falsch rum.

Danke für den Hinweis. Aber das hat seine Richtigkeit. Drehe das Signal 
auserhalb des µC.


Michael Köhler schrieb:
> Ich persönlich würde hier auch nicht mit einem delay() arbeiten sondern
> warten bis der Sinus seinen Maximalwert hat und dann einen Timer
> entsprechend anschubsen.

Wäre eine schönere Variante aber zum Testen müsste es so doch auch 
funktionieren.


c-hater schrieb:
> Dann ist der Code oder die Sinustabelle Rotz.
Das hat mir sehr viel geholfen. Vielen dank.


Habe den Code zum testen mal stark vereinfacht.
Im Main habe ich eine dauerschleife und das setzen der Register wie im 
alten code.  (Siehe professionelle Paint Skizze.)


Hier nur die Interrupt Routine.
•
1
// i und sinewave sind jetzt globale Variablen.
2
ISR ( TIM1_OVF_vect ) {
3
    OCR1BL = sinewave[i];
4
    if ( i <= (254))
5
    i++;
6
}

Meine Sinuskurve fährt nach oben. Nun sollte sie einfach Oben stehen 
bleiben. Sie fällt dann sobald die Spitze erreicht ist wieder auf einen 
Wert der ungfair 1/8 von U_max ist zurück.

von Marius D. (lonestarr102)


Angehängte Dateien:

Lesenswert?

Hab jetzt ein Ergebniss mit dem ich fast zu frieden bin.
Leider habe ich noch diesen doofen Zacken im Verlauf. Hat wer evtl. 
Vermutungen wo dieser herkommen könnte? Siehe Bild

Habe den Code wie folgt verändert.
1
  while(1)
2
  {
3
  
4
    if ( pwm_flag == 1 )
5
    {
6
      //TIMSK1 &= ~( 1 << TOIE1 ); 
7
      OCR1B = 255;
8
      _delay_ms (4);
9
      OCR1B = 0;
10
      _delay_ms (4);
11
      i = 0;
12
      pwm_flag = 0;
13
      //TIMSK1 |= ( 1 << TOIE1 ); 
14
    }
15
  }
16
}
17
ISR ( TIM1_OVF_vect ) {
18
    //if ( pwm_flag = 
19
    if ( i <= (254))
20
    {
21
      i++;
22
      OCR1B = sinewave[i];}
23
    else
24
      pwm_flag = 1;
25
}

von M. K. (sylaina)


Lesenswert?

Ich habe mir deinen Code mal angeschaut. Einige Anmerkungen:

Hexcode: Macht heute praktisch nur mehr arbeit mit Hexcode zu arbeiten, 
programmtechnisch hast du keinen Vorteil wenn du 0xCE schreibst, wenn du 
aber später mal im Code liest musst du erst mal wieder umrechnen. Nicht 
sehr sinnvoll.

TCCR1C sowie OCR1BL werden eh beide mit 0 initialisiert, es macht also 
keinen Sinn diesen Werten extra noch mal 0 zuzuweisen.

Du schreibst DDRB = 0xF. Frage: Welche Pins des Ports B sind nun als 
Ausgang geschaltet? Thema Hexcode ;). Schreib doch gleich DDRB |= (1 << 
PB0)|(1 << PB1)|(1 << PB2)|(1 << PB3). Ist zwar mehr Schreibarbeit aber 
wenn du später mal wieder in den Code schaust siehst du auf den ersten 
Blick, welche Ports du als Ausgang definiert hast.

Dein Code schaut eigentlich nicht falsch aus. Hast du dir die PWM auch 
schon direkt am Ausgangspin angeschaut? Du hast ja nur am Ausgang des TP 
geschaut.

von Marius D. (lonestarr102)


Lesenswert?

@Michael Köhler
danke für deine Hilfe

Habe mir das PWM signal mal genau angeschaut.
Das läuft ordnungsgemäß auf den Maximalwert zu. Wenn es oben angekommt, 
springt das Signal hin und her. Nun wird die Pause gemacht. Dann wird 
die nächste viertel Welle geschrieben.

*Edit Habe noch mal meine Lool-Up-Table überprüft. Dort scheint auch 
alles in Ordnung zu sein.

*Edit2

Der Zacken verschwindet wenn ich nicht bis 255 sonder nur bis 245 laufe.

Könnte das ein Bug in der Hardware sein ?
1
}
2
ISR ( TIM1_OVF_vect ) {
3
    if ( i <= (245-divider))
4
    {
5
      OCR1B = sinewave[i];
6
      i++;
7
    }
8
    else
9
      pwm_flag = 1; 
10
}

: Bearbeitet durch User
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.