Forum: Mikrocontroller und Digitale Elektronik Stabilitätsproblem oder Verständnisproblem bei Timer (PWM) ATmega88


von Bernd A. (Gast)


Angehängte Dateien:

Lesenswert?

Hallo, ich verzweifle gerade.

Möchte mit dem ATmega88 einen Sinus mit 500Hz erzeugen. Das klappt 
prinzipiell auch ganz gut. (siehe Code)
Ich erzeuge den Sinus mit einer PWM, deren Werte ich in einer Tabelle 
abgelegt habe.
Das Signal hat 500Hz, besteht aus 128 Bit/Periode und die PWM hat eine 
Frequenz von 125kHz (64kHz sind nötig! (500*128)
Mein Prozessor läuft mit 8MHz, Prescaler = 0, 8-Bit Timer(Timer 0): Sind 
8MHz / 256 = 31,25kHz. Top-Wert geändert (WGM-Mode:5) auf 64: heißt 8MHz 
/ 64 = 125kHz.
Ich arbeite im PhaseCorrectMode!
An OC0B liegt mein PWM-Siganl, da mein OCR0A Register belegt ist und die 
PWM durch OCR0B geändert wird.
Das Signal durch einen Tiefpass und fertig ist der Sinus.

Jetzt brauche ich aber den Sinus einmal zusätzlich invertiert - und das 
klappt nicht mit meiner Konfiguration. (OCR0A für OC0A ist schon 
belegt.)

Da habe ich mir gedacht, ich erzeuge die PWM manuell und schalte meinen 
OC0B und OC0A korrespondierenden Pin manuell.

Der Timer macht das ja ,indem (durch die COM0A / COM0B) bei einem 
Compare mit OCR0B) der PWM Ausgang (OC0B) geändert wird.
Nun habe ich die PWM abgeschaltet (COM0B auf 0 --> Ausgang als 
I/O-Port).
Dann habe ich in der ISR bei einem OutputCompareInterupt0B meine(n) 
Ausgang(e) wie zuvor geändert, nur manuell.

Problem:
Jetzt habe ich keine stabile Ausgangsfrequenz mehr. Die Flanke springt 
immer, so, als kommt der Interrupt nicht mehr Regelmäßig. Woran liegts? 
Habt Ihr da den Durchblick?

von Di P. (drpepper) Benutzerseite


Lesenswert?

du machst rechenoperationen im array-index.

damit hatte ich schon öfters probleme. versuch mal, den neuen index 
vorher auszurechnen:
1
temp=(top-sinus[(basis-1)-j]);

wird zu
1
uint8_t bla = (basis-1)-j;
2
temp=(top-sinus[bla]);

von Hc Z. (mizch)


Lesenswert?

>    TCCR0A = (1<<WGM00);// | (1<<COM0B1);//--> Manuell, siehe ISR

Nichts mit Manuell.  Zitat Datenblatt:
1
If one or both of the COM0B1:0
2
bits are set, the OC0B output overrides the normal port functionality

von Bernd A. (Gast)


Lesenswert?

Hc Zimmerer schrieb:
>>    TCCR0A = (1<<WGM00);// | (1<<COM0B1);//--> Manuell, siehe ISR
>
> Nichts mit Manuell.  Zitat Datenblatt:If one or both of the COM0B1:0
> bits are set, the OC0B output overrides the normal port functionality

Genau!
COM0B1 ist auskommentiert somit ist der Port für normale I/O-Funktion 
konfiguriert und ich kann meine Ausgänge manuell verwalten!

von Bernd A. (Gast)


Lesenswert?

Ich habs immer noch nicht geschafft.

Hier noch mal zusammengefasst:

automatische PWM:
1
void pwm_init(void) //timer 0 init
2
{   /* PWM-Output: OC0B */
3
    /* OC0B -> ATmega88 PD5 */
4
    
5
    TCCR0A = (1<<WGM00) | (1<<COM0B1);
6
    // Phase Correct PWM-Mode (WGM)
7
    // Compare Match Output Mode: Timer 0:
8
    // Ausg. B enabled Clear on Comp. up-counting, set down-counting (COM0B)
9
10
    TCCR0B = (1<<CS00) | (1<<WGM02);
11
    // WGM02 =1 -> Phase Correct PWM aber: Max !=0xff sondern Max = OCR0A
12
    // CS - Prescaler auf 1 = Clk (CS00=1)
13
    // f_cpu = 8MHz = (Clk / Prescaler)
14
    // 1 Zyklus (0x00 - 0xff) = 8MHz/256/Prescaler = 31,25kHz/Prescaler
15
16
    OCR0A = 64; // Top 
17
    // 1 Zyklus (0x00 - 0x3F) = 8MHz/64 = 125kHz
18
}
19
20
int main (void)
21
{
22
  for(;;) // Programm Sinus
23
  {
24
  OCR0B = 32; // PWM 1:1
25
  }
26
}
Resultat: saubere PWM

