Datum:
Hallo ich möchte mit meinem uC ein Servo ansteuern, per Timer / Interrupt Der Code ist folgendermaßen: Alle 20ms wird ein Interrupt ausgelöst der dann das Signal, je nach Einstellung zw. 1 und 2ms lang einschaltet
/* * servo_timer.c * * Created: 09.04.2012 22:13:40 * Author: Thomas */ /////////////////////////////////////////////////////////////////// // // Servo Steuerung mit Timer / Interrupts // /////////////////////////////////////////////////////////////////// #ifndef F_CPU #define F_CPU 16000000 //CPU Taktfrequenz #endif #define PWM_MIN 1000 //Min. PWM Zeit in US #define PWM_NULL 1500 //Neutralposition - PWM Zeit in US #define PWM_MAX 2000 //Max- PWM Zeit in US #define PWM_LOOP 10 //Signal Widerholungen #include <avr/io.h> #include <avr/interrupt.h> #include "rncontrol.h" int pwm_time=PWM_NULL; //Globale Variable für PWM HIGH Time int main(void) { sei(); //Interrupts aktivieren DDRC |= 1 << PINC0; //Status LED 1 aktivieren PORTC &= ~(1<<PINC0); TCCR1B |= 1<<CS11 | 1<<WGM12; //Prescaler: -> 50Hz OCR1A = 0x9C40; //Prescaler Settings TIMSK |= 1<<OCIE1A; DDRB |= 1<<PINB0; //Ausgang für Servo aktivieren while(1) { if (button()==1) pwm_time=PWM_MAX; else if(button()==2) pwm_time=PWM_NULL; else if(button()==3) pwm_time=PWM_MIN; } } ISR(TIMER1_COMPA_vect) //ISR für Servo PWM - durch Timerinterrupt kann während den ca. 20 ms Wartezeit etwas anderes gemacht werden { int i; PORTC ^= 1<<PINC0; PORTB |= 1<<PINB0; //PWM HIGH Signal for(i=0;i<pwm_time;i++) _delay_us(1); //PWM Time warten PORTB &= ~(1<<PINB0); //PWM LOW Signal } |
Doch das Servo fährt nur bis zum Endanschlag und will dann noch weiterdrehen. Was mache ich falsch? mfg thomas
Datum:
Probier erstmal ob das Servo funzt, vielleicht ist es hin.
Datum:
Wo wird util/delay.h eingebunden? Optimierung aktiviert?
Datum:
Guten Abend, Du solltest die Interupts erst nach dem konfigurieren des Timers aktivieren. Gruß Martin
Datum:
Funktioniert ohne Probleme...habs schon mit folgendem Programm getestet:
#include <avr/io.h> #include <util/delay.h> #define POS 1500 //1000 <--> 2000 (1500 neutral) void main() { DDRB |= 1<<PIND0; while(1) { PORTB |= 1<<PIND0; _delay_us(POS); PORTB &= ~(1<<PIND0); _delay_ms(18); } } |
ok, mal versuchen ;) mfg thomas
Datum:
Thomas P. schrieb: > Funktioniert ohne Probleme...habs schon mit folgendem Programm getestet: Bin ich blind? Oben bindest du <util/delay.h> doch nicht ein?
Datum:
Thomas P. schrieb: > Was mache ich falsch? Läuft der Prozessor wirklich mit der Frequenz, die mit F_CPU angegeben ist? Falls du kein Oszi hast, könntest du das testen, indem du einen 1-Sekunden Takt auf einem Pin erzeugst und per LED anzeigen läßt.
Datum:
Hast du deine 16 Mhz kontrolliert?
Datum:
> TCCR1B |= 1<<CS11 | 1<<WGM12; //Prescaler: -> 50Hz Was ist das für ein Vorteiler > OCR1A = 0x9C40; //Prescaler Settings Schreibs doch dezmal hin! Dann braucht man nicht lange umrechnen, damit man deine Einstellung kontrollieren kann. Oder bist du so gut im Hex-Rechnen? OCR1A = 40000;
Datum:
Ohhhh damt... <util/delay.h> einbinden vergessen..... aber das war trotzdem nicht der Fehler, weil in "rncontrol.h" wird es bereits verwendet, also bereits eingebunden! Wie kann ich die Optimierung einstellen? mfg thomas
Datum:
Ne, hab kein Oszi :( Aber ja, CPU läuft mit 16 MHz Prescaler Settings: http://www.et06.dk/atmega_timers/ ;) Ok, werd ich mir angewöhnen mfg thomas
Datum:
Thomas P. schrieb: > Prescaler Settings: http://www.et06.dk/atmega_timers/ ;) Ist es wirklich so schwer hinzuschreiben, dass CS11 alleine ein Vorteiler von 256 oder 128 oder was weiß ich ist?
Datum:
> for(i=0;i<pwm_time;i++) > _delay_us(1); //PWM Time warten die For-Schleife wird hier bereits einen nicht unbedeutenden Anteil an der Gesamtzeit haben. Die Positionen werden nicht ganz stimmen, aber das ist jetzt noch nicht das Problem.
Datum:
Thomas P. schrieb: > Ne, hab kein Oszi :( > Aber ja, CPU läuft mit 16 MHz Woher weißt du das? Mit dem Programm Beitrag "Re: Servoansteuerung mit Timer (ATmega32)" Nö. Wenn das hier funktioniert und du nicht die 16 Mhz in den Project Settings angegeben hast (was ich bezweifle, denn dann wüsstest du wo man den Optimizer einschaltet, ist gleich darunter), dann nicht. Dann läuft dein µC mit 1Mhz
Datum:
Ja ich weiß, ich bin Anfänger.... Ja, wie könnte man das sonst lösen? Denn _delay_us bzw _ms lässt keine variablen zu, nur konstanten und meine PWM Ausgänge am µC kann ich nicht verwenden, da bereits ein Motortriber verwendet wird... mfg thomas
Datum:
Thomas P. schrieb: > Ja ich weiß, ich bin Anfänger.... > > Ja, wie könnte man das sonst lösen? Denn _delay_us bzw _ms lässt keine > variablen zu, nur konstanten und meine PWM Ausgänge am µC kann ich nicht > verwenden, da bereits ein Motortriber verwendet wird... Du hast noch einen 2-ten Compare Match zur Verfügung. Aber erst mal solltest du dich darum kümmern, ob die 16Mhz tatsächlich stimmen. Bis jetzt hab ich da aus der Ferne noch kein Vertrauen dazu.
Datum:
Inwieweit einen zweiten? Welche Ausgänge sind das? Ich verwende OC1A, OC1B, TOSC1, TOSC2, T0, T1 und OC für Frequenzabhängige Anwendungen, was bleibt denn dann noch über? mfg thomas
Datum:
Thomas P. schrieb: > Inwieweit einen zweiten? OCR1B > Welche Ausgänge sind das? erst mal gar keine. Erst mal sind das nur Interrupts, die bei bestimmten Zählerständen ausgelöst werden.
Datum:
> 3) Ich hab nur die Zeile #define F_CPU 16000000 mitkopieren vergessen...
Sowas lieben wir hier :-)
Das erleichtert das kontollieren der Einstellungen ungemein, wenn die
Hälfte der relevanten Informationen fehlt :-)
Datum:
Ja, weißt eh, war mit reiner Absicht ;) Ok, aber die Interrupts kann ich dann ja auf Ausänge legen oder? mfg thomas PS: ich bin totaler Interrupt Anfänger :(
Datum:
Thomas P. schrieb: > Ja, weißt eh, war mit reiner Absicht ;) > > Ok, aber die Interrupts kann ich dann ja auf Ausänge legen oder? In den Interrupts kannst du dann ganz normal die Pins schalten, so wie du das jetzt auch machst. In der Zwischenzeit hab ich mir im Datenblatt die Prescaler Einstellungen gesucht. CS11 ist ein Prescaler von 8, mit den 40000 ergibt das die 20ms (eigentlich sollten das 39999 sein, spielt aber keine Rolle, die 20ms sind sowieso nicht wichtig). Mach doch in deiner ISR erst mal einfach nur ein _delay_ms(1.5) zwischen den Port Operationen um zu sehen, ob die for-Schleife dir das Timing so weit versaut.
Datum:
Jep...es ist die for Schleife... mit _delay_us(1500); fährts wunderbar in die Mitte ;) Was könntn wir da machen? mfg thomas
Datum:
mach doch hier int pwm_time=PWM_NULL; //Globale Variable für PWM HIGH Time mal ein volatile int pwm_time=PWM_NULL; draus. Wer weiß, was dein Compiler da optimiert.
Datum:
Ein delay in einer ISR ist übrignes nicht so richtig schick. Auf Dauer solltest Du Dir was anderes angewöhnen.
Datum:
Ja und wie soll ich das dann lösen? mfg thomas
Datum:
Thomas P. schrieb: > Jep...es ist die for Schleife... > mit _delay_us(1500); fährts wunderbar in die Mitte ;) > > Was könntn wir da machen? anstatt eines 1µs delays erst mal ein 10µs delay. Dafür die Schleifenwiederholungen entsprechend reduzieren. (Der ganze Ansatz mit dem _delay in der ISR ist nicht zielführend. Da kannst du dir die ganze ISR auch gleich sparen.)
Datum:
Thomas P. schrieb: > Ja und wie soll ich das dann lösen? zb mit 2 Compare Matches. Der Match auf OCR1A schaltet den Pin auf 1, der Match auf OCR1B schaltet ihn wieder auf 0
Datum:
Mit dem 10µs delay gehts nicht -> erhöht auf 50µs zu ungenau :( Wie wäre da dann der Code? mfg thomas
Datum:
Hier
OCR1A = 0x9C40;
TIMSK |= 1<<OCIE1A;
hast du den Compare Match für den Compare A eingerichtet.
Jetzt schaust du ins Datenblatt, ob das für den Comparematch B genauso
geht und rechnest dir den Wert aus für 1.5ms
Hier
ISR(TIMER1_COMPA_vect)
{
hast du für den Compare Match A die ISR eingerichtet.
Wie wird das dann wohl für Compare Match B aussehen?
In der A setzt du den Pin auf 1, in der B wieder auf 0.
Einfacher als einem Baby den Schnuller klauen.
Datum:
Karl Heinz Buchegger schrieb: > Einfacher als einem Baby den Schnuller klauen. Das ist nicht Schnuller klauen, sondern Pferd von hinten durch's Auge erschießen :D Das ist Schnuller klauen:
// ATMega32@16MHz #include <avr/io.h> int main(void) { DDRD = (1<<PD5)|(1<<PD4); // Servo 1 an PD5, Servo 2 an PD4 TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS11); // Prescaler 8 TCCR1A = (1<<COM1A1)|(1<<COM1B1)|(1<<WGM11); // Mode 14, ICR = Top ICR1 = 39999; // Wiederholrate 50Hz OCR1A = 2000; // Servoposition Servo 1, 2000 = 1.0ms OCR1B = 3000; // Servoposition Servo 2, 3000 = 1.5ms while(1) { // Steuere Servos } } |
Datum:
MWS schrieb: > Karl Heinz Buchegger schrieb: >> Einfacher als einem Baby den Schnuller klauen. > Das ist nicht Schnuller klauen, sondern Pferd von hinten durch's Auge > erschießen :D [ ] Ich hab weiter oben gelesen, dass er die Pins nicht frei hat, ist mir aber Wurscht, ich geb meinen Senf trotzdem dazu. [ ] Ich habs nicht gelesen und muss mich trotzdem einbringen
Datum:
Karl Heinz Buchegger schrieb: > MWS schrieb: >> Karl Heinz Buchegger schrieb: >>> Einfacher als einem Baby den Schnuller klauen. >> Das ist nicht Schnuller klauen, sondern Pferd von hinten durch's Auge >> erschießen :D > [ ] Ich hab weiter oben gelesen, dass er die Pins nicht frei hat, ist mir aber Wurscht, ich geb meinen Senf trotzdem dazu. [x] Ich habs nicht gelesen und muss mich trotzdem einbringen Ist doch klar...
Datum:
Eben..Pins sind bereits belegt Also wie soll ich das ganze angehen? Denn so funktionierts mal nicht ;)
/* * servo_timer.c * * Created: 09.04.2012 22:13:40 * Author: Thomas */ /////////////////////////////////////////////////////////////////// // // Servo Steuerung mit Timer / Interrupts // /////////////////////////////////////////////////////////////////// #ifndef F_CPU #define F_CPU 16000000 //CPU Taktfrequenz #endif #define PWM_MIN 700 //Min. PWM Zeit in US #define PWM_NULL 1500 //Neutralposition - PWM Zeit in US #define PWM_MAX 2300 //Max- PWM Zeit in US #define PWM_LOOP 10 //Signal Widerholungen #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include "rncontrol.h" volatile int pwm_time=PWM_NULL; //Globale Variable für PWM HIGH Time int main(void) { DDRC |= 1 << PINC0; //Status LED 1 aktivieren PORTC &= ~(1<<PINC0); DDRC |= 1 << PINC1; //Status LED 2 aktivieren PORTC |= 1<< PINC1; //Timer 1 - 20ms TCCR1B |= 1<<CS11 | 1<<WGM12; //Prescaler: -> 50Hz OCR1A = 40000; //Prescaler Settings TIMSK |= 1<<OCIE1A; //Timer 2 - 1.5 ms TCCR1B |= 1<<CS10 | 1<<WGM12; //Prescaler -> 666Hz OCR1B = 24000; TIMSK |= 1<<OCIE1B; DDRB |= 1<<PINB0; //Ausgang für Servo aktivieren sei(); //Interrupts aktivieren while(1) { if (button()==1) pwm_time=PWM_MAX; else if(button()==2) pwm_time=PWM_NULL; else if(button()==3) pwm_time=PWM_MIN; } } ISR(TIMER1_COMPA_vect) //ISR für Servo PWM - durch Timerinterrupt kann während den ca. 20 ms Wartezeit etwas anderes gemacht werden { PORTB |= 1<<PINB0; //PWM HIGH Signal PORTC ^= 1<<PINC0; } ISR(TIMER1_COMPB_vect) //ISR für Timer B { PORTB &= ~(1<<PINB0); //PWM LOW Signal PORTC ^= 1<<PINC1; } |
Das Servo tickert nur, bewegt sich aber nicht mfg thomas
Datum:
Karl Heinz Buchegger schrieb: > [ ] Du hast weiter oben gelesen, dass er die Pins nicht frei hat Nein, war versteckt, tatsächlich nicht gelesen, wäre vom Eingangspost her auch nicht davon ausgegangen. Die OC1A..B hängen am LM298 und dienen dort zur PWM-Steuerung, Timer1 wäre also nicht verfügbar, sollte man die vernünftig nutzen wollen. > [X] Ich habs nicht gelesen und muss mich trotzdem einbringen Hab' ich doch gern gemacht :D
Datum:
> //Timer 1 - 20ms
TCCR1B |= 1<<CS11 | 1<<WGM12; //Prescaler: -> 50Hz
OCR1A = 40000; //Prescaler Settings
TIMSK |= 1<<OCIE1A;
//Timer 2 - 1.5 ms
TCCR1B |= 1<<CS10 | 1<<WGM12; //Prescaler -> 666Hz
OCR1B = 24000;
Ähm. Du hast da was falsch verstanden.
Du hast nur EINEN Timer 1.
Der hat aber 2 Stück Compare Match Register (A und B). Wenn der
Timerzählerstand identisch mit dem jeweiligen Compare Match Register
ist, dann wird die ISR ausgelöst.
Zusätzlich hast du dann noch den Timer in den CTC Modus geschaltet,
sodass er beim Compare Match A wieder bei 0 zu zählen anfängt. (Was ja
auch in Ordnung ist)
Das ist dein Setup mit dem du arbeiten musst.
Mit dem A-Compare Match machst du dir deine 20ms und setzt den Pin auf
1. Und irgendwann, während der Timer von 0 bis 40000 zählt, schaltet der
Compare Match B den Pin wieder auf 0. Der Timer zählt aber weiterhin bis
40000.
Datum:
Ohhh :D Also ich hab das jetzt mal so umgesetzt: Timersettings:
//Timer Compare Match A - 20ms TCCR1B |= 1<<CS11 | 1<<WGM12; //Prescaler: -> 50Hz OCR1A = 40000; //Prescaler Settings TIMSK |= 1<<OCIE1A; //Timer Compare Match B - 1.5 ms TCCR1B |= 1<<CS10 | 1<<WGM12; //Prescaler -> 666Hz OCR1B = 24000; TIMSK |= 1<<OCIE1B; |
Und die Interrupts:
ISR(TIMER1_COMPA_vect) //ISR für Servo PWM - durch Timerinterrupt kann während den ca. 20 ms Wartezeit etwas anderes gemacht werden { PORTB |= 1<<PINB0; //PWM HIGH Signal PORTC ^= 1<<PINC0; } ISR(TIMER1_COMPB_vect) //ISR für Timer B { PORTB &= ~(1<<PINB0); //PWM LOW Signal PORTC ^= 1<<PINC1; } |
Trotzdem tut sich nix :( Das Servo zuckt nur herum, bewegt sich aber nicht und die LEDs blinken gemächlich (ca 2 - 3 Hz) mfg thomas
Datum:
Nachtrag Der von mir gezeigte Code lässt sich übrigens problemlos umzustellen, hab's mal gemacht und das läuft hier mit 2 Servokanälen auf beliebigen Ports. Aber ich denke einmal wenigstens Dir, Karl-Heinz, ist das aufgefallen, Du wolltest wohl lediglich nicht von Deinem Konzept ablenken, kein Problem für mich. Karl Heinz Buchegger schrieb: > In der A setzt du den Pin auf 1, in der B wieder auf 0. Warum verschwendest Du einen Kanal, wenn Du ICR als Top nehmen kannst ? Reicht dem TO ein Kanal ?
Datum:
Thomas P. schrieb: > Also ich hab das jetzt mal so umgesetzt: > Timersettings: > //Timer Compare Match A - 20ms > TCCR1B |= 1<<CS11 | 1<<WGM12; //Prescaler: -> 50Hz > OCR1A = 40000; //Prescaler Settings > TIMSK |= 1<<OCIE1A; > > //Timer Compare Match B - 1.5 ms > TCCR1B |= 1<<CS10 | 1<<WGM12; //Prescaler -> 666Hz gratuliere. Damit sind deine 20ms zur Makulatur geworden. Und .... 1.5ms sind das auch nicht. Du hast nur EINEN Timer. Also willst du den Vorteiler auch nur EINMAL einstellen! Der Vorteiler gehört zum Timer und nicht zum Compare Match. Der Compare Match schreit nur "Hier", wenn der Timer einen bestimmten Zählerstand erreicht hat. Aber die Compare Match Register haben nichts damit zu tun, wie schnell der Timer zählt. (Jetzt ist es natürlich blöd, dass du bisher immer deinen Timer-Rechner zur Berechnung der Konstanten herangezogen hast. Sorry ... aber jetzt musst du selber ran und dir den Wert für OCR1B ausrechnen. Auf Dauer ist es immer besser, wenn man versteht was man da tut.)
Datum:
MWS schrieb: > Aber ich denke einmal wenigstens Dir, Karl-Heinz, ist das aufgefallen, > Du wolltest wohl lediglich nicht von Deinem Konzept ablenken, kein > Problem für mich. Spätestens nachdem ich auf die Frage nach dem Vorteiler als Antwort einen Link zu einem Timer-Rechner gekriegt habe, war mir klar, dass der TO von Timer, sagen wir mal, wenig Ahnung hat. > Karl Heinz Buchegger schrieb: >> In der A setzt du den Pin auf 1, in der B wieder auf 0. > > Warum verschwendest Du einen Kanal, wenn Du ICR als Top nehmen kannst ? > Reicht dem TO ein Kanal ? Keine Ahnung. Lass uns erst mal das Ding so wie es ist, über die Bühne bringen. Er hat schon genug damit zu tun, 2 OCR Register an einem Timer zu akzeptieren.
Datum:
Ok...das heißt...ich komm nicht mit ;) Also wie muss ich das Ganze jetzt machen? Also ich erzeug mit OCR1A einen Interrupt alle 20ms mit dem ich den Pin auf HIGH ziehe.... Und mit OCR1B einen der alle 1.5ms (1...2 ms) den Pin auf LOW legt... also muss es so passen?
//Timer Compare Match A - 20ms TCCR1B |= 1<<CS11 | 1<<WGM12; //Prescaler: -> 50Hz OCR1A = 40000; //Prescaler Settings TIMSK |= 1<<OCIE1A; //Timer Compare Match B - 1.5 ms OCR1B = 24000; TIMSK |= 1<<OCIE1B; |
mfg thomas
Datum:
Thomas P. schrieb: > also muss es so passen? Ohne das ich rechne. Wenn das ... > OCR1A = 40000; //Prescaler Settings ... 20 Millisekunden sind, dann können ... > OCR1B = 24000; ... keine 1.5 Millisekunden sein. 24-tausend ist etwas mehr als die Hälfte von 40-tausend. 1.5 ist aber sicher nicht etwas mehr als die Hälfte von 20. Der Timer zählt in 20 Millisekunden von 0 bis 40000. Wie weit kann er daher in 1.5 Millisekunden zählen? Bzw. Umgekehrt: wie weit muss er zählen, damit er dafür 1.5ms braucht?
Datum:
Nja einfache Schlussrechnung: 20ms .... 40000 1.5ms ... 3000 Also so läuft das Ganze ab ;) Und jetzt funktioniert das auch :D danke euch allen Ich werde euch noch mein fertiges Programm mit Steuerung posten! mfg thomas
Datum:
Thomas P. schrieb: > also muss es so passen? Ohne dass ich mein Werk ein weiteres Mal anpreisen möchte, aber fällt Dir was auf ? :D MWS schrieb: > ICR1 = 39999; // Wiederholrate 50Hz > OCR1A = 2000; // Servoposition Servo 1, 2000 = 1.0ms > OCR1B = 3000; // Servoposition Servo 2, 3000 = 1.5ms
Datum:
Thomas P. schrieb: > Nja einfache Schlussrechnung: > 20ms .... 40000 > 1.5ms ... 3000 > > Also so läuft das Ganze ab ;) > Und jetzt funktioniert das auch :D Jetzt setzt du noch anstelle der 40000 die Zahl 39999 ein, dann stimmts auch in diesem Detail noch. Der Timer zählt (am Beispiel '8') 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, Zähl nach, wieviele Zählvorgänge wurden gemacht, um einmal von 0 bis wieder zur 0 zu kommen? Auch das 'Rücksetzen' von 8 auf 0 ist ein Zählvorgang. 9 Stück. Also immer um 1 mehr als die größte Zahl. Der Compare Match muss bei 39999 auslösen, damit für einmal 'Rundum' 40000 Zählvorgänge benötigt werden. OK, der Fehler ist nicht gewaltig groß und wie gesagt, dem Servo sind die 20ms ziemlich Wurscht. > Also so läuft das Ganze ab ;) Genau so läuft das mit einem Timer. Die nächste Stufe wäre das was MWS vorgeschlagen hat. Die Timerhardware kann nämlich 'ihren' Pin auch ganz alleine ohne ISR schalten. Aber leider, leider, hast du dir die Pins mit denen das möglich wäre bereits anderweitig verbaut. Nichts desto trotz hab ich das Gefühl, dass du jetzt ein bischen besser verstehst, was man mit einem Timer machen kann und wie man das macht.
Datum:
Nja, ich habs mi nicht verbaut - hab das RN Control Board, und da werden diese Pins schon für den Motortreiber verwendet, und den möchte ich auch nutzen ;) Ja, um einiges besser :D Wie versprochen das Programm mit Links/Mitte/Rechts Steuerung fürs RN Control Board:
/* * servo_timer.c * * Created: 09.04.2012 22:13:40 * Author: Thomas */ /////////////////////////////////////////////////////////////////// // // Servo Steuerung mit Timer / Interrupts // /////////////////////////////////////////////////////////////////// #ifndef F_CPU #define F_CPU 16000000 //CPU Taktfrequenz #endif #define PWM_MIN 700 //Min. PWM Zeit in US #define PWM_NULL 1500 //Neutralposition - PWM Zeit in US #define PWM_MAX 2300 //Max- PWM Zeit in US #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include "rncontrol.h" volatile int pwm_time=PWM_NULL; //Globale Variable für PWM HIGH Time int main(void) { //Timer Compare Match A - 20ms TCCR1B |= 1<<CS11 | 1<<WGM12; //Prescaler: -> 50Hz OCR1A = 39999; //Timer zählt bis da - 0 Setzung und Interrrupt - Größte Zahl - 1 weil Rücksetzung auch Zählvorgang TIMSK |= 1<<OCIE1A; //Interrupt durh Timer 1 A //Timer Compare Match B - 1.5 ms OCR1B = 3000; //Timer zählt bis da - Interrupt - KEINE 0 Setzung TIMSK |= 1<<OCIE1B; //Interrupt durch Timer 1 B DDRB |= 1<<PINB0; //Ausgang für Servo aktivieren sei(); //Interrupts aktivieren while(1) { OCR1B = (39999/20000)*pwm_time; //Positionsberechnung if (button()==1) pwm_time=PWM_MAX; else if(button()==2) pwm_time=PWM_NULL; else if(button()==3) pwm_time=PWM_MIN; } } ISR(TIMER1_COMPA_vect) //ISR für Servo PWM - durch Timerinterrupt kann während den ca. 20 ms Wartezeit etwas anderes gemacht werden { PORTB |= 1<<PINB0; //PWM HIGH Signal } ISR(TIMER1_COMPB_vect) //ISR für Timer B { PORTB &= ~(1<<PINB0); //PWM LOW Signal } |
DANKE noch mal! mfg thomas
Datum:
> OCR1B = (39999/20000)*pwm_time; //Positionsberechnung
Das ist schlecht.
erstens: du hast wirklich 40000 Zählvorgänge. Also musst du auch mit
40000 rechnen.
zweitens 39999 / 20000 ergibt 1
und nicht 1.99995
d.h. bei der danach folgenden Multiplikation hast du durch die fehlenden
Kommastellen einen gewaltigen Fehler. Wenn pwm_time den Wert 1500 hat,
dann kommt bei deiner Rechnung 1500 raus, wo eigentlich 2999 rauskommen
sollte!
Divisionen immer so weit es geht im Rechenprozess nach rechts
verschieben. Dabei aber mögliche Overflows im Auge behalten! Und in
deinem Fall: kürzen!
-> 4 * pwm_time / 2
-> 2 * pwm_time
das ist dann sogar insofern Spitze, weil eine Multiplikation mit 2 für
den µC ein Klacks ist.
Bei deinen Zahlen geht sich das jetzt gut aus, denn 40000/20000 hätte
auch eine glatte 2 ergeben. Aber bei anderen Zahlen schauts da dann
schon schlechter aus.
Datum:
Thomas P. schrieb: > hab das RN Control Board, und da werden > diese Pins schon für den Motortreiber verwendet, und den möchte ich auch > nutzen ;) Und wie machst Du das ? Denn die OC1A..B gehen an den EN-Eingänge des L239, damit wird offenbar die Leistungsregelung der Motoren über HW-PWM gemacht. Dafür würde aber zwingend Timer1 benötigt, den Du ja jetzt anders verwurstet hast. Wenn Du die Leistung der Motoren noch regeln willst, müsstest Du also eine Soft-PWM über PD4..5 an den Motortreiber randengeln. Die dürfte aber anspruchsvoller und vor allem Leistungs-hungriger als diese Sache hier sein.
Datum:
Ja ok, wär noch zu verbessern, aber es funktioniert !! Ich will das Programm auch sozusagen Robotertauglich machen indem ich es auf die 8 Bit Timer umprogrammiere - und somit sind die Pins ja wieder frei, oder nicht? mfg thomas
Datum:
Thomas P. schrieb: > Ich will das Programm auch sozusagen Robotertauglich machen indem ich es > auf die 8 Bit Timer umprogrammiere - und somit sind die Pins ja wieder > frei, oder nicht? Das bedeutet jetzt übersetzt was ? Das Programm oben ? Bekommst Du direkt übersetzt nicht auf 'nen 8Bit Timer, geht allein von der Auflösung nicht. Du hast 3000 für 1.5ms bei 16Bit, bei 8Bit wären's bei gleichen Bedingungen 3000 durch 256 gleich 12, bei 'nem theoretischen Prescaler von 2048. Der höchste ist 1024, damit hättest Du ca. 16 Stufen für 180 Grad Servodrehung, bist aber mit der Wiederholrate zu schnell, die meisten Servos packen das. Aber dieser Ansatz wird Dich nicht glücklich machen.
Datum:
Direkt schon klar, aber es wird ja wohl möglich sein die ca 180° auf 128 bzw am besten wären 256 Schritte auszudehnen, oder? mfg thomas
Datum:
Thomas P. schrieb: > Direkt schon klar, aber es wird ja wohl möglich sein die ca 180° auf 128 > bzw am besten wären 256 Schritte auszudehnen, oder? Dann versuch doch mal zu rechnen, es gibt Beschränkungen bezüglich der möglichen Prescaler und schau auch mal was an Wiederholrate raus kommt und ob Deine Servos da noch mitmachen. Das Beste, was Du direkt bekommen kannst sind so um die 60 Schritte bei ca. 240Hz Wiederholrate.
Datum:
Nur so als Anhaltspunkt, das Konzept wie oben lässt sich auf Timer0 umsetzen, 65 Schritte (93-125) von 1-2ms, Wiederholrate ~49Hz, Prescaler dazu immer während des Betriebs zwischen 256 auf 1024 umschalten. Der Code dazu ist noch recht einfach, also frisch an's Werk ;-)
Datum:
Gut, danke für den Tipp Ich werd mich dann mal an die Arbeit machen und meine Fortschritte posten mfg thomas
Datum:
Hallo Ich hab versucht das Ganze in die Tat umzusetzten, ohne Erfolg :( Wie muss ich den Code denn ungefähr aufbauen? mfg thomas
Datum:
Thomas P. schrieb: > Wie muss ich den Code denn ungefähr aufbauen? Setz' es halt erstmal um den Timer0 herum so auf, dass die Pulslänge zwischen 1-2ms bei maximal möglicher Auflösung verschoben werden kann, unabhängig von der Wiederholrate. Kannst ja den Code dann zeigen. Wenn das geht, dann schaltest Du nach jedem Overflow zwischen dem von Dir dafür verwendeten und dem maximalen Prescaler hin und her. Wobei der Pin nicht in der langen Prescalerphase gesetzt werden darf. Hab's mal gezählt, in den ISR's befinden sich gerade 7 Zeilen aktiver Code, ist also nix Komplexes.
Datum:
MWS schrieb: > Wenn das geht, dann schaltest Du nach jedem Overflow zwischen dem von > Dir dafür verwendeten und dem maximalen Prescaler hin und her. Wobei der > Pin nicht in der langen Prescalerphase gesetzt werden darf. Man könnte auch einfach in einer Variablen die Anzahl der Umläufe zählen...
Datum:
STK500-Besitzer schrieb: > Man könnte auch einfach in einer Variablen die Anzahl der Umläufe > zählen... Klar, es gibt viele Wege nach Rom. Da ich zum Toggeln ein Prescalerbit teste, brauch ich aber nicht mal 'ne Variable :D
Datum:
Angehängte Dateien:Hallo Ich habs endlich geschaft :) Der Code ist im Anhang, funktioniert mit einem Servo. Das ganze reicht im Moment mal für meinen kleinen "Ketten-Bot". Brauch ich nur zum hin und her Bewegen des US Moduls Als nächstes möchte ich mir mit Hilfe eines AVR mit vielen Timern ein Servo Board realisieren, von dem werdet ihr dann natürlich hören! Welcher AVR ist dazu am Besten geeignet? Danke für eure Hilfe!! mfg thomas
Datum:
Thomas P. schrieb: > Als nächstes möchte ich mir mit Hilfe eines AVR mit vielen Timern ein > Servo Board realisieren, von dem werdet ihr dann natürlich hören! > Welcher AVR ist dazu am Besten geeignet? Der gleiche. Nur die Software sieht etwas anders aus... Du programmierst ein Schieberegister, das bei jedem Überlauf um einen Schritt weiter geschaltet wird. Dann brauchst du auch die Umschaltung der beiden Prescaler-Werte nicht mehr zu realisieren...
Datum:
Angehängte Dateien:Ja, aber das hat noch Zeit ;) Zuerst muss mal mein Bot fertig werden.... Ich hab jetzt den Code (Annhang) in eine Routine umgebaut und somit leichter zu handhaben! Aber welcher AVR ist am besten für ein Servo Board für ca 10 Servos geeignet? mfg thomas
Datum:
Thomas P. schrieb: > Als nächstes möchte ich mir mit Hilfe eines AVR mit vielen Timern ein > Servo Board realisieren, von dem werdet ihr dann natürlich hören! > Welcher AVR ist dazu am Besten geeignet? Es gibt zwar ATmegas mit 3, 4 oder 6 Timern, aber ich bin nicht sicher, ob das der richtige Weg ist. Ich glaube, ich würde nur einen Timer verwenden und in dessen ISR die Pins für die Servos selbst steuern. Also, PWM von Hand. Das ist gar nicht sooo kompliziert und läuft dann auch auf den billigsten Mikrocontrollern.
Datum:
Inwieweit von Hand?
Sozusagen:
if(timer == 0) {alle Pins LOW}
if(timer >= 15000) {PIN1 LOW}
usw...
??
mfg thomas
Datum:
Thomas P. schrieb: > Inwieweit von Hand? > Sozusagen: > if(timer == 0) {alle Pins LOW} > if(timer >= 15000) {PIN1 LOW} > usw... > ?? Im Prinzip ja. Aber natürlich nur im Prinzip, sonst schreibst du dir ja einen Wolf. :-) Man kann sowas über Bitmasken machen, die in einer Tabelle stehen. Oder über Einzelabfragen mit Wertvergleichen. Ich kenn leider deine Anwendung zu wenig, als dass ich dir zu einer konkreten Strategie raten könnte.
Datum:
Hallo zusammen, sorry, wenn ich einfach so anonym dazwischen funke. @ThomasP.: Professionell ist die Lösung mit Deinem Code überhaupt nicht. Ein atmega32 kann im FastPWM Mode ohne(!) ISR-Routine ein PWM-Signal selbständig erzeugen, es frisst keinerlei Rechenzeit vom normalen Programmablauf! Du schaltest im Setup auf FastPWM-Mode für z.B. Timer1 und setzt: ICR1 --> Maximum Counter(=TOP), bis hierher wird gezählt OCR1A --> MATCH, Umschalten von high auf low TOIE --> abschalten, Interrupt wird nicht benötigt! Prescaling und passende Werte für TOP und MATCH mußt Du Dir natürlich ausrechnen. OC1A verwendet ausschliesslich Pin PD5 (siehe Datenblatt)! Ablauf dann: Count von 0 bis MATCH -> PD5 = HIGH Count von MATCH bis TOP -> PD5 = LOW usw. Um den Servo links/rechts drehen zu lassen, veränderst Du im normalen Programmablauf einfach den MATCH in OCR1A - fertig! Eleganter geht's nicht mehr! Just my 2 cents ;)
Datum:
Thomas P. schrieb: > Aber welcher AVR ist am besten für ein Servo Board für ca 10 Servos > geeignet? "Am besten" ist immer so eine Sache. Die Steuerung mit dem Schieberegister ist nicht die schlechteste Idee (z.B. ATmega8 + 2x 74HC595) oder wie wäre es mit einer Softwarelösung: Beitrag "Servocontroller mit ATmega8 für 20 Servos" http://www.4finger.net/cms/servomaster.html
Datum:
Hallo @FastPWM: 1) Ich bin Anfänger und bin unheimlich stolz drauf gewesen, dass es so funktioniert hat ;) 2) Geht das nur für Timer1 odr auch für Timer0? 3) Wie soll ich das ohne ISR aufbauen? 4) Falls du es nicht gelesen, von dem ich ausgehe, bei mir ist Timer1 in verwendung für Motorensteuerung => somit ist auch OC1A und OC1B, also PD4/5 belegt Ok, danke für die Tipps, ich werde mal Schaltplan und Grundstruktur anfertigen und posten! mfg thomas
Datum:
FastPWM schrieb: > @ThomasP.: Professionell ist die Lösung mit Deinem Code überhaupt nicht. Die ist für ein einzelnes Servo absolut ok, wenn man keinen Timer1 zur Verfügung hat. > Just my 2 cents ;) Eher 0 Cents, wenn Du Dir den Thread nur ein klein wenig durchgelesen hast.
Datum:
Thomas P. schrieb: > 2) Geht das nur für Timer1 odr auch für Timer0? Ist es so schwer danach mal im Datenblatt zu gucken? Timer0 hat auch die Möglichkeit eine PWM zu erzeugen.
Datum:
Du kannst Timer0 im FastPWM-Modus betreiben. Ihn mit einer Spannweite von 20ms zu betreiben ist natürlich Unfug. Wie ich oben schon beschrieben habe: Betreibe den Timer so, dass du die 2ms am Höchsten auflöst. Ob das Servo alle 20ms oder mehr oder weniger häufig angesprochen wird, war meinen Servos bis jetzt egal (je länger die Pause ist, umso schwächer wurde es).
Datum:
Ja natürlich ist es nicht schwer, aber 1) keine Lust :b 2) Mein Programm läuft für ein Servo perfekt ;) 3) Bin ich im Moment nicht neugiereig drauf um auf 400 Seitn 3 Sätze rus zu suchen Aber gut, dass machn wir ein anderes Mal, dann wenn ich das Servo Board angehe ;) mfg thomas
Datum:
Thomas P. schrieb: > 1) keine Lust :b Kenne ich von mir auch. Ich hab aber nicht gefragt... > 2) Mein Programm läuft für ein Servo perfekt ;) Bis zur ersten Erweiterung. > 3) Bin ich im Moment nicht neugiereig drauf um auf 400 Seitn 3 Sätze rus > zu suchen Schon mal die Suchfunktion des Acrobat Reader's ausprobiert? Schon mal das Inhaltverzeichnis angesehen? Deine Entscheidung.
Datum:
Wieso solls dann nicht mehr funktionieren? Mein Roboter soll folgendes können: fahren und nicht anfahren - aus und fertig :b so was wie Linienverfolgung, Kamera, Funk usw. soll erst später kommen, dannn aber auch miit anderen Funktionen wie Greifarm usw.. Aber dann muss sowieso eine komplett andere Elektronik her...aber das hat noch Zeit! Ja die kenn ich ;) mfg thomas
Datum:
Thomas P. schrieb: > @FastPWM: > 1) Ich bin Anfänger und bin unheimlich stolz drauf gewesen, dass es so > funktioniert hat ;) Das ist doch auch völlig OK - jeder hat wahrscheinlich so mal angefangen. > 2) Geht das nur für Timer1 odr auch für Timer0? siehe Datenblatt - alles vorkauen möchte ich hier nicht, vielmehr möchte ich nur Tipps geben für eine evtl. in Frage kommende, bessere Lösung. > 3) Wie soll ich das ohne ISR aufbauen? Wie gesagt, für einige Servos lässt sich das einfacher und ohne ISR bewerkstelligen. Daß es möglich ist, habe ich skizzenhaft angedeutet, den Rest müsstest Du (aus meiner Sicht) selbst erarbeiten. Für mehrere, z.B. 10 Servos ist die Frage, welchen Microcontroller man verwenden will. Natürlich hat dann auch das bereits vorgeschlagene Software-PWM seine Berechtigung, wenn es nur ein kleinerer Controller sein soll. > 4) Falls du es nicht gelesen, von dem ich ausgehe, bei mir ist Timer1 in > verwendung für Motorensteuerung => somit ist auch OC1A und OC1B, also > PD4/5 belegt Es ist eine Designfrage: Muß PD4/5 unbedingt belegt werden oder kann dasselbe nicht auch an anderen Pins bewerkstelligt werden? Vor allen Dingen gibt es ja auch noch andere Timer (und Counter!). wobei zu prüfen wäre, was die jeweils können. Grundsätzlich wollte ich lediglich auf die Möglichkeit der völlig eigenständig laufenden PWM hinweisen. Zu diskutieren gibt es da eigentlich nichts - es steht ja auch alles im Datenblatt. :) ... just my 0(!) cents. ;)
Datum:
Hallo Ok, ich werd mich in nächster Zeit damit beschäftigen Also nochmals zu 4) Die Pins sind am aktuellen RN-Control Board belegt (fix) Bei einem selbstgebauten Servocontroller dann nicht, aber das ist dann ein anderes Thema.... bis dann ;) mfg thomas