Forum: Mikrocontroller und Digitale Elektronik AVR Timer Fehler


von Basil V. (keine_angaben)


Lesenswert?

Seit gegrüsst Liebe Forum-Gemeinschaft

Ich will ein Programm entwerfen das 3 PWM`s raus gibt. Die Frequenz 
sollte bei allen ca. 100 Hz betragen. Das Tastverhältnis muss bei allen 
drei variabel sein.
Ich arbeite mit dem AT90PWM3B.
Ich habe mir das Programm so vorgestellt das ich einen 16 Bit Timer von 
0 bis 1024 (10bit) laufen lasse. Dies mache ich im Fast PWM Modus (Modus 
7) so das er immer von Bottom nach Top zählt und danach wieder bei 
Bottom beginnt. Mit den 3 variablen Interrupts das Tastverhältnis 
einstellen und bei Top alle wieder auf 0 setzten.

Ich habe noch nie mit Atmel gearbeitet ebenso habe ich noch nie einen 
Timer gebraucht. Darum habe ich mich mit diversen Tutorials vertraut mit 
dem Thema gemacht.
Mein Problem:
Irgendwas habe ich bei der Initialisierung falsch gemacht resp. das 
Datenblatt falsch interpretiert oder etwas übersehen. Denn der Timer 
zählt nicht hoch oder er setzt die Flags nicht. Nur das Flag OCF1A ist 
immer auf 1 aber dies schon von Anfang an. Die anderen 3 Flags sind nie 
auf 1.

Ich habe etliche Kombinationen der Register Beschreibung ausprobiert und 
das Datenblatt komplett auseinander genommen. Und langsam aber sicher 
bin ich mit meinem Latein am Ende :(


Code:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <util/delay.h>

#define MCU = AVR_AT90PWM3B
#define F_CPU 4000000UL
#define PWM_out PORTC
#define PRTIM0 = 0;
#define PRTIM1 = 0;
int main(void)
{
  DDRC = 0xFF;
  TCCR1A = 0b11110011;                //Flag Einstellungen --> setzten 
bei Compare Match, clear at Top , Modus auswählen (Bit0 und Bit1) --> 
Modus 7 = Fast PWM 10bit timer
  TCCR1B = 0b00001101;                //Einstellung Modus (Bit2 und 
Bit3), Prescaler einstellen --> clk/1024
  TCCR1C = 0b00000000;                //Bit6 und Bit7 nur auf 1 setzten 
wenn non PWM modus so wie ich das verstehe.. ^^
  TIMSK1 = 0b00100111;                //Interupts aktivieren/erlauben
  OCR1AH = 0b00000000;                //Wert für OCF1A definieren
  OCR1AL = 0b11111111;                //Wert für OCF1A definieren
  OCR1BH = 0b00000000;                //Wert für OCF1B definieren
  OCR1BL = 0b00000000;                //Wert für OCF1B definieren
  ICR1H = 0b000000000;                //Wert für ICF1 definieren
  ICR1L = 0b00001111;                  //Wert für ICF1 definieren
  GTCCR = 0x00;                    //General Timer Control Register
  EICRA = 0x00;                    //Einstellungen für "äussere 
Interupts" ---> eigentlich nicht von Bedeutung
  EIMSK =  0x00;                    //Einstellungen für "äussere 
Interupts" ---> eigentlich nicht von Bedeutung
  EIFR = 0x00;                    //Flag Register für "äussere 
Interupts" ---> eigentlich nicht von Bedeutung


  TCNT1H = 0x00;                    //Timer Register (Hier zählt er 
hoch/runter)
  TCNT1L = 0x00;                    //Timer Register (Hier zählt er 
hoch/runter)
  TIFR1 = 0x00;                    //Flag Register Bit5 = ICF1, Bit2 = 
OCF1B, Bit1 = OCF1A, Bit0 = TOV1

  sei();

    while(1)
    {
    if( OCF1A == 1)
    {
      PWM_out = 0b00000001;
    }
    if(OCF1B == 1)
    {
      PWM_out = 0b00000010;
    }
    if(ICF1 == 1)
    {
      PWM_out = 0b00000100;
    }
    if(TOV1 == 1)
    {
      PWM_out = 0b00001000;
    }
  }
}

In diesem recht simplen Programm will ich nur Testen ob jemals eins der 
Flag gesetzt wird. Und dies ist eben nur das OCF1A. Dies wird aber auch 
nie gelöscht.

Wäre für jede Hilfe sehr Dankbar
Gruss

: Bearbeitet durch User
von Samuel C. (dragonsam)


Lesenswert?

1. Deine Debugging-Ausgabe ist etwas gruselig. Wenn alle Flags gesetzt 
sind rattert der Port immer wieder alle Zustände durch, das kannst du 
maximal mit einem Oszilloskop sauber anschauen. Benutze statt
1
PWM_out = 0bxxxxxxxx;
lieber
1
PWM_out |= 0bxxxxxxxx;

2. Was willst du mit den Interrupts machen? Die PWM läuft auch ohne dein 
Zutun.

von Christian K. (the_kirsch)


Lesenswert?

Die PWM Ausgänge werden direkt vom Timer angesteuert.

Jeder Timer kann zwei PWM-Ausgange ansteuern (Mit dem AT90PWM3B bin ich 
nicht vertraut, ob der mit einem Timer mehr kann.)


Also brauchst du 2 Timer.

Der Timer läuft dann einfach von 0 bis MAX. (FAST PWM Modus)
in zwei "timer compare match"-Register kannst du dann einen Wert 
reinschreiben

Ist der Zähler kleiner las der Wert ist der PWM-Ausgang 0 ist er größer 
dann der Ausgang 1.

Alternativ gibt es noch den Phasen-Korkten Modus, dann Zahlt der Timer 
immer von 0 auf Max und wieder auf 0 zurück, dann wider auf MAX uns..

von Samuel C. (dragonsam)


Lesenswert?

BTW: Mir fällt gerade auf, dass du die Bit-Namen der Timerregister mit 
Konstanten vergleichst. Diese sind jedoch selbst Konstanten. Auf 
Timer-Register muss wie auf Pin-Register zugegriffen werden.

von Basil V. (keine_angaben)


Lesenswert?

1. Ich überprüfe meine Schaltung mit einem Oszilloskop :P

2. Beim timer1 in meinem uC habe ich 4 Interrupts. OCF1A, OCF1A und ICF1 
sind doch Interrupts die ich einstellen kann. Also je ein Interrupt pro 
PWM. Und wenn der Timer bei max ist, ist TVO1 gesetzt und so kann ich 
die PWM`s wieder auf 0 setzten. Das sollte so doch Funktionieren oder 
mache ich ein Denkfehler?

