pub:silab5

Interfaces de audio en STM32 DISCO-L476VG

Plataforma de evaluación HW para STM32L4

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

  • 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

Convertidor digital a analógico de audio portátil con controlador de altavoz de clase D integrado.

  • Estructura funcional:

  • Conectado en PCB por I2C (dirección 0x94) al SoC STM32L4 vía SAI
  • Esquemático de conexiones en STM32L4Discovery

  • Proporciona un interfaz de comunicación con dispositivos de audio externos
    • Totalmente configurable
    • Soporte de estándares: I2S, PCM, TDM, SPDIF, AC97
    • 2x Sub-Bloques independientes
  • Ventajas para aplicaciones:
    • Soporta gran variedad de dispositivos de audio
    • Operación Full-Dúplex
    • Formatos de audio digital totalmente configurables

  • Caso práctico de uso del módulo SAI: conexión a códec de audio (AD+DA+Amp)
    • Considerando la reproducción de música en un códec externo con tasa de muestreo de 44.1kHz, usando 16bit/canal (señal Stereo) conectaremos una señal de reloj de 11.29MHz al módulo SAI
    • Los datos deberían ser transferidos a la FIFO del SAI1 vía DMA2 canal 1 controlado por SAI1_A
    • Para controlar el DAC externo DAC podemos usar uno de los interfaces I2C (p.ej. I2C1)

  • Frecuencia y parámetros temporales según especificaciones del interfaz I2C por el fabricante del códec:

  • P.ej. derivado del un archivo formato WAV:

  • SAI1 interfaz SAI_A en modo maestro con reloj de salida (Master with Master Clock Out):

  • Configuración de reloj del SAI (MCLK):

Verificar reloj I2C1 (80 MHz) y SAI1 (11.294118 MHz) enCubeMX:

  • Uso de la técnica de doble-buffer con DMA en modo circular:

En CubeMX ajustamos únicamente los parámetros del SAI1 interfaz-SAI_A, para indicar el uso de un canal de DMA en sentido desde memoria al periférico, en este caso el propio SAI, el tamaño de los datos (muestras de 16-bit) y el modo circular (reproducción continua).

El tamaño del buffer concreto se determina en SW cuando se hace la llamada a la función HAL que relaciona al SAI con DMA. Por otro lado, la técnica de doble-buffer se implementa también en SW haciendo uso de las retro-llamadas disponibles en la HAL del SAI para la detección de buffer lleno y medio lleno.

Desarrollo SW: 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.

Desde ventana principal acceder a la selección de placas STBoard e iniciar un nuevo proyecto (Start Project) para la DISCO-L476VG. En la ventana emergente para la inicialización de periféricos a su modo por defecto indicamos que Si para aprovechar parte de la configuración y pines establecidos, p.ej. del display LCD, JoyStick, LEDs.

Suprimiremos los periféricos que no vamos a utilizar para obtener una vista de sistema:

Y una vista del pinout:

Verificamos que la interfaz de depuración está establecida a Serial Wire en la categoría SYS y el temporizador base del sistema el SysTick.

Configuramos en multimedia el SAI1 interfaz SAI_A, en comunicaciones el I2C1 y los relojes según las especificaciones HW del apartado //anterior//, y dejamos las interfaces básicas de E/S incluidas para usarlas con librerías de BSP.

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

/* Private 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 */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define AUDIO_FILE_ADDRESS   0x08080000
#define AUDIO_FILE_SIZE      (180*1024)	// audio.bin (22K)
//#define AUDIO_FILE_SIZE      (448*1024)	// audio1.bin (22K)
//#define AUDIO_FILE_SIZE      (896*1024)	// audio2.bin (44K)
#define PLAY_HEADER          44
#define PLAY_BUFF_SIZE       4096
/* USER CODE END PD */
/* USER CODE BEGIN PV */
//SAI_HandleTypeDef            SaiHandle;
//DMA_HandleTypeDef            hSaiDma;
AUDIO_DrvTypeDef            *audio_drv;
uint16_t                      PlayBuff[PLAY_BUFF_SIZE];
__IO int16_t                 UpdatePointer = -1;
/* USER CODE END PV */
/* USER CODE BEGIN 0 */
 