manuelle PWM:
1
void pwm_init(void) //timer 0 init
2
{   /* PWM-Output: OC0B */
3
    /* OC0B -> ATmega88 PD5 */
4
    
5
    TCCR0A = (1<<WGM00);// | (1<<COM0B1); 
6
/* COM0B = 0 --> normale Portoperation)*/
7
    // Phase Correct PWM-Mode (WGM)
8
    // Compare Match Output Mode: Timer 0:
9
    // Ausg. B enabled Clear on Comp. up-counting, set down-counting (COM0B)
10
11
    TIMSK0 = (1<<OCIE0B);// Output Compare Interrupt 0B einschalten
12
/* wie bei Phase correct Mode wird der Ausgang OC0B (PD5) bei Compare getoggelt! --> siehe ISR*/
13
14
    TCCR0B = (1<<CS00) | (1<<WGM02);
15
    // WGM02 =1 -> Phase Correct PWM aber: Max !=0xff sondern Max = OCR0A
16
    // CS - Prescaler auf 1 = Clk (CS00=1)
17
    // f_cpu = 8MHz = (Clk / Prescaler)
18
    // 1 Zyklus (0x00 - 0xff) = 8MHz/256/Prescaler = 31,25kHz/Prescaler
19
20
    OCR0A = 64; // Top 
21
    // 1 Zyklus (0x00 - 0x3F) = 8MHz/64 = 125kHz
22
}
23
24
ISR(TIMER0_COMPB_vect)
25
{
26
  PORTD ^= (1<<PD5); //Ausgang toggeln
27
}
28
29
int main (void)
30
{
31
  for(;;) // Programm Sinus
32
  {
33
  OCR0B = 32; // PWM 1:1
34
  }
35
}
Resultat:
Weder die Grundfrequenz ist stabil (125kHz) noch die Umschaltzeiten am 
Port D5

Kompletter Code ist im 1. Post...

Wie schaltet ihtr den Port bei manueller PWM?

von Karl H. (kbuchegg)


Lesenswert?

Bernd A. schrieb:

> Resultat:
> Weder die Grundfrequenz ist stabil (125kHz) noch die Umschaltzeiten am
> Port D5

Vielleicht sollten wir mal darüber reden, was du unter 'nicht stabil' 
verstehst. Wie groß sind die Schwankungen?

Wenn der Timer die PWM erzeugt, kann er das taktgenau machen.
Wenn du aber auf ISR angewiesen bist, hast du immer gewisse 
Latenzzeiten, bis der aktuelle Befehl beendet ist und der µC mit der ISR 
abarbeitung anfängt. Dann kommt bei dir natürlich noch dazu, dass eine 
ISR evetuell ein wenig warten muss, weil gerade die andere ISR drann 
ist.
Mit einem geringen Jitter würde ich daher rechnen.

Daher die Frage: Über welche Fluktuationen in deinen Messwerten reden 
wir.

von Bernd A. (Gast)


Lesenswert?

Also ich trigger auf positive Flanke. Die negative hat ein Delta von 
früheste fallende bis späteste fallende von ca. 6µs
Das Delta von steigender Flanke bis früheste fallende Flanke ist ca. 
5,4µs

von Bernd A. (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Dann kommt bei dir natürlich noch dazu, dass eine
> ISR evetuell ein wenig warten muss, weil gerade die andere ISR drann
> ist.

Habe mal den Timer 2 ausgeschaltet und siehe da, der Takt meiner PWM ist 
stabil. Meine Frage: In meinem Timer2 wird nur eine globale Variable 
hochgezählt:
1
void timer2_init(void)
2
{
3
  TIMSK2 = (1<<OCIE2A); // Output Compare Interrupt Enable
4
  TCCR2A = (1<<WGM21);
5
  // WGM21 = CTC - Mode -> Clear Timer on compare (0x00 - OCR2A)
6
  TCCR2B = (1<<CS20);
7
  // CS20 = Prescaler = 0
8
  OCR2A = 125; // Obergrenze, dann wird Timer zurückgesetzt - Clear Timer
9
  // 8MHz / OCRA(125) = 64000 OVF/s / 128 (128 Bits/Periode) = 500 Hz
10
  // gew. Frequenz: 500Hz
11
  // 500Hz => 8MHz/500Hz = 16.000 Overflows/Periode
12
  // z.B. 128 Bit: 16.000 / 128 Bit = 125 Overflows / Bit
13
  // ==> 125 OVF/Bit * 1/8MHz (125µs) = 15,625 µs *128 Bit= 2ms/Periode = 500Hz
14
15
}
16
17
ISR(TIMER2_COMPA_vect)    // Interrupt Vector Compare 2 A
18
{                         // Frequenz des ausgehenden Signals
19
  j++;
20
}

kann das soviel verzögern?

von Karl H. (kbuchegg)


Lesenswert?

6µs sind bei 8Mhz 48 Prozessortakte (wenn ich mich nicht verrechnet 
habe). Das könnte in Summe schon hinkommen. Pro ISR ein paar Push/Pop. 
Variable in Register holen, inkrement und zurück, der ret, dann ein 
Befehl im Hauptprogramm ehe die andere ISR loslegen kann. Die pusht auch 
erst mal ein paar Register ...

So viel sind 48 Takte dann auch wieder nicht.

von Bernd A. (Gast)


Lesenswert?

Wie kann ich jetzt das Problem lösen? (Lösungsansätze)
Den Timer 2 brauche ich ja um der PWM einen neuen Wert zu geben (128 
neue Werte pro Periode - eine Periode 500Hz) um meinen Sinus zu 
erzeugen. Ich habe 32 Werte pro viertel Sinus)
Inkrement klappert also die 128 Werte durch...

