Hallo Leute! Ich versuche eine schlanke USB Schnittstelle auf einem STM32F042 zu implementieren. Über den USB sollen kleine Datenpakete (Messwerte, wenige Bytes) etwa im Sekundentakt übertragen werden. Zu Konfigurationszwecken gibt es auch ein paar Parameter die über Terminal eingestellt werden sollen. Ist aber alles nicht Zeitkritisch. Hab einen ST-Link V2 und arbeite mit CubeIDE. Die Variante aus dem Code-Generator von ST (cubeMX) hab ich schon erfolgreich zum Laufen gebracht, allerdings ist mir die Implementierung zu groß. Ich hab daher versucht, die Variante von http://stefanfrings.de/stm32/stm32l0.html (basierend auf der von W.S.) zu verwenden, kriege diese aber bei mir nicht zum Laufen. Der Controller meldet sich am PC nicht an, scheinbar wird einfach garnichts übertragen. Da meine C-Kompetenzen nicht gerade berauschend sind und ich mit USB bisher nichts zu tun hatte, hab ich versucht, die Ursache durch Vergleichen der cubeMX-Version mit der von W.S. zu finden. Bisherige Erkenntnis: Beim Initialisieren der USB-Register / Endpoint-Konfiguration sind die Schreib-Operationen auf die EPxR-Register nicht erfolgreich. Die Register sind auch nach dem Schreiben noch leer. Die Zeile USB_EP0R = ((3 << 12) | /* STAT_RX = 3, also Empfang enabled */ (2 << 4) | /* STAT_TX = 2, also NAK erstmal */ (1 << 9) | /* EP_TYPE = 1, also Control */ logEpCtrl); in InitEndpoints(void) ist wirkungslos. Ich dachte zuerst an ein Taktproblem und hab daher die Clock-Config vollständig von der cubeMX-Variante übernommen, um das als Ursache auszuschließen. Interessanterweise funktioniert aber aucgh die cubeMX-Variante nur dann, wenn ich beim Initialisieren keine Breakpoints setze. Mit Breakpoints ist es auch dort so dass ein Schreiben auf die EPxR wirkungslos ist, der Controller meldet sich garnicht am PC. Hat jemand eine Idee woran das liegen könnte, bzw einen Ansatz zur weiteren Fehlersuche? Vielen Dank, Alex
Alex schrieb: > Die Zeile ... in InitEndpoints(void) ist wirkungslos. Was genau meinst du mit "wirkungslos"? Hast du den Pull-Up Widerstand an D+ hinzugefügt oder USB_BCDR_DPPU=1 gesetzt (nicht beides zusammen!) ? Stimmt der Name der ISR in usb.c (USB_IRQHandler) bzw. hast du ihn entsprechen deiner Interrupt-Vektor Tabelle angepasst? Taktest du den USB Port mit dem 48MHz HSI Oszillator? Hast du die USB Schnittstelle eingeschaltet? > // Enable USB with pull-up > SET_BIT(RCC->APB1ENR, RCC_APB1ENR_USBEN); > SET_BIT(USB->BCDR, USB_BCDR_DPPU); Du solltest dein Projekt in compilierbarer Form anhängen, damit wir nicht endlos viele Fragen stellen müssen und damit jemand dein Problem reproduzieren kann. (Ich habe keinen STM32F0, deswegen kann ich es nicht ausprobieren). > Interessanterweise funktioniert aber aucgh die cubeMX-Variante nur dann, > wenn ich beim Initialisieren keine Breakpoints setze. Die USB Schnittstelle muss permanent alle Events binnen weniger ms beantworten, deswegen fällt sie aus, sobald ein Breakpoint erreicht wird. Benutze zum Debugging lieber (serielle) Text-Meldungen.
Alex schrieb: > STM32F042 Du darfst nicht annehmen, daß ein Lowlevel-Treiber für den STM32F103 einfach so auch auf einem STM32F042 funktioniert. Deshalb ist das gründliche Lesen des zum USB-Device gehörigen Kapitels in zutreffenden RefManual zwingend notwendig. Stephanus hat meinen Treiber auf den STM32F303 umgeschrieben und im Prinzip müßtest du ihn auf den STM32F042 umschreiben. Jetzt hab ich das Manual für diesen µC nicht gelesen, kann dir also nicht sagen, ob da nur ne winzige oder eine umfängliche Anpassung nötig ist. Eines ist aber klar: alle Vorbereitungen (Pinzuweisung, Takt richtig aufsetzen, richtigen Interrupt wählen, NVIC richtig aufsetzen) mußt du selber machen. Ich hatte (wenn ich mich recht erinnere) auch ein Codestück zum Postmortem-Debugging beigefügt, das dazu dient, Meldungen, die man irgendwo in den Treiber einfügt, im RAM zwischenzuspeichern, so daß man diese von außerhalb des Treibers dann abfragen kann. W.S.
Hallo Stefan und W.S. Schon jetzt Danke für eure Hilfe und vor allem dafür, dass ihr euren Code für alle bereitstellt !!!!! > Hast du den Pull-Up Widerstand an D+ hinzugefügt oder USB_BCDR_DPPU=1 gesetzt (nicht beides zusammen!) ? ja, DPPU wird gesetzt, der PC reagiert darauf -> typische USB-Gerät nicht erkannt - Meldung; Der Controller tut sonst nichts mehr -> Enum. schlägt fehl > Stimmt der Name der ISR in usb.c (USB_IRQHandler) bzw. hast du ihn entsprechen deiner Interrupt-Vektor Tabelle angepasst? ja, ist beim F042 auch "USB_IRQHandler" und auch die Nummer ist 31 > Taktest du den USB Port mit dem 48MHz HSI Oszillator? Der Takt kommt vom USB. Daher hatte ich auch zuerst ein Taktproblem vermutet und meine Takt-CFG komplett durch die vom cubeMX (die ja funktioniert) ersetzt. Da ist zwar jetzt die HAL involviert, aber ich kann zumindest ausschließen(nach meinem Kenntnisstand/bestem Wissen ausschließen), dass es da ein Problem mit der CLK-Config gibt. Alle Timer-Takte usw. hab ich geprüft und stimmen. > Hast du die USB Schnittstelle eingeschaltet?+++ Die Einschalt-Prozedur ist die von W.S. Code, ich hab begonnen die einzelnen Schritte mit dem RefManual abzugleichen/zu verstehen und bin eben bis zu den Schreibzugriffen auf die EPxR-Register gekommen. >> Die Zeile ... in InitEndpoints(void) ist wirkungslos. >Was genau meinst du mit "wirkungslos"? Das Register ist nach dieser Programmzeile immer noch 0, die Zeile hat keine Wirkung. Das gilt für alle Zugriffe auf die EP-Register, Schreiben auf das CNTR-Register beispielsweise klappt.... > Die USB Schnittstelle muss permanent alle Events binnen weniger ms > beantworten, deswegen fällt sie aus, sobald ein Breakpoint erreicht > wird. Benutze zum Debugging lieber (serielle) Text-Meldungen. Ich verstehe, dass es ab dem Zeitpunkt, an dem der Pullup gesetzt wird, zeitkritisch ist. Hintergrund meiner Aussage war, dass beim cubeMX-code auch die EP-Register dann leer bleiben. Meine Interpretation ist, dass die Konfiguration dieser in den IRQs durchgeführt wird, und nicht wie bei W.S. davor. Oder lieg ich da falsch? Zugegebenerweise ist mir der cubeMX-Code extrem schwer nachzuvollziehen. Ich stehe mit der ganzen Thematik ziemlich am Anfang, und hab einfach begonnen den Code und das RefManual Schritt für Schritt durchzuarbeiten. Bis zu dem Punkt, an den ich gekommen bin, gab es imho keine nötige Anpassung für den F042. Die Schritte bis dorthin sind auch im Manual so beschrieben. Dass die EPxR nicht geschrieben werden, ist für mich einfach nicht nachvollziehbar/verständlich. Alex
Hallo Alex, bitte die genaue Funktion der Register nachlesen. Es ist schon spät..und so aus den alten grauen Zellen herraus: CNTR löst z.B. Funkionen im USB-Core aus. EP_R wird durch die USB-Core gesetzt ( z.B. USB-Empfang )und kann nur gelöscht werden. Pieter
Alex schrieb: > Der Takt kommt vom USB. Wiebitte? Der Takt kommt normalerweise vom angeschlossenen Quarz per Quarzoszillator im Chip. Der muß angeworfen werden, dann die PLL und das richtige Teilerverhältnis in der Taktversorgung, so daß der USB-Peripherie-Core seine genauen 48 MHz kriegt. Wenn das bei deinem Chip anders läuft, dann stehen mMn bei dir größere Änderungen am Treiber an, deren Umfang ich hier nicht abschätzen kann. Ich habe aber ein anderes Negativbeispiel: Bei einem Kinetis (von Freescale, exakten Typ hab ich vergessen) ist die Taktversorgung per Quarz so schlecht (jittert), daß dort für den USB auf den internen 48 MHz Takt aus einem RC-Oszillator zurückgegriffen werden muß. Da dieser jedoch frequenzmäßig zu instabil ist, muß er per Software auf die Signale vom Host am USB aufsynchronisiert werden, bevor irgend eine Kommunikation am USB möglich war. Als ich das gelesen hatte, war der Chip bei mir unten durch. Also gönne deinem Chip einen passenden Quarz und benutze HSE+PLL. Alles andere halte ich für Pfusch, der nur mit Klimmzügen zum sauberen Laufen zu bringen ist. Wenn dein Chip für den USB als Takt einen HSI vorsieht, dann gibt es im USB-Core auch irgend welche Synchronisier-Tricks, die in meinem Code nicht vorgesehen sind. W.S.
Alex schrieb: > Der Takt kommt vom USB Das ist unklar. Das USB Kabel stellt kein Taktsignal zur Verfügung. Du musst den HSI Oszillator konfigurieren. Alex schrieb: > Die Einschalt-Prozedur ist die von W.S. Code Der Code von W.S. schaltet das USB Interface nicht ein. Das musst du selber machen. Ich habe die beiden relevanten Zeilen oben zitiert. Du steckst da viel Vertrauen in die HAL. Aber gerade weil du die HAL jetzt nur noch zum Teil verwendest, kann dabei durchaus etwas ganz anderes heraus kommen, als geplant. Das solltest du prüfen, insbesondere die Konfiguration von Taktversorgung und Interrupts. Setze einen Unterbrechungspunkt in die Zeile "UsbSetup()" und prüfe den Inhalt aller relevanten Register. Bedenke dabei, dass meine main.c von einer ganzen Reihe Default-Settings ausgeht, die der Chip beim Reset einstellt. Die HAL fummelt aber daran herum. Alex schrieb: > Das Register ist nach dieser Programmzeile immer noch 0, die Zeile hat > keine Wirkung. Klingt danach, als ob die entsprechende Funktionsgruppe im RCC nicht eingeschaltet ist. Alex schrieb: > Meine Interpretation ist, dass die Konfiguration dieser in den IRQs > durchgeführt wird, und nicht wie bei W.S. davor. Oder lieg ich da > falsch? Keine Ahnung, wie das in der HAL umgesetzt wurde. Durch den Spaghetti-Code blicke ich nicht durch. Eine Initialisierung im Interrupt ist jedenfalls unwahrscheinlich, weil die ISR zwangsläufig erst nach der Initialisierung aufgerufen wird. Hast du einen guten Grund, warum du deinen Quelltext immer noch nicht vorzeigst? Ich habe keine Lust, mich ohne Quelltext weiter in deinen Kopf hinein zu denken.
W.S. schrieb: > Wenn dein Chip für den USB als Takt einen HSI vorsieht, > dann gibt es im USB-Core auch irgend welche Synchronisier-Tricks, die in > meinem Code nicht vorgesehen sind. Korrekt. Das ist im Reference Manual im Clock Recovery System beschrieben. Sollte man vielleicht mal lesen. Der STM32 synchronisiert seinen Oszillator ziemlich selbstständig mit den SOF Paketen vom USB Port, und das ist per Default nach dem Reset aktiv. Aber die HAL fummelt erfahrungsgemäß gerne an den Default Werten herum, darüber bin ich bereits mehrfach gestolpert.
Vorweg: Es läuft (noch nicht voll getestet...) Fehlerursache: ich hatte im SYSCFG versehentlich das remap der Pins PA11 & PA12 überschrieben. Der F042 hat die USB-Pins als shared-pins ausgeführt, daher ist das remap nötig. Lustigerweise kann man dann zwar beim USB alles mögliche initialisieren, aber die Schreibzugriffe auf die EPxR-Register gehen nicht. Nochwas zum USB-Sync Takt: Der F042 (und vmtl andere STM32 auch) haben ein CRS (clock recovery system), das den internen RC-Osc mit den SOFs des USB synchronisiert. Die dafür nötigen Funktionen sind in Hardware implementiert und werden vor dem USB-Treiber initialisiert. Ich hab mir diese Teile im Manual genau durchgearbeitet, die Konfiguration passt und funktioniert. Mit dem USB-Treiber hat das nichts zu tun, der Takt läuft schon vorher. Hab mir auch angesehen, wie das Ding auf Temperaturänderungen reagiert ;-), ich würd sogar sagen, dass die CLK auf diese Weise stabiler* ist als mit einem normalen XO (eben wegen des tempcos). *: genau genommen ist sie eigentlich nicht stabil, sie ist nur auf den USB synchronisiert -> optimal für eine stabile USB-Verbindung. Wenn man eine sehr exakte ReferenzCLK in der Schaltung benötigt (z.B: als Referenz für Messungen etc), ist das eher suboptimal.... aber darum gehts hier ja nicht. Jedenfalls hab ich nirgends einen Hinweis gefunden, dass die Programmierung des USB-Treibers deshalb irgendwie angepasst werden müsste. Zum Thema HAL: ist mir auch nicht ganz geheuer, vor allem weil ich die vielen Unterprogramme extrem unübersichtlich finde.Hat aber sicher auch was mit meiner C-Schwäche zu tun... Nur würde ich die HAL nicht als Spaghetti-Code bezeichnen, die ist wohl eher das Gegenteil davon.... Alex
Die HAL ist ziemlicher Spaghetticode und erweckt bei mir den Eindruck, dass da Leute Angst vor Hardare haben und darum mehrere (sinnlose) Zwischenschichten eingezogen haben. Wenn der Rechner sieht, dass ein Gerät versucht sich zu melden, wie weit ist denn der Protokollstack im Controller lauffähig? Kriegst Du irgend welche Requests und schickst Antworten raus?
Guido Körber schrieb: > Die HAL ist ziemlicher Spaghetticode und erweckt bei mir den Eindruck, > dass da Leute Angst vor Hardare haben und darum mehrere (sinnlose) > Zwischenschichten eingezogen haben. Ist sie nicht. Es ist eher so, dass viele Leute bis heute nicht verstanden haben, dass die Zwischenschichten Abstraktionsschichten sind die bei richtiger Konfiguration des Compilers keinen (!) Overhead verursachen. Abstraktion ist sinnvoll und kostet heutzutage bei richtiger Verwendung weder Laufzeit noch Code. Ein USB-Treiber, von dem dagegen nichts direkt wiederverwendbar ist, ist nicht zeitgemäß (sorry W.S., aber so schreibt man moderne Software nicht).
avr schrieb: > Es ist eher so, dass viele Leute bis heute nicht > verstanden haben, dass die Zwischenschichten Abstraktionsschichten sind > die bei richtiger Konfiguration des Compilers keinen (!) Overhead > verursachen. Nein. Definitiv nicht. Die HAL Funktionen belegen ein vielfaches des Flash Speichers, den man ohne HAL brauchen würde. Der USB CDC Treiber ist das beste Paradebeispiel dafür. Vergleiche den mal mit dem Werk von W.S., dann siehst du es. > Ein USB-Treiber, von dem dagegen nichts > direkt wiederverwendbar ist, ist nicht zeitgemäß Das ist mir zu pauschal. Es gibt nicht nur den einen richtigen Weg.
avr schrieb: > Abstraktion ist sinnvoll und kostet heutzutage bei > richtiger Verwendung weder Laufzeit noch Code. Ein USB-Treiber, von dem > dagegen nichts direkt wiederverwendbar ist, ist nicht zeitgemäß (sorry > W.S., aber so schreibt man moderne Software nicht). Das ist auch nur die ausgeleierte Argumentation von Leuten die nur Arduino-Libs zusammen stöpseln. Die selben Argumente kamen als die STM-Libs noch nicht HAL hießen. Sie StdPerLib ist so schön portabel und man kann sie immer wieder verwenden. Und dann? Angeschissen wenn STM der Meinung ist die neuen MCUs bekommen nur noch die HAL. Und in 3 Jahren vielleicht wieder was neues. Das einzige was sich nicht ändert sind die Register. Mir ist so ein kompakter Code jedenfalls wesentlich lieber wie das ganze HAL-Geraffel. Und von wegen modern und zeitgemäß. Ich will vordergründig ein Problem lösen und nicht ein schönes modernes Programm schreiben. Wer ein paar Jahre auf dem Buckel hat, hat viele Säue durchs Dorf rennen sehen. Nur Idioten rennen jeder hinterher. Und STM hat schon einige Säue losgelassen... Nachdem die NOP() Problematik im Code von W.S. eliminiert ist, ist er jedenfalls 1000mal besser als alle anderen mir bekannten Implementationen. an Stefan ⛄ F. noch die Frage, hast du die von mir vorgeschlagene Änderung bezüglich der DoSetAddress() Funktion und der Eliminierung des letzten nötigen NOP()'s mal probiert? Beitrag "Re: STM32F303 CAN und USB gleichzeitig"
temp schrieb: > hast du die von mir vorgeschlagene > Änderung bezüglich der DoSetAddress() Funktion und der Eliminierung des > letzten nötigen NOP()'s mal probiert? Ja läuft, habe ich in meine Codebeispiele übernommen. Bitte entschuldige, dass ich vergessen habe, Feedback zu geben.
Funktioniert denn der Code aus meinem USB-Tutorial mit STM32? Der hat jedenfalls kein NOP-Problem :)
Stefan ⛄ F. schrieb: > Ja läuft, habe ich in meine Codebeispiele übernommen. Bitte > entschuldige, dass ich vergessen habe, Feedback zu geben. Ich hab mir gerade das Beispiel in STM32L073_usb_test.zip runtergeladen. Da steht noch
1 | void DoSetAddress(void) |
2 | {
|
3 | ACK(); // das muß bei diesem Core davor! |
4 | Nop(1000); |
5 | USB_SetAddress(CMD.SetupPacket.wValue); |
6 | }
|
drin.
Korrekt macht man das mit dem Adresse setzen so, dass man nach der Status Stage - d.h. nach dem Senden des leeren ACK Pakets, beim USB_ISTR_CTR-Interrupt - die Adresse übernimmt (DADDR Register), z.B.: https://github.com/Erlkoenig90/f1usb/blob/minimal/src/usb.cc#L499 Dadurch passiert es exakt im richtigen Moment, und man blockiert nicht den Prozessor durch eine lange Nop-Schleife. Bei einer solchen ist die Dauer auch kritisch - ist sie zu lang, verpasst man das nächste Paket vom Host (ok, wird automatisch neu versucht), ist sie zu kurz, kommt das ACK nicht an. Siehe auch: https://www.mikrocontroller.net/articles/USB-Tutorial_mit_STM32#Adresszuweisung
Genau der SetAdressrequest ist die Ausnahme und funktioniert etwas anders als alle anderen Requests. Üblicherweise wird bei einem Requests erst dann mit ACK bestätigt wenn der Requests alle Aktionen bearbeitet hat. Bei SetAddress wird sofort bestätigt (unter der alten Adresse) und dann umgeschaltet allerdings erst wenn das ACK erfolgreich war. Nop ist nur ein Workarround für einen Fehler im Framework. Thomas
Stefan ⛄ F. schrieb: > Misst, irgendwas habe ich da vergessen - offenbar. Thomas Z. schrieb: > Nop ist nur ein Workarround für einen Fehler Ist jetzt korrigiert. Link zur Webseite: http://stefanfrings.de/stm32/index.html
temp schrieb: > avr schrieb: >> Abstraktion ist sinnvoll und kostet heutzutage bei >> richtiger Verwendung weder Laufzeit noch Code. Ein USB-Treiber, von dem >> dagegen nichts direkt wiederverwendbar ist, ist nicht zeitgemäß (sorry >> W.S., aber so schreibt man moderne Software nicht). > > Das ist auch nur die ausgeleierte Argumentation von Leuten die nur > Arduino-Libs zusammen stöpseln. Die selben Argumente kamen als die > STM-Libs noch nicht HAL hießen. Sie StdPerLib ist so schön portabel und > man kann sie immer wieder verwenden. Und dann? Angeschissen wenn STM der > Meinung ist die neuen MCUs bekommen nur noch die HAL. Und in 3 Jahren > vielleicht wieder was neues. > Das einzige was sich nicht ändert sind die Register. - Ein Großteil der Arduino-Libs sind Käse, das Argument ist reine Polemik - Die PeripheralLib war alles andere als kompatibel, die war eigentlich sehr Hardware-nah. - Die HAL ist schon deutlich besser und wird sich so schnell nicht ändern. - man kann sich durchaus seine eigene HAL schreiben, aber bitte nicht gleich das Applikation-Layer integrieren. > Mir ist so ein kompakter Code jedenfalls wesentlich lieber wie das ganze > HAL-Geraffel. Und von wegen modern und zeitgemäß. Ich will vordergründig > ein Problem lösen und nicht ein schönes modernes Programm schreiben. Darum geht es nicht. Es geht um Wartbarkeit, Wiederverwendbarkeit und Testbarkeit von Code. Ich könnte den Code von WS in den wenigsten Fällen verwenden, obwohl ich sehr oft USB einsetze. Nur virtuelle COM-Ports habe ich eher selten aus USB-Klasse. Und daher setze ich auf Abtraktion, so dass ich nur einen sehr kleinen Teil austauschen muss, um die Klasse zu ändern. Auf der anderen Seite kann ich sehr einfach mehrere Klassen gleichzeitig implementieren.
Es gibt nunmal 2 Extrema in die man sich nicht begeben sollte. Einerseits dem W.S. seinen Magic Number Code und dem aufgeblasenem HAL Code. Der Lagenaufbau von sich des STM32HAL ist garnicht mal so schlecht, aber der Code selbst ist es. zB wird in einem IRQ direkt an der Statemachine eines Task rumgefummelt und DANN wird eine Message geschickt zum Task aufzuwachen. WTF?
Niklas G. schrieb: > Korrekt macht man das mit dem Adresse setzen so, dass man nach der > Status Stage - d.h. nach dem Senden des leeren ACK Pakets, beim > USB_ISTR_CTR-Interrupt - die Adresse übernimmt (DADDR Register), z.B.: Genau das habe ich am Code von W.S. geändert. Der Codeschnipsel oben ist der Zustand vor der Änderung. Alles weitere kann im verlinkten Thread nachgelesen werden. avr schrieb: > Ich könnte den Code von WS in den wenigsten Fällen verwenden, obwohl ich > sehr oft USB einsetze. Und ich habe bisher ausschließlich virtuelle COM-Ports verwendet, und dafür ist dieser schlanke Code ideal. Solange ich nichts anderes brauche besteht deshalb auch keine Notwendigkeit ein funktionierendes System (mit Fehlern) aufzuhübschen.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.