Hallo Community! Ich hab seit längerem eine Hardware mit einem kleinen STM32F042 zum Datenerfassen via USB (VCP) laufen. Über den USB werden kleine Datenpakete (Messwerte, wenige Bytes) etwa im Sekundentakt übertragen. Zu Konfigurationszwecken gibt es auch ein paar Parameter die über Terminal eingestellt werden können. Ist aber alles nicht Zeitkritisch. Hab mit eurer (vor allem mit W.S. und Stefan Frings Hilfe, deren Code ich auch verwende (VIELEN DANK !!!!!!)) den USB hinbekommen. Beitrag "STM32 USB init problem mit Kompaktcode von W.S." Jetzt hab ich wieder ein Problem, bei dem ich nicht weiterkomme: Ich hab mehrere von diesen USB-Datenerfassungsmodulen gebaut, und hab schon mehrmals beobachtet, dass manche Exemplare komisches Verhalten zeigen, wenn man vom PC Befehle zum STM schickt. Aktuell hab ich ein Exemplar, das reagiert einfach garnicht auf Befehle. Reproduzierbar auf verschiedenen PCs, Win7, Win10, Linux. Die Daten vom STM kommen im Sekundentakt zum PC wie sie sollen (ist default). Hab mit wireshark die Pakete von einem 'guten' (Bild links) und dem 'schlechten' (Bild rechts) Modul aufgenommen. Ich sende vom Host aus jeweils den Befehl 'stop<CRLF>', beim 'schlechten' Modul wird offenbar das Paket nochmals gesendet aber nicht mehr bestätigt. Die Datenpakete kommen munter weiter, während beim 'guten' das Senden gestoppt wird. An was könnte das liegen? Viele Grüße, Alex
Ich hab mir die Pakete (und den Unterschied) mal genauer angesehen. In dem ACK-Paket sind bei dem nicht funktionierenden STM zwei Bytes verändert, die beim anderen gleich bleiben:
1 | Der 'schlechte' : |
2 | 0000 1b 00 a0 a9 44 98 0f de ff ff 00 00 00 00 09 00 ....D........... |
3 | 0010 00 01 00 05 00 02 03 06 00 00 00 73 74 6f 70 0d ...........stop. |
4 | 0020 0a . |
5 | Antwort: VV VV |
6 | 0000 1b 00 a0 a9 44 98 0f de ff ff 04 00 00 c0 09 00 ....D........... |
7 | 0010 01 01 00 05 00 02 03 00 00 00 00 ........... |
8 | |
9 | Der 'gute' : |
10 | 0000 1b 00 60 07 77 95 0f de ff ff 00 00 00 00 09 00 ..`.w........... |
11 | 0010 00 01 00 06 00 02 03 06 00 00 00 73 74 6f 70 0d ...........stop. |
12 | 0020 0a . |
13 | Antwort |
14 | 0000 1b 00 60 07 77 95 0f de ff ff 00 00 00 00 09 00 ..`.w........... |
15 | 0010 01 01 00 06 00 02 03 00 00 00 00 ........... |
Leider bin ich (mangels C-Erfahrung) offenbar nicht in der Lage, in der usb.c von S.F. den Codeteil zu finden, der dieses ACK erzeugt. Hat schon mal jemand so ein Problem gehabt?
Was sind das für Daten in dem Paket? Am Anfang des Pakets sollte ja das PID-Byte gesendet werden, bei dem die 4 LSBs invers zu den 4 MSBs sind. Das ist aber bei 0x1b garnicht der Fall. Auch ist am Ende kein CRC bzw. EOF angehängt. Damit zeigt Wireshark offenbar die reinen nettoDaten. Aber warum ist das dann in den AXK-Paketen auch drinnen? Die sollten ja keine Daten beinhalten. Sämtliche erklärungen in der USB-Literatur die ich bisher durchgekaut habe, beschäftigens sich nur mit dem Paketaufbau rund um die Daten. Das passt für mich hinten und vorne nicht zusammen...
Im Februar/März 2020 wurde hier ein Problem im Puffer diskutiert. Beitrag "USB CDC von Stefan Frings und WS" Ich hatte mich mangels technischem Verständnis aus der Diskussion weitgehend heraus gehalten, aber am Ende den Lösungsvorschlag in meinen Code übernommen. Allerdings hatte ich seit dem keinen konkrete Anwendungsfall dafür, er ist also nur sehr oberflächlich getestet. Die Version auf meiner Homepage ist vom 12.03.2020, danach habe ich nichts mehr verändert. http://stefanfrings.de/stm32/stm32f1.html#usb
:
Bearbeitet durch User
Hallo Stefan, Den thread hab ich schon gefunden, allerdings nicht im Detail durchgearbeitet. Ich hab aber im Zuge der Fehlersuche das Projekt auf deine neue Version umgestellt. Das hat aber am Verhalten nichts verändert. Ich würde daher sagen, dass es unwahrscheinlich ist, dass das ein Bug in deiner neuen Version ist. Danke und LG, Alex
Alex schrieb: > Ich würde daher sagen, dass es unwahrscheinlich ist, dass das ein Bug in > deiner neuen Version ist. Es könnte allerdings dennoch ein alter Bug in dem Code sein. Oder ein Seiteneffekt von einem Fehler der ganz woanders versteckt ist. Wenn dir da jemand helfen kann, dass wohl am ehesten Niklas G. Allerdings hat der wenig Freude am Code von W.S. Vielleicht ist es besser, die Cube HAL zu benutzen oder das Ganze auf dem Beispiel von Niklas neu auf zu bauen. Der hilft dir dann bestimmt gerne weiter. https://www.mikrocontroller.net/articles/USB-Tutorial_mit_STM32
:
Bearbeitet durch User
Eine Erklärung warum das Packet im Fehlerfall doppelt kommt wäre zum Beispiel ein falsch gesetztes ToggleBit beim Transfer. Das sollte dann aber immer auftreten und nicht nur bei einzelnen Devices. Beschreibe doch Mal was du auf rs232 Ebene verschickst, bzw wie die Messpackete aussehen.
Die Pakete sind extrem simpel Daten zum Host: 'Sensor1: 24.32°C<RCLF>' So ein Datum kommt etwa alle Sekunden. Daten vom Host: 'stop<CRLF>' stoppt die Messungen 'start<RCLF>' startet sie wieder Es gibt noch einige Zusatzbefehle zu Konfiguration usw., aber das spielt hier eigentlich keine Rolle. Entscheiden ist, das auf ein 'stop<CRLF>' offenbar eine fehlerhafte Antwort folgt, und das Paket dann nochmal gesendet wird. Das wird dann aber vom STM ignoriert. Wenn ich nicht aus dem Terminalprogramm (HTerm) sende, sondern z.B. aus Matlab (Messdatenauswertung), dann erkennt das Programm am PC das das Senden nicht möglich war. Der PCseitige Treiber kriegt das also mit. Alex
Ich verwende den STM32CubeMX HAL USB Code. Sehr einfach zum einbinden und funktioniert in meinem Fall ohne Probleme.
Am Anfang hatte ich auch die HAL CDC in Verwendung, allerdings braucht die leider sehr viel Speicher, weshalb ich auf die schlankere Implementierung von W.S./S.F. gewechselt habe.
Alex schrieb: > Entscheiden ist, das auf ein 'stop' offenbar eine fehlerhafte Antwort > folgt, und das Paket dann nochmal gesendet wird. > Das wird dann aber vom STM ignoriert. nun tatsächlich ist es so, dass der Out Ep zb auf NAK stehen könnte dann würde der Host das einfach etwas später noch mal probieren. Dieser 2te Transfer dürfte dann mit STALL beantwortet werden. Begründung: Ein dauerhaftes NAK würde in einem Timeout enden, d.h. du würdest viel mehr Outs sehen. Probier mal aus was danach passiert. Sende einfach deinen stop nochmal. Falls der EP auf STALL steht dürfte nichts mehr ankommen. Sind GetFeature/SetFeature und GetStatus für deine EPs korrekt implementiert?
Taktprobleme? USB soll ja zimlich zeitkritisch sein. Wie erzeugen deine STM32F042 den internen USB-Takt?
Hallo Thomas Z. Gute Idee, hatte ich noch nicht näher untersucht. Wenn ich den Befehl wiederhole, wird er tatsächlich nicht mehr geschickt. Wenn ich dann den VCP schließe kommt ein Bulk-Out Paket von EP2 (#2194), von dem ich vermute, dass es zu dem Paket #1909 gehört. Ist das plausibel? Wenn ich den VCP erneut öffne, und den Befehl erneut schicke, wird er nur 1mal gesendet, beim Wiederholen nicht mehr. Beim Schließen kommt wieder ein Paket:
1 | + VPC öffnen |
2 | + Befehl senden |
3 | # time src dst len info |
4 | 1907 43.156065 host 1.9.2 33 URB_BULK out 'befehl' |
5 | 1908 43.156174 1.9.2 host 27 URB_BULK out |
6 | 1909 43.156207 host 1.9.2 33 URB_BULK out 'befehl' |
7 | |
8 | + Befehl nochmal senden |
9 | <kein traffic> |
10 | + VCP schließen |
11 | # time src dst len info |
12 | 2194 59.853448 1.9.2 host 27 URB_BULK out |
13 | |
14 | + VCP öffnen |
15 | + Befehl senden |
16 | # time src dst len info |
17 | 2341 79.436340 host 1.9.2 33 URB_BULK out 'befehl' |
18 | |
19 | + Befehl nochmal senden |
20 | <kein traffic> |
21 | |
22 | + VCP schließen |
23 | # time src dst len info |
24 | 2372 87.408990 1.9.2 host 27 URB_BULK out |
Was ich mit dieser Info jetzt anfange, weiß ich allerdings nicht wirklich. Taktproblem würd ich ausschließen, weil 1) die Übertragung an sich ja funktioniert. 2) Takt wird mit CRS auf den USB synronisiert. Alex
Da ist irgendwas im Buffer Code was blockiert. Stefan liegt da mit seiner Vermutung schon richtig. Besorg dir mal UsbCV bei usb.org. Das Tool ist zwar etwas wackelig aber hilfreich. Zumindest die Chapter9 Tests sollten ohne Fehler oder Warnings durchlaufen. Ich selbst setze den Code von WS nicht ein, hab den deshalb nur überflogen. Ich benutze meine eigenen Implementation bzw. auf ARM den 3fach Code von Niklas.
Moin Thomas, mit UsbCV werd ich testen (sicherheitshalber auf einem anderen Rechner, das Installationsprogramm warnt deutlich davor, dass man sich damit Keyboard/Maus vom System wegschießen kann). Weist du, wie diese Pakete aufgebaut sind? Bzw. wo es dazu eine überschaubare Doku gibt? Was ist in den (ersten) 24 Bytes des Pakets drinnen? Alex
Mal meine fehlergkorrigierte Version von W.S.' Code probiert? https://github.com/Erlkoenig90/WSusb Es ist ziemlich umständlich ASCII-Befehle in USB-Pakete einzupacken. Wäre es nicht einfacher, SETUP-Pakete mit den richtigen Codes über den Control Endpoint zu schicken? Das lässt sich auch leichter verarbeiten und spart das Parsen. Ansonsten würde ich auf ein Softwareproblem tippen. Breakpoint in den USB-IRQ setzen und schauen was beim Empfang des Pakets passiert bzw. die Empfangs-Routine durchsteppen. Sonst kann ich natürlich nur empfehlen, meinen USB-Code zu verwenden, der hat solche Probleme bisher nicht...
Hallo Niklas, deine Version kannt ich bisher nicht, werd ich aber definitiv versuchen einzubauen. Das mit den ASCII-Befehlen hat den simplen Grund, ohne irgendeine spezielle Software auf der PC-Seite auszukommen.
Alex schrieb: > Das mit den ASCII-Befehlen hat den simplen Grund, ohne irgendeine > spezielle Software auf der PC-Seite auszukommen. Damit macht man es sich aber letztlich komplizierter. Mit der direkten Ansteuerung des USB-Protokolls z.B. per libusb erspart man sich eine Menge Fehlerpotential. Dass die Befehle wie 'stop<CRLF>' als ein USB-Paket ankommen ist ja auch keineswegs gesagt, der Host-Treiber für CDC-ACM kann die Texte beliebig zerstückeln oder puffern. PS: Wieso muss man die Messung überhaupt stoppen können? Man könnte auch die USB-Standby-Funktionalität implementieren, sodass der Host das Gerät zum Energie sparen abschalten kann, es aber ansonsten einfach immer läuft.
:
Bearbeitet durch User
Alex schrieb: > das Installationsprogramm warnt deutlich davor, dass man sich damit > Keyboard/Maus vom System wegschießen kann). Die Warnung betrifft vor allem PCs bei denen Maus und KB über USB angeschlossen ist. Das Tool schaltet den Hostcontroller in einen spez. Test Mode weshalb danach keines der am Host angeschlossenen Geräte normal funktioniert. Danach werden je nach Auswahl diverse Kommandos abgesetzt und die Ergebnisse ausgewertet. Beim Beenden wird dann der der HostController wieder zurückgeschaltet. Auf einem Laptop sollte es kein Problem geben da Maus und KB üblicherweise nicht an USB hängen. Ich versuche damit gerade ein MSC Device spec konform zu bekommen. Deine Frage zu den 24 Bytes kann ich nicht beantworten, glaube aber dass das irgendwelche Pointer (urb, Sequenznummern usw) sind, die nichts mit den Ep buffern zu tun haben.
hier mal 2 Beispiele für solche reports von UsbCV. Die stammen von meinem Compound Device was MSC und VCP können wird.
:
Bearbeitet durch User
Hab jetzt mal schnell das usbCV auf einem 'guten' und dem 'schlechten' STM durchlaufenlassen. Hab zuvor die usb.c auf die Neue Version von Niklas umgemünzt. Sowohl mit der alten Software (von S.F.) als auch mit der neuen von Niklas kommt ein Fehler beim Halt Endpoint Test. Für mich ergibt sich daraus die Vermutung, dass es an der Anpassung an meinen STM32F042-Controller liegt. Zum ASCII: - der USB Treiber darf die Befehle zerpflücken (tut er auch), das macht aber nix. - Das device muss ohne spezielle Treiber auf verschiedenen Plattformen unkompliziert laufen -> VCP ist die beste Möglichkeit dafür. - das 'stop' ist nur ein möglicher Befehl, und Befehle braucht es für die Funktionalität die ich benötige. Alex
Alex schrieb: > - Das device muss ohne spezielle Treiber auf verschiedenen Plattformen > unkompliziert laufen -> VCP ist die beste Möglichkeit dafür. Bei Verwendung von libusb braucht es keine Treiber.
Alex schrieb: > Sowohl mit der alten Software (von S.F.) als auch mit der neuen von > Niklas kommt ein Fehler beim > Halt Endpoint Test. Dann gibt es einen Bug beim Get/SetFeature bzw GetStatus liefert nicht das erwartete Ergebnis. (STALL oder 0). Das hatte ich schon weiter oben angesprochen.
Alex schrieb: > Für mich ergibt sich daraus die Vermutung, dass es an der Anpassung an > meinen STM32F042-Controller liegt. Falls du in der Nähe von Düsseldorf wohnst könnte ich dir das Nucleo L073 Board ausleihen, mit dem ich damals getestet hatte.
Danke für euren Input Leute, ich komm nicht hinterher :-) + Zum Thema VCP / libUsb: So wie die Devices jetzt sind, kann ich sie ohne Beschreibung oder sonstiger Info jemandem (z.B. einem Studenten) in die Hand drücken, und der kann damit was anfangen. Alles was er dazu braucht ist ein Terminalprogramm. Und das kriegt erfahrungsgemäß jeder hin. Mit allen anderen Lösungen muss auf dem Host-System irgendetwas implementiert/installiert werden. Und dieses irgendwas wird sich im Lauf der Zeit ändern. Damit hab ich permanent Support-Aufwand, den ich nicht haben will. Bei einem VCP ist das nicht nötig. Daher ist das aus meiner Sicht die beste Lösung. + Halt Endpoint Test Der sollte eigentlich mit dem Code von Niklas (und auch S.F. Version von W.S. code) funktionieren. Um auszuschließen, dass das an meinen Zusatzfunktionen liegt, werd ich morgen mal eine Minimalversion auf dem STM32F042 laufen lassen und den Test damit wiederholen. Die Anpassungen an den Controller hat sich bei mir auf UMEM_SHIFT=0 und die USB_IRQ_NUMBER=31 beschränkt. Hab ich da ev. was übersehen? + Das Übertragungsproblem Ist möglicherweise von dem Halt Endpoint Problem unabhängig, weil der Halt Endpoint Test auch bei jenen Modulen fehlschlägt, die für mich einwandfrei funktionieren. Das Suchen dieses Fehlers ist auch etwas knifflig, weil ich das nicht auf einer anderen Hardware nachstellen kann. (Danke Stefan für das großzügige Angebot, aber ich sitz in Österreich ;-)) > Dann gibt es einen Bug beim Get/SetFeature bzw GetStatus liefert nicht > das erwartete Ergebnis. (STALL oder 0). Das hatte ich schon weiter oben > angesprochen. Ich bin nicht sicher, ob ich das richtig interpretiere, aber in void DoGetStatus(void) hab ich folgende Zeile gefunden:
1 | if ((EP == logEpCtrl) || (EP == logEpInt) || (EP == logEpBulkIn) || (EP == logEpBulkIn)) |
Sollte da vielleicht einer der Bulks ein BulkOut sein? Alex
Alex schrieb: > Die Anpassungen an den Controller hat sich bei mir auf UMEM_SHIFT=0 und > die USB_IRQ_NUMBER=31 beschränkt. Hab ich da ev. was übersehen? UMEM_FAKEWIDTH vielleicht? Obwohl: Wenn irgendeiner der Konfigurationsparameter falsch wäre, würde wahrscheinlich gar nichts funktionieren.
:
Bearbeitet durch User
Alex schrieb: > hab ich folgende Zeile gefunden: > if ((EP == logEpCtrl) || (EP == logEpInt) || (EP == logEpBulkIn) || (EP > == logEpBulkIn)) > > Sollte da vielleicht einer der Bulks ein BulkOut sein? Genau so ist es.. Macht ja nicht soviel Sinn den gleichen Ep 2 mal abzufragen. Die Frage ist halt auch ob in den logxxx wirklich Stall conditions gespeichert sind. Mein eigener Code ist an der Stelle wesentlich aufwändiger. Ob das allerdings bei deinem Übertragungsproblem hilft ist fraglich.
Alex schrieb: > 2) Takt wird mit CRS auf den USB synronisiert. Du hast keinen Quarz dran? Dann wäre es das erste was ich probieren würde dem ganzen einen Quarz zu spendieren. Noch dazu wenn manche deiner Module gehen und ander nicht oder nicht immer. Das schreit ja förmlich nach Ursachen bei denen Toleranzen(oder jitter) eine Rolle spielen. Und wenn es nur darum geht andere Ursachen auszuschließen. Ich benutze den Grundcode mit vielen STM32F103, STM32F3x3, STM32L151 ... Oft als USB-CAN Interface mit SLCAN. Das entspricht ja so in etwa dem was du machst, nur mit viel mehr Last. Gut den F042 hatte ich noch nicht. Aber immer mit Quarz. Und es kann bei Taktproblemem ja durchaus sein, dass die Richtung zum PC funktioniert und die ander nicht(immer).
Alex schrieb: > Bei einem VCP ist das nicht nötig. Daher ist das aus meiner > Sicht die beste Lösung. Bei Serial-Ports kommt auch Support-Aufwand: - Welchen Port soll ich auswählen? - Welche Baudrate und welches Frame-Format wähle ich aus? - Was mache ich wenn der Linux ModemManager an neu angeschlossene Serial-Ports automatisch Nachrichten sendet weil es sie für ein Modem hält? - Wie verhindere ich dass Windows das Serial-Gerät als serielle Maus erkennt? - Soll ich das Echo ein/ausschalten? - Welchen Zeilenumbruch soll ich verwenden? Und ja, auch Fangfragen sind Aufwand ;-) Alex schrieb: > Mit allen > anderen Lösungen muss auf dem Host-System irgendetwas > implementiert/installiert werden. Ein Programm, welches mittels libusb und WinUSB auf Geräte zugreift, die als WinUSB deklariert sind, braucht keinerlei Installation/Konfiguration. Gerät anschließen, Programm draufkopieren & starten, fertig. Weniger Aufwand als ein Terminalprogramm (Port-Auswahl usw.). Unter Linux brauchts die WinUSB-Deklaration natürlich nicht, da geht es einfach so, aber sie stört auch nicht.
Niklas G. schrieb: > Ein Programm, welches mittels libusb und WinUSB auf Geräte zugreift, die > als WinUSB deklariert sind, braucht keinerlei > Installation/Konfiguration. Gerät anschließen, Programm draufkopieren & > starten, fertig. Weniger Aufwand als ein Terminalprogramm (Port-Auswahl > usw.). Unter Linux brauchts die WinUSB-Deklaration natürlich nicht, da > geht es einfach so, aber sie stört auch nicht. Da magst du zwar Recht haben, nur spielt das für die Probleme hier keine Rolle. Im Gegenteil, dann hätten wir gleich mehrerer Baustellen gleichzeitig. Es macht sicher auch einen gewaltigen Unterschied ob das ganze in die Großserie geht oder eine kleine Individuallösung ist. Und nicht jeder dringt so tief in die USB Materie ein um eine neue Gerätekategorie auf der Controllerseite zu implementieren. Deshalb ist ja VCP so beliebt, weil man am reinen USB-Code nichts ändern muss. Es ist schließlich auch immer eine Frage des Zeitaufwandes.
Ach noch ein Nachtrag. libusb unter Windows ist in meinen Augen eine Seuche. Vor allem wenn die Geräte mal mit einer Software und dem direkten Windowstreiber spielen soll, und eine andere Software nur libusb kann. Dann heißt es jedes mal die Treiber umkonfigurieren. Schlimmer geht nimmer. Gerade erst wieder für j-link und OpenOCD festgestellt.
temp schrieb: > Es ist schließlich auch > immer eine Frage des Zeitaufwandes. Richtig, der ganze Entwicklungsaufwand zur Paket-Anfang/Ende-Erkennung, Parsen, ASCII-Formatierung, Pufferung zwischen einzelnen USB-Paketen entfällt wenn man keinen VCP nutzt. USB direkt zu nutzen ist viel einfacher - man hat direkt das Konzept von Paketen und kann einfache Transaktionen simpel per SETUP-Paket auf dem Control-Channel abhandeln. Wenn man wenig Datenrate hat (wenige Bytes etwa im Sekundentakt?) würde es sogar reichen alles per Control Channel zu machen, wenn das Gerät nicht selbstständig senden können muss. Bei mir im Studium hat ein Prof das übrigens genau so gemacht - einen PID-Regler auf einem USB-fähigen Mikrocontroller implementiert, der vom PC aus per USB mit einer eigenen Anwendung parametrisiert werden konnte. Hat super funktioniert. temp schrieb: > Vor allem wenn die Geräte mal mit einer Software und dem > direkten Windowstreiber spielen soll, und eine andere Software nur > libusb kann. Für einen VCP hätte man auch keinen eigenen Windows-Treiber, daher keine vergleichbare Situation. temp schrieb: > Dann heißt es jedes mal die Treiber umkonfigurieren. > Schlimmer geht nimmer. Für die libusb+WinUSB Kombination braucht es keine Treiber-Installation oder Konfiguration. Windows lädt automatisch den WinUSB-Treiber und es funktioniert direkt.
Niklas G. schrieb: > Für die libusb+WinUSB Kombination braucht es keine Treiber-Installation > oder Konfiguration. Windows lädt automatisch den WinUSB-Treiber und es > funktioniert direkt. Wenn ich meinen j-link in der Segger- oder Crossworks-IDE benutzen will, wird der normale jlink-Treiber verwendet. Will ich das Teil unter OpenOCD benutzen, muss ich das Gehampele mit Zadig machen damit das geht. Danach gehen die IDEs nicht mehr. Das ist Mist. Vor allem weil ich bei der Umschalterei seit sehr, sehr langer Zeit mal wieder einen Bluescreen gesehen habe. Was mache ich falsch?
temp schrieb: > Was mache ich falsch? Die J-Link Firmware gibt sich nicht als WinUSB Device aus, verwendet einen "normalen" Windows-Treiber, und OpenOCD verwendet wohl kein WinUSB. Blöd, aber nicht relevant für diesen Thread, weil man für das eigene Gerät sowieso keinen normalen Treiber implementieren würde und man problemlos den WinUSB-Deskriptor einbauen kann. Bei Verwendung eines VCP würde man auch keinen eigenen Windows-Treiber einbinden der auf Basis des RS232-Protokoll ein eigenes API zu Verfügung stellt (geht das überhaupt?) und dann abwechselnd eine Anwendung verwenden welche den Port direkt anspricht. Der normale Windows-Treiber hat den Vorteil dass mehrere Anwendungen gleichzeitig drauf zugreifen können (das nutzen die Segger-Tools ja auch), aber das geht beim VCP auch nicht.
:
Bearbeitet durch User
temp schrieb: > Was mache ich falsch? gar nichts. Ich stimme mit dir überein, dass Libusb unter Win eher suboptimal ist. Win hatte sehr lange Zeit einfach kein Konzept für einen Usermode Treiber. Das würde dann durch WinUsb behoben, leider viel zu spät. Deshalb hat sich WinUsb bis heute nicht so richtig durchgesetzt. Ich bin ehrlich gesagt froh, dass es Zadig gibt. Ich bin aber auch mit Niklas der Meinung, dass das Kombo Libusb für Linux, WinUsb für Win die flexiblere Lösung ist. Unter Win funktioniert WinUsb fast wie ein Klassentreiber. Der Anwender braucht sich nie mehr um Treiber Installationen kümmern. Das ist VCP ja auch noch unter W7 immer noch ein Problem.
Niklas G. schrieb: > Blöd, aber nicht relevant für diesen Thread Naja, du hattest damit angefangen libusb ins Spiel zu bringen, was für diesen Thread ja auch nicht relevant ist. Und ich habe dir nur darauf geantwortet, dass ich lieber bei VCP bleibe als mit libusb zu hantieren. Ich denke wir belassen es dabei und warten mal ob Alex seine Probleme lösen kann oder nicht. Ein völlig neuer Ansatz bringt ihn da sicher nicht weiter. Zumal ich bei den STM32F042 mit 32k Flash auch öfters an die Grenzen kommen und der verwendete Code hier kleiner ist als alle anderen Libs. Was noch sehr klein ist ist der USB Teil vom hid-Bootloader: https://github.com/Serasidis/STM32_HID_Bootloader Basierend auf dem Code von W.S. habe ich hier auch schon mal ein Midi-Interface implementiert: Beitrag "STM32 USB-MIDI" Auch dabei kam es zu keinen Problemen die mit denen von Axel zu vergleichen waren. Ich denke so lange nicht das Gegenteil bewiesen ist, wird der Fehler beim Takt oder einer anderen Stelle liegen aber nicht beim Code von W.S., S.F. oder N.G.
temp schrieb: > Naja, du hattest damit angefangen libusb ins Spiel zu bringen, was für > diesen Thread ja auch nicht relevant ist. Ich finde es relevant, weil die libusb+WinUSB Kombo für solche einfachen Selbstbau-Geräte perfekt ist. temp schrieb: > Und ich habe dir nur darauf > geantwortet, dass ich lieber bei VCP bleibe als mit libusb zu hantieren. Aber mit einem Argument welches sich auf normale Windows-Treiber bezieht und nicht auf die vorgeschlagene libusb+WinUSB Lösung. temp schrieb: > Ein völlig neuer Ansatz bringt ihn da sicher > nicht weiter. Ein Ansatz welcher ohne die VCP-Bastelei (insb. mit der davon benötigten Pufferverwaltung) und ohne W.S.' Implementation davon auskommt könnte ihn durchaus weiter bringen.
der JLink bringt doch einen GDB Server mit, ist OOCD da überhaupt nötig?
Niklas G. schrieb: > Der normale Windows-Treiber hat den Vorteil dass mehrere Anwendungen > gleichzeitig drauf zugreifen können (das nutzen die Segger-Tools ja > auch), aber das geht beim VCP auch nicht. Was soll da auch sinnvolles dabei rauskommen? Thomas Z. schrieb: > Ich bin aber auch mit Niklas der Meinung, dass das Kombo Libusb für > Linux, WinUsb für Win die flexiblere Lösung ist. Unter Win funktioniert > WinUsb fast wie ein Klassentreiber. Der Anwender braucht sich nie mehr > um Treiber Installationen kümmern. Das ist VCP ja auch noch unter W7 > immer noch ein Problem. Das will ich ja nicht mal in Abrede stellen. Jedenfalls nicht solange alle! Programme die es für das jeweilige Gerät gibt diesen Weg benutzen. So wie beim SDR-Stick, da stört mich das auch nicht. Es wäre schön, wenn jemand für den Code von W.S. (oder folgende) mal ein Beispiel veröffentlichen würde das den von euch vorgeschlagenen Weg benutzt. So wie ich mit dem MIDI-Beispiel. Danke schon im Voraus an den der es macht.
Johannes S. schrieb: > der JLink bringt doch einen GDB Server mit, ist OOCD da überhaupt nötig? Nein, natürlich nicht. Das war nur ein Beispiel.
Niklas G. schrieb: > Ein Ansatz welcher ohne die VCP-Bastelei (insb. mit der davon benötigten > Pufferverwaltung) und ohne W.S.' Implementation davon auskommt könnte > ihn durchaus weiter bringen. So wie der Code von W.S. jetzt bei S.F. steht ist er nicht so schlecht, dass man ständig davon abraten muss. Deine C++ Implementierung ist auch nicht der letzte Schrei und mir persönlich viel zu aufgebläht. Aber was soll's, jeder hat seine eigenen Vorlieben und macht seine eigenen Erfahrungen.
temp schrieb: > Es wäre schön, wenn jemand für den Code von W.S. (oder folgende) mal ein > Beispiel veröffentlichen würde das den von euch vorgeschlagenen Weg > benutzt. Also einfach nur das Einbauen des WinUSB-Deskriptors und das Entfernen des CDC-ACM-Deskriptors? Der PC-Seite ist es egal ob es W.S.' oder mein Code ist. temp schrieb: > Was soll da auch sinnvolles dabei rauskommen? Genau so viel wie bei der Idee, dass OpenOCD nicht den J-Link-Treiber nutzt. Warum auch immer das so ist... temp schrieb: > Das war nur ein Beispiel. Ein Beispiel wie man etwas auf Basis von Technologie X schlecht umsetzt ist kein Grund Technologie X nicht zu nutzen... temp schrieb: > So wie der Code von W.S. jetzt bei S.F. steht ist er nicht so schlecht, > dass man ständig davon abraten muss. Ich finde das Grundkonzept, mit blockierenden Funktionen USB-Datentransfers zu machen ziemlich fragwürdig. Wenn sich da etwas verheddert mit anderen Interrupts in der Anwendung ist es gut möglich, dass das Programm stecken bleibt. Die Originalversion hat auch einen fragwürdigen C-Stil und diverse Bugs; ob es davon nicht noch mehr gibt weiß man nicht... temp schrieb: > Deine C++ Implementierung ist auch > nicht der letzte Schrei und mir persönlich viel zu aufgebläht. Sie blockiert immerhin nicht die main()-Schleife. Was daran aufgebläht sein soll weiß ich nicht.
:
Bearbeitet durch User
Ich hab mir jetzt nochmal den Code von WS in der Version von Niklas näher angesehen. Ich bin mir ziemlich sicher, dass zumindest OnGetStatus () im Falle der Enpoints (case 0x82:) keine korrekten Antworten liefert. Der Requests sollte ja den Zustand des EPs liefern. 1 für STALL 0 für UNSTALL. Der Code liefert aber für gültige Endpoints immer STALL, und wegen des copypaste Fehlers beim BulkoutEP immer UNSTALL. Ich werde versuchen dafür einen Patch einzustellen auch wenn Git noch nicht so mein Ding ist. Ich bin aber immer noch der Meinung, dass dieser Bug nicht ursächlich für die Probleme des TO ist
Niklas G. schrieb: > Ich finde das Grundkonzept, mit blockierenden Funktionen > USB-Datentransfers zu machen ziemlich fragwürdig. Wenn sich da etwas > verheddert mit anderen Interrupts in der Anwendung ist es gut möglich, > dass das Programm stecken bleibt. Die Originalversion hat auch einen > fragwürdigen C-Stil und diverse Bugs; ob es davon nicht noch mehr gibt > weiß man nicht... In ein paar Punkten muss ich dir widersprechen. Es ist nicht das Grundkonzept mit blockierenden Funktionen zu arbeiten. Der reine USB-Teil arbeitet wie bei dir auch interruptgetrieben. Anderenfalls wäre eine main-loop nötig, die es aber nicht gibt. Die einzige Stelle in der das blockieren kann ist die bool UsbCharOut(char c) und die ist ja wohl dem User-Code zuzurechnen und hier nur ein Beispiel. Und dass sie blockiert ist da auch nicht nötig, das kann jeder handhaben wie er will. Das mit den Bugs weiß man bei deinem Code auch nicht, und der C-Stil ist ja wohl Ansichtssache. Ob dein Code außer für dich für jedermann sofort verständlicher ist? Niklas G. schrieb: > Also einfach nur das Einbauen des WinUSB-Deskriptors und das Entfernen > des CDC-ACM-Deskriptors? Der PC-Seite ist es egal ob es W.S.' oder mein > Code ist. Und genau das ist schon die Stelle, wo viele scheitern. Die wollen so eine Lib benutzen ohne die Deskriptoren jemals verstanden zu haben. Und weil das so ist, wird noch so oft VCP verwendet. Jetzt ist hier aber für mich Schluss mit allen Diskussionen die sich nicht mehr um das eigentliche Problem drehen.
Also Leute, nun habt ihr schon etwa 7 Tage lang hier herumdiskutiert und alles sonstige wie Libusb, Zadig, und sonstwas genannt. Führt das zu etwas? Nö. Und auch die Ansichten von Niklas: "USB direkt zu nutzen ist viel einfacher - man hat direkt das Konzept von Paketen..." sind hier überhaupt nicht zielführend, denn das wäre dann eben kein virtueller COM-Port mehr, sondern etwas ganz anderes. Also merkt doch mal, daß der Sinn eines virtuellen COM-Portes eben genau darin besteht, daß man eine asynchrone Einzelzeichen-Verbindung von einer App auf dem PC bis hin zu einer anderen "App" (sprich Firmware) auf einem anderen gerät haben kann, OHNE daß sich die beiden Apps darum scheren müßten, wie die Daten zwischendurch transportiert werden. Das ist sache des OS und das OS soll das eben so tun, daß die Verbindung funktioniert, ohne daß die Apps sich um deren interne Details kümmern müssen. So und wenn ich das hier lese: Alex schrieb: > Aktuell hab ich ein Exemplar, das reagiert einfach garnicht auf Befehle. > Reproduzierbar auf verschiedenen PCs, Win7, Win10, Linux. > Die Daten vom STM kommen im Sekundentakt zum PC wie sie sollen (ist > default). ..dann kommt mir der Gedanke, daß die Richtung PC-->µC irgendwie gestört ist. Folglich würde ich an einen freien UART des µC mal etwas dranhängen, einen anderen Port des PC zum Beispiel, und im µC den Datenstrom vom USB-Treiber her auf diesen UART ausgeben, damit man mal sehen kann, was da denn so ankommt. Und das ohne irgendwelche Debugger, Usbsniffer etc. und nur mittels der normalen Firmware, damit möglichst nichts an unvorhergesehenen Beeinflussungen passieren kann. Dann dürfte man ja wohl weiter sehen und dem Problem etwas näher kommen. Alex schrieb: > Ich hab mehrere von diesen USB-Datenerfassungsmodulen gebaut, und hab > schon mehrmals beobachtet, dass manche Exemplare komisches Verhalten > zeigen, wenn man vom PC Befehle zum STM schickt. Gegenfrage: hast du in deine Firmware einen kleinen Kommandointerpreter eingebaut? Also etwas, das aus den hereinkommenden Einzelzeichen eine Kommandozeile aufbaut (und die Zeichen echot) und diese nach einen CR oder wenn der Puffer dafür voll ist dann auswertet? Und wenn da etwas unverständliches drin steht, einen Fehlertext zurückschickt? Wäre wohl hilfreich. Nochwas: Alex schrieb: > hab ich folgende Zeile gefunden:... Ja, das ist wohl dieses hier (in DoGetStatus):
1 | ...
|
2 | case 0x82: /* für einen Endpoint */ |
3 | if ((EP==logEpCtrl) || |
4 | (EP==logEpInt) || |
5 | (EP==logEpBulkIn) || |
6 | (EP==logEpBulkIn)) Buf[0] = 1; |
7 | break; |
und das ist offensichtlich ein uralter Schreibfehler, der offenbar noch niemandem (mich eingeschlossen) aufgefallen ist. Ja, der steht auch bei Niklas drin. Ich bin mir aber nicht sicher, ob diese Sequenz überhaupt benötigt wird. Vielleicht sollte mal jemand anderes den Quelltext nach etwaigen weiteren derartigen Schusselfehlern durchsuchen. Noch ein Wort zu Niklas' Version: Ich finde es bedenklich, unschön und unüberlegt, bei jeder Gelegenheit von der Anwendungsebene aus am Interrupt herumzuschalten. Das ist Programmieren mit dem Schmiedehammer. Die Cortexe sind 32 Bit Maschinen und da sind Zugriffe auf die Indizes allesamt atomar und eine jede Seite ändert NICHTS an dem Index, der ihr nicht gehört. Folglich ist all dieses DisableUsbIRQ() etc. nicht erforderlich. Auch mit anderen Dingen (UsbActive(), UsbTxFlush()) hat sich dieser Treiber vom Prinzip des COM-Ports entfernt und kann in der Firmware nicht mehr wirklich genau so wie ein UART benutzt werden. Mir wäre das wichtig, aber wer es partout anders halten will, mag es halt tun. W.S.
W.S. schrieb: > Folglich ist all dieses DisableUsbIRQ() etc. nicht > erforderlich. Dankeschön. Ich habe das eingebaut weil einige Leute darauf bestanden dass es nötig sei, aber auch ich bin immer noch der Meinung, dass das Quatsch war. Ich nehme das wieder raus.
W.S. schrieb: > Noch ein Wort zu Niklas' Version: Ich finde es bedenklich, unschön und > unüberlegt, bei jeder Gelegenheit von der Anwendungsebene aus am > Interrupt herumzuschalten. Das ist Programmieren mit dem Schmiedehammer. Der eigentliche Fehler ist es, aus der main-Schleife heraus auf die Kommunikation zuzugreifen. Würde man das in Interrupts machen, hätte man das Problem nicht. W.S. schrieb: > Folglich ist all dieses DisableUsbIRQ() etc. nicht > erforderlich. In meinem Code werden unter der Interruptsperre jeweils mehrere Bedingungen verknüpft. z.B. bei:
1 | /* liefert true, wenn noch ein Zeichen in den Tx-Buffer passt */
|
2 | bool UsbTxReady(void) |
3 | {
|
4 | DisableUsbIRQ (); |
5 | bool res = configurationSet && !suspended && ((txw + 1) & (txLen - 1)) != txr; |
6 | EnableUsbIRQ (); |
7 | |
8 | return res; |
9 | }
|
Ohne die Interruptsperre könnte zwischen dem Auslesen von configurationSet und txw o.ä. ein Interrupt kommen. Bei Sachen wie:
1 | /* liefert true, wenn Tx-Buffer leer ist */
|
2 | bool UsbTxEmpty(void) |
3 | {
|
4 | DisableUsbIRQ (); |
5 | bool res = (txw == txr); |
6 | EnableUsbIRQ (); |
7 | return res; |
8 | }
|
kann es sogar sein dass die Interruptsperre unnötig ist; ich war ehrlich gesagt zu faul mir das ganz genau für alle Fälle zu überlegen. Wenn du mir genau aufschlüsseln kannst dass das wirklich in allen Funktionen unnötig ist, kann ich das übernehmen. Man muss ja auch beachten dass die Auslese-Reihenfolge der Variablen (hier: txw und txr) nicht garantiert ist. Da das ganze sowieso sehr ineffizient ist (jedes Zeichen einzeln im FIFO verarbeiten), macht das Ein/Aus-Schalten den Braten auch nicht fetter. W.S. schrieb: > und kann in der > Firmware nicht mehr wirklich genau so wie ein UART benutzt werden Kann deiner auch nicht, wenn der Host den Port deaktivieren/aktivieren können ohne das Programm zu blockieren soll (wie im alten Thread gewünscht). Code der mit deiner Lib funktioniert müsste auch mit meiner korrigierten Version funktionieren. W.S. schrieb: > Also merkt doch mal, daß der Sinn eines virtuellen COM-Portes eben genau > darin besteht, daß man eine asynchrone Einzelzeichen-Verbindung von > einer App auf dem PC bis hin zu einer anderen "App" (sprich Firmware) > auf einem anderen gerät haben kann, Hier ist aber keine Einzelzeichen-Übertragung gewünscht, sondern mehrere Zeichen lange Pakete. Und die machen es wieder komplizierter, weil man Paket-Anfang/Ende erkennen muss usw. Und da USB rein zufällig ein Modell für Transaktionen und Datenpakete schon beinhaltet, macht es viel mehr Sinn, das einfach zu nutzen, als das in einen Einzel-Zeichen-Strom zu abstrahieren und da wieder Pakete hinein zu definieren.
:
Bearbeitet durch User
Zur Info: Die Variable "suspended" wurde erst später (nicht von W.S.) hinzugefügt. Dass man dort eine Interrupt-Sperre Sinn macht hatte er wohl deswegen nicht auf dem Schirm.
Schön dass alle Krieger versammelt sind... Niklas G. schrieb: > Der eigentliche Fehler ist es, aus der main-Schleife heraus auf die > Kommunikation zuzugreifen. Würde man das in Interrupts machen, hätte man > das Problem nicht. Sorry aber das tut schon weh. Wo ist denn das ein Problem? Diesen Teil muss doch sowieso jeder an seine Applikation anpassen und damit die UsbCharOut(). Und je nachdem was ich will, breche ich nach einem Timeout ab, oder gleich, oder oder oder. Und wenn jemanden das mit dem char zu wenig ist, dann muss man da ein wenig optimieren. Aber das hat rein gar nichts mit dem USB-Grundcode zu tun. Ich glaube fast du bist auch so ein typischer Programmierer für den alle Räder eckig sind die sie nicht selbst erfunden haben.
temp schrieb: > Wo ist denn das ein Problem? (Blockierender) I/O-Code in der main()-Schleife ist nicht für Nebenläufigkeit skalierbar. Da gehören höchstens rechenintensive Dinge hin. Alles andere handelt man im jeweiligen Interrupt ab. temp schrieb: > Ich glaube fast du bist auch so ein typischer Programmierer für den alle > Räder eckig sind die sie nicht selbst erfunden haben. Rein zufällig ist sowohl die USB-Peripherie als auch ST's eigene Library komplett darauf ausgelegt, asynchron aus ISRs heraus benutzt zu werden.
Jeder kann und darf den Code so weit aufblähen, wie er es braucht. Ich halte die Kritik an diesem Punkt es für unnötige Ablenkung vom Thema.
Niklas G. schrieb: > Rein zufällig ist sowohl die USB-Peripherie als auch ST's eigene Library > komplett darauf ausgelegt, asynchron aus ISRs heraus benutzt zu werden. Das ist doch hier auch der Fall. Mir scheint du hast den Code überhaupt nicht verstanden. Ansonsten zeig die Stellen konkret auf die du dich beziehst. Das wird langsam lächerlich mit dir. Wo soll den der USB Interrupt her wissen wann es etwas zum senden gibt? Und wenn ich was senden will ist es mir in der Regel nicht egal ob der USB-Teil das annimmt oder nicht? Und wie man damit umgeht, wenn etwas nicht gesendet werden konnte, das kannst du ruhig dem Anwender überlassen.
temp schrieb: > Mir scheint du hast den Code überhaupt > nicht verstanden. Genau, deswegen hab ich auch W.S.'s Bugs beheben können, die er seit Jahren nicht gefunden hat. temp schrieb: > Wo soll den der USB Interrupt her wissen wann es etwas zum senden gibt? Schau einfach in meinen Code und in mein Tutorial, da ist alles genau erläutert.
Nun habe ich den Code von W.S. auch mal probiert. Danke auch an Stefan Frings, der auf seiner Webseite nützliche Erklärungen dazu bereitstellt.# Ich habe 2h investiert und beschlossen, die andere Variante vom MCD-Application-Team mit ca. 20 Files aus meiner Bibliothek rauszuschmeissen und künftig die von W.S. zu nehmen, weil sie stabiler läuft. Beim Verlassen von UsbSetup() prüfe ich mit UsbActive() noch ein paar ms, ob das Kabel drinsteckt bzw. USB bereit ist, und ansonsten war da nichts mehr dran zu ändern. Wobei ich mit der SPL programmiere und die ganzen Linkerscripts und Assemblerfiles nicht brauchte. Neu war mir auch, daß man die GPIOs PA11/12 für USB gar nicht mit Alternate Function bzw. als In/Output vorbelegen muß.
Jürgen S. schrieb: > Ich habe 2h investiert und beschlossen, die andere Variante vom > MCD-Application-Team mit ca. 20 Files aus meiner Bibliothek > rauszuschmeissen und künftig die von W.S. zu nehmen, weil sie stabiler > läuft. Ja so ist es auch in der Regel. Warum das bei Axel Probleme machte weiß keiner, aber auch nicht ob eine andere Implementierung läuft. Der Verdacht liegt immer noch auf Hardware bzw. Takt. Klar einen Schönheitspreis gewinnt der Code nicht, muss er aber auch nicht. Dafür ist er aber auch von keiner weiteren Lib abhängig und auch von keiner Headerorgie wo es je nach Umgebung an allen Ecken und Enden klemmen könnte.
Niklas G. schrieb: > Genau, deswegen hab ich auch W.S.'s Bugs beheben können, die er seit > Jahren nicht gefunden hat. Wer hier den Hintergrund verstehen will sollte den folgneden Thread lesen: Beitrag "USB CDC von Stefan Frings und WS" Ich kann bestätigen das Niklas die Bugs gefixed hat und den Code nun auch bereitgestellt hat. Das geht immer wieder in anderen Folge Threads verloren. Warum führt das eigentlich hier immer zu Streit ? Wie dem auch sei, hat ja mit dem Thema nix zu tun. Jedenfalls verwende ich Niklas Version seit einem Jahr ohne irgendwelche Probleme.
Bernd N. schrieb: > Ich kann bestätigen das Niklas die Bugs gefixed hat und den Code nun > auch bereitgestellt hat Dann lest euch nochmal die Threads durch von wem die Bugs mit dem Nops beseitigt wurden. Da war kein Niklas im Spiel. Niklas hat seinen Anteil insgesamt, aber nicht nur er.
temp schrieb: > Dann lest euch nochmal die Threads durch von wem die Bugs mit dem Nops > beseitigt wurden. Da war kein Niklas im Spiel. Die Set-Adress-Korrektur war in der Version, die ich von W.S. übernommen hatte, schon drin. Ich hatte das aber viel früher genau so schon in meinem eigenen USB-Code gemacht. Jürgen S. schrieb: > Beim Verlassen von UsbSetup() prüfe ich mit UsbActive() noch ein paar > ms, ob das Kabel drinsteckt bzw. USB bereit ist Das stammt auch von mir... Bernd N. schrieb: > Warum führt das eigentlich hier immer zu Streit ? Weil gewisse Leute hier gerne stänkern: W.S. schrieb: > Das ist Programmieren mit dem Schmiedehammer. temp schrieb: > Sorry aber das tut schon weh. Wo ist denn das ein Problem? temp schrieb: > Ich glaube fast du bist auch so ein typischer Programmierer für den alle > Räder eckig sind die sie nicht selbst erfunden haben. temp schrieb: > Das wird langsam lächerlich mit dir.
Niklas G. schrieb: > Das stammt auch von mir... Wie auch der gute USB-Artikel, bei dem man aber wirklich tief einsteigen muß, um es zu verstehen. Ich hab's versucht und dann beschlossen, daß ich USB nicht wirklich verstehen muß - es soll nur funktionieren :). Ich nehme an, daß in den Dateien auf stefanus Seite (STM32F103_usb_test.zip) auch Deine Verbesserungen eingeflossen sind. Danke Dir deshalb nochmal extra, und W.S. natürlich ebenfalls.
Jürgen S. schrieb: > Ich nehme an, daß in den Dateien auf stefanus Seite > (STM32F103_usb_test.zip) auch Deine Verbesserungen eingeflossen sind. Ja sind sie. Und zum Teil wieder heraus geflossen. Die unnötigen Interrupt-sperren habe ich gerade wieder entfernt und der oben genannte Tippfehler ist jetzt auch korrigiert. http://stefanfrings.de/stm32/index.html
:
Bearbeitet durch User
Ok, wenn wir schon mal dabei sind möchte ich den Klappstuhl begraben und auf etwas anderes hinweisen. In der InitEndpoints() wird am Ende das FLAG für den 1ms Frameinterrupt SOFM mit gesetzt:
1 | USB_CNTR = |
2 | CTRM | /* Int bei ACKed Paketen in oder out */ |
3 | RESETM | /* Int bei Reset */ |
4 | SUSPM | WKUPM | ESOFM | SOFM; /* Int bei 1 ms Frame */ |
Im Interrupthandler selbst wird aber so gut wie nichts gemacht:
1 | if (I & SOF) /* Start of Frame, alle 1 ms */ |
2 | {
|
3 | //trace("SOF\n");
|
4 | USB_ISTR = ~SOF; /* Int löschen */ |
5 | suspended = false; |
6 | // OnEpBulkIn(); /* immer mal nachschauen... */
|
7 | }
|
Das Rücksetzten des Flags "suspended" an dieser Stelle ist in meinen Augen nicht nötig. Auf alle Fälle scheit mir der 1ms Interrupt nicht nötig zu sein. Bei mir geht das auch ohne und deshalb habe ich das SOFM flag auch nicht gesetzt. Eventuell ist das ja auch die Ursache für Probleme die manche haben. Ich weiss ja nicht, was passiert wenn anderweitige höher priorisierte Interrups so blöd programmiert sind dass sie viel länger dauern.
sorry, hatte noch was falsches im Namen stehen. Der letzte Beitrag kam von "temp"
Der Code ist vielfach erprobt und auch beim TO funktioniert er auf einigen Modulen. Das stinkt gewaltig nach einem Hardwarefehler oder schlechter Taktversorgung. Man könnte zur gegenprobe einfach mal mein simples Beispielprogramm laufen lassen, wo der µC jede Sekunde "Hallo" an den PC sendet. Wenn das klappt, dann probiert man die umgekehrte Richtung. Wenn eins davon scheitert, liegt mit Sicherheit ein Hardwarefehler vor. Ich schätze zu 80%, dass es ein Hardwarefehler ist und <5%, dass es ein Fehler in usb.c ist.
:
Bearbeitet durch User
Stefan ⛄ F. schrieb: > Ich denke, ihr könnt aufhören, über mutmaßliche Softwarefehler in den > beiden USB Dateien zu spekulieren. Lasst ihn zuerst mal die Hardware und > die Taktversorgung untersuchen. gleiche Meinung und auch schon ein paar mal kommuniziert. Wäre trotzdem schön wenn jemand die Sache mit dem 1ms Interrupt mal gegenchecken kann. Sowas muss man ja nicht unbedingt sein wenn es nicht benötigt wird. Und schon gar nicht mit so einer Wiederholrate.
temp schrieb: > Schön dass alle Krieger versammelt sind... Ach nö, als Krieger empfinde ich mich nicht. Aber mal ne Überlegung zum COM-Port als solchem, egal ob nun virtuell oder sonstwie: 1. Wenn nix hereinkommt, dann kommt eben nix herein und sowas wie CharAvail() kann nichts anderes melden als "false". Und das sowohl dann, wenn das Kabel ab ist als auch wenn der Sender nix sendet. 2. Wenn der Empfänger nicht eingeschaltet ist oder das Kabel ab ist oder sonstwo die Übertragung unterbrochen ist, dann sendet der COM-Port die Daten ins Nirwana. Er merkt das nicht einmal! So - und nun sollten wir mal dran denken, ob und wie so ein virtueller COM-Port sich denn benehmen sollte. Ich hab damals danach getrachtet, daß der USB-Comport sich möglichst genau wie ein gewöhnlicher Comport benehmen soll, aber ich hatte mich nicht getraut, die Sendedaten einfach so ins Nirvana zu schicken, wenn sie partout nicht vom Host abgeholt werden. Stattdessen hatte ich - obwohl das für einen richtigen Comport eher unüblich ist - sowas wie UsbTxReady() und UsbTxEmpty() eingebaut, damit sich ggf. das übergeordnete Programm erstmal davon überzeugen kann, ob es die Daten, die es senden will, auch loswerden kann. Und wenn nicht, dann kann es der Treiber auch nicht richten, dann muß eine andere Stelle in der Firmware entscheiden, was zu tun ist. Vielleicht - als Anregung - könnte man das Verhalten eines echten Comports hier nachempfinden: wenn kein Usb-Timertick kommt oder wenn nach etwa 10 ms noch immer nix abgeholt worden ist, dann sollte man den EpBulkIn eben stallen und alle bis dato aufgelaufenen Bytes ins Nirvana befördern. Das würde so etwa dem Verhalten eines echten Comports entsprechen. Ob sich sowas gut macht, wäre auszuprobieren, ich wage da keine Vorhersage. Niklas G. schrieb: > Bei Sachen wie:/* liefert true, wenn Tx-Buffer leer ist */ > bool UsbTxEmpty(void) > { > DisableUsbIRQ (); > bool res = (txw == txr); > EnableUsbIRQ (); > return res; > } > kann es sogar sein dass die Interruptsperre unnötig ist; ich war ehrlich > gesagt zu faul mir das ganz genau für alle Fälle zu überlegen. Letzteres hätte ich aber als selbstverständlich erachtet. Zur Sache: Die Seite, die den Ringpuffer befüllt, besitzt den Füll-Index. Die seite, die den Ringpuffer entleert, besitzt den Entleer-Index. Was passiert nun, wenn du in obigem Code feststellst, daß der Puffer nicht leer ist und der Interrupt direkt nach dem EnableUsbIRQ() den Puffer leert? Dann kehrt die Funktion eben mit "ist nicht leer" zurück, obwohl der Puffer bereits leer ist. Kurzum: das Disable/Enable nützt überhaupt nichts. Bei allen anderen Tests verhält es sich genaus so: Wenn man testet, ob denn noch Platz für ein Byte frei ist, dann ist es unerheblich, ob ein mittendrin hereinrauschender Interrupt noch weitere 64 Plätze frei gemacht hat. Und wenn man im Inputpuffer nachfragt, ob ein Byte drin ist, dann ist es unerheblich, ob ein Interrupt mittendrein noch weitere 64 Bytes dazupackt. Nochwas zu dem elenden Streit wegen der "Nops" und der Trampelschleife: Das ist GCC-spezifisch (der Keil macht es richtig) und es ist eigentlich herzlich nebensächlich. Eigentlich geht es ja nur darum, ein paar Mikrosekunden zu schinden, bis daß der Host das ACK-Paket abgeholt hat, damit man sofort danach auf die zugeteilte Adresse umschalten kann. Ich wollte damals nicht eine bedingte Warteschleife schreiben, a la while (Paket noch nicht abgeholt) do warten; um schlichtweg jegliches Blockieren zu vermeiden. Das ist alles. Natürlich kann man - aus heutiger Sicht - auch den Zustand des ACK-Paketes abfragen, sagen wir mal 10 mal oder 20 mal und sobald es abgeholt ist die Schleife vorzeitig verlassen. Das wäre wohl die sauberste Lösung. temp schrieb: > Wo soll den der USB Interrupt her wissen wann es etwas zum senden gibt? > Und wenn ich was senden will ist es mir in der Regel nicht egal ob der > USB-Teil das annimmt oder nicht? Ähem.. das ist ein tieferes Problem. Es gibt immer einen Sammel-Interrupt und in einem Statusregister steht, welcher Teil der Hardware einen Service benötigt. Aber ein Service-Bit für den EpBulkIn gibt es nur dann, wenn der Host einen zuvor hereingereichten Datenblock gelesen hat. Der ganze "Interrupt" kann NICHT und NIEMALS wissen, ob irgendwo Daten zum senden an den Host bereitstehen. Das ist die Krux. Verstehe mal, daß das Int-Bit für den zuständigen Endpoint nur dafür da ist, daß dieser weiß, daß sein letztes Paket angekommen ist. Das sagt NICHTS aus darüber, ob es in dem µC noch weitere Daten zum senden gibt. Ich hatte damals den Timer-Service Interrupt des USB dazu benutzt, um nachzuschauen, ob es Sendedaten gibt und ggf. deren Absendung zu veranlassen - vorausgesetzt, es ist nicht grad schon eine Übertragung angesagt. Aber diesen Mechanismus zum eigenständigen Wieder-in-Gang-kriegen der Transmission µC-->PC scheint man hier nicht verstanden zu haben. Dabei ist das essenziell! Entweder der USB-Interrupt regelt das selber (er wird sich garantiert NICHT selbst unterbrechen) - oder man muß es eben aus der mainloop heraus anstoßen. Letzteres ist deutlich gefährlicher, weil man genau dabei damit rechnen muß, daß man durch einen Interrupt unterbrochen wird ODER eine grad laufende Hardwarefunktion unterbricht oder diese beeinflußt und dabei eine Fehlfunktion bei irgendwas wie z.B. Toggelbit oder bereits gefüllten Puffer nochma befüllen oder falsche Länge etc. auslöst. Immer dran denken, daß die SIE ein durchaus komplexer eigener Prozessor ist. So etwas muß man vorher bedenken. W.S.
W.S. schrieb: > Nochwas zu dem elenden Streit wegen der "Nops" und der Trampelschleife Der Hauptgrund war ja noch nicht mal das Wegoptimieren der Nops sondern, dass sie im Interrupthandler überhaupt nötig waren. Das wurde damals in diesem Thread behandelt und gelöst und ist im aktuellen Stand auch so: Beitrag "Re: STM32F303 CAN und USB gleichzeitig" Trotzdem auch an dich die Frage: Wie siehts du die Notwendigkeit des 1ms Interrupts von dem ich ein paar Beiträge vorher gesprochen habe?
Stefan ⛄ F. schrieb: > Ja sind sie. Und zum Teil wieder heraus geflossen. Die unnötigen > Interrupt-sperren habe ich gerade wieder entfern Die von mir ausgebauten Funktionen UsbCharOut, UsbTxFlush, UsbTxReady, UsbActive, UsbGetChar brauchen die Interrupt-Sperre definitiv. Die da auszubauen ist... nicht so clever. arduinohasse schrieb: > Auf alle Fälle scheit mir der 1ms Interrupt nicht nötig zu sein. Der war bei W.S.' Code nur "nötig" weil er den SOF-Interrupt zum Absenden missbraucht hat. Kann man aber nach meinen bzw. temp's Korrekturen deaktivieren. W.S. schrieb: > Eigentlich geht es ja nur darum, ein paar > Mikrosekunden zu schinden, bis daß der Host das ACK-Paket abgeholt hat, > damit man sofort danach auf die zugeteilte Adresse umschalten kan Das ist halt schon völlig verkehrt. Man setzt die Adresse einfach nachdem das ACK gesendet wurde. Dann funktioniert alles perfekt und man braucht das dumme NOP gar nicht erst. Auch wenn das mit dem GCC natürlich auch ginge. Das haben "temp" und ich unabhängig voneinander hinbekommen und es funktioniert sauber, ist also definitiv die bessere Lösung als so ein Delay. W.S. schrieb: > Was passiert nun, wenn du in obigem Code feststellst, daß der Puffer > nicht leer ist und der Interrupt direkt nach dem EnableUsbIRQ() den > Puffer leert? Dann kehrt die Funktion eben mit "ist nicht leer" zurück, > obwohl der Puffer bereits leer ist. Kurzum: das Disable/Enable nützt > überhaupt nichts. Das war klar. Aber was passiert wenn der Interrupt genau in die Mitte der Funktion zuschlägt? Bei deinen Original-Funktionen ist die Sperre wohl tatsächlich nicht nötig. Wie gesagt, ich hatte keine Lust das auszutüfteln. Meine erweiterten Funktionen brauchen die Sperre, weil auf mehrere Variablen zugegriffen wird. W.S. schrieb: > Aber diesen Mechanismus zum eigenständigen > Wieder-in-Gang-kriegen der Transmission µC-->PC scheint man hier nicht > verstanden zu haben. Das ist eine Zweckentfremdung des SOF-Pakets. Du hast es nicht verstanden. W.S. schrieb: > Letzteres ist deutlich gefährlicher, Funktioniert bei mir aber perfekt, ohne die bis 1ms Verzögerung. Die Interruptsperre entfällt wie gesagt sowieso, wenn man die gesamte Architektur sauber asynchron in Interrupts packt. Mein eigener Code kommt ohne aus.
Niklas G. schrieb: > Die von mir ausgebauten Funktionen UsbCharOut, UsbTxFlush, UsbTxReady, > UsbActive, UsbGetChar brauchen die Interrupt-Sperre definitiv. Die da > auszubauen ist... nicht so clever. Ich nehme mir die Freiheit, das anders zu sehen.
Stefan ⛄ F. schrieb: > Ich nehme mir die Freiheit, das anders zu sehen. Aus echtem Interesse - wie gehst du so etwas an? Gehst du jede einzelne Zeile der genannten Funktionen durch, überlegst wie der Compiler sie umordnen könnte, überlegst ganz genau was passiert wenn welcher Interrupt genau wo dazwischen kommt und dann was macht? Eine Zeile welche 3 Variablen ausliest hat ja schon 6 Möglichkeiten, wie der Compiler sie umsetzt. Stellst du einen Entscheidungsbaum auf, was bei welcher Umsetzung passiert? Wie lange hast du für die Analyse dieses Programms gebraucht? Was passiert z.B. wenn UsbGetChar ClearBuffer aufruft, und genau zwischen Lesen & Schreiben des EPnR ein Interrupt kommt? Niklas G. schrieb: > Kann man aber nach meinen bzw. temp's > Korrekturen deaktivieren. ... ist doch Quatsch, denn: arduinohasse schrieb: > Das Rücksetzten des Flags "suspended" an dieser Stelle ist in meinen > Augen nicht nötig. Wo soll man es denn sonst zurücksetzen? Wenn der Host das Gerät aktiv halten bzw. reaktivieren will, aber keine Daten sendet/empfängt, kommt sonst kein anderer Interrupt.
Niklas G. schrieb: > Aus echtem Interesse - wie gehst du so etwas an? Gehst du jede einzelne > Zeile der genannten Funktionen durch, überlegst wie der Compiler sie > umordnen könnte, überlegst ganz genau was passiert wenn welcher > Interrupt genau wo dazwischen kommt und dann was macht? So ungefähr. > Eine Zeile welche 3 Variablen ausliest hat ja schon > 6 Möglichkeiten, wie der Compiler sie umsetzt. Erstens sind es nur 2 Variablen und eine Konstante und zweitens spielt es für die Entscheidung keine Rolle, ob der Interrupt dazwischen funkt. W.S. hat bereits in diesem und in früheren Threads mehrfach versucht, dir zu erklären, warum es da keine Probleme gibt. Ich stimme ihm voll zu. Wenn du das nicht verstehst, dann lass es halt. Die Welt geht nicht davon unter, weder durch deine noch anderer Leute Irrtümer.
Stefan ⛄ F. schrieb: > W.S. hat bereits in diesem und in früheren Threads mehrfach versucht, > dir zu erklären, warum es da keine Probleme gibt. W.S. hat erklärt, warum es in seinem Code keine Probleme gibt. Niklas' Code sieht an den genannten Stellen anders aus. Wenn mehrere Variablen hintereinander atomar geändert werden müssen, ist eine Sperre unausweichlich.
:
Bearbeitet durch Moderator
Frank M. schrieb: > W.S. hat erklärt, warum es in seinem Code keine Probleme gibt. Niklas' > Code sieht an den genannten Stellen anders aus. Wenn mehrere Variablen > hintereinander atomar geändert werden müssen, ist eine Sperre > unausweichlich. Danke, der Erste der es versteht. In UsbTxReady sind es sogar 4 Variablen, d.h. 24 Möglichkeiten es zu kompilieren.
Frank M. schrieb: > W.S. hat erklärt, warum es in seinem Code keine Probleme gibt. Niklas' > Code sieht an den genannten Stellen anders aus. Wenn mehrere Variablen > hintereinander atomar geändert werden müssen, ist eine Sperre > unausweichlich. Ja das sehe ich ein. Bei seinen hinzugefügten Code. Aber bei den Teilen, die von W.S. kommen werden keine Interruptsperren gebraucht.
Stefan ⛄ F. schrieb: > Aber bei den Teilen, die von W.S. kommen werden keine Interruptsperren > gebraucht. Das habe ich vorhin auch schon geschrieben. Niklas G. schrieb: > kann es sogar sein dass die Interruptsperre unnötig ist; ich war ehrlich > gesagt zu faul mir das ganz genau für alle Fälle zu überlegen. Du hast die Sperren aber auch in meinen erweiterten Funktionen entfernt.
Niklas G. schrieb: > Du hast die Sperren aber auch in meinen erweiterten Funktionen entfernt. Korrekt erkannt, ich habe es auch schon wieder bereut. Während wir hier diskutierten hatte ich das bereits in Arbeit. Wenn du magst kannst du nochmal kontrollieren, ich habe die ZIP Files gerade nochmal aktualisiert. Die Interrupt-Sperren um deine Codeabschnitte sind wieder drin.
:
Bearbeitet durch User
Stefan ⛄ F. schrieb: > Wenn du magst kannst du > nochmal kontrollieren In UsbCharOut, UsbTxReady, UsbActive, UsbGetChar fehlt es vermutlich weiterhin an Sperren. Habe keine Lust das wie beschrieben detailliert aufzudröseln ob man die irgendwo einsparen kann; ist schließlich eine Mikrooptimierung, wenn man wirklich Rechenleistung einsparen wollte sollte man nicht jedes Zeicheln einzeln verarbeiten sondern ganze Pakete.
Niklas G. schrieb: > In UsbCharOut, UsbTxReady, UsbActive, UsbGetChar fehlt es vermutlich > weiterhin an Sperren. Das musste ja kommen. Niklas, ich weiß deine Hilfe zu schätzen. Du hast hier von dem ganzen ganzen USB Kram von uns allen am meisten Ahnung. Aber bitte akzeptiere, dass man sich auch mal irren kann. Ich weis auch nicht, wie es dir erklären soll. Letztendlich müsste ich dazu erneut die Erklärungen von W.S. wiederholen, die er schon mehrfach wiederholt hat. Das führt zu nichts. > wenn man wirklich Rechenleistung einsparen wollte > sollte man nicht jedes Zeicheln einzeln verarbeiten sondern ganze > Pakete. Irgendwas ist immer. In diesem Fall ist der Code halt doof designt (wie du meinst). Immerhin funktioniert er für andere gut genug. Du musst ihn nicht mögen, du kannst dir ja was anderes programmieren. Es bringt nichts, immer wieder auf diesem Punkt herum zu reiten. Können wir jetzt bitte Ruhe geben und dem TO weiter helfen, falls er sich noch traut, sich wieder zu melden?
:
Bearbeitet durch User
An Alex, du hast wahrscheinlich gemerkt, dass hier wieder eine hitzige Diskussion um alte Wunden ausgebrochen ist. Das war vielleicht mal wieder nötig, hat jedoch mit deinem konkreten Problem sehr wahrscheinlich nichts zu tun. Schau du erst einmal, ob die Hardware und Taktversorgung OK ist. Du hast geschrieben, dass du den HSI48 Oszillator verwendest und das Clock Recovery System (CRS) aktiviert hast. Vielleicht ist da etwas schief gelaufen. Falls du von meinem Beispiel für STM32 L0 abgeguckt hast: Bei dem geht das vielleicht anders, als bei deinem F0.
Hallo Leute, ich sehe ihr habt euch ja prächtig amüsiert! Danke für deine Achtsamkeit Stefan, sie gereicht dir zu Ehre! Ohne alle Beiträge voll gelesen und verstanden zu haben, ich sehe das Thema USB ist relativ komplex und es gibt nicht 'die eine richtige' Lösung. Die Diskussion find ich aber sehr wichtig, so kommt immer mal wieder Bewegung in die Sachen. Ich bin ein Freund hitziger Debatten und daher nicht abgeschreckt und ich würde jedem von euch sofort ein Bier ausgeben! Ich hab gestern leider überhaupt keine Zeit gefunden um an meinem Problem weiter zu tüfteln. Einzig die Zeile mit dem BulkIn -> BulkOut hab ich eben noch ausprobiert und wie erwartet hat das aber keinen Effekt. Zu möglichen Taktproblemen / Hardware-Ursache: Einer meiner ersten Ansätze war es (nach der Suche nach Lötfehlern) den Takt zu prüfen. Der STM läuft auf dem RC-Oszillator, was ja für USB eigentlich ein KO-Kriterium ist. ST hat dem Chip aber dieses Clock-Recovery-System verpasst, das den Oszillator nachjustiert, so dass er mit den SOF-Paketen synchronisiert wird. Das funktioniert auch, jittert allerdings ein wenig rum. Wurde aber von ST genau für solche Anwendungsfälle gemacht und ich gehe davon aus, dass es die Anforderungen von USB erfüllt. Initialisiert hab ich das wie in den Manuals (oder einer Appnote, erinnere mich nicht genau, ist zu lange her) von ST beschrieben. Der Jitter kommt in erster Linie daher, dass die Anpassungsschritte wohl etwas grob sind, und nur alle Millisekunden nachretunt wird. Ich hab auf einem Pin mal einen Takt von ca. 600kHz erzeugt und mit dem Oszi 4 der Module verglichen. Eine Auffälligkeit des 'schlechen' Moduls hab ich so nicht gefunden, intensivere Tests mit Counter hab ich aber nicht gemacht. Grund war folgende Überlegung: Wenn es wirklich der Takt ist, müsste dann nicht die Übertragung in beide Richtungen Probleme bereiten? Immerhin läuft der Transfer von Daten zum PC auch *wochenlang !!* problemlos, und das braucht ja auch die ACK-Pakete in die andere Richtung. Das die Probleme völlig reproduzierbar immer nur beim BULK-OUT auftreten macht die CLK-These für mich unplausibel. Andererseits verhält sich ein Programm nun mal streng deterministisch, d.h. es MUSS eine Hardware-Ursache geben, sonst würde das Problem nicht bei einem bestimmten Modul auftreten. Es ist wohl irgendwas ziemlich knapp an der Grenze betrieben (höchstwahrscheinlich ist das ein Timing), die Software müsste damit zurechtkommen und tut es in bestimmten Fällen (BulkOut) nicht. Nachdem ich mich mit der USB-Programmierung einfach viel zu wenig auskenne, um sinnvolles Debugging betreiben zu können, ist eine genauere Untersuchung der CRS-Clock-Geschichte wohl der Hebel, an dem ich ansetzen sollte. Ich schau mir mal an, weche Möglichkeiten ich habe um rauszufinden, was die tatsächlichen Unterschiede zwischen den Modulen sind, und vor allem wie ich das 'ordentlich' quantifizieren kann. Updates folgen, ihr dürft derweil gerne weiterdiskutieren :-). mit Respekt, Alex
Niklas G. schrieb: > In UsbCharOut, UsbTxReady, UsbActive, UsbGetChar fehlt es vermutlich > weiterhin an Sperren. Habe keine Lust das wie beschrieben detailliert > aufzudröseln ob man die irgendwo einsparen kann; ist schließlich eine > Mikrooptimierung, wenn man wirklich Rechenleistung einsparen wollte > sollte man nicht jedes Zeicheln einzeln verarbeiten sondern ganze > Pakete. Keine Lust ist ein blödes Argument. Betrachten wir nur mal
1 | bool UsbTxReady(void) |
2 | {
|
3 | DisableUsbIRQ (); |
4 | bool res = configurationSet && !suspended && ((txw + 1) & (txLen - 1)) != txr; |
5 | EnableUsbIRQ (); |
6 | return res; |
7 | }
|
txLen ist eine Konstante, txw kann nur im main Programm verändert werden und txr kann im Interrupt nur so verändert werden, dass das Ergebnis des Vergleiches von false auf true springt, aber nicht von true auf false. Damit ist das komplett threadsave und benötigt keine Sperren. Die Funktion selbst verändert ja noch nicht mal irgendeine Variable. configurationSet und suspended können zu jeder Zeit im Interrupt verändert werden. Also auch direkt nach EnableUsbIRQ(). Damit ist die Sperre nutzlos. Sinn würde das z.B. nur so ergeben:
1 | ....
|
2 | DisableUsbIRQ (); |
3 | if (UsbTxReady()) |
4 | {
|
5 | UsbSendString(...); |
6 | }
|
7 | EnableUsbIRQ (); |
8 | ....
|
Man muss also eine Sequenz von Aufrufen Sperren, damit das überhaupt nur einen Anflug von Sinn ergibt. Auf der anderen Seite kann der User zu beliebigen Zeiten den VPC schließen oder das Kabel ziehen. Da kannst man die Flags configurationSet und suspended auswerten wie man will, das kann immer und zu jeder Zeit passieren. Auch das ist nicht weiter schlimm, wenn man in der UsbCharOut(char c) statt der Endlosschleife eine Timeout einbaut. Ein anderer könnte auf die Idee kommen, einfach den Watchdog zu benutzen um solche Fälle abzudecken. Das kommt immer auf die Anwendung an, und keiner hat das Recht zu schimpfen: "sowas ist verpöhnt". Jetzt mal zur UsbCharOut()
1 | /* sendet ein Zeichen (d.h. schreibt es in den Tx-Buffer) */
|
2 | bool UsbCharOut(char c) |
3 | {
|
4 | while (true) |
5 | {
|
6 | DisableUsbIRQ (); |
7 | |
8 | if (!configurationSet || suspended) |
9 | {
|
10 | EnableUsbIRQ (); |
11 | return false; |
12 | }
|
13 | |
14 | // diesen Teil zu Sperren ist genau wie UsbTxReady komplett überflüssig
|
15 | if (((txw + 1) & (txLen - 1)) != txr) |
16 | break; |
17 | |
18 | EnableUsbIRQ (); |
19 | |
20 | // das kann man so machen oder eine einen Timeout einbauen
|
21 | // Egal was passiert, im Fehlerfall läuft der Ringbuffer voll und das
|
22 | // ist eigentlich das einzige auf was ich als User reagieren muss.
|
23 | __asm__ volatile ("wfi"); /* trampeln auf der Stelle!! */ |
24 | }
|
25 | |
26 | // das ist wieder komplett theradsave,
|
27 | // da txw ausschließlich hier verändert wird.
|
28 | // die Abholroutine im Irq zieht txr nach, aber Daten gehen dabei
|
29 | // nie verloren. Das verändern von txr kann nur zusätzlichen Platz
|
30 | // im Buffer schaffen, ihn aber nicht nehmen
|
31 | int i = (txw + 1) & (txLen - 1); |
32 | UsbTxBuf[txw] = c; |
33 | txw = i; |
34 | |
35 | // das will ich nicht kommentieren, da soll jeder selbst
|
36 | // entscheiden wie er was will. Auf alle Fälle gilt hier das
|
37 | // gleiche wie oben, eine Interruptsperre ist nicht nötig
|
38 | |
39 | /* Diese Bedingung einkommentieren, um nur dann automatisch abzusenden, wenn Sendepuffer voll. In diesem Fall kann über UsbTxFlush abgeschickt werden. */
|
40 | // if (((txw + 1) & (txLen - 1)) == txr) {
|
41 | if (!transmitting) { |
42 | EpBulkBeginTransmit (); |
43 | }
|
44 | // }
|
45 | EnableUsbIRQ (); |
46 | return true; |
47 | }
|
Die anderen Funktionen sind alle ähnlich. Niklas G. schrieb: > wenn man wirklich Rechenleistung einsparen wollte > sollte man nicht jedes Zeicheln einzeln verarbeiten sondern ganze > Pakete Es geht ja nicht darum Rechenzeit einzusparen, sondern darum lieber auf Verdacht Interruptsperren einzubauen nur weil man zu faul ist darüeber nachzudenken.
temp schrieb: > Es geht ja nicht darum Rechenzeit einzusparen, sondern darum lieber auf > Verdacht Interruptsperren einzubauen nur weil man zu faul ist darüeber > nachzudenken. Naja, es ist legitim innerhalb eines begrenzten Kontext defensiv zu programmieren und einen Schutz "zur Sicherheit" einzubauen, statt viel Zeit darauf zu verwenden, zu überlegen, ob man ihn wirklich braucht. Vor allem wenn die Kosten minimal sind und man nicht für die Zeit bezahlt wird. Aber schön, dass erst die Beleidigungen kommen und dann die technische Erläuterung. Da ist man richtig motiviert darüber detailliert nachzudenken!
:
Bearbeitet durch User
Alex schrieb: > Wenn es wirklich der Takt ist, müsste dann nicht die Übertragung in > beide Richtungen Probleme bereiten? Immerhin läuft der Transfer von > Daten zum PC auch *wochenlang !!* problemlos, und das braucht ja auch > die ACK-Pakete in die andere Richtung. > Das die Probleme völlig reproduzierbar immer nur beim BULK-OUT auftreten > macht die CLK-These für mich unplausibel. Die Signale müssen ja auf der einen Seite vom PC und auf der anderen Seite vom µC gesampelt werden. Das macht die Hardware, aber es ist nicht die gleiche. Von daher können schon richtungsabhängige Unterschiede auftreten. Die Länge der Pakete dürfte dabei auch eine Rolle spielen. Auch wenn es dir nicht plausibel erscheint, was hast du jetzt noch für Möglichkeiten? Eine andere Softwareimplementierung oder eine andere Hardware. Kannst du nicht mal an das schlechteste Modul einen Quarz dran fummeln und mal testen? Oder einen externen Taktgenerator, da brauchst du nur einen Pin. Die Diskussionen hier werden dich mit deinem Problem nicht weiter bringen.
temp schrieb: > Der Hauptgrund war ja noch nicht mal das Wegoptimieren der Nops sondern, > dass sie im Interrupthandler überhaupt nötig waren. Erkenne doch bitte das Ziel der Übung: zuerst das Kommando vom Host mit der zugewiesenen Adresse mit ACK bestätigen und dann wenn der Host das ACK erhalten hat, auf die zugewiesene Adresse umschalten. Und das möglichst umgehend, weil zumindest mir nicht klar ist, ob man damit ewig warten darf. Und sofort die Adresse umzuschalten hatte ich nicht probiert, weil ich annahm, daß der Host das ACK noch von Adresse 0 erwartet. Ich habe dazu keine Festlegung in den mir zugänglichen Unterlagen gefunden. Und nein, ich habe keine kostenpflichtigen Dokumente eingekauft, sondern mußte mich auf das beschränken, was man im Inet eben so findet. > Trotzdem auch an dich die Frage: Wie siehts du die Notwendigkeit des 1ms > Interrupts von dem ich ein paar Beiträge vorher gesprochen habe? Dieser Interrupt kommt immer und muß behandelt werden, sonst denkt der Host, daß man gestorben ist - und dann klemmt er einen einfach ab. Und da man diesen Interrupt eben immer behandeln muß und man ja dabei bereits im Interupt-Modus ist, ist es auch keine schlechte Idee, mal nachzuschauen, ob der EpBulkIn grad NICHT am Transferieren ist und vor sich hin faulenzt, während die SIE alle Anfragen des Host nach Daten mit NAK abweist und im Ringpuffer des Treibers Daten vor sich hin schmoren. W.S.
Frank M. schrieb: > W.S. hat erklärt, warum es in seinem Code keine Probleme gibt. Niklas' > Code sieht an den genannten Stellen anders aus. Wenn mehrere Variablen > hintereinander atomar geändert werden müssen, ist eine Sperre > unausweichlich. Frank, das sihst du falsch. Sperren sind nur dort nötig, wo zwei unabhängige Instanzen DIESELBE Variable zu ändern versuchen. Das Lesen derselben Variablen ist hingegen für alle Instanzen erlaubt. Und wenn man sauber programmieren will, dann macht man das so, daß nur die Instanz eine Variable verändert, die in ihrem Besitz ist. Und wer welche Variable besitzt und das Recht auf Ändern hat, muß man als Programmierer sorgfältig planen UND DANN AUCH EINHALTEN. W.S.
W.S. schrieb: > Und sofort die Adresse umzuschalten hatte ich nicht > probiert, weil ich annahm, daß der Host das ACK noch von Adresse 0 > erwartet. Richtig. Daher schaltet man die Adresse einfach im Interrupt um, wenn das ACK gesendet wurde. Ein fixes Delay mit NOP ist sehr fehleranfällig, weil die Zeit nicht vorhersehbar ist. W.S. schrieb: > Dieser Interrupt kommt immer und muß behandelt werden, sonst denkt der > Host, daß man gestorben ist - und dann klemmt er einen einfach ab. Das stimmt nicht. Den SOF-Interrupt kann man abschalten, die Hardware behandelt das Paket automatisch. Der USB-Peripherie ist es egal, ob die Software den Interrupt abfragt oder nicht. Schließlich setzt die Software ja auch keine Register im SOF-Handler. Auch ohne Interrupt bleibt das Gerät "online". Ich verwende den Interrupt lediglich um resume zu erkennen. Man könnte den SOF-Interrupt abschalten wenn er das erste Mal auftrat, und reaktivieren wenn man im suspend gelandet ist. W.S. schrieb: > Das Lesen derselben Variablen ist hingegen für alle Instanzen erlaubt. "Erlaubt" ist beides. Das Lesen mehrerer Variablen kann aber inkonsistente Zustände zurückliefern, wenn zwischendurch die Variablen geändert werden. Es ist nur in diesem konkreten Fall eben so, dass die Interrupts die Variablen nicht zwischendurch in inkonsistente Zustände bringen (vermutlich). Man könnte sogar gemeinsam genutzte Variablen ohne Interrupt-Sperre beschreiben indem man STREX / LDREX benutzt, aber der Aufwand lohnt sich hier kaum.
:
Bearbeitet durch User
temp schrieb: > txLen ist eine Konstante, txw kann nur im main Programm verändert werden > und txr kann im Interrupt nur so verändert werden temp schrieb: > Die Diskussionen hier werden dich mit deinem Problem nicht weiter > bringen. Ja, das sehe ich genau so. Ich sehe aber auch Probleme der allgemeineren Art: 1. Zur Hardware: ich habe schon eine Menge an USB-Kabeln gesehen, die zwar sehr schön als ordentlich abgeschirmt und auch sonst hochqualitativ angepriesen wurden, aber bei genauerem Hinsehen sich als miese Billigware erwiesen haben: keine Abschirmung, sondern nur ne blanke Litze beigelegt anstelle Schirm. Insbesondere sind das solche dünnen Typen vom Typ A nach Mini-USB und Mikro-USB. Diese Kabel machen Probleme ohne Ende. Manchmal geht's, manchmal nicht. Häufig bleibt bei sowas das Enumerieren mittendrin stecken. 2. Software, speziell gültige Dokumentationen: schwer oder nur gegen Geld zu kriegen. Wer da als privater Programmierer nicht im Konsortium ist, muß zusehen, wo und was er an Unterlagen kriegt. Und da tun sich Lücken auf, weil man eben hie und da nichts hat, um nachlesen zu können, was da korrekt ist und was nicht. 3. Rechtliches: Als Privater kann man keine pid und vid kriegen, denn das kostet was. Ergo kann man nur mit geklauten ID's arbeiten. Ist ziemlich unschön, aber nicht zu ändern. Dem TO würde ich nach all der Diskussion hier empfehlen, sich über einen UART seines Chips einen zweiten Kanal zur Kontrolle mal einzurichten. Eben um zu kontrollieren, ob und was denn nun in seinem µC am Ende so herein kommt. Und das unter Ausprobieren verschiedener Kabel. W.S.
Niklas G. schrieb: > Man könnte den SOF-Interrupt abschalten wenn er das > erste Mal auftrat, und reaktivieren wenn man im suspend gelandet ist. Das ist eigentlich eine super Idee... Ich habe das mal testweise im Branch disable-sof-irq umgesetzt. Dadurch spart man sich eine Menge Interrupts und möglicherweise Energie. Das geht mit dem Ansatz, im SOF-Interrupt das Senden abzuhandeln, nicht. Möchte jemand den Branch mal ausprobieren? Um das eigentliche Problem hier anzugehen, würde ich mal einen Breakpoint in ClearBuffer und in den Interrupt an die Zeile mit 'trace("out\n");' setzen, Daten vom PC senden und in den Breakpoints die USB-Register ansehen.
:
Bearbeitet durch User
Ein kleiner Hinweis zum Tracen. In "meiner" usb.c befindet sich ziemlich weit oben ein Konfigurations-parameter mit dem man das Tracing aktiviert. Darunter habe ich als Kommentar den Output von einer erfolgreichen Enumerierung eingefügt. Leider hat der F0 keinen TraceSWO Ausgang, also muss man die trace() Funktion so umschreiben, dass sie auf eine andere Schnittstelle schreibt. UART wurde ja schon empfohlen. Ich hatte mir die Mühe damals nicht gemacht, weil der Code auf meinem STM32L0 direkt auf Anhieb lief. Aber beim STM32F303RE war das für mich hilfreich - und vor allem die Erklärungen der zahlreichen Helfer, insbesondere W.S. und Niklas.
:
Bearbeitet durch User
Niklas G. schrieb: >> Man könnte den SOF-Interrupt abschalten wenn er das >> erste Mal auftrat, und reaktivieren wenn man im suspend gelandet ist. > > Das ist eigentlich eine super Idee... der SOF kommt eigendlich immer am Ende eines ms Frame. Der Host wird das Device auch nie in den Suspend schicken, solange das nicht im Deskriptor explizit eingeschaltet ist. Die Spec schreibt allerdings vor, dass ein Device beim ausbleiben des SOF innerhalb von 5ms?? In den powerdown gehen muss. Das ist sehr oft nicht richtig implementiert. Allerdings kommen während der Enumerierung sehr oft Suspend Events genauso wie reset Events vor. Suspend ist kein Paket sondern ein spezieller Zustand auf D+ und D- der dann als Suspend interpretiert wird Da der SOF auch die PLL zum nachsteuern des USB Clocks beim TO steuert, bin ich mir nicht so sicher ob es eine gute Idee ist da rumzuspielen.
:
Bearbeitet durch User
Thomas Z. schrieb: > Die Spec schreibt allerdings vor, dass ein > Device beim ausbleiben des SOF innerhalb von 5ms?? In den powerdown > gehen muss. Das ist sehr oft nicht richtig implementiert. Das sollte durch den Suspend Interrupt abgehandelt werden. Habe es aber nicht ausführlich getestet. Thomas Z. schrieb: > Allerdings kommen während der Enumerierung sehr oft Suspend Events > genauso wie reset Events vor. Sollte nicht schlimm sein, weil während der Enumerierung ja auch noch keine Configuration gesetzt ist und der VCP daher durchgängig "tot" ist. Thomas Z. schrieb: > Da der SOF auch die PLL zum nachsteuern des USB Clocks beim TO steuert, > bin ich mir nicht so sicher ob es eine gute Idee ist da rumzuspielen. Es wird ja lediglich der Software-Interrupt abgeschaltet. Der USB-Kern sollte das SOF ganz normal verarbeiten. Die Hardware sollte überhaupt nichts davon mitbekommen ob man auf den Interrupt reagiert oder nicht.
>> Möchte jemand den Branch mal ausprobieren?
gerne doch.
Niklas G. schrieb: > Das sollte durch den Suspend Interrupt abgehandelt werden. Habe es aber > nicht ausführlich getestet. Das ist was anderes. Eigentlich muss ein Device fehlende SOFs detektieren und dann selbstständig in den Powerdown gehen. Das ist übrigens ganz unabhängig davon ob das Device schon konfiguriert ist. (selfsuspend). An genau dieser Stelle verhalten sich auch kommerzielle Geräte oft nicht Spec Konform. Sowas würde übrigens bei den Plug Fests zumindest früher überprüft.
Ich habe hier ein nucleo32 stm32f042 gefunden, mit dem habe ich mal experimentiert. Sowohl mit HSI als auch mit ext. Clock. Es geht beides. Beim HSI ergab ein 1ms Systick eine gemessene Frequenz von 9956 Hz. Nur so zur Info. Ich habe nicht versucht die Clockrestauration zu machen. Deshalb schließe ich die Clockvariante als Fehler mittlerweile aus. Allerdings bin ich bei den Versuchen über etwas gestolpert, was mit dem Problem von Axel zu tun haben könnte. Ich hatte den rx-Ringbuffer auf 64byte eingestellt, und prompt kam genau 1 beliebiges Commando vom hterm an, danach war Ende im Gelände beim Empfang. Die Ursache ist einfach:
1 | void OnEpBulkOut(void) /* EP2 = Bulk-EP OUT */ |
2 | {
|
3 | int i, n, hdroom, avail; |
4 | UMEM_FAKEWIDTH D; |
5 | char c; |
6 | UMEM_FAKEWIDTH* P; |
7 | |
8 | /* Bulk EP anwählen und Anzahl der Bytes ermittlen */
|
9 | avail = EpTable[2].RxCount & 0x3FF; |
10 | |
11 | i = rxw - rxr; |
12 | if (i < 0) |
13 | i += rxLen; |
14 | hdroom = rxLen - i; |
15 | if (hdroom <= avail) { |
16 | receiving = false; |
17 | return; |
18 | }
|
19 | |
20 | P = (UMEM_FAKEWIDTH*) EP2RxBBuffer; |
21 | n = 2; |
22 | i = avail; |
23 | D = *P++; /* 2 Byte laden */ |
24 | while (i > 0) |
25 | {
|
26 | c = D & 0xFF; /* LSB zuerst */ |
27 | UsbRxBuf[rxw] = c; |
28 | rxw = (rxw + 1) & (rxLen - 1); |
29 | D = D >> 8; |
30 | --n; |
31 | if (!n) |
32 | {
|
33 | D = *P++; |
34 | n = 2; |
35 | }
|
36 | --i; |
37 | }
|
38 | |
39 | if (hdroom - avail >= EpBulkMaxLen) |
40 | ClearBuffer(logEpBulkOut); /* wir haben's gelesen */ |
41 | else
|
42 | receiving = false; |
43 | }
|
Hier gibt es 2 Stellen, wo aus dem Interrupt gesprungen wird ohne das ClearBuffer(logEpBulkOut); zu rufen. Wenn das ausbleibt, steht die Kiste. Wiederholt wird der Interrupt nicht. Bei meinem 64byte Ringbuffer schlägt dieser Vergleich aber immer fehl. Irgendwo im UsbGetChar steht dieses ClearBuffer(logEpBulkOut); nochmal um den Knoten zu lösen. Das halte ich an dieser Stelle aber für nicht klug. Die UsbGetChar() sollte nur Daten aus dem Ringbuffer lesen, und nicht noch solche Hampeleien machen. Ich bin aber noch nicht am Ende mit Nachdenken. Ich glaube ich baue das für mich so um, dass die Daten lieber verworfen werden und ein Errorflag gesetzt wird. Dann kann in diesem Interruptteilstück immer! das ClearBuffer(logEpBulkOut); gerufen werden. Das passt dann auch wieder zum UART. Da gehen die Daten auch verloren, wenn sie keiner abholt und die Buffer überlaufen.
Hallo Axel versuch mal diese beiden Funktionen in deinem Code auszutauschen:
1 | volatile uint32_t rxOverRun; |
2 | void OnEpBulkOut(void) /* EP2 = Bulk-EP OUT */ |
3 | {
|
4 | EpCnt++; |
5 | int i, n, hdroom, avail; |
6 | UMEM_FAKEWIDTH D; |
7 | char c; |
8 | UMEM_FAKEWIDTH* P; |
9 | |
10 | /* Bulk EP anwählen und Anzahl der Bytes ermittlen */
|
11 | avail = EpTable[2].RxCount & 0x3FF; |
12 | |
13 | P = (UMEM_FAKEWIDTH*) EP2RxBBuffer; |
14 | n = 2; |
15 | i = avail; |
16 | D = *P++; /* 2 Byte laden */ |
17 | while (i > 0) |
18 | {
|
19 | c = D & 0xFF; /* LSB zuerst */ |
20 | if (((rxw + 1) & (rxLen - 1)) == rxr) |
21 | {
|
22 | rxOverRun++; |
23 | }
|
24 | else
|
25 | {
|
26 | UsbRxBuf[rxw] = c; |
27 | rxw = (rxw + 1) & (rxLen - 1); |
28 | }
|
29 | |
30 | D = D >> 8; |
31 | --n; |
32 | if (!n) |
33 | {
|
34 | D = *P++; |
35 | n = 2; |
36 | }
|
37 | --i; |
38 | }
|
39 | ClearBuffer(logEpBulkOut); /* wir haben's gelesen */ |
40 | }
|
und
1 | char UsbGetChar(void) |
2 | {
|
3 | char c=0; |
4 | if (rxr != rxw) |
5 | {
|
6 | c = UsbRxBuf[rxr]; |
7 | rxr = (rxr + 1) & (rxLen - 1); |
8 | }
|
9 | return c; |
10 | }
|
Sollten damit deine Probleme nicht mehr auftreten, dann weißt du wenigstens aus welcher Ecke sie kommen.
temp schrieb: > Bei meinem 64byte Ringbuffer > schlägt dieser Vergleich aber immer fehl. Hehe, W.S.' Ringpuffer-Implementation mit 2 Zeigern kann nur N-1 Elemente speichern, wenn N die Größe ist. Dann klappt das nie. Wenn die Paketgröße 64 ist, muss der Puffer also 65 groß sein, oder man fügt noch einen Füllstand-Merker hinzu. temp schrieb: > Das > halte ich an dieser Stelle aber für nicht klug. Ich schon. temp schrieb: > Die UsbGetChar() sollte > nur Daten aus dem Ringbuffer lesen, und nicht noch solche Hampeleien > machen. Ich finde es sinnvoll, den OUT-EP genau dann zu aktivieren, wenn man bereit ist Daten zu empfangen. Und das ist dann der Fall, wenn man mit UsbGetChar() den Puffer entsprechend geleert hat. W.S. Original-Implementation macht es so wie du vorschlägst. temp schrieb: > Das passt dann auch wieder > zum UART. Da gehen die Daten auch verloren, wenn sie keiner abholt und > die Buffer überlaufen. Finde ich nicht gut. Beim UART ist das auch nur so, wenn man keine Flusskontrolle nutzt. Der VCP ist praktisch UART mit Flusskontrolle. Wenn man die Daten nicht abholt, sollte man vom PC auch keine schicken können.
Niklas G. schrieb: > Wenn die > Paketgröße 64 ist, muss der Puffer also 65 groß sein, oder man fügt noch > einen Füllstand-Merker hinzu. Guter Punkt, das werde ich mal Quelltext entsprechend kommentieren.
Mal was anderes: Ich könnte schwören, dass ich auf dem PC einfach
> cat /dev/ttyACM0
benutzen konnte, um Text-Meldungen vom µC --> PC anzuzeigen. Neuerdings
geht das nicht mehr, weiß der Teufel warum (auch nicht als root). Aber
mit jedem beliebigen Terminalprogramm kann ich den Port wie gewohnt
öffnen und nutzen.
Hat da zufällig jemand einen Tipp für mich, was der Knackpunkt sein
könnte?
:
Bearbeitet durch User
Niklas G. schrieb: > Hehe, W.S.' Ringpuffer-Implementation mit 2 Zeigern kann nur N-1 > Elemente speichern, wenn N die Größe ist. Dann klappt das nie. Wenn die > Paketgröße 64 ist, muss der Puffer also 65 groß sein, oder man fügt noch > einen Füllstand-Merker hinzu. Das ist mir selbst klar und es war ein Fehler von mir. Ich will auch nicht mit 64Byte Buffern arbeiten. Mir ist nur aufgefallen, dass sich das System dann genauso verhielt wie Alex es beschreiben hat. Niklas G. schrieb: > Ich finde es sinnvoll, den OUT-EP genau dann zu aktivieren, wenn man > bereit ist Daten zu empfangen. Und das ist dann der Fall, wenn man mit > UsbGetChar() den Puffer entsprechend geleert hat. > Das ist Ansichtssache > W.S. Original-Implementation macht es so wie du vorschlägst. Aber eben nicht ganz. Es gibt halt da die Stellen wo er einfach aus dem Interrupt rausspringt, und als Notanker ist die Hampelei im UsbGetChar verbaut. Und irgendjemand hat da drin auch noch den Aussprung programmiert wenn suspend gesetzt ist oder !configurationSet. Warum um Gottes Willen soll man in dem Zustand keine Daten mehr aus dem Ringbuffer holen? Niklas G. schrieb: > Finde ich nicht gut. Beim UART ist das auch nur so, wenn man keine > Flusskontrolle nutzt. Der VCP ist praktisch UART mit Flusskontrolle. > Wenn man die Daten nicht abholt, sollte man vom PC auch keine schicken > können. UART mit Flusskontrolle ist mir das letzte mal untergekommen als es noch kein Internet gab und Daten per Modem übertragen wurden. Wenn eine 100% UART das Ziel ist hast du recht, aber heute sind 99,xx% der Anwendungen ohne Flußkontrolle. Und jede Wette auch Alex sein Projekt. Das ist ja eine Kette die sich von der Anwendung auf dem PC bis zum µC durchzieht. Wer von den Beteiligten benutzt denn regelmäßig Flußkontrolle?
Stefan ⛄ F. schrieb: >> Wenn die >> Paketgröße 64 ist, muss der Puffer also 65 groß sein, oder man fügt noch >> einen Füllstand-Merker hinzu. > > Guter Punkt, das werde ich mal Quelltext entsprechend kommentieren. Mach es lieber nicht (oder richtig), denn es wird dann noch schlimmer. Wenn du in den Code schaust, wird häufig eine Maske gebaut aus buflen-1. Da kommt bei 256 eben 0xff raus. Versuch das mal mit 65... Also immer schön bei den 2er Potenzen bleiben.
temp schrieb: > Wer von den Beteiligten benutzt denn regelmäßig Flußkontrolle? Wir hatten die Diskussion schon weiter oben. Diese Funktionen UsbGetChar/UsbPutChar darf sich jeder gerne so umschreiben, wie er sie braucht. Das ist ja durchaus überschaubar - auch für Leute wie mich, die von USB keine Ahnung haben.
temp schrieb: > Es gibt halt da die Stellen wo er einfach aus dem > Interrupt rausspringt, und als Notanker ist die Hampelei im UsbGetChar > verbaut. Das ist kein Notanker, das ist der ganz normale Ablauf. temp schrieb: > Und irgendjemand hat da drin auch noch den Aussprung > programmiert wenn suspend gesetzt ist oder !configurationSet. Ich war das. Siehe git-History. temp schrieb: > Warum um > Gottes Willen soll man in dem Zustand keine Daten mehr aus dem > Ringbuffer holen? Weil der PC das Gerät offenbar still legen will. Das Abholen der schon angekommenen Daten könnte man noch erlauben, ja. temp schrieb: > Wer von den Beteiligten benutzt denn regelmäßig > Flußkontrolle? Ich habe das vor 2 Jahren noch benutzt in einem Projekt, wo per UART Datenpakete von einem Sensor abgeschickt werden. Sehr praktisch zur Vermeidung von Overflows. Das Ganze wurde in dem alten Thread übrigens schon durchgekaut. Das sind übrigens alles Überlegungen, die man sich bei der direkten Verwendung von USB ohne VCP sparen kann. Aber wir wollten es ja "einfach" haben!
Braucht sonst noch jemand einen Eimer um sich auszukotzen? Komisch das der TO hier von allen am wenigstens Meckert, dabei ist er doch derjenige, der hier gerade wirklich ein akutes Problem hat.
Ich habe nochmal getestet. Sowohl in meiner aktuellen Fassung als auch der von Niklas sehe ich keine Fehlfunktionen, wenn man mehr Daten sendet oder empfängt, als in den Puffer passen. Meine Hauptschleife:
1 | while (1) |
2 | {
|
3 | // LED On
|
4 | WRITE_REG(GPIOC->BSRR,GPIO_BSRR_BR13); |
5 | delay_ms(100); |
6 | |
7 | UsbStrOut("Hello World!\n"); |
8 | |
9 | // Send echo of received characters back
|
10 | while (UsbRxAvail()) |
11 | {
|
12 | char c=UsbGetChar(); |
13 | UsbCharOut(c); |
14 | }
|
15 | |
16 | // LED Off
|
17 | WRITE_REG(GPIOC->BSRR,GPIO_BSRR_BS13); |
18 | delay_ms(1000); |
19 | }
|
Ich kann mit dem Terminalprogramm wenige Zeichen bis einige hundert Zeichen am Stück senden (der USB Treiber teilt das natürlich auf), das Echo kommt vollständig zurück. Und es blockiert nichts.
:
Bearbeitet durch User
temp schrieb: > Mach es lieber nicht (oder richtig), denn es wird dann noch schlimmer. > Wenn du in den Code schaust, wird häufig eine Maske gebaut aus buflen-1. > Da kommt bei 256 eben 0xff raus. Versuch das mal mit 65... > Also immer schön bei den 2er Potenzen bleiben. Verstehe, danke für den Hinweis.
Stefan ⛄ F. schrieb: > Ich habe nochmal getestet. Sowohl in meiner aktuellen Fassung als auch > der von Niklas sehe ich keine Fehlfunktionen, wenn man mehr Daten sendet > oder empfängt, als in den Puffer passen. Was du jetzt genau meinst weiss ich nicht, aber wenn du dich auf meinen Testvorschlag von oben beziehst hast du was falsch verstanden. Ich unterstelle keinen eine Fehlfunktion, bei mir geht es ja auch schon lange. Nur, als ich heute mit dem 64byte Buffer gespielt habe, was sowieso nie gehen konnte, ist mir ein ähnliches Verhalten wie bei Alex aufgefallen. Ich habe nur das erste Packet vom PC bekommen. Was Alex wie macht wissen wir alle nicht. Deshalb war mein Code-Vorschlag nur für ihn, mit dem Ziel ohne wenn und aber das ClearBuffer(logEpBulkOut); bei jedem Packet was reinkommt auszuführen. Wenn das funktioniert, braucht er nur noch diese beiden Funktionen bei sich zu analysieren. Wenn nicht hilft eine größere Glaskugel.
temp schrieb: > Ich unterstelle keinen eine Fehlfunktion, > bei mir geht es ja auch schon lange. Das wollte ich damit auch nicht sagen. Ich wollte dem TO nur eine Bestätigung geben, dass die USB Software prinzipiell in Ordnung ist. Wenn er einen Softwarefehler hat, dann vermutlich in seinem eigenen Code den er drumherum gebaut hat.
:
Bearbeitet durch User
Ich hatte ja versprochen einen Patch für DoGetStatus() einzustellen... Der Patch ist fertig allerdings hab ich beim Testen festgestellt, das der Request nicht antwortet. Ich habe festgestellt, dass GetFeature, SetFeature und GetStatus alle in einen Timeout reinlaufen. Da scheint noch ein prinzipielles Problem zu existieren. Der VCP Comport selbst funktioniert problemlos. Es ist also eher ein formales Problem. Geprüft hab ich dieses mal mit den Sourcen von Stefan. Mal schauen ob ich den Fehler finde.
Hallo Leute, kurzer Zischenbericht zu meinen Versuchen: Ich hab an das 'schlechte' Modul einen 24MHz TCXO drangebaut, mit PLL->48MHz. Das Problem mit dem Senden von Kommandos bleibt aber bestehen. Das hab ich definitiv nicht erwartet !! @Stefan: Die Annahme, dass der Fehler in meinem Code liegt, teile ich :-) Nächster Test: Neues Projekt, und mal nur den USB-Teil von euch + die Anpassungen an den Chip auf der 'schlechten' Hardware. Alex
Alex schrieb: > Das hab ich definitiv nicht erwartet !! Offenbar sind Taktprobleme damit ausgeschlossen. Alex schrieb: > Nächster Test: Neues Projekt, und mal nur den USB-Teil von euch + die > Anpassungen an den Chip auf der 'schlechten' Hardware. Klingt nach einem guten Plan
So die Ursache für die Timeouts habe ich gefunden.. Es ist das Stall(1); was natürlich falsch für Protokoll Stalls ist. Trotzdem muss da noch was anders sein.
Was ich mir noch wünschen würde, ist eine Möglichkeit festzustellen, ob der PC den Port geöffnet oder geschlossen hat. Wenn man danach bei Google sucht, kommt man zu der Erkenntnis, dass das irgendwie nicht zuverlässig geht. Hat hierzu jemand eine Idee?
temp schrieb: > Was ich mir noch wünschen würde, ist eine Möglichkeit festzustellen, ob > der PC den Port geöffnet oder geschlossen hat. Wenn man danach bei > Google sucht, kommt man zu der Erkenntnis, dass das irgendwie nicht > zuverlässig geht. Das haben wir doch alles schon durchgekaut: Beitrag "Re: USB CDC von Stefan Frings und WS" Das Gerät bekommt nichts davon mit, wenn ein Programm :COM1 o.ä. öffnet. So ist das halt beim VCP.
Niklas G. schrieb: > Das haben wir doch alles schon durchgekaut: Mag sein, ich habe auch nicht alle Forenbeiträge im Kopf. Allerdings habt ihr folgendes doch nicht durchgekaut: Man kann sehr gut das DTR Flag für diese Aufgabe benutzen. Ich habe das jetzt mal mit dem hterm probiert, mit canhacker und lt. Internet soll das steinalte Hyperterminal das auch beim Öffnen setzen und beim Schließen wieder resetten. Sicher viele andere Software auch und bei eigener kann man das selber einbauen. Beim hterm muss man einmalig DTR bei geöffnetem Port drücken. Danach setzt er das beim Öffnen und Schließen alleine. Die Stelle im Code von WS würde dann so aussehen:
1 | volatile bool bIsComPortOpen=false; |
2 | |
3 | /* Zustand von DTR und RTS vom Host zum Gerät merken */
|
4 | void VCOM_Read_DTR_RTS(void) |
5 | {
|
6 | if ((CMD.SetupPacket.wValue & 0x01)!=0) |
7 | bIsComPortOpen=true; |
8 | else
|
9 | bIsComPortOpen=false; |
10 | Dtr_Rts = CMD.SetupPacket.wValue >> 8; |
11 | ACK(); |
12 | }
|
Ich werde das jedenfalls so benutzen, dass ich die Buffer leer mache wenn der Port zu ist. Aber das kommt wie immer auch auf die Anwendung an. Ich nutze nur das hterm oft zum loggen, und da nervt es, wenn beim Öffnen und Schließen noch Daten aus den Buffern kommen.
temp schrieb: > Man kann sehr gut das DTR Flag für diese Aufgabe benutzen. Und ich dachte Flusskontrolle nutzt man nicht mehr... Das verlässt sich auch darauf dass das Programm das setzt. Ein eigenes Programm macht das eventuell nicht.
Niklas, ich denke du hast ganz schön einen an der Waffel. Unterlass doch einfach deine Beiträge, wenn sie so sind wie deine letzten beiden. Niklas G. schrieb: > Und ich dachte Flusskontrolle nutzt man nicht mehr... Das verlässt sich > auch darauf dass das Programm das setzt. Ein eigenes Programm macht das > eventuell nicht. Was soll das? Unter Flusskontrolle verstehe ich was anders. Und ja, mag sein dass man das so nennen kann wenn man es wörtlich nimmt. Und ich will in meinem User-Code auch nichts mit Flusskontrolle zu tun haben. Punkt. Und was du machst und für richtig hältst ist mir sowas von Wumpe... Aber was soll's, ich diskutiere mit dir nicht mehr. Die die es interessiert können und werden es benutzen wie sie wollen und die anderen lassen es bleiben. Niklas G. schrieb: > Ein eigenes Programm macht das > eventuell nicht. Ein eigenes Programm macht das was ich will. Mag sein dass das bei deinen anders ist.