von MWS (Gast)


Lesenswert?

Schreib' einfach mal ein SEI() an den Anfang Deiner Timer2 ISR. Wobei Du 
uim den Jitter der ersten Push's nicht rumkommen wirst.

von Karl H. (kbuchegg)


Lesenswert?

MWS schrieb:
> Schreib' einfach mal ein SEI() an den Anfang Deiner Timer2 ISR.

Das ist sinnlos.
Während eines ISR sind Interrupts sowieso gesperrt.


> Den Timer 2 brauche ich ja um der PWM einen neuen Wert zu
> geben (128 neue Werte pro Periode - eine Periode 500Hz)
> um meinen Sinus zu erzeugen. Ich habe 32 Werte pro viertel Sinus)

Wie wärs, wenn du den Timer 2 den Interrupt nicht auslösen lösst, 
sondern den Overflow in der Hauptschleife pollst?:
Der Timer kann ruhig das Overflow Flag setzen, darf aber keine ISR 
auslösen. In der Hauptschleife fragst du das Overflow Flag ab und 
erhöhst dort zu gegebener Zeit das j und setzt das Overflow Flag wieder 
zurück.

von MWS (Gast)


Lesenswert?

> Das ist sinnlos. Während eines ISR sind Interrupts sowieso gesperrt.

Ich hab' SEI() und nicht CLI() geschrieben, also das Aufheben der 
Sperre. Timer2 sorgt für den Jitter, wenn man explizit dort weitere 
Interrupts erlaubt, dann kommt der Compare Int schneller dran.

Wobei mir ein Polling in der Mainloop auch als besser erscheint.

von Bernd A. (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Wie wärs, wenn du den Timer 2 den Interrupt nicht auslösen lösst,
> sondern den Overflow in der Hauptschleife pollst?:
> Der Timer kann ruhig das Overflow Flag setzen, darf aber keine ISR
> auslösen. In der Hauptschleife fragst du das Overflow Flag ab und
> erhöhst dort zu gegebener Zeit das j und setzt das Overflow Flag wieder
> zurück.

Wie mache ich das? Das Flag wird gesetzt wenn ein Overflow passiert und 
zurückgesetzt wenn die ISR verlassen wird. Umkehrschluss: Wenn das TOIE 
- Flag nicht gesetzt ist, wird das TOV trotzdem gesetzt?
Muss mal schnell nachlesen...

von Bernd A. (Gast)


Lesenswert?

zusatz: wenn es trotzdem gesetzt wird (sofort beim ersten Überlauf), und 
die Interrupts aus sind, heißt das, das das Flag immer gesetzt bleibt? 
Bis der Controller ausgeschaltet wird bzw ich es manuell zurücksetze?

Ja, ich geh der Sache mal nach...

von MWS (Gast)


Lesenswert?

> Wie mache ich das? Das Flag wird gesetzt wenn ein Overflow passiert und
> zurückgesetzt wenn die ISR verlassen wird.

Wenn die entsprechende ISR nicht erlaubt ist, dann wird das Flag beim 
Eintreten des Ereignisses gesetzt und bleibt es auch bis Du es löscht. 
Gelöscht wird durch Schreiben einer 1 in's Flagbit.

von Falk B. (falk)


Lesenswert?

@  Bernd A. (Gast)

>Wie kann ich jetzt das Problem lösen? (Lösungsansätze)
>Den Timer 2 brauche ich ja um der PWM einen neuen Wert zu geben (128
>neue Werte pro Periode - eine Periode 500Hz) um meinen Sinus zu
>erzeugen. Ich habe 32 Werte pro viertel Sinus)
>Inkrement klappert also die 128 Werte durch...

Dann pack einfach deinen Variabelenzähler in den Timer 0!

MFG
Falk

von Karl H. (kbuchegg)


Lesenswert?

MWS schrieb:
>> Das ist sinnlos. Während eines ISR sind Interrupts sowieso gesperrt.
>
> Ich hab' SEI() und nicht CLI() geschrieben

Ah. Mein Fehler.
Das war der 'cli und sei in ISR'-Reflex.

von Bernd A. (Gast)


Lesenswert?

Falk Brunner schrieb:
> Dann pack einfach deinen Variabelenzähler in den Timer 0!