Die Werte für die variablen Flags habe ich doch mit OCR1BH, OCR1BL 
gesetzt oder nicht?

3. Ich verstehe nicht genau was du meinst mit Pin Register abfrage... :/
kann ich die Flags nicht so abfragen?

von Samuel C. (dragonsam)


Lesenswert?

1. Ok, das ist schonmal gut ;)

2. Naja, ich verstehe nicht, was du mit den Interrupts bewirken willst. 
So wie ich verstanden habe willst du einfach nur 3 PWMs laufen lassen. 
Das machen die Timer ohne jedes zutun, du gibst ihnen das Duty-Cycle 
(indirekt) vor und die werkeln ganz allein. Nichts mit Interrupt.

3. Nein, du kannst die Flags nicht so abfragen. Um OCF1A abzufragen 
musst du schreiben:
1
if( TIFR1&(1<<OCF1A) ) {...}
Die jeweiligen Register stehen alle sauber im Datenblatt. (in diesem 
Fall Seite 128)

: Bearbeitet durch User
von Basil V. (keine_angaben)


Lesenswert?

Mein Programm steuert mehrer RGB LED`s an. Die sollen auf Knopfdruck die 
Farbe ändern können. Darum will ich auch die variablen Interrupts 
verwenden so kann ich je nach Knopfdruck einen anderen Wert in die 
Register lesen. Und somit die Tastverhältnisse ändern resp. die Farbe 
der LED`s.

