PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : PWM 2 kleine Probleme.... bitte um Hilfe



Kaiser-F
25.02.2005, 15:55
Hallo,

Ich weiß dass die Fragen zu PWM für Profis sehr nervig sind,

Leider sind für PWM-Anwendungen fast keine "Übungsbeispiele" in C im Netz zu finden.

Nun zu meinen Problemchen....


Soweit ich mir das erarbeitet habe, müsste folgender Code funktionieren




// PWM01 mit ATmega8535 mit 8MHz

#include <io.h>

int main (void)
{

TCCR1A = (1<<PWM11)|(1<<PWM10)|(1<<COM1A1);
TCCR1B = (1<<CS12) | (1<<CS10);

for (;;) {

OCR1A = x; // x ist jetzt mal ein Beispielwert,....

}}


Erklärung (bitte verbessern falls ich mich irre)

TCCR1A = (1<<PWM11)|(1<<PWM10)|(1<<COM1A1);
Befehl für nicht invertierenden 10-Bit PWM.

TCCR1B = (1<<CS12) | (1<<CS10);
Takt von CK / 1024 ( hier ergibt sich dann Frage 2 )

OCR1A = xxx;
Wert....


nunja, wenn ich das ganze nun "HEXen" will, dann kommt die Meldung:

"error: "PWM11" undeclared (first use in this funktion)"
"error: "PWM10" undeclared (first use in this funktion)"


Muss ich vielleicht noch ne andere Datei "includen" ausser io.h ?
Oder woran könnte das liegen.... habe die codes aus:
http://www.mikrocontroller.net/wiki/AVR-GCC-Tutorial#PWM_.28Pulsweitenmodulation.29

Frage 2:

TCCR1B = (1<<CS12) | (1<<CS10);
Takt von CK / 1024

Ich will letzendlich ein Modellbauservo ansteuern, welches ja einen takt von ca. 50Hz braucht.

aus CK / 1024, ergeben sich aber bei 8MHz ca. 7.8 kHz....

wie komme ich runter auf 50Hz ?

Soll ich "Externer Pin 1, positive Flanke" nutzen?

also
TCCR1B = (1<<CS12) | (1<<CS11) | (1<<CS10);
?
und dort einen Oszillator mit 50Hz dranhängen?


Ich bedanke mich schon mal im Vorfeld für eure Bemühungen,

Gruß
Kaiser F

muraad
25.02.2005, 19:22
Include mal noch zusätzlich die Headerdatei <avr/io8535.h>.
Und bei CK/Prescaler kommst du nur auf die Frequenz für einen Takt. Bei 10Bit sind das 2^10 also 1024Takte. Bei dir hast du bei 7.8kHz pro Takt insgesamt ein Frequenz von 7800Hz/1024=7.6Hz. Ist also schon zuwenig.
Hier gibt es ein kleines Windows Programm.
"Avr Timer-Berechnung
Dieses Tool berechnet die Timereinstellungen und erzeugt Quellcode damit eine vorgegebene Timerfrequenz erreicht wird. Jetzt mit Zusatz-Rechenfunktion für Spannungsteiler."
https://www.roboternetz.de/phpBB2/dload.php?action=category&cat_id=18
Gruß Muraad

Kaiser-F
25.02.2005, 20:19
Hallo,

das heisst also die resultierende Pulsfrequenz wird so berechnet:

µC-Takt / BIT / Vorzähler = Pulsfrequenz.

Dadurch ergäbe sich folgendes:




Takt BIT CK/x Hz: ms:

8000000 510 1 15686,27 0,06375
8000000 510 8 1960,78 0,51
8000000 510 64 245,10 4,08
8000000 510 256 61,27 16,32
8000000 510 1024 15,32 65,28
8000000 1022 1 7827,79 0,12775
8000000 1022 8 978,47 1,022
8000000 1022 64 122,31 8,176
8000000 1022 256 30,58 32,704
8000000 1022 1024 7,64 130,816
8000000 2046 1 3910,07 0,25575
8000000 2046 8 488,76 2,046
8000000 2046 64 61,09 16,368
8000000 2046 256 15,27 65,472
8000000 2046 1024 3,82 261,888

16000000 510 1 31372,55 0,031875
16000000 510 8 3921,57 0,255
16000000 510 64 490,20 2,04
16000000 510 256 122,55 8,16
16000000 510 1024 30,64 32,64
16000000 1022 1 15655,58 0,063875
16000000 1022 8 1956,95 0,511
16000000 1022 64 244,62 4,088
16000000 1022 256 61,15 16,352
16000000 1022 1024 15,29 65,408
16000000 2046 1 7820,14 0,127875
16000000 2046 8 977,52 1,023
16000000 2046 64 122,19 8,184
16000000 2046 256 30,55 32,736
16000000 2046 1024 7,64 130,944






Also Bräuchte ich bei 8MHz folgendes:

10BIT PWM mit 64er Vorzähler, also CK/64

ergäbe 61,09Hz = 16,368ms?




Die Fehlermeldung

"error: "PWM11" undeclared (first use in this funktion)"
"error: "PWM10" undeclared (first use in this funktion)"


Erscheint noch immer.... auch mit <avr/iom8535.h>.....


Wäre toll wenn mir jemand hier weiterhelfen könnte....
Sitze jetzt schon über zwei wochen an diesen Problem fest.


mfg

pebisoft
25.02.2005, 22:55
doch, hier ist ein beispiel:

#include <avr/io.h>

/* Diese ganze "Bibliothek" bezieht sich auf ATmega32. Es dürfte aber nicht schwer sein
sie für andere Atmels umzuschreiben */

/* Erstmal ein paar sehr nützliche #defines die ich zum ersten mal bei www.mc-project sah */
#define SETBIT(ADDRESS,BIT) (ADDRESS |= (1<<BIT)) // Setzt bit im gewünschten Register
#define CLEARBIT(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT)) // Löscht bit in ADDRESS
#define CHECKBIT(ADDRESS,BIT) (ADDRESS & (1<<BIT)) // Prüfft ob bit gesetzt ist

