Habe einen uC programmiert, über USB VCOM Messwerte als Text rauszuschreiben. Unter Windows funktioniert es d.h. COM Port erscheint und Text kann ausgelesen werden. Unter Linux erscheint auch der COM Port, nachdem USB eingesteckt wurde: $sudo stty -F /dev/ttyACM0 stty: /dev/ttyACM0: No such file or directory $sudo stty -F /dev/ttyACM0 speed 9600 baud; line = 0; -brkint -imaxbel Aber dann kommt hier kein Text: $sudo tail -f /dev/ttyACM0 Baudrate ist natürlich default = 9600
tail ist evtl. nicht unbedingt die beste Lösung, da das Zeilen puffert. Versuch mal nur "cat /dev/ttyACM0".
Der uC ist zwar kein Arduino - aber unter Windows funktioniert es - natürlich - auch mit dem Arduino IDE Serial Monitor. Daher habe ich jetzt mal testweise unter Linux auch die Arduino IDE installiert. Und wie beschrieben: $ls -l /dev/ttyACM* crw-rw---- 1 root dialout 166, 0 Jan 1 22:03 /dev/ttyACM0 sudo usermod -a -G dialout xxxxxx Das funktioniert nicht - warum auch immer. Damit funktioniert es: sudo arduino Somit sollte jetzt auch das funktionieren - aber auch nach Beenden ist der COM Port noch blockiert: $sudo tail -f /dev/ttyACM0 tail: cannot open '/dev/ttyACM0' for reading: Permission denied tail: no files remaining
Georg A. schrieb: > Versuch mal nur "cat /dev/ttyACM0" Da tut sich was, es werden einige Zeichen gelesen, danach nichts mehr. Lothar schrieb: > sudo usermod -a -G dialout xxxxxx Das wurde jetzt durch einen Neustart wirksam.
Lothar schrieb: > Georg A. schrieb: >> Versuch mal nur "cat /dev/ttyACM0" > > Da tut sich was, es werden einige Zeichen gelesen, danach nichts mehr. > > Lothar schrieb: >> sudo usermod -a -G dialout xxxxxx > > Das wurde jetzt durch einen Neustart wirksam. logout u. login oder (exec) newgrp dialout man newgrp The newgrp command is used to change the current group ID during a login session.
Die Lage ist jetzt so, dass es mit dem Arduino IDE Serial Monitor funktioniert. Im Terminal funktioniert es einige Zeit, dann nicht mehr. Im Arduino Forum steht, man muss dann den Treiber resetten. Unter Windows reicht aus- und einstecken. Unter Linux nicht. Habe schon einige Methoden zum Treiber resetten versucht, bisher ging nichts. Nur nach Neustart geht es wieder.
Lothar schrieb: > Im Arduino Forum steht, man muss dann den Treiber resetten. Unter Jaja, Arduino. Experten@work ;) > Windows reicht aus- und einstecken. Unter Linux nicht. Habe schon einige > Methoden zum Treiber resetten versucht, bisher ging nichts. Nur nach > Neustart geht es wieder. Ich hab schon viel und lang mit USB/RS232 verschiedenster (auch zweifelhafter) Herkunft gewurschtelt, aber sowas eigentlich noch nie gehabt. So gut wie immer ist bei den simplen Treibern eigentlich das Device selbst schuld. Ist das nach dem Ausstecken 100% stromlos? Evtl. sollte man noch das Handshake mit stty -crtscts abschalten. Achja, das Linux-TTY-System hat im Gegensatz zu Windows ein paar Besonderheiten: - Man kann dasselbe Device einfach so mehrfach öffnen. Damit kann man zB. Lesen und Schreiben in getrennte Prozesse auslagern, tw. sehr praktisch. Zwei++mal lesen geht aber auch, aber wer dann die Daten bekommt, ist undefiniert. Schau mal die lsof-Ausgaben an, ob da noch/schon jemand an ttyACM0 hängt. - Wenn Ausgaben nicht rausgehen (zB. wg. Handshake) kann ein nächstes open hängen. Das Device muss man dann nonblocking öffnen, sonst wirds nix mehr. Schau mal mit "strace cat /dev/ttyACM0" nach, wie weit du eigentlich kommst. - Wenn ein Device rausgezogen wird, während der Filedeskriptor dazu noch offen ist, führt das Einstecken zu einem neuen Devicenamen (zB. ttyACM1). Schau in "dmesg" nach, ob das Anstecken wirklich den Devicenamen erzeugt hat, den du vermutest.
Schau eventuell auch noch mit lsof nach, ob nicht noch was anderes darauf zugreift (z.B. modemmanager, gpsd, getty.service, etc.)
Georg A. schrieb: > Ich hab schon viel und lang mit USB/RS232 verschiedenster (auch > zweifelhafter) Herkunft gewurschtelt, aber sowas eigentlich noch nie > gehabt. So gut wie immer ist bei den simplen Treibern eigentlich das > Device selbst schuld Das Device ist hier nicht schuld. Es ist ein LPC845 der über UART rausschreibt. Schuld ist - falls überhaupt - der vom Board-Hersteller als USB-UART Bridge programmierte LPC11U35: https://mcuoneclipse.com/2019/02/02/tutorial-transforming-the-nxp-lpc845-brk-into-a-cmsis-dap-debug-probe/ Der Hersteller wird mir bestimmt sagen, es funktioniert doch unter Windows, und ich soll Linux halt lassen. Aber ich muss es nun mal unter Linux testen. Übrigens, auf stackoverflow gibt es haufenweise Beiträge, dass USB-CDC was unter Windows problemlos läuft, unter Linux instabil ist. Es wird dann ernsthaft zur Ethernet-UART Bridge geraten. Bei Arduino ist die Lage ähnlich. Habe inzwischen ein MEGA getestet. Hier ist auch als USB-UART Bridge ein ATMEGA16U2 drauf. Den Sourcecode gibt es: https://github.com/arduino/ArduinoCore-avr/tree/master/firmwares/atmegaxxu2/arduino-usbserial Dennoch konnte das Problem wohl bisher nicht zu 100% gelöst werden.
Also ich verwende täglich auf der Arbeit Usb-Uart Adapter unter Linux. Sowohl auf großen X86 Maschinen, als auch auf Raspis. Das geht problemloser als auf Windows. Von daher kann ich die Probleme überhaupt nicht nachvollziehen. Ich habe dann die Devices /dev/ttyS0, /dev/ttyS1 .. je nachdem wie viele ich reinstecke. Komisch, dass du andere Devices bekommst? Eine prima GUI-Anzeige ist das Putty Programm. Geht sowohl auf X86 als auch auf Arm, und es ist einfach über apt-get installierbar. Damit arbeite ich seit Jahren.
PittyJ schrieb: > Ich habe dann die Devices /dev/ttyS0, /dev/ttyS1 Das ist aber eher untypisch, bei USB-Adaptern heissen die meistens /dev/ttyUSB0 ... oder /dev/ttyACM0 ...
Habe es jetzt in C programmiert und da funktioniert es halbwegs: USB = open("/dev/ttyACM0", O_RDWR | O_NOCTTY); fcntl(USB, F_SETFL, O_NONBLOCK); Somit kann ich mich nun mit dem nächsten Problem beschäftigen: Unter Windows kann ich in C einen seriellen Interrupt Handler definieren, der aufgerufen wird, wenn ein Zeichen angekommen ist. Gibt es sowas unter Linux auch? Bisher bin ich am Pollen: num = read(USB, &ch, 1); if (num == 0) break;
Pollen, entweder über ein blocking read(), besser mit poll() oder select() ist eigentlich die übliche Vorgehensweise. Damit verschwendet man auch keine CPU-Zeit. Vielleicht könnte man ein Signal definieren, aber die Interaktion von signal handlers und dem restlichen Programm ist eher schlecht definiert und wird noch schlimmer, wenn man Multithreading benutzt. Signale sollte man besser meiden.
Lothar schrieb: > fcntl(USB, F_SETFL, O_NONBLOCK); > Gibt es sowas unter Linux auch? Bisher bin ich am Pollen: > > num = read(USB, &ch, 1); > if (num == 0) break; Naja, wenn du nicht gerade wie oben auf Nonblocking umschaltest, dann kehrt read() erst zurück, wenn was angekommen ist. Aber ja, du kannst auch ein Signal generieren lassen, was so eine Art Userspace-Interrupt ist. Aktivieren mit
1 | fcntl (USB, F_SETFL, O_ASYNC); |
Und dann kannst du mit signal() oder sigaction() einen Handler für das Signal SIGIO registrieren, der bei ankommenden Daten aufgerufen wird. Aber in der Regel braucht man sowas nicht (zumindest hab ich es noch nie gebraucht).
:
Bearbeitet durch User
🐧 DPA 🐧 schrieb: > Schau eventuell auch noch mit lsof nach, ob nicht noch was anderes > darauf zugreift (z.B. modemmanager, gpsd, getty.service, etc.) Unter ubuntu greift der Modemmanager nach dem Einstecken auf ttyACMx devices zu. das macht er nicht bei ttyUSBx devices. Das kann man aber verhindern durch eine rules Datei im Verzeichnis /etc/udev/rules.d. Ich habe mal ein Beispiel angehängt (ist für stm32 usb stack) Du müsstest nur die Zahlen ändern (vendor id und devid) Die bekommt man lsusb heraus. für das zweite Gerät im Beispielfile liefert lsusb bei mir Bus 001 Device 044: ID 0483:5740 STMicroelectronics STM32F407 Die Rechte werden nach dem Einstecken des devices auf die Gruppe user gesetzt. d.h. Du kannst direkt aus dem userland darauf zugreifen. Das andere Verfahren mit add to dialout braucht man dann gar nicht mehr. Der Eintrag ENV{ID_MM_DEVICE_IGNORE}="1" bewirkt, dass der Modemmanager nach dem Einstöpseln nicht mehr darauf zugreift. funktioniert bei mir bei ubuntu16.04 und 18.04. Andere habe ich nicht getested. Martin
S. R. schrieb: > wird noch schlimmer, wenn man Multithreading benutzt Also unter Windows mit .NET funktioniert das ohne Probleme: Private Sub Serial_DataReceived(ByVal sender As System.Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles Serial.DataReceived Me.BeginInvoke(Sub() Serial_handler()) End Sub Private Sub Serial_handler() If Serial_open = True Dim size As Integer = Serial.BytesToRead Dim buffer As Byte() = New Byte(size - 1) {} Serial.Read(buffer, 0, size)
Lothar schrieb: >> wird noch schlimmer, wenn man Multithreading benutzt > Also unter Windows mit .NET funktioniert das ohne Probleme: Das sind ja auch keine Signal-Handler nach POSIX... witzich, wa?
S. R. schrieb: > Das sind ja auch keine Signal-Handler nach POSIX Ja - deswegen muss ich jetzt auch noch das hier machen. Wenn der uC mal genau während read() keinen Strom mehr hat: https://stackoverflow.com/questions/10522277/how-can-i-implement-timeout-for-read-when-reading-from-a-serial-port-c-c Das tcflush(USB, TCIFLUSH) macht auch manchmal merkwürdige Sachen - habe noch nicht den vollen Durchblick ...
Scheint zu funktionieren: struct pollfd pollevents; pollevents.fd = fd; pollevents.events = POLLIN; if ((poll(&pollevents, 1, 500)) == 0) break; else num = read(fd, &ch, 1);
tt2t schrieb: > PittyJ schrieb: >> Ich habe dann die Devices /dev/ttyS0, /dev/ttyS1 > > Das ist aber eher untypisch, bei USB-Adaptern heissen die meistens > /dev/ttyUSB0 ... oder /dev/ttyACM0 ... Stimmt ist /dev/ttyUSB0. Der arbeitet aber problemlos.
PittyJ schrieb: > /dev/ttyUSB0 ... arbeitet aber problemlos Das ist wohl nicht so überraschend: /dev/ttyUSB0 = USB-UART Konverter /dev/ttyACM0 = USB-CDC Programm
Lothar schrieb: >> Das sind ja auch keine Signal-Handler nach POSIX > > Ja - deswegen muss ich jetzt auch noch das hier machen. > Wenn der uC mal genau während read() keinen Strom mehr hat: Du scheinst asynchrone Signalhandler nach POSIX mit einem Event-Dispatcher-System zu verwechseln. Deswegen laufen die Antworten auf Stack Overflow auch alle auf "nimm poll() oder select()" hinaus, denn damit baut man sich sein Event-System erst auf. Dass die VB-Runtime in .net das für dich schon eingebaut hat, hat ja damit nichts zu tun. > Das tcflush(USB, TCIFLUSH) macht auch manchmal merkwürdige > Sachen - habe noch nicht den vollen Durchblick ... Zugegeben, diese APIs haben tatsächlich was von schwarzer Magie bei den alten Römern. Ist aber den Geräten geschuldet, die bei der Einführung der APIs vorhanden waren. Windows hat da den Vorteil der spätereren Geburt. Es hilft auch nicht besonders, dass die API an manchen Stellen wesentlich flexibler ist als die meisten modernen Geräte oder deren Treiber.
pthread ist auch noch notwendig - da IO asynchron sein muss. Und schon gibt es das nächste Problem: delay() nutzt clock() Und somit hat das was der synchrone Thread grade macht Auswirkung auf den asynchronen IO thread. Das ist zwar eigentlich egal, denn der synchrone Thread übernimmt ja die Daten nur zu seinen Timings, aber irgendwie stört es mich :-) Kann ein Thread seine "eigene" clock() bekommen?
Lothar schrieb: > pthread ist auch noch notwendig - da IO asynchron sein muss. Warum? > Und schon gibt es das nächste Problem: > > delay() nutzt clock() Was für ein delay(), und wozu brauchst du das? Wieso nutzt es clock(), wenn das nicht geeignet ist?
Lothar schrieb: > pthread ist auch noch notwendig - da IO asynchron sein muss. Nein. Wie kommst du darauf? Lothar schrieb: > Und somit hat das was der synchrone Thread grade macht > Auswirkung auf den asynchronen IO thread. Was bitte ist ein "synchroner Thread" und was ist ein "asynchroner Thread"? Lothar schrieb: > delay() nutzt clock() Meine libc kennt kein delay(), dafür ist deren clock() die CPU-Zeit und nicht die reale Zeit. Du machst irgendwas sehr seltsames mit ungeeigneten Werkzeugen.
S. R. schrieb: > Was bitte ist ein "synchroner Thread" ... "asynchroner Thread"? Stimmt - hatte ich nicht direkt erwähnt - die IO findet in einer GUI statt. Der Thread vom main() ist der synchrone Thread in dem die GUI Callbacks laufen. Das Problem ist nun, wenn der Nutzer z.B. ein Menü mit der Maus festhält, wird der synchrone Thread blocking, und IO da drin bleibt stehen. Deswegen wird, wenn der Nutzer im GUI auf Connect klickt, jetzt ein neuer asynchroner Thread gestartet, in dem dann die IO läuft, und von der GUI über globale Variablen abgeholt werden kann. Somit keine Mutex erforderlich: int main(void) { ... while (RUNNING) { // event loop RUNNING = Fl::check(); } } static void Connect_CB(Fl_Widget *, void *data) { ... pthread_t thread; pthread_create(&thread, NULL, VCOM_thread, NULL); VCOM_running = true; } Wie schon geschrieben, in Windows ist das bei .NET alles schon "eingebaut"
Lothar schrieb: > Wie schon geschrieben, in Windows ist das bei .NET alles schon > "eingebaut" Solche Funktionalität ist in den größeren GUI-Toolkits normalerweise aber auch eingebaut. Aber wenn du dein Problem gelöst hast, dann ist ja alles gut. :-)
S. R. schrieb: > Aber wenn du dein Problem gelöst hast, dann ist ja alles gut. :-) Ja sieht jetzt stabil aus - delay() Problem auch gelöst - mal abwarten :-) static void delay(double val, bool blocking) { // time-slice 10 msec for (int cnt=0; cnt<val*100; cnt++) { usleep(10000); // delay() called from synchronous thread ? if (! blocking) if (! (RUNNING = Fl::check())) return; } }
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.