Was soll ich denn damit?

Karl heinz Buchegger schrieb:
> Wie wärs, wenn du den Timer 2 den Interrupt nicht auslösen lösst,
> sondern den Overflow in der Hauptschleife pollst?:

(Ich brauche nicht das Overflow- sondern das Output Compare-Flag)

Kann ich das so machen?
1
  if (TIFR2 && OCF2A) //<-- !!
2
   {
3
     ++j;
4
     TIFR2 |= (1<<OCF2A);
5
     }

Ich habe gerade einen Overflow in meinem Hirn.

von Karl H. (kbuchegg)


Lesenswert?

Bernd A. schrieb:

> (Ich brauche nicht das Overflow- sondern das Output Compare-Flag)

Mein Fehler.
War irgendwie auf OVerflow fixiert und hab nicht mehr hochgescrollt um 
das zu kontrollieren ...

> Kann ich das so machen?
> [c]
>   if (TIFR2 && OCF2A) //<-- !!

Nope. Wee fragt man denn ein Bit ab? Funktioniert wie alle anderen 
Abfragen auch:

    if (TIFR2 & (1<<OCF2A) )

>    {
>      ++j;
>      TIFR2 |= (1<<OCF2A);

Diese Art von Flag wird gelöscht, indem man einfach zuweist.

      TIFR2 = (1<<OCF2A);

Wann immer im Handbuch steht, dass das Flag zurückgesetzt wird, indem 
man ein 1 Bit ins Register schreibt, ist genau das gemeint.

von Bernd A. (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Nope. Wee fragt man denn ein Bit ab? Funktioniert wie alle anderen
> Abfragen auch:
>
>     if (TIFR2 & (1<<OCF2A) )

Bernd A. schrieb:
> Ich habe gerade einen Overflow in meinem Hirn.

Da sind schon mal ein paar Grundlagen zuviel... - oh man

von Falk B. (falk)


Lesenswert?

@  Bernd A. (Gast)

>> Dann pack einfach deinen Variabelenzähler in den Timer 0!

>Was soll ich denn damit?

Die Variable hochzählen? Du willst konstant mit 500 Hz eine PWM 
ausgeben, dazu brauchst du nur einen Timer. Der macht die PWM und 
gleichzeiig deinen Timerinterrupt. Der kann parallel auch eine Variabel 
hochzählen, welche für andere Programmteile verwendet werden kann.

MFG
Falk

von Bernd A. (Gast)


Lesenswert?

Falk Brunner schrieb:
>>> Dann pack einfach deinen Variabelenzähler in den Timer 0!
>
>>Was soll ich denn damit?
>
> Die Variable hochzählen? Du willst konstant mit 500 Hz eine PWM
> ausgeben, dazu brauchst du nur einen Timer. Der macht die PWM und
> gleichzeiig deinen Timerinterrupt. Der kann parallel auch eine Variabel
> hochzählen, welche für andere Programmteile verwendet werden kann.

Das ist nur die halbe Wahrheit.
Meine 500Hz sind ein Sinus mit 128 Bit/Periode macht 15,625µs pro Bit. 
Mein Timer 0 läuft mit 8µs.
Sind 488 Hz. Meine Frequenz soll später über ein Poti eingestellt werden 
können zwischen 200Hz und 800Hz.

==========
Zurück zum eigentlichen Problem:
Ich brauche ja nur(!) ein invertiertes Signal am zweiten PWM Ausgang.
Kann ich nicht die HW-PWM machen (die läuft einwandfrei) und den PIN 
abfragen und invertiert am anderen Pin ausgeben?

von Falk B. (falk)


Lesenswert?

@  Bernd A. (Gast)

>Das ist nur die halbe Wahrheit.

Und warum rückst du nicht mit der ganzen raus? Siehe Netiquette!

>Meine 500Hz sind ein Sinus mit 128 Bit/Periode macht 15,625µs pro Bit.

Bit? Eher Byte, oder?

>Mein Timer 0 läuft mit 8µs.
>Sind 488 Hz.

Bei mir sind das eher 125 kHz.

> Meine Frequenz soll später über ein Poti eingestellt werden
>können zwischen 200Hz und 800Hz.

Ja und? Das kann man erstens über einen Ansatz mit konstanter Abtastrate 
lösen oder die Abtastrate der Ausgangsfrequenz anpassen.
Und da man weiss, welche Frequenz man ausgeben will, kann man das auch 
mit dem Hochzählen in den Griff bekommen.
Wozu brauchst du die variable j eigentlich?

>Kann ich nicht die HW-PWM machen (die läuft einwandfrei) und den PIN
>abfragen und invertiert am anderen Pin ausgeben?

Das geht nur über Software. Man kann aber ZWEI PWMs parallel (synchron) 
laufen lassen, an einem Timer (wenn dieser zwei oder mehr OCRx hat).

MFG
Falk

von Karl H. (kbuchegg)


Lesenswert?

Hmm.
SChön langsam überleg ich mir ob die einfachste Variante nicht darin 
bestünde am µC Pin einfach einmal normal rauszugehen und einmal über 
einen Inverter (zb Schalttransistor - NPN).

Wenn ich dich richtig verstehe, willst du ja eigentlich nur zusätzlich 
zum normalen Signal ein um 180° phasenverschobenes Signal haben.

von Bernd A. (Gast)


Lesenswert?

Tja, Falk,
du hast es wohl nicht ganz verstanden!

Falk Brunner schrieb:
>>Das ist nur die halbe Wahrheit.
>
> Und warum rückst du nicht mit der ganzen raus? Siehe Netiquette!

Diese Variante kam erst später hinzu.

Falk Brunner schrieb:
>>Meine 500Hz sind ein Sinus mit 128 Bit/Periode macht 15,625µs pro Bit.
>
> Bit? Eher Byte, oder?

Nein Bit, wer rechnen kann ist klar im Vorteil: 500Hz = 2ms geteilt 
durch 128 Bit sind 15,625 µs pro Bit!

Falk Brunner schrieb:
>>Mein Timer 0 läuft mit 8µs.
>>Sind 488 Hz.
>
> Bei mir sind das eher 125 kHz.

Siehst du, Sache nicht verstanden. Vielleicht habe ich auch zuviel von 
dir erwartet. Wenn du nun rückwärts rechnest und mit deiner 
hochzählvariablen in Timer0 zu arbeiten heißt das 2x8µs = 16µs (nächster 
Wert zu 15,625µs) mal 128 Bit (2,048ms) sind: 488,28125 Hz um es genau 
zu nehmen!

Höchzählen mit 1 (1x8µs): 976 Hz
Hochzählen mit 3 (3x8µs): 325 Hz

Falk Brunner schrieb:
>> Meine Frequenz soll später über ein Poti eingestellt werden
>>können zwischen 200Hz und 800Hz.
> [...]
> Und da man weiss, welche Frequenz man ausgeben will, kann man das auch
> mit dem Hochzählen in den Griff bekommen.

siehe oben

Falk Brunner schrieb:
> Wozu brauchst du die variable j eigentlich?

Damit zähle ich das Bit in der Periode hoch (128 Bit = 1 Periode)
--> zählen kann ich auch

Falk Brunner schrieb:
> Man kann aber ZWEI PWMs parallel (synchron)
> laufen lassen, an einem Timer (wenn dieser zwei oder mehr OCRx hat).

Timer 0 ist meine primäre PWM, Timer 2 ist für die Periode, Timer 1 wird 
später noch benötigt.

von Bernd A. (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Hmm.
> SChön langsam überleg ich mir ob die einfachste Variante nicht darin
> bestünde am µC Pin einfach einmal normal rauszugehen und einmal über
> einen Inverter (zb Schalttransistor - NPN).
>
> Wenn ich dich richtig verstehe, willst du ja eigentlich nur zusätzlich
> zum normalen Signal ein um 180° phasenverschobenes Signal haben.

Das ist auch ein guter Ansatz, da bin ich ja noch gar nicht drauf 
gekommen. Das kommt davon, wenn der Horizont immer so eingeschränkt ist. 
Gut dass man mit Leuten von außen drüber spricht. Vielen Dank, ich 
glaube das ist die vernünftigste Lösung. Externe Beschaltung ist ja auch 
nötig und der eine Transistor macht auch nichts mehr...

Guter Hinweis...

von Falk B. (falk)


Lesenswert?

@  Bernd A. (Gast)

>Tja, Falk,
>du hast es wohl nicht ganz verstanden!

Wir werden sehen . . .

>>>Meine 500Hz sind ein Sinus mit 128 Bit/Periode macht 15,625µs pro Bit.
>
>> Bit? Eher Byte, oder?

>Nein Bit, wer rechnen kann ist klar im Vorteil: 500Hz = 2ms geteilt
>durch 128 Bit sind 15,625 µs pro Bit!

Du gibst aber pro PWM-Zyklus einen 5-Bit Wert aus, denn deine PWM kann 
ja wahrscheinlich Werte zwischen 0..255 annehmen. Macht 1 
BYTE/PWM-Zyklus.

>Siehst du, Sache nicht verstanden. Vielleicht habe ich auch zuviel von
>dir erwartet.

Du bist ja ein ganz Schlauer. Wie alt bist du? 12 1/2?

> Wenn du nun rückwärts rechnest und mit deiner
>hochzählvariablen in Timer0 zu arbeiten heißt das 2x8µs = 16µs (nächster
>Wert zu 15,625µs) mal 128 Bit (2,048ms) sind: 488,28125 Hz um es genau
>zu nehmen!

Das sind keine 128 Bit, sondern 128 Abtastpunkte a 1 Byte.

>Höchzählen mit 1 (1x8µs): 976 Hz
>Hochzählen mit 3 (3x8µs): 325 Hz

>>> Meine Frequenz soll später über ein Poti eingestellt werden
>>>können zwischen 200Hz und 800Hz.
>> [...]
>> Und da man weiss, welche Frequenz man ausgeben will, kann man das auch
>> mit dem Hochzählen in den Griff bekommen.

>siehe oben

Zitieren will auch gelernt sein.

"Das kann man erstens über einen Ansatz mit konstanter Abtastrate
lösen oder die Abtastrate der Ausgangsfrequenz anpassen."

Wenn man 800 Hz Sinus mit 128 Abtastwerte/Periode erzeugen will, braucht 
man logischerweise 102,4 kHz PWM-Frequenz. Will man 5 Bit Auflösung 
macht das 3,2768 MHz.
Aber für einen reinen Sinus ist das alles sowieso Overkill. Mit DDS 
geht das bedeutend einfacher.

>Falk Brunner schrieb:
>> Wozu brauchst du die variable j eigentlich?

>Damit zähle ich das Bit in der Periode hoch (128 Bit = 1 Periode)
>--> zählen kann ich auch

Was ja auch vollkommen unabhängig von der PWM-Erzeugung ist und man 
dafür unbedingt einen zweiten Timer braucht . . .

Na dann mach mal.

MfG
Falk

von Bernd A. (Gast)


Lesenswert?

So langsam gefällt mir die Konversation mit dir.

Falk Brunner schrieb:
> Du gibst aber pro PWM-Zyklus einen 5-Bit Wert aus, denn deine PWM kann
> ja wahrscheinlich Werte zwischen 0..255 annehmen. Macht 1
> BYTE/PWM-Zyklus.

NÖ

Meine PWM nimmt 64 Werte an (6 Bit: 0...63)
Aber ich habe mich unglücklich ausgedrückt:

Ich schrieb:
>Meine 500Hz sind ein Sinus mit 128 Bit/Periode macht 15,625µs pro Bit.

Richtig: Meine 500Hz sind ein Sinus, der aus 128 Schritten besteht 
(500Hz = 2ms --> 2ms/128 Schritte = 15,625 µs pro Schritt). Nach einem 
Schritt wird die PWM mit einem neuen Wert geladen.

Falk Brunner schrieb:
>>Siehst du, Sache nicht verstanden. Vielleicht habe ich auch zuviel von
>>dir erwartet.
>
> Du bist ja ein ganz Schlauer. Wie alt bist du? 12 1/2?

Siehst du, Sache doch nicht verstanden. Vielleicht unterstellst du den 
Fragestellern einfach zuviel! Du denkst für den Fragesteller mit und 
schießt am Ziel vorbei.

Falk Brunner schrieb:
> Das sind keine 128 Bit, sondern 128 Abtastpunkte a 1 Byte.

Hier hast du Recht! Schritte = Abtastpunkte
ABER: 128 Abtastpunkte á 64 Bit

Falk Brunner schrieb:
>>> Wozu brauchst du die variable j eigentlich?
>
>>Damit zähle ich das Bit in der Periode hoch (128 Bit = 1 Periode)
>>--> zählen kann ich auch
>
> Was ja auch vollkommen unabhängig von der PWM-Erzeugung ist und man
> dafür unbedingt einen zweiten Timer braucht . . .

Wieder richtig! Die PWM-Frequenz läuft unabhängig von meiner zu 
erzeugenden Frequenz! Immer stabil mit 125 kHz! Und ca. zwei PWM-Zyklen 
pro Abtastpunkt. (500*128*2=128.000 - Meine PWM: 125kHz)
Der zweite Timer erzeugt die Frequenz des zu erzeugenden Signals!

Wenn es dich wirklich interessiert (davon gehe ich nicht aus) schau doch 
mal in den Code.

Außerdem möchte ich auch keine Grundsatzdiskussion, wie du die Sache 
angegangen wärst, sondern eine Lösung für mein geschildertes Problem!

Und wenn du helfen willst orientiere dich bitte an der Frage und 
unterstelle niemanden Dummheit (Das gilt übrigens ganz allgemein und 
nicht dieses Thread).

Falls du dem Fragesteller was unterstellst, dann kläre es mit ihm ab 
oder frag ihn. Getreu dem Motto: Kann es vielleicht sein, dass du die 
PWM nicht verstehst? o.ä.
Aber nicht: Du hast ja keine Ahnung von PWM! Nimm einfach den Timer mit 
den Einstellungen und meiner Methode, dann gehts schon...

Also mein Motto, getreu den alten Funkern: Denken, drücken, sprechen! 
soll heißen erst denken, das Probelm verstehen (aus der Sicht des 
Fragestellers), dann Antwort formulieren und schließlich ins Forum 
schreiben.

von Falk B. (falk)


Lesenswert?

@  Bernd A. (Gast)

>Meine PWM nimmt 64 Werte an (6 Bit: 0...63)

In deinem Quelltext geht es bis 31, also 5 Bit.

Aber da ich mal wieder (unzulässigerweise) mitdenke, und du ja noch eine 
invertierten Ausgang haben willst, sind es wahrscheinlich 32 Stufen pro 
Polarität, also 64 Stufen von Spitze zu Spitze, erzeugt duch eine 
H-Brücke.

>Aber ich habe mich unglücklich ausgedrückt:

In der Tat.

>Siehst du, Sache doch nicht verstanden. Vielleicht unterstellst du den
>Fragestellern einfach zuviel!

Glaub ich nicht.

> Du denkst für den Fragesteller mit

Berufs- und Forumskrankheit ;-)

>> Das sind keine 128 Bit, sondern 128 Abtastpunkte a 1 Byte.

>Hier hast du Recht! Schritte = Abtastpunkte
>ABER: 128 Abtastpunkte á 64 Bit

Nöö. Du solltest dich a) mal mit Zahlensystemen und b) mit 
Ausdrucksweise in der Welt der Programmierung/Elektronik befassen. Denn 
deine Wortwahl ist falsch und irreführend.
Wenn deine Zahlen von 0..63 laufen sind das 6 Bit ;-)

