PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Kommunikation über usb Schnittstelle mega2560 und linux



what?
20.01.2009, 14:01
Hallo,

wie stelle ich es an, dass der mega2560 und mein Laptop sich über USB Daten austauschen?

Auf dem mega2560 läuft ein Loop mit 1 Sekunde Pause.
Zu Testzwecken gibt dieser Loop nur einen Print #4 "test" Befehl aus.

Auf meinem Laptop (ubuntu Kernel 2.6.24-23-generic) versuche ich vergebens
die gesendeten bytes vom mega2560 zu bekommen.


Hier der entscheidende Codeauszug:



/* test */
usb_detach_kernel_driver_np(udev, 0);

if (usb_claim_interface(udev, 0))
{
fprintf(stderr,
"+++++\nusbdev_open(): %s\n",
usb_strerror());

}

int numBytes;
int reset;
char buffer[64];
if (usb_clear_halt(udev, dev->config[0].interface[0].altsetting[0].endpoint[0].bEndpointAddress)
)
{
fprintf(stderr,
"\nclear_halt(): %s\n",
usb_strerror());

}

if( usb_interrupt_read(udev, dev->config[0].interface[0].altsetting[0].endpoint[0].bEndpointAddress,
buffer, sizeof(buffer), 1000)
)
{
fprintf(stderr,
"\nusb_interrupt_read(): %s\n",
usb_strerror());

}


}


Wie sicher unschwer zu erkennen ist, nutze ich die libusb (Version 0.1.12).
Es scheitert momentan an usb_interrupt_read().
Dort bekomme ich den Fehler "-22" -> error submitting URB: invalid argument.

Da ich gerade erst seit ein paar Tagen damit experimentiere, meine Frage, ob der Weg überhaupt der richtige ist? Also, wie bringe ich am besten den mega2560 und meinen Rechner via usb zum Datenaustausch?

Danke für jeden Tip,
Edgar

just4fun
21.01.2009, 01:03
Hi Edgar,

warum sprichst du nicht unter Linux das vom Atmel erzeugte serial device an? z.B. /dev/ttyUSB0. Keine Extra lib erforderlich. Ich habe das bei mir genau so gemacht. Hier der Teil in meiner Doku:
http://www.direcs.de/doxygen/classDirecsSerial.html

Bei Fragen, frag! :-)

Gruß,

just4fun

what?
21.01.2009, 05:29
Hi just4fun,

und erstmal Danke, dass du so schnell reagiert hast.
Das ist ja ein sehr komplexes Projekt, dein Roboter. Hut ab!
Mein Wissen über Programmieren ist eher als 'laienhaft' einzustufen.
Und auch mit dem Atmel habe ich lange kämpfen müssen, bis ich so einigermassen verstanden habe, wie er funktioniert. Dazu musste ich auch noch mit Elektronik von NULL beginnen. Aber das macht ja Spass! Immerhin habe ich ein paar Sensoren zum laufen bekommen, kann einen Schrittmotor ansteuern und unnötiges Zeugs auf einem LCD ausgeben.
Ich möchte mit dem Atmel mein astronomisches Equipment ein wenig aufpeppen.
Dazu gehört auch das Messen und Speichern der Temperaturen über die ganze Nacht durch, in der ich draussen astronomische Objekte fotografiere. Das soll irgendwann einmal den Sinn ergeben, dass ich die lässtigen und zeitraubenden Dunkelbilder später (am Tag) machen kann. Ziel ist eine kleine Kühlbox, die durch ein Peltierelement gekühlt wird. Die Kamera kommt dann in die Kühlbox und die Aufnahmesequenzen der Nacht nocheinmal gemacht - unter gleichen Temperaturbedingungen. Somit erhalte ich dann hoffentlich meine so dringend benötigten Dunkelbilder.

Lange Rede - kurzer Sinn.
Ich habe mir deine direcsSerial.h mal angeschaut und stolpere schon beim ersten Versuch, sie zu nutzen. Zum Programmieren nutze ich Anjuta unter ubuntu. Geschrieben wird in C.
Hier mal die Fehlermeldungen:



/home/edgar/testordner/usb-atmel-test/src/./direcsSerial.h:42:20: Fehler: QtGlobal: No such file or directory
/home/edgar/testordner/usb-atmel-test/src/./direcsSerial.h:43:19: Fehler: QString: No such file or directory
/home/edgar/testordner/usb-atmel-test/src/./direcsSerial.h:44:18: Fehler: QDebug: No such file or directory
In file included from /home/edgar/testordner/usb-atmel-test/src/main.c:2:
/home/edgar/testordner/usb-atmel-test/src/./direcsSerial.h:46: Fehler: expected »=«, »,«, »;«, »asm« or »__attribute__« before »class«
make: *** [main.o] Fehler 1
Nicht erfolgreich fertiggestellt

Bin mir nicht ganz sicher, aber deine Klasse sieht mir eher nach c++ aus?!
Vielleicht kannst du mir da weiterhelfen. Ich war schon fast geneigt, mit meinem Projekt wieder auf Windows umzusteigen, muss aber nicht... :-b

just4fun
21.01.2009, 09:37
Danke. Ja, ich bastele ja auch schon 4 Jahre dran rum... :-/

Nur nicht aufgeben.... :-) Auch unter Linux läuft das ganz easy.
Die Fehlermeldungen liegen daran, dass ich das Qt Framework von Trolltech (http://www.qtsoftware.com/downloads/opensource) (jetzt Nokia) nutze. Vorteil: Einmal programmieren, läuft unter Linux, Windows, demnächst Nokia S60..... und privat ist das Open Source! Kann ich persönlich nur wärmstens empfehlen!

Und ja, es ist C++
Ich sehe gerade, das die Doku nicht auf dem letzen Stand ist! ARGHHH! Werde das heute abend fixen.

Sieh dir den richtigen Code bitte hier an:
http://github.com/markusk/direcs/tree/master

Oder genauer hier:
http://github.com/markusk/direcs/tree/d7d7460b47b0b97c94650d072f3691d2932d4486/mrs/trunk/src

Wenn du das im C einbinden willst, solltest du dir nur die Methoden

int openAtmelPort(char *dev_name);
int writeAtmelPort(unsigned char *c, int nChars);
int readAtmelPort(unsigned char *buf, int nChars);
int closeAtmelPort();
ansehen. Den Rest wirst du nicht benötigen. Ich nutze die Klasse, auch um einen Laserscanner anzusprechen, darum ist da noch so viel "drum herum".

Die include-Zeilen

#include <QtGlobal>
#include <QString>
#include <QDebug>
in der direcsSerial.h benötigst du nicht! Das ist sozusagen "Qt Code".
Dann wird er über das "qDebug" in der direcsSerial.cpp meckern. Kannst du durch "printf" ersetzen.

Wichtig:
Die Portsettings des seriellen Ports (9600,8,N,1) setze ich ebenfalls in der Methode "openAtmelPort". Nicht sehr schön, eine lange Geschichte... ;-)

In der interfaceAvr.cpp siehst du, wie ich die Methoden aufrufe bzw. nutze.

Ich hoffe, du steigst ein wenig durch...

what?
21.01.2009, 16:07
Hallo nochmal,

also, ich bin mir sowas von unsicher, ob ich den richtigen Weg gehe.
Ich habe mal versucht, aus deinen files codeschnipsel umzusetzen.



#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/time.h>

#include <linux/serial.h>

#define READ_TIMEOUT 250000 /* less than 1e6 */
#define _POSIX
#define USB_DEVICE "/dev/ttyUSB0"

int dev_fd;

/* openAtmel */
int openAtmelPort(char *dev_name)
{

if ((dev_fd = open(dev_name, O_RDWR | O_NOCTTY, 0)) < 0)
{
return (-1);
}

return (dev_fd);
}

/* closeAtmel */
int closeAtmelPort()
{
return close(dev_fd);
}

/* writeAtmel */
int writeAtmelPort(unsigned char *c, int nChars)
{

int n = write(dev_fd, c, 1);

if (n < 0)
{
// error
printf("write() of n bytes failed -> %d", n);
}
else
{
printf("%d byte(s) written.", n);
}

return n;
}