/* USER CODE END 0 */
 
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
	uint32_t PlaybackPosition   = PLAY_BUFF_SIZE + PLAY_HEADER;
  /* USER CODE END 1 */
 
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_I2C1_Init();
  MX_LCD_Init();
  MX_SAI1_Init();
  /* USER CODE BEGIN 2 */
  /* Configure LEDs */
  BSP_LED_Init(LED_GREEN);
  BSP_LED_Init(LED_RED);
 
  /* Check if the buffer has been loaded in flash (audio.bin)*/
  //if(*((uint64_t *)AUDIO_FILE_ADDRESS) != 0x017EFE2446464952 ) Error_Handler();
 
  /* Initialize playback */
  if(0 != audio_drv->Init(AUDIO_I2C_ADDRESS, OUTPUT_DEVICE_HEADPHONE, 80, AUDIO_FREQUENCY_22K))
  {
	  Error_Handler();
  }
 
  /* Initialize the data buffer */
  for(int i=0; i < PLAY_BUFF_SIZE; i+=2)
  {
    PlayBuff[i]=*((__IO uint16_t *)(AUDIO_FILE_ADDRESS + PLAY_HEADER + i));
  }
 
  /* Start the playback */
  if(0 != audio_drv->Play(AUDIO_I2C_ADDRESS, NULL, 0))
  {
    Error_Handler();
  }
  if(HAL_OK != HAL_SAI_Transmit_DMA(&hsai_BlockA1, (uint8_t *)PlayBuff, PLAY_BUFF_SIZE))
  {
    Error_Handler();
  }
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
	  BSP_LED_Toggle(LED_GREEN);
 
	  /* Wait a callback event */
	  while(UpdatePointer==-1);
 
	  int position = UpdatePointer;
	  UpdatePointer = -1;
 
	  /* Update the first or the second part of the buffer */
	  for(int i = 0; i < PLAY_BUFF_SIZE/2; i++)
	  {
		  PlayBuff[i+position] = *(uint16_t *)(AUDIO_FILE_ADDRESS + PlaybackPosition);
		  PlaybackPosition+=2;
	  }
 
	  /* check the end of the file */
	  if((PlaybackPosition+PLAY_BUFF_SIZE/2) > AUDIO_FILE_SIZE)
	  {
		  PlaybackPosition = PLAY_HEADER;
	  }
 
	  if(UpdatePointer != -1)
	  {
		  /* Buffer update time is too long compare to the data transfer time */
		  Error_Handler();
	  }
  }
  /* 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 Tx Transfer completed callbacks.
  * @param  hsai : pointer to a SAI_HandleTypeDef structure that contains
  *                the configuration information for SAI module.
  * @retval None
  */
void HAL_SAI_TxCpltCallback(SAI_HandleTypeDef *hsai)
{
  /* NOTE : This function Should not be modified, when the callback is needed,
            the HAL_SAI_TxCpltCallback could be implemented in the user file
   */
  UpdatePointer = PLAY_BUFF_SIZE/2;
}
 
/**
  * @brief Tx Transfer Half completed callbacks
  * @param  hsai : pointer to a SAI_HandleTypeDef structure that contains
  *                the configuration information for SAI module.
  * @retval None
  */
void HAL_SAI_TxHalfCpltCallback(SAI_HandleTypeDef *hsai)
{
  /* NOTE : This function Should not be modified, when the callback is needed,
            the HAL_SAI_TxHalfCpltCallback could be impleneted in the user file
   */
  UpdatePointer = 0;
}
/* USER CODE END 4 */
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
	/* LED5 On in error case */
	BSP_LED_On(LED_RED);
	while (1)
	{
	}
  /* 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 se requiere previamente incorporar el archivo WAV a reproducir en la memoria Flash del dispositivo, según hemos indicado en la //sección de código de Defines//, la dirección del archivo de audio se corresponde con la macro AUDIO_FILE_ADDRESS fijada inicialmente a 0x08080000 y el tamaño del mismo con AUDIO_FILE_SIZE a 180 KB. Una forma rápida y sencilla de llevar a cabo esta tarea es mediante una herramienta como STM32CubeProgrammer, que permite visualizar las distintas secciones de memoria del dispositivo conectado, así como la edición y programación a partir de archivos binarios externos (File programming) a partir de una dirección concreta (Start Address) según la figura siguiente:

La programación en una zona de memoria requiere en la mayoría de los casos el borrado de los sectores de la Flash que va a ocupar, y dado que los archivos de audio con determinada calidad tienen un tamaño elevado, se aconseja una forma rápida de borrado completa (Full chip erase). Posteriormente se realiza la programación del archivo a partir de la dirección indicada con (Start Programming)

Una vez grabados los datos de audio en la zona prevista de la memoria Flash, desconectamos esta herramienta (Disconnect) o directamente la cerramos, para volver al IDE de TrueStudio desde donde grabaremos el programa de reproducción que los maneja según el método habitual de la perspectiva de Depuración (Debug).

Se recomienda lleva a cabo una ejecución con puntos de ruptura para comprender la técnica de doble-buffer 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 bloqueo en rojo ante errores.

Incorporar al proyecto anterior una interfaz de reproducción más completa, con más de un sonido/archivo de audio, reproducción manual/continua, etc., haciendo uso de la pantalla LCD, JoyStick y LEDs.

Comprobar el tamaño disponible en la memoria Flash para el aprovechamiento de la misma con archivos de audio, de acuerdo a la ocupación de la aplicación.

Otra propuesta más avanzada sería la combinación con el RTC para programar y generar alarmas musicales.

  • pub/silab5.txt
  • Última modificación: 2020/09/28 11:44
  • (editor externo)