PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : PWM ansteuern / programmieren



orko512
22.03.2007, 21:31
ich habe da mal einige fragen zu pwm und hoffe das mir jemand helfen kann.

1.
ich habe einen atmega128 und habe gelesen das er 6 pwm ports hat.

- nun kann ich aber nirgends finden welche ports das sind

- ich kann nichts darüber finden wie man dann das pwn aktiviert und
entsprechend programmiert

2.
wie kann man pwm softwaretechnisch realisieren. man findet zwar an mehreren stellen code-auszüge doch leider immer nicht ganz vollständig



ich wäre echt dankbar wenn mir jemand weiterhelfen könnte.
vielleicht hat sogar jemand ein code-beispiel. in c.

es muss doch jemand geben der seine motoren über pwm in C ansteuert.

gruß orko

jar
22.03.2007, 22:14
2.
wie kann man pwm softwaretechnisch realisieren. man findet zwar an mehreren stellen code-auszüge doch leider immer nicht ganz vollständig
gruß orko

hab nur eine LED Steuerung in HW PWM

RCO
22.03.2007, 23:22
Er hat sogar 8 PWM-Kanäle!
Wenn du mit Ports die Pins des Chips meinst sind das alle die, an denen OCxx dran steht. Steht für Output-Compare.

Zur Programmierung der PWM-Kanäle würde ich dir empfehlen die entsprechenden Seiten im Datenblatt zu sehen. Es kommt schließlich drauf an, welchen Kanal du mit welcher Frequenz und und welcher Auflösung ansteuern willst.

Hier ist ein relativ komplexes Beispiel einer Anwendung des PWM für einen Mega8. entscheidend ist das richtige Setzen der Register TCCR...


#include <avr/io.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include <avr/signal.h>

void pwminit(){
TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS10);
// WGM* = Fast PWM Mode 14
// CS10 = Prescaler = 1
TCCR1A = (1<<COM1A1) | (1<<COM1A0) | (1<<COM1B1) | (1<<COM1B0) | (1<<WGM11);
// COM* = OC1A/B sind aktiv, bei Comparematsch wird der Ausgang gesetzt bei TOP gelˆscht
// WGM* = ICR1 = Obergrenze
TIMSK = (1<<TOIE1) | (1<<OCIE1B);
// OCIE1B = Output Compare Interrupt Enable 1B
// TOIE1 = Timer Overflow Interrupt Enable
ICR1 = 200;
OCR1B = 100;
OCR1A = 0;
};

int main(void) {

pwminit();
SREG |= (1<<7);
DDRB = 0b00111111;
DDRC = 0b00111000;
DDRD = 0b11111110;
PORTB |= ((1<<1) | (1<<0));
for(;;){};
}

orko512
23.03.2007, 19:57
hi moritz,

danke für die hinweise.

ich werde mich mit den infos mal ans werk machen und versuchen meine motoren zum laufen zu bekommen.

gruß orko

RCO
24.03.2007, 11:00
Hi Orko,

wir können ja mal versuchen das zusammen hinzukriegen.
Also wen ich das richtig verstehe willst du den Hardware-PWM benutzen und keinen interrupt auslösen.
Dann müssten wir klären welche Auflösung 8...10 Bits oder variabel und welche Frequenz du benutzen willlst.

hier mal das Datenblatt:
http://www.atmel.com/dyn/resources/prod_documents/doc2467.pdf

Wenn du nichts dagegen hast nehmen wir einfach mal den Timer/Counter0. Der hat eine 8-Bit Auflösung.

OK?

orko512
30.03.2007, 12:14
hi rco,

danke für dein angebot.

würde ich gerne in anspruch nehmen.

Hier mal kurz ne beschreibung was ich eigemtlich machen will:

ich möchte meinen robi mit zwei lego-motoren fahren.
diese möchte ich über pwm ansteuern. ich habe inzwischen mal einige versuche mit software pwm gestartet. die laufen auch, aber sind im prinzip nur schleifen in denen ich hochzähle und den ausgang dann setze oder nicht setze.
alles ohne timer und interrupt.
läuft zwar ist aber auf dauer keine gute lösung.

nun habe ich gelesen das es auch hardware pwm gibt, die mein atmega128 unterstützt.

ich habe auch schon danach gegoogelt und auch einige infos gefunden.
leider verstehe ich es trotzdem nicht wie das ganze abläuft.
in den meisten beispielen werden dann irgendwelche register gesetzt.
wofür diese dann jeweils sind, steht leider meist nicht dabei.
klar könnte ich mir ein beispiel nehmen und solange rumdocktorn bis es läuft, ich würde aber gerne verstehen was ich da mache und nicht einfach nur kopieren.

