PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : RP6 über Joystick steuern (in VB2008)



Mc Delta
15.09.2010, 16:13
Hallo,
Ich habe mir in Visual Basic 2008 ein kleines Programm geschrieben, welches mir ermöglicht die Positioswerte eines Joysticks zu ermitteln.
Ziel dieses Projekts ist es, dass man den RP6 mit einem Joystick über UART steuern kann. Das Visual Basic Programm funktioniert soweit,
aber bei dem Empfangen von Daten mit dem RP6 haperts noch.

Hier sind ein paar Download-Links:
Joystick.exe (Prototyp) (http://mcdelta.bplaced.net/programme/Joystick.exe)
Joystick.exe (Quellcode) (http://mcdelta.bplaced.net/programme/Joystick.txt)

Und hier der Code für den RP6:
#include "RP6RobotBaseLib.h"

int main(void)
{


initRobotBase();
powerON();
void bewegdich()
{
char receiveBuffer[5]; // <<-- da kommt der Text rein
uint16_t value[5]; // Array deklarieren
clearReceptionBuffer(); // Empfangspuffer leeren

uint8_t buffer_pos = 0;
while(buffer_pos < 4) // Bis 4 Zeichen empfangen sind
{
if(getBufferLength()) // Sind neue Zeichen da?
{
setLEDs(0b111111);
receiveBuffer[buffer_pos] = readChar(); // Ein Zeichen lesen
value[buffer_pos] = atoi(receiveBuffer[buffer_pos]); //Zahlenwert in Array schreiben
buffer_pos++; // um eins erhöhen
}
}
uint8_t richtung = value[0] ; // Variablen die empfangenen Werte zuweisen
uint8_t geschwindigkeit = value[1];
uint8_t r= value[2];
uint8_t l= value[3];

if (richtung == 1 )
{
changeDirection(FWD);
moveAtSpeed(l*geschwindigkeit,r*geschwindigkeit);
}
if (richtung == 2 )
{
changeDirection(BWD);

moveAtSpeed(l*geschwindigkeit,r*geschwindigkeit);
}
if (richtung == 3 )
{
rotate(geschwindigkeit, LEFT, 30,true);
}
if (richtung == 4 )
{
rotate(geschwindigkeit, RIGHT, 30,true);
}


}

while(true)
{

task_RP6System();
bewegdich();

}
}

Jeder der einen Joystick hat kann das Programm ausprobieren. (Framework 3.5 erforderlich)
Vielleicht kann mirja auch jemand mit dem Code vom RP6 helfen.
Er zeigt mir zwar, dass er Daten empfängt (durch das blinken der LED´s), aber er fährt nicht.
Würde mich über Verbesserungsvorschläge freuen.

Mc Delta
17.09.2010, 15:01
Hat sich schon jemand das Programm angeschaut? Wiegesagt wenn ihr Verbesserungsvorschläge habt, oder Fehler entdeckt, schreibt es mir.

Martinius11
17.09.2010, 15:15
Ich schätze du soltest Else If nehmen. Auserdem wird wahr scheinlich das Komando immer wieder gelöscht ich würde Ausserhalb der Hauptschleife einmal den Puffer leeren und dann nur nach jedem ausgeführten Komando.

radbruch
17.09.2010, 15:21
Hallo

Ich hatte mal ein Poti auf die Antriebe des RP6 geschaltet, Wertebereich war von 0 bis 1023:

https://www.roboternetz.de/phpBB2/viewtopic.php?p=452519#452519

Das berücksichtigt allerdings nur eine Achse des Joysticks. Dein VB-Programm kann ich nicht testen, weil ich keinen Joystick für den PC besitze ;)

Gruß

mic

Mc Delta
17.09.2010, 20:04
Hi,
@Martinius11,
Ich habe die Vorschläge umgesetzt und musste leider feststellen, dass es das Problem nicht gelöst hat.
@radbruch
Das Programm für das Poti kann ich vielleicht so umscreiben, dass ich statt der ADC-Werte die Werte empfange die über USART reinbekomme.

Mc Delta
17.09.2010, 21:29
Habt ihr vielleicht eine Idee wie man Zahlen, die über Uart (vom Terminal geschickt) ankommen mit dem RP6 in Integer-Werte umwandeln kann, sodass die dann zum Rechnen verwendet werdet können?

Martinius11
17.09.2010, 21:53
da wird dein Problem liegen den du wilst den Adc in char receiveBuffer[5];
zu schreiben. char ist aber ein 8-bit datentyp

radbruch
18.09.2010, 02:30
Hallo

Für die Übertragung der Daten kenne ich eigentlich nur zwei grundsätzliche Möglichkeiten: Direkte Übertragung von Bytewerten oder als Zeichenkette.

Bytewerte wäre für den RP6 wohl die einfachste Lösung. Das VB-Programm auf dem PC rechnet den Joystickwert in einen Wert von 0 bis 255 um und sendet das Ergebniss zum RP6. Bei zwei Joystickachsen errechnet der PC die Mischerwerte für jede Seite getrennt und sendet dann die Ergebnisse zum RP6. Dieser errechnet aus dem empfangenen Byte die Drehrichtung (< oder > als 127) und die Geschwindigkeit (0-210).

Mit einem kleinen Protokoll sollte das einfach umzusetzen sein. Entweder mit Start- und Ende-Kennung, z.B.:

print "*"; Wert_links; Wert_rechts; "#"

Wenn der RP6 im Eingang ein * erkennt, sind die nächsten beiden Bytes die gesuchten Werte. Das # zeigt das Ende der Übertagung und ist gleichzeitig ein Indiz für eine fehlerfreie Übertragung der zuvor gesendeten Bytes. Alternativ wäre auch ein solches Protokoll sinnvoll:

Print "L"; Wert_links
Print "R"; Wert_rechts

Der RP6 prüft dann immer auf L oder R und interpretiert den darauffolgenden Wert das Parameter. Das könnte man auch einfach mit dem RP6Loader testen. Dieser beherrscht beim Senden auch verschiedene Zahlenformate (mit .help im Terminalfenster zu erfahren), ein Test im Terminalfenster des RP6Loader könnte dann so aussehen:

L
.n100
R
.n100

Sendet ein 'L', ein Byte mit 100, ein 'R', ein Byte mit 100. Das angepasste Programm der Potiauswertung würde dann etwa so aussehen:


// Steuerung beider Antriebe 0-255 18.9.10 mic

// Einlesen über die serielle Schnittstelle fehlt noch!

// Die für jede Seite getrennt eingelesen Werte werden in Richtung
// und Geschwindigkeit umgerechnet.

#include "RP6RobotBaseLib.h"

#define joy_x_min 0
#define joy_x_max 255
#define joy_x_mid 127
#define joy_y_min 0
#define joy_y_max 255
#define joy_y_mid 127

uint8_t speed_l, speed_r;
uint8_t dir_l, dir_r;
uint8_t VB_wert_l, VB_wert_r; // empfangene Werte von 0-255
uint16_t joy_x, joy_y;

int main (void)
{
initRobotBase();
powerON();

while(1)
{
joy_x=VB_wert_l;
joy_y=VB_wert_r;

writeInteger(joy_x, 10);
writeChar(':');
writeInteger(joy_y, 10);
writeChar(' ');

if(joy_x > joy_x_mid)
{ // vorwärts
writeChar('F');
dir_l=FWD;
speed_l=(joy_x-joy_x_mid)*105/(joy_x_max-joy_x_mid);
}
else
{ // rückwärts
writeChar('B');
dir_l=BWD;
speed_l=(joy_x_mid-joy_x)*105/(joy_x_mid-joy_x_min);
}
writeInteger(speed_l*2, 10);
writeChar('\n');

if(joy_y > joy_y_mid)
{ // vorwärts
writeChar('F');
dir_r=FWD;
speed_r=(joy_y-joy_y_mid)*105/(joy_y_max-joy_y_mid);
}
else
{ // rückwärts
writeChar('B');
dir_r=BWD;
speed_r=(joy_y_mid-joy_y)*105/(joy_y_mid-joy_y_min);
}

writeInteger(speed_r*2, 10);
writeChar('\n');

setMotorDir(dir_l,dir_r);
moveAtSpeed(speed_l*2,speed_r*2);
task_motionControl();
mSleep(200);
}
return(0);
}

Bei der byteweisen Übertagung wird jeweils ein Wert in einem Byte übertragen. Als Alternative kann man auch den zu übertragenden Wert in eine Zeichenfolge (=String) umwandeln und dann die Zeichen der Zeichenkette übertragen. Zur Optimierung kann man bei diesem Verfahren noch jeweils zwei Zifffern in einem Byte übertragen (BCD) oder eine andere Zahlenbasis verwenden (Hex). Das wollte ich der Form halber erwähnen. Großer Vorteil ist dabei, dass man den Datenstrom quasi im Klartext mitlesen könnte, Nachteil der erhebliche Aufwand bei der Stringumwandlung und die größere zu übertragende Datenmenge.

Um sicherzustellen, dass der RP6 alles richtig verstanden hat, wäre es einfach, wenn der RP6 das empfangene Zeichen bzw. das Byte direkt zurückschickt. Der PC kann dann erkennen, ob das Byte richtig übertragen wurde (Echo). Schneller und etwas komplizierter und deshalb erst bei größeren Datenmengen sinnvoll wäre ein Blockcheck. Ein Block mehrerer Bytes wird übertragen und nach der Übertragung antwortet der RP6 mit einer Prüfsumme die er aus dem empfangenen Block errechnet hat. Der PC vergleicht diesen Wert dann mit dem von ihm selbst errechneten Wert (CRC)

Eine kleine Linkliste zum Thema Zahlendarstellungen hatte ich hier mal zusammengestellt:
https://www.roboternetz.de/phpBB2/zeigebeitrag.php?p=353264

Sorry, der Beitrag wurde irgendwie immer länger.

Gruß

mic

Mc Delta
18.09.2010, 12:36
Danke für diese umfangreiche Antwort.
Ich habe mir jetzt eine LIB gebastelt. Das funktioniert auch soweit. Ich verwende die Motorsteuerung der rblib.
Aber Ich habe noch ein Problem beim Empfangen von Bytes über UART.
Ich verwende diese Funktion:


uint8_t readByte(void)
{
while(!(UCSRA&(1<<RXC))); //Warten, bis ein Byte empfangen wurde!
return UDR; //Lies dieses Byte
}

Jetzt versuche ich die Funktion zu testen:


#include "lib.h"

int main(void)
{
char a;
delta_init();
usart_init();

while(1)
{
writeString("Empfangen:");
a = readByte();
writeChar(a);
}
}

Aber das funktioniert nicht so wie ich mir das vorstelle. Der RP6 sendet nur "Empfangen:" aber dann keinen Wert. Eigentlich sollte er wenn ich ihm über das Terminal ein Byte sende dieses wieder zurückgeben.
Hat jemand eine Idee wo der Fehler liegen könnte?

Mc Delta
18.09.2010, 17:47
Hat vielleicht schon jemand anderes eine Lib für den RP6 geschrieben, um ohne ISR über USART kommunizieren zu können?

Mc Delta
20.09.2010, 09:37
Habe das Problem selber entdeckt. Die Initialisierung war nicht ganz richtig.
Es muss UCSRB = (1 << RXEN) | (1 << TXEN);
heißen, weil ich ja keine Interrupts benutzen möchte.

Mc Delta
28.09.2010, 14:47
Nach ein bisschen Arbeit ist das Programm fertig und funktioniert.
Wer will kann das Programm testen. Die Links oben habe ich erneuert.
Hier nur noch mal die HEX-Datei, mit der der RP6 gefüttert wird:
Joystick.hex (http://mcdelta.bplaced.net/programme/Joystick.hex)
und die C-Datei, wenn sich jemand für den Code interessiert:
Joystick->C-Datei (http://mcdelta.bplaced.net/programme/Joystick1.txt)
Ich würde mich über Rückmeldungen freuen.

radbruch
28.09.2010, 14:59
Hallo

Sehr gut. Der Inhalt deiner Lib würde mich noch interessieren.

Warum programmierst du den RP6 nicht in Bascom? Sind Kommentare in deinen Programmen für dich kostenpflichtig?

Gruß

mic

Mc Delta
28.09.2010, 15:21
Die Lib ist nicht besonders groß, aber die UART und die Motorsteuerung sind drin. Hier die H-Datei

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>

#ifndef F_CPU
#define F_CPU 8000000
#endif

#define BAUD_LOW 38400 //Low speed - 38.4 kBaud
#define UBRR_BAUD_LOW ((F_CPU/(16*BAUD_LOW))-1)
#define FWD 0
#define BWD 1








void startADC(uint8_t channel);

void setMotorDir(uint8_t left_dir, uint8_t right_dir); //Richtungsangabe

void setMotorPWM(uint8_t power_left, uint8_t power_right); //Gibt an wie schnell der RP6 links und rechts fahren a´soll

void delay(uint8_t d); //Verzögerung

void delta_init(void); //Initialisierung der Hauptfunktionen

void usart_init(void); //Initialisierung der USART

uint8_t usart_byte_avail(void); //Prüft ob ein Byte im Puffer liegt

uint8_t readByte(void); //liest dieses Byte

uint8_t readInteger(void); //liest einen Integerwert

void writeChar(char ch); //liest ein Zeichen

void writeString(char *string); //schreibt eine Zeichenfolge

void writeInteger(int16_t number, uint8_t base); //schreibt einen Integerwert

void usart_test(void); //UART-Test: bestätigt jede Eingabe mit zurücksenden der Zeichen




Und noch die C-Datei:

#include <avr/io.h> // I/O Port definitions
#include <avr/interrupt.h> // Interrupt macros
#include <stdlib.h> // C standard functions (e.g. itoa...)
#ifndef F_CPU
#define F_CPU 8000000
#endif

#define BAUD_LOW 38400 //Low speed - 38.4 kBaud
#define UBRR_BAUD_LOW ((F_CPU/(16*BAUD_LOW))-1)
#define FWD 0
#define BWD 1


// Motor Power
void setMotorPWM(uint8_t power_left, uint8_t power_right)
{
if (power_left>210) power_left=210;
if (power_right>210) power_right=210;
OCR1BL = power_left;
OCR1AL = power_right;
if (power_left || power_right)
TCCR1A = (1 << WGM11) | (1 << COM1A1) | (1 << COM1B1);
else
TCCR1A = 0;
}
// Motor Richtung
void setMotorDir(uint8_t left_dir, uint8_t right_dir)
{
if (left_dir)
{ PORTC |= (1 << PINC2);}
else
{PORTC &= ~(1 << PINC2);}
if (right_dir)
{PORTC |= (1 << PINC3);}
else
{PORTC &= ~(1 << PINC3);}
}
void startADC(uint8_t channel)
{
ADMUX = (1<<REFS0) | (0<<REFS1) | (channel<<MUX0);
ADCSRA = (0<<ADIE) | (1<<ADSC) | (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADIF);
}
uint16_t readADC(uint8_t channel)
{
if((ADCSRA & (1<<ADSC))) return 0; // check if ADC is buisy...
ADMUX = (1<<REFS0) | (0<<REFS1) | (channel<<MUX0);
ADCSRA = (0<<ADIE) | (1<<ADSC) | (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADIF);
while ((ADCSRA & (1<<ADSC)));
ADCSRA |= (1<<ADIF);
return ADC;
}


void writeChar(char ch)
{
while (!(UCSRA & (1<<UDRE)));
UDR = (uint8_t)ch;
}
void writeString(char *string)
{
while(*string)
writeChar(*string++);
}
void writeInteger(int16_t number, uint8_t base)
{
char buffer[17];
itoa(number, &buffer[0], base);
writeString(&buffer[0]);
}

void delay(uint8_t d)
{
uint16_t d1, dummy;
for (d1=d*255; d1>0; d1--) dummy^=d1;
}
void delta_init(void)
{
cli();


// Initialize Timer1 - PWM:
// PWM, phase correct with ICR1 as top value.
TCCR1A = (0 << WGM10) | (1 << WGM11) | (1 << COM1A1) | (1 << COM1B1);
TCCR1B = (1 << WGM13) | (0 << WGM12) | (1 << CS10);
ICR1 = 210;
OCR1AL = 0;
OCR1BL = 0;

sei();
}
void usart_init(void){
// UART:

UBRRH = UBRR_BAUD_LOW >> 8; // Setup UART: Baudrate is Low Speed
UBRRL = (uint8_t) UBRR_BAUD_LOW;
UCSRA = 0x00;
UCSRC = UCSRC|=0x86;
UCSRB = (1 << TXEN) | (1 << RXEN);

}

uint8_t usart_byte_avail(void){
if(UCSRA&(1<<RXC))
return 1;
else return 0;
}

uint8_t readByte()
{
while(!(UCSRA&(1<<RXC)));

return UDR;
}

void usart_test(void)
{
char test;
while(1)
{
test = readByte();
writeString("Byte:");
writeChar(test);
writeString("\n");
delay(200);


}

}



uint8_t readInteger()
{
// Liefer das empfangene Zeichen, falls etwas empfangen wurde; -1 sonst
return (UCSRA & (1 << RXC)) ? (int) UDR : -1;
}

funkheld
28.09.2010, 23:25
Ich steuere ihn über Purebasic mit dem Joy, klappt wunderbar.

Gruss