Forum: Mikrocontroller und Digitale Elektronik Kurze Frage zu PWM und Timer bei AVR's


von Florian G. (suffix)


Lesenswert?

Hallo zusammen,

ich habe mit dem 8Bit Timer/Counter0 des AtMega16 ein PWM-Signal 
erzeugt. Das funktioniert wunderbar. Ich habe den Timer initialisiert 
und in auf Phase Correct PWM eingestellt. Er generiert nun am OC0-Pin 
das richtige Signal und ein Voltmeter zeigt mir je nach Wert im 
OCR-Register ca 0,2 bis 5V an. Eine LED leuchtet dann auch, je nach 
Einstellung, unterschiedlich hell.

Ich betreibe den AVR mit 4Mhz, wenn ich den Prescaler auf 1024 einstelle 
blinkt die LED schon sichtbar, bei "No Prescaling" leuchtet sie schön 
dauerhaft, was ja auch logisch ist.

Ich würde diese LED aber gerne per Programmaufruf dimmen, also immer von 
00 bis ff und umgekehrt den Wert im OCR ändern. Im Timing-Diagramm des 
Datenblatts steht, dass man den OCR-Wert immer bei Zählerstand ff 
updaten soll. Wie soll denn das gehen? Der Compare-Interrupt sorgt ja 
für das umschalten des Pins, und der Overflow-Interrupt wird ja in 
diesem Modus bei Bottom ausgelöst und eben nicht bei Top.

Ich hab mir gedacht: Egal, wertest halt mal den Overflow-Interrupt aus, 
aber nicht mal das funktioniert. Im Simulator ruft er einfach die ISR 
nicht auf und im Controller natürlich auch nicht.

Das Programm dazu
1
    .INCLUDE "m16def.inc" ;Baustein
2
    .EQU takt = 1000000   ;Takt
3
    .DEF akku = r16      ;Arbeitsregister
4
    .CSEG          ;Flash-Speicher verwenden
5
    rjmp  start
6
    .ORG  OVF0addr    ;Overflow-Adresse
7
    jmp    erh        ;festlegen
8
    .ORG  $2A        ;restliche Interrupts überspringen
9
start:   ldi  akku,LOW(RAMEND)  ;Stapel initialisieren
10
    out  SPL,akku
11
    ldi akku,HIGH(RAMEND)
12
    out SPH,akku
13
    ldi akku,$ff      ;Register ff
14
    out DDRB,akku      ;PortD als Ausgang
15
    ldi r16,0b01110001    ;Timer initialiseiren
16
    out TCCR0,r16      ;ohne Prescaling, FC-PWM
17
    ldi r16,0x01      ;Overflow-Interrupt enablen
18
    out TIMSK,r16      ;
19
    ldi r16,0x50      ;Beliebigen Startwert laden
20
    out OCR0,r16      ;außer 00 und ff
21
    sei            ;Interrupts an
22
loop:  rjmp loop        ;Arbeitsschleife tut nix
23
erh:  inc r16          ;ISR: r16 erhöhen
24
    out OCR0,r16      ;raus damit
25
    reti          ;und zurück
26
    .EXIT

warum funktioniert das nicht?

Gibt es eine Möglichkeit, an diesen Top-Zustand ranzukommen, um eben OCR 
upzudaten, wanns genau sein muss?

Es wäre auch nett, wenn ihr mir sagen könntet, mit welchen Werten ich 
die Register laden muss, um einen sichtbaren Dimmvorgang erzeugen zu 
können.

von ozo (Gast)


Lesenswert?

Frohe Kunde:

The OCR0 Register is double buffered when using any of the Pulse Width 
Modulation(PWM) modes. For the normal and Clear Timer on Compare (CTC) 
modes of operation,the double buffering is disabled. The double 
buffering synchronizes the update of the OCR0 Compare Register to either 
top or bottom of the counting sequence. The synchro-nization prevents 
the occurrence of odd-length, non-symmetrical PWM pulses, thereby making 
the output glitch-free.The OCR0 Register access may seem complex, but 
this is not case. When the double buffering is enabled, the CPU has 
access to the OCR0 Buffer Register, and if double buffering is disabled 
the CPU will access the OCR0 directly.


