Placa de desarrollo STM32 Discovery kit //32L476GDISCOVERY// con MCU STM32L476VG
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.
Divisor = Frecuencia_reloj_DFSDM / (Frecuencia_muestreo × Factor_diezmado)
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.
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:
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).
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.
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
/* 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 */
/* USER CODE BEGIN PM */ #define SaturaLH(N, L, H) (((N)<(L))?(L):(((N)>(H))?(H):(N))) /* USER CODE END PM */
/* 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 */
/* USER CODE BEGIN 1 */ uint32_t i; /* USER CODE END 1 */
/* 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 */
/* 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 */
/* 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 */
/* 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 */
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:
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.