Fluctuación de entrada de audio USB (?)

Esperemos que este sea el subtítulo correcto para hacer esta pregunta. De todos modos, estoy trabajando en un dispositivo de audio USB en un microcontrolador y tengo problemas para obtener una entrada de audio consistente.

Algún prólogo:

A partir de ahora, he conectado el mcu y el códec a través de I2S (el códec es esclavo) con las siguientes configuraciones:

MCLK = 11.2896 MHz
BCLK = 2.8224 MHz
LRCLK = 44.1 kHz
USB CLK = 12 MHz

Cuando hago un bypass de DSP simple (entrada de códec -> mcu -> salida de códec), el audio está libre de fallas y es increíble. Sin embargo, cuando habilito USB, es cuando las cosas se ponen raras. Obtendré fallas ocasionales y la señal a veces se repite. No estoy totalmente seguro de si esto se debe a que no estoy usando suficientes búferes cuando escribo en USB.

En este momento, mis lecturas de códec y las escrituras USB se realizan por separado:

Codec Flow: Codec Input -> MCU I2S RX -> DMA Interrupt when I2S RX buffer is full 
            -> Write this buffer into USB buffers -> Calculate next input frame len

USB: USB initiates input -> Write buffer to USB endpoint -> USB Write Callback 
     called when USB transfer is complete -> Start reading next buffer

Cómo calculo las longitudes de mis fotogramas:

#define AUDIO_POLL_INT     4
#define FRAME_BYTES        3
#define NUM_CHANNELS       STEREO

// Calculate the frame length
uint16_t frame_len = 44 (44.1kHz/1000 samples) * NUM_CHANNELS * FRAME_BYTES;
// Every 10 ms, calculate frame length with additional frame (only applies to 44.1kHz sample rate)
if (!(frame_pos % 9)) frame_len += (1 * NUM_CHANNELS * FRAME_BYTES)

// Increment frame position per usb call
frame_pos = (((frame_pos + 1) / 8) * (2 << (AUDIO_POLL_INT-1))) % 10;
frame_pos = (frame_pos + 1) % 10;

Pregunta real/¿Qué creo que va mal (?)

Supongo que hay algunos problemas de fluctuación debido a que las tasas de códec MCLK y USB CLK son diferentes (11.2896 frente a 12). Solo adivino esto porque las interrupciones/problemas ocurren periódicamente. También he notado que los búferes de códec "alcanzan" a los búferes USB de vez en cuando (el búfer de códec sobrescribirá un búfer USB aunque el búfer USB esté marcado como "lleno"). Sin embargo, no estoy seguro de cómo evitar que esto suceda porque el códec se desincronizaría y perdería los datos de audio. Obviamente, tampoco puedo acelerar las transferencias USB... Estoy transfiriendo un máximo de 270 bytes por escritura USB para una frecuencia de muestreo de 44,1 kHz.

Si alguien ha tenido experiencia con audio USB y sincronización, ¡se agradecería su ayuda! No estoy seguro de cómo abordar el problema desde aquí honestamente. Sin embargo, si se lo pregunta, la salida de audio USB está bien (con la excepción de algunos canales que no están sincronizados).

¿Qué tan preciso es su 11.lo que sea MCLK? ¿Cómo se genera?
@pjc50 Bastante preciso porque es generado por un cristal externo
¿ Qué método de controlador USB se comunica con qué ? ¿Es esto un micro conectado a una PC? ¿Qué controlador y modo está usando?
@rdtsc Estoy usando USB Audio 2.0 y mi micro se comunica con una Mac. Uso del modo asíncrono para mi entrada de audio USB.
"El hombre con un reloj sabe qué hora es, el hombre con dos nunca está seguro" => los dos cristales suenan como un problema.

Respuestas (1)

Es difícil estar seguro (ya que obviamente este no es su código real), pero parece que ha codificado el patrón de longitudes de fotogramas en el lado USB, suponiendo que la frecuencia de muestreo de audio (es decir, el códec de 11,2896 MHz reloj) está estrictamente bloqueado a la velocidad de datos USB (es decir, el reloj USB de 12.000 MHz). Esto solo sería cierto si el reloj del códec estuviera controlado por un PLL que hace referencia al reloj USB. De hecho, pueden ser diferentes en decenas o incluso en centenas de ppm, dadas las tolerancias habituales asociadas con los cristales de uso general.

En su lugar, debe elegir la longitud del marco USB de forma dinámica, en función de la cantidad real de datos que tiene en los búferes locales que ha recibido del códec. Si este total está por encima de algún umbral, debe enviar el marco USB más largo; de lo contrario, envíe el marco USB más corto. Esto permite que la velocidad promedio de las transferencias USB rastree la frecuencia de muestreo real definida por el reloj del códec.

Ah, ya veo, así que si fuera un PLL, entonces podría codificar estas longitudes de marco. Me hicieron creer que solo funcionaría con varios recursos. Esencialmente, necesito realizar un seguimiento del tiempo tanto del USB como del códec para poder calcular la verdadera "longitud del cuadro". Ugh suena como mucho más código que necesito hacer jaja. ¡Gracias!
No puede burlarse de la longitud del marco USB, tiene límites estrictos. El audio USB es un área fea del bus. Las especificaciones USB 2.0 tienen alrededor de 20 páginas (!!!) de redacción sobre el tema de la sincronización del reloj USB y el almacenamiento en búfer para la coincidencia de velocidad, consulte la Sección 5.12.
@AliChen: El OP ya está enviando dos longitudes de cuadro diferentes, pero en un horario fijo. Solo digo que el cronograma debe ser dinámico, pero aún así usar las mismas dos longitudes de cuadro. Lo siento si eso no fue claro!
@AliChen Definitivamente debería leer la documentación USB más a fondo sobre esto, pero sí, estoy enviando estas longitudes de cuadro en un horario fijo en este momento. Creo que Dave tiene razón acerca de que debe ser dinámico, ¡así que probaré esto la próxima semana!
El objetivo del modo de transferencia asincrónica es que el reloj de muestra (derivado de su MCLK) no está sincronizado con el reloj USB.
Cambiando cómo se cargaban los cuadros en mis búferes USB/cómo se rellenaban mis búferes de códec (y probando a una frecuencia de muestreo de 48kHz porque no tengo que cambiar la longitud de los cuadros), obtuve una onda sinusoidal limpia con 48kHz (obviamente con un códec de reloj de 12,288 MHz). Obviamente, las longitudes de cuadro son diferentes con 44,1 kHz, pero creo que ahora estoy en el camino correcto. Marqué esto como la respuesta porque esto también me envió por el camino correcto. Gracias :)