/* readAtmel */
int readAtmelPort(unsigned char *buf, int nChars)
{

int amountRead = 0, bytes_read = 0;
struct timeval t;
fd_set set;
int err;

while(nChars > 0)
{
t.tv_sec = 0;
t.tv_usec = READ_TIMEOUT;
FD_ZERO(&set);
FD_SET(dev_fd, &set);
err = select(dev_fd + 1, &set, NULL, NULL, &t);

if(err == 0)
return -2;

amountRead = read(dev_fd, buf, nChars);
if(amountRead < 0 && errno != EWOULDBLOCK)
return -1;
else if(amountRead > 0) {
bytes_read += amountRead;
nChars -= amountRead;
buf += amountRead;
}
}
return bytes_read;
}


int main()
{
unsigned char *buffer;
int nChars;
int result;

result = openAtmelPort(USB_DEVICE);
printf("openAtmelPort: %d\n", result);
result = readAtmelPort(buffer, 1);
printf("readAtmelPort: %d\n", result);
closeAtmelPort();
return (0);
}


bei openAtmelPort() wird '3' zurückgegeben.
Also gehe ich davon aus, dass zumindest das funktioniert.
Bei readAtmelPort() erhalte ich den Rückgabewert '-2' bzw. direkt mit write() '0' , was darauf schliessen lässt, dass ich Mist gebaut habe... 8-[

Nun frage ich mich, ob ich den Code auf dem Atmel auch richtig habe.
Dort lasse ich in einer Loop Schleife jede Sekunde "!" ausgeben.



Do
Print #4 , "!"
Wait 1
Loop

Hab den ganzen Tag gesucht, probiert und jetzt hänge ich durch.
Vielleicht fehlt ja auch der Linux Usb-Treiber für den Atmel?!
Leider gibt es da nur den für Kernel 2.4.x - ich habe aber ubuntu unter 2.6.x laufen.

just4fun
21.01.2009, 16:29
Bevor ich mir den Code heute Abend anschaue, existiert denn die Datei "/dev/ttyUSB0", sprich das Device? Unter Linux ist ja ein Gerät immer auch eine Datei.

Treiber sind für den Atmel direkt nicht erforderlich, da hier ein Standard-Seriell-USB-Chip zum Einsatz kommt. Dieser ist meines Wissens nach im 2.6.x-Kernel integriert. Nix extra Treiber! Ist ja nicht Windows. ;-)

Wenn das Device /dev/ttyUSBx nicht existiert, was steht im Log, wenn du den Atmel steckst? In der Konsole "dmesg" eingeben, die letzen Zeilen sind die spannenden...

Ach so:
Wie ist denn üebrhautp der Atmel an den PC angeschlossen? Ich war jetzt davon ausgegangen, dass es sich um ein komplettes Board wie das hier handelt: http://www.shop.robotikhardware.de/shop/catalog/product_info.php?cPath=64&products_id=162

Liege ich da überhaupt richtig???

what?
21.01.2009, 16:43
Danke erstmal für deine Mühe, mir zu helfen!



edgar@edgar-laptop:~$ ls /dev/ttyUSB0
/dev/ttyUSB0


und dmesg - die letzten Zeilen:


[31592.668977] usb 1-1: configuration #1 chosen from 1 choice
[31592.676923] cp2101 1-1:1.0: cp2101 converter detected
[13539.791394] usb 1-1: reset full speed USB device using uhci_hcd and address 13
[13539.937798] usb 1-1: cp2101 converter now attached to ttyUSB0
[31638.606873] usb 1-1: USB disconnect, address 13
[31638.607578] cp2101 ttyUSB0: cp2101 converter now disconnected from ttyUSB0
[31638.607617] cp2101 1-1:1.0: device disconnected
[32040.393906] usb 1-1: new full speed USB device using uhci_hcd and address 14
[32040.562229] usb 1-1: configuration #1 chosen from 1 choice
[32040.566442] cp2101 1-1:1.0: cp2101 converter detected
[13731.755064] usb 1-1: reset full speed USB device using uhci_hcd and address 14
[13731.905197] usb 1-1: cp2101 converter now attached to ttyUSB0
[32751.498512] usb 1-1: USB disconnect, address 14
[32751.499174] cp2101 ttyUSB0: cp2101 converter now disconnected from ttyUSB0
[32751.499214] cp2101 1-1:1.0: device disconnected
[32870.099653] usb 1-1: new full speed USB device using uhci_hcd and address 15
[32870.263964] usb 1-1: configuration #1 chosen from 1 choice
[32870.268057] cp2101 1-1:1.0: cp2101 converter detected
[14087.336667] usb 1-1: reset full speed USB device using uhci_hcd and address 15
[14087.483037] usb 1-1: cp2101 converter now attached to ttyUSB0
[14959.968773] usb 1-1: USB disconnect, address 15
[14959.969236] cp2101 ttyUSB0: cp2101 converter now disconnected from ttyUSB0
[14959.969255] cp2101 1-1:1.0: device disconnected
[36085.264878] usb 1-1: new full speed USB device using uhci_hcd and address 16
[36085.429203] usb 1-1: configuration #1 chosen from 1 choice
[36085.433409] cp2101 1-1:1.0: cp2101 converter detected
[15465.286839] usb 1-1: reset full speed USB device using uhci_hcd and address 16
[36086.026459] usb 1-1: cp2101 converter now attached to ttyUSB0