also wenn du lust hättest einen anfänger (der auch auf anhieb nicht immer gleich alles versteht) zu helfen würde ich mich echt freuen.

gruß orko

RCO
30.03.2007, 13:18
Hi Orko,

also wenn du zwei Motoren ansteuern willst, dann verwenden wir nicht Timer0, da der nur einen Ausgang hat.
Das siehst du im Datenblatt auf Seite 2 (OC0).

Wir verwenden besser Timer1, dann brauchen wir nur einen Timer.
Wir hätten sogar 3 (OC1A-OC1C) zur Verfügung.

Der Vorteil bei der PWM-Ansteuerung eines Motors ist, dass wir in Sachen Frequenz und Genauigkeit nicht festgelegt sind.

Im folgenden werden wir uns jetzt durchs Datenblatt und durch die Register arbeiten. (Ich hoffe, dein Englisch ist gut ;-))

Ab Seite 111 wird es also interessant. Wobei, da am Anfang erstmal sehr viel über die prinzipielle Funktionsweise geht. Danach noch etwas Assembeler etc. Für uns erstmal nicht interessant. Wenn du Lust hast, kanst du dich da ja mal durcharbeiten.

Der Vorteil des des Hardware-PWMs ist ja, dass außer der Initialisierung keine Rechenzeit verschwendet wird. das ganze läuft vollkommen im Hintergrund.

Auf Seite 135 findest du jetzt erstmal einen Überblick über die möglichen Einstellungen der Timer.
Da wir keine besonderen Ansprüche an den PWM haben, würde ich vorschlagen, dass wir ihn im Mode 5,6 oder 7 betreiben, je nachdem, welche Auflösung du haben willst. Das ist der Fast PWM. Der ist am einfachsten zu erklären.

Was heißt das jetzt:
Der Counter1 zählt mit einer Frequenz, die wir später festlegen, bis zu einer Grenze (entweder 256, 512 oder 1024). Wobei, wenn der Counter bei 256, 512, 1024 ankommt wieder auf Null springt. Das ist ein einfacher Überlauf. Jetzt haben wir 3 Register, in die wir Werte schreiben. Wenn der Counter1 auf einen Zahl = einem der 3 Register kommt, wird ein Ausgang gesetzt oder gelöscht, je nachdem wie wir das noch einstellen.
Der Counter1 Zählt also einfach und vergleicht, ob der Wert, an dem ein Ausgang gesetzt werden soll, schon erreicht ist. Kommt der Counter an seine Obergrenze, so setzt oder löscht er den Zustand des Ausgangs automatisch.

Schau dir am besten mal die Seiten 124 und 125 dazu an.

Timer und PWM beruhen nur auf einem Counter, der immer nach einer bestimmten Zeit seinen Wert inkrementiert.

Bei welcher Frequenz betreibst du den AVR eigentlich?

Ist soweit alles klar? Kommst du mit, sonst stell Fragen.
Wie du siehst, kommst du um das Datenblatt nicht herum.

orko512
31.03.2007, 19:59
hi rco,

super von dir. ist ja wie ein online-kurs.

ich fange gleich mal an zu fragen.
sollte ich irgendwo einen fehler machen bitte gleich anmarkern.

>> (Ich hoffe, dein Englisch ist gut)
ob mein englisch reicht???
sieht eher schlecht aus.

>>also wenn du zwei Motoren ansteuern willst, dann verwenden wir nicht
>>Timer0, da der nur einen Ausgang hat.
>>Das siehst du im Datenblatt auf Seite 2 (OC0).

verstehe ich das richtig:
wenn an den pins OC0 steht dann sind die für Timer0?
entsprechend für die anderen TimerX.

danach hätte ich einen
timer0 mit einen ausgang (oc0)
timer1 mit drei ausgängen (oc1a, oc1b, oc1c)
timer2 mit einem ausgang (oc2)
timer3 mit drei ausgängen (oc3a, oc3b, oc3c)

also insgesamt 4 timer.

>>Auf Seite 135 findest du jetzt erstmal einen Überblick über die
>>möglichen Einstellungen der Timer

buh... ganz schön viele infos.

>>Der Counter1 zählt bis zu einer Grenze (entweder 256, 512 oder 1024)

wenn ich das richtig verstehe ist das nun abhängig vom modus.
modus 5 = 8bit = 256
modus 6 = 9bit = 512
modus 7 = 10bit = 1024

in der tabelle61 auf seite 135 steht nun welche bits ich in den timer laden muss damit ich den entsprechenden modus setze.
richtig??????

