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.
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.
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
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ß
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).
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
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
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
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
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
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
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
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
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).
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.
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).
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.
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
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
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.
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:> 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
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
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
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
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
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
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
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.
"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...
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
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.
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.
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.
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.
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
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?
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
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
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.
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 */
> 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.
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.
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 */
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
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...
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
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.
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
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
Ä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:
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.
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
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
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
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
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:
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
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
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
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
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
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.