Anzahl der verschiedenen Zahlen = 2 hoch Anzahl der Bits (im 
Binärsystem)

>> Was ja auch vollkommen unabhängig von der PWM-Erzeugung ist und man
>> dafür unbedingt einen zweiten Timer braucht . . .

>Wieder richtig! Die PWM-Frequenz läuft unabhängig von meiner zu
>erzeugenden Frequenz!

Ja und? Damit ist das Problem doch noch einfacher.

> Immer stabil mit 125 kHz! Und ca. zwei PWM-Zyklen
>pro Abtastpunkt. (500*128*2=128.000 - Meine PWM: 125kHz)
>Der zweite Timer erzeugt die Frequenz des zu erzeugenden Signals!

Was aber nicht sonderlich gut ist, denn damit kann man sich schöne 
Artefakte ins Signal holen, wenn es da zu dummen Frequenzverhältnissen 
kommt. Stichwort Schwebung etc.
Und ist auch sonst nicht sonderlich sinnvoll. Ein CD-Player hat auch nur 
einen 44.1 kHz Takt (Jaja, ist ein alter CD-Player ;-), und mit dem 
werden sämtliche Berechnungen/Datenausgaben gemacht.

>Außerdem möchte ich auch keine Grundsatzdiskussion, wie du die Sache
>angegangen wärst, sondern eine Lösung für mein geschildertes Problem!