Ich habe eure Tipps angewendet aber leider werden die Flags immer noch 
nicht gesetzt. Mit Ausnahme von dem OCF1A aber dies ist immer 1 :(

von Samuel C. (dragonsam)


Lesenswert?

Dann brauchst du trotzdem keine Interrupts von den Timern.

Zeig mal deinen aktuellen kompletten Code.

Und mach bitte das AVR-GCC-Tutorial durch, dir fehlen ein paar 
Grundlagen.

von Basil V. (keine_angaben)


Lesenswert?

Im Anhang ist mein momentaner Code.
1
 
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <stdio.h>
5
#include <util/delay.h>
6
7
#define MCU = AVR_AT90PWM3B
8
#define F_CPU 4000000UL
9
#define PWM_out PORTC
10
#define PRTIM0 = 0
11
#define PRTIM1 = 0
12
#define DDRC = 0xFF
13
#define TCCR1A = 0b11110010                //Flag Einstellungen --> setzten bei Compare Match, clear at Top , Modus auswählen (Bit0 und Bit1) --> Modus 7 = Fast PWM 10bit timer
14
#define TCCR1B = 0b00001101                //Einstellung Modus (Bit2 und Bit3), Prescaler einstellen --> clk/1024
15
#define TCCR1C = 0b00000000                //Bit6 und Bit7 nur auf 1 setzten wenn non PWM modus so wie ich das verstehe.. ^^
16
#define TIMSK1 = 0b00100111                //Interupts aktivieren/erlauben
17
#define OCR1AH = 0b00000000                //Wert für OCF1A definieren
18
#define OCR1AL = 0b11111111                //Wert für OCF1A definieren
19
#define OCR1BH = 0b00000000                //Wert für OCF1B definieren
20
#define OCR1BL = 0b00000000                //Wert für OCF1B definieren
21
#define ICR1H = 0b000000000                //Wert für ICF1 definieren
22
#define ICR1L = 0b00001111                  //Wert für ICF1 definieren
23
#define GTCCR = 0x00                    //General Timer Control Register
24
#define EICRA = 0x00                    //Einstellungen für "äussere Interupts" ---> eigentlich nicht von Bedeutung
25
#define EIMSK =  0x00                    //Einstellungen für "äussere Interupts" ---> eigentlich nicht von Bedeutung
26
#define IFR = 0x00                    //Flag Register für "äussere Interupts" ---> eigentlich nicht von Bedeutung
27
28
int main(void)
29
{
30
  TCNT1H = 0x00;                    //Timer Register (Hier zählt er hoch/runter)
31
  TCNT1L = 0x00;                    //Timer Register (Hier zählt er hoch/runter)
32
  TIFR1 = 0x00;                    //Flag Register Bit5 = ICF1, Bit2 = OCF1B, Bit1 = OCF1A, Bit0 = TOV1
33
34
  sei();                        //alle Interups erlauben (spezial Befehel, cli() währe das gegenteil --> alle löschen)
35
  
36
  while(1)
37
  {
38
    if( TIFR1&(1<<OCF1A) )
39
    {
40
      PWM_out |= 0b00000001;
41
      sei();
42
    }
43
    if( TIFR1&(1<<OCF1B) )
44
    {
45
      PWM_out |= 0b00000010;
46
      sei();
47
    }
48
    if( TIFR1&(1<<ICF1))
49
    {
50
      PWM_out |= 0b00000100;
51
      sei();
52
    }
53
    if( TIFR1&(1<<TOV1) )
54
    {
55
      PWM_out |= 0b00001000;
56
      sei();
57
    }
58
  }
59
}

: Bearbeitet durch User
von Christian K. (the_kirsch)


Lesenswert?

1
//Timer0:
2
//Fast PWM
3
//Clear OC0A on Compare Match, set OC0A at TOP
4
//Clear OC0B on Compare Match, set OC0B at TOP
5
TCCROA = (1 << COMOA1) | (0 << COM0A0) | (1 << COMOB1) | (0 << COM0B0) | (1 << WGM01) | (1 << WGM00);
6
TCCROB = (0 << WGM02) | (1 << CS02) | (0 << CS01) | (0 << CS00);
7
8
9
//Timer 1:
10
//Fast PWM, 8-bit
11
//Clear OC0A on Compare Match, set OC0A at TOP
12
//Normal port operation, OC1B disconnected.
13
TCCR1A = (1 << COM1A1) | (0 << COM1A0) | (0 << COMOB1) | (0 << COM0B0) | (0 << WGM01) | (1 << WGM00);
14
TCCR1B = (0 << WGM03) | (1 << WGM02) | (1 << CS12) | (0 << CS11) | (0 << CS10);
15
OC1AH = 0;

Danach laufen beide Timer immer von 0 bis 255 und wieder von 0 bis 255, 
usw.
Die PWMs liegen an folgenden Pins:

OC0A -> PD3
OC0B -> PE1
OC1A -> PD2

Die PWMs stellst du ein, indem du in die folgenden Register jeweils 
einen Wert zwischen 0 und 255 reinschreibst:

OC0A, OC0B, OC1AL

Die PWM-Frequenz ist bei Prescaller=256 dann 61Hz (F_CPU durch 
TimerTopWert durch Prescaller)

: Bearbeitet durch User
von Samuel C. (dragonsam)


Lesenswert?

1. Rück mal damit raus, was du mit dem Interrupt des Timers anfangen 
willst. Wie gesagt hat der PWM nichts zu tun.

2. Schau ins Datenblatt. Von deinen abgefragten Bits liegt nur OCF1A in 
TIFR1. Die anderen liegen in anderen Registern.

von spess53 (Gast)


Lesenswert?

Hi

>...
>#define OCR1AH = 0b00000000                //Wert für OCF1A definieren
>#define OCR1AL = 0b11111111                //Wert für OCF1A definieren
>#define OCR1BH = 0b00000000                //Wert für OCF1B definieren
>#define OCR1BL = 0b00000000                //Wert für OCF1B definieren
>#define ICR1H = 0b000000000                //Wert für ICF1 definieren
>...

Dir ist aber bewusst, das dies Bezeichner schon mit anderen Werten 
(Registeradresse) vorbelegt sind?

MfG Spess

von uwe (Gast)


Lesenswert?

Benutz doch lieber die Power Stage Controller vom AT90PWM3. Der normale 
timer hat nur 2 PWM Kanäle. Und lies mal das Datenblatt.
-OCF1A ist das PWM Alarmflag zum gleichnamigen OCR1A
-OCF1B ist das PWM Alarmflag zum gleichnamigen OCR1A
-ICF1 ist kein PWM Alarmregister sonderrn Input Capute Flag der Input 
Capture Einheit.

von Samuel C. (dragonsam)


Lesenswert?

Tu dir einen Gefallen und mach das AVR-GCC-Tutorial durch, sonst wird 
das hier nichts, ehrlich.

von Christian K. (the_kirsch)


Lesenswert?

Basil V. schrieb:
> #define MCU = AVR_AT90PWM3B
> #define F_CPU 4000000UL
> #define PWM_out PORTC
> #define PRTIM0 = 0
> #define PRTIM1 = 0
> #define DDRC = 0xFF
> #define TCCR1A = 0b11110010                //Flag Einstellungen -->
> setzten bei Compare Match, clear at Top , Modus auswählen (Bit0 und
> Bit1) --> Modus 7 = Fast PWM 10bit timer
> #define TCCR1B = 0b00001101                //Einstellung Modus (Bit2 und
> Bit3), Prescaler einstellen --> clk/1024
> #define TCCR1C = 0b00000000                //Bit6 und Bit7 nur auf 1
> setzten wenn non PWM modus so wie ich das verstehe.. ^^
> #define TIMSK1 = 0b00100111                //Interupts
> aktivieren/erlauben
> #define OCR1AH = 0b00000000                //Wert für OCF1A definieren
> #define OCR1AL = 0b11111111                //Wert für OCF1A definieren
> #define OCR1BH = 0b00000000                //Wert für OCF1B definieren
> #define OCR1BL = 0b00000000                //Wert für OCF1B definieren
> #define ICR1H = 0b000000000                //Wert für ICF1 definieren
> #define ICR1L = 0b00001111                  //Wert für ICF1 definieren
> #define GTCCR = 0x00                    //General Timer Control Register
> #define EICRA = 0x00                    //Einstellungen für "äussere
> Interupts" ---> eigentlich nicht von Bedeutung
> #define EIMSK =  0x00                    //Einstellungen für "äussere
> Interupts" ---> eigentlich nicht von Bedeutung
> #define IFR = 0x00                    //Flag Register für "äussere
> Interupts" ---> eigentlich nicht von Bedeutung

