-
Mein originalercode 16Mhz:
Code:
#include <avr/io.h> //I/O Port definitions
#include <avr/interrupt.h> //Interrupt macros
#include <util/twi.h> //TWI STATUS
#define F_CPU 16000000UL //CPU Tackt
/*----------------------------------------------------------------------------------------*/
//PWM
/*----------------------------------------------------------------------------------------*/
//1000 = 1ms(links), 1500 = 1,5ms(mitte), 2000 = 2ms(rechts)
volatile int schulter= 0;
volatile int huefte = 0;
volatile int knie = 0;
void pwm_init(int schulter, int huefte, int knie); //PWM_init Funktion
void pwm_chance(int schulter, int huefte, int knie);//Anpassen der PWM grössen
/*----------------------------------------------------------------------------------------*/
//MAIN AUFRUF
/*----------------------------------------------------------------------------------------*/
int main(void)
{
DDRB = 0xFF; //B... AUSGANG
PORTB &= ~((1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3)); //B.. Low
sei(); //Globale Interupts zulassen
pwm_init(schulter, huefte, knie); //PWM initialisieren
while(1)
{
}
}
ISR(TIMER2_OVF_vect) //Überlaufinterupt Timer2
{
TCCR2 = 0x00; //Löschen von Timer2
}
ISR(TIMER1_CAPT_vect) //Captureinterupt Timer1
{
TCCR2 = (1<<WGM20) | (1<<WGM21) |(1<<COM21) | (1<<CS22) | (1<<CS20); //Setzten von Timer2
}
void pwm_init(int schulter, int huefte, int knie) //PWM initialisieren
{
TIMSK |= (1<<TICIE1) | (1<<TOIE2); //Interupt initialisieren, freischalten
//Timer 1 (Port 0/1)
TCCR1A = (1<<COM1A1)| (1<<COM1B1); //Clear OC on Comp Match when up-count, set downcount
TCCR1B = (1<<WGM13) | (1<<CS11) ; //PWM Phase abd Freq Correct. Prescaler 8
ICR1 = 20000; //Periodendauer Servo 20ms
OCR1A = schulter; //Servosignal (Port 0, Schulter)
OCR1B = huefte; //Servosignal (PORT 1, Hüfte)
//Timer 2 (Port 2)
TCCR2 = (1<<WGM20)| (1<<WGM21) | (1<<COM21) | (1<<CS22) | (1<<CS20); //Fast PWM
OCR2 = (knie/8); //Servosignal (Port2, Knie)
}
Ich muss den Code noch auf 8mhz anpassen. Ich versuche noch heute dazu zu kommen.
-
A propos... wenns nicht klappt (aus welchen Gründen auch immer), könnte ich dir auch noch ein Software-PWM anbieten. Es verbraucht zwar etwas Rechenleistung (um genau zu sein ca. 2,5 von 20ms), aber es funktioniert.
Aber natürlich werden wir zuerst versuchen es so hinzubekommen, wie es jetzt ist.
Ich bin gerade dabei, ein Software-PWM zu schreiben (in ASM), dass in 2,5 von 20ms gleichzeitig 18 Servos bedienen kann (für meinen Hexa). Ob es gelingt, weiß ich noch nicht, aber mit 5 von 20ms sollte es sicherlich funktionieren).
Dann könntest auch du dieses Prog beutzen, um alle Servos gleichzeitig mit einem Controller anzusteuern. Ich schreibe das Prog für einen Atmega 644, weil der 20MHz kann und mehr pins hat.
Gruß, Yaro
-
Ursprünglich habe ich auch mit einem Atmega644 mit 20Mhz gearbeitet und Software PWM eingesetzt. Das hat aber nicht zufriedenstellend funktioniert.
Deshalb diese Lösung.......
(Bin nicht zuhause, deshalb auch noch kein angepasster Code)
-
Hier der Code, sorry hat etwas gedauert:
Code:
#include <avr/io.h> //I/O Port definitions
#include <avr/interrupt.h> //Interrupt macros
#include <util/twi.h> //TWI STATUS
#define F_CPU 16000000UL //CPU Tackt
/*----------------------------------------------------------------------------------------*/
//PWM
/*----------------------------------------------------------------------------------------*/
//750 = 1.5ms
volatile int schulter= 750;
volatile int huefte = 750;
volatile int knie = 750;
void pwm_init(int schulter, int huefte, int knie); //PWM_init Funktion
void pwm_chance(int schulter, int huefte, int knie);//Anpassen der PWM grössen
/*----------------------------------------------------------------------------------------*/
//MAIN AUFRUF
/*----------------------------------------------------------------------------------------*/
int main(void)
{
DDRB = 0xFF; //B... AUSGANG
PORTB &= ~((1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3)); //B.. Low
sei(); //Globale Interupts zulassen
pwm_init(schulter, huefte, knie); //PWM initialisieren
while(1)
{
}
}
ISR(TIMER2_OVF_vect) //Überlaufinterupt Timer2
{
TCCR2 = 0x00; //Löschen von Timer2
}
ISR(TIMER1_CAPT_vect) //Captureinterupt Timer1
{
TCCR2 = (1<<WGM20) | (1<<WGM21) |(1<<COM21) | (1<<CS22) | (1<<CS20); //Setzten von Timer2
}
void pwm_init(int schulter, int huefte, int knie) //PWM initialisieren
{
TIMSK |= (1<<TICIE1) | (1<<TOIE2); //Interupt initialisieren, freischalten
//Timer 1 (Port 0/1)
TCCR1A = (1<<COM1A1)| (1<<COM1B1); //Clear OC on Comp Match when up-count, set downcount
TCCR1B = (1<<WGM13) | (1<<CS11) ; //PWM Phase abd Freq Correct. Prescaler 8
ICR1 = 10000; //Periodendauer Servo 20ms
OCR1A = schulter; //Servosignal (Port 0, Schulter)
OCR1B = huefte; //Servosignal (PORT 1, Hüfte)
//Timer 2 (Port 2)
TCCR2 = (1<<WGM20)| (1<<WGM21) | (1<<COM21) | (1<<CS22) | (1<<CS20); //Fast PWM
OCR2 = (knie/8); //Servosignal (Port2, Knie)
}
Müsste so für 8Mhz passen
-
soooooooo =)
ich hab mir das alles nochmal angeschaut, und tatsächlich hat es bei mir auch nicht funktioniert (was zu erwarten war).
Komisch war es dennoch, denn... der Servo ließ sich per Hand zwar in die eine Richtung drehen, in die andere sperrte er aber. Ich kenn mich zwar mit der Elektronik von Servos nichtt richtig aus, aber es deutet schon darauf hin, dass da irgendwo was kleines schief läuft.
Dann hab ich mich nochmal an deine Spannungsspitze erinnert und versucht, in der Software eine Erklärung dafür zu finden, und wurde fündig (zumindest glaube ich, dass es "diese" Spitze war (hab wie gesagt grad kein Oszi zur Hand).
Und zwar ist das Problem folgendes: Beim Overflow-Interrupt wird ja der Timer abgestellt, was dazu führt, dass die PWM-Unit nicht mehr die Kontrolle über den Port hat, sondern sie an die normalen Portbefehle übergibt. Und dabei kommt es wahrscheinlich zu dieser Spitze.
Wie ändert man das nun? Ganz einfach! Man schaltet den Timer nicht mit dem overflow-interrupt, sondern mit dem compare-interrupt ab. Zu den Zeitpunkt macht die Spannungsspitze nämlich nix aus =)
Ich hab den Code ausprobiert, er funktioniert!
Code:
#include <avr/io.h> //I/O Port definitions
#include <avr/interrupt.h> //Interrupt macros
#include <util/twi.h> //TWI STATUS
/*----------------------------------------------------------------------------------------*/
//PWM
/*----------------------------------------------------------------------------------------*/
//750 = 1.5ms
volatile int schulter= 750;
volatile int huefte = 750;
volatile int knie = 750;
void pwm_init(int schulter, int huefte, int knie); //PWM_init Funktion
//void pwm_chance(int schulter, int huefte, int knie);//Anpassen der PWM grössen
/*----------------------------------------------------------------------------------------*/
//MAIN AUFRUF
/*----------------------------------------------------------------------------------------*/
int main(void)
{
DDRB = 0xFF; //B... AUSGANG
PORTB &= ~((1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3)); //B.. Low
sei(); //Globale Interupts zulassen
pwm_init(schulter, huefte, knie); //PWM initialisieren
while(1)
{
}
}
ISR(TIMER2_COMP_vect) //Überlaufinterupt Timer2
{
TCCR2 = 0x00; //Löschen von Timer2
}
ISR(TIMER1_CAPT_vect) //Captureinterupt Timer1
{
TCNT2 = 0;
TCCR2 = (1<<WGM20) | (1<<WGM21) |(1<<COM21) | (1<<CS22) | (1<<CS20); //Setzten von Timer2
}
void pwm_init(int schulter, int huefte, int knie) //PWM initialisieren
{
TIMSK |= (1<<TICIE1) | (1<<TOIE2); //Interupt initialisieren, freischalten
//Timer 1 (Port 0/1)
TCCR1A = (1<<COM1A1)| (1<<COM1B1); //Clear OC on Comp Match when up-count, set downcount
TCCR1B = (1<<WGM13) | (1<<CS11) ; //PWM Phase abd Freq Correct. Prescaler 8
ICR1 = 10000; //Periodendauer Servo 20ms
OCR1A = schulter; //Servosignal (Port 0, Schulter)
OCR1B = huefte; //Servosignal (PORT 1, Hüfte)
//Timer 2 (Port 2)
TCCR2 = (1<<WGM20)| (1<<WGM21) | (1<<COM21) | (1<<CS22) | (1<<CS20); //Fast PWM
OCR2 = (knie/8); //Servosignal (Port2, Knie)
}
Gruß, Yaro
-
Vielen Dank für deine bemühungen.
Ich habs jetzt ausprobiert. Und es hat funktioniert.. fast. Der Servo hat nicht richtig kraft und knurrt :)
Was auf ein falsches Timing des Signals deutet.
Und mit dem dem Oszi kontrolliert hat sich gezeigt, das die Perioden dauer zu kurz ist(2ms anstatt 20ms).
Und ich frage mich wieso du TCBT2 auf 0 setzt in der Capture ISR, das führt bei mir zu einem unsymetrischen Signal...
Ich bin heut irgendwie echt nicht ganz fit, ich versteh mein eigenes Programm nicht mehr richtig ](*,)
-
Wir lassen den Timer ja nicht mehr zuende laufen, sondern brechen bei dem compare-Wert ab, was bedeutet dass der Timer sich nicht selber auf 0 stellt, sondern bei dem compare-Wert bleibt. Wenn wir ihn nicht zurückstellen, dann wird das Signal unregelmäßig, denn beim ertsen mal bricht er nach dem comp-Wert ab, beim zweiten läuft er erstmal zuende durch, fängt von vorne an und bricht wieder beim comp-Wert ab, was natürlich einen längeren Impuls zufolge hat.
Bist du dir sicher, dass du das Programm richtig abgeschrieben hast? Bei mir funktioniert es nämlich so wie es ist ganz gut, und der Servo hat auch die volle Kraft. Versuch es doch mal bei dir reinzukopieren, und nur die Prescaler zu ändern.
a propos.... ich habe auch noch die #define F_CPU 16000000UL weggenommen.....die solltest du auch wieder setzten, sonst funktioniert es nicht richtig.
Bin mal gespannt, obs jetzt klappt.
Gruß, Yaro
-
Ich denke schon das ich es richtig übernommen habe.
Die einzigen änderungen sind ja:
Code:
ISR(TIMER2_COMP_vect)
und
in der ISR TIMER1_CAPT_vect
TCNT2 = 0;
die CPU definition hab ich drin und natürlich den rest an 16Mhz angepasst.
Aber da scheint echt was nicht zu passen.
So siehts jetzt aus:
Code:
#include <avr/io.h> //I/O Port definitions
#include <avr/interrupt.h> //Interrupt macros
#include <util/twi.h> //TWI STATUS
#define F_CPU 16000000UL //CPU Tackt
/*----------------------------------------------------------------------------------------*/
//PWM
/*----------------------------------------------------------------------------------------*/
//750 = 1.5ms
volatile int schulter= 1500;
volatile int huefte = 1500;
volatile int knie = 1500;
void pwm_init(int schulter, int huefte, int knie); //PWM_init Funktion
void pwm_chance(int schulter, int huefte, int knie);//Anpassen der PWM grössen
/*----------------------------------------------------------------------------------------*/
//MAIN AUFRUF
/*----------------------------------------------------------------------------------------*/
int main(void)
{
DDRB = 0xFF; //B... AUSGANG
PORTB &= ~((1<<PORTB1) | (1<<PORTB2) | (1<<PORTB3)); //B.. Low
sei(); //Globale Interupts zulassen
pwm_init(schulter, huefte, knie); //PWM initialisieren
while(1)
{
}
}
ISR(TIMER2_COMP_vect) //Überlaufinterupt Timer2
{
TCCR2 = 0x00; //Löschen von Timer2
}
ISR(TIMER1_CAPT_vect) //Captureinterupt Timer1
{
TCNT2 = 0;
TCCR2 = (1<<WGM20) | (1<<WGM21) |(1<<COM21) | (1<<CS22) | (1<<CS20); //Setzten von Timer2
}
void pwm_init(int schulter, int huefte, int knie) //PWM initialisieren
{
TIMSK |= (1<<TICIE1) | (1<<TOIE2); //Interupt initialisieren, freischalten
//Timer 1 (Port 0/1)
TCCR1A = (1<<COM1A1)| (1<<COM1B1); //Clear OC on Comp Match when up-count, set downcount
TCCR1B = (1<<WGM13) | (1<<CS11) ; //PWM Phase abd Freq Correct. Prescaler 8
ICR1 = 20000; //Periodendauer Servo 20ms
OCR1A = schulter; //Servosignal (Port 0, Schulter)
OCR1B = huefte; //Servosignal (PORT 1, Hüfte)
//Timer 2 (Port 2)
TCCR2 = (1<<WGM20)| (1<<WGM21) | (1<<COM21) | (1<<CS22) | (1<<CS20); //Fast PWM
OCR2 = (knie/8); //Servosignal (Port2, Knie)
}
-
hmm, das ist wirklich sehr ungewöhnlich....
Ich weiß jetzt aber, wieso der dritte Servo bei dir um 10ms verschoben ist: das ist so, weil du beim 16bit Timer ein phase-correct PWM benutzt, da ist der Overflow natürlich in der Mitte der Ganzen Zeit, und nicht am Ende, wie beim Fast-PWM. Das sollte aber keine Probleme bereiten.
Die Periodenlängen und so sind alle richtig umgestellt.
Könntest du vielleicht noch einige Messungen mit dem Oszi hochladen?
Die vom neuen Programm.
Das ist wirklich komisch alles...
Gruß, yaro
-
ok, ich stell gleich mal ein paar fotos hoch
Der neue Code erzeugt folgendes Signal ( Bild 1 und 2)
Bild hier
Bild hier
Und dieses Signal ist ohne das auf Null setzen von TCNT2
Bild hier