;-)
Jaja, klassicher Fall von Netiquette, oder vielmehr ein Mangel 
derer.

Zitat

"Besonders für Anfänger gilt: Gerade am Anfang ist es immer gut zu 
sagen, was man erreichen will und nicht so sehr Annahmen darüber zu 
treffen, wie man es erreichen könnte und dann das Wie zu hinterfragen. 
Oft ist der Denkfehler nämlich schon im Ansatz und man kann besser 
helfen, wenn man das Ziel des Fragenden kennt."

>Und wenn du helfen willst orientiere dich bitte an der Frage und
>unterstelle niemanden Dummheit

Deine Aussagen sprechen ihre eigene Sprache. Und als Supercrack kommst 
du dabei nicht weg . . .

> (Das gilt übrigens ganz allgemein und nicht dieses Thread).

>Also mein Motto, getreu den alten Funkern: Denken, drücken, sprechen!

Geh mal davon aus, dass du mich nicht über solche Dinge belehren musst.

MfG
Falk

von Bernd A. (Gast)


Lesenswert?

Falk Brunner schrieb:
>>Meine PWM nimmt 64 Werte an (6 Bit: 0...63)
>
> In deinem Quelltext geht es bis 31, also 5 Bit.
>
> Aber da ich mal wieder (unzulässigerweise) mitdenke, und du ja noch eine
> invertierten Ausgang haben willst, sind es wahrscheinlich 32 Stufen pro
> Polarität, also 64 Stufen von Spitze zu Spitze, erzeugt duch eine
> H-Brücke.