Dieser ganze part ist mist.
MCU und F_CPU sollte über deine Toolchain oder make script definiert 
werden, und nicht im Programm Code.

Bei allen anderen defines hast du wohl was falsches verstanden.
PRTIM0,DDRC, usw. sind Zeiger auf Speicheradessen die durch die 
<avr/io.h> definiert werden.
Die verwendest du wie normale Variablen. Also in die main- bsw. 
init-Funktion und dann einfach
DDRC = 0xFF;
kein #define davor!

: Bearbeitet durch User
von Basil V. (keine_angaben)


Lesenswert?

Ich habe das Tutorial gelesen und mein Programm umgestaltet. Mit dem 
Oszilloskop habe ich die Ausgänge OC0A, OC0B sowie OC1A gemessen. Aber 
da war immer noch nichts :(
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <stdio.h>
4
#include <util/delay.h>
5
#include <inttypes.h>
6
7
int main(void)
8
{
9
  sei();
10
  DDRC = 0xFF;
11
    
12
  TCCR0A = (1 << COM0A1) | (1 << COM0A0) | (1 << COM0B1) | (1 << COM0B0) | (1 << WGM01) | (1 << WGM00);
13
  TCCR0B = (0 << WGM02) | (1 << CS02) | (0 << CS01) | (1 << CS00);
14
  TIMSK0 = (OCIE0A << 1) | (OCIE0B << 1) | (TOV0 << 1);
15
16
  
17
  TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (0 << COM1B1) | (0 << COM1B0) | (0 << WGM11) | (1 << WGM10);
18
  TCCR1B = (0 << WGM13) | (1 << WGM12) | (1 << CS12) | (0 << CS11) | 
19
(1 <<CS10) | (0<<FOC0A) | (0<<FOC0B);
20
  TIMSK0 = (OCIE1A << 1) | (OCIE1B << 1) | (TOV1 << 1);
21
  
22
  OCR1A = 0x0F;
23
  OCR0A = 0x0F;
24
  OCR0B = 0x0F;
25
  
26
while(1)
27
  {
28
    sei();
29
  }
30
  
31
}

Danke für eure Rückmeldungen

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

1
  TIMSK0 = (OCIE0A << 1) | (OCIE0B << 1) | (TOV0 << 1);

man gibt keine Interrupts frei, für die man keine ISR hat!
Sowas wird mit einem Prozessorreset bestraft.

Du brauchst keine Interrupts, die PWM kann der Timer ganz alleine in 
Hardware erzeugen. Keine Interrupts benötigt - auch keine Interrupts 
freigeben!

von Karl H. (kbuchegg)


Lesenswert?

grundsätzlich daher:
1
int main(void)
2
{
3
  DDRC = 0xFF;
4
    
5
  TCCR0A = (1 << COM0A1) | (1 << COM0A0) | (1 << COM0B1) | (1 << COM0B0) | (1 << WGM01) | (1 << WGM00);
6
  TCCR0B = (0 << WGM02) | (1 << CS02) | (0 << CS01) | (1 << CS00);
7
8
  
9
  TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (0 << COM1B1) | (0 << COM1B0) | (0 << WGM11) | (1 << WGM10);
10
  TCCR1B = (0 << WGM13) | (1 << WGM12) | (1 << CS12) | (0 << CS11) | 
11
(1 <<CS10) | (0<<FOC0A) | (0<<FOC0B);
12
  
13
  OCR1A = 0x0F;
14
  OCR0A = 0x0F;
15
  OCR0B = 0x0F;
16
  
17
  while(1)
18
  {
19
  }
20
  
21
}

Das allerdings
1
  TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (0 << COM1B1) | (0 << COM1B0) ...

muss ich noch mal im Datenblatt nachschlagen. Kein einziges COM Bit 
gesetzt kommt mir komisch vor.

: Bearbeitet durch User
von spess53 (Gast)


Lesenswert?

Hi

>Kein einziges COM Bit gesetzt kommt mir komisch vor.

Ist dann disconnected.

@ Basil

Die OC-Pins müssen auch als Ausgang geschaltet. Sonst rührt sich nichts.

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

> Das allerdings
>
1
>   TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (0 << COM1B1) | (0 << COM1B0) 
2
> ...
3
>
>
> muss ich noch mal im Datenblatt nachschlagen. Kein einziges COM Bit
> gesetzt kommt mir komisch vor.

Jup. Ist es auch.
Ist auch bei diesem AVR nicht anders als bei allen anderen.
Beide COM Bits auf 0 bedeutet 'normal Port Operation'.
du musst mindestens eines (oder beide, je nach gewünschtem Modus) in 
Übereinstimmung mit Tabelle 15.3 (Seite 125) setzen.
http://www.atmel.com/Images/Atmel-4317-8-bit-AVR-Flash_Microcontroller-AT90PWM2-3-2B-3B_datasheet.pdf

von Karl H. (kbuchegg)


Lesenswert?

spess53 schrieb:
> Hi
>
>>Kein einziges COM Bit gesetzt kommt mir komisch vor.
>
> Ist dann disconnected.
>
> @ Basil
>
> Die OC-Pins müssen auch als Ausgang geschaltet. Sonst rührt sich nichts.

Das war das nächste was ich kontrollieren wollte.

OC0A ist am Pin PD3
OC0B ist am Pin PE1
OC1A ist am Pin PD2

damit ist
1
  DDRC = 0xFF;

völlig falsch.
die genannten Pins am Port D bzw. Port E müssen auf Ausgang geschaltet 
werden.

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.