Hallo zusammen, in meinem aktuellen Projekt beschäftige ich mich mit der Umsetzung einer USB Audio Schnittstelle. Die Audiodaten werden dann entweder über I²S oder TDM, je nach benötigter Kanalanzahl ausgegeben. Momentan alles nur im Rahmen von UAC1. Implementiert ist die Schnittstelle auf einem Atmel SAMD21, da ich den schon von anderen Projekten kenne. Die USB-Schnittstelle hatte ich vorher noch nie verwendet, deshalb hatte ich auch Interesse mal etwas in diese Richtung umzusetzen. Die Basisentwickung habe ich auf dem Xplained Pro Eval Board gemacht und nun auf eine eigene Platine migriert. Die Einarbeitungszeit war zwar enorm, aber die USB Audio Schnittstelle funktioniert nun soweit! Treiberseitig ist der USB Treiber aus dem ASF und die Audioklasse ist vom AVR32 portiert. Das Device enumeriert unter Windows sauber und wird problemlos als Audiodevice erkannt. Das Abspielen funktioniert ebenfalls (im momentanen Testaufbau in Kombination mit einem DSP, der die DA-Wandlung übernimmt). Das Device läuft im asynchronen Modus, womit wir zum aktuellen Problem kommen: die Synchronisation des USB- und des I²S-Clocks. Beim Abspielen kommt es nach ca. 2-3 Minuten zu starken Verzerrungen. Entsprechend läuft der Buffer durch die nicht vorhandene Synchronisation über/unter. Eine Hardware-Synchronisierung der Clocks oder den synchronen Modus will ich aus qualitativen Gründen zunächst nicht als mögliche Lösung berücksichtigen. Entsprechend befasse ich mich gerade mit dem Thema Feedback/Sync Endpoint. Der SAMD21 unterstützt dieses Feature. Nun habe ich mich schon durch die Tiefen der USB-Spezifikation gearbeitet und die nötigen Infos zum Vorhaben gefiltert. Den Descriptor für den Sync Endpoint habe ich soweit implementiert und dieser wird auch unter Windows erkannt (ausgelesen mit USBlyzer). Was mir noch nicht so recht klar ist, wie der Datentransfer aus dem Sync Endpoint abläuft. Muss ich den Feedback-Wert nur im Endpoint ablegen und der Host "holt" diesen dann ab oder brauche ich eine entsprechende Senderoutine, die das Datum im eingestellten Zeitintervall (2^P Frames, mit P=1..9) übermittelt? Gibt es dazu eine Doku in der USB Spec die ich bloß noch nicht gefunden habe? Die Recherche im Netz allgemein nach dem Thema und nach vorhandenem Code gestaltet sich äußerst mühsam. Hat hier noch jemand nützlichen Input oder hat selbst so etwas mal implementiert? Bin für konstruktive Kommentare dankbar. Beste Grüße, Markus
Schwieriges Kapitel Welche Usbaudio spec nutzt du unter welchem OS. Welcher Treiber wird geladen? Beachte USB Audio 2.0 wird unter Win erst seit kurzem unterstützt. Thomas
Das Ganze läuft unter USB 2.0. Der SAMD21 kann maximal nur Fullspeed, was aber fürs erste auf jeden Fall ausreichend ist. Wie gesagt, ich beschränke mich zunächst auf USB Audio 1 (UAC1). Betriebssystem ist Windows 10. Welcher Treiber genau geladen wird, habe ich mir noch nie angeschaut. Werde ich morgen mal schauen, wenn ich wieder im Bastelkeller bin.
Hier ein paar Dinge zum lesen https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers USB Audio 1.0 macht nichts mit den Sync Endpoints. Es bleibt dir nichts anders übrig als die Synchronisation mit dem SOF selbst zu machen. Been there done that. Thomas
Noch ein paar Hinweise USB fullspeed reicht für 8 Kanäle @16 Bit 48k. Die SR wird mit einem Classrequest eingestellt. Wenn du vorhast Aufnahme und Wiedergabe gleichzeitig mit untersch.SRs zu verwenden vergiss es oder baue HW SR Konverter ein. Ansonsten wird immer der WIN eigene SR Konverter aktiv. Das Thema ist kompliziert, schlecht dokumentiert und WIN unterstützt nur wenige Teile der Audiospec. Falls du vor hast irgendwann USB Audio 2 zu unterstützen bleibt bis W7 nur ein Fremdtreiber. Bei Interesse kann ich mehr dazu sagen. Thomas
Thomas schrieb: > Hier ein paar Dinge zum lesen > https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers > > USB Audio 1.0 macht nichts mit den Sync Endpoints. Es bleibt dir nichts > anders übrig als die Synchronisation mit dem SOF selbst zu machen. > > Been there done that. > > Thomas Danke für die Infos. Mein Vorhaben funktioniert mit UAC1 also schonmal nicht. Ich habe derweil keine vernünftige Möglichkeit gefunden, den SOF im uC auch für die Ausgabeseite zu verwenden. Atmel hat das wohl auf den asynchronen Modus ausgelegt. Synchron ginge zwar vermutlich, aber nur mit Kunstgriffen. > USB fullspeed reicht für 8 Kanäle @16 Bit 48k. Ist bekannt, hatte ich schonmal getestet, funktionierte auch. > Wenn du vorhast Aufnahme und Wiedergabe gleichzeitig mit untersch.SRs zu verwenden Nein das wird nicht vorkommen. Wenn das System als Zuspieler für einen DSP ohne ASRC verwendet wird, muss die Samplerate sowieso fix sein. Fürs erste ist für mich ohnehin nur die Abspielseite von Belangen. > Falls du vor hast irgendwann USB Audio 2 zu unterstützen bleibt bis W7 > nur ein Fremdtreiber. Bei Interesse kann ich mehr dazu sagen. Naja, alles vor Win7 ist auch nicht relevant. Wenn es Unterschiede zwischen Win7 und 10 gibt, würde ich mich ansich auf Win10 konzentrieren. Ich hatte mal spaßeshalber den Treiber meines MiniDSP Streamers (USB Audio 2) für mein Device installiert und wenn ich mich recht entsinne funktionierte das sogar! Spricht prinzipiell etwas dagegen, das Projekt gleich auf USB Audio 2 auszulegen? Vermutlich höhere Komplexität, allerdings ist die Lösung der Synchronisation mit einem Feedback Endpoint vermutlich die professionellste.
Markus G. schrieb: > Spricht prinzipiell etwas dagegen, das Projekt gleich auf USB Audio 2 > auszulegen? > Vermutlich höhere Komplexität, allerdings ist die Lösung der > Synchronisation mit einem Feedback Endpoint vermutlich die > professionellste. Um ganz ehrlich zu sein ich bin mir nicht so sicher ob USB Audio 2 für ein Fullspeed Device überhaupt geladen wird, das musst du wohl einfach ausprobieren. Wenn ja wäre es wohl die Lösung. Viele Hersteller benutzen unter W7 den Audio Class Treiber von Thesycon. Mir sind allerdings nur Highspeed devices bekannt die den Treiber benutzen. MS hat bei USB Audio 1 seit 98SE nicht mehr viel verändert. Mit Me wurde Midi supportet dafür waren Selektoren kaput. 98Se hing bei Midi W2000 machte einen Bluescreen. Ich hätte damals für XP entwickelt. Mein Device geht aber immer noch auch unter W10. Ich kenne andere die seit W7 nicht mehr gehen. Thomas
Thomas schrieb: > USB Audio 1.0 macht nichts mit den Sync Endpoints. Ich muss mich da korrigieren. USB Audio 1.0 macht unter XP nichts mit den Sync Endpoints. Ich glaube es zwar nicht aber vielleicht ist das ja unter W10 anders. Hab das nie überprüft. Thomas
Thomas schrieb: > Thomas schrieb: >> USB Audio 1.0 macht nichts mit den Sync Endpoints. > > Ich muss mich da korrigieren. > USB Audio 1.0 macht unter XP nichts mit den Sync Endpoints. > Ich glaube es zwar nicht aber vielleicht ist das ja unter W10 anders. > Hab das nie überprüft. Wenn das mit UAC1 möglich ist, wäre mir das natürlich fürs erste am liebsten. Nach folgendem Dokument müsste es seitens der USB-Schnittstelle möglich sein (Kapitel 4.6.2): https://www.usb.org/sites/default/files/audio10.pdf Wie es seitens des Treibers aussieht konnte ich derweil nicht eindeutig feststellen.
:
Bearbeitet durch User
Markus G. schrieb: > Nach folgendem Dokument müsste es seitens der USB-Schnittstelle möglich > sein (Kapitel 4.6.2): > https://www.usb.org/sites/default/files/audio10.pdf > Wie es seitens des Treibers aussieht konnte ich derweil nicht eindeutig > feststellen. Ja spezifiziert ist das schon. Die Frage ist halt ob MS das in usbaudio.sys implementiert hat. Ich kann das zumindest für XP verneinen. Ich würde an deiner Stelle einfach mal simple UAC2 Deskriptoren implementieren und schauen ob für dein Device dann usbaudio2.sys geladen wird Thomas
Hallo Markus, Ich kann mich im Punkt der "guten Dokumentation" zum Thema Synchronisierung meinen Vorrednern nur anschließen. Ich arbeite selbst gerade an einem ähnlichen Projekt (allerdings mit ARM Prozessor) wie du und habe es auch versucht mich in die USB Dokumentation einzulesen. Mein Projekt ist zwar auch noch nicht wirklich lauffähig, aber ich kann dir zumindest bestätigen, dass (soweit ich herausgefunden habe) ab Win XP die Sync Endpoints funktionieren sollen (in XP wohl nur rudimentär, ab Win7 aber halbwegs brauchbar). Auf meinem Evaluation Board habe ich das ganze auch schon lauffähig hinbekommen und muss es nun noch auf meine eigene Platine übertragen. Die Synchronisation läuft so ab, dass du dem Host über deinen Sync Endpoint eine Sendefrequenz vorgibst. (bspw. Wenn du 48kHz Samplerate nutzt und der PC zu langsam liefert, musst du beim PC eine höhere Samplerate anfordern bspw 48.01kHz, so dass du auf deinem Gerät passend deine 48kHz bekommst). Die Sync Daten bestehen dabei aus 3 Bytes im 10.10 bzw. 10.14 format - heißt die oberen 10bits stellen den integer Part dar und die unteren 14 Bits den gleitkommapart. Aufgebaut sind sie nach dem Muster: 10^9 10^8 10^7 ... 10^0 10^-1 ... 10^-14 Ich hoffe ich konnte dir damit zumindest teilweise etwas helfen. Gruß Hannes
Hallo Hannes, das ermuntert natürlich schon mal, dass es noch Leidensgenossen bei dem Thema gibt :) Ich arbeite auch auf einem ARM (SAMD21 = Cortex-M0). Implementierst du auch auf einem Atmel uC? Die Info, dass es bei dir läuft und dass die Sync Endpoints mit Win7 funktionieren müssten, hilft mir auf jeden Fall. Gibt es dazu eine Quelle, bzw. ist das irgendwo dokumentiert? Bezieht sich die Aussage auf UAC1 oder 2? Wie generierst du denn deinen Referenzwert für die aktuelle Übertragungsrate des USB? Ich hatte mir überlegt, dass einfach mit einem Timer zu machen, sprich ich zähle die CPU Ticks, die zwischen einem/oder mehreren SOFs vergangen sind und kann so die reale Übertragungsrate ausrechnen. Gruß Markus
:
Bearbeitet durch User
Ich mach mal ein Beispiel für 44.1 weil das nicht so genau in 1ms SOFs aufgeteilt werden kann. Bei 16 Bit muss der EP 44.1 * 32 Bit in einer ms übertragen (2 Kanäle = stereo) Dein Device wird also 9mal 44*32 Bit senden und 1 mal 45*32 Bit. Der SOF wird als konstant angenommen 1ms die spec schreibt das vor. Du musst also den Korrektur Wert aus den den tatsächlich bereitgestellten Daten berechnen. Weicht der Wert nach 10ms ab musst du die Samplerate entsprechend verstellen. Damit das nicht so sehr sehr bemerkbar wird würde ich den Korrektur Wert langsam äendern aber die Geschwindigkeit mit der das passiert hängt von der Polling Rate ab. Thomas
Thomas schrieb: > Ich mach mal ein Beispiel für 44.1 weil das nicht so genau in 1ms > SOFs > aufgeteilt werden kann. > > Bei 16 Bit muss der EP 44.1 * 32 Bit in einer ms übertragen (2 Kanäle = > stereo) > Dein Device wird also 9mal 44*32 Bit senden und 1 mal 45*32 Bit. > Der SOF wird als konstant angenommen 1ms die spec schreibt das vor. > Du musst also den Korrektur Wert aus den den tatsächlich > bereitgestellten Daten berechnen. Weicht der Wert nach 10ms ab musst du > die Samplerate entsprechend verstellen. Damit das nicht so sehr sehr > bemerkbar wird würde ich den Korrektur Wert langsam äendern aber die > Geschwindigkeit mit der das passiert hängt von der Polling Rate ab. > > Thomas Okay, das macht die Vorgehensweise schon klarer! Ich bin momentan dabei, eine Testimplementierung zu machen: Den Descriptor habe ich soweit angepasst, dass der Feedback Endpoint richtig erkannt wird (zumindest kein Descriptor error). Nun würde ich gerne als Feedback konstant 48 kHz ausgeben, um die allgemeine Funktion zu testen. Das führt mich wieder zu einer meiner Eingangsfragen: wie läuft die Datenübertragung aus dem Feedback Endpoint heraus ab? Muss ich den Wert im eingestellten Intervall (z.B. 8 oder 16 ms) mit einer Routine übermitteln oder holt sich der Host die Daten selbst aus dem Endpoint?
Hallo Markus, meine Aussagen beziehen sich komplett auf UAC1. Mein Projekt basiert auf einem ARM Coartex M4 (EVAL Board) bzw ARM Coartex M7 (eigene Platine) beide aber nicht von Atmel, sondern von ST. Aktuell habe ich die Synchronisierung noch nicht auf die Ticks des uC bezogen sondern erst einmal eine kleine Funktion gebastelt, die durch DMA Interrupts aufgerufen wird, und dort dann den Offset der Schreib zum Lesezeiger im Ringpuffer berechnet und Somit erkennt, ob ich vom PC zu viele oder zu wenig Samples bekommen habe. Wenn dann der Schreibzeiger von seiner Sollposition (1/2 Puffergrößenversatz) abweicht, dann aktualisiere ich die Samplerate über den Feedback Endpoint (bei 48KHz in meinem Bsp. fordere ich bei einem zu kleinem Versatz dann 48.010KHz Samplerate über den Feedback Endpoint an, bei einem zu großem 48.010KHz). Die Methode ist zwar Audiotechnisch sicher nicht perfekt, aber es funktioniert vorerst einmal und hörbar ist die geringe abweichung auch nicht. Im Idealfall würde man die I2S Masterclock mit einem Timer zählen und diese dann über einen oder mehrere USB SOF's verrechnen. Damit ließe sich dann der Tatsächliche Versatz von PC Samplefrequenz zu I2S Samplefrequenz berechnen und man könnte über den Sync Endpoint die Datenraten Synchronisieren. Entspricht ja auch fast deinem Ansatz und wäre sicher auch die sauberere Methode :). Quellen habe ich leider gerade keine für dich zur Hand, die meisten Infos, die ich zu dem Thema gefunden habe, habe ich in diversen Russischen Foren gefunden (Google Translate sei Dank :)). Gruß Hannes
Du must dich von der Vorstellung verabschieden dass dein Device irgend etwas selbstständig macht dem ist nicht so. Dein Device regiert nur auf Anfrage des Hosts. Du stellst deinen Wert bereit und der Host fragt diesen Wert ab. Wenn das geschehen ist bekommst du vermutlich einen Interrupt. Dann kannst du den nächsten Wert bereitstellen. Falls der Host deinen Endpoints nicht abfragt ist das eben so und deine Clocks sind free running. Thomas
Wenn ich die Definitionen korrekt verstanden habe, dann stellst du im Descriptor deine Aktualisierungsrate ein und der PC erwartet dann in diesem Intervall die neue Samplerate. Jedoch ist es so, dass wenn keine Änderung stattgefunden hat, auch keine neue Samplerate gemeldet werden muss - jedenfalls funktioniert es bei mir auf diese Art und Weise :). EDIT: Die Ausführung von Thomas ist natürlich sehr viel besser als meine (ich bin ja auch noch ein wenig laienhaft unterwegs auf dem Gebiet). Jedoch hat bei mir das ganze nicht per Interrupt durch USB funktioniert (vielleicht mache ich hier auch etwas falsch) - Bei meiner Lösung stelle ich jedenfalls die Daten nicht in dem nach dem Endpoint definiertem Zeitintervall bereit. Gruß
:
Bearbeitet durch User
Thomas schrieb: > Du must dich von der Vorstellung verabschieden dass dein Device irgend > etwas selbstständig macht dem ist nicht so. Dein Device regiert nur auf > Anfrage des Hosts. Du stellst deinen Wert bereit und der Host fragt > diesen Wert ab. Das ist mir soweit schon klar. Ich hänge momentan etwas bei der Implementierung selbst. Den Descriptor für den FB Endpoint habe ich soweit zusammengebaut und er enumeriert fehlerfrei. Der vereinfachte, momentane Ablauf (nur USB Audio Transfer): - der Host schickt Audio samples (die MaxPacketSize steht auf 288 Byte, da 2 Kanäle, 48 Khz, 24 Bit) - der Interrupt im Endpoint schiebt die Daten in den Buffer des DMA-Moduls - der Interrupt im DMA-Modul schiebt die Daten ins I²S-Modul/auf den Serializer Die Konvertierung ins 10.10 bzw. 10.14 Format habe ich soweit implementiert und überprüft. So weit, so gut. Nur wo schiebe ich jetzt die Aktion des FB EPs da zwischen rein? Mir scheint folgende Vorgehensweise am sinnvollsten: Wenn ich Daten vom USB empfangen habe (Interrupt), sende ich meinen Feedback Wert in den Endpoint buffer. Dann inkrementiere ich einen Zähler (also in jedem Frame ein Mal). Sobald der Zähler bei dem eingestellten Intervall ist, sende ich wieder einen Feedback Wert und setze den Zähler zurück. Sprich ich mache die Feedback Aktion immer nach dem Empfangen der Audio samples. Ist das so gangbar? Hannes M. schrieb: > Wenn ich die Definitionen korrekt verstanden habe, dann stellst du im > Descriptor deine Aktualisierungsrate ein und der PC erwartet dann in > diesem Intervall die neue Samplerate. Jedoch ist es so, dass wenn keine > Änderung stattgefunden hat, auch keine neue Samplerate gemeldet werden > muss - jedenfalls funktioniert es bei mir auf diese Art und Weise :). Genau so ist es (beides). - Der FB EP ist ein isochroner IN Endpoint mit festem Intervall (siehe USB Spec Rev. 2.0, Kapitel 5.12.4.1, Seite 72, Table 5.12). - Das Feedback wird nur gebraucht, wenn sich etwas geändert hat (am Anfang reicht also theoretisch zum Testen eine Übertragung von Ff): "The endpoint may choose to report Ff only if the updated value has changed from the previous Ff value." (USB Spec Rev. 2.0, Seite 75).
:
Bearbeitet durch User
Meiner Meinung nach müsste es theoretisch so funktionieren. Du könntest aber alternativ auch den USB SOF Interrupt für das zählen benutzen und dann die Daten bereitstellen. So wärst du etwas unabhängig von dem Audio EP selbst.
Markus G. schrieb: > Mir scheint folgende Vorgehensweise am sinnvollsten: > Wenn ich Daten vom USB empfangen habe (Interrupt), sende ich meinen > Feedback Wert in den Endpoint buffer. Dann inkrementiere ich einen > Zähler (also in jedem Frame ein Mal). Sobald der Zähler bei dem > eingestellten Intervall ist, sende ich wieder einen Feedback Wert und > setze den Zähler zurück. > Sprich ich mache die Feedback Aktion immer nach dem Empfangen der Audio > samples. Ist das so gangbar? Ja, aber aufwändiger als nötig. Der Feedback-Wert ist idempotent, d.h., es macht überhaupt nichts, wenn der selbe Wert mehrmals an den Host übermittelt wird. Wenn du einen neuen Feedback-Wert hast, schreibst du ihn in den Endpoint-Buffer. Unabhängig davon, wenn dir der Interrupt mitteilt, dass der Endpoint-Buffer vom Host gelesen wurde, sendest du ihn erneut. (Der Host kontrolliert selbst, wann er das Paket wirklich abfragt; in der Firmware musst du dich um das Intervall nicht kümmern.)
Markus G. schrieb: > - der Host schickt Audio samples (die MaxPacketSize steht auf 288 Byte, > da 2 Kanäle, 48 Khz, 24 Bit) das ist zu wenig probier mal 304 Bytes. Ev ist das sogar dein Hauptproblem. Ich versuchs noch mal mit anderen Worten (Nur playback 48k/24 Bit) Der Host stellt x Bytes in den ISO EP. Die Anzahl ist indirekt die Samplerate. Bei genau genau 48k bekommst du 288 Bytes es kann aber auch mehr (zu langsam) oder weniger (zu schnell) sein. Du kommulierst die tatsächlich empangenen Bytes, rechnest das um und stellst den Wert in den Sync EP. Nachdem der Polling Intervall abgelaufen ist wird der Host den Wert abholen. Was und ob etwas damit gemacht wird hast du nicht im Zugriff das ist dann Sache von usbaudio.sys bzw usbaudio2.sys. Natürlich muss für den Sync EP das Toggle Bit korrekt gestellt sein falls deine HW das nicht schon automatisch macht. Ich habe damals den TUSB3200 verwendet und einfach mit dem SOF den DMA Clock verstellt ganz ohne Sync. Das sollte es schon gewesen sein, komplizierter wird es erst wenn unterschiedliche SRs in Spiel kommen. Thomas
Clemens L. schrieb: > Der Feedback-Wert ist idempotent, d.h., es macht überhaupt nichts, wenn > der selbe Wert mehrmals an den Host übermittelt wird. > > Wenn du einen neuen Feedback-Wert hast, schreibst du ihn in den > Endpoint-Buffer. Das ist schonmal eine interessante Info. Thomas schrieb: > Markus G. schrieb: >> - der Host schickt Audio samples (die MaxPacketSize steht auf 288 Byte, >> da 2 Kanäle, 48 Khz, 24 Bit) > > das ist zu wenig probier mal 304 Bytes. Ev ist das sogar dein > Hauptproblem. > > Ich versuchs noch mal mit anderen Worten (Nur playback 48k/24 Bit) Den Buffer habe ich jetzt vergrößert. Macht natürlich auch Sinn Platz für eventuell zu viel übermittelte Samples zu haben. Die Vorgehensweise ist mir ansich mittlerweile durchaus klar. Momentan scheitert es wirklich eher an der reinen Implementierung. Sprich wie und mit welchen Routinen muss ich den IN Endpoint mit Daten bedienen. Da muss ich jetzt wieder etwas tiefer ins System eindringen. Die USB Audio Geschichte lag zwischendrin einige Monate brach. Meine Endpoints habe ich so konfiguriert (ausgelesen mit USBlyzer): OUT Endpoint: Endpoint Descriptor 02 2 Out, Isochronous, 1 ms Offset Field Size Value Description 0 bLength 1 09h 1 bDescriptorType 1 05h Endpoint 2 bEndpointAddress 1 02h 2 Out 3 bmAttributes 1 05h Isochronous, Asynchronous, Data 1..0: Transfer Type ......01 Isochronous 3..2: Sync Type ....01.. Asynchronous 5..4: Usage Type ..00.... Data 7..6: Reserved 00...... 4 wMaxPacketSize 2 0120h 288 bytes 6 bInterval 1 01h 1 ms 7 bRefresh 1 00h 8 bSynchAddress 1 82h IN Endpoint (gekürzt): 2 bEndpointAddress 1 82h 2 Out 3 bmAttributes 1 05h Isochronous, Asynchronous, No Sync, Feedback 4 wMaxPacketSize 2 0003h 3 bytes 6 bInterval 1 04h 8 ms Das müsste denke ich laut Spec so passen. Es wird zumindest in Windows korrekt erkannt, dass der IN Endpoint mit seiner Adresse als Sync Endpoint zum OUT Endpoint 2 gehört und bekommt entsprechend die gleiche Nummer. Jetzt ist mir allerdings etwas aufgefallen in der USB Application Note zum uC: http://ww1.microchip.com/downloads/en/AppNotes/Atmel-42261-SAM-D21-USB_Application-Note_AT06475.pdf Auf Seite 6 wird das Thema Feedback Endpoint thematisiert. Dort steht allerdings der Endpoint als Interrupt Typ. Allerdings kann ich mir nicht vorstellen, dass der Feedback Endpoint aus Sicht des Controllers nur als Interrupt Typ funktionieren würde, da isochrone Data IN Endpoints (also Audio-Aufnahmeseite) ja auch funktionieren. Den Feedback Wert sende ich im 10.14 Format. Die drei Bytes übergebe ich als LSB first an den Endpoint buffer. Müsste so passen oder? Beste Grüße Markus
:
Bearbeitet durch User
Hannes M. schrieb: > Mein Projekt ist zwar auch noch nicht wirklich lauffähig, aber ich kann > dir zumindest bestätigen, dass (soweit ich herausgefunden habe) ab Win > XP die Sync Endpoints funktionieren sollen (in XP wohl nur rudimentär, > ab Win7 aber halbwegs brauchbar). Auf meinem Evaluation Board habe ich > das ganze auch schon lauffähig hinbekommen und muss es nun noch auf > meine eigene Platine übertragen. Hallo Hannes, melde mich nochmals. Habe nun meine Routine, um den FB Endpoint zu bedienen implementiert. Die Funktion habe ich als Callback an das SOF-Event geheftet, im eingestellten Polling Intervall im Descriptor sende ich dann die FB Daten in den Endpoint. Ich toggle zusätzlich einen GPIO, mit dem Oszi kann ich dann nachvollziehen, ob das so läuft. Die Funktion ansich funktioniert, die toggles kommen im richtigen Intervall. Der Controller hängt aber in der Routine fest, sobald ich Audio abspielen will. Ich habe nur den MiniDSP Streamer als Vergleichsdevice für die Descriptors, dieser nutzt aber UAC2. Soweit ich weiß, sind die Deskriptoren von UAC1 zu UAC2 nicht übertragbar?! Entsprechend würde ich gerne erst kontrollieren, ob die Deskriptoren für UAC1 passen. Könntest du mal deine Konfiguration der Deskriptoren posten? Beste Grüße Markus
Markus G. schrieb: > . Die Funktion habe ich als Callback an das SOF-Event geheftet, OK also 1mal pro ms wird deine Berechnung angeworfen.... Den startwert hast du beim Set SampleRate request eingestellt(deine 288Bytes)? Du stellst bei jedem SOF einen neuen passendenden Wert ein sobald dein alternate Setting eingestellt ist. Das Schema in Samples ist etwa (alter Wert + neuer Wert ) <<1. Das Ergebnis musst du dann noch in das 10.14 Format konvertieren und in den Sync EP Puffer schreiben. Die Polling Rate interessiert dich nicht wenn der Host will wird er den EP abfragen und die richtige SR bekommen. Nochmal Solange dein alternate Setting auf 0 steht machst du gar nichts Wenn den alternate auf fs48/24 gestellt wird wartet du auf den SR request Im SR request gibst du deinen Startwert vor 288 Bytes. Ab diesem Zeitpunkt in jedem SOF den Wert berechnen und Bereitstellen. In keinem Fall sendest du was an den Host der holt es ab wenn er es braucht. Thomas
Vergessen zu sagen: Es ist sinnvoll anstelle des Orignialwerts das 256fache für die Berechnung zu verwenden damit sich Rundungsfehler nicht so schnell im Ergebniss niederschlagen. Rechenleistung dürfte ja weniger ein Problem sein. Als Buffersize würde ich 296 oder 302 Vorschlägen. Das reicht dann für 49k bzw 50k. Thomas
Thomas schrieb: > Den startwert hast du beim Set SampleRate request eingestellt(deine > 288Bytes)? Gute Frage :) Also das ganze Projekt ist im Prinzip aus drei Quellen zusammengenagelt: USB Treiber aus dem ASF für den SAMD21, USB Audio Treiber vom SAM3X und USB Audio Class 1 vom AVR32. Das ließ sich alles noch "relativ" gut ohne größere Reibungsverluste adaptieren, weil der Code großteils portierbar ist. > Du stellst bei jedem SOF einen neuen passenden Wert ein sobald dein > alternate Setting eingestellt ist. Momentan will ich noch nicht korrigieren. Ich will den Status Quo von "Audio funktioniert - ohne sync" zu "Audio funktioniert - ohne sync - mit Feedback Endpoint" bringen. Sprich ein Testsetup, um sicherzustellen, dass der FB EP funktioniert. Dazu will ich erstmal fix 48 kHz anfordern. Dazu habe ich meine Funktion an den SOF Interrupt gehängt und habe dort einen Zähler, der mir anzeigt, wann das polling interval rum ist und ich einen neuen Wert liefern muss. Das kann ich jetzt natürlich rausnehmen und den Wert in jedem Frame in den Buffer des FB EP schreiben. Wie man sieht, das ist mein erstes Projekt mit USB und entsprechend fische ich noch bei Vielem im Trüben. > Nochmal > Solange dein alternate Setting auf 0 steht machst du gar nichts > Wenn den alternate auf fs48/24 gestellt wird wartet du auf den SR > request > Im SR request gibst du deinen Startwert vor 288 Bytes. > Ab diesem Zeitpunkt in jedem SOF den Wert berechnen und Bereitstellen. Wo sehe ich das alternate Setting, bzw. was macht das Ding? Gruß Markus
:
Bearbeitet durch User
Markus G. schrieb: > Wo sehe ich das alternate Setting, bzw. was macht das Ding? Ok dann fehlen noch ein paar Grundlagen .... Das Alternate Setting wird mit std request SetInterface vorgegeben Solange keine Streaming pipe offen ist steht das auf 0. Für jede Samplerate die du unterstützt gibt es ein alternate Setting. Das alternate kommt dabei auf wValueLo. Du bekommst also jedes mal wenn eine App was abspielen will den entsprechenden SetInterface request. Die Alternate Setting sind Teil der Deskriptoren. Vielleicht solltest du mal die Ausgaben von UsbView hier einstellen damit wir sehen können welche Deskriptoren du hast. Ich bin bisher davon ausgegangen dass du USB grunzätzlich klar ist und du nur der sync das Problem ist. Thomas
im Anhang mal ein Satz Deskriptoren alle UAC1 allerdings ohne Sync EPs Das ganze ist Teil eines MultiConfig Devices das heist ein Treiber (in diesem Fall ein Asio Treiber wählt genau eine Config. Deshalb ist der Deskriptor Satz 4 mal da. Vieleicht hilft das ja zum Verständniss. Ich bin gerne bereit Fragen zu den Deskriptoren zu beantworten werde aber nichts zum Device oder Treiber sagen. Einige hier werden sowieso wissen, um was für ein Device es sich handelt Thomas
Thomas schrieb: > Markus G. schrieb: >> Wo sehe ich das alternate Setting, bzw. was macht das Ding? > > Ok dann fehlen noch ein paar Grundlagen .... > Das Alternate Setting wird mit std request SetInterface vorgegeben > Solange keine Streaming pipe offen ist steht das auf 0. Für jede > Samplerate die du unterstützt gibt es ein alternate Setting. Das > alternate kommt dabei auf wValueLo. > Du bekommst also jedes mal wenn eine App was abspielen will den > entsprechenden SetInterface request. > Die Alternate Setting sind Teil der Deskriptoren. Okay das kann ich soweit nachvollziehen. Da ich am Ausgang unter anderem einen DSP ohne ASRC anschließen will, muss die sample rate ohnehin immer fix auf 48 kHz liegen. Entsprechend habe ich auch im Deskriptor nur 48 kHz bei 24 Bit zugelassen. Alles andere Material, das evtl. kommen könnte, muss dann seitens Windows erst durch den sample rate converter. > Vielleicht solltest du mal die Ausgaben von UsbView hier einstellen > damit wir sehen können welche Deskriptoren du hast. Ich bin bisher davon > ausgegangen dass du USB grundsätzlich klar ist und du nur der sync das > Problem ist. Den Sync ansich habe ich soweit durchdrungen, es scheitert grade an der Umsetzung. Bei den Deskriptoren bin ich mir auch nicht sicher. Hier die Ausgabe von USBView (kann man hier eigentlich sinnvoll Code z.B. in einem Spoiler posten?): [Port1] : USB-Verbundgerät Is Port User Connectable: yes Is Port Debug Capable: no Companion Port Number: 11 Companion Hub Symbolic Link Name: USB#ROOT_HUB30#4&34571c9a&0&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8} Protocols Supported: USB 1.1: yes USB 2.0: yes USB 3.0: no Device Power State: PowerDeviceD0 ---===>Device Information<===--- English product name: "USB-I2S-Interface" ConnectionStatus: Current Config Value: 0x01 -> Device Bus Speed: Full (is not SuperSpeed or higher capable) Device Address: 0x16 Open Pipes: 0 *!*ERROR: No open pipes! ===>Device Descriptor<=== bLength: 0x12 bDescriptorType: 0x01 bcdUSB: 0x0200 bDeviceClass: 0x00 -> This is an Interface Class Defined Device bDeviceSubClass: 0x00 bDeviceProtocol: 0x00 bMaxPacketSize0: 0x40 = (64) Bytes idVendor: 0x03EB = Atmel Corporation idProduct: 0x2423 bcdDevice: 0x0100 iManufacturer: 0x01 English (United States) "Test_Manufacturer" iProduct: 0x02 English (United States) "USB-I2S-Interface" iSerialNumber: 0x03 English (United States) "v0.0.0.0.1" bNumConfigurations: 0x01 ---===>Full Configuration Descriptor<===--- ===>Configuration Descriptor<=== bLength: 0x09 bDescriptorType: 0x02 wTotalLength: 0x006D *!*ERROR: Invalid total configuration size 0x6D, should be 0x6B bNumInterfaces: 0x02 bConfigurationValue: 0x01 iConfiguration: 0x00 bmAttributes: 0x80 -> Bus Powered MaxPower: 0x32 = 100 mA ===>Interface Descriptor<=== bLength: 0x09 bDescriptorType: 0x04 bInterfaceNumber: 0x00 bAlternateSetting: 0x00 bNumEndpoints: 0x00 bInterfaceClass: 0x01 -> Audio Interface Class bInterfaceSubClass: 0x01 -> Audio Control Interface SubClass bInterfaceProtocol: 0x00 iInterface: 0x00 ===>Audio Control Interface Header Descriptor<=== bLength: 0x09 bDescriptorType: 0x24 (CS_INTERFACE) bDescriptorSubtype: 0x01 (HEADER) bcdADC: 0x0100 wTotalLength: 0x001E bInCollection: 0x01 baInterfaceNr[1]: 0x01 ===>Audio Control Input Terminal Descriptor<=== bLength: 0x0C bDescriptorType: 0x24 (CS_INTERFACE) bDescriptorSubtype: 0x02 (INPUT_TERMINAL) bTerminalID: 0x01 wTerminalType: 0x0101 (USB streaming) bAssocTerminal: 0x00 bNrChannels: 0x02 wChannelConfig: 0x0003 (Left Front (L)) (Right Ront (R)) iChannelNames: 0x00 iTerminal: 0x00 ===>Audio Control Output Terminal Descriptor<=== bLength: 0x09 bDescriptorType: 0x24 (CS_INTERFACE) bDescriptorSubtype: 0x03 (OUTPUT_TERMINAL) bTerminalID: 0x02 wTerminalType: 0x0301 (Speaker) bAssocTerminal: 0x00 bSourceID: 0x01 iTerminal: 0x00 ===>Interface Descriptor<=== bLength: 0x09 bDescriptorType: 0x04 bInterfaceNumber: 0x01 bAlternateSetting: 0x00 bNumEndpoints: 0x00 bInterfaceClass: 0x01 -> Audio Interface Class bInterfaceSubClass: 0x02 -> Audio Streaming Interface SubClass bInterfaceProtocol: 0x00 iInterface: 0x00 ===>Interface Descriptor<=== bLength: 0x09 bDescriptorType: 0x04 bInterfaceNumber: 0x01 bAlternateSetting: 0x01 bNumEndpoints: 0x02 bInterfaceClass: 0x01 -> Audio Interface Class bInterfaceSubClass: 0x02 -> Audio Streaming Interface SubClass bInterfaceProtocol: 0x00 iInterface: 0x00 ===>Audio Streaming Class Specific Interface Descriptor<=== bLength: 0x07 bDescriptorType: 0x24 (CS_INTERFACE) bDescriptorSubtype: 0x01 (AS_GENERAL) bTerminalLink: 0x01 bDelay: 0x01 wFormatTag: 0x0001 (PCM) ===>Audio Streaming Format Type Descriptor<=== bLength: 0x0B bDescriptorType: 0x24 (CS_INTERFACE) bDescriptorSubtype: 0x02 (FORMAT_TYPE) bFormatType: 0x01 (FORMAT_TYPE_I) bNrChannels: 0x02 bSubframeSize: 0x03 bBitResolution: 0x18 (24) bSamFreqType: 0x01 (Discrete) tSamFreq[1]: 0x00BB80 (48000 Hz) ===>Endpoint Descriptor<=== bLength: 0x09 bDescriptorType: 0x05 bEndpointAddress: 0x02 -> Direction: OUT - EndpointID: 2 bmAttributes: 0x05 -> Isochronous Transfer Type, Synchronization Type = Asynchronous, Usage Type = Data Endpoint wMaxPacketSize: 0x0130 = 0x130 bytes wInterval: 0x0001 bSyncAddress: 0x82 ===>Audio Streaming Class Specific Audio Data Endpoint Descriptor<=== bLength: 0x07 bDescriptorType: 0x25 (CS_ENDPOINT) bDescriptorSubtype: 0x01 (EP_GENERAL) bmAttributes: 0x80 (MaxPacketsOnly) bLockDelayUnits: 0x00 (Undefined) wLockDelay: 0x0000 ===>Endpoint Descriptor<=== bLength: 0x07 bDescriptorType: 0x05 bEndpointAddress: 0x82 -> Direction: IN - EndpointID: 2 bmAttributes: 0x11 -> Isochronous Transfer Type, Synchronization Type = No Synchronization, Usage Type = Feedback Endpoint wMaxPacketSize: 0x0003 = 0x03 bytes bInterval: 0x04 ===>Descriptor Hex Dump<=== bLength: 0x00 bDescriptorType: 0x00
Deine Deskriptoren sehen erst mal gut aus (2 Bytes zu lang aber das ist sicher nicht das Problem. Der As EP desc für den Strem EP sieht allerdings etwas seltsam aus. Meine sind da anders. Thomas
Thomas schrieb: > Deine Deskriptoren sehen erst mal gut aus (2 Bytes zu lang aber > das ist > sicher nicht das Problem. Der As EP desc für den Strem EP sieht > allerdings etwas seltsam aus. Meine sind da anders. > > Thomas Du meinst den vorletzten Endpoint? Sprich: ===>Audio Streaming Class Specific Audio Data Endpoint Descriptor<=== bLength: 0x07 bDescriptorType: 0x25 (CS_ENDPOINT) bDescriptorSubtype: 0x01 (EP_GENERAL) bmAttributes: 0x80 (MaxPacketsOnly) bLockDelayUnits: 0x00 (Undefined) wLockDelay: 0x0000 Ich tue mich ehrlich gesagt etwas schwer, bei dir das entsprechende Pendant zu finden. Gruß Markus
Such einfach nach AS_GENERAL bmAttribute steht bei mir auf 1 SR Request bLockDelayUnits auf 1 ms wLockdelay auf 0x01, 0x00 Das sind Unterschiede die mir aufgefallen sind. Attribute 0x80 kenn ich nicht was soll das sein? Mit 0x01 bekommst du auf jedenfall den SR Request Thomas
Hallo Markus, ich sehe das genauso wie Thomas -> Attribute 0x80 finde ich auch seltsam hier mein funktionierender Descriptor von meinem EVAL Board: ---------------------- Device Descriptor ---------------------- bLength : 0x12 (18 bytes) bDescriptorType : 0x01 (Device Descriptor) bcdUSB : 0x200 (USB Version 2.00) bDeviceClass : 0x00 (defined by the interface descriptors) bDeviceSubClass : 0x00 bDeviceProtocol : 0x00 bMaxPacketSize0 : 0x40 (64 bytes) idVendor : 0x0483 (STMicroelectronics) idProduct : 0x5741 bcdDevice : 0x0200 iManufacturer : 0x01 (String Descriptor 1) Language 0x0409 : "STMicroelectronics" iProduct : 0x02 (String Descriptor 2) Language 0x0409 : "STM32 Audio Class" iSerialNumber : 0x03 (String Descriptor 3) Language 0x0409 : "00000000001A" bNumConfigurations : 0x01 (1 Configuration) Data (HexDump) : 12 01 00 02 00 00 00 40 83 04 41 57 00 02 01 02 .......@..AW.... 03 01 .. ------------------ Configuration Descriptor ------------------- bLength : 0x09 (9 bytes) bDescriptorType : 0x02 (Configuration Descriptor) wTotalLength : 0x0076 (118 bytes) bNumInterfaces : 0x02 (2 Interfaces) bConfigurationValue : 0x01 (Configuration 1) iConfiguration : 0x00 (No String Descriptor) bmAttributes : 0xC0 D7: Reserved, set 1 : 0x01 D6: Self Powered : 0x01 (yes) D5: Remote Wakeup : 0x00 (no) D4..0: Reserved, set 0 : 0x00 MaxPower : 0x32 (100 mA) Data (HexDump) : 09 02 76 00 02 01 00 C0 32 09 04 00 00 00 01 01 ..v.....2....... 00 00 09 24 01 00 01 27 00 01 01 0C 24 02 01 01 ...$...'....$... 01 00 02 03 00 00 00 09 24 06 02 01 01 01 00 00 ........$....... 09 24 03 03 01 03 00 02 00 09 04 01 00 00 01 02 .$.............. 00 00 09 04 01 01 02 01 02 00 00 07 24 01 01 01 ............$... 01 00 0B 24 02 01 02 02 10 01 80 BB 00 09 05 01 ...$............ 05 C4 00 01 00 81 07 25 01 00 00 00 00 09 05 81 .......%........ 11 03 00 01 02 00 ...... ---------------- Interface Descriptor ----------------- bLength : 0x09 (9 bytes) bDescriptorType : 0x04 (Interface Descriptor) bInterfaceNumber : 0x00 bAlternateSetting : 0x00 bNumEndpoints : 0x00 (Default Control Pipe only) bInterfaceClass : 0x01 (Audio) bInterfaceSubClass : 0x01 (Audio Control) bInterfaceProtocol : 0x00 iInterface : 0x00 (No String Descriptor) Data (HexDump) : 09 04 00 00 00 01 01 00 00 ......... ------ Audio Control Interface Header Descriptor ------ bLength : 0x09 (9 bytes) bDescriptorType : 0x24 (Audio Interface Descriptor) bDescriptorSubtype : 0x01 (Header) bcdADC : 0x0100 wTotalLength : 0x0027 (39 bytes) bInCollection : 0x01 baInterfaceNr[1] : 0x01 Data (HexDump) : 09 24 01 00 01 27 00 01 01 .$...'... ------- Audio Control Input Terminal Descriptor ------- bLength : 0x0C (12 bytes) bDescriptorType : 0x24 (Audio Interface Descriptor) bDescriptorSubtype : 0x02 (Input Terminal) bTerminalID : 0x01 wTerminalType : 0x0101 (USB streaming) bAssocTerminal : 0x00 bNrChannels : 0x02 (2 channels) wChannelConfig : 0x0003 (L, R) iChannelNames : 0x00 (No String Descriptor) iTerminal : 0x00 (No String Descriptor) Data (HexDump) : 0C 24 02 01 01 01 00 02 03 00 00 00 .$.......... -------- Audio Control Feature Unit Descriptor -------- bLength : 0x09 (9 bytes) bDescriptorType : 0x24 (Audio Interface Descriptor) bDescriptorSubtype : 0x06 (Feature Unit) bUnitID : 0x02 (2) bSourceID : 0x01 (1) bControlSize : 0x01 (1 byte per control) bmaControls[0] : 0x01 D0: Mute : 1 D1: Volume : 0 D2: Bass : 0 D3: Mid : 0 D4: Treble : 0 D5: Graphic Equalizer : 0 D6: Automatic Gain : 0 D7: Delay : 0 bmaControls[1] : 0x00 D0: Mute : 0 D1: Volume : 0 D2: Bass : 0 D3: Mid : 0 D4: Treble : 0 D5: Graphic Equalizer : 0 D6: Automatic Gain : 0 D7: Delay : 0 iFeature : 0x00 (No String Descriptor) Data (HexDump) : 09 24 06 02 01 01 01 00 00 .$....... ------- Audio Control Output Terminal Descriptor ------ bLength : 0x09 (9 bytes) bDescriptorType : 0x24 (Audio Interface Descriptor) bDescriptorSubtype : 0x03 (Output Terminal) bTerminalID : 0x03 wTerminalType : 0x0301 (Speaker) bAssocTerminal : 0x00 (0) bSourceID : 0x02 (2) iTerminal : 0x00 (No String Descriptor) Data (HexDump) : 09 24 03 03 01 03 00 02 00 .$....... ---------------- Interface Descriptor ----------------- bLength : 0x09 (9 bytes) bDescriptorType : 0x04 (Interface Descriptor) bInterfaceNumber : 0x01 bAlternateSetting : 0x00 bNumEndpoints : 0x00 (Default Control Pipe only) bInterfaceClass : 0x01 (Audio) bInterfaceSubClass : 0x02 (Audio Streaming) bInterfaceProtocol : 0x00 iInterface : 0x00 (No String Descriptor) Data (HexDump) : 09 04 01 00 00 01 02 00 00 ......... ---------------- Interface Descriptor ----------------- bLength : 0x09 (9 bytes) bDescriptorType : 0x04 (Interface Descriptor) bInterfaceNumber : 0x01 bAlternateSetting : 0x01 bNumEndpoints : 0x02 (2 Endpoints) bInterfaceClass : 0x01 (Audio) bInterfaceSubClass : 0x02 (Audio Streaming) bInterfaceProtocol : 0x00 iInterface : 0x00 (No String Descriptor) Data (HexDump) : 09 04 01 01 02 01 02 00 00 ......... -------- Audio Streaming Interface Descriptor --------- bLength : 0x07 (7 bytes) bDescriptorType : 0x24 (Audio Interface Descriptor) bDescriptorSubtype : 0x01 bTerminalLink : 0x01 bDelay : 0x01 wFormatTag : 0x0001 (PCM) Data (HexDump) : 07 24 01 01 01 01 00 .$..... ------- Audio Streaming Format Type Descriptor -------- bLength : 0x0B (11 bytes) bDescriptorType : 0x24 (Audio Interface Descriptor) bDescriptorSubtype : 0x02 (Format Type) bFormatType : 0x01 (FORMAT_TYPE_I) bNrChannels : 0x02 (2 channels) bSubframeSize : 0x02 (2 bytes per subframe) bBitResolution : 0x10 (16 bits per sample) bSamFreqType : 0x01 (supports 1 sample frequence) tSamFreq[1] : 0x0BB80 (48000 Hz) Data (HexDump) : 0B 24 02 01 02 02 10 01 80 BB 00 .$......... ----------------- Endpoint Descriptor ----------------- bLength : 0x09 (9 bytes) bDescriptorType : 0x05 (Endpoint Descriptor) bEndpointAddress : 0x01 (Direction=OUT EndpointID=1) bmAttributes : 0x05 (TransferType=Isochronous SyncType=Asynchronous EndpointType=Data) wMaxPacketSize : 0x00C4 (196 bytes) bInterval : 0x01 (1 ms) bRefresh : 0x00 bSynchAddress : 0x81 (Direction=IN EndpointID=1) Data (HexDump) : 09 05 01 05 C4 00 01 00 81 ......... ----------- Audio Data Endpoint Descriptor ------------ bLength : 0x07 (7 bytes) bDescriptorType : 0x25 (Audio Endpoint Descriptor) bDescriptorSubtype : 0x01 (General) bmAttributes : 0x00 bLockDelayUnits : 0x00 wLockDelay : 0x0000 Data (HexDump) : 07 25 01 00 00 00 00 .%..... ----------------- Endpoint Descriptor ----------------- bLength : 0x09 (9 bytes) bDescriptorType : 0x05 (Endpoint Descriptor) bEndpointAddress : 0x81 (Direction=IN EndpointID=1) bmAttributes : 0x11 (TransferType=Isochronous SyncType=None EndpointType=Feedback) wMaxPacketSize : 0x0003 (3 bytes) bInterval : 0x01 (1 ms) bRefresh : 0x02 (4 ms) bSynchAddress : 0x00 Data (HexDump) : 09 05 81 11 03 00 01 02 00 .........
Thomas schrieb: > Such einfach nach AS_GENERAL > bmAttribute steht bei mir auf 1 SR Request > bLockDelayUnits auf 1 ms > wLockdelay auf 0x01, 0x00 > > Das sind Unterschiede die mir aufgefallen sind. Attribute 0x80 kenn ich > nicht was soll das sein? Mit 0x01 bekommst du auf jedenfall den SR > Request > Thomas Das war mir noch nicht aufgefallen. Die Basiskonfiguration hatte ich von einem Example übernommen und nicht jeden Eintrag explizit geprüft nachdem das Audio Playback im Grunde funktionierte. Habe das Thema jetzt mal in der Spec nachgelesen: https://www.usb.org/sites/default/files/audio10.pdf Auf Seite 62 ist der entsprechende Endpoint beschrieben. 0x80 (Bit 7 auf 1) entspricht also der Option wMaxPacketSize. Dabei werden dann kürzere Pakete immer auf wMaxPacketSize mit Nullen gepadded. Das Thema Requests hatte ich bisweilen nur peripher auf dem Schirm, da ich ohnehin eine feste SR nutzen will. Deswegen bin ich davon ausgegangen, das bis auf weiteres außen vor lassen zu können. Soweit ich das bisher verstanden habe, kann man mit den Requests im Betrieb Parameter ändern. Z.B. die SR kann so im Betrieb verstellt werden, sofern das Device das unterstützt (mit DACs die als Clock-Slave laufen in der Regel kein Problem). So muss dann Windows-seitig nicht interpoliert werden. Mit Bit 0 kann ich wie du sagtest den Sample Rate Request einstellen. Bei Hannes ist das ausgeschaltet (Bit 0 ist 0). So weit ich das verstehe müsste das bei fixer SR auch so passen. Oder? Die Optionen bLockDelayUnits and wLockDelay müssen beim asynchronen Endpoint sowieso auf 0 stehen (siehe Absatz über Tabelle 4-21). Entscheidend dürfte das in Hinblick auf den Feedback Endpoint aber alles nicht sein. Ich vermute die Deskriptoren passen jetzt. Dann liegt es noch an der reinen Implementierung die Daten in den Endpoint zu bekommen. Gruß Markus
:
Bearbeitet durch User
Also vielen Dank erstmal für die tolle Unterstützung hier! Durch die letzten Modifikationen an Deskriptoren und weiterem Code kann ich nun mit Feedback Endpoint, allerdings ohne Funktion, zumindest schon mal wieder kurz Audio abspielen. Das ging vorher nicht. Die Wiedergabe ist gar nicht erst angelaufen, sondern hat sofort gestoppt. Wenn ich meine Funktion, um den Endpoint zu bedienen aktiv habe, verhält es sich genau so wie ohne. Beim Abspielen von Audio läuft es ca. 15 Sekunden normal und dann kommt ein Durchgangston oder starke Verzerrungen. Ich gehe davon aus, dass entweder die Daten nicht korrekt im Endpoint ankommen oder nicht aus diesem geladen werden. Ich bin mir gerade nicht sicher, wie ich den errechneten Wert korrekt in den Endpoint buffer schreiben muss. Die Makros die ich verwende sehen folgendermaßen aus (siehe Anhang). Das habe ich im Arduino (ESP32) kurz via Konsolenausgabe getestet. Das resultiert wie erwartet (manuell berechnet) in: 0x0C 0x00 0x00 buf[2] - buf[1] - buf[0] Das Datum ansich müsste korrekt sein. Ich bin gerade nur am Überlegen wie sich die Konstellation ARM=Little Endian und Senden via USB verhält. Sprich wie rum müssen die Daten in den Endpoint buffer? Gruß Markus
:
Bearbeitet durch User
USB ist little endian kann man sehr schön an den Deskriptoren erkennen z.b bei den Samplerates. Ob dein Konvertiermakro so richtig ist kann und will ich nicht beurteilen. Ich bin aber der Meinung dass es nicht so einfach ist. Meiner Meinung müsste das Makro aus 48.5k folgendes in den epbuffer schreiben: Buffer[0] = 0x00 Buffer[1] = 0x80 Buffer[2] = 48 Thomas
Hallo Markus, wie du es geschrieben hast ist die Formatierung für 48KHz korrekt. Buffer[0] = 0x00; Buffer[1] = 0x00; Buffer[2] = 0x0C; Ich habe dann am Ende mit Wireshark den USB Traffic mitgeloggt um zu schauen, dass die Daten korrekt vom Host gelesen werden. In diesem Beispiel sieht das ganze unter Wireshark bei mir so aus (siehe letzte Zeile): Frame 2333: 54 bytes on wire (432 bits), 54 bytes captured (432 bits) on interface 0 USB URB [Source: 3.2.1] [Destination: host] USBPcap pseudoheader length: 51 IRP ID: 0xfffffa8014b314a0 IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000) URB Function: URB_FUNCTION_ISOCH_TRANSFER (0x000a) IRP information: 0x01, Direction: PDO -> FDO URB bus id: 3 Device address: 2 Endpoint: 0x81, Direction: IN URB transfer type: URB_ISOCHRONOUS (0x00) Packet Data Length: 3 [Request in: 2331] [Time from request: 0.003001000 seconds] Isochronous transfer start frame: 3928 Isochronous transfer number of packets: 1 Isochronous transfer error count: 0 USB isochronous packet ISO Data offset: 0x00000000 ISO Data length: 0x00000003 (relevant) ISO USBD status: USBD_STATUS_SUCCESS (0x00000000) (relevant) ISO Data: 00000c Gruß Hannes
:
Bearbeitet durch User
Hallo Hannes, danke für den Tipp mit Wireshark. Die Frage nach einem Sniffer Program wäre meine nächste gewesen. Momentan sieht das bei mir so aus: HOST -> Device -------------------- Frame 10989: 51 bytes on wire (408 bits), 51 bytes captured (408 bits) on interface 0 USB URB [Source: host] [Destination: 2.18.2] USBPcap pseudoheader length: 51 IRP ID: 0xffffc10ceb593900 IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000) URB Function: URB_FUNCTION_ISOCH_TRANSFER (0x000a) IRP information: 0x00, Direction: FDO -> PDO URB bus id: 2 Device address: 18 Endpoint: 0x82, Direction: IN URB transfer type: URB_ISOCHRONOUS (0x00) Packet Data Length: 0 [Response in: 10990] Isochronous transfer start frame: 3304183 Isochronous transfer number of packets: 1 Isochronous transfer error count: 0 USB isochronous packet ISO Data offset: 0x00000000 ISO Data length: 0x00000000 (irrelevant) ISO USBD status: USBD_STATUS_SUCCESS (0x00000000) (irrelevant) Device -> HOST -------------------- Frame 10990: 51 bytes on wire (408 bits), 51 bytes captured (408 bits) on interface 0 USB URB [Source: 2.18.2] [Destination: host] USBPcap pseudoheader length: 51 IRP ID: 0xffffc10ceb593900 IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000) URB Function: URB_FUNCTION_ISOCH_TRANSFER (0x000a) IRP information: 0x01, Direction: PDO -> FDO URB bus id: 2 Device address: 18 Endpoint: 0x82, Direction: IN URB transfer type: URB_ISOCHRONOUS (0x00) Packet Data Length: 0 [Request in: 10989] [Time from request: 0.000953000 seconds] Isochronous transfer start frame: 3304183 Isochronous transfer number of packets: 1 Isochronous transfer error count: 0 USB isochronous packet ISO Data offset: 0x00000000 ISO Data length: 0x00000000 (relevant) ISO USBD status: USBD_STATUS_SUCCESS (0x00000000) (relevant) Die Packet Data Length steht allerdings auf 0, folglich kommen da auch keine Daten. Da ist also noch irgendwo der Wurm drin. Selbst wenn meine Daten nicht im Endpoint Buffer ankommen, müsste die Datenübertragung der 3 Byte mit jeweils dem Inhalt 0 trotzdem funktionieren?! Hat jemand einen Tipp was das sein könnte? Gruß Markus
Auf den meisten USB cores gibt es sowas wie ArmEp(len) Das ist oft nur ein Makro. Schau mal nach wie deine Firmware Deskriptoren bereitstellt. So ähnlich sollte das auf auf anderen EPs funktionieren. Thomas
Hallo nochmal, ich habe mich bei der Implementierung ziemlich festgefahren. Sobald es keine fertige und dokumentierte Klasse für den Anwendungsfall (hier Audio) gibt, wird es vor allem bei Atmel mit dem ASF schwierig. Eine funktionale Implementierung des FB EP als Teil des Interfaces für das Audio Streaming ist mir derweil nicht gelungen. Ich kann die Endpoints im gleichen Interface nicht voneinander separiert ansteuern/auslesen. Was aber lauffähig ist, ist zusätzlich die IN streaming Richtung für Audio. Sprich voll-duplex mit jeweils 2 Kanälen Audio. Nun war meine Idee, einfach das Audio IN Interface für den Feedback Endpoint zu missbrauchen (im Deskriptor entsprechend umkonfiguriert). Normalerweise wird es ja so gehandhabt: 1 Interface mit 2 Endpoints, einer für Audio OUT und einer für FB IN. Wäre es möglich, den FB Endpoint in ein eigenes Interface zu legen? Also Interface 1 ist Audio OUT und Interface 2 ist FB IN. Die Verbindung könnte man mit der SynchAddress machen. Wenn das gehen würde wäre der restliche Aufwand vermutlich überschaubar, da der IN Endpoint für Audio schon funktioniert (und nicht gebraucht wird). Ist das seitens der USB Spec überhaupt möglich? Gruß Markus
Hallo zusammen, ich wundere mich gerade warum hier noch keiner LUFA vorgeschlagen hat: http://www.fourwalledcubicle.com/LUFA.php
Markus G. schrieb: > ich habe mich bei der Implementierung ziemlich festgefahren. Sobald es > keine fertige und dokumentierte Klasse für den Anwendungsfall (hier > Audio) gibt, wird es vor allem bei Atmel mit dem ASF schwierig Nun du hast dir mit Sicherheit kein einfaches Kapitel rausgesucht. Die Audio spec gehört mit zu den komplexesten Specs unter USB. Du kannst also nicht erwarten dass Atmel das alles für dich macht zumal du noch nicht mal an der Oberfläche gekratzt hast. Der wirklich interessante Part kommt noch wenn z.B. OSX oder andere OS ins Spiel kommen. Zusätzlich sind Sync EPs eine Sache für Pro Audio genauso wie TDM. Für das Wissen um diese Dinge lassen sich Entwickler gut bezahlen. Es ist also kein Wunder dass in dem Bereich nicht so wahnsinnig viele Quellen existieren. Ein profundes Wissen wie USB funktioniert ist Voraussetzung. Dazu gehört beispielsweise wie man einen EP scharf schaltet. Das bekommt man raus wenn man sich mal anschaut wie die Kommunikation generell funktioniert. Ob dein Device einen Deskriptor zurückmeldet oder auf einem anderen EP eine 3Byte SR ist abgesehen vom EP und der Länge erst mal das Gleiche. Da musst du dich schon selber reinfrickeln. Ich kann dir nur soviel sagen: Es gibt zig Audiodevices auch im Pro Segment die ohne Sync EPs auskommen. Thomas
Noch ein paar Hinweise: selbst der steinalte UDA 1335 von Philips hat es asynchron vor über 20Jahren hinbekommen, zumindest meistens ein 48k File abzuspielen. Meistens deshalb weil der nur mit Intel USB Chipsets richtig konnte und alle 5 sec ein kurzer Klick zu hören war. TI, CMedia und einige andere beweisen das USB Audio funktioniert. Ich kenne allerdings kein Device mit Atmel. Alle mir bekannten Implementierungen setzen auf dezidierte USB Audio chips oder verwenden potente Cores z.B Cypress ev. in Verbindung mit FPGA und manchmal noch mit DSPs. Ich glaube auch nicht das dein Problem vom nicht funktionierenden Sync EP kommt. Alle Devices die ich bis jetzt gesehen habe unterstützten z.B den SR request. Ich bin mir sicher das ist bei deinem MiniDSP Streamer nicht anders ist. Thomas
LUFA ist doch nur für AVR? Thomas schrieb: > Nun du hast dir mit Sicherheit kein einfaches Kapitel rausgesucht. > Die Audio spec gehört mit zu den komplexesten Specs unter USB. Dass das Vorhaben ambitioniert ist, ist mir durchaus bewusst. Die USB Spec finde ich handlebar, die ist ansich sauber dokumentiert. Es hapert eher auf der Windows-Treiberseite und bei der uC Implementierung selbst. Wenn man da die Standardklassen verlässt, wird das Debugging mühsam. Habe den FB EP jetzt aber zum Laufen gebracht! Es lag im Endeffekt an einer Eigenheit des Controllers. Durch eine Verschiebung von 2 Zeilen Code war das Problem behoben. Jetzt kommen also schonmal Daten aus dem FB EP. Ich habe mich dann auch gleich dran gemacht, den Algorithmus zu implementieren, um die SR im Betrieb nachzustellen. Das funktioniert rudimentär, bedarf aber noch etwas Feintuning (man hört noch periodisch, dass wohl etwas verloren geht). Ich habe mal einige Test über jeweils den Zeitraum von 1 Sekunde (am Anfang der Übertragung) gemacht: Ich bekomme da Samplerates im Bereich von ca. 47,35 bis 47,7 kHz. Ist das normal? In der Spec steht, dass man mindestens 1 Sekunde lang messen sollte, um eine Präzision von 1 Hz oder besser zu erreichen. Allerdings ist doch am Anfang der Audioübertragung mit dem Initalwert 48 kHz die Abweichung tendenziell am größten. Wenn ich dann eine Sekunde warten muss, bis überhaupt begonnen wird nachzuregeln, habe ich am Anfang Verzerrungen. Gruß Markus
Hallo, bin gerade dabei auszutüfteln, wie man die Regelung der Samplerate am besten macht. Gibt es dafür einen Algorithmus der sich anbietet? Ich hatte überlegt, die ersten Samples zur Generierung des Startwerts für die Sampleratekorrektur zu nehmen und dann nach Bedarf nachzustellen. Also z.B. so: Nach 1000 Samples messe ich 47,500 (mit 1000 Samples kann ich zumindest theoretisch eine Auflösung von 1 Hz bekommen) kHz. Dann gebe ich als ersten FB Wert 48,500 kHz an. Wenn der Wert im darauffolgenden Messintervall von 48 kHz abweicht, stelle ich ausgehend von der momentanen Abweichung z.B. in 3 Stufen zu: - Abweichung größer x: vorherigen FB-Wert um 100 Hz nachstellen - Abweichung größer y & kleiner x: FB-Wert +- 10 Hz - Abweichung kleiner y: FB-Wert +- 1 Hz Das wäre natürlich ein sehr einfacher Algorithmus. Kann man bei USB von einer halbwegs konstanten Abweichung ausgehen? Wenn der Clock im Betrieb auch noch hin und her driftet, müsste evtl. ein Algorithmus her, der schneller nachregelt. Gibt es einen Regelalgorithmus der sich dafür anbietet? Gruß Markus
Markus G. schrieb: > In der Spec steht, dass man mindestens 1 Sekunde lang messen sollte, um > eine Präzision von 1 Hz oder besser zu erreichen. Nicht zu schnell aufgeben. Weiter unten steht: > A sink can determine F_f by counting cycles of the master clock F_m for > a period of 2^(K-P) (micro)frames. The counter is read into F_f and reset > every 2^(K-P) (micro)frames.
ich habe viel weiter oben schon beschrieben wie ich das machen würde. Beim SetAlternate oder falls implementiert SetSampleRate würde ich eine Var mit 256* 288 initialisieren. Wenn ein ISO Packet kommt die Anzahl der Bytes * 256 Zum ersten Wert dazu addieren und dann halbieren. Diese Zahl ist die Mittler SampleRate *256. Beachte dabei das 288 der Normwert für 2* 3 * fs48 ist. Den Wert in die ent. FP Zahl umrechnen und im Sync EP bereitstellen Immer wenn ein neues Streampacket da ist einfach wie oben neu berechnen. Der Host wird die Sync Daten abholen und vielleicht was damit machen. Dieses Verfahren setzt voraus dass du irgendwie an die Anzahl Bytes kommst, die gestreamt werden Du brauchst auch keine Sekunde warten das sollte sich automatisch einstellen und wenn du nach ein zwei Sekunden nachschaust sollte genau 48k erscheinen. Thomas
Thomas schrieb: > 256* 288 initialisieren. Wenn ein ISO Packet kommt die Anzahl der Bytes > * 256 Zum ersten Wert dazu addieren und dann halbieren. Diese Zahl ist > die Mittler SampleRate *256. Die empfangenen Bytes kenne ich, da die USB-Funktion die Anzahl an Bytes liefert. Du würdest also die Samplerate über 256 Frames mitteln? > Den Wert in die ent. FP Zahl umrechnen und im Sync EP bereitstellen > Immer wenn ein neues Streampacket da ist einfach wie oben neu berechnen. So wie ich dich verstehe, willst du den momentan tatsächlichen Wert der Samplerate (in der Regel weniger als 48 kHz) zurückgeben? Die Spec verstehe ich aber so, dass der korrigierte Wert zurückgegeben werden muss: "The information carried over the synch path consists of a 3-byte data packet. These three bytes contain the Ff value in a 10.14 format as described in Section 5.10.4.2, “Feedback” of the USB Specification. Ff represents the average number of samples the endpoint must produce or consume per frame to match the desired sampling frequency Fs exactly." (USB Audio Device Class Definition 1) Und: "An asynchronous sink must provide explicit feedback to the host by indicating accurately what its desired data rate (Ff) is, relative to the USB (micro)frame frequency. This allows the host to continuously adjust the number of samples sent to the sink so that neither underflow or overflow of the data buffer occurs." (USB 2.0 Specification) Es ist die Rede von "desired data rate". Das heißt für mich: Wenn der Host nur mit z.B. 47,5 kHz schickt, brauche ich einen korrigierten Feedback-Wert, z.B. 48,5 kHz, damit am Ende genau 48 kHz resultiert!? Gruß Markus
:
Bearbeitet durch User
Markus G. schrieb: > Du würdest also die Samplerate über 256 Frames mitteln? Nein das ist ein willkürlicher Faktor damit nicht so schnell Rundungsfehler entstehen. Du bekommst vom Host ja 288 Bytes +- 6 Bytes oder ev +- 12 Bytes. Bei 12Bytes würde die SR um max 2Hz schwanken. Es wird aber vermutlich nur um 1HZ schwanken. diese Zahlen must du irgendwie kommulierenen und dann als FP Zahl bereitstellen. Wie das genau auszusehen hat ist m.M. nicht 100% genau beschrieben. Ich würde wahrscheinlich alle 16 ms einen neuen Wert bereitstellen. Der Wert könnte meines Erachtens die Differenz oder auch die tatsächliche Rate sein. Das kommt auf einen Versuch an. Wie gesagt das ist etwas spekulativ ich sowas nie gemacht. Thomas
Ich hab mehrere Wochen auf einem STM32 mit Host lib Gebastelt und eine UVC class geschrieben. Ende vom lied war das die CPU last zu hoch war und der Stack mit der gefrickelten RTOS Implementierung gekrankt hat. Aber das nochmal neu zu bauen... War mir auch zu viel Schmerz... Moral der Geschichte.. Die libs und stacks dieser Welt sind auch nicht immer Allheilmittel. Und Versuch Mal was für USB Host mit UVC class zu finden.. Ist eben so ein Thema wie audio.. Irgendwie scheint es zu gehen, aber wenn man was detailliert möchte ... Sucht man sich einen Wolf
Markus G. schrieb: > Es ist die Rede von "desired data rate". Das heißt für mich: > Wenn der Host nur mit z.B. 47,5 kHz schickt, brauche ich einen > korrigierten Feedback-Wert, z.B. 48,5 kHz, damit am Ende genau 48 kHz > resultiert!? Nicht unbedingt. Der primäre Zweck des Feedbacks ist es, die unterschiedlichen Frequenzen von Host und Device auszugleichen. Da beide getrennte Quarzkristalle verwenden, wird die I²S-Hardware im Gerät die Samples nicht mit der exakten Frequenz aus dem internen Buffer lesen, wie sie der Host dort hinein schreibt. Die Aufgabe deiner Firmware ist es, zu messen, wie viele Samples deine Hardware in jedem USB-Frame verarbeitet; das hat erst mal nichts damit zu tun, wie viele Samples der Host wirklich sendet. (Wenn du dazu die Master Clock vor dem Herunterdividieren verwendest, geht das schneller und genauer; siehe oben.) Falls der Host den Feedback-Wert zu stark rundet, oder nicht schnell genug verarbeitet, dann kann es passieren, dass sich Fehler aufaddieren, und dass die Host-Sample-Rate dauerhaft von deiner abweicht. In diesem Fall wird dein interner Buffer irgendwann zu wenig oder zu viele Samples enthalten, und du solltest den Feedback-Wert kurz anpassen. Das ist aber erst mal nicht so wichtig.
Hallo zusammen, habe am Wochenende noch diverse Änderungen in der Verarbeitung der Audiodaten vorgenommen. Ich musste nochmals zurück ans Reißbrett, um einen generellen Fehler in der Verarbeitung auszumerzen. Zudem habe ich die Größe meines FIFOs maximal groß gemacht. Das läuft nun schon ohne FB Endpoint ca. 15 min ohne hörbare Probleme. Dann kommt der Under/Overrun. Die Abweichung scheint sich folglich in Grenzen zu halten. Clemens L. schrieb: > Der primäre Zweck des Feedbacks ist es, die unterschiedlichen Frequenzen > von Host und Device auszugleichen. Da beide getrennte Quarzkristalle > verwenden, wird die I²S-Hardware im Gerät die Samples nicht mit der > exakten Frequenz aus dem internen Buffer lesen, wie sie der Host dort > hinein schreibt. > > Die Aufgabe deiner Firmware ist es, zu messen, wie viele Samples deine > Hardware in jedem USB-Frame verarbeitet; das hat erst mal nichts damit > zu tun, wie viele Samples der Host wirklich sendet. (Wenn du dazu die > Master Clock vor dem Herunterdividieren verwendest, geht das schneller > und genauer; siehe oben.) Mein DMA-Modul sendet einen Sample pro Beat. Danach kann ich einen Interrupt generieren lassen. Somit könnte ich die Anzahl an Samples, die gesendet wurden recht komfortabel messen. Ich würde jetzt so vorgehen: Das Reshesh-Intervall des FB EP würde ich auf 8 (= 128 ms) einstellen. Das ist dann auch mein Messintervall. Dann zähle ich über das Intervall wie viele Samples gesendet wurden und zähle wie viele Bytes auf dem USB angekommen sind. Dann erhalte ich einen Faktor, mit dem ich die Wunsch-Samplerate multipliziere. That's it? Oder vergesse ich hierbei etwas? Gruß Markus
Markus G. schrieb: > Mein DMA-Modul sendet einen Sample pro Beat. Danach kann ich einen > Interrupt generieren lassen. Somit könnte ich die Anzahl an Samples, die > gesendet wurden recht komfortabel messen. Was ist der Trigger für das DMA-Modul? Mit der Master-Clock wäre das genauer.
Clemens L. schrieb: > Markus G. schrieb: >> Mein DMA-Modul sendet einen Sample pro Beat. Danach kann ich einen >> Interrupt generieren lassen. Somit könnte ich die Anzahl an Samples, die >> gesendet wurden recht komfortabel messen. > > Was ist der Trigger für das DMA-Modul? Mit der Master-Clock wäre das > genauer. Der Trigger ist das I2S-Modul. Das fordert im Prinzip die Samples vom DMA-Modul an. Das I2S-Modul hängt am 49,152 MHz Clock der DPLL. Gruß Markus
Hallo Markus, Ich bin auch gerade wieder dabei an meinem Audio Device weiterzuarbeiten. Bisher habe ich ja den Offset der Schreib/Lesezeiger benutzt und dann einfach die angeforderte Samplerate verändert. Gestern habe ich erste Tests durchgeführt und Versuche gerade nach den aktuellen USB Specs vorzugehen - heißt ich habe die i2s master clock (welche bei mir 12,288 MHz beträgt an einen Timer gehängt und zähle diese immer zwischen den SOFs welche vom USB Host im 1ms Takt generiert werden. Somit erhalte ich jede ms einen zählwert der dann in meinen Fällen etwa bei 12290 liegt. Geteilt durch 256 erhalte ich somit die Sync rate, die ich dem PC mitteilen muss, damit dieser für meinen i2s Takt passend die Daten schickt. Das ganze scheint wunderbar zu funktionieren - jedoch muss man natürlich trotzdem noch seinen Versatz der Pufferzeiger im Auge behalten und die errechnete rate noch feinjustieren. Man kann natürlich auch über mehrere SOFs zählen und dementsprechend seinen Teiler verdoppeln/verdreifachen etc um hier mit einem besseren Mittelwert arbeiten zu können. Ein zählen der Daten hingegen halte ich für nicht sonderlich sinnvoll, da man ja gar nicht weiß, wann der Host nach empfangen der neuen Sync rate seine rate tatsächlich anpasst. Dadurch kann schnell ein fehler bei dem bestimmen der neuen Sync rate entstehen und man muss über einen längeren Zeitraum messen um diesen Fehler möglichst klein zu bekommen. Gruß Hannes Edit: bei meiner Vorgehensweise komme ich aktuell mit einem Buffer für ca 4ms Audiodaten aus ohne Buffer under/overruns in 30minuten Betrieb zu bekommen. Bei einem Buffer für 10ms kommt dies gar nicht mehr vor (so lang ich getestet habe).
:
Bearbeitet durch User
Hannes M. schrieb: > heißt ich habe die i2s master clock > (welche bei mir 12,288 MHz beträgt an einen Timer gehängt und zähle > diese immer zwischen den SOFs welche vom USB Host im 1ms Takt generiert > werden. Somit erhalte ich jede ms einen zählwert der dann in meinen > Fällen etwa bei 12290 liegt. Geteilt durch 256 erhalte ich somit die > Sync rate, die ich dem PC mitteilen muss, damit dieser für meinen i2s > Takt passend die Daten schickt. Das ganze scheint wunderbar zu > funktionieren - jedoch muss man natürlich trotzdem noch seinen Versatz > der Pufferzeiger im Auge behalten und die errechnete rate noch > feinjustieren. Man kann natürlich auch über mehrere SOFs zählen und > dementsprechend seinen Teiler verdoppeln/verdreifachen etc um hier mit > einem besseren Mittelwert arbeiten zu können. Hallo Hannes, das heißt du überträgst dann den gemessenen tatsächlichen Takt, mit dem die Samples auf dem I2S gesendet werden an den Host?! In deinem Fall also: Fs * 12290/12288? Werde ich nachher gleich mal testen. Gruß Markus
Nein, viel einfacher - ich bestimme so die Anzahl der i2s Takte auf meinem Device für die Periode von 1ms des Hosts. Heißt für mein Device wären 1ms = 12288 Takte = 48 kHz. Wenn ich nun zwischen jedem SOF 12290 Takte zähle heißt dies, dass der Host langsamer sendet als ich über i2s (also ich bekomme zu wenig Daten über USB wenn der Host mit 48khz sendet). Der gezählte wert ist natürlich abhängig vom jeweiligen Takt des Hosts (also es kann auch durchaus sein, dass wir nur 12285 Takte zählen, was bedeutet, dass der Host zu schnell Daten sendet - für ihn also 1ms schneller vergehen als für das Device) Teilt man nun den gezählten wert durch 256 erhält man die sync rate, die man dem PC mitteilen muss : In dem Beispiel 12290 / 256 = 48,0078125 kHz Oder 12285 / 256 = 47,98828125 kHz Dieser wer entspricht dann direkt der Sync rate und der PC sendet dann im ersten Fall mehr Daten oder im 2. Fall weniger, was dann der synchrongeschwindigkeit unseres Device entspricht. Mit dem Verfahren lässt sich sehr schnell (in 1ms) eine Recht genaue Sync rate ermitteln und das ohne Bytes zählen zu müssen. Wenn es noch genauer werden soll, kann man natürlich auch über mehrere SOFs zählen und erhält so einen genaueren Mittelwert.
:
Bearbeitet durch User
Hannes M. schrieb: > Nein, viel einfacher - ich bestimme so die Anzahl der i2s Takte > auf > meinem Device für die Periode von 1ms des Hosts. Hallo Hannes, nun der Rechenweg ist im Endeffekt egal. Habe es aber nun so wie du implementiert. Ich nehme einen 16 Bit Zähler, den klemme ich an den I2S Clock, teile per pre-divider auf 12,288 MHz runter und zähle über 4 SOFs. Das ergibt dann rechnerisch einen Wert von 49152 des Zählers, passt also in 16 Bit. Der Ablauf sieht so aus: - Audio soll abgespielt werden (Alternate Setting wird gesetzt) - im SOF Callback: --> Clock-Zähler wird gestartet und Routine verlassen, zugehöriges Flag wird gesetzt, dass der Clock-Zähler läuft und nicht mehr gestartet werden muss - in jedem SOF wird der Counter für die Mittelung erhöht, bis 4 erreicht wird. Der Clock-Zählerwert wird ausgelesen, die neue Samplerate berechnet und in das zugehörige Array geschrieben. Dann wird der Clock-Zähler und der Counter für die Mittelung zurückgesetzt und die SOF-Routine verlassen. So weit so gut. Wenn ich mir nun im Debug-Modus die Werte anschaue, sehe ich, dass der erste Wert beim Auslesen des Zählers stimmt (ergibt z.B. 48,00195 kHz). Die darauffolgenden Werte sind allerdings viel zu klein (Zählerwert in Größenordnung 15000-30000), obwohl der Zählerstand am Ende der Berechnungen zurückgesetzt wird. Ich vermute einen Logikfehler, der evtl. einen Overflow oder dergleichen auslöst. Ich brüte nun schon 2 Tage darüber und komme einfach nicht drauf. Hat jemand einen Tipp, habe vorher noch nie einen Timer/Counter (TC) gebraucht. Gibt es da bekannte Fallstricke? Gruß Markus
Hat dein Device nur Audio-Ausgänge oder auch Eingänge? Mit Eingängen wird es wesentlich einfacher, dann kannst du "implicit feedback" nutzen, und auf den Feedback Endpoint ganz verzichten. Der Host orientiert sich dann an der Datenmenge auf dem Audio-In-Endpoint. Voraussetzung ist natürlich, dass hardwareseitig Inputs und Outputs synchron sind (gleiche Clockdomain).
:
Bearbeitet durch User
Hallo Joe, soweit mir bekannt ist, unterstützt erst die UAC2 Claas implicit Feedback, daher ist dies in unseren Fällen leidrr nicht möglich, da es sich um UAC1 Geräte handelt.
Laut UAC1 Spec müsste implicit Feedback für IN Endpoints schon gehen. Allerdings sind in meiner Anwendung keine Eingänge geplant, entsprechend muss ich mich auf das explizite Feedback konzentrieren. Gruß Markus
Markus G. schrieb: > So weit so gut. Wenn ich mir nun im Debug-Modus die Werte anschaue, sehe > ich, dass der erste Wert beim Auslesen des Zählers stimmt (ergibt z.B. > 48,00195 kHz). Die darauffolgenden Werte sind allerdings viel zu klein > (Zählerwert in Größenordnung 15000-30000), obwohl der Zählerstand am > Ende der Berechnungen zurückgesetzt wird. Was genau wird zurückgesetzt? Ohne Code kann dir keiner helfen.
Markus G. schrieb: > Der Clock-Zählerwert wird ausgelesen, die neue Samplerate > berechnet und in das zugehörige Array geschrieben. Dann wird der > Clock-Zähler und der Counter für die Mittelung zurückgesetzt und die > SOF-Routine verlassen. > > So weit so gut. Wenn ich mir nun im Debug-Modus die Werte anschaue, sehe > ich, dass der erste Wert beim Auslesen des Zählers stimmt (ergibt z.B. > 48,00195 kHz). Die darauffolgenden Werte sind allerdings viel zu klein > (Zählerwert in Größenordnung 15000-30000), obwohl der Zählerstand am > Ende der Berechnungen zurückgesetzt wird. Was machst Du mit den I2S Ticks, die zwischen Auslesen und Zurücksetzen Deines Zählers auflaufen? Laß Deinen Zähler frei laufen, also ohne Zurücksetzen. Den aktuellen Zählwert bekommts Du als Differenz zwischen aktuell ausgelesenem und beim vorhergenden Lauf ausgelesenem Wert (Überlauf beachten!).
Clemens L. schrieb: > Was genau wird zurückgesetzt? Ohne Code kann dir keiner helfen. Da hast du natürlich vollkommen recht :) guest schrieb: > Was machst Du mit den I2S Ticks, die zwischen Auslesen und Zurücksetzen > Deines Zählers auflaufen? Genau deswegen setze ich den Zähler erst nach den Berechnungen zurück. > Laß Deinen Zähler frei laufen, also ohne Zurücksetzen. Den aktuellen > Zählwert bekommts Du als Differenz zwischen aktuell ausgelesenem und > beim vorhergenden Lauf ausgelesenem Wert (Überlauf beachten!). Das habe ich nun implementiert. Die Problematik ist die selbe wie zuvor. Der erste Wert den ich ermittle stimmt, die darauffolgenden nicht mehr. Anbei der relevante gekürzte Code. Kurz zur Erklärung, wenn das alternate Setting des Interfaces gesetzt wird, beginnt die Routine mit dem Streaming. In der Routine wird auf das Ankommen neuer Samples gepollt. Das Handling des Fb Werts mache ich im SOF callback. Der Ablauf gestaltet sich so: - Der Zähler läuft direkt beim Systemstart los - Wiedergabe wird gestartet - alternate Setting wird gesetzt, mein Flag für den ersten Zählerdurchlauf (FB_EP_start_counter) wird dann auf 1 gesetzt. - sobald der nächste SOF kommt, wird der aktuelle Zählerwert abgespeichert (FB_EP_tc_last_val). Das flag wird zurückgesetzt und die SOF-Routine verlassen. Das ist dann mein Initialwert des Zählers. - in den folgenden SOFs warte ich, bis 4 SOFs vergangen sind und hole dann den aktuellen Zählerwert ab. Dann wird verglichen, ob es einen Overflow gab und entsprechend verrechnet. Dann durch 1024 geteilt (das ergäbe bei 12288 * 4 ms dann genau 48). - Dann wird der Wert für den FB EP umgerechnet und in den Buffer gelegt. - Am Ende wird FB_EP_tc_last_val neu gesetzt, um die vergangenen Ticks während der Berechnung zu eliminieren. Im ersten Durchlauf passt der resultierende Wert. Danach nicht mehr. Ich tippe wie gesagt auf einen Logikfehler. Ansonsten müsste etwas vorsichgehen, was ich bislang noch nicht auf dem Schirm hatte. Gruß Markus Edit: Nunja, wie ich bereits erwartet habe, sobald ich hier das Problem genauer schildere, finde ich den Fehler :) Am Ende der Routine habe ich den FB_EP_tc_last_val neu gesetzt und zwar mit dem aktuellen Zähler und nicht mit dem am Anfang ermittelten Wert. Dadurch ist der Zeitraum natürlich zu kurz. Was mir aufgefallen ist: im Debugger kam der Wert trotzdem nicht korrekt. Ich denke im laufenden Betrieb mit USB und Timer kann man evtl. auch einfach nicht mehr sinnvoll debuggen. Werde jetzt mal einige Zeit hören und schaunen wie es mit den Buffer Einstellungen klappt. Auf dem USB konnte ich mit dem Sniffer auf jeden Fall schonmal sinnvolle FB Werte sehen. Momentan ist mein buffer 30 SOFs groß. Sobald die ersten 10 im Speicher liegen, fange ich an mit dem senden via DMA. Da könnte ich auch noch optimieren. Bei der Mittelung könnte ich auch noch etwas feintunen.
:
Bearbeitet durch User
Vielen Dank an alle Beteiligten! Das waren wirklich konstruktive und produktive Beiträge. Für das Fein-Tuning werde ich aber womöglich nochmal auf euch zurückkommen. @Hannes: Um ein Monitoring des Buffer levels werde ich denke ich trotzdem nicht herum kommen. Hast du das schon implementiert? Stellst du dabei pauschal um einen Wert zu? Also z.B. zu wenig Daten im Speicher --> FB-Wert um 0,001 bzw 1 Hz erhöhen. Eine elegantere Lösung fällt mir gerade nicht ein. Gruß Markus
Markus G. schrieb: > Vielen Dank an alle Beteiligten! Das waren wirklich konstruktive und > produktive Beiträge Ja solche Thread gibts hier immer noch. Wobei seltener werden. Leider ist es mittlerweile die Regel dass nach spätestens 40 Posts die Sache aus dem Ruder läuft. Liegt vermutlich daran dass bei dem Thema nicht so viele mitreden können/wollen. Thomas
Hallo Markus, Genauso mache ich das - also ich habe bei mir hierzu aktuell eine Routine im i2s Interrupt definiert, die eine variable auf + bzw. - beispielsweise 1 setzt. Diese Variable addiere ich einfach auf meinen Zählwert und verändere so die Sync rate um einen minimalen wert um den Schreibzeiger so mittig auszuregeln. Perfektioniert habe ich das ganze aber auch noch nicht. Wenn du hier eine bessere Idee hast wäre ich natürlich auch interessiert :). Gruß Hannes
Hannes M. schrieb: > Perfektioniert habe ich das ganze aber auch noch nicht. Wenn du hier > eine bessere Idee hast wäre ich natürlich auch interessiert :). Hallo Hannes, eine bessere Idee hatte ich bisweilen auch noch nicht. Habe aber gerade einige Basisüberlegungen angestellt. Messung auf der Device-Seite: Teilt man für den Zähler den Masterclock des I2S-Moduls auf 12,288 MHz herunter, bekommt man 12288 Counts pro SOF. Das führt zu einer Genauigkeit von 3,9 Hz (12289 Counts entsprechen 48,0039 kHz). Eine Mittelung über 4 SOFs liefert also rein rechnerisch eine Genauigkeit <1 Hz. Das ist schonmal in Ordnung. Umsetzung auf der Host-Seite: - Für eine Auflösung von 100 Hz braucht der Host 10 USB-Frames. Klassisches Beispiel sind die audiotypischen 44,1 kHz: 9 Frames kommen 44 Samples, 1 Frame kommen 45 Samples. - Für eine Auflösung von 10 Hz ergibt sich 100 Frames und für 1 Hz 1000 Frames. 1000 Frames sind wiederum eine Sekunde, was auch zur Angabe in der Spec passt, man möge mindestens 1 Sekunde lang messen. Was mich nun stutzig macht: Angenommen wir arbeiten z.B. mit einem Polling-Intervall des FB Endpoints von 4 ms. Wir mitteln über 4 Frames und bekommen eine Abweichung von einem Count. Das ist dann rund 1 Hz Abweichung. Der FB-Wert wird an den Host übermittelt. Der Host stellt die Samplerate entsprechend nach. Der Host benötigt aber 1 Sekunde Zeit, um die Auflösung überhaupt umsetzen zu können. Nach 4 ms kommt allerdings schon der nächste Fb-Wert. Dieser ist vllt schon wieder in Ordnung (genau 48 kHz). Dann geht dem Host theoretisch dieser eine Sample, den er ausgehend vom vorherigen FB-Wert senden sollte verloren. Über längere Zeit akkumuliert sich das Ganze und es kommt so ein Drift des Buffer-Levels zustande (zusätzlich zur ohnehin vorhandenen Abweichung der Clock-Domains, diese wird aber auf längere Zeit problemlos ausgeregelt). Habe ich irgendwas nicht bedacht? Aus dieser Sicht kommt mir die ganze Umsetzung des FB-Mechanismus etwas kurios vor. Bin gespannt auf eure Anmerkungen. Gruß Markus
:
Bearbeitet durch User
Markus G. schrieb: > Habe ich irgendwas nicht bedacht? Aus dieser Sicht kommt mir die ganze > Umsetzung des FB-Mechanismus etwas kurios vor. Bin gespannt auf eure > Anmerkungen. Ja das sehe ich auch so und das ist vielleicht auch der Grund warum es nicht so viele Devices gibt die das implementieren. Ich persönlich kenne nur ein etwas seltsames Teil von Yamaha. Kommerzielle Devices die ich kenne regeln mit dem SOF eine PLL die dann wiederum fs256 oder fs512 generiert. Der SOF Takt wird per Definition als genau angesehen. Thomas
Thomas schrieb: > Kommerzielle Devices die ich > kenne regeln mit dem SOF eine PLL die dann wiederum fs256 oder fs512 > generiert. > Der SOF Takt wird per Definition als genau angesehen. > > Thomas Hallo Thomas, ich denke die Lösung mit FB-Endpoint ist schon mit Abstand die professionellste und hochqualitativste. Bei der Implementierung kann man sich einfach nicht 100% an die USB Spec halten (die Dokumentation hätte anwendungsnäher ausfallen sollen). Man muss zwingend eine Überprüfung des Buffer-Levels implementieren und dann nach Bedarf manuell nachregeln. Dann scheint mir die Lösung keine Nachteile zu haben. @Hannes: Wenn ich einen neuen Buffer-Block anfange zu beschreiben schaue ich, wie viele Samples derweil auf dem I2S gesendet wurden. Ich hab jetzt einfach mal folgendes implementiert: Der Index darf +- 20% vom Idealwert abweichen. Innerhalb dieser Grenzen wird nichts korrigiert. Ist die Abweichung größer, korrigiere ich um +- 1 Hz. Die Werte sind allerdings völlig aus der Luft gegriffen. Der Test muss dann zeigen, ob das so praxisgerecht ist. Gruß Markus
Markus G. schrieb: > ich denke die Lösung mit FB-Endpoint ist schon mit Abstand die > professionellste und hochqualitativste. Das kann ich nicht beurteilen. Dazu muss mein eine Jitter Messung für beide Varianten machen. Dies wiederum würde bedeuten dass man beide Varianten fehlerfrei implementiert hat und eine entsprechende Messumgebung z.B. eine AP mit Digital Option vorhanden ist. Thomas
Hallo Markus, ich habe heute auch wieder eine ganze Zeit lang an der Synchronisation gearbeitet. Ich verfahre mittlerweile so, dass ich eine Variable mit Startwert von 48KHz vorbelege, diese dann immer nach 5 SOFs mit meinem Zählwert verrechne (also die Differenz bilde). Diese Differenz rechne ich dann zu 1/100 zum Aktualwert dazu. Durch diese verrechnung ist mein Aktualwert sehr viel genauer als wenn ich nur über 5 SOFs zähle und der Wert schwankt auch nicht mehr so stark. Zusätzlich benutze ich meinen Fehloffset vom Schreib zum Lesezeiger und nutze diesen direkt als Regelparameter mit einem Regelwert von 0.01Hz/Byte. Mit dieser Einstellung läuft mein Device gerade mit einem Jitter von etwa +/- 0.5Hz. Gruß Hannes
Hallo, bin wieder am Thema dran. Ich werde die Tage noch einige grundsätzliche Überlegungen darlegen, nachdem alles noch etwas gären konnte. Bin momentan dabei, eine zweite Samplerate zu implementieren. Das Ganze soll dann darauf hinauslaufen, im Betrieb die SR wechseln zu können (mit einem Request). Im ersten Schritt hatte ich ja alles fix auf 48 kHz SR ausgelegt. Nun bin ich dabei das auf 44,1 kHz anzupassen (ebenfalls erstmal fix implementiert). Die nicht ganzzahlige SR (in kHz) bringt einige Fallstricke mit sich. Entsprechend tue ich mich momentan noch etwas schwer, das zum laufen zu bringen. Die Clock-config des I2S-Moduls muss ich nur beim Basisclock anpassen. Sprich von 49,152 MHz habe ich auf 45,1584 MHz gewechselt. Die Teilerverhältnisse der restlichen Clocks bleiben natürlich gleich im Vergleich zu 48 kHz. Den Output habe ich mittels Oszi gecheckt, das passt alles. Im Deskriptor habe ich die MaxPaketSize geändert: (45 Samples (aufgerundet) + 1 Sample Spielraum) 2 Kanäle 3 Byte = 276 Byte Den einen Sample dazu, um die SR im Betrieb via FB Endpoint stellen zu können, könnte ich mir wohl auch sparen, da ich ja sowieso auf 45 aufrunden muss. Ist wohl aber nicht entscheidend. Das Ganze enummeriert auch wie gewohnt. Allerdings die Audio-Wiedergabe funktioniert nicht. Ich hatte erst meine Routinen auf dem uC in Verdacht, diese habe ich allerdings zu Begin gleich dynamisch, sprich für unterschiedliche SRs kompatibel ausgelegt. Wenn ich mit Wireshark auf dem USB logge, dann kommen da erst garkeine Daten. Einen Fehler in Windows gibt es allerdings nicht. Irgendwas scheine ich nicht bedacht zu haben. Hat jemand einen Tipp? @Hannes: hast du deine Software auch schon mal für 44,1 kHz, was für Audio (mp3) wesentlich verbreiteter ist, getestet? Gruß Markus
Wenn Windows nicht will, dann probier mal mit den Pinguin ...
Wie immer zeig die Deskriptoren.... Windows verhält sich ziemlich gutmütig beim Enum. Ein enumeriertes Device ist noch lange kein Audiodevice. Unter der Verraussetzung dass du ein korrektes Deskriptorset hast passieren beim Abspielen 2 Dinge: 1. SetInterface(alternate) std request um Bandbreite zu reservieren 2. SetSr(sr) Classrequest um die Samplerate einzustellen. Beide Request müssen korrekt bearbeitet werde und sollten auch mit wireshark zu sehen sein. Falls nicht wird der Audiotreiber nichts streamen und du bekommst keine Daten. Nun zum Classrequest: Ist das korrekt implementiert? Hast du dafür einen Händler? Beachte das der SetSr ein Outrequest ist der eine OutDatastage hat. Ich hab schon genügend USB code gesehen der das nicht richtig macht. Das liegt u.A. auch daran das std requests keinen Outrequest mit OutDatastage stage definieren. Thomas
Thomas schrieb: > Wie immer zeig die Deskriptoren.... > Windows verhält sich ziemlich gutmütig beim Enum. Ein enumeriertes > Device ist noch lange kein Audiodevice. Unter der Verraussetzung dass du > ein korrektes Deskriptorset hast passieren beim Abspielen 2 Dinge: > 1. SetInterface(alternate) std request um Bandbreite zu reservieren > 2. SetSr(sr) Classrequest um die Samplerate einzustellen. Es geht mir noch garnicht um die Requests! Ich will erstmal nur von meiner festen Konfig. 48k - 24 Bit auf 44,1k - 16 Bit umstellen und testen, ob das funktioniert (sprich in eine andere Clockbasis wechseln). Die Deskriptoren habe ich überprüft, sind komplett identisch bis auf die andere SR. Ich habe zwischenzeitlich das Problem verortet, warum Windows die Wiedergabe gesperrt hat: Wenn das Gerät mit gleichem Namen und gleicher Seriennummer einmal mit 48k - 24 Bit angeschlossen war, speichert Windows diese Konfig natürlich. Schließt man das vermeintlich gleiche Gerät dann mit einer anderen SR-Konfig (z.B. 48k- 16 Bit) an, sperrt Windows die Audioausgabe, weil gespeicherte Konfig und aktuell ausgelesene Konfig nicht mehr zusammenpassen. Ich habe das im Code nun übergangsweise so gelöst, dass ich für jede Konfiguration eine eigene Seriennummer vergeben habe. Also z.B. 48k - 24 Bit => 0.0.1 44,1k - 16 Bit => 0.0.2 Windows erkennt das dann alles als unterschiedliche Geräte mit gleichem Namen. Soweit so gut.
:
Bearbeitet durch User
Du kannst es natürlich machen wie du willst, aber mit der SN bist du auf dem Holzweg. Usbaudio braucht keine SN das einzige was du damit verhindern kannst ist eine erneute Treiber Installation wenn das Device an einem anderen USB Port angesteckt wird. Anstelle einer neuen SN kannst du auch die entsprechende PNF Datei im Treiber Ordner löschen das hätte die gleiche Wirkung. Usbaudio parst die Deskriptoren deines devices und baut daraus einen Deskriptor Satz für KS.sys. Das Streaming wird von KS.sys gemacht nicht von usbaudio.sys. KS.sys ist ein sogenannter miniport driver. Wenn du also einen Fehler in den Deskriptoren hast wird ein falscher Satz KS Deskriptoren erzeugt und nichts geht. Es gab / gibt ein tool von MS ksstudio oder so sollte immer noch in ddks zu finden sein. Ein anders Stichwort wäre graphview in alten ddks. Wenn dein Device also mit anderen Deskriptoren auf einmal nicht mehr streamt hast du einen Fehler in den Deskriptoren. Beliebte Ursache ist eines der Längenfelder Z.B im AC control Header. Thomas
Thomas schrieb: > Wenn dein Device also mit anderen Deskriptoren auf einmal nicht mehr > streamt hast du einen Fehler in den Deskriptoren. Beliebte Ursache ist > eines der Längenfelder Z.B im AC control Header. Der Deskriptor war 100% identisch bis auf die andere Samplerate. Der Trick mit der unterschiedlichen SN hat zunächst mal funktioniert und erlaubt mir weitere Tests. 44,1 kHz Playback funktioniert jetzt schonmal grundsätzlich. Ich habe mich parallel dazu wieder mit dem Feedback-Thema beschäftigt. Hier einige Gedanken dazu: - Übertragung des FB-Werts im Format 10.10 (mit 4 Nullen gepadded). - Der kleinste Wert 0,0000 0000 01 (binär) entspricht dezimal 0,0009766 --> Man kann also schonmal nicht genau einen Sample oder ein Hz anfordern. Unschön. Weniger als einen Sample kann man folglich auch nicht anfordern, sprich genauer gehts also nicht. Insofern macht zumindest der angegebene Messzeitraum von min. 1 Sekunde in der USB-Spec Sinn, da: 1 Sample pro Sekunde = 1 Hz, bzw. man braucht 1000 Frames, um 1 Hz auflösen/übermitteln zu können. - Folgende Frage resultiert: Angenommen ich fordere 1 Sample mehr an, also 48,001 kHz. Innerhalb einer Sekunde kann der Host das also gar nicht auflösen, da er nur 0,976 Samples als FB-Wert erhält. Wie handhabt das Windows nun, wenn der Sample noch nicht gesendet wurde, aber schon ein neuer FB-Wert anliegt? Geht der zuvor angeforderte Sample dann verloren? Man kommt zum Schluss, dass die USB-Audio Spec nicht so sonderlich durchdacht ist. Ohne ein Monitoring des Füllstands des FIFOs funktioniert das einfach nicht. Ich hatte gehofft mir das zusätzliche Monitoring des buffers sparen zu können, weil der uC schon ziemlich gut ausgelastet ist. Nun muss ich schauen/testen, ob ich das Monitoring noch unterbringe. Gruß Markus
Markus G. schrieb: > Wie handhabt das Windows nun, wenn der Sample noch nicht gesendet wurde, > aber schon ein neuer FB-Wert anliegt? Geht der zuvor angeforderte Sample > dann verloren? Der Host-Treiber hat einen Zähler, der aussagt, wie viele Samples das Device haben will. In jedem Frame wird der FB-Wert auf den Zähler addiert, und die Anzahl der wirklich gesendeten Samples abgezogen. Beispiel (dezimal statt binär), mit Feedback = 44,099: Frame 1: Zähler = 44,099 -> sende 44 Samples -> Zähler = 0,099 Frame 2: Zähler = 44,198 -> sende 44 Samples -> Zähler = 0,198 Frame 3: Zähler = 44,297 -> sende 44 Samples -> Zähler = 0,297 Frame 4: Zähler = 44,396 -> sende 44 Samples -> Zähler = 0,396 Frame 5: Zähler = 44,495 -> sende 44 Samples -> Zähler = 0,495 Frame 6: Zähler = 44,594 -> sende 44 Samples -> Zähler = 0,594 Frame 7: Zähler = 44,693 -> sende 44 Samples -> Zähler = 0,693 Frame 8: Zähler = 44,792 -> sende 44 Samples -> Zähler = 0,792 Frame 9: Zähler = 44,891 -> sende 44 Samples -> Zähler = 0,891 Frame 10: Zähler = 44,990 -> sende 44 Samples -> Zähler = 0,990 Frame 11: Zähler = 45,089 -> sende 45 Samples -> Zähler = 0,089 Frame 12: Zähler = 44,188 -> sende 44 Samples -> Zähler = 0,188 ... Dieses Verfahren funktioniert auch, wenn sich der Feedback-Wert zu jedem beliebigen Zeitpunkt ändert. > - Der kleinste Wert 0,0000 0000 01 (binär) entspricht dezimal 0,0009766 > --> Man kann also schonmal nicht genau einen Sample oder ein Hz > anfordern. Unschön. In der Praxis ist der Unterschied zwischen Device- und Host-Clock eh kein exaktes Vielfaches von 1 Hz.
Clemens L. schrieb: > Der Host-Treiber hat einen Zähler, der aussagt, wie viele Samples das > Device haben will. In jedem Frame wird der FB-Wert auf den Zähler > addiert, und die Anzahl der wirklich gesendeten Samples abgezogen. > > Beispiel (dezimal statt binär), mit Feedback = 44,099: > Frame 1: Zähler = 44,099 -> sende 44 Samples -> Zähler = 0,099 > Frame 2: Zähler = 44,198 -> sende 44 Samples -> Zähler = 0,198 > Frame 3: Zähler = 44,297 -> sende 44 Samples -> Zähler = 0,297 > Frame 4: Zähler = 44,396 -> sende 44 Samples -> Zähler = 0,396 > Frame 5: Zähler = 44,495 -> sende 44 Samples -> Zähler = 0,495 > Frame 6: Zähler = 44,594 -> sende 44 Samples -> Zähler = 0,594 > Frame 7: Zähler = 44,693 -> sende 44 Samples -> Zähler = 0,693 > Frame 8: Zähler = 44,792 -> sende 44 Samples -> Zähler = 0,792 > Frame 9: Zähler = 44,891 -> sende 44 Samples -> Zähler = 0,891 > Frame 10: Zähler = 44,990 -> sende 44 Samples -> Zähler = 0,990 > Frame 11: Zähler = 45,089 -> sende 45 Samples -> Zähler = 0,089 > Frame 12: Zähler = 44,188 -> sende 44 Samples -> Zähler = 0,188 > ... > > Dieses Verfahren funktioniert auch, wenn sich der Feedback-Wert zu jedem > beliebigen Zeitpunkt ändert. Hallo Clemens, super Information, besten Dank. Das heißt, dass zumindest einmal angeforderte mehr/weniger-Samples nicht verloren gehen. Ist diese Methode irgendwo offiziell dokumentiert? Würde gern noch etwas stöbern, tue mich bei der Suche aber etwas schwer. Vermutlich ermangelt es mir am richtigen Suchbegriff. Momentan stocke ich etwas beim Zeitpunkt, an dem ich die Zeiger von Einlesen und Ausgeben aus dem Ringbuffer vergleiche. Evtl muss ich meine DMA-Konfiguration noch etwas anpassen. Gruß Markus
Markus G. schrieb: > Ist diese Methode irgendwo offiziell dokumentiert? Nein, aber offensichtlich so vorgesehen, wenn man schon mal mit Festkommazahlen gearbeitet hat.
Hallo nochmal, ich hänge gerade beim Problem, dass ich an den Zeiger, der im DMA-Block auf den aktuellen Sample zeigt, nicht rankomme. Mein DMA-Modul ist folgendermaßen konfiguriert: - Blocklänge = 2 Frames - 4 Blocks, dann wird von vorne begonnen (Ringbuffer mode) - Beatsize = 3 Byte, also ein Sample eines Kanals - Getriggert wird die DMA-Transaktion sample und kanalweise vom I2S-Modul (bei 48 kHz, 2 Kanäle also mit 96 kHz). Das ist zwar ziemlich komfortabel durch den automatischen Ringbuffer mode und die Übertragung, die direkt vom I2S-Modul angestoßen wird. Allerdings habe ich das Problem, dass ich nicht weiß bei welchem Sample in einem Block das DMA-Modul momentan steht. Somit bekomme ich keine Info darüber, wo der Index momentan tatsächlich (!) steht. Ich kann lediglich am Ende eine Blocks einen Interrupt auslösen. Nach jedem Sample wäre zwar praktisch, da ich einen Zähler mitzählen lassen könnte. Das ist nun aber auch nicht Sinn und Zweck eines DMAC (unnötige Rechenleistung). Damit meine ich, dass ich natürlich Samples oder Bits anhand des Clocks des I2S-Moduls zählen kann. Damit kann ich berechnen, wo der Index stehen müsste. Nach einiger Zeit läuft das aber durch Rundungsungenauigkeit, Messungenauigkeit etc. von der tatsächlichen Position weg und der Buffer Over/Underflowed trotzdem irgendwann. @Hannes: Wie hast du das denn implementiert dass du an den Ausgabepointer ran kommst im Betrieb? Machst du die I2S-Ausgabe irgendwie manuell oder getriggert von einem Counter? Gruß Markus
Hallo Markus, Ich lasse bei meiner Lösung (genau wie du) DMA im Circular-Mode laufen und prüfe bei den DMA half/full Interrupts ob der Abstand vom Schreib- zum Lesezeiger im Toleranzbereich liegt. Driftet er hier zu stark weg verändere ich die Samplerate minimal +/- um ihn wieder in den Optimalbereich zu verschieben. Funktioniert bei mir als Lösung so ganz gut - lediglich ergeben sich hier ab und zu Ungenauigkeiten, da durch den Clock Offset zwischen PC (USB Frame) und i2s Interrupt Abweichungen auftreten können, und der nächste bereits erwartete USB Frame beim I2S Interrupt Zeitpunkt noch nicht eingetroffen ist und somit hier Daten fehlen. Dadurch kommt es dazu, dass der Offset ab und zu etwas springt. Ich denke statt den i2s Interrupts den USB SOF Interrupt zur Offsetberechnung zu nutzen wäre sicherlich besser - vielleicht versuche ich auch noch einmal diese Methode zu implementieren... Gruß Hannes
:
Bearbeitet durch User
Hannes M. schrieb: > Ich lasse bei meiner Lösung (genau wie du) DMA im Circular-Mode laufen > und prüfe bei den DMA half/full Interrupts ob der Abstand vom Schreib- > zum Lesezeiger im Toleranzbereich liegt. Hallo Hannes, genau dort liegt meine Frage. Wie machst du das genau? Sprich wie kommst du an die Position des Zeigers, der für die Ausgabe verantwortlich ist (im DMA-Modul) ran? Dieser Zeiger scheint mir nicht zugänglich zu sein. Ich sage dem DMAC was er machen soll wenn der Trigger kommt (einen Beat senden) und wie groß ein Block ist. Die Indizierung im Block macht der DMAC selbstständig. Machst du das per Interrupt des I2S-Moduls nach jedem Sample und erhöhst dabei einen Zähler? Gruß Markus
Hallo Markus, ich nutze als Zeigerersatz einfach den Interrupt -> Wenn der DMA Full Interrupt aufgerufen wird, hat der Lesezeiger vom DMA-Modul ja das Ende des Puffers erreicht. Also prüfe ich in dem Interrupt, wie weit der Schreibzeiger (USB-Daten) hier von der Mitte des Ringbuffers abweicht und speichere den Offset für den USB Audio-Sync Routine zwischen. Das ganze mache ich jeweils beim "DMA Full <-> USB bei Half" sowie bei "DMA Half <-> USB bei Full". Ich hoffe das hilft dir weiter. Gruß Hannes
Hannes M. schrieb: > Das ganze mache ich jeweils beim "DMA Full <-> USB bei Half" sowie bei > "DMA Half <-> USB bei Full". Hallo Hannes, danke für deine Rückmeldung. Wenn ich mir das recht überlege, hast du mit deiner Methode aber auch keinen sauberen Zeitbezug oder? Ich mache die Generierung des FB Werts immer im SOF Interrupt. Das ergibt einen sicheren zeitlichen Bezug. Deshalb hatte ich das Problem, an den tatsächlichen Index im DMA-Block nicht heranzukommen. Ich habe meine Herangehensweise nochmals überdacht und bin nun dabei diverses zu ändern. Ich verfahre so: - Ich zähle mit einem Counter die Bitclocks z.B. eine Sekunde lang mit. Somit weiß ich, wie viele Bits in den letzten 1000 SOFs auf dem I2S rausgetaktet wurden. Der Counter ist free-running. - Diesen Bitcounter vergleiche ich dann mit dem Sollwert. So erhalte ich rechnerisch (!) die Abweichung meines read-Index des FIFO. - Diese Abweichung summiere ich solang, bis ein ganzer Sample zusammengekommen ist (24 Bit * Anzahl Kanäle). Daraufhin kann ich dann entsprechend Samples mehr/weniger vom Host anfordern, um die Abweichung auszugleichen. Bei der Übertragung des FB-Werts werde ich zusätzlich eine andere Herangehensweise implementieren. Bisher bin ich immer hergegangen und habe z.B. wenn ich 44,1 kHz haben will, eine Sekunde lang diesen Wert auf dem FB übertragen. Nun ist es so, dass man mit 10.10 Format 44,1 genauso wenig präzise anfordern kann wie 1 Hz. 44,1 (dec) 101100.0001100110 (bin - truncated auf .10) 44.099609375 (dec) Das gibt also von vornherein schon Abweichungen. Nicht tragisch, im Dauerbetrieb aber trotzdem unschön. Ich hatte das weiter oben im Thread schon thematisiert. Die neue Herangehensweise ist nun folgende: anstatt ich eine Sekunde lang z.B. 48,001 kHz anfordere, um einen Sample mehr zu bekommen, was ich nicht genau auflösen kann, fordere ich 999 Mal 48 kHz/Samples an und 1 Mal 49 kHz. Die Integerzahl kann problemlos exakt übertragen werden. Das Ergebnis, also die übertragene Gesamtanzahl der Samples sollte identisch sein. Allerdings habe ich die Abweichung bei der Übertragung auf dem Feedback schonmal ausgemerzt. Ich denke mir im Nachhinein, dass es seitens der USB-Spec eventuell auch so gedacht war. Ansonsten wäre das gewählte Format mit 10.10 eigentlich völliger Blödsinn. Andererseits hätte man dann die Nachkommastellen gleich weglassen können. Ich werde nun mal eine Routine bauen, die mir entsprechend dem ermittelten FB die passende Anzahl an Samples für eine Sekunde anfordert. Um den Rechen- und Übertragungsaufwand zu verringern, kann man auch z.B. warten, bis man 4 Samples für eine Pollingrate von 4 ms zusammen hat. Gruß Markus
:
Bearbeitet durch User
Hallo Markus, das ist so nicht ganz korrekt -> Ich erzeuge meinen Feedback Wert natürlich auch im SOF Interrupt. Generell funktioniert das ja auf die Art und Weise, wie wir es ja weiter oben schon besprochen hatten. Mal abgesehen davon, dass sich 44.1 nicht perfekt darstellen lässt, sowie von den Übertragungsabweichungen der Treiber mal abgesehen, würde das ganze so - Theoretisch - auch ohne die Zeigerkontrolle funktionieren und Schreib- sowie Lesezeiger hätten immer den gleichen Abstand. Wie wir ja nun aber leider schmerzlich feststellen musste, ist das ganze ja nicht soo genau, wie wir es in der Theorie erwartet hätten und dadurch entsteht ein minimaler Drift zwischen Schreib- und Lesezeiger. Ich behandle das ganze so, dass ich wie in meiner letzten Nachricht ja schon beschrieben, in den DMA Interrupts - also 2 mal pro Bufferdurchlauf - die Position von Schreib- zu Lesezeiger überprüfe. Stelle ich hier nun eine Abweichung größer X fest, dann lege ich - je nach Größe und Richtung der Abweichung - einen Offset Wert fest, den ich dann in den SOF-Interrupts mit auf den Feedback Wert aufrechne (ist nur ein minimaler Offset, der ja auch nur dafür gedacht ist, den Abstand von Schreib- zu Lesezeiger zu korrigieren). Das ganze Funktioniert auch recht gut - bis auf die kleinen Probleme, die ich oben ja schon beschrieben hatte. Ich verstehe allerdings nicht, warum die Übertragung im 10.10 Format keinen Sinn ergeben soll?! Gerade die Nachkommastellen bieten dir doch die Möglichkeit im Dauerbetrieb den korrekten Datenstrom von der Quelle zu erhalten?! Gruß Hannes
Hannes M. schrieb: > Ich behandle das ganze so, dass ich wie in meiner letzten Nachricht ja > schon beschrieben, in den DMA Interrupts - also 2 mal pro > Bufferdurchlauf - die Position von Schreib- zu Lesezeiger überprüfe. > Stelle ich hier nun eine Abweichung größer X fest Hallo Hannes, Bitte korrigiere mich, wenn ich falsch liege, aber der Schreibzeiger (der Index der Daten die in den FIFO geschrieben werden/vom USB kommen) ist nicht zeitlich an den Lesezeiger gekoppelt, da die Daten vom USB auf einen Schlag in den FIFO geschrieben werden und nicht sauber über einen Frame verteilt. Dementsprechend entsteht so kein vergleichbarer Zeitbezug von Lese- und Schreibzeiger. > Ich verstehe allerdings nicht, warum die Übertragung im 10.10 Format > keinen Sinn ergeben soll?! Gerade die Nachkommastellen bieten dir doch > die Möglichkeit im Dauerbetrieb den korrekten Datenstrom von der Quelle > zu erhalten?! Näherungsweise stimmt das ja auch. Aber wenn ich einen Sample mehr anfordere mit 48,001 als Feedback Wert, dann bekomme ich nur 0,97 Samples durch die Begrenzung der Nachkommastellen. Was ich sagen will ist, dass da unnötig eine Abweichung integriert wird. Klar, der Drift der darauf entsteht ist relativ gering, aber dennoch unschön. Ich habe noch eine Frage zum Intervall/der Übertragung des FB-Werts. Wenn ich mir das mit Wireshark anschaue, stelle ich 2 Sachen fest (siehe angehängtes Bild): 1.) Frame 585: In Wireshark werden immer 10 Audioframes zusammengefasst dargestellt (10 * 288 Byte + Klimbim = 3039 Byte). Ich vermute das ist normal. 2.) Der Host erhält den FB-Wert immer abwechselnd einmal korrekt (Frame 586) und einmal nicht (Frame 587-588). In Wireshark sieht man das an der übertragenen Datenmenge. Wenn der FB-Wert korrekt kommt, werden 3 Byte übertragen (exemplarisch 00000C, was korrekt ist). Die Gesamtdatenmenge beträgt dann 54 Byte. Die Anfrage des Host ist ebenfalls 51 Byte lang. Ich habe hier die Polling rate zum Test auf 1 ms gestellt, bei einer Erhöhung ist das Phänomen aber identisch. @Hannes: Verhält sich das bei dir genauso? Könntest du mal einen Ausschnitt von Wireshark posten? Ich vermute das könnte an der chronologischen Reihenfolge der Abarbeitung in meinem Code liegen, dass dann eine Übertragung des FB-Werts unter den Tisch fällt. Gruß Markus
Hallo zusammen, das Projekt ist noch aktiv und ich versuche es voranzutreiben. In der Zwischenzeit habe ich eine Testoption implementiert, um die Genauigkeit des Zählers zu kontrollieren. Das ist im Grunde nur eine einfache Interpolation im FIFO. Sprich ich habe den Feedback Endpoint deaktiviert und mache eine rudimentäre Samplerate-Conversion. Mit rudimentär meine ich: zu viele Samples im Buffer -> Samples löschen, zu wenig Samples im Buffer -> letzten Sample kopieren und so buffer auffüllen. Das ist natürlich im Sinne der Audioqualität nicht zielführend, es dient aber nur dazu, um meinen Bitclock Zähler zu überprüfen. Nachdem ich keinen Zugriff auf den tatsächlichen Wert des Read-Pointers/Ausgabepointers habe, muss ich mir den tatsächlichen Bitclock durch mitzählen ermitteln. Dafür habe ich einfach einen TC an den Bitclock gehängt und zähle da entsprechend mit. Ergebnis des Tests war nun, dass der Buffer Over/Underrun trotzdem auftritt! Zwar nach ca. der dreifachen Zeit, wie ganz ohne Korrektur, aber die Verzerrungen treten trotzdem auf. Ich habe dann verschiedenes optimiert: statt dem BCLK den MCLK (12,288 MHz) gezählt, den Zählerwert nicht jeden Frame abgeholt und summiert, sondern eine ganze Sekunde einen 32 Bit Zähler laufen lassen und dann nur einmal den Wert abgeholt. Der Zähler ist zudem free-running, sprich ich prüfe, ob ein overrun aufgetreten ist und korrigiere entsprechend meinen Wert. Der Zählerwert wird immer konsistent am Anfang der SOF-Interrupt-Routine eingelesen. Die Maßnahmen haben das Ergebnis aber nur marginal verbessert (ca. 30 %). Bei einer Buffergröße von 8 Frames tritt der Over/Underrun trotzdem noch nach ca 15 Min. auf. Nun frage ich mich, wie kommt diese offensichtlich vorhandene Abweichung/der Drift zustande? Ist das Zählen per TC des I2S MCLK so unpräzise? Gruß Markus
:
Bearbeitet durch User
Hallo, habe es nun hinbekommen auf den Read-Pointer Zugriff zu haben (Event des DMA via Eventsystem auf einen TC). So funktioniert die einfache Interpolation schonmal (dauerhaft)! Der Feedback hat als Basiswert den ermittelten tatsächlichen Clock des I2S-Moduls (als gleitender Mittelwert). Wenn die Abweichung des Buffer Level einen Schwellwert überschreitet, stelle ich mit einem zusätzlichen Offset nochmals eine Zeit lang nach (Ausgleich des Buffer Levels). Sprich ich mache das so wie Hannes es auch implementiert hat. Das funktioniert soweit auch. Ein Problem habe ich nun zusätzlich erkannt: Der Feedback-Mechanismus funktioniert nach dem Hochfahren/Neustart des PCs wie gewünscht. Wenn man aber die Wiedergabe unterbricht, das Programm schließt etc. und dann später wieder neu startet funktioniert der Feedback-Mechanismus nicht mehr (sprich alt_setting geht auf 1, dann auf 0, dann wieder auf 1). Ein Aus/Einschalten des uC bewirkt nichts. Nur beim Neustart des PCs wird der Feedback-Wert wieder angenommen. Das äußert sich darin, dass immer nur die nominelle Anzahl an Samples gesendet wird. Seitens des Controllers ist alles wie es sein sollte: Variablen werden zurückgesetzt, DMA wird neu gestartet, der Feedbackwert wird korrekt gesendet (getestet mit Wireshark), nur der Host scheint diesen Feedback-Wert zu ignorieren. Woran kann das liegen? Da scheint mir womöglich irgendeine Einstellung zu fehlen. Gruß Markus
:
Bearbeitet durch User
Hallo, nachdem ich nun nochmal einen Schritt zurückgegangen bin, um die Besonderheiten der Videoklasse im Audiodevice ebenfalls zu berücksichtigen, läuft die USB-I2S-Bridge mit der einfachen Buffer-Interpolation nun stabil. Jetzt werde ich mich wieder dem Feedback-Endpoint zuwenden. Ich habe zunächst noch diverse Bugs hinsichtlich der Grundfunktion ausgemerzt. Nun bin ich an der Überarbeitung des Feedback-Mechanismus. Zunächst wollte ich wie einen Post zuvor beschrieben, den Basis-Offset anhand des Zählers auf dem MCLK machen und dann nur bei Bedarf etwas nachstellen. Mir ist diese Methode eigentlich etwas zu aufwändig und unnötig unpräzise, zudem der Regelaufwand recht hoch. Wie ich es eigentlich gerne angehen würde: - nur die ausgegebenen Samples auf dem I2S über 10 Frames zählen (um 44,1 kHz auch abdecken zu können) - eingehende Samples vom USB über 10 Frames zählen - einen Schwellwert/max. Bufferoffset definieren, momentan sind das 3 Samples - das Pollingintervall steht auf 2 ms (kleinst möglich) - standardmäßig sende ich die nominelle Sampleanzahl pro Frame als Feedback (hier 48 für 48 kHz) --> Wenn mein Bufferoffset größer/gleich dem max. Offset wird, fordere ich per Feedback einen Sample mehr oder weniger an (47 oder 49), da das polling-Intervall auf 2 ms steht, bekomme ich somit entsprechend über 2 Frames einen Ausgleichssample, also 2 Samples. Das scheint mir die einfachste und zielführendste Methode zu sein. Im Prinzip nach dem Motto "ich hab einen sample zu wenig, sende mir beim nächsten Frame einen mehr". Einen ganzen Sample kann man zudem exakt per Feedback anfordern, eine fractional Samplerate hingegen nicht (bzw. die Abweichung liegt sowieso in der Größenordnung der Fb-Genauigkeit). Außerdem spare ich mir den ganzen float Rechenaufwand. Ich habe das nun soweit implementiert. Es funktioniert aber eher mäßig (Over/Underflow kommt). Die Frage, die sich mir stellt ist: wenn ich einen Feedback-Wert sende, nach wie vielen Frames wird dann tatsächlich dieser Wert berücksichtigt? Irgendwo scheine ich bei dieser Methode einen Denkfehler zu haben, der die Regelung so mit dem Fb-Mechanismus von Windows nicht funktionieren lässt. Gruß Markus
:
Bearbeitet durch User
Markus G. schrieb: > wenn ich einen Feedback-Wert sende, nach wie vielen Frames wird dann > tatsächlich dieser Wert berücksichtigt? Das ist nicht spezifiziert. Auch nicht, für wie viele der folgenden Frames er verwendet wird.
Clemens L. schrieb: > Markus G. schrieb: >> wenn ich einen Feedback-Wert sende, nach wie vielen Frames wird dann >> tatsächlich dieser Wert berücksichtigt? > > Das ist nicht spezifiziert. Auch nicht, für wie viele der folgenden > Frames er verwendet wird. Hallo Clemens, Gibt es zu dem Feedback-Mechanismus keinerlei Dokumentation? Wie machen das denn dann die professionellen Anbieter von Soundkarten? Da werden die Ingenieure vermutlich auch nicht mit Try&Error auf gut Glück versuchen. Ich war davon ausgegangen, dass das eingestellte Feedback-Intervall automatisch auch der Zeitraum der Gültigkeit eines übertragenen Wertes ist. Das würde zumindest Sinn machen. Ggf. muss ich dann wirklich eine Regelung im klassischen Sinn implementieren (der Buffer-Level schwankt dann immer um den ideal Füllstand). Meine bisherige Herangehensweise „es fehlt ein Sample im Buffer, sende mir im nächsten Frame einen mehr“ scheint nicht zu funktionieren. Es macht den Anschein, als würde der Host den mehr angeforderten Sample „vergessen“. Der geänderte Feedback-Wert wird übertragen, das prüfe ich explizit im Code ab. Muss ich dann den Feedback-Wert anstatt nur ein Polling-Intervall lang, so lange senden, bis der Mehr/Weniger-sample tatsächlich übertragen wurde? Evtl. wird der Feedback-Wert auf der Hostseite nochmal ausgemittelt? Gruß Markus
:
Bearbeitet durch User
Markus G. schrieb: > Gibt es zu dem Feedback-Mechanismus keinerlei Dokumentation? Es gibt die Spezifikation, die die Zusammenarbeit zwischen Host und Device regelt. Für beides gibt es viele Implementationen, die sich in Details unterscheiden. > Ich war davon ausgegangen, dass das eingestellte Feedback-Intervall > automatisch auch der Zeitraum der Gültigkeit eines übertragenen Wertes > ist. Der Feedback-Werte ist (theoretisch) unendlich lange gültig. Er ist deine beste Voraussage für die mittel- bis langfristige Datenrate, die du sehen willst. > Ggf. muss ich dann wirklich eine Regelung im klassischen Sinn > implementieren (der Buffer-Level schwankt dann immer um den ideal > Füllstand). Und Schwankungen (sowohl Buffer-Level als auch Feedback-Wert) sind kein Problem, solange Over-/Underflow vermeidet werden.
Clemens L. schrieb: > Der Feedback-Werte ist (theoretisch) unendlich lange gültig. Er ist > deine beste Voraussage für die mittel- bis langfristige Datenrate, die > du sehen willst. > > Und Schwankungen (sowohl Buffer-Level als auch Feedback-Wert) sind kein > Problem, solange Over-/Underflow vermeidet werden. Hallo Clemens, okay, dann habe ich folgendes Vorgehen im Kopf: - ich starte mit dem Feedback-Wert 48 - ich messe wie viele samples per I2S rausgesendet werden (das Messintervall von 10 auf 100 Frames/ms zu vergrößern, macht dann vermutlich Sinn?) - entsprechend der Messung bilde ich einen gleitenden Mittelwert und sende den als Feedback - ich definiere eine maximale Bufferabweichung: z.B. 10 Samples - wenn die max. Abweichung erreicht ist, addiere ich zu meinem Feedback einen festen Offset (z.B. 0,00001 kHz = 0,01 Hz schneller (die gemessene mittlere Abweichung lag in der Größenordnung von 0,06 Hz)), das könnte man je nach Abweichung im Bufferlevel auch in 2 oder mehr Stufen tun - wenn im darauffolgenden Frame die Bufferabweichung kleiner geworden ist, belasse ich den Wert wie er ist, ist die Abweichung hingegen größer geworden, addiere ich nochmals den Offset - entsprechend wird auch in die andere Richtung verfahren Macht das so Sinn? Gruß Markus
:
Bearbeitet durch User
Markus G. schrieb: > Macht das so Sinn? Nein. Der Feedbackwert gibt die Anzahl der Samples pro Paket (mit Nachkommastellen) an. Das gilt für das gesamte Interval zwischen zwei Feedback Paketen. Langfristig stimmt die Summe aus Feedbackwert multipliziert mit der Anzahl der Audio-Pakete zwischen zwei Feedback Paketen mit der Anzahl an Samples, die der Host erwartet und rausschickt genau überein. Irgendwelche Mittelwertbildungen oder sonstige Tricksereien führen zwangsläufig zu Fehlern (Feedback-Summe stimmt nicht mit Sample-Summe überein). Ein Missverständnis ist, dass der Host unmittelbar auf eine große Feedback-Änderung reagieren muss. Das passiert nicht. Damit die Audio-Geschwindigkeit nicht stark schwank, nehmen die Treiber natürlich auch eine sehr langsame Anpassung der Geschwindigkeit vor, stellen dabei aber trotzdem sicher, dass die Sampleanzahl langfristig ganz genau dem (langfristig gemittelten) Feedbackwert entspricht. Es ist sehr schwierig, den Feedbackwert über Samples zu ermitteln, denn um eine hohe Genauigkeit zu erreichen müsste man ca. 1 Sekunde lang Samples zählen. Besser ist es die Masterclock oder Bitclock auszuwerten.
:
Bearbeitet durch User
"Simple" Lösung: du stellst die Pollingrate für den Sync Endpoint auf die max. zulässige Zeit (512 ms) und summierst die Samples (für einen Audio-Kanal) über 512 SOFs in einem Counter auf. Diesen Wert shiftest du 5 Bits nach links, das ist der Feedbackwert im Q10.14 Format. Dann Counter resetten und wieder die Samples über die nächsten 512 SOFs zählen usw...
:
Bearbeitet durch User
Hallo, Joe F. schrieb: > Ein Missverständnis ist, dass der Host unmittelbar auf eine große > Feedback-Änderung reagieren muss. Das passiert nicht. Ok dann lag dort mein Irrtum. > Es ist sehr schwierig, den Feedbackwert über Samples zu ermitteln, denn > um eine hohe Genauigkeit zu erreichen müsste man ca. 1 Sekunde lang > Samples zählen. Besser ist es die Masterclock oder Bitclock auszuwerten. Das macht natürlich Sinn. Einen Counter auf dem Bitclock hatte ich auch schon implementiert und so die "tatsächliche" Samplerate ermittelt und als Basiswert für den Feedback genommen. Die ermittelte Abweichung lag im Bereich der max. Auflösung des Feedback-Werts. Dort entsteht dann hauptsächlich der Offset der Buffer-Indizes. Die maximale Auflösung im 10.14 Format ist 0,061 Hz. Also mache ich es vom Vorgehen nun so: - Bitclock oder Masterclock wird gezählt und ein gleitender Mittelwert gebildet. Dieser dient als "Basis" für den Feedback-Wert. - zusätzlich zähle ich die Samples die ankommen und die die rausgeschickt werden. So ermittle ich einen langsamen Drift im Buffer (Schreib- und Lesezeiger). Wie handhabe ich nun den Ausgleich eines Drifts im Buffer? Ich muss dann ja einen fixen Offset auf den Feedback-Wert addieren, um den Buffer State wieder auszuregeln. In welcher Größenordnung macht dieser Offset Sinn? Gruß Markus
:
Bearbeitet durch User
Markus G. schrieb: > Also mache ich es vom Vorgehen nun so: > - Bitclock oder Masterclock wird gezählt und ein gleitender Mittelwert > gebildet. Dieser dient als "Basis" für den Feedback-Wert. Nein, kein gleitender Mittelwert. Nur zählen. Das Mitteln geschieht automatisch, da eine ausreichend grosse Anzahl an Samples bzw. Clockticks innerhalb der Feedback-Intervalls aufsummiert wird. > - zusätzlich zähle ich die Samples die ankommen und die die > rausgeschickt werden. Warum? Masterclock oder Bitclock reicht doch. > So ermittle ich einen langsamen Drift im Buffer (Schreib- und > Lesezeiger). Wenn der Feedbackwert stimmt gibt es keinen Drift, denn der Host passt seine Samplerate genau an die des Gerätes an. Das ist ja der Sinn des ganzen. > Wie handhabe ich nun den Ausgleich eines Drifts im Buffer? Es wird keinen Drift geben. > Ich muss dann ja einen fixen Offset auf den Feedback-Wert addieren, um > den Buffer State wieder auszuregeln. In welcher Größenordnung macht > dieser Offset Sinn? Du hast es offenbar nicht verstanden. Der Feedback-Wert ist stumpf die Anzahl an Samples, die pro Paket erwartet werden. Nicht mehr, nicht weniger. Und zwar als Fixpoint-Zahl mit einer Genauigkeit von ca. +/-1 Sample/Sekunde. Um diese Anzahl zu ermitteln, musst du lediglich die USB SOFs als Zeitgeber nehmen und über eine (sinnvoll ausgewählte) Zeitspanne die Samples deines ADCs/DACs zählen. So einfach. Die Regelung übernimmt komplett der Host. Versuche es mal so, wie ich es dir beschrieben habe (ganze Samples über 512 SOFs) und du wirst sehen, dass es funktioniert. Wenn du das am Laufen hast, kannst du auf Zählen der Bitclock oder Masterclock "umbauen". Dabei verkürzt sich lediglich die Intervalllänge, die zum Ermitteln der genauen Samplerate nötig ist.
:
Bearbeitet durch User
Joe F. schrieb: > Markus G. schrieb: >> Also mache ich es vom Vorgehen nun so: >> - Bitclock oder Masterclock wird gezählt und ein gleitender Mittelwert >> gebildet. Dieser dient als "Basis" für den Feedback-Wert. > > Nein, kein gleitender Mittelwert. Nur zählen. Wenn ich es mir recht überlege, spielt es im Grunde keine Rolle. Die entstehende Differenz muss ohnehin anderweitig ausgeregelt werden. >> - zusätzlich zähle ich die Samples die ankommen und die die >> rausgeschickt werden. > > Warum? Masterclock oder Bitclock reicht doch. > Wenn der Feedbackwert stimmt gibt es keinen Drift, denn der Host passt > seine Samplerate genau an die des Gerätes an. Das ist ja der Sinn des > ganzen. > >> Wie handhabe ich nun den Ausgleich eines Drifts im Buffer? > > Es wird keinen Drift geben. Da muss ich dir widersprechen. Es gibt allein durch die Ungenauigkeit des FB-Werts durch das 10.14 Format schon eine Abweichung. Deine Aussage wäre korrekt, wenn man den Feedback unendlich genau übermitteln könnte! Entsprechend laufen die Buffer-Indizes über einen längeren Zeitraum auseinander und es kommt zum Over/Underflow.
:
Bearbeitet durch User
Markus G. schrieb: > Joe F. schrieb: >> Es wird keinen Drift geben. > > Da muss ich dir widersprechen. Es gibt allein durch die Ungenauigkeit > des FB-Werts durch das 10.14 Format schon eine Abweichung. > Deine Aussage wäre korrekt, wenn man den Feedback unendlich genau > übermitteln könnte! > Entsprechend laufen die Buffer-Indizes über einen längeren Zeitraum > auseinander und es kommt zum Over/Underflow. Irrtum. Ein fehlendes Sample wirkt sich minimal auf den darauf folgenden Feedback-Wert aus, und so bleibt das auf alle Zeiten synchron. 10.14 wurde nicht zufällig gewählt, es reicht aus um die Abweichung von nur einem einzelnen Sample zu repräsentieren.
:
Bearbeitet durch User
Vielleicht nochmal zum besseren Verständnis: Die Feedback-Information ist im Grunde ein Sample-Counter. Beim implicit feedback Mechanismus hat der Treiber die Information, wie viele Samples pro Paket vom Gerät kommen über den IN endpoint. Er passt dann die Geschwindigkeit bzw. Datenrate auf dem OUT endpoint an genau diese Sample-Anzahl an. Und ganz genau so funktioniert es mit dem SYNC endpoint, nur dass das Feedback-Paket eben nicht alle ms sondern seltener kommt, und übermittelt wird jeweils die Summe aller Samples im zurückliegenden Intervall. Der Host bekommt also bei beiden Synchronisations-Verfahren die gleiche Information: wie viele Samples erwartet/sendet das Gerät pro Paket. Da die Intervalllänge für SYNC unterschiedlich sein kann (2ms - 512ms), hat man sich entschlossen, diesen Counter-Wert zu normieren, nämlich auf Q14.10. Das hat in der Regel zur Folge, dass die unteren Bits "0" bleiben, die Genauigkeit ist also höher als benötigt. An meinem Vorschlag (über 512 SOFs zu summieren) siehst du auch, dass der Counter genau das 512-fache des Sample-pro-Paket Wertes erreicht, und man diesen Wert daher nochmal um ganze 5 Bits nach links shiften muss, um in ins Q10.14 Format zu bringen. Wenn man die Bitclock zählen würde, ergäbe sich genau der 64-fache Wert ggü. der Zählung der Samples = 5 Bits mehr, kein Bitshift mehr nötig... so ein Zufall... ;-) Das schöne an der ganzen Sache: das Device kann strohdumm sein (wichtig für günstige Hardware), es muss selbst nichts regeln oder rechnen, es muss nur richtig zählen können und die Regelung übernimmt komplett der Treiber. Er sorgt dafür, dass die Buffer im Gleichgewicht bleiben.
:
Bearbeitet durch User
Hallo Joe, danke für die ausführlichere Beschreibung der Thematik. Das hat nochmal etwas Licht ins Dunkel gebracht. Habe das nun zum Test so implementiert: Joe F. schrieb: > "Simple" Lösung: du stellst die Pollingrate für den Sync Endpoint auf > die max. zulässige Zeit (512 ms) und summierst die Samples (für einen > Audio-Kanal) über 512 SOFs in einem Counter auf. > Diesen Wert shiftest du 5 Bits nach links, das ist der Feedbackwert im > Q10.14 Format. Dann Counter resetten und wieder die Samples über die > nächsten 512 SOFs zählen usw... Mein Zähler hängt am DMA-Event und zählt somit jeden einzelnen Sample (96 bei 2 Kanälen). Es ergibt sich folglich ein Count von 49152 über 512 Frames. Ich muss also nur um 4 Bit schieben. Ich habe nun auch die Berechnungsweise nochmal neu nachvollzogen und verstanden. Durch das Schieben kann es auch nicht zu Werten kommen, die nicht exakt abgebildet werden können (zumindest wenn man immer über ein Vielfaches von 2 zählt). 49152 = 2^10 * 48 --> schieben um weitere 4 Stellen für 10.14 Format, die restlichen Bits sind Nullen. Das Ganze liefert auch plausible Werte: im Bild im Anhang sind die ersten 25 Zählintervalle. Ist der Zählerwert 49153 (ein "halber" Sample Abweichung bei 2 Kanälen), entspricht das 49153/1024 = 48,0009765625. Der übertragene FB-Wert ist dann 10-00-0C (ausgelesen mit Wireshark) was 48,0009765625 entspricht. Vorgehensweise und Implementierung passen also. Soweit zur Theorie. Buffer Over/Underflow kommt nach ca 4-5 min :D Wo kommt nun dieser Drift her? Gruß Markus
:
Bearbeitet durch User
2 Kanäle sind doch Stereo, wie kann da eine ungerade Sample-Anzahl (in Summe) zustande kommen? Deine USB Pakete enthalten ja hoffentlich auch immer gleich viele Samples für L und R. Und: wenn du einen Mac hast, und das Audiosystem mal mit einem Gerät verwirrt hast, das Bufferprobleme erzeugt, dann hilft meist nur ein Reboot um alles wieder ins Lot zu bekommen. Und: wieviele Samples hast du als pre-buffering vorgesehen?
:
Bearbeitet durch User
Joe F. schrieb: > 2 Kanäle sind doch Stereo, wie kann da eine ungerade Sample-Anzahl > (in Summe) zustande kommen? Ich zähle jeden einzelnen DMA-Beat (geht nicht anders). Das ist jeweils ein Abtastwert, sprich ein Abtastwert eines Kanals. Bei 48 kHz zähle ich folglich 96 Counts pro Frame bei stereo. Getestet wird unter Windows. Edit: Over/Underflow kommt nach ca. 2,5 min. Habe ich da noch einen neuen Denkfehler produziert? Buffer ist für 4 Frames ausgelegt. Entsprechend buffere ich 2 Frames und starte dann im SOF vom dritten Frame die I2S-Ausgabe. Gruß Markus
:
Bearbeitet durch User
Achso, verstehe, dein DMA Handler wird 2x aufgerufen, einmal fürs linke einmal fürs rechte Sample. Das ist gut, damit erhöhst du die Genauigkeit von 2 auf 1 Sample/Sekunde. Ein oft gemachter Fehler ist, den Counter z.B. in deiner DMA Routine zu erhöhen, und dann woanders (z.B. im SOF Interrupt) zu resetten. Das kann zu einer Racecondition führen. Besser ist im DMA Handler den Counter zu erhöhen, und im SOF Interrupt die Differenz zu einem Sample-Count-Buffer zu nehmen anstatt den Counter zurückzusetzen.
1 | uint32_t sample_cnt = 0; |
2 | uint32_t feedback_value = 0x000c0000; // good start value for 48 KHz |
3 | |
4 | dma_interrupt() |
5 | { |
6 | sample_cnt++; |
7 | } |
8 | |
9 | sof_interrupt() |
10 | { |
11 | uint32_t sample_cnt_delta; |
12 | static uint32_t sample_cnt_buf = 0; |
13 | static uint16_t sof_cnt = 0; |
14 | |
15 | sample_cnt_delta = sample_cnt - sample_cnt_buf; |
16 | sof_cnt++; |
17 | if (sof_cnt >= 512) |
18 | { |
19 | feedback_value = sample_cnt_delta << 4; |
20 | sample_cnt_buf = sample_cnt; |
21 | sof_cnt = 0; |
22 | } |
23 | } |
24 | |
25 | sync_endpoint_handler() // make sure polling interval is set to 512 ms |
26 | { |
27 | // send out current "feedback_value" |
28 | } |
:
Bearbeitet durch User
Joe F. schrieb: > Achso, verstehe, dein DMA Handler wird 2x aufgerufen, einmal fürs > linke > einmal fürs rechte Sample. Genau so ist es. > Ein oft gemachter Fehler ist, den Counter z.B. in deiner DMA Routine zu > erhöhen, und dann woanders (z.B. im SOF Interrupt) zu resetten. Das kann > zu einer Racecondition führen. > Besser ist im DMA Handler den Counter zu erhöhen, und im SOF Interrupt > die Differenz zu einem Sample-Count-Buffer zu nehmen anstatt den Counter > zurückzusetzen. Habe den Counter extra free-running implementiert. Sprich wenn ich im SOF-Interrupt den Zähler auswerte, prüfe ich erst, ob ein Overflow auftreten ist und verrechne das dann entsprechend. In meiner SOF-Routine mache ich das im Prinzip genau so wie in deinem Code.
:
Bearbeitet durch User
Markus G. schrieb: > Sprich wenn ich im SOF-Interrupt den Zähler auswerte, prüfe ich erst, ob > ein Overflow auftreten ist und verrechne das dann entsprechend. Vielleicht liegt da ja der Fehler? Bei der Differenzbildung zweier unsigned Werte muss keine Sonderbehandlung beim Overflow gemacht werden (vorausgesetzt es ist kein mehrfacher Overflow aufgetreten, was in diesem Fall nicht sein kann). Generell: dein Feedback-Wert sieht plausibel aus. Alles nahe 0x0c0000 ist korrekt. Du kannst ja mal gucken, ob du irgendwo Aussreisser bekommst (deutlich zu groß, deutlich zu klein).
Joe F. schrieb: > Vielleicht liegt da ja der Fehler? > Bei der Differenzbildung zweier unsigned Werte muss keine > Sonderbehandlung beim Overflow gemacht werden (vorausgesetzt es ist kein > mehrfacher Overflow aufgetreten, was in diesem Fall nicht sein kann). Da hatte ich mir noch garnicht genauer Gedanke zu gemacht, dass das bei unsigned keine Rolle spielt. Momentan ist das so implementiert, das müsste auch stimmen, da mein Testfall ohne Feedback, sondern mit einfacher Buffer-Interpolation funktioniert:
1 | /* calc send samples over period of Tmeas from I2S sample counter */
|
2 | if (tc_sample_count > tc_sample_count_last_frame) |
3 | {
|
4 | //counter hasn't overflowed
|
5 | as_buffer_level = tc_sample_count - tc_sample_count_last_frame; |
6 | }
|
7 | else
|
8 | { //counter has overflowed |
9 | as_buffer_level = tc_sample_count + 65536 - tc_sample_count_last_frame; |
10 | }
|
> Generell: dein Feedback-Wert sieht plausibel aus. > Alles nahe 0x0c0000 ist korrekt. > Du kannst ja mal gucken, ob du irgendwo Aussreisser bekommst (deutlich > zu groß, deutlich zu klein). Habe gerade noch auf Abweichungen größer 2 Counts geprüft, sprich counter > 49154 oder < 49150. Dieser Fall tritt nicht auf.
:
Bearbeitet durch User
Markus G. schrieb: > as_buffer_level = (tc_sample_count + 65536 - > tc_sample_count_last_frame); Von welchem Typ sind die Variablen jeweils? Bei 32 Bit hättest du ein Problem... ;-) Und bei 16 Bit sind die +65536 eh sinnlos, also bleibt: as_buffer_level = tc_sample_count - tc_sample_count_last_frame; Das funktioniert in beiden Fällen, egal ob overflow oder nicht. Voraussetzung ist, dass alle 3 Variablen unsigned und gleich breit sind.
:
Bearbeitet durch User
Joe F. schrieb: > Von welchem Typ sind die Variablen jeweils? > Bei 32 Bit hättest du ein Problem... ;-) > Und bei 16 Bit sind die +65536 eh sinnlos, also bleibt: Die +65536 waren noch aus einer vorherigen Implementierung. Funktionierte zwar, habe das aber bei der Gelegenheit gleich auf 16 Bit umgebaut. Spart immerhin auch etwas Code. Sieht nun in meinem SOF-Interrupt hauptsächlich so aus:
1 | //count over T_MEAS frames
|
2 | if (++as_sof_cnt == as_Tmeas) |
3 | {
|
4 | /* calc sent samples over period of Tmeas from I2S sample counter */
|
5 | fb_new_SR = tc_sample_count - tc_sample_count_last_frame; |
6 | |
7 | //save current sample count
|
8 | tc_sample_count_last_frame = tc_sample_count; |
9 | |
10 | //convert to 10.14 format (uint32)
|
11 | fb_new_SR_1014 = (fb_new_SR << 4); |
12 | |
13 | //put bytes in fb endpoint buffer
|
14 | PutBytesToBuf(fb_in_buf, fb_new_SR_1014); |
15 | |
16 | //reset frame counter
|
17 | as_sof_cnt = 0; |
18 | }
|
Ausreiser größer ein Samplepaar gibt es nicht, die ermittelten Werte sind auch plausibel (entweder 49152 oder 49153 in den ersten 100 Messungen). Der FB-Wert stimmt auch. Trotzdem Buffer Over/Underflow nach ein paar Minuten. Irgendwo muss nun im Betrieb noch ein Drift entstehen. Gruß Markus
:
Bearbeitet durch User
Tja. Jedenfalls hast du jetzt die Funktion des SYNC Endpoints (reiner Samplecounter) verstanden und lieferst korrekte Werte an den Treiber. Es gibt noch eine Reihe weiterer Fehlerquellen, die in Betracht kommen, und dir wird wohl nichts anderes übrig bleiben als nicht nur die ersten 100 Pakete, sondern den Traffic über mehrere Minuten intensiv zu analysieren. Keine Ahnung welche Möglichkeiten dir Wireshark bietet, ich vermute mal man kann USB Pakete minutenlang loggen und dann offline auswerten. Dann kannst du dich auf die Suche machen nach - fehlenden Paketen (mehr als 1 SOF Abstand von Audio-Paket zu Audio-Paket) - Audio-Pakete falscher Größe (zu viele, zu wenig Samples als erwartet) - Doch irgendwo einen Ausreisser in der Feedback Information (nach mehreren Minuten) - Falscher Abstand (!= 512 SOFs) von 2 Sync-Paketen - Auch kannst du den Audiostream dazu verwenden die Bufferlevel zu debuggen (Bufferlevel als Audiosamples übertragen). Dann siehst du, ob es wirklich kontinuierlich drifted, oder eigentlich relativ lange stabil ist und irgendwo plötzlich ein Sprung auftritt. - auch ist es möglich, dass der Wireshark Treiber Störungen verursacht...
:
Bearbeitet durch User
Joe F. schrieb: > Dann kannst du dich auf die Suche machen nach > - fehlenden Paketen (mehr als 1 SOF Abstand von Audio-Paket zu > Audio-Paket) > - Audio-Pakete falscher Größe (zu viele, zu wenig Samples als erwartet) > - Doch irgendwo einen Ausreisser in der Feedback Information (nach > mehreren Minuten) > - Falscher Abstand (!= 512 SOFs) von 2 Sync-Paketen Habe nun diverses Debugging gemacht. Das meiste lässt sich recht einfach im Debugger-Modus von Atmel Studio erledigen, bzw. entsprechende Haltepunkte setzen. Habe für die jeweiligen möglichen Problem if-cases eingebaut und dort dann einen Haltepunkt gesetzt, für den Fall, dass die Bedingung zutrifft. Das habe ich dann einfach laufen lassen bis der Over/Underrun kam. Folgendes habe ich ausschließen können: - ausbleibende Pakete - Audio-Pakete falscher Größe - Ausreißer in der Feedback Information - Falscher Abstand Was mir im Endeffekt aufgefallen ist, als ich nach falscher Größe der Audiopakete gesucht habe: Der Host schickt immer nur max. 48 Samples. Eigentlich müsste in nahezu konstanten Abständen 49 Samples kommen. Dem muss ich morgen nochmal genauer auf den Grund gehen. Im ersten Moment kann ich mir das allerdings nicht erklären. Der Endpoint (maxPacketSize) ist ausreichend groß, um auch mehr Samples übertragen zu können. Gruß Markus
:
Bearbeitet durch User
Joe F. schrieb: > Ein Missverständnis ist, dass der Host unmittelbar auf eine große > Feedback-Änderung reagieren muss. Das passiert nicht. Damit die > Audio-Geschwindigkeit nicht stark schwank, nehmen die Treiber natürlich > auch eine sehr langsame Anpassung der Geschwindigkeit vor, stellen dabei > aber trotzdem sicher, dass die Sampleanzahl langfristig ganz genau dem > (langfristig gemittelten) Feedbackwert entspricht. Das ist falsch. Das Verhalten des Hosts ist in Abschnitt 5.12.4.2 der USB-2.0-Spezifikation genau definiert: > Each (micro)frame, an adaptive source adds F_f to any remaining > fractional sample count from the previous (micro)frame, sources the > number of samples in the integer part of the sum, and retains the > fractional sample count for the next (micro)frame. The source can look > at the behavior of F_f over many (micro)frames to determine an even > more accurate rate, if it needs to. Feedback-Werte mit großen Änderungen müssen also sofort angewendet werden. Und die Hosts sind auch so implementiert; siehe z.B. snd_usb_handle_sync_urb() und snd_usb_endpoint_next_packet_size() in https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/sound/usb/endpoint.c Das Gerät ist dafür verantwortlich, starke Schwankungen zu vermeiden: > P should not be zero in order to keep the deviation in the number of > samples sourced to less than 1 in the event of a lost F_f value. ... und Drift auszugleichen: > It is possible that the source will deliver one too many or one too few > samples over a long period due to errors or accumulated inaccuracies in > measuring F_f . The sink must have sufficient buffer capability to > accommodate this. When the sink recognizes this condition, it should > adjust the reported F_f value to correct it. This may also be necessary > to compensate for relative clock drifts. The implementation of this > correction process is endpoint-specific and is not specified.
Clemens L. schrieb: > Joe F. schrieb: >> Ein Missverständnis ist, dass der Host unmittelbar auf eine große >> Feedback-Änderung reagieren muss. (...) > > Das ist falsch. > > Das Verhalten des Hosts ist in Abschnitt 5.12.4.2 der > USB-2.0-Spezifikation genau definiert: >> Each (micro)frame, an adaptive source adds F_f to any remaining >> fractional sample count from the previous (micro)frame, sources the >> number of samples in the integer part of the sum, and retains the >> fractional sample count for the next (micro)frame. > > Feedback-Werte mit großen Änderungen müssen also sofort angewendet > werden. Ja, der geänderte Feedback-Wert geht natürlich sofort in die Berechnungen des Hosts ein. Was ich meinte ist, dass der Feedback-Wert keine "Anforderung einer bestimmten Paketgröße ist". Beispiel so sieht es nicht aus: FB: 40 40 40 50 40 40 40 40 40 ... OUT: 40 40 40 40 40 50 40 40 40 ... sondern eher so: FB: 40 40 40 50 40 40 40 40 40 ... OUT: 40 40 40 40 40 41 41 41 41 ... D.h. der Host reagiert auf einen geänderten FB-Wert sehr moderat, um hörbare Frequenzschwankungen zu verhindern. Dass der Host die genaue Samplerate über einen längeren Zeitraum ermittelt ergibt sich auch aus: >> The source can look >> at the behavior of F_f over many (micro)frames to determine an even >> more accurate rate, if it needs to. > Das Gerät ist dafür verantwortlich, starke Schwankungen zu vermeiden: >> P should not be zero in order to keep the deviation in the number of >> samples sourced to less than 1 in the event of a lost F_f value. P>0 meint, dass für die Berechnung des Feedback-Wertes möglichst eine Clock genommen werden soll, die ein Vielfaches der Samplerate ist, das wurde weiter oben ja schon festgestellt. Da der DMA callback beim PO ja mit FS*2 aufgerufen wird, ist dieses Kriterium erfüllt, und die Abweichung kann auf 1 Sample/s genau berechnet werden. > ... und Drift auszugleichen: >> It is possible that the source will deliver one too many or one too few >> samples over a long period due to errors or accumulated inaccuracies in >> measuring F_f . The sink must have sufficient buffer capability to >> accommodate this. When the sink recognizes this condition, it should >> adjust the reported F_f value to correct it. This may also be necessary >> to compensate for relative clock drifts. The implementation of this >> correction process is endpoint-specific and is not specified. Das sind 2 Dinge. Warum man "relative clock drifts" noch zusätzlich kompensieren sollte erschließt sich mir nicht, denn genau dies geschieht bereits durch die Berechnung des FB Wertes über den Vergleich von SOF rate zu Masterclock/Bitclock rate. Den 1. Punkt ("errors or accumulated inaccuracies") habe ich in der Praxis auch im Betrieb über 24h noch nie beobachtet, aber gut, man kann sich darauf vorbereiten. Die Korrektur sollte aber dann eher im Bereich < 1 Sample/Sekunde geschehen (da dieser Fehler ja auch im Vergleich sehr, sehr klein ist). Das Problem im Moment ist aber wohl, dass der Host beim PO nicht auf den SYNC endpoint reagiert. Das muss natürlich zuerst mal gefixed werden.
:
Bearbeitet durch User
Joe F. schrieb: > Häng mal deinen aktuellen Descriptor rein.
1 | [Port2] : USB-Verbundgerät |
2 | |
3 | |
4 | Is Port User Connectable: yes |
5 | Is Port Debug Capable: no |
6 | Companion Port Number: 0 |
7 | Companion Hub Symbolic Link Name: |
8 | Protocols Supported: |
9 | USB 1.1: yes |
10 | USB 2.0: yes |
11 | USB 3.0: no |
12 | |
13 | Device Power State: PowerDeviceD0 |
14 | |
15 | ---===>Device Information<===--- |
16 | English product name: "USB2I2S-Bridge" |
17 | |
18 | ConnectionStatus: |
19 | Current Config Value: 0x01 -> Device Bus Speed: Full (is not SuperSpeed or higher capable) |
20 | Device Address: 0x0A |
21 | Open Pipes: 0 |
22 | *!*ERROR: No open pipes! |
23 | |
24 | ===>Device Descriptor<=== |
25 | bLength: 0x12 |
26 | bDescriptorType: 0x01 |
27 | bcdUSB: 0x0200 |
28 | bDeviceClass: 0x00 -> This is an Interface Class Defined Device |
29 | bDeviceSubClass: 0x00 |
30 | bDeviceProtocol: 0x00 |
31 | bMaxPacketSize0: 0x40 = (64) Bytes |
32 | idVendor: 0x03EB = Atmel Corporation |
33 | idProduct: 0x2423 |
34 | bcdDevice: 0x0100 |
35 | iManufacturer: 0x01 |
36 | English (United States) "Atmel" |
37 | iProduct: 0x02 |
38 | English (United States) "USB2I2S-Bridge" |
39 | iSerialNumber: 0x03 |
40 | English (United States) "v0.232" |
41 | bNumConfigurations: 0x01 |
42 | |
43 | ---===>Full Configuration Descriptor<===--- |
44 | |
45 | ===>Configuration Descriptor<=== |
46 | bLength: 0x09 |
47 | bDescriptorType: 0x02 |
48 | wTotalLength: 0x006D -> Validated |
49 | bNumInterfaces: 0x02 |
50 | bConfigurationValue: 0x01 |
51 | iConfiguration: 0x00 |
52 | bmAttributes: 0xC0 -> Self Powered |
53 | MaxPower: 0x00 = 0 mA |
54 | |
55 | ===>Interface Descriptor<=== |
56 | bLength: 0x09 |
57 | bDescriptorType: 0x04 |
58 | bInterfaceNumber: 0x00 |
59 | bAlternateSetting: 0x00 |
60 | bNumEndpoints: 0x00 |
61 | bInterfaceClass: 0x01 -> Audio Interface Class |
62 | bInterfaceSubClass: 0x01 -> Audio Control Interface SubClass |
63 | bInterfaceProtocol: 0x00 |
64 | iInterface: 0x00 |
65 | |
66 | ===>Audio Control Interface Header Descriptor<=== |
67 | bLength: 0x09 |
68 | bDescriptorType: 0x24 (CS_INTERFACE) |
69 | bDescriptorSubtype: 0x01 (HEADER) |
70 | bcdADC: 0x0100 |
71 | wTotalLength: 0x001E |
72 | bInCollection: 0x01 |
73 | baInterfaceNr[1]: 0x01 |
74 | |
75 | ===>Audio Control Input Terminal Descriptor<=== |
76 | bLength: 0x0C |
77 | bDescriptorType: 0x24 (CS_INTERFACE) |
78 | bDescriptorSubtype: 0x02 (INPUT_TERMINAL) |
79 | bTerminalID: 0x01 |
80 | wTerminalType: 0x0101 (USB streaming) |
81 | bAssocTerminal: 0x00 |
82 | bNrChannels: 0x02 |
83 | wChannelConfig: 0x0003 |
84 | (Left Front (L)) |
85 | (Right Ront (R)) |
86 | iChannelNames: 0x00 |
87 | iTerminal: 0x00 |
88 | |
89 | ===>Audio Control Output Terminal Descriptor<=== |
90 | bLength: 0x09 |
91 | bDescriptorType: 0x24 (CS_INTERFACE) |
92 | bDescriptorSubtype: 0x03 (OUTPUT_TERMINAL) |
93 | bTerminalID: 0x02 |
94 | wTerminalType: 0x0301 (Speaker) |
95 | bAssocTerminal: 0x00 |
96 | bSourceID: 0x01 |
97 | iTerminal: 0x00 |
98 | |
99 | ===>Interface Descriptor<=== |
100 | bLength: 0x09 |
101 | bDescriptorType: 0x04 |
102 | bInterfaceNumber: 0x01 |
103 | bAlternateSetting: 0x00 |
104 | bNumEndpoints: 0x00 |
105 | bInterfaceClass: 0x01 -> Audio Interface Class |
106 | bInterfaceSubClass: 0x02 -> Audio Streaming Interface SubClass |
107 | bInterfaceProtocol: 0x00 |
108 | iInterface: 0x00 |
109 | |
110 | ===>Interface Descriptor<=== |
111 | bLength: 0x09 |
112 | bDescriptorType: 0x04 |
113 | bInterfaceNumber: 0x01 |
114 | bAlternateSetting: 0x01 |
115 | bNumEndpoints: 0x02 |
116 | bInterfaceClass: 0x01 -> Audio Interface Class |
117 | bInterfaceSubClass: 0x02 -> Audio Streaming Interface SubClass |
118 | bInterfaceProtocol: 0x00 |
119 | iInterface: 0x00 |
120 | |
121 | ===>Audio Streaming Class Specific Interface Descriptor<=== |
122 | bLength: 0x07 |
123 | bDescriptorType: 0x24 (CS_INTERFACE) |
124 | bDescriptorSubtype: 0x01 (AS_GENERAL) |
125 | bTerminalLink: 0x01 |
126 | bDelay: 0x00 |
127 | wFormatTag: 0x0001 (PCM) |
128 | |
129 | ===>Audio Streaming Format Type Descriptor<=== |
130 | bLength: 0x0B |
131 | bDescriptorType: 0x24 (CS_INTERFACE) |
132 | bDescriptorSubtype: 0x02 (FORMAT_TYPE) |
133 | bFormatType: 0x01 (FORMAT_TYPE_I) |
134 | bNrChannels: 0x02 |
135 | bSubframeSize: 0x03 |
136 | bBitResolution: 0x18 (24) |
137 | bSamFreqType: 0x01 (Discrete) |
138 | tSamFreq[1]: 0x00BB80 (48000 Hz) |
139 | |
140 | ===>Endpoint Descriptor<=== |
141 | bLength: 0x09 |
142 | bDescriptorType: 0x05 |
143 | bEndpointAddress: 0x02 -> Direction: OUT - EndpointID: 2 |
144 | bmAttributes: 0x05 -> Isochronous Transfer Type, Synchronization Type = Asynchronous, Usage Type = Data Endpoint |
145 | wMaxPacketSize: 0x0138 = 0x138 bytes |
146 | wInterval: 0x0001 |
147 | bSyncAddress: 0x82 |
148 | |
149 | ===>Audio Streaming Class Specific Audio Data Endpoint Descriptor<=== |
150 | bLength: 0x07 |
151 | bDescriptorType: 0x25 (CS_ENDPOINT) |
152 | bDescriptorSubtype: 0x01 (EP_GENERAL) |
153 | bmAttributes: 0x00 |
154 | bLockDelayUnits: 0x01 (Milliseconds) |
155 | wLockDelay: 0x0000 |
156 | |
157 | ===>Endpoint Descriptor<=== |
158 | bLength: 0x09 |
159 | bDescriptorType: 0x05 |
160 | bEndpointAddress: 0x82 -> Direction: IN - EndpointID: 2 |
161 | bmAttributes: 0x11 -> Isochronous Transfer Type, Synchronization Type = No Synchronization, Usage Type = Feedback Endpoint |
162 | wMaxPacketSize: 0x0003 = 0x03 bytes |
163 | wInterval: 0x0901 |
164 | bSyncAddress: 0x00 |
Müsste eigentlich soweit passen. Habe einfach mal pauschal 4 Samples pro Kanal extra Platz im Endpoint gelassen. Gruß Markus
Joe F. schrieb: > Was ich meinte ist, dass der Feedback-Wert > keine "Anforderung einer bestimmten Paketgröße ist". Doch. Wenn der Feedback-Wert xx.yy ist, dann ist die Paketgröße immer xx oder xx+1. > Beispiel > > so sieht es nicht aus: > FB: 40 40 40 50 40 40 40 40 40 ... > OUT: 40 40 40 40 40 50 40 40 40 ... > > sondern eher so: > FB: 40 40 40 50 40 40 40 40 40 ... > OUT: 40 40 40 40 40 41 41 41 41 ... Die Spezifikation verlangt zwingend das erste Verfahren, nämlich "adds F_f to any remaining fractional sample count ..., sources the number of samples in the integer part of the sum ...". Wie auch in Beitrag "Re: USB Audio Sync Endpoint Implementierung" beschrieben. Und genau so ist es auch in der Praxis implementiert. > Dass der Host die genaue Samplerate über einen längeren Zeitraum > ermittelt ergibt sich auch aus: > >>> The source can look at the behavior of F_f over many (micro)frames to >>> determine an even more accurate rate, if it needs to. Wenn der Feedback-Wert zwischen 40 und 41 pendelt, dann kann ein Durchschnitt helfen, um einen genaueren Wert zu ermitteln. Aber bei großen Sprüngen wie 40->50 helfen die vorherigen 4x-Werte nicht dabei, den genauen Wert zwischen 4F/50/51 zu bestimmen. > Warum man "relative clock drifts" noch zusätzlich kompensieren sollte > erschließt sich mir nicht Es könnte sich auf verschiedene Clocks im Host beziehen. Auf jeden Fall kann man normalerweise nicht herausfinden, was genau die Fehlerquelle ist.
Joe F. schrieb: > Das Problem im Moment ist aber wohl, dass der Host beim PO nicht auf den > SYNC endpoint reagiert. > Das muss natürlich zuerst mal gefixed werden. Das ist fürs erste die Hauptbaustelle. Habe grade nochmals geprüft, aber das Device bekommt immer 288 Byte (48 Samples) pro Frame, obwohl ein anderer Feedback-Wert übertragen wird. Ich habe nochmal die Spec hinsichtlich des Descriptors gecheckt, das müsste aber eigentlich alles passen. Ich tue mich gerade etwas schwer, das Problem genauer einzukreisen. Habt ihr dahingehend Tipps? Gruß Markus
Spontan fallen mir nur 3 Dinge auf: - die Endpoint-Nummern haben eine Lücke. 0x01 und 0x81 sind nicht benutzt, du solltest besser diese EP-Nummern nehmen statt 0x02 und 0x82 - bcdUSB: 0x0200 Ich bin mir nicht sicher, ob man für ein Audio 1.0 Gerät nicht auch hier 0x0100 eintragen müsste, damit nicht der Audio 2.0 Treiber verwendet wird...?! und - bmAttributes: 0x11 -> Isochronous Transfer Type, Synchronization Type = No das sollte glaube ich auf 0x01 stehen D3..2: Synchronization type 00 = None D1..0: Transfer type 01 = Isochronous
:
Bearbeitet durch User
Joe F. schrieb: > Spontan fallen mir nur 3 Dinge auf: > - die Endpoint-Nummern haben eine Lücke. 0x01 und 0x81 sind nicht > benutzt, du solltest besser diese EP-Nummern nehmen statt 0x02 und 0x82 Habe ich angepasst. > - bcdUSB: 0x0200 > Ich bin mir nicht sicher, ob man für ein Audio 1.0 Gerät nicht auch hier > 0x0100 eintragen müsste, damit nicht der Audio 2.0 Treiber verwendet > wird...?! Habe mal geschaut, welcher Treiber momentan geladen wird (siehe Bild). Es wird der UAC1 Treiber geladen. > - bmAttributes: 0x11 -> Isochronous Transfer Type, Synchronization Type > = No > das sollte glaube ich auf 0x01 stehen > > D3..2: Synchronization type 00 = None > D1..0: Transfer type 01 = Isochronous Das stimmt. Die Bits 4 und 5 für den Usage Type gibt es erst in der UAC2. Habe ich angepasst. Die Implementierung wird richtiger, aber noch keine Verbesserung des Problems. Gruß Markus
:
Bearbeitet durch User
Ändere mal testweise den Seriennummernstring ("v0.232") auf irgendetwas anderes. Ich kann mich dunkel erinnern, dass Windows so ne Macke hat und sich die Konfiguration eines Gerätes "merkt" (zur Seriennummer), und wenn man dann an der Konfiguration etwas ändert hat man ein Problem. Und du hattest das Gerät ja schon mal ohne Sync-Endpoint am Rechner, vermutlich mit gleicher Seriennummer...
Joe F. schrieb: > Ich kann mich dunkel erinnern, dass Windows so ne Macke hat und > sich die Konfiguration eines Gerätes "merkt" (zur Seriennummer), und > wenn man dann an der Konfiguration etwas ändert hat man ein Problem. Ja dem ist so. Auf den Umstand bin ich bei Tests mit 44,1 kHz schon gestoßen. Deswegen habe ich auch immer die Seriennummer und ggf. den Device-Name erhöht.
Hallo, bin derweil etwas festgefahren bei der Suche nach der Ursache, dass der FB-Wert nicht berücksichtigt wird. Der Descriptor müsste stimmen, habe ich explizit nochmal geprüft. Die FB-Werte werden vom Host auch korrekt angefordert und dann übertragen. Die Werte ansich sind auch plausibel (auch über längere Zeit). Es kommen aber immer nur 48 samples pro Frame. Hat jemand noch einen Tipp, woran das liegen könnte? Gruß Markus
Bin derweil etwas weitergekommen. Habe vom Zählen der Samples auf Zählen des Masterclock umgebaut. Das verbessert immerhin die Kontrolle über den Datenstrom, da öfter ein FB übertragen werden kann. Nach der USB 2.0 Spec: Tmeas = 2^K mit K=10 --> 1024 Frames werden gemessen, um Genauigkeit 1 Sample zu erreichen. F_mclk = Fs * 2^P 12288 kHz = 256 * 48 kHz = 2^8 * 48 --> P = 8 Tmeas = 2^(K-P) = 4 ms Es muss also mindestens 4 Frames lang gemessen werden. Das passt bei 12,288 MHz noch gut, da mit einem 16 Bit Counter machbar. Habe das nun so implementiert. Am Descriptor habe ich nur von pollingrate 9 auf 2 umgestellt (von 512 auf 4 Frames). Sonst habe ich im Prinzip nichts geändert. Auf wundersame Art und Weise liefert USB nun von 48 abweichende Werte (47 und 49 Samples pro Frame). Der Feedback scheint also nun vom Host auch angewendet zu werden. Die übertragenen Werte sind nach wie vor plausibel. Die Laufzeit bis zum Over/Underflow hat sich ca. verdreifacht. Er kommt aber nach wie vor. In der USB 2.0 Spec S. 75 steht dazu:
1 | It is possible that the source will deliver one too many or one too few samples over a long period due to |
2 | errors or accumulated inaccuracies in measuring Ff. The sink must have sufficient buffer capability to |
3 | accommodate this. |
4 | When the sink recognizes this condition, it should adjust the reported Ff value to correct |
5 | it. This may also be necessary to compensate for relative clock drifts. The implementation of this |
6 | correction process is endpoint-specific and is not specified. |
Das heißt für mich: Es gibt eine relativen Drift, Ungenauigkeiten bei der Messung etc. Diesen muss man durch ein Tracking der Schreib/Lesepointer im Buffer manuell korrigieren. Sprich man muss einen Offset auf den FB-Wert addieren, um den Drift im Buffer auszugleichen. Gruß Markus
Markus G. schrieb: > Auf wundersame Art und Weise liefert USB nun von 48 abweichende Werte > (47 und 49 Samples pro Frame). Der Feedback scheint also nun vom Host > auch angewendet zu werden. Die übertragenen Werte sind nach wie vor > plausibel. Das klingt gut. Markus G. schrieb: > Das heißt für mich: > Es gibt eine relativen Drift, Ungenauigkeiten bei der Messung etc. > Diesen muss man durch ein Tracking der Schreib/Lesepointer im Buffer > manuell korrigieren. Sprich man muss einen Offset auf den FB-Wert > addieren, um den Drift im Buffer auszugleichen. Tja. Ich glaube da eher noch an einen Messfehler. Meine USB Audio 2.0 Geräte machen keine Korrektur und laufen über 48h stabil durch, vermutlich auch länger, aber länger habe ich nie gemessen. Der Buffer schwankt etwas, aber nie so weit, dass es zu Underruns oder Overflows kommt (allerdings mit Mac OSX). Wenn du eine Korrektur einbauen willst ist das bestimmt keine schlechte Idee, nur muss die eben sehr minimal und langsam eingreifen. Also < 1 Sample/s.
Joe F. schrieb: > Tja. Ich glaube da eher noch an einen Messfehler. Meine USB Audio 2.0 > Geräte machen keine Korrektur und laufen über 48h stabil durch, > vermutlich auch länger, aber länger habe ich nie gemessen. > Der Buffer schwankt etwas, aber nie so weit, dass es zu Underruns oder > Overflows kommt (allerdings mit Mac OSX). Wo du das Thema Messfehler geschrieben hast, habe ich nochmal mit Wireshark die ankommenden Feedback Pakete beim Host geprüft. Die Pakete sind zum Großteil plausibel: Der Wert pendelt zwischen 00000c (48) und 10000c (48,000977), vereinzelt noch 20000c (48,001953). Das scheint in Ordnung. Es gibt aber vereinzelt einen Ausreißer: f0ff0b, das entspricht 46,999023. Also eine Abweichung von 1 kHz. Und das auch noch in die entgegengesetzte Richtung wie die restlichen Werte. Da scheint mir noch ein Problem zu liegen. Ich konnte aber noch nicht festmachen, woran das liegt. Die Zeitpunkte, zu denen dieser Wert auftaucht sind auch nicht äquidistant soweit ich das sehe. Ich habe den Zähler momentan an den Clock der PLL gehängt. Die läuft mit 49,152 MHz. Der Counter nutzt dann einen Teiler von 4. Kann dort evtl. eine Ungenauigkeit entstehen? > Wenn du eine Korrektur einbauen willst ist das bestimmt keine schlechte > Idee, nur muss die eben sehr minimal und langsam eingreifen. Also < 1 > Sample/s. Das hätte immerhin den Vorteil, wenn der Zähler auf dem Mclk Unfug treibt, regelt es die Buffer-Korrektur mit aus.
:
Bearbeitet durch User
Markus G. schrieb: > Es gibt aber vereinzelt einen Ausreißer: f0ff0b, das entspricht > 46,999023 0x0bfff0 sind 47,999 KHz, passt also, kein Ausreisser.
Joe F. schrieb: > Markus G. schrieb: >> Es gibt aber vereinzelt einen Ausreißer: f0ff0b, das entspricht >> 46,999023 > > 0x0bfff0 sind 47,999 KHz, passt also, kein Ausreisser. Du hast recht! Hatte einen Vorzeichenfehler in meinem Excel-Umrechnungstool in diesem Fall. Anbei der Wertebereich des FB, der an den Host gesendet wird. Die Werte sind also plausibel, mit einer max. Abweichung von +- 3 Samples pro Sekunde. Etwas genauer würde es wohl werden, wenn ich direkt den Clock der PLL zähle. Dann werde ich jetzt noch den Samplezähler zusätzlich wieder implementieren, um ein Auge auf den Buffer-Level haben zu können. Eine andere Fehlerquelle fällt mir momentan sonst nicht ein. Gruß Markus
:
Bearbeitet durch User
Etwas verwunderlich ist schon, warum der Wert um +/-3 schwankt. Ein Quarz kann nicht so ungenau sein, und der Wert sollte eigentlich immer zwischen nur 2 Werten hin- und her springen. Evtl. auch mal 3 Werte. Also +/-1.
Joe F. schrieb: > Etwas verwunderlich ist schon, warum der Wert um +/-3 schwankt. > Ein > Quarz kann nicht so ungenau sein, und der Wert sollte eigentlich immer > zwischen nur 2 Werten hin- und her springen. Evtl. auch mal 3 Werte. > Also +/-1. Habe nun direkt den PLL-clock von 49,152 MHz mit einem 32 Bit Counter gezählt. Dabei sind 2 Erkenntnisse aufgetreten: 1. Die Laufzeit bis zum Bufferproblem ist jetzt ca. 2,5 mal so lang. 2. Die Schwankungen beim FB-Wert sind noch größer, teilweise bis knapp 2,5 Hz. Ich frage mich, ob der Controller damit klar kommt, wenn die PLL für I2S mit 49,152 MHz läuft, die CPU aber mit 48 MHz. Könnte mir vorstellen, dass es da zu Genauigkeitsproblemen bei der Auswertung des Zählers kommen kann. Im Endeffekt scheint es so aber schon genauer zu sein. Gruß Markus
Braucht dein Codec fs512? Vielleicht kannst du die PLL auch auf fs256 laufen lassen. Dann hättest du mehr Abstand zu den 48MHz. Thomas
Thomas Z. schrieb: > Braucht dein Codec fs512? Vielleicht kannst du die PLL auch auf > fs256 > laufen lassen. > Dann hättest du mehr Abstand zu den 48MHz. Hallo, bin nun bei der PLL-Frequenz von 1024fs auf 512fs runter gegangen (24,576 MHz). Ich könnte auch auf 256*fs verringern. Ich denke das werde ich noch machen. Mit der 512fs Einstellung lief das Device nun ca. 33 min bis zum Bufferproblem (ohne manuelle Korrektur). Gruß Markus
Joe F. schrieb: > Wir nähern uns langsam dem Kern des Problems... ;-) Sehe ich auch so. Ein Fortschritt ist immerhin spürbar :)
Hallo, ich befasse mich nun mit dem Thema Buffer-Level Korrektur. Überlege gerade, wie ich das am besten implementiere. Ich denke, ab einem Schwellwert sollte die Korrektur eingreifen. Dies sollte sie aber auch abhängig von der Abweichung tun und nicht mit einem festen Wert. Hannes hatte dazu hier schon was geschrieben: Hannes M. schrieb: > Zusätzlich benutze ich meinen Fehloffset vom Schreib zum Lesezeiger und > nutze diesen direkt als Regelparameter mit einem Regelwert von > 0.01Hz/Byte. > > Mit dieser Einstellung läuft mein Device gerade mit einem Jitter von > etwa +/- 0.5Hz. Wenn ich es mir recht überlege, muss man eigentlich hauptsächlich dafür sorgen, dass der Korrekturwert in jedem Fall größer ist, als der Drift (sonst driftet der Bufferlevel weiter), diesen dabei aber so klein wie möglich halten. Dafür werde ich wohl mal einen Test machen müssen, mit welcher Geschwindigkeit der Bufferlevel wegläuft. Gruß Markus
Hallo, habe es nun so gemacht: mit 10.14 Format ist minimal möglich: 0000 0000 00.0000 0000 0000 01 = 0,000 061035 Hz = 0,06 Hz Wenn ich eine Bufferabweichung >= 5 Samples habe, addiere ich 1 für jeden Sample Abweichung zu meinem Feedback-Wert. Der Startwert ist also bei 5 Samples = 5 * 0,06 Hz = 0,3 Hz. Das ist dann ein sehr moderater Eingriff in den Feedbackwert. Werde das nun so testen. Ggf. muss ich den Wert noch etwas skalieren, aber ich denke die Größenordnung passt. Gruß Markus
Hallo zusammen, die ersten Tests waren soweit erfolgreich. Ich habe den Schwellwert des Buffers zusätzlich mit LEDs visualisiert, um auch bei längeren Laufzeiten sehen zu können, ob der Bufferlevel wegläuft. Bisher ist das Device sauber gelaufen. Längere Tests (> 4h) muss ich allerdings noch machen. Bin aber guter Dinge, dass es sich wenn dann nur noch um Feintuning handeln müsste. An dieser Stelle besten Dank für die zahlreichen hilfreichen Antworten zum Thema! Als nächstes, längerfristiges Ziel würde ich das Projekt dann gerne auf einen Atmel mit USB-Highspeed portieren. Habe aber noch keinen Kandidaten auserkoren. Als erstes würde ich gerne UAC2 nutzen. Das hätte den Vorteil, dass ich 32 Bit Audio übertragen könnte und somit bei der Verarbeitung der Daten nicht jeden 24 Bit Sample einzeln byteweise handhaben muss (24 Bit Daten kommen per USB an, der FIFO hat aber 32 Bit Speicherstellen). Das spart Code und Rechenleistung. Was muss ich denn tun, um UAC2 zu verwenden? Reicht es dabei bcdUSB anzupassen, sodass automatisch der USB Audio 2 Treiber geladen wird oder gibt es Inkompatibilitäten zwischen den beiden USB Audio Klassen? Beste Grüße Markus
:
Bearbeitet durch User
Deviceseitig gibt es durchaus Unterschiede. z.B: 8000 statt 1000 Pakete/s und das Format auf dem Sync-Endpoint ist leicht verändert (4 Bytes statt 3).
nun ja ich habe zwar noch kein UAC2 Device gebaut aber so weit ich gelesen habe ist nun zwingend (wie für alle neuen Compound Devices) IAD erforderlich. Das ist jedenfalls so in USB Complete beschrieben. Zusätzlich gilt natürlich sowieso dass UAC2 erst neuere Win 10 Versionen Treiber für UAC2 eingebaut haben. W7 braucht custom Treiber. Die meisten Anbieter haben dazu die Treiber von Thesycon lizenziert. Ich habe dazu ganz am Anfang des Threads auch was gepostet. Die UAC2 Spec sollte Beispiel Deskriptoren haben. Thomas
Markus G. schrieb: > gibt es Inkompatibilitäten zwischen den beiden USB Audio Klassen? Die Deskriptoren sind ähnlich, aber natürlich nicht 100 % gleich. Du solltest UAC2 nur verwenden, wenn du ein Feature wirklich benötigst. (Implicit Feedback wäre für dich interessant, wird aber von Windows nicht unterstützt.)
Joe F. schrieb: > Deviceseitig gibt es durchaus Unterschiede. > z.B: 8000 statt 1000 Pakete/s und das Format auf dem Sync-Endpoint ist > leicht verändert (4 Bytes statt 3). Zunächst soll das noch mit USB Fullspeed laufen. Das unterschiedliche Feedbackformat ist mir bekannt. Wie weiter oben im Thread schon erkannt, kann bei UAC2 zudem der Endpoint des Feedback explizit als FB-Endpoint definiert werden. Die Frage ist, mit welcher Einstellung im Deskriptor ich von UAC 1 auf 2 „umstellen“ kann. Thomas Z. schrieb: > Zusätzlich gilt natürlich sowieso dass UAC2 erst neuere Win 10 > Versionen Treiber für UAC2 eingebaut haben. W7 braucht custom Treiber. Win7 ist für mich relativ unwichtig, habe nur noch Win10 Geräte. Alternativ wäre noch MacOS interessant. Somit dürfte das Treiberproblem auf Seite des Host umgangen werden, da Win10 und MacOS UAC2 unterstützen. Oder? Clemens L. schrieb: > Markus G. schrieb: >> gibt es Inkompatibilitäten zwischen den beiden USB Audio Klassen? > > Die Deskriptoren sind ähnlich, aber natürlich nicht 100 % gleich. > > Du solltest UAC2 nur verwenden, wenn du ein Feature wirklich benötigst. > (Implicit Feedback wäre für dich interessant, wird aber von Windows > nicht unterstützt.) Ich meine gelesen zu haben, dass Windows auch implicit feedback unterstützt?! Nunja, unbedingt brauchen momentan nicht wirklich. Auf längere Sicht wäre es natürlich interessant das Projekt auf USB Highspeed und UAC2 mit z.B. 8 Kanälen auszubauen (man muss sich ja auch irgendwie die Zeit vertreiben :D ). Ich dachte da wäre es am sinnigsten, erstmal auf der gleichen Hardware zu bleiben, auf UAC2 umzubauen und dann irgendwann das ganze Projekt/die Routinen im Ganzen auf die neue Hardware zu portieren. Gruß Markus
Ein paar Vorschläge: Im Device descriptor wird class auf 0xFE gestellt subclass auf 0x02 und Protokoll auf 0x01 gestellt. (Compound Device IAD). Ich habe auf StackOverflow mal gelesen dass OSX das IAD noch nicht kann allerdings in anderem Zusammenhang. Audio Class wird dann auf Interface Level definiert. Der Config Descriptor bleibt gleich. Vor jedem Interface gibt es nun einen IAD Descriptor. Interface0 beschreibt dein AC Control Interface mit der Topologie und controls die du exportierst. Dort ist der wesentliche Punkt dass Input und Output Pins nun einen Clock haben den du bedienen musst. (Set Get SampleRate) Interface1 wird dein Streaming Interface. Im Control Interface kann optional nun ein Interrupt EP eingebaut werden für Rückmeldungen des devices. Das wäre es auch schon im wesentlichen. Ich bin aber sicher dass ich noch ein paar Dinge vergessen habe. Ob so ein Device auch die usbaudio2 Treiber unter fullspeed lädt musst du ausprobieren. https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers#descriptors Thomas
Markus G. schrieb: > Die Frage ist, mit welcher Einstellung im Deskriptor ich von UAC 1 auf 2 > „umstellen“ kann. Keine; du musst alle Deskriptoren durch die entsprechenden UAC2-Deskriptoren ersetzen. > Ich meine gelesen zu haben, dass Windows auch implicit feedback > unterstützt?! https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers sagt: | The driver does not support implicit feedback. Dort steht auch, wie die Deskriptoren aussehen sollen.
noch eine Idee. Du hast doch ein UAC2 Gerät im Zugriff. Falls das Gerät keine Bendorf Implementation ist sollten da ein UAC2 kompatibler Deskriptor Satz eingebaut sein. Ließ das mal aus und poste es hier. Das kannst du dann als Basis benutzen. Thomas
Thomas Z. schrieb: > noch eine Idee. Du hast doch ein UAC2 Gerät im Zugriff. Falls das > Gerät > keine Bendorf Implementation ist sollten da ein UAC2 kompatibler > Deskriptor Satz eingebaut sein. > > Ließ das mal aus und poste es hier. Das kannst du dann als Basis > benutzen. Ja, habe den miniDSP USBStreamer, den kann ich auslesen. Hier die Deskriptoren:
1 | [Port1] : USB-Verbundgerät |
2 | |
3 | Is Port User Connectable: yes |
4 | Is Port Debug Capable: no |
5 | Companion Port Number: 11 |
6 | Companion Hub Symbolic Link Name: USB#ROOT_HUB30#4&34571c9a&0&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8} |
7 | Protocols Supported: |
8 | USB 1.1: yes |
9 | USB 2.0: yes |
10 | USB 3.0: no |
11 | |
12 | Device Power State: PowerDeviceD0 |
13 | |
14 | ---===>Device Information<===--- |
15 | English product name: "USBStreamer" |
16 | |
17 | ConnectionStatus: |
18 | Current Config Value: 0x01 -> Device Bus Speed: High (is not SuperSpeed or higher capable) |
19 | Device Address: 0x0F |
20 | Open Pipes: 0 |
21 | *!*ERROR: No open pipes! |
22 | |
23 | ===>Device Descriptor<=== |
24 | bLength: 0x12 |
25 | bDescriptorType: 0x01 |
26 | bcdUSB: 0x0200 |
27 | bDeviceClass: 0xEF -> This is a Multi-interface Function Code Device |
28 | bDeviceSubClass: 0x02 -> This is the Common Class Sub Class |
29 | bDeviceProtocol: 0x01 -> This is the Interface Association Descriptor protocol |
30 | bMaxPacketSize0: 0x40 = (64) Bytes |
31 | idVendor: 0x2752 = miniDSP Ltd. |
32 | idProduct: 0x0016 |
33 | bcdDevice: 0x06B3 |
34 | iManufacturer: 0x01 |
35 | English (United States) "miniDSP" |
36 | iProduct: 0x03 |
37 | English (United States) "USBStreamer" |
38 | iSerialNumber: 0x02 |
39 | English (United States) "00001" |
40 | bNumConfigurations: 0x02 |
41 | *!*CAUTION: Most host controllers will only work with one configuration per speed |
42 | |
43 | ---===>Full Configuration Descriptor<===--- |
44 | |
45 | ===>Configuration Descriptor<=== |
46 | bLength: 0x09 |
47 | bDescriptorType: 0x02 |
48 | wTotalLength: 0x017D -> Validated |
49 | bNumInterfaces: 0x04 |
50 | bConfigurationValue: 0x01 |
51 | iConfiguration: 0x00 |
52 | bmAttributes: 0x80 -> Bus Powered |
53 | MaxPower: 0xFA = 500 mA |
54 | |
55 | ===>IAD Descriptor<=== |
56 | bLength: 0x08 |
57 | bDescriptorType: 0x0B |
58 | bFirstInterface: 0x00 |
59 | bInterfaceCount: 0x03 |
60 | bFunctionClass: 0x01 -> Audio Interface Class |
61 | bFunctionSubClass: 0x00 |
62 | *!*CAUTION: This appears to be an invalid bFunctionSubClass |
63 | bFunctionProtocol: 0x20 |
64 | iFunction: 0x00 |
65 | |
66 | ===>Interface Descriptor<=== |
67 | bLength: 0x09 |
68 | bDescriptorType: 0x04 |
69 | bInterfaceNumber: 0x00 |
70 | bAlternateSetting: 0x00 |
71 | bNumEndpoints: 0x00 |
72 | bInterfaceClass: 0x01 -> Audio Interface Class |
73 | bInterfaceSubClass: 0x01 -> Audio Control Interface SubClass |
74 | bInterfaceProtocol: 0x20 |
75 | *!*WARNING: must be set to PC_PROTOCOL_UNDEFINED 0 for this class |
76 | iInterface: 0x03 |
77 | English (United States) "USBStreamer" |
78 | |
79 | ===>Audio Control Interface Header Descriptor<=== |
80 | bLength: 0x09 |
81 | bDescriptorType: 0x24 (CS_INTERFACE) |
82 | bDescriptorSubtype: 0x01 (HEADER) |
83 | bcdADC: 0x0200 |
84 | wTotalLength: 0xA708 |
85 | bInCollection: 0x00 |
86 | |
87 | ===>Audio Control Feature Unit Descriptor<=== |
88 | bLength: 0x2A |
89 | bDescriptorType: 0x24 (CS_INTERFACE) |
90 | bDescriptorSubtype: 0x06 (FEATURE_UNIT) |
91 | bUnitID: 0x0A |
92 | bSourceID: 0x02 |
93 | bControlSize: 0x0F |
94 | *!*WARNING: bLength is greater than number of bmaControls (bLength > ( 7 + (ch + 1) * n) |
95 | bmaControls[master]: 00 00 00 0F 00 00 00 0F 00 00 00 0F 00 00 00 |
96 | bmaControls[channel 0]: 0F 00 00 00 0F 00 00 00 0F 00 00 00 0F 00 00 |
97 | iFeature: 0x00 |
98 | |
99 | ===>Audio Control Feature Unit Descriptor<=== |
100 | bLength: 0x2A |
101 | bDescriptorType: 0x24 (CS_INTERFACE) |
102 | bDescriptorSubtype: 0x06 (FEATURE_UNIT) |
103 | bUnitID: 0x0B |
104 | bSourceID: 0x01 |
105 | bControlSize: 0x0F |
106 | *!*WARNING: bLength is greater than number of bmaControls (bLength > ( 7 + (ch + 1) * n) |
107 | bmaControls[master]: 00 00 00 0F 00 00 00 0F 00 00 00 0F 00 00 00 |
108 | bmaControls[channel 0]: 0F 00 00 00 0F 00 00 00 0F 00 00 00 0F 00 00 |
109 | iFeature: 0x00 |
110 | |
111 | ===>Interface Descriptor<=== |
112 | bLength: 0x09 |
113 | bDescriptorType: 0x04 |
114 | bInterfaceNumber: 0x01 |
115 | bAlternateSetting: 0x00 |
116 | bNumEndpoints: 0x00 |
117 | bInterfaceClass: 0x01 -> Audio Interface Class |
118 | bInterfaceSubClass: 0x02 -> Audio Streaming Interface SubClass |
119 | bInterfaceProtocol: 0x20 |
120 | *!*WARNING: must be set to PC_PROTOCOL_UNDEFINED 0 for this class |
121 | iInterface: 0x04 |
122 | English (United States) "USBStreamer " |
123 | |
124 | ===>Interface Descriptor<=== |
125 | bLength: 0x09 |
126 | bDescriptorType: 0x04 |
127 | bInterfaceNumber: 0x01 |
128 | bAlternateSetting: 0x01 |
129 | bNumEndpoints: 0x02 |
130 | bInterfaceClass: 0x01 -> Audio Interface Class |
131 | bInterfaceSubClass: 0x02 -> Audio Streaming Interface SubClass |
132 | bInterfaceProtocol: 0x20 |
133 | *!*WARNING: must be set to PC_PROTOCOL_UNDEFINED 0 for this class |
134 | iInterface: 0x04 |
135 | English (United States) "USBStreamer " |
136 | |
137 | ===>Audio Streaming Format Type Descriptor<=== |
138 | bLength: 0x06 |
139 | bDescriptorType: 0x24 (CS_INTERFACE) |
140 | bDescriptorSubtype: 0x02 (FORMAT_TYPE) |
141 | bFormatType: 0x01 (FORMAT_TYPE_I) |
142 | bNrChannels: 0x04 |
143 | bSubframeSize: 0x18 |
144 | bBitResolution: 0x07 (7) |
145 | bSamFreqType: 0x05 (Discrete) |
146 | tSamFreq[1]: 0xA00501 (10487041 Hz) |
147 | tSamFreq[2]: 0x080101 (524545 Hz) |
148 | tSamFreq[3]: 0x000125 (293 Hz) |
149 | tSamFreq[4]: 0x080200 (524800 Hz) |
150 | tSamFreq[5]: 0x050700 (329472 Hz) |
151 | |
152 | ===>Endpoint Descriptor<=== |
153 | bLength: 0x07 |
154 | bDescriptorType: 0x05 |
155 | bEndpointAddress: 0x01 -> Direction: OUT - EndpointID: 1 |
156 | bmAttributes: 0x05 -> Isochronous Transfer Type, Synchronization Type = Asynchronous, Usage Type = Data Endpoint |
157 | wMaxPacketSize: 0x01A0 = 1 transactions per microframe, 0x1A0 max bytes |
158 | bInterval: 0x01 |
159 | |
160 | ===>Endpoint Descriptor<=== |
161 | bLength: 0x07 |
162 | bDescriptorType: 0x05 |
163 | bEndpointAddress: 0x81 -> Direction: IN - EndpointID: 1 |
164 | bmAttributes: 0x11 -> Isochronous Transfer Type, Synchronization Type = No Synchronization, Usage Type = Feedback Endpoint |
165 | wMaxPacketSize: 0x0004 = 1 transactions per microframe, 0x04 max bytes |
166 | bInterval: 0x04 |
167 | |
168 | ===>Interface Descriptor<=== |
169 | bLength: 0x09 |
170 | bDescriptorType: 0x04 |
171 | bInterfaceNumber: 0x01 |
172 | bAlternateSetting: 0x02 |
173 | bNumEndpoints: 0x02 |
174 | bInterfaceClass: 0x01 -> Audio Interface Class |
175 | bInterfaceSubClass: 0x02 -> Audio Streaming Interface SubClass |
176 | bInterfaceProtocol: 0x20 |
177 | *!*WARNING: must be set to PC_PROTOCOL_UNDEFINED 0 for this class |
178 | iInterface: 0x04 |
179 | English (United States) "USBStreamer " |
180 | |
181 | ===>Audio Streaming Format Type Descriptor<=== |
182 | bLength: 0x06 |
183 | bDescriptorType: 0x24 (CS_INTERFACE) |
184 | bDescriptorSubtype: 0x02 (FORMAT_TYPE) |
185 | bFormatType: 0x01 (FORMAT_TYPE_I) |
186 | bNrChannels: 0x02 |
187 | bSubframeSize: 0x10 |
188 | bBitResolution: 0x07 (7) |
189 | bSamFreqType: 0x05 (Discrete) |
190 | tSamFreq[1]: 0xD00501 (13632769 Hz) |
191 | tSamFreq[2]: 0x080100 (524544 Hz) |
192 | tSamFreq[3]: 0x000125 (293 Hz) |
193 | tSamFreq[4]: 0x080200 (524800 Hz) |
194 | tSamFreq[5]: 0x050700 (329472 Hz) |
195 | |
196 | ===>Endpoint Descriptor<=== |
197 | bLength: 0x07 |
198 | bDescriptorType: 0x05 |
199 | bEndpointAddress: 0x01 -> Direction: OUT - EndpointID: 1 |
200 | bmAttributes: 0x05 -> Isochronous Transfer Type, Synchronization Type = Asynchronous, Usage Type = Data Endpoint |
201 | wMaxPacketSize: 0x00D0 = 1 transactions per microframe, 0xD0 max bytes |
202 | bInterval: 0x01 |
203 | |
204 | ===>Endpoint Descriptor<=== |
205 | bLength: 0x07 |
206 | bDescriptorType: 0x05 |
207 | bEndpointAddress: 0x81 -> Direction: IN - EndpointID: 1 |
208 | bmAttributes: 0x11 -> Isochronous Transfer Type, Synchronization Type = No Synchronization, Usage Type = Feedback Endpoint |
209 | wMaxPacketSize: 0x0004 = 1 transactions per microframe, 0x04 max bytes |
210 | bInterval: 0x04 |
211 | |
212 | ===>Interface Descriptor<=== |
213 | bLength: 0x09 |
214 | bDescriptorType: 0x04 |
215 | bInterfaceNumber: 0x02 |
216 | bAlternateSetting: 0x00 |
217 | bNumEndpoints: 0x00 |
218 | bInterfaceClass: 0x01 -> Audio Interface Class |
219 | bInterfaceSubClass: 0x02 -> Audio Streaming Interface SubClass |
220 | bInterfaceProtocol: 0x20 |
221 | *!*WARNING: must be set to PC_PROTOCOL_UNDEFINED 0 for this class |
222 | iInterface: 0x05 |
223 | English (United States) "USBStreamer " |
224 | |
225 | ===>Interface Descriptor<=== |
226 | bLength: 0x09 |
227 | bDescriptorType: 0x04 |
228 | bInterfaceNumber: 0x02 |
229 | bAlternateSetting: 0x01 |
230 | bNumEndpoints: 0x01 |
231 | bInterfaceClass: 0x01 -> Audio Interface Class |
232 | bInterfaceSubClass: 0x02 -> Audio Streaming Interface SubClass |
233 | bInterfaceProtocol: 0x20 |
234 | *!*WARNING: must be set to PC_PROTOCOL_UNDEFINED 0 for this class |
235 | iInterface: 0x05 |
236 | English (United States) "USBStreamer " |
237 | |
238 | ===>Audio Streaming Format Type Descriptor<=== |
239 | bLength: 0x06 |
240 | bDescriptorType: 0x24 (CS_INTERFACE) |
241 | bDescriptorSubtype: 0x02 (FORMAT_TYPE) |
242 | bFormatType: 0x01 (FORMAT_TYPE_I) |
243 | bNrChannels: 0x04 |
244 | bSubframeSize: 0x18 |
245 | bBitResolution: 0x07 (7) |
246 | bSamFreqType: 0x05 (Discrete) |
247 | tSamFreq[1]: 0xA00582 (10487170 Hz) |
248 | tSamFreq[2]: 0x080101 (524545 Hz) |
249 | tSamFreq[3]: 0x000125 (293 Hz) |
250 | tSamFreq[4]: 0x080200 (524800 Hz) |
251 | tSamFreq[5]: 0x040900 (264448 Hz) |
252 | |
253 | ===>Endpoint Descriptor<=== |
254 | bLength: 0x07 |
255 | bDescriptorType: 0x05 |
256 | bEndpointAddress: 0x82 -> Direction: IN - EndpointID: 2 |
257 | bmAttributes: 0x05 -> Isochronous Transfer Type, Synchronization Type = Asynchronous, Usage Type = Data Endpoint |
258 | wMaxPacketSize: 0x01A0 = 1 transactions per microframe, 0x1A0 max bytes |
259 | bInterval: 0x01 |
Die Deskriptoren für DFU und HID habe ich der Übersicht wegen rausgeschmissen. Das Ganze deckt sich aber schon recht gut mit dem was Thomas bereits geschrieben hatte: Thomas Z. schrieb: > Ein paar Vorschläge: > Im Device descriptor wird class auf 0xFE gestellt subclass auf 0x02 und > Protokoll auf 0x01 gestellt. (Compound Device IAD). Ich habe auf > StackOverflow mal gelesen dass OSX das IAD noch nicht kann allerdings in > anderem Zusammenhang. > Audio Class wird dann auf Interface Level definiert. > Der Config Descriptor bleibt gleich. Vor jedem Interface gibt es nun > einen IAD Descriptor. > Interface0 beschreibt dein AC Control Interface mit der Topologie und > controls die du exportierst. Dort ist der wesentliche Punkt dass Input > und Output Pins nun einen Clock haben den du bedienen musst. (Set Get > SampleRate) > Interface1 wird dein Streaming Interface. > Im Control Interface kann optional nun ein Interrupt EP eingebaut werden > für Rückmeldungen des devices. Gruß Markus
:
Bearbeitet durch User
naja die Deskriptoren sehen ziemlich kaput aus. Was für ein Treiber wird da geladen? nach usbaudio2.sys sieht das nicht aus. Ich vermisse die Topologie im AC Controlinterface, also sowas wie: usboutpin -> Feature -> Digital Out Der Deskriptor mit den SRs ist komplett im Off. wLength im AC Interface stimmt nicht. Da ist mindestens MSB und LSB vertauscht. Schalte mal die Option ein dass auch Rohdaten ausgegeben werden. Thomas
Thomas Z. schrieb: > die Deskriptoren sehen ziemlich kaput aus Nur mit Software, die UAC2 nicht kennt.
Hallo, Clemens L. schrieb: > Thomas Z. schrieb: >> die Deskriptoren sehen ziemlich kaput aus > > Nur mit Software, die UAC2 nicht kennt. Richtig, hatte grade nochmals die Möglichkeit an den miniDSP USB Streamer ranzukommen und auszulesen. USBView aus dem Windows SDK liefert bei UAC2 großteils Müll. Habe es grade mit USBlyzer versucht, das ist zwar besser, aber auch beileibe nicht vollständig. Weiß jemand ein USB-Tool, mit dem man auch UAC2 Deskriptoren auslesen kann? Gruß Markus
Naja die Bytes sind ja alle da wie wäre es denn wenn du das selbst über bLength formatierst? Ist ja nicht soviel Arbeit und du lernst auch was über die UAC Deskriptoren. Thomas
Bin Mac User, könnte mir aber vorstellen, dass dieses Tool hier nützlich sein könnte: https://www.thesycon.de/eng/usb_descriptordumper.shtml
Hallo, natürlich könnte ich das selbst formatieren, die Zeitersparnis mit einem Tool wäre mir dann aber doch recht. Der Tipp mit dem Tool von Thesycon war nicht schlecht. Das Auslesen hat dem Anschein nach ohne Probleme funktioniert. Habe den Log mal als File mit angehängt. Dann muss ich also den UAC2 Deskriptor komplett neu aufbauen, wenn ich das richtig verstanden habe? Außer ich finde natürlich irgendwo ein Template, das man ggf. als Arbeitsgrundlage verwenden kann. Dann würde ich also am besten so verfahren, mir in meinem uC Code einen zusätzlichen Deskriptor für UAC2 zu bauen. Am besten so, dass ich per define zwischen UAC1 und 2 umschalten kann. Gruß Markus
Ja für den Anfang würde ich es einfach mit einem 2. Deskriptorsatz mal austesten. Ich bin mir immer noch nicht so ganz sicher ob Fullspeed überhaupt mit UAC2 umgehen kann. Die Deskriptoren sehen schon mal gut aus, allerdings hab ich mir nur das Control Interface näher angesehen. Das kannst du prinzipiell übernehmen. Ich würde allerdings beim Feature Deskriptor die controls für mute und Volume auf 0 setzen. Die stehen im Moment auf rw. Es sei denn du hast die Möglichkeit z.b per DSP diese Sachen zu kontrollieren. Beim Clock sind controls fcontrol rw und vality r eingebaut. Vality würde ich rausnehmen für fcontrol musst du die Control requests einbauen. Der clockselektor at nur einen Input Pin. Den kannst du wahrscheinlich rausschmeißen. Win10 hat sowieso keinen Support dafür. W10 fragt den nur einmal ab und setzt dann den Clock. Falls du den Sektor drin lässt müssen auchcdie Control requests eingebaut werden. Auch den Mic Part kannst du rausnehmen du willst ja nur abspielen. Natürlich musst du die Kanalzahl erst mal an Fullspeed anpassen. Noch ein Literatur Hinweis: USB Complete von Jan Axelson. Das ist auch in vielen Online Bibliotheken vorhanden. Das ist ganz hilfreich für die spätere Umstellung auf Highspeed (Otherspeed Descriptor....) Aktuell wäre die 4 Ausgabe mit IAD Beschreibungen. Thomas
:
Bearbeitet durch User
Hallo zusammen, war jetzt einige Zeit inaktiv bei der Erweiterung der Software. Die USB->I2S Brücke funktioniert seitdem tadellos. Höre immer noch fleißig damit. Habe jetzt aber wieder etwas Lust an der Software zu basteln. Bevor ich nun weitere Baustellen aufmache, würde ich gerne als erstes eine Debug-Schnittstelle implementieren. Vom Arduino kenne ich das als einfache serielle Ausgabe. Das wäre eine charmante und einfache Lösung. Habe nun etwas recherchiert und diverse Beispiel-Projekte getestet. Mir scheint, als wäre die meisten USB-Projekte mit mehreren Schnittstellen mit IAD Deskriptoren programmiert. Im Grunde hätte ich eigentlich nur gern mein Audio-Interface und unabhängig davon einen virtual COM-Port. Jede Teilfunktion hätte im Prinzip einen eigenen Treiber. Kann ich die einfach in unterschiedliche Interfaces legen und die werden dann nur aktiviert, wenn sie auch gebraucht werden/Daten übertragen werden? Wie läuft das denn treiberseitig? Sehe ich das richtig, wenn ich einen IAD Deskriptor verwende, dass ich dann einen speziellen Treiber bräuchte, der sowohl Audio als auch COM-Port abdeckt. Der Einstieg in das Thema Composite Device ist etwas mühsam. Habt ihr dazu vielleicht eine Literaturquelle? Das von Thomas empfohlene Buch von Jan Axelson bin ich gerade dabei zu beschaffen. Womöglich gibt es derweil aber schonmal eine Internetquelle. Viele Grüße Markus
:
Bearbeitet durch User
Markus G. schrieb: > Mir scheint, als wäre die meisten USB-Projekte mit mehreren > Schnittstellen mit IAD Deskriptoren programmiert. Wozu hier IAD? > Kann ich die einfach in unterschiedliche Interfaces legen Ja. > und die werden dann nur aktiviert, wenn sie auch gebraucht werden/Daten > übertragen werden? Das Gerät bemerkt das Aktivieren nur, wenn es Alternate Settings hat.
So wie ich das verstanden habe ist IAD immer notwendig wenn mehrere Interfaces von einem Treiber benutzt werden. Beispiel Audio wo es Audio Control, Audio Stream und Midi Interfaces gibt. Das gleiche bei CDC. Neuere Specs schreiben in diesen Fällen IAD vor. Ich habe das bisher nur bei CDC verwendet. Ja so ein Debug Interface ist schon ne feine Sache, ich bin teilweise sogar den Weg gegangen mir ein Hid Interface dazuzubasteln um Variablen lesen und schreiben zu können. Debuggen von USB Apps ist ja nicht so ganz trivial.
Clemens L. schrieb: > Wozu hier IAD? > >> Kann ich die einfach in unterschiedliche Interfaces legen > > Ja. Habe das nun nochmal nachgelesen. Grundsätzlich bräuchte ich den IAD hier nicht, weil ich jede Funktion/Interface getrennt voneinander nutzen will. Es ist aber in der Tat so: Thomas Z. schrieb: > So wie ich das verstanden habe ist IAD immer notwendig wenn mehrere > Interfaces von einem Treiber benutzt werden. Beispiel Audio wo es Audio > Control, Audio Stream und Midi Interfaces gibt. Das gleiche bei CDC. > Neuere Specs schreiben in diesen Fällen IAD vor. Ich habe das bisher nur > bei CDC verwendet. Um mal die Passage aus Jan Axelsons - USB Complete, Aufl. 5 zu zitieren: "The Interface Association Descriptors ECN says that the descriptor must be supported by future implementations of devices that use multiple interfaces to manage a single device function. Devices that comply with the video and audio-class 2.0 specications must use interface association descriptors. Class specications that predate the IAD don’t require it. For example, the audio 1.0 class specication denes a class-specic descriptor to associate audio interfaces in a function." Das heißt im Endeffekt, für UAC1 ist das Thema IAD nicht relevant. Da ich aber als nächsten Schritt gerne auf UAC2 aufbohren möchte, wäre das für dann doch ggf. relevant. Für das Update auf UAC2 will ich hauptsächlich davor die Implementierung des Debug-Interface machen. Gibt es eigentlich ein Programm oder Webservice, über den ich Fehler in Deskriptoren auslesen kann? Wenn am Deskriptor des Device etwas gröberes nicht passt, kommt unter Windows im Prinzip nur die Meldung "geht nicht". Damit ist es natürlich schwer die Ursache einzukreisen. > Ja so ein Debug Interface ist schon ne feine Sache, ich bin teilweise > sogar den Weg gegangen mir ein Hid Interface dazuzubasteln um Variablen > lesen und schreiben zu können. > Debuggen von USB Apps ist ja nicht so ganz trivial. Das schmerzvolle Debugging hab ich ja auf meinem (Leidens)Weg der Programmierung kennengelernt. Vieles kann man zwar mit dem Debugger im Betrieb machen, man tut sich aber doch etwas leichter, wenn man eine stetige Ausgabe von Parametern einfach umsetzen kann. Gruß Markus
Markus G. schrieb: > Gibt es eigentlich ein Programm oder Webservice, über den ich Fehler in > Deskriptoren auslesen kann? Ich kenn da leider auch nichts nur die üblichen USB View Geschichten. > Wenn am Deskriptor des Device etwas gröberes nicht passt, kommt unter > Windows im Prinzip nur die Meldung "geht nicht". Damit ist es natürlich > schwer die Ursache einzukreisen Am wichtigsten ist erst mal, dass im Config Deskriptor die wTotalLength korrekt ist. Falls das nicht stimmt gibt es alle möglichen lustigen Fehler. Ansonsten gibt UsbDevView ja schon Hinweise und Warnungen aus. Wenn ein Device gar nicht gefunden wird weil es nicht durch die Enum kommt kann der Fehler nur daran liegen, dass mindestens einer der unten beschriebenen Requests nicht so funktioniert wie er sollte. Damit die Enum funktioniert müssen folgende Requests funktionieren 1. Getdescriptor für Device und Config 2. SetAddress 3. SetConfig Danach kann usbview auch was anzeigen. Ein Getdescriptor muss auch mit seltsamen Angaben im wLength Feld umgehen können und der Host kann jederzeit einen Request abbrechen wenn er keine Lust mehr hat mehr Daten zu lesen.
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.