hm....
new full speed USB device using uhci_hcd and address 16
Immer eine neue Adresse? Oder was hat das zu bedeuten?

**edit
Ja, genau um das Atmel board geht es... mega2560

just4fun
21.01.2009, 22:39
Danke erstmal für deine Mühe, mir zu helfen!
Gerne. Ich bin froh, dass ich auch immer mal Hilfe hatte und insbesondere hier im Forum viel Hilfe und Tipps erhalten und gefunden habe.

Auf deinem System sieht ja schonmal alles ganz gut aus. Beobachten solltest du ggf. die vielen "reset full speed device" Meldungen. Die sollten - denke ich - nicht ständig auftreten; es sein denn du hast hier zum Testen das USB-Kabel öfter gezogen und wieder gesteckt. Zum Testen.

Die aktuelle Doku ist auch wieder online. Wie gewohnt auf meiner Homepage (siehe Signatur).

Deine verwendeten Code-Schnipsel sehen auf den ersten Blick schon gut aus. ABER: Du hast in der openAtmelPort einen elementaren Codeteil weggelassen!


int openAtmelPort(char *dev_name)
{
// This method is only used for the atmel serial port!
// *Not* for the laser scanners!
if ((dev_fd = open(dev_name, O_RDWR | O_NOCTTY, 0)) < 0)
{
return (-1);
}



//============ DIESE ZEILEN HIER SETZEN DIE PORT SETTINGS ================================
struct termios ctio;

tcgetattr(dev_fd, &ctio); /* save current port settings */
ctio.c_iflag = IXON | IGNPAR; // TODO: IXON oder IXOFF ?!??
ctio.c_oflag = 0;
// | SW flow control | Parity 0 | 8 Databits | 1 StopBit
ctio.c_cflag = CREAD | CLOCAL | 0 | CS8 | 1;
ctio.c_lflag = 0;
ctio.c_cc[VTIME] = 0; /* inter-character timer unused */
ctio.c_cc[VMIN] = 0; /* blocking read until 0 chars received */
cfsetispeed(&ctio, (speed_t) B9600);
cfsetospeed(&ctio, (speed_t) B9600);
tcflush(dev_fd, TCIFLUSH);
tcsetattr(dev_fd, TCSANOW, &ctio);
//================================================== ========================


return (dev_fd);
}

Dieser Teil stellt den USBPort auf 9600,8,N,1 ein. Er stellt FlowControl außerdem auf "Software". Ohne dass dürfte es nie klappen!

readAtmelPort liefert im Erfolgsfall immer einen Wert > 0; nämlich die Anzahl der gelesenen Bytes.

Zum Testen, ob dein Code auf dem Atmel läuft, könntest du minicom auf der Konsole (Shell) nutzen. Hier unbedingt den richtigen Anschluss wählen, die Portsettings setzen und Hardflowcontrol ABSCHALTEN! Ggf. als "minicom -s" und ggf. als root starten, falls es ein Rechteproblem sein sollte.

what?
22.01.2009, 02:46
So.....minicom zeigt das vom Atmel gesendete Zeichen. Das ist ja schonmal vielversprechend!
readAtmel() leider immer noch 0.
Habe den fehlenden Code natürlich eingefügt...aber leider immer noch nix.

