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
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.
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..
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.
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?
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
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 :(
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.
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
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
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.
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
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.
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
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
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!
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
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
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.