für modus 7:
WGMn3 = 0
WGMn2(CTCn) = 1
WGMn1(PWMn1) = 1
WGMn0(PWMn0) = 1

das bedeutet = Fast PWM, 10-bit


wie sieht das jetzt softwaretechnisch aus??
ich möcht timer1 benutzen.
er soll in modus 7 betrieben werden.

genau hier hört es jetzt bei mir auf.
ich schaue in das datenblatt und komme nicht weter.
ich finde Timer/Counter1 Control und
Timer/Counter3 Control
gibt es denn mehrere??

nun gleich noch ne frage:
wenn ich mir so einige beispiele anschaue finde ich dort zum setzen des modus folgendes:
TCCR1A|= (1<<WGM10);
TCCR1A|= (1<<WGM11);
TCCR1B|= (1<<WGM12);
TCCR1B|= (1<<WGM13);

warum werden wgm10 und wgm 11 in TCCR1A gesetzt und
wgm12 und wgm13 in TCCR1B ?????
im datenblatt kann ich dazu nichts finden.


>>Wenn der Counter1 auf einen Zahl = einem der 3 Register kommt, wird >>ein Ausgang gesetzt oder gelöscht,
Ist das dann immer der ausgang der zu dem timer gehört???
in meinem fall (OC3A/AIN1) = PE3??

>>Bei welcher Frequenz betreibst du den AVR eigentlich?
mit 4 mhz;start-up time: 6 ck + 64ms
schon kommt die nächte frage:
wo ist der unterschied zwischen 6ck +0ms, 6ck +4ms und 6ck +64ms??


So ich glaube ich mache jetzt hier erstmal schluss und
freue mich schon auf eine antwort.
ich hoffe das du nicht verzweifelst wenn du meine fragen liest.

gruß und ein schönes wochenende

orko

RoboPunk
01.04.2007, 16:09
@RCO
Ich hätte auch eine Frage dazu:
ich benutze zwar das Mega2650, aber im Prinzip dürfte das ja das gleiche sein.

Wenn ich z.B. Timer1 nehme, wie kann ich einstellen, welcher Pin (A,B oder C) dann geschalten wird? und wie lege ich für diese Pins einen Vergleichswert fest?

Bene

RCO
03.04.2007, 13:33
verstehe ich das richtig:
wenn an den pins OC0 steht dann sind die für Timer0?
entsprechend für die anderen TimerX.

Genau.


danach hätte ich einen
timer0 mit einen ausgang (oc0)
timer1 mit drei ausgängen (oc1a, oc1b, oc1c)
timer2 mit einem ausgang (oc2)
timer3 mit drei ausgängen (oc3a, oc3b, oc3c)

also insgesamt 4 timer.

Genau, deshalb steht im Datenblatt auch 8 PWM-Channels.


wenn ich das richtig verstehe ist das nun abhängig vom modus.
modus 5 = 8bit = 256
modus 6 = 9bit = 512
modus 7 = 10bit = 1024

in der tabelle61 auf seite 135 steht nun welche bits ich in den timer laden muss damit ich den entsprechenden modus setze.
richtig??????

für modus 7:
WGMn3 = 0
WGMn2(CTCn) = 1
WGMn1(PWMn1) = 1
WGMn0(PWMn0) = 1

das bedeutet = Fast PWM, 10-bit

wie sieht das jetzt softwaretechnisch aus??
ich möcht timer1 benutzen.
er soll in modus 7 betrieben werden.


nun gleich noch ne frage:
wenn ich mir so einige beispiele anschaue finde ich dort zum setzen des modus folgendes:
TCCR1A|= (1<<WGM10);
TCCR1A|= (1<<WGM11);
TCCR1B|= (1<<WGM12);
TCCR1B|= (1<<WGM13);

Genau so gehts. Man kann es allerdings kompakter schreiben:
TCCR1A = (1<<WGM11) | (1<<WGM10);
TCCR1B = (1<<WGM12);


warum werden wgm10 und wgm 11 in TCCR1A gesetzt und
wgm12 und wgm13 in TCCR1B ?????
im datenblatt kann ich dazu nichts finden.

Warum? Keine Ahnung. Wo du was setzten muss, findest du aber im Datenblatt (Seite 133 und 136).
Es gibt auch noch eine Übersicht auf Seite 365ff.


>>Wenn der Counter1 auf einen Zahl = einem der 3 Register kommt, wird >>ein Ausgang gesetzt oder gelöscht,
Ist das dann immer der ausgang der zu dem timer gehört???
in meinem fall (OC3A/AIN1) = PE3??

