STM32: restablecer el puntero de dirección I2S DMA

Para mi aplicación, tengo un DAC que se comunica con un STM32F4 a través de I2S. Del lado del microcontrolador, las informaciones se envían al DAC a través de un flujo DMA, con un búfer circular. En una interrupción externa, me gustaría pausar el DMA, cambiar algunos parámetros de sonido y luego reanudar el flujo de DMA.

Mi rutina de interrupción es básicamente así:

void HAL_GPIO_EXTI_Callback(uint16_t pin)
{
    if(pin == GPIO_PIN_13)
    {
        HAL_I2S_DMAPause(&hi2s2);
        changeSound(&sound);
        HAL_I2S_DMAResume(&hi2s2);
    }
}

Y tengo 2 rutinas de interrupción activadas en DMA TxHalf/TxCplt que llenan cada mitad de mi búfer DMA con el sonido. ( EDITAR : durante mi investigación también aprendí que hay un "modo de doble búfer" que permite hacer eso con algunas ventajas, pero eso está fuera de tema).

Lo que quiero evitar es transferir algunas muestras restantes del sonido anterior después de mi interrupción Exti. Entonces, mi idea era llenar todo el búfer después de cambiar el sonido y luego restablecer el puntero de memoria del flujo DMA al comienzo de mi búfer.

Se vería algo así:

void HAL_GPIO_EXTI_Callback(uint16_t pin)
{
    if(pin == GPIO_PIN_13)
    {
        HAL_I2S_DMAPause(&hi2s2);
        changeSound(&sound);
        fillBuffer( address_of_1st_half, &sound);
        fillBuffer( address_of_2nd_half, &sound);
        *reset_DMA(&hi2s2)*; <==== this is the function I'm looking for
        HAL_I2S_DMAResume(&hi2s2);
    }
}

nb: como puede ver, estoy usando los controladores HAL de CubeMX

He buscado en la hoja de datos de HAL para tal función, pero no he encontrado ninguna.

Lo único que se acerca a eso es usar DMAStop() y luego Transmit_DMA() nuevamente. Pero me temo que llevaría demasiado tiempo...

Si alguien tiene una idea, ¡me encantaría escucharla!

Gracias

Respuestas (1)

Ok, es posible que no haya encontrado una forma de restablecer el puntero DMA sin deshabilitar la transmisión (todavía), pero encontré más información y una forma de evitar el problema.

  • Cuando utiliza el modo circular con incremento de puntero, la dirección de los datos que se transferirán se calcula a partir del registro DMA_SxNDTR (que contiene el número restante de datos que se enviarán) y el registro DMA_SxM0AR (que contiene la dirección ASE para el flujo DMA) . El puntero es la dirección base compensada por el tamaño del búfer menos el valor DMA_SxNDTR. Si restablece DMA_SxNDTR al tamaño de los datos, restablece el puntero a la dirección base (por lo que pude entender, podría estar equivocado). Sin embargo, no puede cambiar el valor del registro a menos que la transmisión esté deshabilitada. Entonces, para restablecer el puntero, tendría que deshabilitar la transmisión, restablecer DMA_SxNDTR y luego reactivar la transmisión.
  • Sin embargo, puede leer el registro DMA_SxNDTR para saber en qué parte del búfer se encuentra, de modo que pueda rellenar solo la otra mitad. En ese caso, el periférico recibiría los restos del sonido anterior y luego comenzaría a leer el nuevo sonido de forma segura.
  • Una tercera posibilidad sería leer el DMA_SxNDTR y comenzar a rellenar el búfer solo cuando no queden datos para transferir. Agrega latencia pero evita que el sonido antiguo quede por reproducir.

La última solución se vería así:

void HAL_GPIO_EXTI_Callback(uint16_t pin)
{
    if(pin == GPIO_PIN_13)
    {
        changeSound(&sound);
        while(DMA1_Stream4->NDTR >= 1);
        HAL_I2S_DMAPause(&hi2s2);
        fillBuf(&buf, &sound);
        HAL_I2S_DMAResume(&hi2s2);
    }
}

Todavía no lo he probado, así que no puedo estar seguro de si funciona o no, actualizaré esta respuesta tan pronto como lo haga.

Fuentes: