Forum: Analoge Elektronik und Schaltungstechnik ATmega88: Wechselspannung mit variabler Frequenz erzeugen


von Philipp B. (philipp_burch)


Lesenswert?

Hallo zusammen,

ich stehe mal wieder irgendwie auf dem Schlauch und komme nicht 
runter...

Auf meiner Platine werkelt ein ATmega88 als Frequenzgenerator für eine 
Wechselspannung (Per H-Brücke) mit einer Frequenz von wahlweise 25kHz 
oder 30kHz. Die beiden Eingänge der H-Brücke hängen an OC1A und OC1B, 
sollten sich also prima per Output Compare steuern lassen. Funktioniert 
auch recht gut.
Problematisch wird's allerdings, wenn ich die Frequenz ändere. Den Timer 
lasse ich im CTC-Modus laufen, OCR1A = OCR1B = F_CPU / (Frequenz  2  
8), Prescaler 8. Wenn ich die Frequenz aufdrehe und TCNT1 gerade 
oberhalb vom neuen OCR-Wert ist, dann läuft der Timer natürlich komplett 
durch -> Lange Verzögerung -> Müll. Ist allerdings mit einer Anweisung à 
la
1
if (TCNT1 > AC_OCR_MOD) TCNT1 = 0;
vor der Änderung recht einfach zu beheben. Mehr Probleme bereitet mir 
dagegen die Tatsache, dass die beiden Ausgänge teilweise nachher nicht 
mehr synchronisiert sind, sie sind dann also nicht mehr gegenläufig und 
meine Wechselspannung ist weg. Beim Initialisieren lässt sich das ja 
recht gut mit dem Force-Output-Compare-Bit bewerkstelligen, doch im 
laufenden Betrieb ist das irgendwie etwas komplizierter.

Daher die Frage: Wie kann ich es möglichst einfach hinkriegen, dass die 
beiden Pins mit gleicher Phasenverschiebung und ohne grössere 
Unterbrüche toggeln? Irgendwie finde ich da keinen passenden Modus, denn 
eigentlich sollte mit einem PWM-Modi doch genau das möglich sein...

Danke und Gruss,
Philipp

von Philipp B. (philipp_burch)


Lesenswert?

Ich habe eben nochmal die diversen PWM-Modi im Datenblatt studiert. 
Irgendwie will mir etwas einfach nicht ganz klar werden:
Es gibt ja diverse Modi, in denen die PWM-Frequenz mittels dem 
OCR1A-Register eingestellt werden kann. Aber wo muss ich denn dann den 
Compare-Wert für den OC1A-Pin ablegen? verwirrtbin

EDIT: Nochwas: Eine Änderung der Hardware möchte ich vermeiden, da die 
Platine bereits gemacht ist (Ist schon etwas länger her, da hatte ich 
die Verwendung der Hardware-Frequenzerzeugung zwar eingeplant (Signale 
entsprechend geroutet), hab's aber dann doch per Software gelöst).

von winne (Gast)


Lesenswert?

joop

an HW dachte ich als erste einfach ausgang negieren fertig und Port 
gesparrt.

variante 2
den 2. ausgan aus der HW PWM herausnehmen und per SW setzen

Igitt was für eine Krücke HW-PWM mit SW Holzbein

nein sowas macht man Per inverter oder Transistor auf der Treiberseite 
der Hbrücke aber nicht mit nem Portpin. Das ist viel zu Schade und zu 
umständlich noch dazu aus eben dem von dir herausgefundenem Grund ;-)

von Philipp B. (philipp_burch)


Lesenswert?

Schon klar, mit Hacks an der Software oder Hardware würde das schon 
funktionieren, aber das muss doch auch so gehen. Es gibt ja extra Modi 
für derartige PWM-Erzeugung (Phase and frequency correct zum Beispiel), 
aber wenn man OCR1A als TOP verwendet, dann kann man OC1A nicht 
verwenden. Na aber hallo?! Das kann's doch wohl nicht sein. Verwende ich 
dagegen ICR1 als TOP, ist der Vorteil der Pufferung wieder weg, was in 
einem unsauberen Signal resultiert.

Die HW-Lösung funktioniert im Übrigen auch nur, wenn die Pins (Wie in 
meinem Fall) IMMER gegenläufig angesteuert werden müssen.

von Philipp B. (philipp_burch)


Lesenswert?

Bäh, der verdammte PDF-Scheiss...


Also, das Datenblatt verstehe ich im Moment irgendwie einfach nicht so 
recht. In der Tabelle auf Seite 130 sind ja die diversen 
Einstellungsmöglichkeiten der COM1xy-Bits aufgeführt. Für den 
Fast-PWM-Mode gibt es da z.B. sowas:

WGM13:0 = 14 or 15: Toggle OC1A on Compare
Match, OC1B disconnected (normal port operation).
For all other WGM1 settings, normal port operation,
OC1A/OC1B disconnected.

Modus 14 verwendet ICR1 als TOP und Modus 15 OCR1A. Angenommen ich 
verwende Modus 15 und oben genannte OC-Einstellung, dann wäre also OC1B 
unbenutzt und OC1A toggelt jeweils beim Compare-Match. Da sehe ich den 
Sinn dieser Einstellung irgendwie nicht wirklich, das ist ja dann etwa 
das Gleiche wie CTC...

von Jörg X. (Gast)


Lesenswert?

Kannst du nicht im mit ICR1 als Top (Mode 8, 10 oder 14) die OCR 
register in einem Overflow-interrupt updaten, und das ICR1 im nächsten?
Ich interpretiere das mal so ins Datenblatt (Beispiel: Mode 14, ICR1 als 
TOP, FastPWM):
 - eine Änderung im ICR register wird sofort übernommen
 - der Overflow-interrupt kommt bei TOP (= alter ICR1-Wert)
 - die OCR register werden bei BOTTOM geupdatet (1xPRESCALER Takte nach 
dem Overflow), und man kann man die OCR1n nicht innerhalb von 8 Takten 
in  einer ISR beschreiben (allein der aufruf dauert 4 + 2(rjmp) + 
2(push) Takte)

Also könnte doch es so gehen:
 ICR und OCR wert(e) global speichern
 flag setzen
 Overflow-interrupt aktivieren:
    beim ersten Overflow-interrupt werden die OCR register beschrieben
    beim nächsten Overflow-interrupt wird das ICR register beschrieben 
und
    der Overflow-interrupt wieder deaktiviert

hth. Jörg
ps.: alles ungetestet

von Philipp B. (philipp_burch)


Lesenswert?

Könnte man wohl machen, allerdings wär's dann eher einfacher, die 
Frequenzerzeugung direkt in die Interrupts auszulagern. Die Verwendung 
der Interrupts wollte ich eigentlich komplett vermeiden, da ich das Prog 
in C schreibe und dazu Inline-ASM verwenden müsste (Bei C gibt's einen 
recht grossen Overhead bei Interrupt-Aufrufen).

Im Moment habe ich es so gelöst, dass ich ICR1 als TOP verwende und 
jeweils vor dem ändern TCNT1 auf den neuen TOP-Wert setze, sollte es 
denn höher sein. Ist nicht ideal, müsste aber funktionieren.

von Jörg X. (Gast)


Lesenswert?

Naja, ich hatte das so gedacht, dass der Overflow-interrupt nur zum 
ändern der Frequenz kurz aktiviert wird, zweimal zuschlägt, und danach 
die Hardware weiter PWM machen lässt...

? Jörg

von Philipp B. (philipp_burch)


Lesenswert?

Achso, ja das wäre natürlich möglich. Mal sehn, danke.

von Peter D. (peda)


Lesenswert?

Also, Du willst beide Pins zum gleichen Zeitpunkt entgegengsetzt 
togglen.

Dann setz doch einfach OCR1A und OCR1B auf 0 und ICP auf die halbe 
Periodendauer.
Und dann setz die Pins per Force.. auf entgegengesetzte Pegel, dann auf 
Toggle on Compare und starte den Timer.

Die Frequenz änderst Du dann mit ICP.


Peter

von Philipp B. (philipp_burch)


Lesenswert?

@Peter:

Klar, etwa so wollte ich es ja auch machen. Das Problem ist nur, dass 
der Zeitpunkt der Änderung am ICR (Ich nehme an, du meinst ICR und nicht 
ICP) nicht definiert ist. Wenn ich den Wert nach unten korrigiere und 
der Timer gerade einen höheren Wert hat, dann läuft er komplett durch 
und erzeugt einen viel zu langen Impuls. Das ist allerdings nicht so 
tragisch, das kann ich mit einer Anpassung von TCNT1 in den Griff 
kriegen.
Problematischer ist dabei, dass manchmal die Synchronisation verloren 
geht und dann ist die Spannung weg.

Ich muss mir die ganze Sache aber nochmal ansehn, irgendwas ist grade 
komisch...

von Peter D. (peda)


Lesenswert?

Philipp Burch wrote:

> Klar, etwa so wollte ich es ja auch machen. Das Problem ist nur, dass
> der Zeitpunkt der Änderung am ICR (Ich nehme an, du meinst ICR und nicht
> ICP) nicht definiert ist. Wenn ich den Wert nach unten korrigiere und
> der Timer gerade einen höheren Wert hat, dann läuft er komplett durch
> und erzeugt einen viel zu langen Impuls.