/*PWM-Teil
Hier kommen meine #defines und Funktionen und zur PWM Ausgabe mit Timer2(16Bit Timer)
Beim ATmega32 geschiet die PWM Ausgabe an PD4(OC1B) und PD5(OC1A). Mit den #defines stellt
man die ganze Voreinstellungen ein. Mit den Funtkionen pwmXbit() kann man dann das PWM
Ausgangssignal steuern. Mit pwm_index setzt man fest an welchen Ausgang. limit_range
unterteil den PWM Bereich in 0.0-100.0 Das ganze sieht dann so aus
z.B pwm9bit(35.0,1) damit ist PWM Ausgabe an Ausgang 2 wobei im OutputCompareRegister
35.0*327.68 = (int) 11468.8 steht
*/
#define PWMchannel_init DDRD= _BV(PD4) | _BV(PD5); // PWM Ports als ausgang deklarieren
#define PWM8bit_init TCCR1A |=_BV(WGM10) // Gewünschte PWM 8,9 oder eben 10Bit
#define PWM9bit_init TCCR1A |=_BV(WGM11)
#define PWM10bit_init TCCR1A = _BV(WGM10) | _BV(WGM11)
#define PWMdisable TCCR1A = ~_BV(WGM10) & ~_BV(WGM11) // Timer2 wieder normaler Timer
#define PWMnoCO1A TCCR1A = ~_BV(COM1A0) & ~_BV(COM1A1) // Kein PWM an Ausgang1
#define PWMnoCO1B TCCR1A = ~_BV(COM1B0) & ~_BV(COM1B1)
#define PWM1upcounting TCCR1A = _BV(COM1A0) | _BV(COM1A1) // invertierende PWM
#define PWM2upcounting TCCR1A = _BV(COM1B0) | _BV(COM1B1)
#define PWM1downcounting TCCR1A |= _BV(COM1A1) // nicht invertierend
#define PWM2downcounting TCCR1A |= _BV(COM1B1)
#define Timer2_prescaler_1 TCCR1B |= _BV(CS10) // verschiedene Prescaler
#define Timer2_prescaler_8 TCCR1B |= _BV(CS11)
#define Timer2_prescaler_64 TCCR1B = _BV(CS11) | _BV(CS10)
#define Timer2_prescaler_256 TCCR1B |= _BV(CS12)
#define Timer2_prescaler_1024 TCCR1B = _BV(CS12) | _BV(CS10)
#define Timer2_stop TCCR1B = ~_BV(CS12) & ~_BV(CS11) & ~_BV(CS10) // stopt Timer2

float limit_range(float val,float low,float high)
{
if(val<low) return low;
else if(val>high) return high;
else return val;
}

void pwm8bit(float vel, unsigned char pwm_index) // pwb_index 0 für Ausgang 1, 1 für Ausgang 2
{ // für 8Bit Pwm
vel = limit_range(vel,0.0,100.0);
if(pwm_index==0)
OCR1A= (int) (163.84*vel);
if(pwm_index==1)
OCR1B= (int) (163.84*vel);
}

void pwm9bit(float vel, unsigned char pwm_index) // pwb_index 0 für Ausgang 1, 1 für Ausgang 2
{
vel = limit_range(vel,0.0,100.0);
if(pwm_index==0)
OCR1A= (int) (327.68*vel);
if(pwm_index==1)
OCR1B= (int) (327.68*vel);
}

void pwm10bit(float vel, unsigned char pwm_index) // pwb_index 0 für Ausgang 1, 1 für Ausgang 2
{
vel = limit_range(vel,0.0,100.0);
if(pwm_index==0)
OCR1A= (int) (655.36*vel);
if(pwm_index==1)
OCR1B= (int) (655.36*vel);
}

/* Analog/Digiatl konverting */
#define ADCchannel_init DDRA=0x00 // ADC Port als Eingang deklarieren
#define ADCinit ADCSRA|=_BV(ADEN) // Teilt dem Board mit das der jeweilige Port für ADC verwendet wird
#define ADCdisable ADCSRA &=~_BV(ADEN) // machs das vorherige wieder rückgänig
#define ADCstart ADCSRA|=_BV(ADSC) // startet eine konvertierung auf dem gewünschten Kannal/Pin
#define ADCfree ADCSRA|=_BV(ADATE) // schaltet den freilaufenden Modus ein
#define ADCvintern ADMUX|=_BV(REFS0) // interne Spannungsversorgung
#define ADCinterrupt_on ADCSRA|=_BV(ADIE) // ADC interrupt wird freigeschalten
#define ADCprescaler_2 ADCSRA |=_BV(ADPS0) // gewünschter Teilungsfaktor/Prescaler
#define ADCprescaler_4 ADCSRA|=_BV(ADPS1)
#define ADCprescaler_8 ADCSRA=_BV(ADPS1) | _BV(ADPS0)
#define ADCprescaler_16 ADCSRA|=_BV(ADPS2)
#define ADCprescaler_32 ADCSRA=_BV(ADPS2) | _BV(ADPS0)
#define ADCprescaler_64 ADCSRA=_BV(ADPS2) | _BV(ADPS1)
#define ADCprescaler_128 ADCSRA=_BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0)
#define ADCprescaler_reset ADCSRA = ~_BV(ADPS2) & ~_BV(ADPS1) & ~_BV(ADPS0)
#define ADCchannel_1 //gewünschter Kannal z.B bei ATmega32 PINA0 - PINA7
#define ADCchannel_2 ADMUX|=_BV(MUX0) // bei nicht freilaufen muss ADCchannel_x vor
#define ADCchannel_3 ADMUX|=_BV(MUX1) // ADCstart kommen dann kann man mit getadc() der
#define ADCchannel_4 ADMUX= _BV(MUX1) | _BV(MUX0) // Adcwert des gewählten Kannals auslesen
#define ADCchannel_5 ADMUX|=_BV(MUX2)
#define ADCchannel_6 ADMUX= _BV(MUX2) | _BV(MUX0)
#define ADCchannel_7 ADMUX= _BV(MUX2) | _BV(MUX1)
#define ADCchannel_8 ADMUX= _BV(MUX2) | _BV(MUX1) | _BV(MUX0)
#define ADCchannel_reset ADMUX= ~_BV(MUX2) & ~_BV(MUX1) & ~_BV(MUX0)
inline unsigned int getadc(void)
{
while (ADCSRA & _BV(ADSC)) {}
return ADC;
}

