PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : RP6 Fernsteuerung über RC-Empfänger



vogel0815
01.05.2008, 12:14
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.

Hubert.G
01.05.2008, 12:42
Sieh mal hier wie ein Servo angesteuert wird: www.roboternetz.de/wissen/index.php/Servo

radbruch
01.05.2008, 13:18
Hallo

Wenn man zur Ansteuerung der Servos die Variante aus dem RN-Wissen verwendet, ist die Auswertung eines RC-Empfängers unglaublich einfach:


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; }
}

(Codeschnippsel aus meinem Dominoday-Thread (https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=328824#328824))

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:


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

Gruß

mic

vogel0815
01.05.2008, 14:47
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.

radbruch
01.05.2008, 16:01
Hallo


Ich nehme mal an das PINC & 1 so viel bedeutet wie PortC Pin 1 oder?
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:

https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=322579#322579


...dann kann ich ja erst darauf durch fahren reagieren wenn der komplette Impuls anliegt. ...hab ich da grad einen Denkfehler?
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.


Und wirklich genau wird das doch auch erst wenn ein Schleifendurchlauf des kompletten Programmes dann möglichst kurz ist.
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.


Mein RP6 soll eben ein automatisches Programm erledigen bis ich anfange ihn zu steuern.
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.

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

vogel0815
02.05.2008, 08:45
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

radbruch
02.05.2008, 16:14
Hallo

Ohne Interrupts, dafür aber blockierend könnte es so funktionieren (noch nicht getestet):

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
}

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=16000

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

radbruch
02.05.2008, 17:57
Nochmal hallo

Nun habe ich die Funktion getestet, wie erwartet funktioniert das wunderbar:

// 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--; }
}

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 (https://www.roboternetz.de/phpBB2/zeigebeitrag.php?t=32585)) 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:

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

Im Anhang nochmals meine minimale Lib auf Basis der RP6-Lib.

Gruß

mic

vogel0815
05.05.2008, 07:53
Vielen Dank. Ich werde das dann gleich mal probieren wenn ich endlich dazu gekommen bin das mal einzubauen. :)

Pr0gm4n
05.05.2008, 13:56
Hi radbruch,

meintest du bei



void pause(uint16_t p_dauer)
{
p=p_dauer;
while(p);
}



nicht doch eher ein



void pause(uint16_t p_dauer)
{
p=p_dauer;
while(p) p--;
}


ohne ein p--; kann das irgendwie keine andere Möglichkeit als eine dauerschleife geben, beispiel bei pause(10); :

p=10
while(p);//10 halt


irgendwie sieht das für mich sehr nach dauerschleife aus


MfG Pr0gm4n

KayH
05.05.2008, 18:23
@Pr0gm4n: schau dir diese zeile mal genau an:


volatile uint16_t p=0;


das schluesselwort "volatile" ist wichtig ...
es bedeutet, das der Inhalt der Variable sich durch "aeussere" Einfluesse aendern kann

btw. da eine ISR kein Rueckgabewert hat, wird ueber volatil definierte Variablen der Datenaustausch geregelt

hth
KaY

radbruch
05.05.2008, 18:25
Hallo Pr0gm4n

Wenn ich schreibe:

Nun habe ich die Funktion getestet, wie erwartet funktioniert das wunderbar
dann habe ich es auch getestet und es funktioniert. Das ist p:

volatile uint16_t p=0;

und wird in der ISR runtergezählt sofern sein Wert !=0 ist:

if(count<2000)count++; else { count=1; if(p) p--; }

Gruß

mic

Pr0gm4n
06.05.2008, 15:30
Hi radbruch,

sry, mein Fehler...


hab das irgendwie übersehen, aber danke für die nette Erklärung

ich meinte ja nur, dass es beim "testen" auch manchmal (wie bei mir letztes mal) einfach klappt, weil bestimmte programmabschnitte garnicht aufgerufen werden bzw. nicht so sehr auffallen wenn dann Dauerpause ist


MfG Pr0gm4n

radbruch
06.05.2008, 16:45
Die Pause ist in der Hauptschleife. Es wäre ungünstiig wenn es eine endlose Pause wäre...


sry, mein Fehler...
kein Problem

vogel0815
07.05.2008, 19:08
Also ich weiß ja nich wie du das gemacht hast mit der abgespeckten RP6-Library aber egal wie ich es dreh und wende ich bekomm imme rnur haufenweise Warnungen und Fehler wenn ich das 1 zu 1 kopierte kompilieren will.