***edit
hab das TIMEOUT mal erhöht und um eine Null erweitert und siehe da...es kommt was zurück vom Port.
Jetzt aber erstmal ins Bett...

***edit
konnte es dann doch nicht erwarten \:D/

Danke dir für deine Hilfe!!!
Jetzt kann ich alles über USB steuern, Daten empfangen und das dicke Kabel für LPT1 kann weg.

Hier der funktionierende Code


#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/time.h>

#include <linux/serial.h>

#define READ_TIMEOUT 100000
#define _POSIX
#define USB_DEVICE "/dev/ttyUSB0"

int dev_fd = 0;

/* openAtmel */
int openAtmelPort(char *dev_name)
{

if ((dev_fd = open(dev_name, O_RDWR | O_NOCTTY, 0)) < 0)
{
return (-1);
}

//============ DIESE ZEILEN HIER SETZEN DIE PORT SETTINGS ========================
struct termios ctio;

tcgetattr(dev_fd, &ctio); /* save current port settings */
ctio.c_iflag = IXON | IGNPAR; // TODO: IXON oder IXOFF ?!?? < < < <
ctio.c_oflag = 0;
// | SW flow control | Parity 0 | 8 Databits | 1 StopBit
ctio.c_cflag = CREAD | CLOCAL | 0 | CS8 | 1;
ctio.c_lflag = 0;
ctio.c_cc[VTIME] = 0; /* inter-character timer unused */
ctio.c_cc[VMIN] = 0; /* blocking read until 0 chars received */
cfsetispeed(&ctio, (speed_t) B9600);
cfsetospeed(&ctio, (speed_t) B9600);
tcflush(dev_fd, TCIFLUSH);
tcsetattr(dev_fd, TCSANOW, &ctio);

return (dev_fd);
//================================================== ========================
return (dev_fd);
}

/* closeAtmel */
int closeAtmelPort()
{
return close(dev_fd);
}

/* writeAtmel */
int writeAtmelPort(char *c)
{

int n = write(dev_fd, c, 1);

if (n < 0)
{
// error
printf("write() of n bytes failed -> %d", n);
}

return n;
}

/* readAtmel */
int readAtmelPort(unsigned char *buf, int nChars)
{

int amountRead = 0, bytes_read = 0;
struct timeval t;
fd_set set;
int err;

while(nChars > 0)
{
t.tv_sec = 0;
t.tv_usec = READ_TIMEOUT;
FD_ZERO(&set);
FD_SET(dev_fd, &set);
err = select(dev_fd + 1, &set, NULL, NULL, &t);

if(err == 0)
return 0;

amountRead = read(dev_fd, buf, nChars);
if(amountRead < 0 && errno != EWOULDBLOCK)
return -1;
else if(amountRead > 0) {
bytes_read += amountRead;
nChars -= amountRead;
buf += amountRead;
}
}
return bytes_read;
}


int main()
{
unsigned char *buffer;
int result;
char *myChar[1];
myChar[0]="w";


result = openAtmelPort(USB_DEVICE);
printf("openAtmelPort: %d\n", result);
result = readAtmelPort(buffer, 2);
printf("readAtmelPort: %d\n", result);
printf("in: %d\n", buffer[0]);
printf("out: %s\n", myChar[0]);
result = writeAtmelPort(myChar[0]);
printf("writeAtmelPort: %d\n", result);
closeAtmelPort();
return (0);
}


Das TIMEOUT konnte ich wieder runtersetzen, da ich beim Atmel den Loop hab schneller laufen lassen.

just4fun
22.01.2009, 09:40
02:46 Uhr. Respekt! ;-)

Prima, freue mich, dass ich helfen konnte. Habe selber sehr sehr viel Zeit damit verbracht, bis es bei mir wie gewünscht lief.

***edit
Das mit dem Timout läst sich vielleicht besser regeln, wenn du mit der Methode "numChars" vor dem readAtmelPort prüftst, ob überhaupt Zeichen zum Lesen anliegen! Ich wollte das auch immer noch mal machen, bei mir liefs aber auch so. Steht noch als TODO bei mir im Code. :-)



long numChars(int dev_fd)
{
long available = 0;

if(ioctl(dev_fd, FIONREAD, &available) == 0)
return available;
else
return -1;
}