Guten Morgen!
Hast du meinen Quelltext wirklich gelesen?
1
void pwm_init(void) //timer 0 init
2
{   /* PWM-Output: OC0B */
3
    /* OC0B -> ATmega88 PD5 */
4
    
5
    TCCR0A = (1<<WGM00) | (1<<COM0B1); 
6
    TCCR0B = (1<<CS00) | (1<<WGM02);
7
8
    OCR0A = 64; // Top 
9
    // 1 Zyklus (0x00 - 0x3F) = 8MHz/64 = 125kHz
10
}
Wer lesen kann ist klar im Vorteil! Steht hier irgendetwas von 32??? Ich 
denke nicht!

Falk Brunner schrieb:
>>Siehst du, Sache doch nicht verstanden. Vielleicht unterstellst du den
>>Fragestellern einfach zuviel!
>
> Glaub ich nicht.

Glaub ich immer noch.

>> Du denkst für den Fragesteller mit
>
> Berufs- und Forumskrankheit ;-)
Solltest du vielleicht lassen. Es sit nicht immer wie es scheint.

Falk Brunner schrieb:
> Jaja, klassicher Fall von Netiquette, oder vielmehr ein Mangel
> derer.
>
> Zitat
>
> "Besonders für Anfänger gilt: Gerade am Anfang ist es immer gut zu
> sagen, was man erreichen will
Habe ich gemacht: eine invertierte PWM, um einen invertierten Sinus zu 
erzeugen
>und nicht so sehr Annahmen darüber zu
> treffen, wie man es erreichen könnte und dann das Wie zu hinterfragen.
Die Annahmen triffst du! Und liegst ganz oft kräftig daneben.
> Oft ist der Denkfehler nämlich schon im Ansatz und man kann besser
> helfen, wenn man das Ziel des Fragenden kennt."
Genau, du sollst erst richtig und alles lesen, dann das wichtigste: es 
VERSTEHEN und nicht glauben es zu wissen was der Fragende möchte.
Wenn dir das zuviel ist (das lesen), dann halt dich halt zurück.

