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
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)
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.
#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.. ^^
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)
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!
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 :(
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!
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
>> 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.