und weiter:


#include <adc_pwm.h> (so heisst oben die datei für diese main)

// PWM Beispiel

int main(void)
{
PWMchannel_init;
PWM9bit_init;
PWM1upcounting;
PWM2upcounting;
Timer2_prescaler_256;
pwm9bit(35.0,0);
pwm9bit(45.0,1);
Timer2_stop;
Timer2_prescaler_1024;
pwm9bit(35.0,0);
pwm9bit(45.0,1);
}

habe ich hier vom forum.

weil es so schön war, noch für adc (bibliothek von ganz oben für diese main):


#include <adc_pwm.h>

// AD beispiel
int main(void)
{
unsigned int x;
ADCinit;
ADCchannel_init;
ADCvintern;
ADCprescaler_16;
ADCchannel_3;
x=getadc();
ADCprescaler_reset;
ADCchannel_reset;
ADCprescaler_128;
ADCchannel_5;
x=getadc();
}
mdf pebisoft

Minifriese
26.02.2005, 08:33
Moin moin,

Also erstmal sollte man nicht die ioxxxx.h fuer den jeweiligen AVR direkt includen, sondern nur die avr\io.h. Die bindet dann naemlich die jeweils noetige ioxxx.h ein (wird vom Compiler anhand des im Makefile eingestellten AVR-Typs erkannt).

Um ein PWM-Signal fuer ein Servo zu erzeugen, wuerde ich nicht die PWM-Ausgaenge des AVRs nehmen, sondern normale Ausgangspins und die Timer, um das Signal selbst zu erzeugen.

Hab meinen Code gerade in einem anderen Thread gepostet, also vielleicht mal hier spicken:
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=7252

Wenn ich den Beitrag von Pebisoft oben sehe (geht das echt nur mit diesem voluminoesen code?), scheint mir meine Loesung doch etwas einfacher zu sein. Und ich kann mehrere Servos versorgen, nicht nur soviele wie der AVR PWM-Ausgaenge hat (2 Stueck?). Wobei das natuerlich bei vielen Servos die verfuegbare Rechenzeit fuer andere Aufgaben etwas einschraenkt, ich weiss (noch) nicht, wie gross dieser Effekt ist...

Nils

Kaiser-F
26.02.2005, 09:52
Hallo,

Vielen Dank an euch beiden für eure Tipps und Codes.

Hebe nun das ganze in Gang gebracht!
Die ursache war diese zeile:

TCCR1A = (1<<PWM11)|(1<<PWM10)|(1<<COM1A1);

PWM ist anscheinend falsch, muss WGM heissen, also:

TCCR1A = (1<<WGM11)|(1<<WGM10)|(1<<COM1A1);

Hab ich aus Pebisoft's Code gefolgert.

Der lange Code von Pebisoft lässt sich durch die Defines begründen, die eine Headerdatei darstellen, um die PWM"Befehle" zu vereinfachen.

Wer sein PWM-Signal oft ändert, für den ist das sehr Vorteilhaft!

Normal genügen folgende Zeilen um ein Servo anzusteuern:

(mit 8MHz µC-Frequenz)



#include <io.h>

{

// den entsprechenden Pin muss man glaub ich als Ausgang definieren, nicht vergessen

TCCR1A = (1<<WGM11)|(1<<WGM10)|(1<<COM1A1); //10BIT PWM aktivieren
TCCR1B = (1<<CS11); //Vorzähler 8 also CK/8

for (;;) {

OCR1A = "Wert"; // wert liegt zwischen 50 und 150 (Vollausschläge)

}}




Die Softwarelösung werd ich mir noch anschauen!

MfG

muraad
26.02.2005, 16:43
Der Code ist von mir. Ich bin mal erfreut drüber das ihn welche benutzen und er funktioniert O:) Und es stimmt schon das es mit den Defines viel Code wird wobei der Code auch als Beispiel dafür gedacht war wie die ganzen Register zu setzten sind.
Gruß Muraad
PS: Ist komisch das der Code von dir nicht ging da die Bits nur in der Headerdatei vom ATmega32 WGM11 und WGM10 heissen, in der io8535.h heissen die Defines wirklich PWM11 und PWM10. Naja aber wenns funktioniert ists gut.

Minifriese
27.02.2005, 20:30
Hallo Kaiser-F,

Kannst du nochmal erklaeren, wie du die Werte berechnet hast? Irgendwie kann ich das nicht so ganz nachvollziehen.
Dein Prescaler steht auf CPUCLK/8, also laeuft der Timer mit 1MHz, ein Timertick entspricht also einer Mikrosekunde.
Du benutzt 10bit-PWM und der Timer laeuft jeweils hoch und runter, also wiederholt sich dein PWM-Puls immer nach 1/(1MHz/(1024*2))ms=2ms.
Du setzt den Vergleichswert auf 50 (fuer Vollausschlag links), damit ist dein Puls also 2*50*1Mikrosekunde, also 100 Mikrosekunden lang. Du hast "nicht invertierend" eingestellt, also bekommst du fuer Vollausschlag links einen Puls von 100 Mikrosekunden HIGH (bzw 300 Mikrosekunden bei Vollausschlag rechts). Dieser Puls wiederholt sich alle 2 ms. Ein Servo braeuchte aber einen Puls von 1 bis 2ms alle 20ms. Hast du das mal mit nem Servo getestet? Wenn ja und erfolgreich: Wo ist denn mein Denkfehler?

Gruss,

Nils

Kaiser-F
27.02.2005, 23:20
Hallo Minifriese,

Da ist was drann!!!

Ich Muss mal ein Oszi anschließen!

Du hast sicherlich recht! ich hab halt den vergleiswert so tief gesetzt, dass es trotzdem funktioniert hat! ---- zufall ----

Ich poste morgen den korrekten Code. Danke für die Aufmerksamkeit!

Ich hab den invertierten und nicht invertierten voll durcheinandergebracht.

Danke für den Tipp!


Gruß
Kaiser

Siggi83
28.02.2005, 14:58
OK, damit lässt sich auch mein Versuch von gestern erklären. Hab den oben verwendeten Quellcode in einem Testaufbau von mir verwendet und die Servos sind gelaufen haben aber nie die Fahrtrichtung verändert...

@Kaiser-F:

also wenn du mal den neuen Code postest, hat sich auch meine pm an dich erledigt...

gruss Siggi

Kaiser-F
28.02.2005, 16:37
Hallo,

Ich habe beschlossen, dass ich PWM nun intensiever forschen werde...

Es gibt nämliche mehrere PWM-Betriebsarten....

Eventuell kann man auch das PWM signal erzeugen, welches mit einem RC-Sender übermittelt wird!

Wenn man einen RC-Empfänger betrachtet, dann kommen beispielsweise bei einem 8Kanal-Empfänger sämtliche Servo-stellungen in einem einzigen PWM signal.

Das sog. PPM-Signal ( http://www.mftech.de/ppm.htm

man würde nur einen PWM-Kanal benötigen...

Ob es nun tatsächlich geht weiss ich jetzt noch nicht genau.

Aber Antworten folgen.....

Gruß Kaiser

Kaiser-F
28.02.2005, 21:23
Irgentwie komme ich einfach nicht mir der Formel klar von Atmel:

Die schreiben dass die PWM Fraquent Folgende ist:

f(PWM) = F(CLK) / ( 2* N*TOP)

Mit N ist der prescaler
TOP ist die Obergranze


Mit f(CLK) = 8000000
N = 64
TOP= 1023

ergibt sich: f(PWM) = 61,09 Hz (= 16,35ms)


Wenn ich das Laufen lasse, dann messe ich im Oszi jedoch für f(PWM) = 8,33333333 Hz ( 120 ms )


WARUUUUUUUUMMMMM???????????????????????

Minifriese
28.02.2005, 21:40
Wo hast du denn die Formel gefunden? Wenn das ein Druckfehler war, gibts vielleicht auf der AVR-Freaks-Homepage schon ein korrigiertes Datenblatt. Die veroeffentlichen da immer so Errata...
Nils

Kaiser-F
28.02.2005, 21:46
Gefunden hab ich die Formel im ATMEGA8535 Datenblatt auf Seite 104

"The PWM frequency for the output when using phase correct PWM can be calculatet by the following equation":

Die PWM Fraquenz für den Ausgang bei "phase correct" PWM kann durch folgende Gleichung berechnet werden:

f(OCnxPCPWM) = f(clk_IO) / ( 2*N*TOP);





Im Datenblatt des ATMEGA128 steht aber das Selbe....

Vielleicht hab ich irgendwas übersehen... aber die Formel ist ja eindeutig.....

Mein Code stimmt normalerweise auch:

TCCR1A = (1<<WGM11)|(1<<WGM10)|(1<<COM1A1)|(1<<COM1A0); // 10Bit PWM, invertiert
TCCR1B = (1<<CS11)|(1<<CS10); //Prescaler = 64
OCR1A = 500; // Testwert... spielt für Fraquenzmessung keine rolle

Minifriese
28.02.2005, 22:06
Hm. Das Datenblatt hab ich zwar nicht, weil ich diesen Chip nicht benutze, aber die Formel ist ja die gleiche, die ich mir oben zusammengereimt habe. Wird also stimmen ;-)
Wenn deine Bits korrekt gesetzt sind, fällt mir eigentlich bloß noch ein, daß die CPU-Frequenz falsch ist. Bist du sicher, daß du die Fuses auf 8MHz umprogrammiert hast? Wenn der AVR nämlich mit den voreingestellen 1MHz des internen Oszillators läuft, würde das erklären, warum du erstens etwa ein Achtel des erwarteten Wertes mißt und zweitens nicht GENAU ein Achtel mißt. Der interne Oszillator ist ja recht ungenau...
Nils

Mir faellt grad auf, das wuerde ja auch erklaeren, warum deine fruehere Einstellung mit den 100 us alle 2ms so halbwegs funktioniert hat. Das waeren dann naemlich in Wirklichkeit 0,8ms alle 16ms gewesen, was einem normalen Servosignal schon ziemlich nahe kommt... Ich taete mal die Fuses checken :-b

Siggi83
01.03.2005, 11:54
Servus Kaiser-F,

hab mich grad mal hingesetzt und mein Oszi abgestaubt.
Bei mir hat das PWM-Signal ungefähr den von dir errechneten Wert (16,35ms). Verwende einen AT90S2313, einen Fehler habe ich aber noch korregieren müssen, und zwar musste ich

TCCR1A = (1<<PWM11)|(1<<PWM10)|(1<<COM1A1);

anstatt von

TCCR1A = (1<<WGM11)|(1<<WGM10)|(1<<COM1A1);

verwenden. Du hast es ja genau andersrum... Liegt das am verwendeten Controller, oder an der Programmierumgebung- ich verwende WinAVR. Weis da jemand bescheid?

gruss Siggi

muraad
01.03.2005, 13:35
Das liegt am verwendeten Controller. Schaut doch mal eure inlcude Datei des jeweiligen Controllers an. In der iom32.h ist es WGM in der io8535.h PWM. Aber ihr müsst eigentlich nur mal reinschaun.
Gruß Muraad

Minifriese
01.03.2005, 14:07
Moin moin!
Nachdem ich jetzt mein neues PC-USB-Digi-Oszi-Teil hab *freu*, hab ich eure Methode, PWMs zu erzeugen, auch mal probiert. Mit ATmega8, da heissen die Bits ebenfalls WGM... Und siehe da:Es geht. Ich messe bei CPU=8MHz und Prescaler=64 eine Periode von 15,6ms, was recht nah an den rechnerischen 16,35 liegt. Und bei einem Vergleichswert von 64 komme ich rechnerisch und messtechnisch auf ungefaehr 1ms fuer die Pulsdauer (nicht invertierend).

Wieder was gelernt :-)

Kaiser-F, klappts bei dir inzwischen auch?

Nils

Kaiser-F
01.03.2005, 22:56
Hallo Nils,

Ich hab irgendwie noch mit kleineren Problemen zu kämpfen...

Würdest du mir den Gefallen tun, und mal deinen Versuch nochmal messen,
Aber mit invertierten PWM? Dann müsstest du deinen Vergleichswert von 64 auf "OBERGRENZE - 64" legen.... dann müsste rein theoretisch das selber Ergebnis rauskommen....

Das wär ne super Sache!

MfG,