Falk Brunner schrieb:
> Und als Supercrack kommst
> du dabei nicht weg . . .

1. Möchte ich das gar nicht und 2. bin ich kein Supercrack, denn dann 
würde ich nicht fragen.

Falk Brunner schrieb:
> Geh mal davon aus, dass du mich nicht über solche Dinge belehren musst.

Vielleicht nimmst du das Denken nicht allzu ernst. Geh davon aus, dass 
der Fragende sich schon ein Weilchen damit beschäftigt hat und nicht 
erst seit zwei Minuten am Thema hängt. Wenn dir das Klar ist, kannst du 
ganz anders argumentieren und auch hoffentlich helfen dem Fragenden das 
Problem zu lösen.

von Klaus F. (kfalser)


Lesenswert?

Um eueren Hick-Hack zu stören :
a) Mit Interrupts das Pin setzen geht es nicht, weil Du nicht 
garantieren kannst, dass das Interrupt sofort ausgeführt wird, bzw. dass 
die Zeit zwischen Compare Match und Abarbeiten der Interrupt immer 
konstant ist. Du bekommst in jeden Fall einen Jitter.
Außerdem können die Interrupts bei sehr hohen oder sehr kleinen 
PWM-Werten sehr schnell hintereinander kommen, weil die Flanken sehr 
nahe sind. Da bleibt möglicherweise nicht mehr genug Rechenzeit.
b) Nach meiner Rechnung sollte die Periode der PWM 63.5 KHz sein. Du 
setzt TOP auf 64, aber der Zähler zählt doch auf und ab, also 1-64 und 
dann 63 - bis 0. Ergibt einen Zählperiode von 126.
c) Timer 1 kann das alles was Du möchtest. Er hat ein getrenntes TOP 
Register (ICR1) und du kannst OCR1A und OCR1B verwenden.

von Bernd A. (Gast)


Lesenswert?

Klaus Falser schrieb:
> Timer 1 kann das alles was Du möchtest. Er hat ein getrenntes TOP
> Register (ICR1) und du kannst OCR1A und OCR1B verwenden.

Danke Klaus, du hast das Problem erkannt. Vielen Dank für den Hinweis, 
werde mir den Timer 1 genauer betrachten. Timer 1 brauchte ich 
eigentlich als 16-Bit-Timer für eine andere Geschichte, muss aber mal 
schauen wenn es mit der PWM klappt, ob ich dann mit Timer 2 zurecht 
komme.

von Klaus F. (kfalser)


Lesenswert?

Auf den Timer 2 kann man auch verzichten, den verwendest Du ja, um der 
PWM den PWM Wert unter der Nase zu verdrehen.
Der Zähler der PWM kann aber auch ein Interrupt auslösen, wenn der 
BOTTOM Wert erreicht ist, in deinem Fall alle 16 us. In diesem Interrupt 
lädst Du den neue OCRx Wert, der dann bei BOTTOM übernommen wird. Das 
ist der schnellste PWM update, der Sinn macht; jede PWM Periode bekommt 
dann einen neuen Wert.
In der ISR hast Du 2 Möglichkeiten :
- Entweder Du bereitest ein Feld vor (volle Periode oder 1/4 Periode mit 
Spiegeln) mit der genauen Länge im 16 us Raster und die Werte werden nur 
mehr ausgelesen,
- oder Du bereitest eine allgemeine Sinus-Tabelle vor, addierst die 
Phase auf, bestimmst daraus den Index für die Sinustabelle und die Werte 
werden interpoliert (oder auch nicht).

von Matthias L. (Gast)


Lesenswert?

>in deinem Fall alle 16 us

>- oder Du bereitest eine allgemeine Sinus-Tabelle vor, addierst die
>Phase auf, bestimmst daraus den Index für die Sinustabelle und die Werte
>werden interpoliert (oder auch nicht).


Die Frage ist nur, ob das alles in die 16µs reinpasst.

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.