Dann mach das neu laden einfach im Interrupt, also direkt nachdem das 
letzte Compare erfolgte.
Den Interrupt nur dann enablen, wenn der Wert geändert werden soll 
(Interruptflag vorher löschen!).


> Problematischer ist dabei, dass manchmal die Synchronisation verloren
> geht und dann ist die Spannung weg.

Wie soll denn das gehen ?

Wenn OCR1B = OCR1A und nie geändert werden, müssen sie auch synchron 
bleiben.

Du änderst ja nur ICR.


Peter

von Philipp B. (philipp_burch)


Lesenswert?

Hm, da fällt mir jetzt aber grade was auf. Mit welchem Modus würdest du 
das eigentlich lösen? Verwende ich einen PWM-Modus kann ich die Pins 
nicht einfach toggeln lassen (Jeweils nur OC1A, OC1B ist dann nicht 
angeschlossen), im Normal-Mode läuft der Timer immer komplett durch und 
bei CTC kommt er wegen dem "Clear timer on COMPARE" gar nie aus dem 
Interrupt bei OCR1A = 0. Setze ich beide OCRs auf die gewünschte 
PWM-Frequenz funktioniert's wiederum nicht, da dann eben die 
Synchronisation verloren geht :(
So gesehen ist der CTC-Modus mit ICR als TOP eigentlich ziemlich 
sinnlos, denn TOP kann ja eigentlich gar nie erreicht werden, ausser OCR 
ist höher als TOP...

von Peter D. (peda)


Lesenswert?

Philipp Burch wrote:
> Hm, da fällt mir jetzt aber grade was auf. Mit welchem Modus würdest du
> das eigentlich lösen? Verwende ich einen PWM-Modus kann ich die Pins
> nicht einfach toggeln lassen


PWM geht natürlich nicht.

Mode 12 ist der einzig richtige. Da hast Du immer 50% Tastverhältnis und 
mußt nur ICR1 ändern zur Frequenzeinstellung.


Peter

von Philipp B. (philipp_burch)


Lesenswert?

> Mode 12 ist der einzig richtige. Da hast Du immer 50% Tastverhältnis und
> mußt nur ICR1 ändern zur Frequenzeinstellung.

Das dachte ich auch. Doch es ist trotzdem ein CTC-Modus, der Timer wird 
immer beim Compare gelöscht. kA warum, aber sowohl im Simulator als auch 
im realen Controller verhält er sich so und kommt einfach nie mehr aus 
dem Interrupt...
1
  //Setup timer 1 to generate the 25kHz AC signal
2
  TCCR1A = _BV(COM1A0) | _BV(COM1B0);
3
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS11);
4
  TCCR1C = _BV(FOC1B);
5
  OCR1A = OCR1B = 0;
6
  ICR1 = AC_OCR_NORMAL;
7
  TIMSK1 = _BV(OCIE1A);

ICR1 hat dann den Wert 50.

von Peter D. (peda)


Lesenswert?

Philipp Burch wrote:

> Das dachte ich auch. Doch es ist trotzdem ein CTC-Modus, der Timer wird
> immer beim Compare gelöscht.

Das soll er ja auch, damit bestimmst Du doch die Frequenz.


> kA warum, aber sowohl im Simulator als auch
> im realen Controller verhält er sich so und kommt einfach nie mehr aus
> dem Interrupt...


Du kriegst nur alle 400 Zyklen nen Interupt. Was machst Du darin 
solange, daß er nicht mehr rauskommen soll ?

Wozu überhaupt der Interrupt ?


Peter

von Philipp B. (philipp_burch)


Lesenswert?

Ähm, jetzt komme ich aber nicht mehr mit...

Peter Dannegger wrote:
> Philipp Burch wrote:
>
>> Das dachte ich auch. Doch es ist trotzdem ein CTC-Modus, der Timer wird
>> immer beim Compare gelöscht.
>
> Das soll er ja auch, damit bestimmst Du doch die Frequenz.

Oben schriebst du noch, die Frequenz würde mit ICR bestimmt. Daher ja 
auch Modus 12 und nicht Modus 4.

>> kA warum, aber sowohl im Simulator als auch
>> im realen Controller verhält er sich so und kommt einfach nie mehr aus
>> dem Interrupt...
>
>
> Du kriegst nur alle 400 Zyklen nen Interupt. Was machst Du darin
> solange, daß er nicht mehr rauskommen soll ?

Im Interrupt mache ich gar nix, das ist es ja. Das OC-Flag ist einfach 
dauernd gesetzt (Durch die Hardware).