Genau. Das passiert natürlich nur, wenn die Register so eingestellt sind, dass die Pins gesetzt werden sollen.
Vielleicht möchte man ja auch nur einen Interrupt auslösen und den PWM garnicht nutzen.
Was mit den Pins passiert, kann man für OC1A über COM1A1 und COM1A0 einstellen (siehe seite 134).
Das werden wir auch noch machen müssen.


ich schaue in das datenblatt und komme nicht weter.
ich finde Timer/Counter1 Control und
Timer/Counter3 Control
gibt es denn mehrere??

Timer1 und Timer3 werden quasi genau gleich angesteuert.
Das kleine n ist dann durch 1 oder 3 zu ersetzen.


>>Bei welcher Frequenz betreibst du den AVR eigentlich?
mit 4 mhz;start-up time: 6 ck + 64ms
schon kommt die nächte frage:
wo ist der unterschied zwischen 6ck +0ms, 6ck +4ms und 6ck +64ms??

Ich weiß leider nicht, was mit 6ck + 64ms gemeint ist. ck steht für cycles, aber sonst...
4Mhz ist das Entscheidende.

OK, wenn ich nichts übersehen habe, sollte das hier zur Initialisierung des Timers reichen:

TCCR1A = (1<<WGM11) | (1<<WGM10) | (1<<COM1A1) | (1<<COM1B1) | (1<<COM1A0) | (1<<COM1B0);
TCCR1B = (1<<WGM12) | (1<<CS11);

OCR1AH = 0;
OCR1AL = 128;
OCR1BH = 0;
OCR1BL = 128;

WGM setzt den entsprechenden Modus (10-Bit Fast-PWM), COM regelt das Setzen/Löschen der Pins:
"Set OCnA/OCnB/OCnC on compare match, clear OCnA/OCnB/OCnC at BOTTOM, (inverting mode)"

CS regelt den Prescaler. Hier ist nur CS11 gesetzt. Aus der Tabelle auf Seite 137 entnehmen mir nun,
dass CS11 bedeutet, dass der Takt durch 8 geteilt wird.
==> 4 Mhz / 8 = 500 kHz
Der Timer wird allso alle 1/500k s erhöht.
Da der Timer auf 10 Bit (1024) gesetzt ist, ergibt sich eine Frequenz von 500k/1024 Hz (=488,28... Hz).

Mit welcher Frequenz du einen Motor am besten ansteuert weiß ich leider nicht.

OCR setzt den Wert an dem der Ausgang gesetzt wird. Der "inverting mode" eignet sich eignetlich nicht so gut für deinen Zweck,
war aber einfacher als Erklärung.

OK, soweit alles klar? ;-)

@Bene: Ich hoffe, dass das was ich oben geschrieben hat dir weiterhilft.

orko512
03.04.2007, 16:16
hi rco,

danke für deine antwort.

ich werde jetzt mal versuchen das gelernte umzusetzen und in einem programm zum laufen zu bringen.

werde meine versuche hier mal posten (inkl. erfolg / nicht erfolg meldung)

gruß orko

orko512
03.04.2007, 17:20
hi,

ich habe mal versucht das mit dem pwm umzusetzen.
leider ohne erfolg.

der motor läuft zwar,
aber er läuft immer gleich egal wie ich die werte setze.
ich wollte mal eine langsam fahrt machen, bekomme ich nicht hin.
hmm...
irgendwo habe ich noch einen fehler.

hier kommt mein code:

void main (void)
{
pwminit();
SREG |= (1<<7);
DDRB = 0b11111111;

PORTB |= ((1<<5) | (1<<6));
for( ; ; ){};
}

void pwminit()
{


// Nachfolgend wird Modus 14 gesetzt fast pwm top = ICRn
TCCR1A = (1<<WGM11);
TCCR1B = (1<<WGM13) | (1<<WGM12);

//Nachfolgend wird Prescaler clk/1024 gesetzt
TCCR1B = (1<<CS10) | (1<<CS12);

// COM* = OC1A/B sind aktiv, bei Comparematsch wird der Ausgang gesetzt bei TOP geloescht
TCCR1A = (1<<COM1A1) | (1<<COM1A0) | (1<<COM1B1) | (1<<COM1B0);

// OCIE1B = Output Compare Interrupt Enable 1B
// TOIE1 = Timer Overflow Interrupt Enable
TIMSK = (1<<TOIE1) | (1<<OCIE1B);

// ICR1 = Obergrenze
ICR1 = 200;
OCR1AH = 0;
OCR1AL = 10;
OCR1BH = 0;
OCR1BL = 10;
}


was macht eigentlich de befehl SREG |= (1<<7); genau???

gruß orko

RCO
03.04.2007, 17:33
Hi Okro,

