Hallo zusammen,
ich habe den USB HS Core vom STM32F4VGT auf einem Discovery mit dem
externen PHY USB3320 zum laufen bekommen und möchte mein
Beispielprogramm zu Verfügung stellen. Den USB3320 gibt es leider nur im
QFN32, Alternativen habe ich allerdings keine gefunden. Mein
funktionierender, wirrer Aufbau um das Discovery Board zeigt aber, dass
es auch mit Adapterplatine geht.
Der USB3320 brauch einen externen Takt (oder eigenes Quarz) zur Speisung
seiner 60Mhz PLL. Ich verwende dazu den MCO2 Ausgang des STM32F4 in
Verbindung mit der internen PLL2, die 52Mhz ausgibt. Gegen Jitter
scheint der USB3320 relativ unempfindlich zu sein, siehe auch
Datenblatt.
Für die restliche Verdrahtung gibt es nur eine Möglichkeit, da die PINs
des ULPI Interfaces vom STM32F4 fest sind. Versorgt wird der USB3320 mit
den 3V des Discovery Boards (was so ziemlich die untere Grenze
darstellt), die 1,8V werden mittels Labornetzteil zur Verfügung
gestellt. RESETB liegt auf PC15.
Ich verwende den STM32F4 nur als CDC Device (VCP), deshalb liegt der
ID-Pin des USB3320 auf VDDIO.
Zur Software: Ich habe jetzt einfach mal das komplette CooCox CoIDE
Projekt angehängt. Für alle, die kein CoIDE verwenden, es müssen
folgende defines vorhanden sein:
STM32F407VG
HSE_VALUE=8000000
STM32F40_41xxx
USE_STDPERIPH_DRIVER
USE_USB_OTG_HS
USE_ULPI_PHY
Nachdem ein beliebiges Zeichen an den STM32F4 gesendet wird, werden
10000 mal 4096 Bytes an den Host gesendet. Vor jedem Senden werden die
4096 Bytes in den Sendepuffer des USB Stacks kopiert. Mit einem kleinen
LabVIEW Programm habe ich so die Geschwindigkeit gemessen und komme auf
ca. 3,4MByte pro Sekunde.
Wenn man sich das Umkopieren spart (bzw. in der tatsächlichen Anwendung
per DMA erledigt) und lediglich den Pointer in der Funktion VCP_DataTx
um 4096 erhöht
ergibt sich eine Übertragungsgeschwindigkeit von ca. 11,6MByte pro
Sekunde. Dabei muss allerdings in der main-Schleife zwischen den
Übertragungen mit delay_us(300) 300us gewartet werden, sodass der Puffer
nicht überläuft. Könnte man natürlich auch noch etwas eleganter lösen,
für einen ersten Test reichts aber.
Prinzipiell funktioniert das ganze auch mit dem LS Core und der
Schnittstelle, die das Discovery schon on board hat. Dazu muss
USE_USB_OTG_HS durch USE_USB_OTG_FS ersetzt werden und USE_ULPI_PHY aus
den defines entfernt werden. Außerdem ist auf das Timing beim schreiben
des Puffers zu achten.
Ich habe das Programm hauptsächlich aus Teilen der
STM32_USB-Host-Device_Lib_V2.1.0 und den enthaltenen Beispielen
erstellt, außerdem hat mir die Demo auf dieser Seite geholfen:
http://mikrocontroller.bplaced.net/wordpress/?page_id=1263
Wer Fragen oder Verbesserungsvorschläge hat, bitte hier posten.
Wirklich klasse, Gratulation!
Zur Adapter-Platine: Hast Du die per Hand bestückt? Kann man sowas
kleines löten?
Zur Schaltung: Hast Du vlt. einen kleinen Schaltplan für die Anbindung
des USB3320?
Gruß Peter
Hallo Peter,
Schaltplan habe ich mal erstellt. Abblockkondensatoren der ICs und
Versorgung des STM32F4 fehlen, aber das Prinzip sollte klar werden.
Zum Löten: Ich hab mir bei Ebay die Adapterplatine für das QFN32 geholt
und dann per Hand gelötet. Kniffelig war, dass der USB3320 das große Pad
unter dem Gehäuse verlötet haben muss (GND). Bei der Adapterplatine ist
dieses Pad zum Glück mit einigen Vias durchkontaktiert. Ich habe den
Lötstopplack auf der Unterseite der Platine entfernt, das Pad auf der
Oberseite verzinnt, das Zinn durch Hitzezufuhr mit dem Lötkolben von der
Unterseite zum schmelzen gebracht und das IC mit der Pinzette auf das
geschmolzene Zinn gelegt.
Die äußeren Pads zu verlöten war absolut unproblematisch, da Kontakte an
den Seitenrändern des ICs vorhanden sind.
Ich muss aber dazu sagen, dass ich eine JBC Lötstation und ein
Stereomikroskop verwende ;-)
Adapterplatine ist dieser hier:
http://www.ebay.de/itm/1x-QFN32-5mm-x-5mm-Adapter-P011-25-/281190068538?pt=Elektromechanische_Bauelemente&hash=item41783bf13a
Hallo Markus,
vielen Dank für die zusaätzliche Info. Die ganze Sache ist sehr
interessant.
Die Lösung mit der Adapterplatine ist wohl die günstigste aber es wird
den ein oder anderen verschrecken. Ich kauf mir auch lieber mal alles
doppelt.
Ansonsten Spitze! Evtl. kann man noch eine etwas aussagekrfäftigeres
Beispiel machen. Per DMA einen Port abfragen und an den USB übergeben
o.ä.. Ist nat. auch eine Frage des Aufwandes.
Und die 11,6 MB sind nat. sehr viel. Ein FX2 macht aber so um die 24.
Stellt sich die Frage, ob das überhaupt möglich ist.
Gruß Peter
Peter schrieb:> Ich kauf mir auch lieber mal alles doppelt.
IC und Adapterplatine sind ja zum Glück nicht teuer, da lässt sich das
verschmerzen.
Noch ein kleiner Nachtrag zum code. Die Funktion usb_gets(...) in
usbd_cdc_vcp.c muss geändert werden, da sonst empfangene Pakete verloren
gehen können:
1
uint32_tusb_gets(uint8_t*buf)
2
{
3
uint32_tlen,i;
4
5
if(usb_rx_len)
6
{
7
len=usb_rx_len;
8
usb_rx_len=0;
9
// copy buffer
10
for(i=0;i<len;i++)
11
buf[i]=APP_Tx_Buffer[i];
12
}
13
else
14
{
15
len=0;
16
}
17
18
returnlen;
19
}
Eleganter wäre es natürlich, noch einen Empfangs-Fifo zu implementieren,
sodass keine Pakete überschrieben werden können.
Ob sich noch etwas mehr Geschwindigkeit machen lässt muss ich mal
ausprobieren, momentan habe ich aber noch ein anderes Problem: Ich muss
ca. pro Millisekunde 4200 Byte versenden. Das klappt sehr gut, wenn ich
das Device direkt an einen USB-Port anschließe. Gehe ich aber über einen
(Exsys 7 Port High Speed) Hub, hängt sich der USB Stack auf.
USB_Tx_State bleibt auf 1, allerdings wird usbd_cdc_DataIn(...) nicht
mehr angesprungen, die Daten werden also nicht mehr abtransportiert.
Das ganze passiert nach einer zufälligen Anzahl von verseneten
4200-Byte-Paketen, allerdings scheinbar immer, nachdem die ersten 512
Byte eines solchen Paketes durch Handle_USBAsynchXfer(...) versendet
wurden. Es scheint, als würde dann der EP1-Transfer-Complete-Interrupt
ausbleiben. Dieser ist aber nötig, damit usbd_cdc_DataIn(...) weiterhin
angesprungen wird.
Den Füllstand von APP_Rx_Buffer prüfe ich manuell durch die Anfangs- und
Endpointer APP_Rx_ptr_out und APP_Rx_ptr_in und sorge dafür, dass er
nicht überläuft.
Im myST-Forum habe ich eine ähnliche Problembeschreibung gefunden,
allerdings keine Lösung dazu.
Peter schrieb:> So ein Hub braucht nat. auch seine Zeit, um den Datenstrom zu> koordinieren. Kontinuierliches Streaming wird da wohl nicht möglich> sein.
Dass die Datenrate irgendwie aufgeteilt werden muss ist klar, aber das
erklärt nicht warum das Paket scheinbar nicht ankommt (oder zumindest
der Interrupt nicht ausgelöst wird). Um solche Fälle wie verlorene
Pakete / zu hohe Buslast müsste sich doch eigentlich der Stack kümmern.
Ein Empfang von Daten ist auch weiterhin möglich, nachdem beim Senden
nichts mehr geht.
Markus R. schrieb:> Dass die Datenrate irgendwie aufgeteilt werden muss ist klar, aber das> erklärt nicht warum das Paket scheinbar nicht ankommt (oder zumindest> der Interrupt nicht ausgelöst wird). Um solche Fälle wie verlorene> Pakete / zu hohe Buslast müsste sich doch eigentlich der Stack kümmern.
Da hast Du auch wieder Recht.
Ich würde einfach versuchen ein paar Dinge auszuschließen:
- Einfache Senderoutine (irgendwas konstantes, unabhängig vom Empfang)
- oder nur Senden / gar keinen Empfang.
- oder Datenrate senken.
- oder Polling-Betrieb.
- oder das Ganze mal an einem Full-Speed Hub betreiben (falls man sowas
heutzutage noch kaufen kann).
- oder einen anderen High-Speed Hub verwenden.
Was passiert eigentlich, wenn usb_rx_len = -1 ist oder 9999999?
Markus R. schrieb:> static uint16_t VCP_DataTx (uint8_t* Buf, uint32_t Len)> {> uint32_t i=0;>> APP_Rx_ptr_in = (APP_Rx_ptr_in + 4096) % APP_RX_DATA_SIZE;>>> return USBD_OK;> }
Auch das macht vermutlich nicht was es soll.
usb_for_free schrieb:> kann mir jemand kurz erklären, warum ein extra IC verwendet wird, wenn> im STM32F4xx ein HS-USB Port vorhanden ist?
Das ist wie beim UART am ATmega8. Ohne MAX232 IC geht da auch nix.
Gerd schrieb:> Das ist wie beim UART am ATmega8.
Naja, so ähnlich. Der MAX232 ist ja nur ein Pegelwandler.
Das Interface zwischen STM32F4 und USB3320 ist ein paralleles mit 60Mhz
Takt. Der USB3320 kümmert sich dann u.A. um die Serialisierung der Daten
und das "Treiben" der D+ und D- Leitungen mit den passenden Pegeln.
ARMs mit HS USB PHY on board sind leider nicht ganz so verbreitet.
Peter schrieb:> Auch das macht vermutlich nicht was es soll.
Dem Stack wird signalisiert, dass neue Daten in APP_Rx_Buffer bereit
liegen, indem APP_Rx_ptr_in inkrementiert bzw. bei erreichen von
APP_RX_DATA_SIZE auf 0 zurückgesetzt wird. Da ich ausschließlich Blöcke
mit 4096 Byte senden will, schiebt der obige code den pointer immer um
4096 weiter und setzt ihn am Ende auf 0 zurück. APP_RX_DATA_SIZE ist
eine ganzzahliges vielfaches von 4096. Das ganze ist deshalb so, damit
ich per DMA immer 4096 Byte schreiben kann, ohne über die Grenzen von
APP_Rx_Buffer hinauszukommen.
Momentan bin ich etwas weiter, aber nicht klüger: Ich habe die Tipps von
Peter teilweise umgesetzt und APP_Rx_Buffer leer gelassen und lediglich
APP_Rx_ptr_in erhöht, wenn der "Füllstand" des Buffers es zulässt. Und
siehe da, schon erreiche ich auch am USB-Hub die volle Datenrate von nun
ca. 13MByte pro Sekunde.
Was ich aber nicht verstehe ist folgendes: Wenn ich an manchen Stellen
in APP_Rx_Buffer den Inhalt ändere (ich wollte z.B. am Anfang jedes
Paketes die Werte 255, 255, 0 haben), dann bricht die Übertragung nach
einer (scheibar zufälligen) Anzahl von Paketen ab. Bei Anschluss an
einem Port direkt funktioniert es unabhängig vom Inhalt des Buffers.
Mittlerweile denke ich, dass das ganze eher ein Hardwareproblem ist. Mit
dem on Board Fullspeed PHY funktioniert es - zumindest soweit ich das
bisher testen konnte.
Ich habe die Leitungen zwischen Discovery-Board und der PHY-Platine mal
um ca. die Hälfte verkürzt und die Übertragung ist erheblich sicherer
geworden. Vor allem die Änderung vieler Bits auf einmal (also z.B. die
Übertragung der Zeichenfolge 255, 0, 255, 0, 255, 0... ) ist aber nach
wie vor problematisch. Nachdem ich im Ausgangsposting immer den gleichen
Wert übertragen habe, ist mir das damals nicht aufgefallen.
Um ein vernünftiges Layout wird man wahrscheinlich trotzdem nicht
herumkommen, wenn beliebige Zeichenketten versendet werden sollen. Oder
hat jemand einen Vorschlag, wie man es auch mit Discovery-Board und
Adapterplatine halbwegs vernünftig zum laufen bekommen könnte?
Was ich noch nicht verstehe: Zeichenketten, die am USB Hub nicht
funktionieren, funktionieren bei direkter Verbindung schon. Dabei müsste
das doch eigentlich vom parallelen ULPI-Interface unabhängig sein?
- Die Drähte auf Deinem Bild sind recht dick. Hast Du Fädeldraht?
- Die Drähte kürzen
- einen Widerstand von ca. 22 Ohm in die Drähte löten
- mehrere Abblockkondensatoren dem PHY spendieren, deiner hat ja gar
nichts.
Große Geschwindigkeit = Störanfällig und da muss schon etwas für den
EMV-Schutz getan werden.
hi,
hast du die Treiber für VCP von ST mit installiert oder kann man das
ohne diese Treiber verwenden? Bin nämlich auf der Suche nach einer
Lösung den USB-HS von einem STM32f4 zu verwenden, ohne diese Treiber
extra installieren zu müssen.
Werde mir dein Projekt mal was näher ansehen. Danke.
Irgend einen Treiber wird man schon benötigen.
In der Regel meldet man sich mit VID/PID an und dann initialisiert man
seine Art wie z.B. HID Device oder MassStorage und anhand dem wird der
Treiber geladen.
Man kann somit auch z.B. LibUSB nutzen oder sonstige Tools.
Bin noch nicht so der Experte in USB-Sachen, da dies auch ein komplexes
Thema ist. Ist es beim STM32F4 nicht möglich das mit Windowstreibern zu
realisieren, also in der Art Plug and Play!?
WinUSB mit WCID müsste IMO gehen, ist aber erst ab Windows 8 ab Werk
eingebaut. Mit WCID sagt das USB Gerät dem Windows OS Bescheid, dass es
den WinUSB Treiber laden soll. Für STM32 gibt es AFAIK sogar fertigen
Code.
Für Windows 7 und älter braucht man die übliche Inf Datei.
@Karl: Ich hab den Treiber von ST installiert. Falls sich eine Lösung
ohne den ST-Treiber findet, wäre ich auch interessiert!
@Markus Müller: Danke für die Vorschläge. Ich hätte zwar noch Fädeldraht
und Wiederstände zum terminieren da, wollte jetzt aber nicht noch weiter
an dem Verhau rumpfuschen. Bzgl. der Abblockkondensatoren: Die sind
unter der QFN32-Adapterplatine montiert.
Ich hab jetzt mal ein 4-lagiges Layout mit STM32F4 und USB3320 (u.A.)
drauf gemacht, wenn die Platine geliefert und bestückt ist werde ich das
Ganze nochmal testen.
Hi Markus,
ich sitze gerade auch am USB3300
(allerdings ein fertiges Modul von E.B.A.Y)
konntest du die Daten, die du von der CPU aus sendest, am Empfänger
verifizieren ?
(auf Vollständigkeit und Fehlerfreiheit ?)
ich habe das Problem das ich im Moment kein PC-Terminal-Programm finde,
das eine Datenrate größer 1MBit/sec fehlerfrei mitmacht :-(
du schreibst du hast es mit "LabVIEW" getestet
kennst du noch ein anderes Programm (das kostenlos ist)
und diese Transferraten unterstützt ?
Gruss Uwe
Hallo Uwe,
aufgrund der oben beschriebenen Probleme möchte ich noch kein Urteil
über die Übertragungssicherheit fällen (auch wenn diese bei USB über
einen "tieferen" Layer des Übertragungsmodells eigentlich sichergestellt
sein sollte). Was ich aber bisher über meine Tests mit LabVIEW sagen
kann ist:
- Wenn eine bestimmte Anzahl von Bytes gesendet wird (und sich die
Kommunikation zwischen STM32F4 und PHY nicht durch die beschriebenen
Hardwareprobleme aufhängt), dann kommen sie auch an.
- Zumindest die Stichproben, die ich bisher "per Hand" überprüft habe,
waren immer fehlerfrei.
Genaueres wird sich zeigen, wenn meine Platine angekommen, bestückt und
hoffentlich auch lauffähig ist. Das wird aber noch etwas dauern.
Zum Thema Terminalprogramm: Das von dir beschriebene Problem hatte ich
auch (mit Hterm), deshalb bin ich dann auf LabVIEW zur Messung der
Geschwindigkeit umgestiegen. Mit Terminalprogramm könnte es gehen, wenn
du nur kleine Datensätze sendest (also nicht gleich mehrere 10MB).
Allerdings müsste man dann die Zeit auch genauer stoppen können.
Eine LabVIEW Studentenversion ist übrigens im Buch "Einführung in
LabVIEW" dabei, das häufig in FH/Uni-Bibliotheken zu finden ist.
Markus R. schrieb:> und möchte mein> Beispielprogramm zu Verfügung stellen.
Das ist sehr nett und löblich, aber trotzdem gefällt mir dein Beispiel
nicht. Zuerst geht es nach meinem Gefühl viel zu kunterbunt
durcheinander. Dein USB-Code ist mir viel zu wenig gekapselt und - was
ganz wichtig ist - deine Herangehensweise zur Geschwindigkeitsmessung
ist praxisfern. Mag ja sein, daß du immer nur 4K große Blöcke
transportieren willst, aber das ist für ne serielle Schnittstelle (auch
für eine virtuelle) sehr untypisch.
Ich hab das anders gemacht, allerdings für den STM32F103ZET6. Von NXP
bin ich es gewöhnt, daß es in jedem µC nen anderen USB-Core hat, also
geb ich keine Garantie, daß mein Code auch auf nem STM32F4VGT läuft.
Aber die Herangehensweise ist m.E. das wichtigste. Ich hab einen Sende-
und einen Empfangspuffer vorgesehen, die vom aufrufenden Programm
byteweise mittels vorgehaltener Funktionen gelesen bzw. gefüllt werden,
ohne daß dieses sich in irgendeiner Weise um die USB-spezifischen Dinge
kümmern muß. Das erledigt komplett der Interrupt. Auch das Inteface,
also das, was man in .h findet, ist überall gleich. Mir war das wichtig,
daß für das aufrufende Programm die Zugriffe auf die Puffer NICHT
direkt, sondern NUR über die vorhandenen Funktionen möglich sind, denn
dadurch erspart man sich ne Menge Debugging.
Ich hab solche äußerlich vereinheitlichten Treiber für den STM32F103,
Nuvoton NUC120, LPC2478, LPC1343, LPC17xx geschrieben und die .h Datei
enthält zum Herausschneiden die .inf für den ganzen Kram - egal, welcher
µC dahinter steckt.
W.S.
Markus R. schrieb:> Eine LabVIEW Studentenversion ist übrigens im Buch "Einführung in> LabVIEW" dabei, das häufig in FH/Uni-Bibliotheken zu finden ist.
hmm...ok, mal sehen, Danke
Gruss
Hallo W.S.,
danke für die Kritik. Hier noch ein paar Anmerkungen:
> Zuerst geht es nach meinem Gefühl viel zu kunterbunt> durcheinander. Dein USB-Code ist mir viel zu wenig gekapselt
Sehe ich ähnlich. Wie geschrieben, habe ich die Inhalte der
STM32_USB-Host-Device_Lib_V2.1.0 von ST quasi 1:1 übernommen und das
CDC-Beispiel auf den STM32F407V und meine Bedürfnisse umgebaut. Durch
die Modularität (die anfänglich tatsächlich sehr unübersichtlich
erscheint) sollten sich auch andere Devices (Audio, Massenspeicher...)
relativ einfach unter Vorbild der ST-Beispiele implementieren lassen.
> deine Herangehensweise zur Geschwindigkeitsmessung> ist praxisfern. Mag ja sein, daß du immer nur 4K große Blöcke> transportieren willst, aber das ist für ne serielle Schnittstelle (auch> für eine virtuelle) sehr untypisch.
Naja, die Geschwindigkeitsmessung (die übrigens im LabVIEW Programm und
nicht in der uC-Software steckt...) bringt mir die Ergebnisse, die mich
interessieren: Wie viele solcher Pakete kann ich pro Zeiteinheit maximal
übertragen. Man muss halt immer im Hinterkopf behalten, dass bei High
Speed 512 Byte auf einmal übertragen werden. Wenn nur 1 Byte des 512
Byte Pakets belegt ist, dann ist klar, dass die effektive
Geschwindigkeit in den Keller geht.
Dass CDC für große Datenmengen nicht ganz so geeignet ist, ist richtig.
Dafür ist es aber schön einfach.
> geb ich keine Garantie, daß mein Code auch auf nem STM32F4VGT läuft.
Ohne es ausprobiert zu haben: Wird es wahrscheinlich nicht, zumindest
nicht mit dem ULPI Interface. Inwiefern sich der FS-Core vom F4 und F1
unterscheiden weiß ich aber auch nicht.
> Aber die Herangehensweise ist m.E. das wichtigste. Ich hab einen Sende-> und einen Empfangspuffer vorgesehen, die vom aufrufenden Programm> byteweise mittels vorgehaltener Funktionen gelesen bzw. gefüllt werden,> ohne daß dieses sich in irgendeiner Weise um die USB-spezifischen Dinge> kümmern muß. Das erledigt komplett der Interrupt.> ...> Mir war das wichtig,> daß für das aufrufende Programm die Zugriffe auf die Puffer NICHT> direkt, sondern NUR über die vorhandenen Funktionen möglich sind, denn> dadurch erspart man sich ne Menge Debugging.
Das ist im code vom Ausgangsposting prinzipiell genau so. Zugriff auf
die Puffer erhält man mit usb_puts() und usb_gets() in der
usb_cdc_vcp.c. Um alles andere kümmern sich die Interrupts.
> Ich hab solche äußerlich vereinheitlichten Treiber für den STM32F103,> Nuvoton NUC120, LPC2478, LPC1343, LPC17xx geschrieben und die .h Datei> enthält zum Herausschneiden die .inf für den ganzen Kram - egal, welcher> µC dahinter steckt.
So ein Herstellerübergreifender Stack ist natürlich auch eine schöne
Sache ;-)
Hallo zusammen,
nachdem jetzt alles aufgebaut ist (USB3320 und STM32F4 auf einer
4-lagigen Platine), hier mal ein kurzes Update.
Die Übertragung ist deutlich sicherer geworden, ich übertrage damit die
Daten, die ich aus einem angeschlossenen ADC per DMA auslese. Beim
Anschluss an einen Port des PCs hatte ich bis jetzt noch keine
Aussetzer, beim Anschluss an den USB Hub hingegen schon. Ich kann das
Problem mit dem USB Hub allerdings nicht eingrenzen, mal funktioniert
es, mal bricht er nach ein paar Paketen ab. Die Kabellängen vom und zum
Hub scheinen keine Einfluss zu haben.
Die Datenrate habe ich jetzt nicht nochmal gemessen, meine Anwendung
braucht maximal 3,9 MByte/s und die werden locker erreicht.
Hallo,
ich bin neu hier und wollte mal nachfragen, ob schon jemand versucht hat
WINUSB zu implementieren bzw. ob dies überhaupt HighSpeed fähig ist. Die
HighSpeed CDC Variante ist zwar schnell und einfach, aber wenn man es
etwas professioneller aussehen lassen will, ist dies doch die bessere
Variante oder?
gruss Tommy