> Wozu überhaupt der Interrupt ?

Na zum ändern des ICR-Wertes, hast du doch selbst geschrieben. Den 
Interrupt habe ich jetzt mal testweise dauernd aktiviert gelassen, dafür 
ist die Routine leer. Aber wie gesagt: Der Controller hängt immer drin.

von Peter D. (peda)


Lesenswert?

Philipp Burch wrote:

> Oben schriebst du noch, die Frequenz würde mit ICR bestimmt. Daher ja
> auch Modus 12 und nicht Modus 4.

Ja, clear on Compare mit ICR1.


> Im Interrupt mache ich gar nix, das ist es ja. Das OC-Flag ist einfach
> dauernd gesetzt (Durch die Hardware).

Durch die Hardware alle 400 Zyklen, also nicht dauernd.


> Na zum ändern des ICR-Wertes, hast du doch selbst geschrieben.

Ich wußte ja nicht, daß Du die Frequenz ständig ändern willst.


> Den
> Interrupt habe ich jetzt mal testweise dauernd aktiviert gelassen, dafür
> ist die Routine leer. Aber wie gesagt: Der Controller hängt immer drin.

Wenn er leer ist, dann braucht er nur 10 Zyklen, also 390 Zyklen (97,5%) 
bist Du nicht drin.


Peter

von Philipp B. (philipp_burch)


Lesenswert?

Ich will dich ja nicht angreifen, aber irgendwie willst du mich einfach 
nicht verstehen. Oder wir reden aneinander vorbei.

Mach' doch mal bitte ein neues Projekt (Assembler) für den Mega88 auf 
und füge diesen Code ein:
1
.include <m88def.inc>
2
3
.org 0 rjmp RESET
4
.org OC1Aaddr rjmp TIMER1_COMPA_vect
5
6
RESET:
7
8
  ldi r16, HIGH(RAMEND)
9
  out SPH, r16
10
  ldi r16, LOW(RAMEND)
11
  out SPL, r16
12
13
14
  ldi r16, 1 << WGM13 | 1 << WGM12 | 1 << CS11
15
  sts TCCR1B, r16
16
  clr r16
17
  sts OCR1AH, r16
18
  sts OCR1AL, r16
19
  sts ICR1H, r16
20
  ldi r16, 50
21
  sts ICR1L, r16
22
  ldi r16, 1 << OCIE1A
23
  sts TIMSK1, r16
24
25
  sei
26
27
  mainloop:
28
29
  rjmp mainloop
30
31
32
33
34
TIMER1_COMPA_vect:
35
  nop
36
reti

Timer1 wird (Der Einfachheit halber ohne die OC-Pins) in Modus 12 (CTC, 
TOP = ICR) initialisiert. Prescaler ist 8, OCR ist 0 und ICR ist 50. Der 
Output-Capture-Interrupt A ist aktiviert und macht einfach mal nix.
Wenn du das Prog ausführst (also simulierst) wirst du feststellen, dass 
der Controller andauernd im Interrupt hängt.
Wird OCR1A auf einen anderen Wert gelegt (z.B. 3), dann läuft der Timer 
eben bis zu diesem Wert und wird dann zurückgesetzt. Der Wert von ICR 
scheint ihn überhaupt nicht zu interessieren...

Vielleicht stehe ich ja auch einfach komplett auf dem Schlauch.

von Peter D. (peda)


Lesenswert?

Philipp Burch wrote:

> Wenn du das Prog ausführst (also simulierst) ...

Nein, Ausführen und Simulieren sind 2 völlig verschiedene Sachen.

Ich hab schon ewig nichts mehr simuliert, weil nicht alle Funktionen 
aller Derivate unterstützt werden.
Lies mal die Doku des Simulators gründlich.

Nur das Ausführen zeigt wirklich, ob etwas funktioniert.


Peter

von Philipp_Burch (Gast)


Lesenswert?

> Nur das Ausführen zeigt wirklich, ob etwas funktioniert.

Das tut es ja eben NICHT. Ob im Simulator oder auf dem Proz.

von Philipp B. (philipp_burch)


Lesenswert?

Philipp_Burch wrote:
>> Nur das Ausführen zeigt wirklich, ob etwas funktioniert.
>
> Das tut es ja eben NICHT. Ob im Simulator oder auf dem Proz.

Ok, ich nehme alles zurück und behaupte das Gegenteil. Es funktioniert 
doch. Anscheinend hab' ich beim letzten Mal irgendwo anders 'nen Hund 
vergraben...

Jetzt weiss ich auch wieder, warum ich den Simulator normalerweise nicht 
verwende...

Nix für ungut und danke für die Hilfe.

Gruss,
Philipp

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.