Sieh mal hier wie ein Servo angesteuert wird: www.roboternetz.de/wissen/index.php/Servo
Hallo auch,
ich hab vor für meinen RP6 eine Art Fernsteuerung zu bauen.
Dazu habe ich von meinem damaligen Fluzeug noch Fernsteuerung und Empfänger sowie Servos übrig die alle noch funktionieren. (Bis auf die Akkus *g*)
Ich hatte mir überlegt, dass ich über Funk den RP6 am Anfang nur sage entweder vorwärts fahren, rückwärts fahren, links drehen oder rechts drehen. Alles einzeln also erstmal nicht manches gleichzeitig. Das kommt später evtl dazu.
Nun hab ich mir gedacht ich könnte ja das Signal des Empfängers irgendwie zur Fernsteuerung des RP6 nutzen. Da fängt aber auch schon mein Problem an. Der Empfänger gibt das empfangene Signal ja über eine Pulsweitenmodulation an die Servos weiter. Zumindest habe ich das so rausgefunden. Wie darf ich das denn aber verstehen. Pulst der zwischen 0V und +Vcc (Vcc nenn ich mal die Spannung die der über die Akkus bekommt) oder zw. -Vcc und +Vcc.
Und wenn er dann da die Pulsweite ändert, hat der dann ohne ein Signal nen Taktverhältnis von 1:1 und verschiebt das dann nur in Richtung von z.B. 2:1 bzw 1:2?
Falls der nämlich nur zw. 0 und +Vcc rumtaktet un da das Tastverhältnis ändert könnte ich ja einfach hingehen, das über ne Diode "gleichrichten" un dann an nen AD-wandler schicken. Wenn dann das Signal über dem wert von eben 1:1 ist soll er vorwärts fahren sonst rückwärts und für rechts und links eben analog dazu.
Sieh mal hier wie ein Servo angesteuert wird: www.roboternetz.de/wissen/index.php/Servo
Hallo
Wenn man zur Ansteuerung der Servos die Variante aus dem RN-Wissen verwendet, ist die Auswertung eines RC-Empfängers unglaublich einfach:
(Codeschnippsel aus meinem Dominoday-Thread)Code:ISR(TIMER0_COMP_vect) { //static uint16_t count=0; static uint16_t rc_temp_pwr=0; static uint16_t rc_temp_dir=0; //if(count>x) PORTA &= ~16; else PORTA |= 16; // E_INT1 (Pin8) //if(count>y) PORTC &= ~1; else PORTC |= 1; // SCL (Pin10) //if(count>z) PORTC &= ~2; else PORTC |= 2; // SDA (Pin12) //if(count<1000)count++; else count=0; if (PINC & 1) rc_temp_dir++; else if (rc_temp_dir) { rc_input_dir=rc_temp_dir-1; rc_temp_dir=0; } if (PINC & 2) rc_temp_pwr++; else if (rc_temp_pwr) { rc_input_pwr=rc_temp_pwr-1; rc_temp_pwr=0; } }
Das ist die Timer-ISR die normalerweise die Impulslängen der Servosignale generiert. Mit ihr kann man auch sehr bequem die Impulslängen des Empfängers messen. Die lokalen Variablen rc_temp_pwr und rc_temp_dir sind die Hilfsvariablen für die Impulsmessung, rc_input_pwr und rc_input_dir die globalen Variablen für die Werte der Kanäle die man dann im Programm auswerten kann.
Das funktioniert dann so:
GrußCode:if (PINC & 1) rc_temp_dir++; // solange der Impuls anliegt wird der Zähler erhöht else if (rc_temp_dir) // Es liegt kein Impuls an und der Zähler ist !=0 rc_input_dir=rc_temp_dir-1; // dann ist der gezählte Wert die Impulsdauer rc_temp_dir=0; // Zähler wieder rücksetzen, Ergebniss steht jetzt in rc_input_dir
mic
Bild hier
Atmel’s products are not intended, authorized, or warranted for use
as components in applications intended to support or sustain life!
Ich danke euch schonmal.
Hab aber gleich noch ein paar weitere Fragen an radbruch.
Zum einen, ich hab meinen RP6 noch nicht sehr lange. Ich nehme mal an das
PINC & 1 so viel bedeutet wie PortC Pin 1 oder?
Und andererseits, wenn er solange den Zähler erhöhen soll wie der Impuls anliegt, dann kann ich ja erst darauf durch fahren reagieren wenn der komplette Impuls anliegt. Dann kann der ja nur Stück für Stück fahren oder hab ich da grad einen Denkfehler.
Und wirklich genau wird das doch auch erst wenn ein Schleifendurchlauf des kompletten Programmes dann möglichst kurz ist.
Mein RP6 soll eben ein automatisches Programm erledigen bis ich anfangen ihn zu steuern. Wenn ich aufhöre zu steuern soll er auch nach kurzer Wartezeit wieder mit dem automatischen Programm anfangen.
Hallo
Das sind die SDA/SCL-Anschlüsse des XBUS-Steckers. (Es ist aber PortC.0 mit der Wertigkeit 1, ATMega32-Pin 22(SCL)) Ich hatte mir mal für den XBUS einen kleinen Adapter gebastelt:Ich nehme mal an das PINC & 1 so viel bedeutet wie PortC Pin 1 oder?
https://www.roboternetz.de/phpBB2/ze...=322579#322579
Mach dir mal bewusst, dass die ISR 100000 mal pro Sekunde aufgerufen wird! Das ergibt 50 Impulse pro Sekunde (=alle 20ms), also 50 Messwerte pro Sekunde....dann kann ich ja erst darauf durch fahren reagieren wenn der komplette Impuls anliegt. ...hab ich da grad einen Denkfehler?
Wir verwenden einen Timerinterrupt, deshalb sind wir unabhängig vom Hauptprogramm das nur die rc_input_x-Werte auswerten muss. Übrigens, was bedeutet in diesem Zusammenhang "wirklich genau"? Wirklich entschiedend ist doch nur, dass die selbe Stellung des RC-Steuerknüppels immer mit dem selben Wert gemessen wird. Je nach gewünschter Auflösung kann man den Timertakt variieren um die Anzahl der ISR-Aufrufe zu optimieren.Und wirklich genau wird das doch auch erst wenn ein Schleifendurchlauf des kompletten Programmes dann möglichst kurz ist.
Dazu muss das automatische Programm prüfen ob ein RC-Input anliegt(rc_input_x != 0) Wenn ein Signal erkannt wird, folgt der RP6 der Fernsteuerung, wenn kein Signal erkannt wird (rc_inputx == 0) geht er wieder in den Automatik-Modus.Mein RP6 soll eben ein automatisches Programm erledigen bis ich anfange ihn zu steuern.
Obwohl sich das alles recht einfach anhört, der Hacken ist die orginale Library des RP6. Sie verwendet alle Timer und somit funktioniert meine (und die im RN-Wissen) Lösung nicht ohne Tricks auf dem RP6. Mein hier gezeigter Weg ist eine minimale Lib mit den nötigsten Funktionen. Eine Alternative wäre z.b. den ADC-Interrupt zu nutzen.
Gruß
mic
Bild hier
Atmel’s products are not intended, authorized, or warranted for use
as components in applications intended to support or sustain life!
Ach mensch bin ich blöd. Hab nicht gesehen das des von der Interrupt Service Routine erledigt wird. Ok dann ist es ja klar
Programm laufen lassen, ISR misst den Impuls un speichert dessen länge in ner Variablen, das Programm ruft die variable ab und handelt entsprechend oder eben nicht un läuft weiter, dann das ganze wieder von vorn.
danke dir
Hallo
Ohne Interrupts, dafür aber blockierend könnte es so funktionieren (noch nicht getestet):
Kleiner Überschlag: Bei 8MHz und 2ms Impulslänge und Erhöhung des Zählers bei jedem Takt: 0,002s/(1/8000000)=0,002/0,000000125=16000Code:uint16_t get_RC(uint8_t kanal) { uint16_t count=0; if(kanal==1) // Erster RC-Kanal { while(PINC & 1); // warten bis Ende des aktuellen Impuls while(!(PINC & 1)); // warten auf Ende Pause while(PINC & 1) count++; // Impulslänge messen } if(kanal==2) // Zweiter RC-Kanal { while(PINC & 2); while(!(PINC & 2)); while(PINC & 2) count++; } return(count); // Zählwert übergeben }
Weil "while(PINC & 1) count++;" aber sicher mehr als einen Takt benötigt wird dieser Wert nie erreicht und eine 16-Bit-Variable reicht als Zähler.
Gruß
mic
Bild hier
Atmel’s products are not intended, authorized, or warranted for use
as components in applications intended to support or sustain life!
Nochmal hallo
Nun habe ich die Funktion getestet, wie erwartet funktioniert das wunderbar:
Das Progamm steuert per ISR ein Servo an Pin ADC0 an. Gleichzeitig werden im Hauptprogramm am selben Pin die Impulslängen gemessen und angezeigt. (Ein auf Ausgang geschalteter Pin kann auch eingelesen werden: IR-Kommunikation mit dem RP6) Mit Prescaler /8 und OCR0=13 ist der Wert für Servomitte ungefähr 100 (Basis des Programms ist der Testcode meines aktuellen "Projekts"). Die Ausgabe am Terminal sieht so aus:Code:// RC-Signal messen und auswerten 2.5.2008 mic #include "rblib.h" #include "rblib.c" uint8_t servopos; volatile uint16_t p=0; void pause(uint16_t p_dauer) { p=p_dauer; while(p); } uint16_t get_RC(uint8_t kanal) { uint16_t count=0; if(kanal==1) // Erster RC-Kanal { while(PINA & 1); // warten bis Ende des aktuellen Impuls while(!(PINA & 1)); // warten auf Ende Pause while(PINA & 1) count++; // Impulslänge messen } return(count); // Zählwert übergeben } int main(void) { rblib_init(); DDRA |= 1; servopos=0; TCCR0 = (0 << WGM00) | (1 << WGM01); // CTC-Mode TCCR0 |= (0 << COM00) | (0 << COM01); // ohne OCR-Pin TCCR0 |= (0 << CS02) | (1 << CS01) | (0 << CS00); // prescaler /8 TIMSK = (1 << OCIE0); // Interrupt ein OCR0 = 13; sei(); servopos=40; while(1) { writeInteger(servopos, 10); writeString(" - "); writeInteger(get_RC(1), 10); writeString("\n\r"); servopos+=10; if(servopos>170) { servopos=40; writeString("\n\r"); pause(50); } pause(10); } return 0; } ISR(TIMER0_COMP_vect) { static uint16_t count=1; if(count>servopos) PORTA&=~1; else PORTA|=1; if(count<2000)count++; else { count=1; if(p) p--; } }
Im Anhang nochmals meine minimale Lib auf Basis der RP6-Lib.Code:Terminal cleared! [RP6BOOT] [READY] 40 - 368 50 - 460 60 - 552 70 - 644 80 - 736 90 - 828 100 - 920 110 - 1012 120 - 1104 130 - 1196 140 - 1288 150 - 1380 160 - 1472 170 - 1564 40 - 368 50 - 460 60 - 552
Gruß
mic
Bild hier
Atmel’s products are not intended, authorized, or warranted for use
as components in applications intended to support or sustain life!
Vielen Dank. Ich werde das dann gleich mal probieren wenn ich endlich dazu gekommen bin das mal einzubauen.
Hi radbruch,
meintest du bei
void pause(uint16_t p_dauer)
{
p=p_dauer;
while(p);
}
nicht doch eher ein
ohne ein p--; kann das irgendwie keine andere Möglichkeit als eine dauerschleife geben, beispiel bei pause(10); :Code:void pause(uint16_t p_dauer) { p=p_dauer; while(p) p--; }
p=10
while(p);//10 halt
irgendwie sieht das für mich sehr nach dauerschleife aus
MfG Pr0gm4n
Lesezeichen