Um es besser zu erklären,
den code oben hab ich 1 zu 1 kopiert und in ne C datei gepoackt.
Das makefile hab ich dahingehend angepasst das ich eben den namen der c datei als target eingetragen hab.
Nur was ich mit den 2 Library files machen muss weiß ich noch nicht so genau.
In einen eigenen ordner in RP6Lib kopieren und diesen unter RP6_LIB_PATH_OTHERS= eintragen hat nichts gebracht. Auch den Ordner direkt angeben als RP6_LIB_PATH=
hat nichts gebracht.

Was ist an deinen Librarys denn anders bis auf die tatsache das da einiges fehlt was in den normalen drin ist?

radbruch
08.05.2008, 07:26
Hallo


Was ist an deinen Librarys denn anders bis auf die tatsache das da einiges fehlt was in den normalen drin ist?
Es sind nur ein paar grundsätzliche Funktionen die man in erster Linie zum Debugen benötigt. Das Tasksystem und die Stopwatches fehlen komplett, dafür sind aber auch die betreffenden Timer frei und können beliebig genutzt werden.

Mit den makefiles kenne ich mich nicht aus, ich verwenden kamavr, das erstellt die makefiles automatisch. Die Dateien sollten sich im selben Verzeichniss wie der Quellcode des Programms befinden.

Gruß

mic

vogel0815
08.05.2008, 12:41
Hmmm hab mir mal nun das kamavr runtergalden (http://www.avrfreaks.net/index.php?module=Freaks%20Tools&func=viewItem&item_id=632)
Hab dort dann ein neues Projekt erstellt, ne c Datei mit dem Code von dir hinzugefügt, und dann die zwei Bilbiothek-Files von dir ebenfalls ins Projekt integriert und in den gleiche ordner geschoben wo das projekt gespeichert wird.
Dann noch den CPU Typ von atmega128 auf atmega32l gestellt (auf der mainplatine is es ja nen atmega32l) wenn ich dann ihm sag er soll compilieren oder er soll des make mahen oder des build bringt er mir 90 fehler und 2 Warnungen. Als erstes bringt er die Meldung:
MCU 'atmega32l' supported for assembler only

Wie darf ich denn das verstehen?

Wenn ich atmega32 als CPU Typ eintrage compiliert er mir zwar fehlerlos, aber wenn ich dort dann make oder build sag bringt er mir 54 Fehler und 2 Warnungen.

Tut mir leid das ich so viele Fragen stell, aber ich bastel das erste mal mit dieser Software rum. Und ich muss erstmal hinbekommen das die so funktioniert wie ich will, bzw muss ich erstmal wissen wie die funktioniert.


Was anderes, immer wenn ich kamavr starte bringt er die Meldung er findet den WinAVR Ordner nicht. Wenn ich ihm zeige wo er is, is er zufrieden. Kann man das nicht irgendwo fest eintragen?


EDIT: Bin nun nach weiterem probieren und studieren etwas weiter gekommen.
Hab mir als erstes mal die letzte Version von kamavr gezogen (dieses kamavrwork das ist nun v 5.6.5.56) und dort dann weiter gebastelt.
Das problem mit dem winavr ordner und dem CPU Typ ist zwar immer noch, aber wenn ich jetzt mit atmega32 das mache compiliert er mir ohne Probleme. Wenn ich aber nen make mache bringt er irgendwann die Meldung "Done, with unknown error."
Lediglich im log steht dann das

avr-gcc -mmcu=atmega32 -Os -mno-interrupts -funsigned-char -funsigned-bitfields -Wall -Wstrict-prototypes -gstabs -c -DF_CPU=8000000UL -Wa,-acdhlmns=Fernsteuerung.lst Fernsteuerung.c -o Fernsteuerung.o
avr-gcc -mmcu=atmega32 -Os -mno-interrupts -funsigned-char -funsigned-bitfields -Wall -Wstrict-prototypes -gstabs Fernsteuerung.o -o Fernsteuerung.elf -Wl,-Map=Fernsteuerung.map --cref -lm
avr-objcopy -j .text -j .data -O ihex Fernsteuerung.elf Fernsteuerung.hex
avr-objcopy -j .eeprom --change-section-lma .eeprom=0 -O ihex Fernsteuerung.elf Fernsteuerung_eeprom.hex
avr-objcopy: there are no sections to be copied!
avr-objcopy: --change-section-lma .eeprom=0x00000000 never used
make: *** [Fernsteuerung_eeprom.hex] Error 1
2


EDIT2: Wie ich nach ettlichem hin und her gesehen hab hat die fehlermeldung nichts mit meinem Programm an sich zu tun. Wenn man genau hinschaut sieht man ja das er nur meckert das des hex-File für den EEProm keine Daten enthällt. Is ja schließlich auch nich gedacht das ich da irgendwas in nen eeprom speichern will.
Die Programme laufen nun. Nun muss ich nur endlich mal die ganzen Anschlüsse für das proejtk hier basteln un dann kann ich das alles testen :)))
Danke euch allen