Also einfach reinschreiben, Wert wird erst bei TOP übernommen!

von Florian G. (suffix)


Lesenswert?

aha. wunderbar dann mach ich das mal.

könnt ihr mir noch sagen, warum dieser blöde Interrupt nicht 
funktioniert?

von ozo (Gast)


Lesenswert?

Nee, sorry. keine Ahnung.
Sowohl "Timer/Counter0 Output Compare Match Interrupt" als auch 
"Timer/Counter0 Overflow Interrupt" sollten eigentlich funktionieren...


von Florian G. (suffix)


Lesenswert?

Mit dem Overflow Interrupt funktioniert das zwar, aber nicht wirklich 
zufriedenstellend.

Der updatet das dann halt bei bottom und eben nicht bei Top, würde der 
andere Interrupt gehen, wäre das kein Problem.

Könnte jemand bitte meinen Quelltext mal im Simulator ausprobieren und 
mir dann sagen, warum der Interrupt nicht ausgelöst wird?

von ozo (Gast)


Lesenswert?

Also in dem von dir geposteten Code ist aber nur der Overflow an!

von Florian G. (suffix)


Lesenswert?

Jop. Und exakt der wird nicht ausgelöst. Der Compare funktioniert.

von Florian G. (suffix)


Lesenswert?