Kaiser

Minifriese
01.03.2005, 23:21
Yups, gerade gemessen. Kommt genau das gleiche raus. Mit COM1A1 und COM1A0 gesetzt, und 1024-64=960 als Vergleichswert.
Hast du deine CPU-Frequenz gecheckt?

Nils


#include <avr\io.h>

int main(void) {
DDRB = 0xFF;
TCCR1A = (1<<WGM11)|(1<<WGM10)|(1<<COM1A1)|(1<<COM1A0);
TCCR1B = (1<<CS11)|(1<<CS10);
OCR1A = 1024-64;
}

Kaiser-F
07.03.2005, 18:50
Servus Nils,

Danke für deine Bemühungen,

Du hattest mir deiner Vermutung Recht!

Meine Eingangsfrequenz war nicht richtig!

mein Fehler :-(

Gruß,
Kaiser

Rubi
29.06.2005, 07:51
Normal genügen folgende Zeilen um ein Servo anzusteuern:

(mit 8MHz µC-Frequenz)



#include <io.h>

{

// den entsprechenden Pin muss man glaub ich als Ausgang definieren, nicht vergessen

TCCR1A = (1<<WGM11)|(1<<WGM10)|(1<<COM1A1); //10BIT PWM aktivieren
TCCR1B = (1<<CS11); //Vorzähler 8 also CK/8

for (;;) {

OCR1A = "Wert"; // wert liegt zwischen 50 und 150 (Vollausschläge)

}}




Hallo

Bitte um Hilfe, irgendwo habe ich einen Denkfehler.
Ich würde gerne die Hardware PWM vom Mega8 verwenden um 2 Servos anzusteuern.
Leider habe ich aber keinen 8Mhz Quarz verwendet, sondern einen mit 16Mhz.
Mit dem obrigen Code funktioniert es zwar, aber das Servo läßt sich nicht in der Geschwindikeit ändern, es dreht sich mMn immer gleich schnell, egal welchen Wert ich in OCR1A verwende.
Außerdem bräuchte ich einen Prescaler von 16, anstelle von 8 dies ist aber auch nicht möglich, weil nach 8 der nächste Wert 64 ist.

Bitte um Hilfe.

LG
Rubi

Kaiser-F
29.06.2005, 09:13
Hallo Rubi.

Um ein Modellbauservo Anzusteuern müssen sich deine Impulse min. alle 20ms (soweit ich mich erinnere) erfolgen.

Zunächst musst du dich auf eine PWM "Betriebsart" einigen.

Soweit ich mich aus dem Datenblatt schla gemacht habe gibt es über vier Stück:

1: Normale Mode
2: Clear Timer on Compare Match (CTC) Mode
3: Fast PWM Mode
4: Phase Correct PWM Mod
und noch ein paar wenige.

Erklärung: ATMEGA8535 Datenblatt Seite 99 - 104
Übersicht: ATMEGA8535 Datenblatt Seite 133


Für uns Interessant ist aber nur der FAST PWM MODE!


Dies erklärt dir schonmal, warum "WGM10" und "WGM11" in "TCCR1A" auf "1" gesetzt werden müssen.

Beim Fast PWM Mode zählt der Counter von 0 bis zur Obergrenze.
( beim 8 Bit timer ist die Obergrenze "volle 8 Bit", also 256.

beim 16 Bit kannst du zwischen 8, 9 und 10 Bit wählen.


Der Counter zählt also ständig von 0 bis Obergrenze und fängt dann wieder von neu an.

Erreicht er in dieser Zeit den wert von OCR1A bzw. OCR1B oder auch OCR1C ( beim Mega128 ), dann schaltet er auf High, bzw auf Low, je nach Invertierung des PWM signals.

Kurze Rede Langer Sinn:

Deine Kompunenten berechnen Sich bei FAST PWM wie Folgt:

f.PWM = f.CPU/(N*(1+TOP))

N ist der prescaler, den kannst du in TCCR1B mit den "CS"-Bits Festlegen. ( 1, 8, 64, 256, oder 1024 )

ATMEGA8535 Datenblatt Seite 111.



TOP die Obergrenze.

Die Obergrenze wird je nach gewählten PWM 8,9 oder 10 Bit festgelegt.

Nachsehen kannst du das im
ATMEGA8535 Datenblatt auf seite 133.

Hier sieht man auch dass man auch andere Werte für TOP selbst definieren kann.
zB kann man in ICRn den gewünschten Wert schreiben,

Aber so genau geht es ja bei den Servos nicht.



Ach ich fang schonwieder an so viel zu labern, dabei wolltest du ja nur den Code :-)


Also:

folgende Gleichung:

f.PWM = f.CPU/(N*(1+TOP))

alle 20ms muss eine neue Flanke kommen.

20ms = 0,020s, und i Hz ist ja 1/secunde.

Also bruachen wie 1/0,020s, macht 50Hz.


mit 10 Bit PWM und 265er Prescaler komm ich auf ca. 61Hz.



also sieht das so aus:





#include <io.h>

{
TCCR1A = (1<<WGM12)|(1<<WGM11)|(1<<WGM10)|(1<<COM1A1)|(1<<COM1B1);
//"(1<<WGM12)|(1<<WGM11)|(1<<WGM10)" bedeutet 10Bit FAST PWM Mode
//"(1<<COM1A1)|(1<<COM1B1)"Ausgang A und B sind nicht invertiert.
// Es wird also so lange High gehalten, bis der Counter den wert OCR1A bzw. OCR1B überschreitet.
TCCR1B = (1<<CS12);
//Prescaler = 256



for (;;) {

OCR1A = "Wert"; //
OCR1B = "Wert"; //
}}



Ich hoffe ich konnte helfen.

Du kannst eigentlich alles in den Datenblättern erfahren.
---Ich weiss, dass sagt dir jeder, und ich weiss auch dass man da nur was rauslesen kann, wenn man eine gewisse Grundahnung hat, und ich hoffe die hast du jetzt. zumindest über PWM.

Und ich hoffe auch dass ich keinen MIST erzählt habe ;-)

Gruß
Franz

Kaiser-F
29.06.2005, 09:19
PS: wenn du nen anderen Mode nimmst, wie schon an gesprochen wo du TOP festlegen kannst, dann kommst du eventuell genau auf 20ms!