SREG aktiviert nur den Globalen Interrupt, das brauchst du hier nicht, da kein Interrupt ausgelöst wird.
ICR1 ist mMn garnicht nötig. Auch TIMSK bracuhst du eigentlich nicht, dass ist auch nur für Interrupts.
Wenn du die Register setzt musst du immer | schreiben, sonst werden nur die nachfolgenden Werte gesetzt und alle anderen gelöscht.

Z.B.:
TCCR1A = (1<<COM1A1);
löscht alle gesetzten Bits und setzt nur COM1A1.
(Schau mal wie ich das oben gamacht habe.

>>TCCR1B = (1<<CS10) | (1<<CS12);
löscht das was du darüber gesagt hast.

Es kann sein, dass du auch noch einiges einbinden musst, sowas wie io.h, aber da bin ich nicht sicher.

orko512
03.04.2007, 17:49
hi,

ich habe nun mein code angepasst und deine änderungen eingebaut.

sieht wie folgt aus:


void pwm_main (void)
{
pwminit();
DDRB = 0b00111111;

PORTB |= ((1<<5) | (1<<6));
for( ; ; ){};
}


void pwminit()
{

TCCR1A = (1<<WGM11) | (1<<COM1A1) | (1<<COM1A0) | (1<<COM1B1) | (1<<COM1B0);
TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS10) | (1<<CS12);

OCR1AH = 0;
OCR1AL = 1;
OCR1BH = 0;
OCR1BL = 1;
}


jetzt läuft nichts mehr.

ich verstehe es einfach nicht

RCO
03.04.2007, 18:00
Hi Orko,

Sorry, ich habe übersehen, dass du gar nicht mehr Modus 7 benutzt.
Dann kannst du natürlich ICR... nicht löschen.

Also probier folgendes mal aus:


#include <avr/io.h>

void pwminit()
{
TCCR1A = (1<<WGM11) | (1<<WGM10) | (1<<COM1A1) | (1<<COM1B1) | (1<<COM1A0) | (1<<COM1B0);
TCCR1B = (1<<WGM12) | (1<<CS11);

OCR1AH = 1;
OCR1AL = 1;
OCR1BH = 1;
OCR1BL = 1;
}

void main (void)
{
DDRB = 0b11111111;
pwminit();
for( ; ; ){};
}

Ich habe leider gerade keinen Compiler und keinen AVR zu Hand, daher ist es für mich im Moment schwer zu überprüfen, ob ich ncih irgendwas vergessen habe.

orko512
03.04.2007, 18:38
jo,

das sieht schon besser aus. :)

der motor läuft jetzt schön langsam vor sich hin.

erstmal danke.

nun noch eine frage:

ich habe nun den modus 7, er zählt bis 1024.

OCR1AH = 1; -> wird hier der ausgang auf high gesetz?
OCR1AL = 1; -> wird der ausgang auf low gesetzt?

warum sind beide gleich??

gruß orko

RCO
03.04.2007, 18:57
jo,

das sieht schon besser aus. :)

der motor läuft jetzt schön langsam vor sich hin.

Sehr gut!


nun noch eine frage:

ich habe nun den modus 7, er zählt bis 1024.

OCR1AH = 1; -> wird hier der ausgang auf high gesetz?
OCR1AL = 1; -> wird der ausgang auf low gesetzt?

warum sind beide gleich??

Nein, das meint es nicht. H und L steht für High- und Lowbyte.
Die Auflösung ist ja 10 Bit. Ein Byte hat nur 8 Bit. Deshalb gibt es zwei Bytes um diese Werte auszudrücken.

OCR1AH = 1;
OCR1AL = 1;

heit also 1x256 und 1x1, also der Wert 257.

Willst du z.B. den Wert 800 setzen, dann ist das:
OCR1AH = 3;
OCR1AL = 32;

Das hieße dann, dass der Ausgang 1 - 800/1024 high ist.
(Oder auch nur 1023, bin mir da nicht ganz sicher)
Das 1 - ist wegen dem Inverting Mode.

Der Ausgang wird beim Inverting Mode beim Überlauf also 1024 zurückgesetzt.

orko512
03.04.2007, 19:09
hi rco,

super lieben dank.

habe durch deine hilfe viel gelernt.


ich bin jetzt soweit, dass ich jetzt mit den richtigen geschwindigkeiten experimentiere.

damit habe ich jetzt erstmal was zu tun.

wenn alles so läuft wie gewünscht, werde ich mich mal melden.
als nächstes ziel habe ich mir dann die programmsteuerrung über interrupts vorgenommen.

schönen abend noch

gruß orko