Interfaces de audio en STM32 DISCO-L476VG
Plataforma de evaluación HW para STM32L4
DISCO-L476VG
Placa de desarrollo STM32 Discovery kit //32L476GDISCOVERY// con MCU STM32L476VG
- Depurador/programador en PCB ST-LINK/V2-1 con conector SWD
- Alimentación (4x opciones):
- ST-LINK/V2-1
- Conector USB FS
- Externa 5 V
- Batería CR2032
- 2x LEDs de usuario: LD4 (rojo), LD5 (verde)
- 2x botón pulsador: USER y RESET
- 1x puerto USB OTG FS con conector micro-AB, capacidad de re-enumeración USB y 3x interfaces USB:
- VCP (Virtual Com port)
- Almacenamiento masivo (USB Disk drive) para programación por arrastre
- Depuración (Debug port)
- 1x Joystick de 4x direcciones y selección central
- SAI Audio DAC, estéreo, con conector Jack de salida
- Micrófono digital MEMS
- Acelerómetro y magnetómetro MEMS
- Giróscopo MEMS
- 128-Mbit memoria Quad-SPI Flash
- Amperímetro de corriente del MCU con 4 rangos y auto calibración
MCU STM32L476VGT6 (encapsulado LQFP100)
- ARM®32-bit Cortex®-M4 CPU con FPU y acelerador ART
- 80 MHz frecuencia máxima de CPU
- VDD de 1.71V a 3.6 V
- 1 MB Flash
- 128 KB SRAM
- 114x GPIOs con capacidad de interrupción externa
- 3x 12-bit ADCs con 16 canales
- 2x 12-bit DAC
- 3x USART
- 2x UART
- 1x LPUART
- 2x SAI
- 3x I2C
- 3x SPI
- 1x Quad SPI
- 7x temporizadores (Timers) de propósito general, 2x básicos y 2x avanzados
- 2x temporizadores de bajo consumo (low-power)
- 2x temporizadores tipo Watchdog
- 1x CAN
- USB OTG FS
- SDMMC
- SWPMI
- LCD COM x SEG
- RTC
- TRNG (generador de aleatorios por HW)
- 21x sensores capacitivos
- 2x comparadores analógicos
- 2x amplificadores operacionales
Micrófono MEMS MP34DT01
- Micrófono MEMS digital (ST-MP34DT01) con salida PDM para aplicaciones de audio con pequeño tamaño y coste, alta calidad de sonido e inmunidad al ruido (SNR=61dB; -26dBFS; AOP=120 dBSPL).
- Opera a una frecuencia de reloj desde 1 MHz a 3,25 MHz.
- Esquema funcional:
- Conexión a MCUs Arm® Cortex® mediante interfaces serie SPI / I2S, SAI y DFSDM.
- Adquisición mono/estéreo en PDM (modulación por densidad de impulsos) a transformar en datos estándar para audio, p.ej. PCM (modulación por impulsos codificados).
- Esquemático de conexiones en STM32L4Discovery:
Interfaz DFSDM
Un DFSDM (Filtro digital para moduladores Sigma-Delta) es periférico nuevo incorporado a la familia STM32, que actúa como un ADC estándar con velocidad/resolución escalable (hasta 24-bit), con un interfaz (front-end) analógico externo separado/aislado de la parte digital (1/2 hilos).
La parte digital realiza el procesamiento digital de señal a partir de los datos externos, p.ej. para procesar la señal de audio a partir de micrófonos digitales MEMS con salida de datos PDM, que puede ser conectados directamente al DFSDM. También pueden procesar datos internos en paralelo (16-bit) transferidos por CPU/DMA desde memoria.
- Diagrama de bloques:
- Transceptores:
- Entradas de datos externas (hasta 8 canales) en Serie y modo rápido (20 MHz):
- Codificación SPI (2 hilos: reloj y datos) o Manchester (1 hilo, coste mínimo).
- Generación de reloj: derivado de sistema o PLL de audio (divisor).
- Entradas de datos internas (hasta 8 canales) Paralela (16-bit):
- Registro de datos de entrada de 16-bit (escritura por CPU/DMA)
- Filtros:
- Etapa de filtrado digital sobre un flujo (stream) de datos de entrada mediante media fija (averaging), o media móvil (SincX) por sobremuestreo
- Sinc1, Sinc2, Sinc3, Sinc4, Sinc5 y FastSinc con tasa de sobremuestreo (FOSR) hasta 1024 y resolución de hasta 31-bit
- Integrador con tasa de sobremuestreo (IOSR) de hasta 1024 y número de muestras (N) de hasta 256
- Ventajas para aplicaciones:
- Soporte para varios suministradores de moduladores SD (ST, TI, Analog Devices,…)
- Selección de velocidad vs. resolución por configuración del filtro
- Post-procesado de datos internos (resultados de SAR ADC, …)
- Funciones adicionales: perro guardián (WD), detector de corto circuito, detector de extremos, corrección de offset.
- Rendimiento muy alto para DSP en HW y reducción de consumo respecto ADC convencional:
- Reloj DFSDM máximo = reloj sistema (máx. 80 MHz)
- Reloj entrada serie (tasa de datos de entrada):
- SPI: Reloj DFSDM / 4 (máx. 20 MHz)
- Manchester: Reloj DFSDM / 6 (máx. 10 MHz)
- Frecuencia de reloj de salida: Reloj DFSDM / 4 (máx. 20 MHz)
- Tasa de datos de salida = Tasa de datos de entrada / (FOSR *IOSR)
- FOSR = 1-1024
- IOSR = 1-256
- Selección del tipo de conversión:
- Conversiones regulares:
- Selección de un solo canal (de entre 8 canales)
- Iniciadas por SW (no disparo HW)
- Posibilidad de modo continuo
- Posibilidad de interrupción por conversión inyectada (flag)
- Conversiones inyectadas:
- Grupo de canales: más de un canal de entrada
- Modo Scan (disparo): todo el grupo de canales es convertido
- Modo Single (disparo): sólo un canal es convertido y el siguiente es seleccionado
- Iniciadas por disparo SW o HW (temporizadores o pines externos)
- No hay posibilidad de modo continuo (emulado por disparo de temporizador)
- Adecuado para micrófonos MEMS y medidores STPMS2
Configuración de Entrada de Audio
- Una de las principales aplicaciones de los DFSDM es la de soporte de entrada de audio a partir de micrófonos con salida PDM, debido principalmente a la considerable reducción de consumo de energía y mínima utilización/intervención del MCU al realizarse el procesamiento directamente en HW.
- El conexionado también se simplifica:
- Sólo 2 hilos para una entrada estéreo, comunes a ambos micrófonos L/R, compartiendo señales de reloj y datos.
- La separación de canales se hace internamente: diferentes flancos para datos L/R.
- La tasa de datos de salida y la resolución está en función de las configuraciones de los filtros.
- A modo de ejemplo, acorde a la figura, podrían realizarse conversiones regulares en ambos canales en modo continuo, con transferencia directa a memoria vía DMA, y una segunda transferencia DMA envía los datos de audio a una interfaz I2S (asociada a un códec de audio externo).
- La siguiente sección de //desarrollo SW// muestra los pasos para crear, con la herramienta STM32CubeMX, el código de inicialización de una aplicación que adquiere datos PDM del micrófono digital MP34DT01, incluido en la PCB STM32L4Discovery, en modo mono, y los convierte a datos PCM utilizando el HW de DFSDM. Se requiere:
- Configuración de pines GPIO
- Configuración de canal/es DFSDM
- Configuración del orden del filtro Sinc, de cada canal, acorde a la frecuencia de muestreo, p.ej.
- Configuración del modo de operación del filtro/s
- Configuración del reloj de salida (divisor):
Divisor = Frecuencia_reloj_DFSDM / (Frecuencia_muestreo × Factor_diezmado)
- Configuración de DMA del DFSDM
- Configuración de reloj fuente del DFSDM, p.ej.
Desarrollo SW: grabador/reproductor de audio
El objetivo del tutorial es la reproducción de un archivo de audio en formato WAV que grabaremos en la memoria Flash del dispositivo incluido en la plataforma STM32L4Discovery, de acuerdo a la configuración del módulo SAI y comunicación con el códec de audio CS43L22 indicada en el apartado anterior.
Creación del proyecto con STM32CubeMX
En este caso se parte del proyecto de CubeMX creado en el tutorial anterior haciendo una copia del mismo, para ello abriremos el proyecto origen (p.ej. SIlab5.ioc) desde la ventana principal, y se selecciona la opción de menú (File→Save Project As ..) o la combinación de teclas (Ctrl-A). En la ventana emergente subimos un nivel respecto la carpeta original y creamos una nueva carpeta con el nuevo nombre del proyecto destino (p.ej. SIlab6), nos movemos a la nueva carpeta creada desde la misma ventana y procedemos a guardar, en este punto se crea el nuevo archivo de proyecto (p.ej. SIlab6.ioc) dentro de la nueva carpeta creada con el mismo nombre.
Puede comprobarse en la pestaña de Project Manager que la ubicación es correcta, e incluso puede ya generarse el código inicial a partir de la configuración origen.
La configuración copiada incluye la selección de la placa DISCO-L476VG así como la configuración y pines establecidos en el tutorial anterior: salida de audio (I2C, SAI, DMA), display LCD, JoyStick, LEDs.
Revisamos en el nuevo proyecto que la interfaz de depuración está establecida a Serial Wire en la categoría SYS y el temporizador base del sistema el SysTick, heredado del proyecto origen en multimedia debe aparecer el SAI1 interfaz SAI_A, en comunicaciones el I2C1, los relojes, y las interfaces básicas de E/S que conservamos para usarlas con librerías de BSP.
Siguiendo los pasos indicados en el apartado //anterior// sobre la configuración propuesta:
Configuración de pines
Configuración del canal DFSDM y orden del filtro Sinc
Configuración del filtro Sinc
Configuración de DMA del DFSDM
Configuración del reloj de salida (divisor)
El reloj del micrófono MP34DT01 viene generado por el DFSDM, en base al reloj de audio (11.294 MHz) dividido por 4 en este caso (2.8235 MHz), dentro de los márgenes del MEMS mic (1-3.25 MHz).
Configuración de relojes general y audio
Tabla de interrupciones NVIC
Para evitar problemas en servicios de interrupción como el derivado del uso de retardos HAL_Delay() a partir del SysTick, se recomienda poner éste con mayor prioridad respecto el resto de ISR usadas en la aplicación.
Generación y edición de código
Se procede a la generación de código a partir del proyecto desde CubeMX y pasamos al IDE del TrueStudio donde introducimos el código proporcionado aquí, que incluye parte del BSP de audio y diferentes modificaciones en las secciones de código de usuario del main.c
Includes
/* USER CODE BEGIN Includes */ #include "stm32l4xx_hal.h" #include "stm32l476g_discovery.h" #include "stm32l476g_discovery_glass_lcd.h" //#include "stm32l476g_discovery_audio.h" #include "../Components/Common/audio.h" #include "../Components/cs43l22/cs43l22.h" /* USER CODE END Includes */
Macro
/* USER CODE BEGIN PM */ #define SaturaLH(N, L, H) (((N)<(L))?(L):(((N)>(H))?(H):(N))) /* USER CODE END PM */
Variables
/* USER CODE BEGIN PV */ AUDIO_DrvTypeDef *audio_drv; int32_t RecBuff[2048]; int16_t PlayBuff[4096]; uint32_t DmaRecHalfBuffCplt = 0; uint32_t DmaRecBuffCplt = 0; uint32_t PlaybackStarted = 0; /* USER CODE END PV */
Código 1
/* USER CODE BEGIN 1 */ uint32_t i; /* USER CODE END 1 */
Código 2, 3
/* USER CODE BEGIN 2 */ /* Configure LEDs */ BSP_LED_Init(LED_GREEN); BSP_LED_Init(LED_RED); /* Initialize playback */ if(0 != audio_drv->Init(AUDIO_I2C_ADDRESS, OUTPUT_DEVICE_HEADPHONE, 90, AUDIO_FREQUENCY_44K)) { Error_Handler(); } /* Start DFSDM conversions */ if(HAL_OK != HAL_DFSDM_FilterRegularStart_DMA(&hdfsdm1_filter0, RecBuff, 2048)) { Error_Handler(); } /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ BSP_LED_Toggle(LED_GREEN); /* USER CODE BEGIN 3 */ if(DmaRecHalfBuffCplt == 1) { /* Store values on Play buff */ for(i = 0; i < 1024; i++) { PlayBuff[2*i] = SaturaLH((RecBuff[i]>> 8), -32768, 32767); PlayBuff[(2*i)+1] = PlayBuff[2*i]; } if(PlaybackStarted == 0) { if(0 != audio_drv->Play(AUDIO_I2C_ADDRESS, (uint16_t *) &PlayBuff[0], 4096)) { Error_Handler(); } if(HAL_OK != HAL_SAI_Transmit_DMA(&hsai_BlockA1, (uint8_t *) &PlayBuff[0], 4096)) { Error_Handler(); } PlaybackStarted = 1; } DmaRecHalfBuffCplt = 0; } if(DmaRecBuffCplt == 1) { /* Store values on Play buff */ for(i = 1024; i < 2048; i++) { PlayBuff[2*i] = SaturaLH((RecBuff[i]>> 8), -32768, 32767); PlayBuff[(2*i)+1] = PlayBuff[2*i]; } DmaRecBuffCplt = 0; } } /* USER CODE END 3 */
Código SAI1
/* USER CODE BEGIN SAI1_Init 2 */ /* Enable SAI to generate clock used by audio driver */ __HAL_SAI_ENABLE(&hsai_BlockA1); /* Initialize audio driver */ if(CS43L22_ID != cs43l22_drv.ReadID(AUDIO_I2C_ADDRESS)) { Error_Handler(); } audio_drv = &cs43l22_drv; audio_drv->Reset(AUDIO_I2C_ADDRESS); /* USER CODE END SAI1_Init 2 */
Código 4
/* USER CODE BEGIN 4 */ /** * @brief Half regular conversion complete callback. * @param hdfsdm_filter : DFSDM filter handle. * @retval None */ void HAL_DFSDM_FilterRegConvHalfCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter) { DmaRecHalfBuffCplt = 1; } /** * @brief Regular conversion complete callback. * @note In interrupt mode, user has to read conversion value in this function using HAL_DFSDM_FilterGetRegularValue. * @param hdfsdm_filter : DFSDM filter handle. * @retval None */ void HAL_DFSDM_FilterRegConvCpltCallback(DFSDM_Filter_HandleTypeDef *hdfsdm_filter) { DmaRecBuffCplt = 1; } /* USER CODE END 4 */
Código Debug
/* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ while (1) { /* Toggle LED4 with a period of one second */ BSP_LED_Toggle(LED_RED); HAL_Delay(1000); } /* USER CODE END Error_Handler_Debug */
Añadir fuentes BSP
Como en otros proyectos en los que se hace uso de fuentes de BSP proporcionados en el FW, localizamos la carpeta de instalación de CubeMX:
[carpeta_STMCubeMX-version]/Repository/STM32Cube_FW_L4_V1.13.0/Drivers/BSP
Y dentro de ese directorio, encontraremos una carpeta por cada placa de evaluación y una carpeta para los componentes de las placas (acelerómetros, drivers de audio…). Debemos arrastrar y copiar (Copy files and folders) la carpeta BSP a nuestro proyecto en Eclipse, dentro de Drivers, posteriormente iremos eliminando archivos y carpetas innecesarias de otras PCBs y dispositivos hasta que queden únicamente los necesarios, en este proyecto se requiere la incorporación y configuración mínima de archivos de Drivers mostrada en las figuras siguientes:
Ejecución y pruebas de reproducción
Para realizar las pruebas no se requiere ningún archivo de audio, se han definido un buffer de reproducción y otro de grabación de forma circular en modo de doble-buffer, cuando se llena la mitad del buffer de grabación se rellena la primera mitad del buffer de reproducción, cuando se llena totalmente el buffer de grabación se pone la segunda parte del buffer de grabación en la segunda mitad del buffer de reproducción.
Se recomienda lleva a cabo una ejecución con puntos de ruptura para comprender la técnica de dobles-buffers entrelazadas de grabación/reproducción implementada con DMA e interrupciones asociadas.
También se ha dispuesto una vía de depuración adicional en ejecución con el encendido del LEDs: verde intermitente por cada llenado del buffer de 4K PlayBuff, y parpadeo lento en rojo ante errores.
Modificaciones propuestas
- Incorporar al proyecto anterior una interfaz de grabación/reproducción asistida por una interfaz, haciendo uso de la pantalla LCD, JoyStick y LEDs.
- Generar un archivo de audio grabado en memoria Flash y/o en memoria USB externa, añadiendo la correspondiente cabecera WAV, para extraerlo y comprobar en PC.
- Otra propuesta más avanzada sería detección de comandos vocales para gobernar algún actuador, donde pueden emplearse librerías recientes basadas en redes neuronales con modelos pre-entrenados, como https://github.com/ARM-software/ML-KWS-for-MCU
dokuwiki\Exception\FatalException: Allowed memory size of 134217728 bytes exhausted (tried to allocate 4096 bytes)
An unforeseen error has occured. This is most likely a bug somewhere. It might be a problem in the authplain plugin.
More info has been written to the DokuWiki error log.