Und dadurch kannst du mit OCR1A / B auch viel exakter deine 1-2ms halten,

Achja, das Servo reagiert übrigens zwischen 1 und 2 ms ( positive Flanke )

Rubi
29.06.2005, 09:47
Hallo

Danke für die Hilfe habe es mit deiner Formel jetzt auch rechnersich nachvollziehen können. ;-)
Der "ideale" prescaler wäre nach meiner Rechnung 312.
256 ist ja nicht so weit davon entfernt.
Meine Servos wechseln die Drehrichtung wenn ich die PWM invertiere.
Ist das bei Dir auch so? Oder steuerst Du auch die Drehrichtung über OCR1A / B ?1

Vielen Dank auf jeden Fall, werde es gleich am Abend ausprobieren.

P.S.: Ad Datasheet
Natürlich steht das im Datenblatt, wäre schlimm wenn es nicht so wäre,...
Aber, ich habe mir den Timer Teil gestern ausgedruckt und war erschlagen von den Details (und den kryptischen Namen).
Ansonsten arbeite ich sehr gerne mit den AVRs und die Dantenblätter sind auch erste Sahne, aber halt sehr umfangreich.

LG
Rubi

Kaiser-F
29.06.2005, 10:11
Ich muss gestehen, der Code der ganz oben steht, also der uralte von damals... da hab ich mich nochnicht ausgekannt... und der is eigendlich nur aus Zufall gegangen.... da hab ich zwar ein 8MHz Quarz angeschlossen, aber vergessen es in den securitybits zu aktivierren :-)

tja... DAMALS... :-)

Kaiser-F
29.06.2005, 10:18
zwecks drehrichtung,

Das servo hat in dem sinne keine Drehrichtung. Es ist ein geschlossener Regelkreis. Es hat also Stellungen, welche du mit der Pulslänge ( 1- 2 ms ) bestimmst. Diese Pulse aktualisierst du alle 20ms ( 30ms is auch OK ).

Das Servo versucht also ständig seinen Hebel auf die vorgegebene Stellung zu bringen.

Drehrichtung oder Positiv/negativ - Stellungen werden uns durch die "Computerfernsteuerungen" aus dem Modellbau vorgegaukelt...

so mit + - 100% und so... haben die nur gemacht um es schönder Darzustellen.

Mittelstellung sollte also eigentlich bei einer Pulslänge von 1,5ms sein.
-100% ist dann 1ms und +100% sind 2ms.

Wenn dein Programm das so immer umwandelt, dann kannst du wunderbar mit servos arbeiten!

Gruß
Franz

Rubi
29.06.2005, 10:31
Hallo Franz

Ich verwende modifizierte, "gehackte" Servos.
Da bekommt der Regelkreis kein Feedback mehr vom Seuerpotenziometer.
Die einzige Möglichkeit, die ich gestern beim herumspielen gefunden habe,
um die Drehrichtung zu ändern, war die PWM zu invertieren.

LG
Rubi

Kaiser-F
29.06.2005, 11:57
achso,
Du willst sie als Getriebemotor mit Geschwindigkeitsregelung verwenden.

Mein tipp dabei wäre, du misst das Poti wenn es in Mittelstellung ist, und ersetzt es durch Feste Widerstände.

Wenn du dann eine Pulslänge von 1,5ms hast, steht es.

Verlängerst du die Pulslänge, so dreht es sich immer schneller in die eine Richtung

Verringerst du die Pulslänge, so geschieht das Selbe in die andere Drehrichtung.


Das Servo ist nämlich so "programmiert", dass es seine Drehzahl bei annäherung des sollwertes verringert. Ansonsten würde es ständig zittern und nicht zur Ruhe kommen.

Rubi
29.06.2005, 12:46
Hallo Franz

Vielen Dank, ich werde das Heute probieren.
Alles in allem, bin ich mir ziemlich sicher, das mein nächster
Bot keine Servos als Motor bekommen wird.
Eine H-Brücke und die Hardware PWM, wäre um einiges einfacher zu realisieren gewesen.

Zum Datenblatt noch etwas:
Ich hätte mir viel leichter getan das ganze zu verstehen, wenn ich irgendwo explizit lesen hätte können, dass TOP gleich 2 hoch der eingestellten PWM Bitbreite ist.
Im Datenblatt steht aber z.B.:
The figure shows fast PWM mode when OCR1A or ICR1 is used to
define TOP.

Das würde praktisch bedeuten das man mit OCR1A die Frequenz bestimmt anstelle der Pulsbreite.
Eigentlich finde ich die Atmel Datenblätter sehr gut, die PWM ist aber imho verwirrend beschrieben.

DANKE FÜR DIE HILFE

LG
Rubi

Kaiser-F
29.06.2005, 12:53
HMMM wenn du auf der Übersicht der PWM modes schaust, ist eigentlich alles Logisch... Da steht dann da was bei welcher Betriebsart als TOP hergenommen wird.

Bei Mega8535 auf Seite 110

( hab anscheinend vorher die Seitenzahlen aus dem Mega128 Datenblatt angegeben) :-)

Gruß
Franz

Kaiser-F
29.06.2005, 12:59
Achja, und Servos glaub ich sind einfacher zum Ansteuern...

Bei der H-brücke brauchst du auch wieder ein PWM Signal.... Ausserdem is die Besch*ss**n zu bauen....

Nochdazu kannst du sie bei Fehlprogrammierung schrotten....

Du könntest ja auch wenn du mehr Power brauchst ein stärkeres Servo (unmodifiziert) kaifen, und dieses Selbst modifizieren... da ist nicht viel dabei!

Poti Raus und ersetzen, (oder abschneiden und fest einstellen, dann brauchst nicht lange rumlöten und kannst es drinnen lassen), und den mechanischen Anschlag mit nem Messer rausschneiden.

Über Servo oder "Hbrücke mit getriebemotor" lässt sich streiten, aber ein Servo ist sicher die Kompaktere und einfachere Lösung, mit geringsten Pinaufwand.

Und warum getriebemotor nehmen und Hbrücke, wenn ein Servo das schon alles fertig beinhaltet ?
:-b ;-)

Rubi
29.06.2005, 13:05
Hallo Franz

Die Kraft ist glaube ich nicht so wichtig.
Ich befürchte nur, nach dem was ich Gestern gesehen habe,
das die Geschwindigkeit ziemlich lahm wird.
Hoffentlich wird es Heute, wo dank deiner Hilfe die richtige Frequenz erzeugt wird besser.

Die PWM für die H-Brücke halte ich deswegen für einfacher, weil die Frequenz keine große Rolle spielt.


LG
Rubi

Rubi
29.06.2005, 17:08
Hallo

Leider funktioniert es nicht.

Der folgende Code, erzeugt eine PWM mit 8,22 ms oder 122hz, anstelle der angepeilten 50 bzw 61 hz.
Was interesant ist, ist das die Frequenz doppelt so groß ist wie sie sein sollte.
@Franz, ist dein Code vielleicht für 8mHz ausgelegt ?



void init_servo ( void )
{
TCCR1A = (1<<WGM12)|(1<<WGM11)|(1<<WGM10)|(1<<COM1A1) |(1<<COM1B1); //10BIT FAST PWM aktivieren für 1A und 1B
TCCR1B = (1<<CS12) ; //Vorzähler CK/256

DDRB |= ( 1<<PB1 ); //Servo links
DDRB |= ( 1<<PB2 ); //Servo rechts

}



LG
Rubi, welcher ratlos ist

Kaiser-F
29.06.2005, 20:51
Hallo Rubi,

Ich hab den Fehler

Nimm mal 9 BIT PWM...



Ich hab in der Formel mit 9 Bit berechnet, hab aber 10 Bit hingeschrieben....

Sorry

Kaiser-F
29.06.2005, 21:10
So müsste es gehen:



void init_servo ( void )
{
TCCR1A = (1<<WGM11)|(1<<COM1A1) |(1<<COM1B1); //9BIT FAST PWM aktivieren für 1A und 1B
TCCR1B = (1<<CS12) ; //Vorzähler CK/256

DDRB |= ( 1<<PB1 ); //Servo links
DDRB |= ( 1<<PB2 ); //Servo rechts

}

Rubi
30.06.2005, 08:25
Hallo

Ich hatte gestern schon probiert die PWM von 10 Bit auf 9Bit und sogar auf 8Bit zu ändern, die Ausgabefrequenz ist jedoch gleich geblieben, egal ob 10,9 oder 8 bit PWM.
Normalerweise würde ich vermuten, das ich was falsch gemacht habe, ich kann jedoch keinen Fehler finden.
Ich hatte das Oszi am Ausgang, habe die PWM von 10 auf 9 Bit geändert und die Frequenz ist gleich geblieben.

LG
Rubi

Kaiser-F
30.06.2005, 13:03
hm ,,

kann aber nicht sein, da MUSS sich was ändern...

Nimm mal den Code her den ich gepostet hab, den aktuellen...
Gruß
Franz

Rubi
30.06.2005, 13:28
Hallo Franz

OK vielen Dank für deine Hilfe.
Probiere Heute Abend weiter und melde mich dann.
Muß man vielleicht TCNT1 explizit setzen,
wenn man weniger als 10Bit verwendet ?

LG
Rubi

Kaiser-F
30.06.2005, 13:34
nö...

Villeicht hast du ja in aller eile vergessen deine Datei neu zu Compilen.. oder die falsche... passiert mir auch öfters, und ich wundere mich warum sich nix tut :-)...

Wie gesagt, es MUSS sich was ändern wenn du von 10 auf 9 wechselst

Und ich hab auch oben mit 9Bit gerechnet, hab aber blöderweise 10 Hingeschrieben... Sorry.

A meint er, B sagt er, C schreibt er, und D ist richtig :-)

Rubi
30.06.2005, 18:04
Hallo jetzt funzt es!
Vielen Dank!

Zuerst war wieder kein Unterschied, dann habe ich in Set_Speed
folgendes auskommentiert:


if( direction == backward )
{
TCCR1A = (1<<COM1A0) | (1<<COM1B0); // inverted pwm
}
else
{
TCCR1A = ~(1<<COM1A0) & ~(1<<COM1B0); // non inverted pwm
}

Und dann hat sich der Mega8 wieder so verhalten wie es sein soll!

VIELEN DANK, besonders für deine Geduld!

LG
Rubi

Kaiser-F
01.07.2005, 07:21
Für das sind Foren und Mitglieder ja da ;-) ,

Gruß
Franz

Rubi
04.07.2005, 14:58
So müsste es gehen:

TCCR1A = (1<<WGM11)|(1<<COM1A1) |(1<<COM1B1); //9BIT FAST PWM
}[/code]

Eine Frage hätte ich noch, denn leider so ganz zufrieden bin ich doch noch nicht mit der Pwm.
Ist das absichtlich das Du im Beispielcode 9bit Phase Correct PWM aktivierst, im Kommentar aber Fast PWM schreibst ?

Mein Problem:
Ich habe keinen wirklichen Spielraum bei der Geschwindigkeit.
die 1 bis 2,5 ms sind im OCR1A bei Werten zwischen ungefähr 5 und 25 schon überschritten. Außerdem erreiche ich keinen Wert, bei dem das Servo steht.
Setze ich z.B. 15 dreht es sich noch ganz langsam, bei 16 dreht es sich schon wieder relativ Flott in die andere Richtung.
Was mir allerdings aufgefallen ist, ist das sich nur modifizierte Servos so auffällig verhalten. Ein nicht modifiziertes (ich verwende Futaba) ist viel gutmütiger.

LG
Rubi

Kaiser-F
04.07.2005, 15:10
Rubi,

Nochmals Sorry wegen den Codes,
War an dem Tag anscheinend nicht ganz auf dieser Welt ;-)

damit ich die übersicht mal nicht wieder verliehre, hier mal ein Ausschnitt:

https://www.roboternetz.de/phpBB2/files/pwm-modes.jpg





void init_servo ( void )
{
TCCR1A = (1<<WGM11)|(1<<WGM12)|(1<<COM1A1) |(1<<COM1B1); //9BIT FAST PWM aktivieren für 1A und 1B, nicht Invertiert
TCCR1B = (1<<CS12) ; //Vorzähler CK/256

OCR1A = 0; // Werte für Servo A
OCR1B = 0; // Werte für Servo A

}



