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:
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
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
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
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?
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.
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.
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:
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.
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.
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.
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.
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.
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
case0x82:/* 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 */
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
boolUsbTxEmpty(void)
3
{
4
DisableUsbIRQ();
5
boolres=(txw==txr);
6
EnableUsbIRQ();
7
returnres;
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.
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.
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
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.
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.
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.
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.
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?
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
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
boolUsbCharOut(charc)
3
{
4
while(true)
5
{
6
DisableUsbIRQ();
7
8
if(!configurationSet||suspended)
9
{
10
EnableUsbIRQ();
11
returnfalse;
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
inti=(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
returntrue;
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!
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.
temp schrieb:> txLen ist eine Konstante, txw kann nur im main Programm verändert werden> und txr kann im Interrupt nur so verändert werdentemp 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.
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.
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.
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.
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
voidOnEpBulkOut(void)/* EP2 = Bulk-EP OUT */
2
{
3
inti,n,hdroom,avail;
4
UMEM_FAKEWIDTHD;
5
charc;
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.
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?
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
charc=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.
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.
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
volatileboolbIsComPortOpen=false;
2
3
/* Zustand von DTR und RTS vom Host zum Gerät merken */
4
voidVCOM_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.
Guten Morgen!
Update:
Das Hello World von S.F. Webseite läuft auf meiner 'schlechten' Hardware
mit dem externen TCXO,
die runtergeschickten Zeichen kommen als Echo zurück.
Damit ist bewiesen, dass der Fehler an meinem Code liegt.
Weiter Updates folgen...
Alex
Alex schrieb:> Das Hello World von S.F. Webseite läuft auf meiner 'schlechten' Hardware> mit dem externen TCXO,> die runtergeschickten Zeichen kommen als Echo zurück.
Dann ist es so wie ich vermutet habe, die Daten werden irgendwann und
irgendwie nicht mehr abgeholt und dann steht die Kiste. Ich hatte dir
hier mal einen Test vorgeschlagen:
Beitrag "Re: STM32 USB Übertragungsproblem mit Code von S.F."
hast du das mal probiert?
Hallo temp,
ich hatte deinen Beitrag übersehen (bin bei eurer Diskussion leicht
'ausgestiegen'), vielen Dank für die Erinnerung !!
Ich werds heute Abend ausprobieren und umgehend reporten!
DANKE!!
Hallo zusammen,
ich habs jetzt im wesentlichen zusammen:
1. der Configdescriptor ist ein byte zu lang (unkritisch)
2. OnGetStatus, OnGetSetFeature laufen wegen Sall(1) in einen Timeout
3. Stall() und UnStall() sind fehlerhaft
Überprüft habe ich das ganze mit Thesycons UsbIo Treiber, weil man damit
jeden Chapter 9 Request einzeln absetzen kann. Da ich nur die V2.5
besitze hab ich das ganze in einer VM unter W7 und XP getestetet, das
aber nur am Rande.
1
voidDoGetStatus(void)
2
{
3
...
4
case0x82:/* für einen Endpoint */
5
trace("forEndpoint\n");
6
switch(EP)
7
{
8
/* nur für bulk + int eps notwendig
9
case 0x80: //ctrl in
10
case 0x00: //ctrl out */
11
case0x02://bulk out stall condition
12
if(((USB_EP2R>>12)&0x03)==1)Buf[0]=1;
13
break;
14
case0x81://bulk in stall condition
15
if(((USB_EP1R>>4)&0x03)==1)Buf[0]=1;
16
break;
17
case0x83://interrupt in stall condition
18
if(((USB_EP3R>>4)&0x03)==1)Buf[0]=1;
19
break;
20
default:
21
StallLogEP(logEpCtrl);
22
return;
23
}
24
...
25
}
Stall bzw UnSTall ist kritischer, da ist STAT_RX bzw STAT_TX verdreht.
Das hat mich etwas Zeit gekostet wegen der Magic Numbers.
Der Einfachheit halber hab ich einfach die Abfrage von != auf ==
geändert,
was bei STall() funktioniert bei Unstall aber nicht. Da muss ich die XOR
Bedingungen noch anpassen. Generell bin ich mir beim XOR unsicher ob das
die Togglebedingungen immer richtig abbildet, da muss ich noch etwas
nachdenken.
Ich hab mir allerdings noch nicht überlegt ob das alles auch richtig
wäre wenn man zB beide BulkEps auf einen bidirektionalen Endpoint legen
würde.
Der gezeigte Code ist zwar korrekt und getestet. Das ist aber noch nicht
alles. Ich würde warten, bis ich usb.c hochlade. Dann ist auch alles
drin. Meine Änderungen beziehen sich ja nur auf USB Funktionen. Am VCP
hab ich nichts gemacht.
Wie gesagt noch gibt's ein Problem beim UnStall().
Thomas Z. schrieb:> Ich würde warten, bis ich usb.c hochlade.
Ok, dann warte ich ab und hoffe dass es dann korrekt funktioniert. Weil:
Fachlich kann ich euch da nicht folgen, dazu habe ich zu wenig (fast gar
keine) Ahnung von USB.
Ich hab die beiden Funktionen, die temp mir oben (24.02.2021 15:41)
vorgeschlagen hat mal reinkopiert, und es hat funktioniert !!!!
Nur um zu verifizieren, dass das die Ursache wirklich adressiert, hab
ich wieder auf die beiden vorigen Funktionen zurückgestellt, und es hat
auch funktioniert kopfkratz.
Ich glaub ich hab mittlerweile so heftig in dem Code rumgepfuscht, dass
ich nicht mehr sauber nachvollziehen kann, wann ich was ausprobiert
habe. Ein log hab ich nicht gemacht (schäm).
Ich schließ micht jetzt mal dem Stefan beim Warten an, und verwende die
Version die Thomas dann hochlädt. Sonst wird der Saustall in meinem Code
noch schlimmer..
Alex schrieb:> hab ich wieder auf die beiden vorigen Funktionen zurückgestellt,> und es hat auch funktioniert kopfkratz.
Hast du vielleicht ganz knapp zu wenig RAM (Stack versus Heap Überlauf)?
Das müsste sofort eindeutig werden, wenn du den Sende-Puffer (txLen) mal
deutlich größer oder kleiner machst. Zum Beispiel 16 Bytes versus 1024
Bytes.
(txLen muss nicht >64 sein, aber eine Zweierpotenz (4,8,16,32,...)
Alex schrieb:> Ich hab die beiden Funktionen, die temp mir oben (24.02.2021 15:41)> vorgeschlagen hat mal reinkopiert, und es hat funktioniert !!!!>> Nur um zu verifizieren, dass das die Ursache wirklich adressiert, hab> ich wieder auf die beiden vorigen Funktionen zurückgestellt, und es hat> auch funktioniert kopfkratz.
Das war jetzt auch mit dem exterenen Takt oder mit dem HSI48?
Ich hatte bei meinem STM32F042 auch mal am Takt gespiel mit einem LED
Tooglen in einem 1ms Systick-Handler:
HSE: 499.9 Hz
HSI: 497.6 Hz
HSI48: 505.0 Hz
HSI48 mit CRS: 499.9 Hz
nur um mal eine Hausnummer zu haben wieweit der Takt bei mir abweicht.
Zuerst hatte ich in geistiger Umnachtung versucht den HSI mit CRS gerade
zu rücken. Natürlich ohne Erfolg. Dafür aber wieder was gelernt.
Wenn dich der Teil interessiert:
Thomas Z. schrieb:> Wie gesagt noch gibt's ein Problem beim UnStall().
Gut, daß Du das testen kannst.
Inzwischen habe ich den Code auch auf einer Bluepill mit
Original-STM32F103C8T6 getestet (zuvor auf einem F103RCT6), und er läuft
ebenfalls gut. Wobei mir die Stalls bzw. Bugs vielleicht bei meinen
Anwendungen gar nicht auffallen.
Ich steuere über die Bluepill einen RFM69, der an andere RFM69 Daten und
auch ganze AVR-Programme überträgt. da flutschen also schon auch
'größere' Datenmengen von bis zu 16kB hin und her, abwechselnd mit
Mini-Paketchen von 1-7 bytes als Statusbericht. Kein Problem bisher.
Aus Begeisterung habe ich sogar der Bluepill einen Erkennungsstring
verpaßt. Das PC-Programm klappert alle Comports durch, ob sie einen
bekannten string senden, um die Bluepill auch auf verschiedenen PCs
anschließen zu können. Hat sofort funktioniert - mit den alten
usb-Dateien war das ein Gekrampfe mit Timing, Aufhängern etc..
Ging sowohl mit HSI48 als auch mit dem HSE.
Ganz ausschließen kann ich die Takt-Geschichte glaub ich nicht, weil die
CRS mittelt ja die Taktabweichung immer über 1ms, wenn die Schwankungen
innerhalb dieser ms aber sehr groß sind, könnte das durchaus ein Problem
für den USB sein.
Meine Chips sind im TSOP20-Package, die haben leider keinen MCO (main
clock out) als Alternate Function.
Sonst hätte ich mit einem Frequenzzähler mal bei kürzerer gatetime den
jitter verglichen...
in folgenden Funktionen hab ich was verändert:
Stall();
UnStall;
DoGetSetFeature();
DoGetStatus();
und bei SetInterface.
Ein paar Kleinigkeiten bei den Deskriptoren sind unwesendlich ich hab da
nur ein paar Dinge geändert um in Zukunft flexibler zu sein.
Bei SetInterface lösche ich die ToggleBits, wie von der Spec verlangt.
Da ich nicht genau weis wann usbserial.sys SetInterface absetzt kann
dies sich auch auf den VCP auswirken. Meine Tests waren zwar erfolgreich
aber der Teufel steckt ja bekanntlich im Detail.
Alle anderen Änderungen sollten sich nicht auf den VCP auswirken, Sie
betreffen lediglich die Kompatibilität mit der Spec. Die VCP
ClassRequests habe ich noch nicht geprüft, Ich gehe aber davon aus, dass
die ok sind.
Eine Änderung hätte ich noch für die Zukunft:
Im Moment melden sich ja alle Devices mit der gleichen Seriennumer.
Besser wäre es die Serien Nummer aus dem Chip zu lesen oder abschaltbar
zu machen.
Viel Spass beim Ausprobieren.
Thomas Z. schrieb:> Im Moment melden sich ja alle Devices mit der gleichen Seriennumer.> Besser wäre es die Serien Nummer aus dem Chip zu lesen oder abschaltbar> zu machen.
gute Idee
Ich habe gerade Dein usb.c mit der Bluepill verwendet (schreibe bewußt
nicht getestet, weil ich das gar nicht kann), und es geht nach wie vor,
also bleibt das neue usb.c drin ;-). Danke dafür.
2 Sachen noch:
1.)
Nop habe ich wieder durch
1
voidNop(volatileuint32_tcount)
2
{
3
while(count--);
4
}
ersetzt, damit es nicht wegoptimiert werden kann.
2.)
Woran ich beim letzten Mal geknobelt habe:
Oben in der usb.c steht
#define NAME_OF_USB_IRQ_HANDLER USB_LP_CAN_RX0_IRQHandler
und der Hinweis, daß der Name des Handlers mit startup_stm32.s matchen
muß.
Coocox- bzw. SPL-User rennen vielleicht auch in die Falle; es gibt keine
Fehlermeldung, aber der Handler USB_LP_CAN_RX0_IRQHandler wird nicht
angesprungen.
Es sollte in diesem Fall USB_LP_CAN1_RX0_IRQHandler heißen.
Jürgen S. schrieb:> Ich habe gerade Dein usb.c
na ja meine usb.c ist sehr übertrieben wenn schon ist das von W.S. mit
Änderungen von einigen Forenmitgliedern.
Ich hab das bewusst im Original Stil belassen, und auch deshalb deutsche
Kommentare verwendet.
Ich persönlich würde sowas genau wie W.S. machen auch wenn mein Code Sil
natürlich ein etwas anderer wäre. Bare Metal Rules.
Ich lege allerdings sehr viel Wert auf Kompatibilität zur Spec. Viele
meiner Devices laufen unabhängig vom OS seit XP, oder Linux ohne dass
eine Zeile verändert wurde.
Da wäre z.B. zu sagen, dass VCP eigendlich IAD Deskriptoren vorschreibt.
Das habe ich bewusst weggelassen, weil's bisher noch nicht notwendig
ist, und noch funktioniert. Ich habe allerdings VCPs mit IAD am laufen.
Die zwei Dinge die du anmerkst sind genauso in der Version von Stefan
drin. Mit den zig IDEs stehe ich etwas auf Kriegsfuss.
Danke Thomas, ich habe deine Änderungen auf meine Homepage übernommen
und noch ein bisschen aufgeräumt (Formatierung und Reihenfolge der
Abschnitte).
Auch Danke an Dich Jürgen. Ich habe das volatile hinzugefügt aber das
NOP stehen gelassen weil ich unsicher bin, ob das wirklich
bei jedem Compiler weg gelassen werden darf.
Ich habe die API zum Anwendungsprogramm hin erweitert.
Es gibt dort jetzt eine neue Funktionen mit der man testen kann, ob ein
bestimmtes Zeichen im Empfangspuffer liegt (zum Beispiel '\n').
Ich habe Funktionen zum Senden und Empfangen von Zeichenketten sowie
Datenpaketen mit fester Länge hinzugefügt.
Bei den Funktionen zum Senden/Empfangen kann man jetzt einen Timeout
angeben.
Wer daran Interesse hat, kann sich das von meiner Homepage downloaden:
http://stefanfrings.de/stm32/index.html
Thomas Z, ich habe noch eine kleine Bitte an dich. Der Code enthält
vermutlich noch einen alten Bug. Mit "alt" meine ich, das ich den Effekt
schon vor unseren Änderungen bemerkt hatte.
Wenn ich den Port /dev/ACM0 mit Cutecom öffne sehe ich jede Sekunde die
erwartete Meldung "Hello World!".
Wenn ich nun etwas zum Senden eintippe, dann sendet der µC diesen String
als Echo zurück. Manchmal beginnt das Echo aber mit einem Fragment von
"Hello World!", zum Beispiel:
Ich Sende: bla
Ich empfange: Helbla
Wenn ich main.c auf dem Mikrocontroller so ändere, dass das Echo doppelt
zurück gesendet wird, dann passiert dies:
Ich Sende: bla
Ich empfange: Helbla
Ich empfange: Helbla
Ich Sende: gaga
Ich empfange: Hellgaga
Ich empfange: Hellgaga
Da scheint ein Zusammenhang bezüglich der Anzahl der Zeichen zu
bestehen.
Der Effekt tritt nur bei ersten gesendeten String auf. Danach nicht
mehr. Und es passiert auch nicht immer.
Es wäre super, wenn du mal danach schauen könntest. Vielleicht hast du
eine Idee, woran das liegen könnte.
Thomas, ich habe noch eine Info dazu: Diese Problem aus meinem
vorherigen Beitrag tritt unter Windows 10 nicht auf.
Unter Linux aber häufig. Es ist Debian Testing mit Kernel 5.10.13-1.
Ich schau mir das mal an, wird allerdings etwas dauern. Vielleicht hab
ich dann auch schon die Sache mit der SN eingebaut.
Ich hab die Conig Params zusammengefasst beim Keil bekommt man dann ein
Einstellmenu. In anderen IDEs sind es weiterhin nur defines.
In Ubuntu 20.04 (Live Stick) mit Kernel 5.8.0-43-generic tritt der
Fehler auch auf.
Ich habe inzwischen eine Methode gefunden, den Fehler schneller
auszulösen. Und zwar Klicke ich links oben wiederholt auf den Open/Close
Button. Manchmal erscheinen dann falsche Zeichen. Der Fehler tritt sogar
im read-only Modus auf.
Was mir dabei sehr auffällt ist: Die falschen Zeichen sind immer
Buchstaben aus "Hello World!" oder LF oder CR. Niemals andere
Buchstaben.
Stefan, kannst du mal tracen was unter linux bei open/close für Requests
kommen? Ich erwarte da ein paar ClassRequests, die hab ich bisher noch
garnicht getestet.
Anbei ein Bild meines ConfigMenus und hier der source dafür:
Die core Einstellungen laufen schon die VCP Einträge machen noch nichts
1
/* USB als virtuellen COM Port betreiben */
2
#include"usb.h"
3
4
// <<< Use Configuration Wizard in Context Menu >>>
5
// <h> Configuration for STM bare metal VCP
6
// <h> CORE params
7
8
// <o> UMEMSHIFT <0=> off
9
// <1=> on
10
// <i> Off for devicec like STM32L0x2, STM32L0x3, STM32F0x2, STM32F303xD and xE
11
// <i> On for devicec like STM32F103, STM32F302, STM32F303xB and xC
12
#define UMEM_SHIFT 1
13
14
// <o> USB_IRQ_NUMBER <0-31>
15
// <i> Take the number from the reference manual for ypor controller
16
#define USB_IRQ_NUMBER 20
17
18
// <o> ENABLE_TRACING <0=> false
19
// <0=> true
20
// <i> Enable trace messages via SWO
21
#define ENABLE_TRACING 0
22
23
// </h>
24
// <h> VCP params
25
26
// <o> USB_VID <0-0xFFFF>
27
// <i> USB Vendor ID
28
#define USB_VID 0x0416
29
30
// <o> USB_PID <0-0xFFFF>
31
// <i> USB Prodct ID
32
#define USB_PID 0x5011
33
34
// <o> USB_SERIAL <0=> none>
35
// <1=> fixed>
36
// <2=> from chip
37
// <i> Select what kind of USB Serial Number is used
38
#define USB_SERIAL 0
39
40
// <o> IAD_SUPPORT <0=> false
41
// <1=> true
42
// <i> Enable extra IAD conform descriptors
43
// <i> Spec requires IAD for devices using which use 2 or more interfaces for one function
44
// <i> although neither WIN nor Linux requires that up to now
Allerdings kann ich da keinen 1:1 Zusammenhang zu den unerwarteten
Zeichen an COM Port finden. Beides tritt unabhängig voneinander auf.
In den allermeisten Fällen sehen die Trace Meldungen so aus, während der
Fehler auftritt:
1
validateBuf logEpNum=1 --> Hello World!
2
CTR in
3
logEpBulkIn
4
validateBuf logEpNum=1 --> Hello World!
5
CTR in
6
logEpBulkIn
7
validateBuf logEpNum=1 --> Hello World!
8
CTR in
9
logEpBulkIn
10
CTR out <-- qqq
11
logEpBulkOut
12
clrBuf logEpNum=2
13
validateBuf logEpNum=1 --> Hqqq (das H ist hier falsch)
Ich bin ein Stück weiter. Die folgende main() Funktion zeigt mir, dass
der Fehler in dem Moment des Öffnens vom COM-Port auftritt.
Dass ich den Fehler vorher erst nach Eingabe einer Zeichenkette (im
CuteCom) gesehen habe lag daran, dass das Programm vorher alle
hereinkommenden Zeichen bis zum ersten LF sammelte.
1
intmain(void)
2
{
3
// Initialize system timer
4
SysTick_Config(SystemCoreClock/1000);
5
6
init_io();
7
UsbSetup();
8
9
while(1)
10
{
11
// LED On
12
WRITE_REG(GPIOA->BSRR,GPIO_BSRR_BS5);
13
WRITE_REG(GPIOC->BSRR,GPIO_BSRR_BR13);
14
delay_ms(100);
15
16
UsbSendStr("Hello World!\n",10);
17
// Alternative: puts("Hello World!");
18
19
// Echo received characters
20
while(UsbRxAvail())
21
{
22
UsbSendStr("rx:",10);
23
charc;
24
UsbGetChar_noWait(&c);
25
UsbSendChar(c,10);
26
UsbSendChar('\n',10);
27
}
28
29
/*
30
// If a line has been received, then send an echo back
31
if (UsbRxBufferContains('\n'))
32
{
33
char buf[256];
34
UsbGetStr(buf,sizeof(buf),'\n',0);
35
UsbSendStr(buf,10);
36
}
37
*/
38
39
// LED Off
40
WRITE_REG(GPIOA->BSRR,GPIO_BSRR_BR5);
41
WRITE_REG(GPIOC->BSRR,GPIO_BSRR_BS13);
42
delay_ms(900);
43
}
44
45
}
Die Ausgabe zeigt ganz deutlich dass der µC seine "Hello World!"
Meldungen einwandfrei sendet. Aber kurz nach dem Öffnen des COM Portes
empfängt er fälschlicherweise ein unerwartetes Fragment, scheinbar aus
dem Sende-Puffer.
Jürgen S. schrieb:> Woran ich beim letzten Mal geknobelt habe:> Oben in der usb.c steht> #define NAME_OF_USB_IRQ_HANDLER USB_LP_CAN_RX0_IRQHandler> und der Hinweis, daß der Name des Handlers mit startup_stm32.s matchen> muß.> Coocox- bzw. SPL-User rennen vielleicht auch in die Falle; es gibt keine> Fehlermeldung, aber der Handler USB_LP_CAN_RX0_IRQHandler wird nicht> angesprungen.> Es sollte in diesem Fall USB_LP_CAN1_RX0_IRQHandler heißen.
Das hat Stefan eingeführt und ich bin mit so etwas nicht glücklich.
Ich war ganz früher auch mal auf diesem Trip, aber mittlerweile stehe
ich auf dem Standpunkt, daß ein usb.c eben nur genau für den Chip sein
soll, für den er geschrieben ist.
Und wenn der gleiche Quellcode zu 99.9% auch für einen anderen Chip
brauchbar sein sollte, dann ändert man die restlichen 0.1% und speichert
ihn unter anderem Namen oder woanders ab, so daß eben immer gilt:
1 Plattform = 1 Handler.
Soviel Platz haben wir mittlerweile auf unseren Festplatten. Wenn man
das so tut, dann braucht man auch keine Flags in der Quelle, keine
unnötigen externen Referenzen, keine #ifdef und so weiter.
Vielleicht sollte man sich mal auf irgend eine Förmlichkeit einigen,
z.B.:
usb_stf103.c
usb_lpc4088.c
usb_nuc120.c
und so ähnlich.
W.S.
Thomas Z. schrieb:> Ich lege allerdings sehr viel Wert auf Kompatibilität zur Spec.
Und hast du selbige da und darfst du sie auch mal posten?
Originalunterlagen würde ich mir gern mal ansehen.
W.S.
Stefan ⛄ F. schrieb:> Es gibt dort jetzt eine neue Funktionen mit der man testen kann, ob ein> bestimmtes Zeichen im Empfangspuffer liegt (zum Beispiel '\n').>> Ich habe Funktionen zum Senden und Empfangen von Zeichenketten sowie> Datenpaketen mit fester Länge hinzugefügt.
Also Stefan, da knurrt aber der Blindenhund ganz gewaltig!
Also: der USB-Treiber ist der Lowlevel-Treiber, was du hingegen machst,
ist etwas, das 1..2 Ebenen höher angesiedelt ist. Also soll das auch in
eine andere Quelle, die sich nur um das von dir Angesprochene kümmert
und die je nach Bedarf auch andere LL-Treiber benutzt, z.B. einen für
einen UART.
Ich häng dir mal so etwas ähnliches zur Ansicht dran, damit du siehst,
wie z.B. ich da etwas Ordnung in die Datenströme kriege. Die Datei ist
eine Vorlage, die an die konkrete Plattform angepaßt wird. Manche haben
keinen UART0, andere keine UART6..7..sonstwas. Obendrein gibt es bei den
meisten µC-Anwendungen bei mir auch kein Kommandofenster und kein
Filesystem und deshalb auch keine Textfiles, dennoch ist sowas wie
CharToFileStream(..) oder CharToCommandWindow(..) formal vorgesehen. Die
Anwendungseben spricht bei mir NIEMALS den usb.c oder serial.c oder so
an, sondern macht das grundsätzlich nur über gio.c.
Und weil es bei mir häufig vorkommt, daß einige Programmstücke nicht
wissen (sollen), was der aktuelle I/O-Kanal ist, gibt es in Analogie zum
PC sowas wie stdout und stdin als generalisierten Kanal.
Und jetzt kommst du und wirfst das Zusammensetzen von Kommandozeilen und
Datenpaketen in den LL-Treiber, wo sowas definitiv nicht hin gehört.
Und mal abgesehen davon, erzeugst du damit firmwareinterne Probleme: Was
ist, wenn eine gewünschte Pufferlänge größer ist als der FIFO des
LL-Treibers? Ich denke da grad an eine meiner Anwendungen zum
Programmieren von µC, wo ich einen Programmierpuffer habe, der 2..8K
oder so groß ist.
Nein, der Ringpuffer im LL-Treiber dient NUR zur Entkopplung und nicht
als Puffer für eine Kommandozeile oder eines sonstigen
anwendungsspezifischen Blockpuffers. Sowas muß separat gemacht werden
und es gehört auch logisch auf eine höhere Ebene.
W.S.
Zu dem Thema mit der Seriennummer kann ich vielleicht etwas beitragen.
Ich nutze dazu die Unique ID vom STM. Die StringSerial ist bei mir keine
Konstante, sondern eine Variable, die in der UsbSetup() gesetzt wird.
1
// Device ID vom chip zum Erzeugen einer USB-Seriennummer
Ich bin mir aber nicht sicher, ob die Seriennummer dann nicht zu lang
ist.
Im Gerätemanager werden nur 12 von den 24 hex digits angezeigt.
Ist sicher noch verbesserungswürdig, aber funktioniert bei mir.
@ Stefan und W.S.
Ich denke W.S. hat mir seiner Kritik einen wichtigen Punkt getroffen.
Ich hatte bei mir im Code auch eine Zusatzfunktion eingefügt, mit der im
Empfangsbuffer auf ein CR oder LF geprüft wurde.
Erst dann wurden die Daten weiterverarbeitet.
Ich glaube, dass das mit ein Grund war, warum der Code bei mir solche
Schwierigkeiten gemacht hat.
Ob man die Funktionen des Application Layers in der gleichen Datei hat,
oder nicht, ist Geschmackssache. Ich möchte darüber nicht streiten.
Jeder kann sich seine Variante des Codes so gestalten, wie er es haben
möchte.
Ich möchte viel lieber den Fehler lösen. Aus irgend eine Grund empfängt
der µC nur unter Linux beim Öffnen des COM Port falsche Bulk Out
Interrupts in denen sich Fragmente zuvor gesendeter Daten befinden.
Ich habe sicherheitshalber nochmal den Gegentest gemacht. Der Fehler
tritt wirklich auch mit dem Original-Code von W.S. aus dem Jahr 2015
auf. Ich habe ihn angehängt.
Wenn ich den COM Port öffne, empfängt der µC häufig (aber nicht immer)
unerwünschte Zeichen. Im Screenshot sind das die Zeilen mit dem Präfix
"rx:".
Im Hammer Terminal sieht es genau so aus. Wie gesagt passiert das aber
nur unter Linux.
W.S. schrieb:> Das hat Stefan eingeführt und ich bin mit so etwas nicht glücklich.> Ich war ganz früher auch mal auf diesem Trip, aber mittlerweile stehe> ich auf dem Standpunkt, daß ein usb.c eben nur genau für den Chip sein> soll, für den er geschrieben ist.
Ohoh, da bin ich ganz anders veranlagt. Ich habe im Gegensatz sogar
meine 407/429/103-Bibliotheken mit #ifdefs ausgestattet, um ja nicht für
jeden Chip wieder eine separate Datei zu haben. Hab tatsächlich nur noch
eine spi.h/c, usb.h/c, diskio.h/c etc. für diese 3 Prozessoren, und für
manche Projekte auch ein CPU-übergreifendes H-File für STM32 und AVR.
Ich komme einfach nicht mehr hinterher, in einem Projekt eine Datei zu
ändern und dann daran zu denken, diese Änderung auch für die anderen
Prozessoren/Projekte einzuflechten. Irgendwann hat man dann wieder
drölfzig Versionen, von denen die eine die x-Verbesserung hat, aber
dafür nicht die y-Verbesserung, und vice versa.
Das war also meinerseits keine Kritik, den IRQ-Handler oben wählbar
auszuführen, sondern eher Kritik an Coocox bzw. eher SPL oder gar CMSIS,
daß der IRQ-Handler nicht semantisch geprüft wird.
@Alex, vielleicht verwende ich Dein Seriennummerprogramm mit dem
nichtkonstanten SerialString. Ich hatte selbst auch dran gesetzt (die 12
Byte der CPU_UNIQUE_ADDRESS passen einfach zu gut in den Descriptorstr,
als daß man die Gelegenheit versäumen sollte :). Ich hatte dann nur den
*P pointer für diesen String mit einem nichtkonstanten ersetzt - aber
irgendwas lief nicht richtig. Egal.
Für die 103er User und die anderen STMs hier die entsprechenden
Startadressen der CPU_UNIQUE_ADDRESS bzw. bei Dir DEVICE_ID_ADDRESS
1
// Unique device ID register (96 bits), Base address:
W.S. schrieb:>> Ich lege allerdings sehr viel Wert auf Kompatibilität zur Spec.>> Und hast du selbige da und darfst du sie auch mal posten?> Originalunterlagen würde ich mir gern mal ansehen.
Na ja die Specs hab ich von der usb.org. Ich nehme mir allerdings die
Freiheit raus üblicherweise die v1.1 Spec ranzuziehen, wenn ich sowieso
nur ein FS Device habe.
Die zweite Informationsquelle sind dann noch die Testbeschreibungen die
mit UsbCv kommen.
Zum testen nehme ich usbio.sys in einer älteren Demo Version. Die
aktuelle Version (mit W10 Support) ist ja nicht mehr frei verfügbar und
zu stark eingeschränkt.
Es gibt natürlich auch ein paar Dokumente die nicht öffentlich sind,
aber wirklich relevantes steht da auch nicht drin. Das sind mehr so
Hands On Dokumente.
Ich kann aber bestätigen, das die Dokumente von der usb.org teilweise
unter aller Sau sind. Typisch halt für Gremien. So haben Sie
beispielsweise bei der Midi2.0 Spec. einfach vergessen, dass da IAD rein
muss.
Ich bin überrascht dass W.S. für seinen obigen Einwurf
W.S. schrieb:> Also Stefan, da knurrt aber der Blindenhund ganz gewaltig!
ein -1 bekommen hat. Ich finde seine Ansicht durchaus legitim, den USB
Treiber und den Applikations-Layer nicht zu vermischen. Auf der Arbeit
in meinen Java Projekten trenne ich die Layer schließlich auch streng.
Dennoch mache ich es hier absichtlich anders, weil ich dieses
Testprojekt einfach halten will. In einer ernsthaften Anwendung wird
man sich das sowieso passend umschreiben.
Ich habe ihm jetzt ein +1 gegeben, um das auszugleichen :-)
Wie dem auch sei, entweder ist in dem Code mindestens seit 2015 ein Bug
drin, oder in Linux.
@Alex
Das ist so ziemlich genau das was ich in usb.c einbauen werde.
Allerdings möchte sich es konfigurierbar machen. So in der Art off |
fixed | chipid.
UsbStrings können bis zu 63 Zeichen lang werden. Lediglich die MSC Spec
schreibt dass Sie genau 12 Zeichen lang sein müssen, wenn das Device
bootbar sein muss.
Irgendwie riecht das stark nach einem Linux Bug. Das Programm usbmon
erzeugt mir folgende Ausgabe:
1
ffff9ea3abec8000 3590089985 S Bo:1:025:2 -115 1 = 65 "e"
2
ffff9ea3abec8480 3590089988 S Bo:1:025:2 -115 1 = 6c "l"
3
ffff9ea3abec8600 3590089990 S Bo:1:025:2 -115 1 = 6c "l"
4
ffff9ea3abec8780 3590089991 S Bo:1:025:2 -115 1 = 6f "o"
5
ffff9ea3abec8a80 3590089993 S Bo:1:025:2 -115 1 = 20 " "
6
ffff9ea3abec86c0 3590089995 S Bo:1:025:2 -115 1 = 57 "W"
7
ffff9ea340ba0480 3590089996 S Bo:1:025:2 -115 1 = 6f "o"
8
ffff9ea340ba0c00 3590089998 S Bo:1:025:2 -115 1 = 72 "r"
9
ffff9ea340ba0180 3590090000 S Bo:1:025:2 -115 1 = 6c "l"
10
ffff9ea340ba03c0 3590090002 S Bo:1:025:2 -115 1 = 64 "d"
11
ffff9ea340ba0300 3590090003 S Bo:1:025:2 -115 1 = 21 "!"
12
ffff9ea340ba0900 3590090006 S Bo:1:025:2 -115 2 = 0d0a
Bo heisst nach meinem Verständnis, dass dies Daten sind, die der PC an
den µC gesendet hat. Dann kann der µC wohl gar nichts dafür.
Ich bin aber unsicher, ob ich das so korrekt interpretiere, denn ich
habe dieses Tool noch nie benutzt und von USB eigentlich keine Ahnung.
Kann jemand meine Schlussfolgerung bestätigen oder mich korrigieren?
Ich habe einen etwas längeren Abschnitt angehängt. In dieser Zeit habe
ich den COM Port dreimal geöffnet:
1) Beim ersten mal trat kein Fehler auf
2) beim zweiten mal habe ich nur ein falsches Zeichen 48 "H" gesehen
3) beim Dritten mal fast die gesamte "Hello World!" Meldung gefolgt von
einem 0d (CR) dessen Herkunft mir völlig suspekt ist. In meinen
Quelltexten habe ich nämlich konsequent nur 0a (LF) als Zeilenumbruch
verwendet.
Jürgen S. schrieb:> (die 12 Byte der CPU_UNIQUE_ADDRESS passen einfach zu gut in den> Descriptorstr, als daß man die Gelegenheit versäumen sollte :)
Du täuschst dich.. der String ist 12*4 + 2 Bytes lang.
24 Digits in Unicode + 2
Ich werde irre, ich habe die Erklärung gefunden:
https://stackoverflow.com/questions/14866899/linux-cdc-acm-device-unexpected-characters-sent-to-device
Also es ist wohl so, dass der Linux Kernel diese CDC Geräte als serielle
Konsole behandelt. Auf diesen Konsolen sendet er alle Zeichen als Echo
zurück, und zwar in dieser charakteristischen Sequenz von Einzel-Bytes.
So ein Scheiß!
Ich habe nicht herausgefunden, wie man dem Kernel sagen kann dass er von
meinem µC die Finger lassen soll. Aber immerhin kann man dieses echo su
deaktivieren:
1
sudo stty -F /dev/ttyACM0 -echo
Danach funktioniert auch wieder mein von früher gewohnter cat Befehl zum
Anzeigen der Ausgaben:
1
cat /dev/ttyACM0
Ich danke für eure Aufmerksamkeit :-)
Thomas, meine Bitte an dich (zu helfen), hat sich damit erledigt.
Stefan ⛄ F. schrieb:> So ein Scheiß. Jetzt müsste man das nur irgendwie deaktivieren können> ...
Willkommen in der Welt der OS unabhängigen USB Devices. Da gibt's sicher
einen workaround. Ich kann mir nicht vorstellen dass kommerzielle CDC
Devices auch das Problem haben. Das sollte man mit den Silabs Dingern
doch gegentesten können.
Die Kunst beim USB Firmware Design ist auch, dass es überall gleich
funktioniert. Wenn ein Mac im Spiel ist wird das noch viel lustiger.
Ich hab schon Usbfunktionen gebaut, die aufgrund der Requests beim
Enumerieren erkennen konnten ob das Device am Mac oder unter Win
arbeitet. Der Grund dafür war das beide OS eine unterschiedliche
Teilmenge der Spec gut unterstützten. Ich hatte also für jedes OS einen
eigenen Satz Deskriptoren und Firmware Abschnitte.
Thomas Z. schrieb:> Das sollte man mit den Silabs Dingern doch gegen testen können.
Ich habe ein ESP8266 Modul mit Silabs CP2102, aber die melden sich nicht
als /dev/ttyACM* Gerät, sondern als /dev/ttyUSB*
Thomas Z. schrieb:> Die Kunst beim USB Firmware Design ist auch, dass es überall gleich> funktioniert.
Du bist lustig, genau deswegen wollte ich CDC benutzen.
Stefan ⛄ F. schrieb:> Ich habe ein ESP8266 Modul mit Silabs CP2102, aber die melden sich nicht> als /dev/ttyACM* Gerät, sondern als /dev/ttyUSB*
Nun das könnte ja schon der Workaround sein.
Poste mal die Deskriptoren...
Win ist es vermutlich egal. Wie ich MS kenne, machen die aus allem einen
VCP solange da nichts grob falsch ist.
Thomas Z. schrieb:> Nun das könnte ja schon der Workaround sein.
Sag bloß ich kann theoretisch einfach die Deskriptoren ändern und dann
wird aus /dev/ttyACM0 ein /dev/ttyUSB0. Sonst ist keine Änderung an dem
komplexen Quelltext nötig?
Jürgen S. schrieb:> Ohoh, da bin ich ganz anders veranlagt. Ich habe im Gegensatz sogar> meine 407/429/103-Bibliotheken mit #ifdefs ausgestattet, um ja nicht für> jeden Chip wieder eine separate Datei zu haben...
Das finde ich schlimm. Schließlich hast du zwar nur eine Datei, dafür
aber für jede elende Plattform dort drin irgendwelche #ifdef's, die du
dann jedesmal neu einstellen mußt, entweder durch Editieren der Quelle
oder durch eine Abhängigkeit von einem Projektfile, einer weiteren .h
oder einem Makefile.
Wohlgemerkt: die Datei usb.h sollte für alle Plattformen gleich sein,
damit eben die höheren Schichten problemlos portierbar sind, aber die
usb.c sollte wirklich auf "ihre" Plattform passen und möglichst
keinerlei Anpassungen nötig haben. In dieser Hinsicht begrüße ich, daß
Thomas die Definition der HW-Register in die usb.c transferiert hat,
damit ist dann auch die Abhängigkeit von einer 'prozessorname'.h
beseitigt.
> Irgendwann hat man dann wieder> drölfzig Versionen, von denen die eine die x-Verbesserung hat, aber> dafür nicht die y-Verbesserung, und vice versa.
Das muß doch nur einmal (und hoffentlich richtig) für eine Plattform
gemacht sein und fertig ist die Laube, solange es diesen Chip zu kaufen
gibt.
> Das war also meinerseits keine Kritik, den IRQ-Handler oben wählbar> auszuführen, sondern eher Kritik an Coocox bzw. eher SPL oder gar CMSIS,> daß der IRQ-Handler nicht semantisch geprüft wird.
Du meinst wohl nicht den Handler, sondern dessen Namen. Der wiederum ist
eigentlich überhaupt nicht von einer IDE prüfbar, weil er ja nur genau
mit dem Namen im Startupcode übereinstimmen muß - und der sollte zwar
zum RefManual passen, muß das aber nicht. Prinzipiell könnte man den
USB-Interrupt auch 'Ottokar' nennen, wenn man sich in allen beteiligten
Quellen daran hält. Aber wie soll eine IDE das alles prüfen? Da sind wir
wieder bei den Versionen für mehrere Chips, wo es dann eben verschiedene
Namen gibt. Auch deshalb bin ich gar sehr dafür, so einen Treiber NUR
für eine einzige Plattform zu machen. Das eliminiert dann auch solche
Namensprobleme.
W.S.
W.S. schrieb:> Das muß doch nur einmal (und hoffentlich richtig) für eine Plattform> gemacht sein und fertig ist die Laube, solange es diesen Chip zu kaufen> gibt.
Alleine schon an den Beiträgen in diesem Forum siehst du, wie oft die
usb.c in den vergangenen 5 Jahren aufgrund akuter Fehlfunktionen
verändert wurde. dazu kommen Verbesserungen die von konkreten Projekten
getrieben werden, die man sich aber auch für künftige Projekte (auch auf
anderen µC) konservieren möchte.
Mit "einmal richtig machen" ist es hier (und in vielen anderen
Projekten) nicht getan.
Thomas Z. schrieb:> Win ist es vermutlich egal. Wie ich MS kenne, machen die aus allem einen> VCP solange da nichts grob falsch ist.
Das ist ja auch völlig korrekt aus meiner Sicht. Immerhin ist sowas ja
erstmal nichts anderes als ein bidirektionaler serieller Kanal. Also
sollte der Treiber im OS diesen zwar bereitstellen, damit Applikationen
darauf zugreifen können, aber nicht mehr.
Da ist es ja doch wohl eher die Aufgabe einer Shell oder einer anderen
Anwendung, gegebenenfalls anzunehmen, daß dieser Kanal zu einer
Fern-Konsole führt und dann ggf. die Zeichen als Echo zurückzusenden. So
einen Kurzschluß im OS-Kernel zu haben, verstehe ich wirklich nicht.
W.S.
W.S. schrieb:> In dieser Hinsicht begrüße ich, daß Thomas die Definition der> HW-Register in die usb.c transferiert hat, damit ist dann auch die> Abhängigkeit von einer 'prozessorname'.h beseitigt.
Du irrst dich, die Definitionen waren schon drin als ich das angeschaut
habe. Ich hab da nichts geändert, nur mal mit dem Datenblatt
gegengecheckt.
Was ich gemacht habe ist EPs mit 0x07 zu maskieren, da es nur 8 EPs
gibt. Ich bin mir aber nicht sicher ob ich alle Stellen erwischt habe.
Thomas Z. schrieb:> Du irrst dich
Macht nix, es ist eine Abhängigkeit weniger und das ist gut.
Andererseits bin ich ziemlich unzufrieden mit sowas wie UsbTxFlush und
der Aufblähung von UsbCharOut. Mein eigentliches Ziel war und ist es,
die beiden Ringpuffer als einzige Schnittstelle zwischen der allgemeinen
Firmware und dem zum USB gehörigen Treiberzeugs zu haben.
Und jetzt wird von dort, also quasi von außen aus massiv dem USB in die
Gedärme gegriffen. Muß das sein? Bei meinen anderen (älteren)
Implementationen (LPC, NUC) und auch meiner zugrundeliegenden
Treiberversion war das nicht nötig, bzw. war mir nichts negatives
aufgefallen. Da wird alles direkt im Interrupt erledigt, teilweise eben
durch den Timertick am USB. Für die übrige Firmware sollte es doch
ausreichend sein, festzustellen, ob es Bytes abzuholen gibt oder ob man
Bytes senden kann oder nicht. Warten muß man ja auch bei einem realen
COM-Port, bis die Zeichen im Puffer endlich nach draußen geschaufelt
sind.
Ich hatte das bei anderen Plattformen so gelöst, daß ich im Timertick
einfach den Int auf NAK-BulkIn eingeschaltet hatte. Damit gibt es dann
alsbaldig einen Int, sobald der Host die SIE vergeblich nach Daten
abfragt und dieser Int konnte dann feststellen, ob etwas im Ringpuffer
steht. Und wenn ja, wurde ein Paket fertiggemacht und aktiviert und
voila, der Datenfluß war wieder angelaufen - alles ohne Eingriffe von
außen.
Im EpBulkIn-Handler hatte ich da jeweils den Int auf NAK-BulkIn wieder
ausgeschaltet, damit der µC nicht alle paar µS einen Interupt abkriegt.
Bloß bei dem Core in dem STM32 habe ich eine derartige Möglichkeit zur
Kommunikation mit der SIE nicht gefunden. Vielleicht hast du ja eine
Idee, wie man das hier sauber wieder hinkriegt, dann wäre das ganze
Gehampel mit Int verbieten und von außen dem Treiber in die Gedärme zu
greifen überflüssig.
W.S.
W.S. schrieb:> Andererseits bin ich ziemlich unzufrieden mit sowas wie UsbTxFlush und> der Aufblähung von UsbCharOut. Mein eigentliches Ziel war und ist es,> die beiden Ringpuffer als einzige Schnittstelle zwischen der allgemeinen> Firmware und dem zum USB gehörigen Treiberzeugs zu haben.
Du meinst wohl die (inzwischen umbenannte) Funktion:
1
// Send a character to the host (via send buffer).
2
// Returns false if the buffer is full.
3
boolUsbSendChar_noWait(charc)
4
{
5
// check space
6
inti=(txw+1)&(USB_TXLEN-1);
7
if(i==txr)
8
{
9
returnfalse;
10
}
11
12
// write into the buffer
13
UsbTxBuf[txw]=c;
14
txw=i;
15
16
// Comment in the following condition to delay sending until the buffer is full
17
// Then you may call UsbTxFlush() to trigger sending
18
19
// if (((txw + 1) & (txLen - 1)) == txr)
20
{
21
// trigger sending
22
if(!transmitting)
23
{
24
DisableUsbIRQ();
25
EpBulkBeginTransmit();
26
EnableUsbIRQ();
27
}
28
}
29
returntrue;
30
}
Was ist denn da aufgebläht? Der Aufruf von EpBulkBeginTransmit() wurde
vor einem Jahr (Beitrag "USB CDC von Stefan Frings und WS")
hinzugefügt, weil die ganze Übertragung unter gewissen Bedingungen
stecken bleibt.
Ich denke, dass deine Idee "die beiden Ringpuffer als einzige
Schnittstelle" schlicht nicht funktioniert hatte.
Entweder braucht es oben den Aufruf von EpBulkBeginTransmit(), oder eben
als separate Funktion:
1
// Trigger sending the remaining characters from the
2
// send buffer (asynchronously, not blocking)
3
voidUsbTxFlush(void)
4
{
5
if(!transmitting)
6
{
7
DisableUsbIRQ();
8
EpBulkBeginTransmit();
9
EnableUsbIRQ();
10
}
11
}
Wie sonst willst du dafür sorgen, dass das Senden zuverlässig getriggert
wird?
> Ich hatte das bei anderen Plattformen so gelöst, daß ich im Timertick> einfach den Int auf NAK-BulkIn eingeschaltet hatte.
Im oben verlinkten Thread hat Niklas begründet, warum er es anders
gelöst hat. Ist das jetzt so schlimm? Auch andere Stream basierte
Schnittstellen haben mehr als nur die Puffer zwischen Anwendung und
Treiber. Daran ist nichts überraschendes.
Das eigentliche Problem ist doch vermutlich eher, dass diese Änderung
von Niklas stammt und ihr beiden euch zerstritten habt. Immerhin
funktioniert seine Anpassung, ist das nicht wichtiger?
an W.S:
Ich möchte nochmal klarstellen, dass ich und viele andere hier für
deinen Code sehr dankbar sind. Dank deiner Arbeit muss ich nicht diese
scheiß HAL verwenden.
Nur sei doch bitte nicht angepisst, dass Leute deinen Code weiter
entwickeln, so wie es ihnen beliebt. So ist das halt bei Open-Source.
Dadurch geht dein Werk weder kaputt noch wird es herab gewürdigt. Ganz
im Gegenteil, das ist ein Ausdruck der Wertschätzung. Meinst du ich
hätte freiwillig die vergangenen drei Tage durchgehend daran gearbeitet,
wenn er nicht entsprechend wertvoll wäre? Ganz sicher nicht.
Auch Niklas hat sich vor einem Jahr tagelang damit befasst, obwohl er es
gar nicht nötig gehabt hätte (er hat ja seine eigene Implementierung).
Auch Niklas hat dir damit großen Respekt gezollt.
Nicht zu vergessen Thomas, der sich offenbar auch mit deinem Werk
beschäftigt. Er würde das nicht tun, wenn dein Code schlecht wäre.
Wir sind halt nur nicht in allen Punkten der gleichen Meinung. Aber wir
sind dir dankbar. Lass uns bitte Freiheit darauf aufzubauen und es für
uns selbst nach unserem Geschmack zu ändern. Deine Veröffentlichung
(Beitrag "Re: STM32F4 USB CDC") betrifft das
nicht.
W.S. schrieb:> Vielleicht hast du ja eine> Idee, wie man das hier sauber wieder hinkriegt
Mir ist bekannt, dass es cores gibt die einen Interrupt auf NAK bieten,
z.B. FX2 Das ist vielleicht interessant zu Debugzwecken sonst fällt mir
dazu nichts ein.
NAK steht ja nunmal dafür Ich hab nichts, musst du später noch mal
probieren. Das weiß die FW aber auch so. Simulieren könntest du sowas
indem ein Flag generiert wird was epnum,dir und ctr auswertet im
InterruptStatusReg auswertet.
Angenommen machst sowas auf dem InEp:
ArmEp(EpNo, nBytes); dann weißt du wenn im FlagReg CTR für diesen EP
gesetzt ist dass der Code nun NAKs schickt bis ein neues ArmEp kommt.
W.S. schrieb:> Das finde ich schlimm. Schließlich hast du zwar nur eine Datei, dafür> aber für jede elende Plattform dort drin irgendwelche #ifdef's, die du> dann jedesmal neu einstellen mußt, entweder durch Editieren der Quelle> oder durch eine Abhängigkeit von einem Projektfile, einer weiteren .h> oder einem Makefile.
Es wird schon einen guten Grund haben, wenn Du das so handhabst, wie Du
es machst. Denn gute Treiber zeichnen sich dadurch aus, daß man sie nur
noch wenig anfassen muß, weil sie eben ausgereift sind. Da ist schon was
dran.
Zudem haben die #ifdef-Konstruktionen bei größeren Projekten neben der
schlechteren Lesbarkeit den Nachteil, daß die Objektfiles nur für die
ausgewählten Definitionen gelten, man also bei anderer Konstellation
alles neu compilieren muß. Bei kleinen Hobbybastler-Sachen fällt das
aber nicht ins Gewicht.
> Wohlgemerkt: die Datei usb.h sollte für alle Plattformen gleich sein,> damit eben die höheren Schichten problemlos portierbar sind, aber die> usb.c sollte wirklich auf "ihre" Plattform passen und möglichst> keinerlei Anpassungen nötig haben. In dieser Hinsicht begrüße ich, daß> Thomas die Definition der HW-Register in die usb.c transferiert hat,> damit ist dann auch die Abhängigkeit von einer 'prozessorname'.h> beseitigt.
Ich denke da nochmal drüber nach, wenn ich die Deine bzw. inzwischen
eure usb.h und usb.c für den STM32F4 einge-#ifdef-t habe :). Meine Tests
auf dem F4 stehen noch aus.
> Das muß doch nur einmal (und hoffentlich richtig) für eine Plattform> gemacht sein und fertig ist die Laube, solange es diesen Chip zu kaufen> gibt.
Das wäre der Idealfall. Man hat das knowhow, entsprechende
Testmöglichkeiten, konstruiert eine gute Software und legt sie als
'erledigt' ab. Bei den Nichtprofis wie mir gab es in den ersten Jahren
immer wieder etwas zu ändern, teils durch Wissenszuwachs, teils, weil
sich andere Ansätze als erfolgversprechender herausstellten, teils, weil
beim Ändern einer Datei eine andere auch geändert werden muß.
> Du meinst wohl nicht den Handler, sondern dessen Namen. Der wiederum ist> eigentlich überhaupt nicht von einer IDE prüfbar, weil er ja nur genau> mit dem Namen im Startupcode übereinstimmen muß - und der sollte zwar> zum RefManual passen, muß das aber nicht.
Das mit den IRQ-Handlernamen ist nicht einfach, wenn es ganz sicher sein
soll, ja. Die billigste, aber für oberflächliche Fehlervermeidung
ausreichende Variante wäre, nicht angesprungene Prozeduren als #warning
auszugeben. So würden falsch benamte IRQ-Handler auffliegen.
Als weitere Sicherheit und Minimalkonvention könnte man IRQ-Handlernamen
noch abverlangen, daß sie "isrvect_" oder sowas fest im Namen haben
müssen. So daß man gleich weiß, daß es ein IRQ-Handler sein sollte, der
da ungenutzt rumbaumelt.
Advanced wäre es, wenn der Compiler/Linker anhand der CPU-Register
erkennt, daß ein Interrupt gesetzt ist, und untersucht, ob der
Einsprungpunkt bzw. die Adresse, auf die der Interruptvektor zeigt, als
Prozedur definiert ist. Aber was ist wahrscheinlich zu utopisch/komplex.
Edit:
Stefan ⛄ F. schrieb:> Aber wir sind dir dankbar.
Definitiv. Nur zwei Dateien, plattformunabhängig, kompakt, und stabiler
als andere Versionen, die ich probiert habe. Das ist schon sehr gut.
Ich möchte darauf hinweisen, dass die usb.c auf meiner Homepage keine
#ifdef enthält.
Da gibt es nur 4 Plattform spezifische Zeilen. Die habe ich ganz bewusst
ganz nach oben platziert, damit man bei der spezifischen Anpassung
nichts falsch macht:
1
// For devices with 2x16 bits/word access schema
2
// (e.g. STM32L0x2, STM32L0x3, STM32F0x2, STM32F303xD and xE)
3
#define UMEM_SHIFT 0
4
#define UMEM_FAKEWIDTH uint16_t
5
6
// For devices with 1x16 bits/word access schema
7
// (e.g. STM32F103, STM32F302, STM32F303xB and xC)
8
// #define UMEM_SHIFT 1
9
// #define UMEM_FAKEWIDTH uint32_t
10
11
// The name of the IRQ handler must match startup_stm32.s
12
#define NAME_OF_USB_IRQ_HANDLER USB_IRQHandler
13
14
// Take the number from the reference manual of your µC.
15
#define USB_IRQ_NUMBER 31
Tatsächlich habe ich diese Datei 4 mal auf meiner Festplatte in
Plattform-Spezifischen Verzeichnissen. Für mich hat es sich als sehr
praktisch heraus gestellt, dass alles unterhalb dieser #defines in allen
4 Dateien identisch ist. So kann ich Korrekturen einfach herüber
kopieren.
Thomas Z. schrieb:> NAK steht ja nunmal dafür Ich hab nichts, musst du später noch mal> probieren. Das weiß die FW aber auch so.
Ähem... das ist so ein Problem: Wenn in Richtung µC-->PC mal eine Weile
keine Daten vorhanden sind und nach dem letzten ACK der SIE an den Host,
also wenn die letzten vorhandenen Daten gesendet worden sind, nix mehr
zu senden da ist, dann kann man auch kein weiteres Paket fertig machen
und muß den Interrupt unverrichteterdinge beenden.
Und genau DA passiert es nämlich, daß ab da die SIE dem Host nur noch
NAK sagt, es aber keinen Interrupt mehr gibt. Wer also soll dann später,
wenn wieder Daten im Ringpuffer aufgelaufen sind, sich darum kümmern?
Ohne Interrupt auch keine Routine im Treiber. Das ist das Problem.
Ich hatte das so geregelt, daß sich eben der Timertick so alle
Millisekunde mal drum kümmern soll, Niklas hingegen hat das nicht
verstehen/akzeptieren wollen und stattdessen sich sowas wir diese Flush
aus dem Grundprogramm heraus ausgedacht.
Natürlich ist sonnenklar, daß es irgendwen geben muß, der sich drum
kümmert. Das eleganteste ist da natürlich, immer mal den
NAK-BI-Interrupt einzuschalten, dann kümmert sich nämlich die SIE quasi
darum. Dann regelt das der Treiber von selbst und das Grundprogramm muß
sich nicht drum kümmern. Und so soll es eigentlich sein: Der Treiber
soll die höheren Schichten nicht mit seinen internen Befindlichkeiten
belästigen.
Alternativ kann man natürlich irgendwo im Treiber ein Flag haben, so wie
das jetzt von euch gehandhabt wird. Aber bei so einem Flag ist wieder
die Frage, wer es denn anschauen soll. Der UsbCharOut ist dafür kein
guter Kandidat, denn er kriegt ja nur einzelne Zeichen ab und würde
entweder bei jedem Zeichen den Transfer anstoßen müssen, also immerzu
nur Pakete der Länge 1 machen oder er würde sammeln müssen, aber bis
wohin? Der Treiber weiß ja niemals, ob das aktuelle Zeichen jetzt nun
definitiv das Letzte ist für eine Weile.
Ich hatte mir das vor Jahren schon einmal alles überlegt und kam zu dem
Ergebnis, daß es wohl das erträglichste ist, wenn der CharOut einfach
nur den Ringpuffer vollstopft und ansonsten wartet - und spätestens nach
einer Millisekunde der Timertick im Treiber sich um das Starten der
Übertragung kümmert.
Eine Millisekunde an Verzögerung ist für einen virtellen COM-Port m.E.
durchaus akzeptabel, immerhin geht dann ja ein Block von 255 Bytes auf
die Reise und wenn die Firmware noch mehr Bytes hat und den Ringpuffer
zwischendurch schnell genug wieder nachfüllt, dann geht es ab da ja
ratzfatz, bis mal wieder eine ausreichend große Lücke kommt und die SIE
anfängt NAK zu sagen und später aus diesem Zustand wieder herausgeholt
werden muß.
Und noch ein Wort an Stefan: Natürlich kann das ein jeder machen wie er
will. Ich habe dazu allerdings eine Meinung und mit der will ich nicht
hinterm Berg halten, schließlich hatte ich mir dazu Gedanken gemacht.
Ich möchte allerdings auch nicht ohne sachliche Argumente einfach nur so
angeblafft werden, wie Niklas das getan hat. Verstehe das mal.
W.S.
Jetzt dreh ich langsam durch...
Wenn ich Stefans Hello World (usb.c. vom 27.2. um 19:05) mit Echo bei
mir aus dem Debugger laufen lasse, dann klappt es.
Als Release compiliert und aus dem Flash kommt kein Echo.
Als Debug compiliert läuft es auch mit Echo, wenn ich es aus dem Flash
boote.
Anpassung lediglich an meinen Chip, mit TCXO.
USB läuft, Hello World kommt immer.
Unterschied in den Einstellungen: (jeweils zuerst die debug-cfg)
GCC Assembler:
-mcpu=cortex-m0 -g3 -c -x assembler-with-cpp --specs=nano.specs
-mfloat-abi=soft -mthumb
-mcpu=cortex-m0 -c -x assembler-with-cpp --specs=nano.specs
-mfloat-abi=soft -mthumb
GCC Compiler:
-mcpu=cortex-m0 -std=gnu11 -g3 -DUSE_HAL_DRIVER -DSTM32F042x6 -DDEBUG -c
-I../Core/Inc -I../Drivers/STM32F0xx_HAL_Driver/Inc
-I../Drivers/STM32F0xx_HAL_Driver/Inc/Legacy
-I../Drivers/CMSIS/Device/ST/STM32F0xx/Include
-I../Drivers/CMSIS/Include -O0 -ffunction-sections -fdata-sections -Wall
-fstack-usage --specs=nano.specs -mfloat-abi=soft -mthumb
-mcpu=cortex-m0 -std=gnu11 -DUSE_HAL_DRIVER -DSTM32F042x6 -c
-I../Core/Inc -I../Drivers/STM32F0xx_HAL_Driver/Inc
-I../Drivers/STM32F0xx_HAL_Driver/Inc/Legacy
-I../Drivers/CMSIS/Device/ST/STM32F0xx/Include
-I../Drivers/CMSIS/Include -Os -ffunction-sections -fdata-sections -Wall
-fstack-usage --specs=nano.specs -mfloat-abi=soft -mthumb
GCC Linker:
-mcpu=cortex-m0
-T"C:\Users\Alex\STM32CubeIDE\workspace_1.4.0\SFusbTest2\STM32F042F6PX_F
LASH.ld" --specs=nosys.specs -Wl,-Map="${ProjName}.map"
-Wl,--gc-sections -static --specs=nano.specs -mfloat-abi=soft -mthumb
-Wl,--start-group -lc -lm -Wl,--end-group
-mcpu=cortex-m0
-T"C:\Users\Alex\STM32CubeIDE\workspace_1.4.0\SFusbTest2\STM32F042F6PX_F
LASH.ld" --specs=nosys.specs -Wl,-Map="${ProjName}.map"
-Wl,--gc-sections -static --specs=nano.specs -mfloat-abi=soft -mthumb
-Wl,--start-group -lc -lm -Wl,--end-group
Wenn ich in der Debug-CFG die Optimierungen (-Os) einschalte und die
debuglevel (-g3) ausschalte hat das keinen Effekt.
Ich steh völlig auf der Leitung.
Was zum Kuckuck hab ich da noch übersehen?
Wenn ich das .bin vom release in den Flash schreibe und von dort starte,
dann kommt das Echo nicht.
Wenn ich mit dem Debugger das .elf vom selben compile-Vorgang laufen
lasse, dann hab ich das Echo.
Im Debugger gehen also beide Versionen, aus dem Flash nur die als Debug
compilierte-Version (das .elf).
Kapieren tu ich das noch nicht.
Dem Cube Programmer traue ich schon lange nicht mehr. Bei mit zickt er
immer wieder herum (nicht nur wegen der Java Version). Versuche mal zum
Vergleich das alte ST-Link Utility.
> Das SELBE ELF im Debugger gibt ein Echo.
Ich erinnere mich an ein Problem das ich mit der System Workbench hatte.
Da lief eins meiner Programme mit falscher Taktfrequenz bei diese beim
Flashen und die Taktfrequenz auf 64 MHz via HSI änderte und mein eigener
Code danach nicht mehr imstande war, die von mit gewünschte
Takteinstellung vorzunehmen. Da war die Lösung, dass ich zunächst die
PLL abschalten musste, bevor ich sie um-konfiguriere.
Das ist jetzt nicht direkt dein Problem, aber könnte in eine ähnliche
Richtung gehen. Da dein Programm im Debugger läuft, wäre jetzt mal
interessant, ob es danach auch noch läuft, wenn du den µC Stromlos
machst und neu startest.
Irgendwelche Takt-Zeug hab ich ausgeschlossen, da aber das Hello World
brav kommt.
> Da dein Programm im Debugger läuft, wäre jetzt mal interessant, ob es> danach auch noch läuft, wenn du den µC Stromlos machst und neu startest.
Schreibt der Debugger das Programm in den Flash?
Da hab ich noch garnicht drüber nachgedacht. Musste er ja eigentlich,
vor allem wenn ich das SELBE elf auch flashen kann, muss es so sein.
Hab das grad versucht, und es schein so, als ob es garnichts damit zu
tun hat, ob Debugger oder nicht. Mal gehts, mal nicht, aber eher nicht.
Bin grad immer am selben elf-file, und sehe grad kein Muster...
Die Tests gestern Abend hab ich 3mal wiederholt und es war konsistent.
Jetzt grad ist nichts konsistent.
Ich probiers später mal mit einer 'guten' Hardware, vielleicht hat der
Chip ja wirklich einen an der Waffel (immerhin tapse ich da jetzt schon
wochenlang dran rum, und die Luft ist zu der Jahreszeit eher etwas
trocken...)
Bringt ja nix, Geister zu jagen.
Alex schrieb:> Irgendwelche Takt-Zeug hab ich ausgeschlossen, da aber das Hello World> brav kommt.
Ach ja, stimmt.
> Schreibt der Debugger das Programm in den Flash?
Normalerweise schon. Jedenfalls bei den Mikrocontrollern, die ich
verwende (F1, F3, L0).
> Mal gehts, mal nicht, aber eher nicht, sehe grad kein Muster...
Das ist bitter. Suche weiter!
Könnte es an der Verkabelung liegen?
Alex schrieb:> Wenn ich das .bin vom release in den Flash schreibe und von dort starte,> dann kommt das Echo nicht.
Das kann doch nicht alles sein was du zur Fehlersuche beitragen kannst?
Klappt die USB-Enumeration überhaupt noch oder geht das auch schon nicht
mehr.
Wenn du komplett im Dunkeln tappst, kannst du ja mal nur bestimmte
Dateien mit -O(x) übersetzen und den Rest mit -O0.
Und wenn das nicht hilft, kannst du immer noch bestimmte Teile gezielt
mit #pragmas optimierenm oder auch nicht.
W.S. schrieb:> Ich hatte mir das vor Jahren schon einmal alles überlegt und kam zu dem> Ergebnis, daß es wohl das erträglichste ist, wenn der CharOut einfach> nur den Ringpuffer vollstopft und ansonsten wartet - und spätestens nach> einer Millisekunde der Timertick im Treiber sich um das Starten der> Übertragung kümmert.>> Eine Millisekunde an Verzögerung ist für einen virtellen COM-Port m.E.> durchaus akzeptabel, immerhin geht dann ja ein Block von 255 Bytes auf> die Reise und wenn die Firmware noch mehr Bytes hat und den Ringpuffer> zwischendurch schnell genug wieder nachfüllt, dann geht es ab da ja> ratzfatz, bis mal wieder eine ausreichend große Lücke kommt und die SIE> anfängt NAK zu sagen und später aus diesem Zustand wieder herausgeholt> werden muß.>
Deine Überlegungen verstehe ich durchaus.
> Und noch ein Wort an Stefan: Natürlich kann das ein jeder machen wie er> will. Ich habe dazu allerdings eine Meinung und mit der will ich nicht> hinterm Berg halten, schließlich hatte ich mir dazu Gedanken gemacht.> Ich möchte allerdings auch nicht ohne sachliche Argumente einfach nur so> angeblafft werden, wie Niklas das getan hat. Verstehe das mal.
Und diese ganz besonders.
Aber zum Thema, mir gefällt auch absolut nicht, daß das OnEpBulkIn();
aus dem Usercode gerufen wird. Auf der anderen Seite möchte ich aber die
1ms Interrupts nicht haben. Ich hab mir jetzt so geholfen.
1
// 1. SOF und ESOF Interrupt disabled.
2
voidInitEndpoints(void)
3
{
4
....
5
USB_CNTR=
6
CTRM|/* Int bei ACKed Paketen in oder out */
7
RESETM|/* Int bei Reset */
8
SUSPM|WKUPM
9
// | ESOFM | SOFM; /* Int bei 1 ms Frame */
10
;
11
}
12
13
// 2. TxFlush
14
// bei TxFlush wird nur der SOF-Interrupt enabled
15
voidUsbTxFlush(void)
16
{
17
// SOF Interrupt enablen
18
// for handling EpBulkBeginTransmit();
19
DisableUsbIRQ();
20
if(!transmitting)
21
USB_CNTR|=SOFM;
22
EnableUsbIRQ();
23
}
24
// oder
25
voidUsbTxFlush(void)
26
{
27
// SOF Interrupt enablen
28
// for handling EpBulkBeginTransmit();
29
USB_CNTR|=SOFM;
30
}
31
32
// 3. Im Interrupt:
33
// der SOF-Interrupt wird sofort wieder disabled, aber das Flag
34
// bleibt stehen. Damit sollte der Interrupt sofort anspringen
35
// wenn jemand UsbTxFlush() aufruft ohne max. 1ms warten zu müssen
36
// Ein SOF Interrupt wird dann nur noch optional gerufen wenn jemand
37
// UsbTxFlush() ruft.
38
// Selbst wenn man die Überprüfung auf "transmitting" in UsbTxFlush()
39
// weglässt, kommt der Code nicht durcheinander.
40
// Es gibt dann nur ein paar wenige SOF Interrupts mehr.
41
// In dem Fall sollte es nicht mal nötig sein das DisableUsbIrq()
42
// einzubauen.
43
voidNAME_OF_USB_IRQ_HANDLER(void)
44
{
45
....
46
if(I&SOF)/* Start of Frame, alle 1 ms */
47
{
48
if((USB_CNTR&SOFM)!=0)
49
{
50
USB_CNTR&=~SOFM;// Int disablen, Flag nicht löschen
51
if(!transmitting)
52
OnEpBulkIn();// immer mal nachschauen...
53
}
54
}
55
.....
56
}
Das UsbTxFlush() baue ich dann nach Belieben in UsbCharOut oder
UsbStrOut ein, darauf will ich nicht näher eingehen.
An der Verkabelung kanns nicht liegen, da das Ding wie ein USB Stick
direkt an den Host gesteckt wird.
Hab jetzt mal mit wireshark mitgeloggt, was passiert und die Daten etwas
zusammengestutzt (Maus-Bewegungen rausgeschnitten).
2mal folgende Prozedur:
- einstecken
- Hterm öffnen
- Hello World empfangen
- mehrfach etwas runterschicken
- Hterm schließen
- ausstecken
Beim ersten Mal kam ein Echo, beim zweiten Mal nicht.
Ich hab jetzt den Serial String implementiert analog wie die trace
Geschichte. InitSerial() mache ich in UsbSetup().
Zusätzlich habe ich IAD fest eingebaut und testweise überprüft ob die
Enumeration auch mit anderen EP0 sizes (8,16,32) klarkommt. Das ist ein
Test den ich eigentlich immer mache. Funktioniert perfekt. Das wäre z.B
bei einem LowSpeed Device wichtig, da dort nur EP0 Size=8 erlaubt ist.
Noch ein Hinweis:
Da ich plane meine usb11.h und usbcdc12.h in Zukunft für die Spec.
Konstanten zu verwenden wird usb.c sich immer weiter von der Version von
Stefan entfernen. Deshalb ist es vermutlich besser wenn ich meiner
Version einen neuen Name gebe.
Hier nun der Serial No code.
Thomas Z. schrieb:> Deshalb ist es vermutlich besser wenn ich meiner> Version einen neuen Name gebe.
Wirst du das irgendwo veröffentlichen? Wenn ja, dann schreibe mal, wo
man es finden kann.
Hallo,
gibt es diesen Code auch für den STM32F072?
Ich finde da nichts passendes, am besten SPL oder LL, HAL würde ich aber
auch nehmen.
Ich brauchen einen VCP für genau diesen Prozessor, weil die Hardware
vorgegeben ist.
Das ganze brauche ich nur zur Inbetriebnahme der Hardware.
Dazu soll das hier bekannte MCURSES darauf laufen und mir Informationen
anzeigen bzw auf Tastendruck Aktionen ausführen.
Halt das was man so braucht um eine Hardware langsam zu testen.
VG Dirk
Reden wir hier über deine STM32F103_usb_test.zip?
Oder ist das der falsche Code?
In diesem Code finde ich keine passenden Defines.
Es freut mich das es Code gibt, dann sollte ich ja wenn ich ihn habe,
recht schnell Ergebnisse haben. Wenn mir dann was auffällt helfe ich
gerne beim Fehler suchen, ist ja dann auch in meinem Interesse.
VG Dirk
Gut jetzt habe ich das besser verstanden.
Auch wenn ich jetzt noch nicht die richtigen Parameter kenne, sollten
die ja nicht so schwer herauszufinden sein.
Habe Beide mir geladen und werde versuchen die zum laufen zu bringen.
Vg Dirk
Dirk schrieb:> Auch wenn ich jetzt noch nicht die richtigen Parameter kenne, sollten> die ja nicht so schwer herauszufinden sein.
Nummer und Name des IRQ Handler kann ich dir auch nicht nennen. Ich habe
keinen F0. Du wirst sie finden.
Das ist nicht schwer zu finden, kann nur gerade nicht nachsehen.
Hast keinen M0, da verpasst Du was.
Die sind inzwischen bei mir auf gut 7 Platinen drauf und verdrängen
einen AVR nach dem anderen.
Nach dem Inbetriebnahme System kommt der erste M3 (103) zum Einsatz, dem
verpasse ich dann auch einen VCP mit MCURSES zum Testen, wenn alles gut
läuft.
Die 2 Projekte werde ich in den nächsten 4 Wochen ausgiebig testen und
wie gesagt wenn ich was finde helfe ich gerne.
VG Dirk
Dirk schrieb:> Hast keinen M0, da verpasst Du was.
Doch habe, einen STM32L073. Der macht einen deutlich aufgeräumteren
Eindruck als der F103.
> Nach dem Inbetriebnahme System kommt der erste M3 (103) zum Einsatz
Nimm lieber gleich den F303, das ist der direkte Nachfolger zum F103.
Der ist auch deutlich aufgeräumter und kostet nicht mehr. Außerdem hat
er eine FPU, und du riskierst nicht, eine schlechte Fälschung zu
bekommen.