niemand noch eine ahnung`?

von Karl H. (kbuchegg)


Lesenswert?

> Könnte jemand bitte meinen Quelltext mal im Simulator ausprobieren und
> mir dann sagen, warum der Interrupt nicht ausgelöst wird?

Der Simulator hat so seine Schwierigkeiten mit den Timern.
Besonders in den PWM Modi funktioniert da bei weitem nicht
alles so wie es sein soll.

Du brauchst aber auch keinen Interrupt um OCR neu zu beschreiben.
Je nach eingestelltem PWM und Pin Umschaltmodus macht sich
das die CPU selbst, so dass ein korrekter Update erfolgt.

> Der updatet das dann halt bei bottom und eben nicht bei Top,
> würde der andere Interrupt gehen, wäre das kein Problem.

Selbst dann würde der Update erst bei Bottom erfolgen.
Das ist ja gerade der Sinn der Sache eines phase korrektem
PWM


von Florian G. (suffix)


Lesenswert?

Ja. Aber für einen Dimmvorgang muss ich nun mal das OCR Register von 0 
nach 255 bzw andersherum erhöhen, bzw erniedrigen.

Theorethisch kann man das klar ohne Interrupt machen, geht halt 
wahnsinnig Zeit drauf, in welcher der Controller außer für Interrupts 
nicht empfänglich ist.

Der Compare Match Interrupt funktioniert sowohl in AVR Studio als auch 
im Controller selbst. Der OV bei beiden nicht. Das mag ein Bug sein im 
studio, jedoch funktioniert der auch im Controller nicht.

In der Simulation wird der Timer auch immer pro Schritt um 2 verändert, 
sodass 00 übersprungen wird. Aber im Controller dürfte das doch nichzt 
passieren, oder?

von Karl H. (kbuchegg)


Lesenswert?

Florian Glaser wrote:
> Ja. Aber für einen Dimmvorgang muss ich nun mal das OCR Register von 0
> nach 255 bzw andersherum erhöhen, bzw erniedrigen.

Und?
Was hindert dich daran das zu tun?

>
> Theorethisch kann man das klar ohne Interrupt machen, geht halt
> wahnsinnig Zeit drauf, in welcher der Controller außer für Interrupts
> nicht empfänglich ist.

Quatsch.
Du weist dem OCR Register den Wert zu den du haben willst.
Fertig.
Mit welcher Frequenz willst du den die PWM von 0 bis 255
durchdimmen? Du wirst doch nicht bei jedem PWM Durchlauf
einen neuen OCR Wert setzen wollen, oder doch?

von Florian G. (suffix)


Lesenswert?

Okey, das versteh ich. Ich initialisiere also meinetwegegen mit 00 und 
weiße dem dann einfach einen neuen Wert zu, um ihn zu dimmen.

Korrekt?

Ich sehe nur nix vom dimmen. Selbst wenn ich den Timer mit Prescaler 
1024 (bei 4MHZ) betreibe und ihn dann auf zb 127 update, geht die LED 
einfach sprunghaft auf den Wert und dimmt nicht.

Wenn ich den Wert bei jedem Output Compare Interrupt um eins erhöhe 
sieht man selbst bei voller Taktfrequenz ein schnönes dimmen. Hat halt 
den Nachteil, dass dann andauernd ein Interrupt ausgelöst wird.

Praktisch funktioniert das schon. Ich habe im Controller ein 
I2C-Programm, dass nach dem Empfang mit statuskontrolle(ebenfalls 
Interrupt) das empfanenge Zeichen auf einem LCD ausgibt. Das kommt alles 
an, selbst während des Dimmvorgangs treten keinerlei sichtbaren 
Verzögerungen auf.

Aber das ist mir irgendwie zu unsicher. Wer weiß, was passiert, wenn es 
doch einmal dumm läuft.

Verstehst du mein Problem?

von Karl H. (kbuchegg)


Lesenswert?

Florian Glaser wrote:
> Okey, das versteh ich. Ich initialisiere also meinetwegegen mit 00 und
> weiße dem dann einfach einen neuen Wert zu, um ihn zu dimmen.
>
> Korrekt?
>
> Ich sehe nur nix vom dimmen. Selbst wenn ich den Timer mit Prescaler
> 1024 (bei 4MHZ) betreibe und ihn dann auf zb 127 update, geht die LED
> einfach sprunghaft auf den Wert und dimmt nicht.

Mal ganz langsam.
Unter dimmen einer LED versteht man wenn die LED nicht mit
voller Helligkeit brennt.

Was du noch zusätzlich willst, ist eine Rampe auf der die
LED zum gewünschten Wert ein-'fadet'. Das hat zunächst mal
nichts mit dimmen zu tun.

Da gibt es ganz banale Lösungen
* mittels delay in einer Schleife neue Werte setzen


  for( i = 0; i < 127; i += 2 ) {
    OCR = i;
    _delay_ms( 10 );
  }

  alle 10 Millisekunden wird ein neuer OCR Wert gesetzt.
  Macht bei einer Schrittweite von 2  127 / 2 = 63 * 10 = 630
  Millisekunden, also etwas länger als eine halbe Sekunde.

Nun ist delay natürlich so eine Sache. Wenn der Prozessor
in der Zwischenzeit eigentlich etwas anderes tun sollte, dann
möchte man ihn natürlich nicht in einer Schleife hängen lassen.
Dann nimmt man einen weiteren Timer der einem eine 1 ms oder
5 ms oder 10 ms Zeitbasis erzeugt. Sowas braucht man sowieso
in den meisten Fällen in jedem Programm. Und in dessen
Interrupt Behandlung, die zb. alle 1 ms aufgerufen wird, wird
der OCR Wert wieder ein Stück näher an seinen Zielwert
gesetzt. Neben all den anderen Dingen, die alle 1 ms getan
werden müssen.

uint8_t FadeTo;

ISR( TIMER0_OVF_vect)
{
  if( OCR < FadeTo )
    OCR++;
  if( OCR > FaceTo )
    OCR--;
}

Du setzt FadeTo auf den Wert den die LED haben soll und
der regelmäßige Interrupt kümmert sich im Hintergrund darum,
dass der Dimmer/Timer schön langsam an diesen Wert herangeführt
wird. Wenn es dir zu langsam ist:
  * Entweder den Timer öfter feuern lassen
  * oder OCR nicht um 1 sondern um 2 oder 3 erhöhen/erniedrigen

> Wer weiß, was passiert, wenn es doch einmal dumm läuft.

Mach dir da mal keine grossen Sorgen. Der µC ist schnell
genug.




von Florian G. (suffix)


Lesenswert?

Wunderbar. Die Idee mitdem 2ten Timer ist wunderbar ;) Das muss ichn ur 
noch in ASM portieren, aber das ist gar kein Problem. Auch wenn dann ein 
Timer draufgeht.

Aber kannst du mir noch sagem, warum dieser sch*** Overflow Interrupt 
nicht geht? Das wäre doch teilweise zuuuuuuu praktisch.

von Hannes L. (hannes)


Lesenswert?

Florian Glaser wrote:
> Wunderbar. Die Idee mitdem 2ten Timer ist wunderbar ;) Das muss ichn ur
> noch in ASM portieren, aber das ist gar kein Problem. Auch wenn dann ein
> Timer draufgeht.
>
> Aber kannst du mir noch sagem, warum dieser sch*** Overflow Interrupt
> nicht geht? Das wäre doch teilweise zuuuuuuu praktisch.

Vermutlich, weil Du mit ihm schimpfst (sch***). Sei lieb zu ihm, frag 
das Datenblatt, was er gerne hätte, dann tut's er auch...

...

von Florian G. (suffix)


Lesenswert?

Hmpf. Naja, dann nehm ich mir in einer langweiligen Unterrichtsstunde 
mal die Timereinheit genau vor. Vielleicht finde ich ja den Bug mit 
Datenblatt. Wenn den sonst jemand hier findet, wärs super nett, wenn ers 
sagen könnte ;)

von Hannes L. (hannes)


Lesenswert?

Florian Glaser wrote:
> Hmpf. Naja, dann nehm ich mir in einer langweiligen Unterrichtsstunde
> mal die Timereinheit genau vor. Vielleicht finde ich ja den Bug mit
> Datenblatt. Wenn den sonst jemand hier findet, wärs super nett, wenn ers
> sagen könnte ;)

Es wäre nett, wenn Du statt dieses Bit-Kauderwelsches
1
    out  SPL,akku
2
    ldi akku,HIGH(RAMEND)
3
    out SPH,akku
4
    ldi akku,$ff      ;Register ff
5
    out DDRB,akku      ;PortD als Ausgang
6
    ldi r16,0b01110001    ;Timer initialiseiren
7
    out TCCR0,r16      ;ohne Prescaling, FC-PWM
8
    ldi r16,0x01      ;Overflow-Interrupt enablen
9
    out TIMSK,r16      ;
10
    ldi r16,0x50      ;Beliebigen Startwert laden
11
    out OCR0,r16      ;außer 00 und ff
12
    sei            ;Interrupts an

die Bits mit den im Datenblatt genannten Namen ansprechen würdest. Dann 
hättest Du vermutlich auch die Chance, dass jemand beim Drüberschaun 
einen Fehler findet. Wenn man aber erst das Datenblatt öffnen muss und 
sich die von Dir benutzten Bits dort zusammensuchen muss, dann ist die 
Motivation zum Helfen schnell dahin.

Wie gesagt, es wäre nett... - Du musst das aber nicht tun. Genausowenig 
muss ich aber um die Ecke recherchieren, was Du da initialisiert hast. 
Denn die Timer in meinen Programmen machen schon das, was sie sollen. 
Zwar nicht immer auf Anhieb, aber dann half das Datenblatt sehr schnell.

Nette Grüße,
Hannes

von Florian G. (suffix)


Lesenswert?

jawohl meister, kommt sofort ;)

von Florian G. (suffix)


Lesenswert?

So, hier der bessere Quelltext:
1
    .INCLUDE "m16def.inc" ;Baustein
2
    .EQU takt = 1000000   ;Takt
3
    .DEF akku = r16      ;Arbeitsregister
4
    .CSEG          ;Flash-Speicher verwenden
5
    rjmp  start
6
    .ORG  OVF0addr    ;Overflow-Adresse
7
    jmp    erh        ;festlegen
8
    .ORG  $2A        ;restliche Interrupts überspringen
9
start:   ldi  akku,LOW(RAMEND)  ;Stapel initialisieren
10
    out  SPL,akku
11
    ldi akku,HIGH(RAMEND)
12
    out SPH,akku
13
    ldi akku,$ff      ;Register ff
14
    out DDRB,akku      ;PortD als Ausgang
15
    ldi   r16,(1 << WGM00) | (1 << COM01) | (1 << COM00) | (1 << CS00)  ;Timer initialiseiren
16
    out   TCCR0,r16      ;ohne Prescaling, FC-PWM
17
    ldi   r16,(1 << TOIE0)  ;Overflow-Interrupt enablen
18
    out    TIMSK,r16      ;
19
    ldi   r18,0xff      ;Beliebigen Startwert laden
20
    out   OCR0,r18      ;außer 00 und ff
21
    sei              ;Interrupts setzen
22
loop:  rjmp loop        ;Arbeitsschleife tut nix
23
erh:  inc r16          ;ISR: r16 erhöhen
24
    out OCR0,r16      ;raus damit
25
    reti          ;und zurück
26
    .EXIT

so besser l(e/ö)sbar?

von Florian G. (suffix)


Lesenswert?

irgendjemand das problem gesehen?

von spess53 (Gast)


Lesenswert?

Hi

Wo kommt der Wert in r16 her, den du in der Interruptroutine
erhöhst? Dort fehlt 'in r16,OCR0'.
Ausserdem solltest du dir angewöhnen in Interruptroutinen das
SREG zu sichern. In deinem Programm stört das zwar im Moment
nicht. Aber wenn du in 'Loop' noch was anderes machen willst
bekommst du Probleme.

MfG Spess

von Florian G. (suffix)


Lesenswert?

Das fehlt natürlich, genauso wie das SREG. Aber in dem Programm macht 
das nichts, da ändert niemand was an r16.

Das eigentliche Problem, dass der overflow interrupt nicht ausgelöst 
wird, ist damit leider immer noch nicht gelöst :(

von Hannes L. (hannes)


Lesenswert?

Florian Glaser wrote:

> jawohl meister, kommt sofort ;)

> so besser l(e/ö)sbar?

wäre eigentlich ein Grund, sich rauszuhalten.

> irgendjemand das problem gesehen?

Ja sicher doch...

> Das eigentliche Problem, dass der overflow interrupt nicht ausgelöst
> wird, ist damit leider immer noch nicht gelöst :(

Nagut, dann will ich mal nicht aso sein. Ich habe es zwar nicht 
ausprobiert, aber es wäre eine logische Erklärung:

Bist Du sicher, dass Dein Timer jeweils überläuft?

Du hast mit WGM Phase-correct-PWM eingestellt, das heißt, Dein Timer 
legt oben den Rückwärtsgang ein und rattert wieder runter. Das von Dir 
erwartete Überlauf-Ereignis bleibt also aus.

Wie gesagt, ich habe es nicht ausprobiert. Ich käme auch nicht auf die 
Idee, den Timer hoch und runter laufen zu lassen und dann einen Überlauf 
zu erwarten.

...

von spess53 (Gast)


Lesenswert?

Hi

Scheint ein Simulatorproblem zu sein.
Ich habe gerade mal das Programm mit einem ATMega16 und JTAG getestet.
Dort wird der Overflowinterrupt angesprungen.

MfG Spess

von Florian G. (suffix)


Lesenswert?

echt? merkwürdig, bei meinem nicht.

und @hannes: das waren 2 späßchen, meister und das andere war in 
keinster weise irgendwie veräppelnd gemeint, verstehe mich da bitte 
nicht falsch.

Ich versuch das heute abend gleich nochmal!

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.