Tut mir wirklich leid wenn ich dich oder andere verwirrt habe.

So stimmt es sicher ...

Gruß
Franz

Kaiser-F
04.07.2005, 15:25
Achja, das mit dem Modifizierten Servo.....

Anscheinend erwischt du eben gerade nicht die entsprechende Pulsweite....


L-------------+-------------R Servo vorgegaukelter Istwert = 0 Rotation:
L------------+--------------R Erster Annäherungsversuch
L--------------+------------R Erster Annäherungsversuch + 1
L-------------+-------------R uns so sollte es sein damit das servo steht

Leider gibt es keinen 128er Vorzähler, sonst könntest du mit 10Bit PWM arbeiten, dann währen die Schritte feiner.

Du könntest aber auch das Poti entsprechend verstellen, sodass er zum stehen kommt. Suchst dir einfach einen wert aus andem es fast steh, stellst das poti nach, und fixierst die stellung mit heißkleber oder so...

Demnach sind Feste widerstände halt doch nicht so gut geeignet...

Lieber das Poti dann drinnen Lassen und die Welle abschneiden....

Gruß
Franz

Rubi
19.07.2005, 09:52
Hallo

Nach viel Frust (und Selbstzweifel) habe ich es nun endlcih geschafft.

Die hier verwendete PWM ist IMHO für gehackte Servos nicht zu brauchen.
z.B OCR1A auf Wert 50 Servo steht (wenn man Glück hat), auf 52 ist schon Vollgas. Da ist einfach zu wenig Spielraum.

Ich habe nun eine PWM Einstellung gefunden die
1. Genau 50 Hz hat
2. Dadurch das ein Prescaler von 8 verwendet wird sehr feinfühlig ist
3. einfach OCR1A = 1000 == 1ms Puls, OCR1A = 1500 == 1,5ms Puls

Bei interesse kann ich den Code gerne posten.

@Kaiser Franz
Da du mich darauf verwiesen hast das Datenblatt zu lesen, kann ich es mir nicht verkneifen.
In deinem Beispielcode, den Du netterweise gepostet hast, ist noch ein Fehler. ;-)

LG
Rubi

Kaiser-F
19.07.2005, 17:14
Hallo Rubi,

War wohl nicht meine Zeit :-)

Wäre nett wenn du den Code mal posten könntest.

Gruß
Franz

Rubi
19.07.2005, 17:42
Hallo Franz

Ist ja kein Problem und war auch nicht als Kritik gemeint. Konnte es mir halt nicht verkneifen.

Der Fehler falls es dich interessiert ist:
TCCR1A = (1<<WGM11)|(1<<WGM12)|(1<<COM1A1) |(1<<COM1B1);

WGM12 wird in TCCR1B gesetzt.

Mein (hoffentlich finaler) Code:


void init_servo ( void )
{
/*
//TCCR1A = (1<<WGM11)|(1<<COM1A1) |(1<<COM1B1); //10BIT FAST PWM aktivieren für 1A und 1B
TCCR1A = (1<<WGM11)|(1<<COM1A1) |(1<<COM1B1); //10BIT FAST PWM aktivieren für 1A und 1B
//TCCR1B = (1<<WGM13)|(1<<CS12) ; //Vorzähler CK/256
TCCR1B = (1<<WGM13)|(1<<CS12)|(1<<CS10) ; //Vorzähler CK/256

ICR1H = 1;
ICR1L = 644;*/

TCNT1 = 0xB1E0;
TCCR1A = (1<<COM1A1)| (1<<COM1B1);
TCCR1B = (1<<WGM13)|(1<<CS11); 0x13; //start Timer
OCR1A = 1500;
OCR1B = 1500;
ICR1 = 0x4E20;

DDRB |= ( 1<<PB1 ); //Servo links
DDRB |= ( 1<<PB2 ); //Servo rechts

}

Damit werden beide Servos auf Nullstellung (1,5ms) initialisiert.
Wenn Du voll Gas möchtest setzt Du
OCR1A = 2500; //(==2,5ms)

LG
Rubi

Kaiser-F
20.07.2005, 12:13
Hallo Rubi,

Da hab ich ja was gigantisches Übersehen,

WGM10 und WGM11 sind ja in TCCR1A
und WGM 12 und WGM 13 in TCCR1B

Naja, gut, dass du es gefunden hast... Ich habs wohl nicht bemerkt, da ich nur WGM10 und 11 benutzt habe, aber dake für den Hinweis, und nochmals sorry für die Verwirrung.


Du benutzt nun den "PWM Phase and Frequency correct Mode"
Der als TOP den einstellbaren ICR1 hat.

Nur, was mir noch fremd ist;

TCNT1 ist ja der Counter selbst, warum setzt du den auf HEX:B1E0 ( Dez: 45536) ?

Rubi
20.07.2005, 13:40
Hallo Franz

Ich habe da ein Programm das heißt Application Builder.
Da gibst Du ein welche Frequenz Du möchtest und den PWM Modus
den Du haben möchtest und das Programm bildet Dir dann den Source Code.

Tja und warum jetzt TCNT1 setzen?
Ich weis es auch nicht. :-(

Habe mich das auch gefragt und diese Zeile zuerst weggelassen, dann sind es aber nicht 50 sondern 300hz.
Dann habe ich die Zeile wieder eingefügt und alles hat gefunzt,... :-)

LG
Rubi

Rubi
20.07.2005, 13:44
Hallo Rubi,

Da hab ich ja was gigantisches Übersehen,

WGM10 und WGM11 sind ja in TCCR1A
und WGM 12 und WGM 13 in TCCR1B

Naja, gut, dass du es gefunden hast... Ich habs wohl nicht bemerkt, da ich nur WGM10 und 11 benutzt habe, aber dake für den Hinweis, und nochmals sorry für die Verwirrung.


Wie schon gesagt kein Problem, bin auch nur durch Zufall dahinter gekommen.
Das seltsamste dabei ist, das die PWM mit 62 hz sich so verhalten hatte als wäre alles richtig gesetzt.

Manchmal tue ich mir schwer diese AVRs zu verstehen.
Dabei sind die eh schon soviel "einfacher" zu proggen als z.B. ein Pic.

LG
Rubi