Autor Tema: [Proyecto] Vúmetro con pantalla LED  (Leído 9061 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
[Proyecto] Vúmetro con pantalla LED
« en: 10 de Mayo de 2017, 19:38:55 »
Hace rato que terminé y pude ensamblar unos paneles RGB (http://www.todopic.com.ar/foros/index.php?topic=43643.0) y estaba buscando un proyecto para usarlos.
Se me ocurrió un Vúmetro y como estoy muy metido con los STM32 decidía hacerlo con una placa de evaluacion de ST, en este caso la STM32F103 Nucleo, la cual posee un ARM M3.
En un principio hice un intento con un STM32F0 (ARM M0) pero me encontré con algunos problemas al portar mi driver escrito para un M4F a este micro. Así que decidí pasar a un M3.
Si bien el F103 no tiene manejo de punto flotante por hardware, en principio no me molesta ya que las CMSIS tienen librerías de DSP con funciones para punto fijo en formato Q31 y Q15.

Bien, un vúmetro es un indicador de amplitud de señal en funcion de la frecuencia. En el eje X del panel tendremos la frecuencia y en el eje Y tendremos la amplitud. Es una especie de analizador de espectro, muy básico, para audio. A mi me gusta ya que son muy decorativos para un centro musical.

Para realizar un vúmetro de forma digital tenemos que tener presentes 3 bloques. Primero el muestreo de la señal de audio. Según el teorema de nyquist la frecuencia de muestreo debe ser, al menos, el doble de la máxima frecuencia de nuestro sistems. Si lo que se va a muestrear es audio en la banda de 20Hz a 20KHz lo ideal es muestrear a 40KHz. Muestrear a 40KHz con un ADC de 12bits puede suponer una carga un poco excesiba para nuestro micro. Podemos considerar que la señal de entrada está acotada en banda y supongamos que queremos llegar a sonidos de hasta 10KHz. En dicho caso el muestreo será a 20KHz. Esto es, necesitamos que nuestro uC convierta y almacene en RAM 1 muestra cada 50uSeg.
El segundo bloque es el "calculador de espectro en frecuencia" o más conocido como FFT (transformada rápida de fourier). Con la FFT podes descoponer una señal almacenada en un array en sus componentes frecuenciales, dándonos como resultado la "potencia" de la señal en cada rango discreto de frecuencias. Un parámetro de la FFT es la cantidad de "bines" o divisiones de frecuencia que se realizarán. Este número es una potencia de 2. Valores típicos son de 32, 64, 128, 256.... 4096 puntos de FFT. A mayor cantidad de bines, mayor tiempo de cómputo y mejor resolucion en frecuencia. Por ejemplo, si eligo una FFT de 64 puntos dividiré la Fs de 20KHz entre 64 y tendré una resolucion de 312 Hz. Alguno se habrá dado cuenta que si la señal esta acotada en banda a 10KHz, que pasa con los bines 32 en adelante. Pues se repetirá el espectro que tenemos en los primeros 32 bines, pero espejado. Entonces, el bin 0 del array que devuelve la FFT corresponde a la F=0 a 106Hz -> nivel de continua de la señal. El segundo bin contendra la potencia de las componentes en frecuencia entre 106 y 418 Hz, centrado en F=312. Y así sucesivamente.
Finalmente el 3er bloque tendrá que tomar los bines de la FFT y convertirlos en el buffer de memoria que usará la pantalla LED para mostrar contenido. Este buffer y la forma de refrescar la pantalla es dependiente de la pantalla/driver que usemos.

Bien, siempre me gustó ir de atras para adelante, así que muestro el resultado y luego comenzaré con los pasos de diseño y mostrarles como el CubeMX de ST crea casi todo el código. Tambien mostrarles como usar las funciones de DSP en un M3!


En los próximos post el step by step de cada bloque.

Saludos!
« Última modificación: 10 de Mayo de 2017, 22:12:28 por elgarbe »
-
Leonardo Garberoglio

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
Re:[Proyecto] Vúmetro con pantalla LED
« Respuesta #1 en: 10 de Mayo de 2017, 22:11:47 »
El muestreo

Bien el primer problema al que nos enfrentamos es poder muestrar la señal de audio a una tasa mas o menos respetable y almacenar los datos en la RAM.
La señal ingresará a nuestro sistema por un canal ADC. En mi caso tengo AGND a 0V y AVDD a 3.3V por lo que necesito que la señal de audio que entre sea siempre positiva y este centrada en 1.65V. Hay uchas formas elegantes de hacer eso y ustedes me van a sugerir la mejor. En mi caso, para hacer la primeras pruebas colgé la entrada del ADC de un divisor resistivo, formado por 2 resistencias de 15K. De este modo, en reposo obtengo 1.65V. Luego con un capacitor cerámico entre la entrada del ADC y la señal de audio consigo el desacoplo de la señal y de este modo obtengo una señal que se movera centrada en 1.65V. La tension que sale de la mayoría de las placas de sonido o los celulares o los reproductores de mp3 no llegan a 1.65V por lo que no me preocupa la saturacion.
Bien, con esto y sabiendo que el ADC es de 12bits, sé que la señal de entrada estará en 2048 cuentas en reposo y se moverá en mas o menos cuentas de acuerdo al sonido que ingrese.

El siguiente paso es la adquisicion de los valores del ADC. Para esto tenemos al CubeMX que me permite fácilmente configurar el canal del ADC elegido e inicializarlo.
Aquí vemos que hemos elegido la entrada PA0 que es la AN1 como entrada analógica.


* VU1-min.png
(44.58 kB, 1235x813 - visto 1278 veces)


En el siguiente cuadro vemos la frecuencia de los distintos periféricos, la del ADC es de 10.66MHz.


* VU2-min.png
(51.16 kB, 1545x724 - visto 1093 veces)


En la siguiente pestaña podemos configurar los periféricos. En el caso del ADC tenemos el siguiente cuadro:


* VU3-min.png
(15.47 kB, 654x621 - visto 1061 veces)


En él podemos elegir el modo de conversion, la alineación de los datos, el trigger del ADC el sampling time (que no es La frecuencia de muestreo de nuestro sistema).
Debido a que nuestro muestreo tiene que ser lo más constante/regular posible y como el uC además de manejar el muestreo tiene que manejar el refresco de la pantalla led y tiene que hacer la FFT (costosa en procesamiento) se necesita que el ADC trabaje de forma lo más autónoma posible.
Primero verifiquemos que el ADC puede muestrear a más de 20.000 muestras por segundo. El Tconv = Sampling time + 12.5 cycles. En nuestro caso tenemos elegido un sampling time de 1.5 cycle. Por lo que el Tconv es 14 cycles. A 10.6666 MHz son necesarios 1.31 useg para la conversion, por lo que estamos por demas de bien ya que tenemos 50uSeg.
Verificado esto tenemos que ver como hacer para que el ADC haga conversiones a una tasa regular. Para ello recurrimos al External Trigger Conversion Source. En este canal del ADC podemos elegir varios evento que disparen la conversion del ADC, uno de ellos es Timer 3 Trigger Out Event. o sea que podemos configurar un timer, el 3, para que nos de un evento que dispare la conversion del ADC. Los timer son muy precisos y será una excelente base de tiempos.
El siguiente tema es qué hacer cuando el ADC finalice una conversion. Una opcion es que interrumpa y nosotros tomemos el valor leído y lo almacenemos en un buffer, el cual deberemos mantener para hacerlo de un modo circular... Otra opción es mi querido DMA!!!! Configurando un canal DMA podemos hacer que el ADC almacene cada conversion directamente en la RAM, incluso podemos configurar al DMA en modo circular, con lo que al llegar al final del buffer, solito va a reiniciar el índice y empezará a pisar los datos del comienzo. Pero como, nos va a pisar los datos???? Si, obvio, pero nosotros nos tenemos que asegurar de haberlos usado antes que se pisen. Como hacemos esto? Pues dando un tamaño grande al buffer y configurando la interrupcion del DMA por transferencia "medio completa". Por ejemplo si nosotros necesitamos 128 muestras, podemos poner un buffer de 1024. Entonces cuando el DMA llegue a la muestra 512 interrumpirá, nosotros podemos usar las muestras 384 a 512 (las más recientes) y tendremos el tiempo de 896 muestras antes que se nos pisen los datos que estamos usando. Como ven, configurando el ADC disparado por timer y el DMA para almasenar las muestras tenemos un sistema de muestreo a tasa constante completamente for hardware!!!!
Entonces veamos como habilitar el DMA para el ADC y su configuracion:


* VU4-min.png
(9.38 kB, 654x621 - visto 1076 veces)


Como ven hemos elegido el modo circular y el incremento automatico de la memoria de destino.
Veamos ahora la configuracion del timer para el sampleo a 20.000 KHz.


* VU5-min.png
(11.41 kB, 654x603 - visto 1052 veces)


Con el core clock a 64 MHz podemos dividirlo por 16 para tener un tick de 4MHz en el timer. Luego contar 200 ticks para tener una frecuencia de 20KHz o una interrupcion/conversion cada 50uSeg. Para ello ponemos el prescaler en 16-1 y el Counter en 200.

Finalmente veamos el código que hay que agregar al generado por el CubeMX para que el sampleo automático funcione:

Código: C
  1. int main(void)
  2. {
  3.  
  4.   /* USER CODE BEGIN 1 */
  5.  
  6.   /* USER CODE END 1 */
  7.  
  8.   /* MCU Configuration----------------------------------------------------------*/
  9.  
  10.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  11.   HAL_Init();
  12.  
  13.   /* Configure the system clock */
  14.   SystemClock_Config();
  15.  
  16.   /* Initialize all configured peripherals */
  17.   MX_GPIO_Init();
  18.   MX_DMA_Init();
  19.   MX_SPI2_Init();
  20.   MX_TIM1_Init();
  21.   MX_ADC1_Init();
  22.   MX_TIM3_Init();
  23.  
  24.   /* USER CODE BEGIN 2 */
  25.         //Configuro los TLC
  26.         TLC_Config();
  27.         Cartel_Memoria_Init();
  28.         Cartel_make_mem_DMA();
  29.         // Inicializo el barrido vertical
  30.         vSync_Init();
  31.  
  32.         HAL_TIM_Base_Start(&htim3);
  33.         HAL_ADC_Start_DMA(&hadc1, (uint32_t *)DMA_ADCvalues, 1000);
  34.   /* USER CODE END 2 */
  35.  
  36.   /* Infinite loop */
  37.   /* USER CODE BEGIN WHILE */
  38.   while (1)
  39.   {
  40.   /* USER CODE END WHILE */
  41.  
  42.   /* USER CODE BEGIN 3 */
  43.           Cartel_put_32bars(msg);
  44.           Cartel_make_mem_DMA();
  45.           HAL_Delay(10);
  46.   }
  47.   /* USER CODE END 3 */
  48.  
  49. }

La idea es colocar nuestro código entre dos marcadores de este tipo:

Código: C
  1. /* USER CODE BEGIN 1 */
  2.  
  3.   /* USER CODE END 1 */
De ese modo cuando hacemos algun cambio en el CubeMX y regeneramos el código se nos actualiza todo pero no perdemos lo que hayamos puesto entre esos marcadores.

Básicamente yo hago la inicializacion de la pantalla LED, creo el buffer de memoria de la misma y llamo a dos funciones:

Código: C
  1. HAL_TIM_Base_Start(&htim3);
  2.         HAL_ADC_Start_DMA(&hadc1, (uint32_t *)DMA_ADCvalues, 1000);

La primera enciende la base de tiempos del TIMER 3 y la segunda comienza la transferencia por DMA de 1000 valores y modo circular. Al estar modo circular no necesito volver a iniciar la transferencia pasados los primeros 1000 muestreos. Hermoso!!!!

Bueno, dejo acá por un rato. En la próxima tomaremos los datos y haremos la FFT para obtener el espectro de frecuencia.

Saludos!
-
Leonardo Garberoglio

Desconectado vixctor

  • PIC16
  • ***
  • Mensajes: 110
Re:[Proyecto] Vúmetro con pantalla LED
« Respuesta #2 en: 10 de Mayo de 2017, 22:33:28 »
Hola elgarbe

Siempre me han gustado los páneles de LED y más con analizadores de espectros, pero déjame hacerte unas sugerencias:

1.- El primer factor de la FFT NO es tal cual el valor de frecuencia de 0 a X Hz, en realidad es el valor de voltaje DIRECTO que tiene la señal, en éste caso lo puedes notar porque la primera barra siempre está al tope, lo cual significa qué tu ADC NO esta centrado a la mitad del valor de voltaje medido, es decir, que tu FFT pareciera estar mostrando voltajes positivos cuando en realidad debería capturar el rango positivo y negativo.

2.- Hay un punto muy importante y a la vez curioso cuando se desarrolla una FFT, el cual normalmente se menciona en un solo párrafo en los libros (DSP Proakis Manolakis por ejemplo) y tiene que ver con qué tanto es tantito la resolución necesaria en bits para la FFT. 

Este valor, tiene qué ver con el nivel señal ruido, el cual normalmente es de 60 dB.
¿Qué significa? Que dado un tamaño de puntos de FFT y su frecuencia, se puede determinar cuantos bits es necesario tener en el ADC para asegurar una relación señal-ruido de 60 dB, lo cual, para aplicarse en un display bien podrían ser 8 bits (12 bits está sobrado)

3.- Lo más importante al momento de hacer un analizador de espectros, es precisamente mantener en tiempo REAL la captura y despliegue de la FFT, porque si capturas un segmento de audio, lo procesas y lo envías al display y luego comienzas de nuevo, vas a perder segmentos de audio...
Es en esos pequeños segmentos de audio qué se pierden, en donde las frecuencas media-altas se encuentran, tal que podrás notar que los graves y los medios se despliegan correctamente más pareciera que los agudos no.

Ese efecto NO se nota cuando pruebas tu sistema bajo un generador de señales, pues la señal aplicada es periódica, pero en audio, el sonido de los platillos de la batería suele no graficarse con la intensidad necesaria.

Para ello, lo correcto es que el tiempo de procesamiento de tu FFT, sea igual o menor al tiempo de captura de muestras, tal qué si tomas por ejemplo 32 muestras @ 50 uS = 1.6 milisegundos, ese sea el tiempo de procesamiento máximo de tu FFT, y al mismo tiempo, por interrupción o DMA, captures el siguiente bloque de muestras mientras procesas la FFT del bloque actual...

4.- La frecuencia de sampleo que tienes es muy baja, en realidad SI qué debería de ser de 40 Khz y hasta de 80 Khz para samplear el canal izquierdo y el derecho y promediarlos, más es posible que tu procesamiento de FFT tarde mucho (debido a los 12 bits del ADC) y hayas restringido el valor de frecuencia del ADC

Ahora, te recomiendo separar en dos partes el procesamiento de FFT versus el refresco de la pantalla de LEDS, pues eso te va a permitir hacer cosas bastante interesantes:

Si estableces tu refresco de pantalla a 30 o 60 hz, lo que puedes hacer entre refresco y refresco es capturar N FFT y promediarlas u obtener los PICOS de intensidad más altos de ellas tal qué cuando procedas a graficar la señal puedas precisamente representar los picos de los bines capturados entre periodos de refresco...

Ejemplo: Si usas 30 Hz de refresco y capturas 32 puntos @ 50 uS puedes capturar 20.83 FFTs entre los tiempos de refresco de tu pantalla

Por otro lado, al tener un periodo de refresco de pantalla independiente a la captura de FFT, puedes "simular" capacitores qué hagan qué cada barrita baje suavemente en lugar de presentar a "secas" la FFT procesada, recuerda que un analizador de espectros es más un elemento estético qué un instrumento de medición...

Este es un ejemplo, pero en un PIC18, en una pantalla OLED de 256 x 64 pixeles en 16 tonos de grises, sampleando a 80 Khz para capturar canal derecho e izquierdo, obtener el valor RMS de cada canal de audio y promediarlos para generar la FFT de 128 puntos usando el ADC en modo de 8 bits.

Primero se captura el primer bloque de audio, y mientras se procesa se continua en paralelo la captura del siguiente bloque de audio, de tal manera qué al final se capturar 7 FFT, se obtienen los picos de señal de cada una y se simula un capacitor de decay para suavizar las barras...


Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
Re:[Proyecto] Vúmetro con pantalla LED
« Respuesta #3 en: 10 de Mayo de 2017, 23:25:22 »
Muchísimas gracias por el aporte!!!! si bien la idea era mostrar una aplicacion hecha más o menos para ver lo facil que es trabajar con los micros de ST y el CubeMX, pues si se agregan conocimiento y podemos hacer algo más elavorado bienvenido sea.
Algunos de los conceptos que expones los tengo presente, simplemente mostré una simplificación del problema. Pero debo reconocer que la gran moyoría de los temas no los tenía presente.

Cuando termine la exposicion de la resolucion del Vúmetro básico, te propongo me ayudes a incluir las mejoras propuestas para obtener un mejor equipo!

Saludos y gracias!

-
Leonardo Garberoglio

Desconectado vixctor

  • PIC16
  • ***
  • Mensajes: 110
Re:[Proyecto] Vúmetro con pantalla LED
« Respuesta #4 en: 11 de Mayo de 2017, 00:21:05 »
Claro qué sí, con gusto iré proponiendo esas partes, no son difíciles, solo capciosas, digo, llevo mucho tiempo en esto de los analizadores de espectros y he pasado por cada etapa viendo los resultados...

Mi primera FFT se veía como la tuya, y no comprendía el porqué no mostraba todos los elementos de audio hasta que fui observando y aprendiendo uno x uno...

Saludos

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
Re:[Proyecto] Vúmetro con pantalla LED
« Respuesta #5 en: 11 de Mayo de 2017, 19:17:13 »
Bien, nos quedamos entonces en que tenemos un bonito array de 1000 valores en la RAM, los cuales se van pisando constantemente. Por otra parte, cuando el canal DMA transfirió la mitad de los 1000 valores me dará una interrupcion.
Las HAL tienen un modo interesante de manejar las interrupciones. El punto de entrada de ellas esta en un archivo stm32f1xx_it.c. En él vamos a encontrarnos con algo como esto:

Código: C
  1. /**
  2. * @brief This function handles DMA1 channel1 global interrupt.
  3. */
  4. void DMA1_Channel1_IRQHandler(void)
  5. {
  6.   /* USER CODE BEGIN DMA1_Channel1_IRQn 0 */
  7.  
  8.   /* USER CODE END DMA1_Channel1_IRQn 0 */
  9.   HAL_DMA_IRQHandler(&hdma_adc1);
  10.   /* USER CODE BEGIN DMA1_Channel1_IRQn 1 */
  11.  
  12.   /* USER CODE END DMA1_Channel1_IRQn 1 */
  13. }

Ese es el canal del DMA que maneja al ADC. Por lo que o unico que tiene este ISR es una llamada a otra funcion de la HAL. Veamos una parte de lo que contiene:

Código: C
  1. /* Half Transfer Complete Interrupt management ******************************/
  2.   if(__HAL_DMA_GET_FLAG(hdma, __HAL_DMA_GET_HT_FLAG_INDEX(hdma)) != RESET)
  3.   {
  4.     if(__HAL_DMA_GET_IT_SOURCE(hdma, DMA_IT_HT) != RESET)
  5.     {
  6.       /* Disable the half transfer interrupt if the DMA mode is not CIRCULAR */
  7.       if((hdma->Instance->CCR & DMA_CCR_CIRC) == 0)
  8.       {
  9.         /* Disable the half transfer interrupt */
  10.         __HAL_DMA_DISABLE_IT(hdma, DMA_IT_HT);
  11.       }
  12.       /* Clear the half transfer complete flag */
  13.       __HAL_DMA_CLEAR_FLAG(hdma, __HAL_DMA_GET_HT_FLAG_INDEX(hdma));
  14.  
  15.       /* Change DMA peripheral state */
  16.       hdma->State = HAL_DMA_STATE_READY_HALF;
  17.  
  18.       if(hdma->XferHalfCpltCallback != NULL)
  19.       {
  20.         /* Half transfer callback */
  21.         hdma->XferHalfCpltCallback(hdma);
  22.       }
  23.     }
  24.   }

Se verifican y borran algunas banderas y lo más importante es lo que esta al final y es "hdma->XferHalfCpltCallback(hdma);"
En esa instruccion se usa lo que se denomina callback y es una forma standar de darle al usuario funciones en donde poner el código para cada interrupcion en particular, dejando todo lo referente a banderas a las HAL.
Entonces lo que nosotros tenemos que hacer es implementar esa funcion en nuestro código y la misma será llamada de forma automática.

En mi código pongo:

Código: C
  1. void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
  2. {
  3. }

y en ese lugar pongo el código que quiero que se ejecute cuando el DMA valla por la mitad de la transferencia.
En ese momento lo que yo quiero hacer es calcular la FFT y obtener la magnitud (la FFT devuelve numeros complejos) de cada bin.

Código: C
  1. void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
  2. {
  3.         uint32_t i;
  4.  
  5.         for(i=0;i<128;i+=2)
  6.         {
  7.                 FFT_ADCvalues[i]=DMA_ADCvalues[i/2];
  8.                 FFT_ADCvalues[i+1]=0;
  9.  
  10.         }
  11.         arm_cfft_q31(&arm_cfft_sR_q31_len64, FFT_ADCvalues, 0, 1);
  12.     for (i = 0; i < 64 * 2; i+=2)
  13.     {
  14.         arm_sqrt_q31((FFT_ADCvalues[i] * FFT_ADCvalues[i]) + (FFT_ADCvalues[i+1] * FFT_ADCvalues[i+1]), &MAG_of_fft[i/2]);
  15. //Escalado para que llene las 16 lineas de la pantalla.
  16.         MAG_of_fft[i/2] *= 16;
  17.         MAG_of_fft[i/2] /= 2000000;
  18.         if(MAG_of_fft[i/2] > 16)
  19.                 MAG_of_fft[i/2]=16;
  20.         msg[i/2]=MAG_of_fft[i/2];
  21.     }
  22. }

Entonces, el DMA guarda en forma circular 1000 valores, cuando va por el 500 me interrumpe. Yo tomo los primeros 64 valores y armo un array con ADC[0], 0, ADC[1], 0, ADC[2], 0... esto es necesario por que la funcion arm_cfft_q31 de la CMSIS calcula los valores complejos de la FFT.
Dicha funcion calcula la FFT inplace entonces solo le enviamos ese array y le decimos la cantidad de puntos de FFT que haremos. En este caso 64 puntos. El paso siguiente es componer los 128 valores (parrte real e imaginaria de cada bin) en un nuevo array de 64 puntos con los valores de la magnitud de cada bin. En este caso en realidad estoy tomando solo 64 puntos para obtener mis 32, ya que la matriz tiene 64 puntos horizontal pero estoy mostrando cada barra con 2 columnas de ancho
Ya vimos (gracias vixctor) que esta implementaciòn no es la mejor, pero es la que me asegura un funcionamiento del sistema sin renegar demasiado. Hay mucho por optimizar!!!
La ultima parte del segundo for me arma el array msg[] con los valores que voy a mostrar en la pantalla led.

Finalmente en el while principal tengo:

Código: C
  1. while (1)
  2.   {
  3.   /* USER CODE END WHILE */
  4.  
  5.   /* USER CODE BEGIN 3 */
  6.           Cartel_put_32bars(msg);
  7.           Cartel_make_mem_DMA();
  8.           HAL_Delay(10);
  9.   }

Aquí, en forma asíncrona, tomo el array msg y lo transformo en barras de mi cartel. Luego tomo el buffer "de video" y lo convierto en un buffer que usará el DMA del SPI para refrescar la pantalla.
El refresco de la pantalla esta funcionando de fondo, todo con DMA e interrupciones.

Bien, hasta aquí lo que quería mostrar, una aplicacion de un stm32 a un proyecto puntual usando el CubeMX y ver que casi no hay código escrito más que el que crea el mismo CubeMX.

Veamos ahora como ir mejorando cada aspecto del sistema para obtener un resultado más profesional. Para ello espero contar con la ayuda de vixctor!!!

Saludos!
-
Leonardo Garberoglio

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
Re:[Proyecto] Vúmetro con pantalla LED
« Respuesta #6 en: 11 de Mayo de 2017, 19:51:23 »
Me gustaría aprender a mejorar el sistema por lo que vamos a tratar de entablar un intercambio de ideas.

Yo tengo una noción de la FFT, de lo que la relacion señal ruido y algunos otros conceptos, pero en otro contexto. Estoy trabajando en el desarrollo de un equipo de otoemisiones aucústicas. En dicho sistema debemos exitar 2 speaker miniaturas, con 2 tonos de diferente frecuencias. Uno en 60dBSPL y el otro en 50dBSPL. Las frecuencias se eligen de modo que f2=1.2f1. Exitando el oido humano con dichos sonidos se ha demostrado que se exita la coclea y ésta responde con lo que se llama producto de distorcion. Es una señal que esta hubicada en 2f1-f2 y tiene una amplitud de entre -10 a 10dB SPL. Uno de los requerimientos del sistema es poder leer la DPOAE al mismo tiempo que se leen los estímulos. En este caso tenemos unos 70dB de diferencia. Otro requisisto es llevar con algunas técnicas el piso de ruido a -20dB SPL. Lo que nos da una SNR de 80dB. En ese equipo trabajamos arduamente con FFT, promedios sincrónicos en tiempo para aumentar la SNR, etc.
Es por ello que me interesa muchísimo compartir ideas y tratar de aprender más sobre estos temas.

1.- El primer factor de la FFT NO es tal cual el valor de frecuencia de 0 a X Hz, en realidad es el valor de voltaje DIRECTO que tiene la señal, en éste caso lo puedes notar porque la primera barra siempre está al tope, lo cual significa qué tu ADC NO esta centrado a la mitad del valor de voltaje medido, es decir, que tu FFT pareciera estar mostrando voltajes positivos cuando en realidad debería capturar el rango positivo y negativo.
He leído decenas de explicaciones de que frecuencia representa cada bin de la FFT hasta que encontré un documento (que no tengo acá, esta en la facu) donde explicaba a la perfeccion y pude comprobar prácticamente. Si mal no recuerdo, el bin 0 no tiene solo la componente de continua. Cada bin de la FFT, por ser ésta discreta, presenta el contenido de un grupo de frecuencias y tiene una respuesta más precisa a una frecuencia central. Entonces con una Fs de 20.000Hz y una FFT de 1024puntos tenemos una resolucion de 19.53 Hz. Yo creo que eso no significa que el bin 1 me indique la potencia de la señal solo de 19.53Hz. De hecho, si coloco el GAF y voy variando la frecuencia de a 1 Hz puedo ver como el espectro se mueve de un bin a otro "de a poco" no hay un salto discreto entre 19.53 y 39.06, si no que se va "transfiriendo" de un bin a otro. Lo que no recuerdo es si el bin 0 es una excepcion y solo mide el contenido de DC (leí en muchos lugares que es así, pero en lugares donde para mi erran en el concepto anterior). Aquí tengo algunas dudas ya que la transferencia entre bines es medio rara, ya mostrare un video de lo que digo para que me ayuden a entender.
Coincido que hay algo raro en mi adquisicion y en mi FFT. Mi señal de entrada es toda positiva, ya que el ADC va de 0 a 4096. Yo pongo un bias, como explique al principio para que sin señal tenga 1.65V y mi ADC mida 2048 cuentas. Eso lo probe y es así. El tema está en que al momento de hacer los cálculos creo que debería restar ese ofset para que la señal tenga parte positiva y parte negativa. De hecho uso el formato Q31 porque es punto fijo de numeros positivos y negativos, pero creo que lo estoy usando mal.
Algo como esto:
https://community.arm.com/processors/f/discussions/6997/how-to-prepare-adc-data-for-q31_t-cmsis-dsp-functions
Una vez que solucione esto seguro que la barra del bin0 se irá a 0.

2.- Hay un punto muy importante y a la vez curioso cuando se desarrolla una FFT, el cual normalmente se menciona en un solo párrafo en los libros (DSP Proakis Manolakis por ejemplo) y tiene que ver con qué tanto es tantito la resolución necesaria en bits para la FFT. 

Este valor, tiene qué ver con el nivel señal ruido, el cual normalmente es de 60 dB.
¿Qué significa? Que dado un tamaño de puntos de FFT y su frecuencia, se puede determinar cuantos bits es necesario tener en el ADC para asegurar una relación señal-ruido de 60 dB, lo cual, para aplicarse en un display bien podrían ser 8 bits (12 bits está sobrado)

No lo sabía y es muy pero muy interesante para mi.
Aquí encontre lo que me dices:

* ADC.png
(89.37 kB, 688x790 - visto 1033 veces)


12 bits es mucho. Pero me entra una duda. El uC es de 32bits por lo que maneja de forma nativa las funciones de 32 bits. Realmente será mas lento calcular una FFT en Q31 que en Q15 o que en 8 bits? algo para investigar.

3.- Lo más importante al momento de hacer un analizador de espectros, es precisamente mantener en tiempo REAL la captura y despliegue de la FFT, porque si capturas un segmento de audio, lo procesas y lo envías al display y luego comienzas de nuevo, vas a perder segmentos de audio...
Es en esos pequeños segmentos de audio qué se pierden, en donde las frecuencas media-altas se encuentran, tal que podrás notar que los graves y los medios se despliegan correctamente más pareciera que los agudos no.

Ese efecto NO se nota cuando pruebas tu sistema bajo un generador de señales, pues la señal aplicada es periódica, pero en audio, el sonido de los platillos de la batería suele no graficarse con la intensidad necesaria.

Para ello, lo correcto es que el tiempo de procesamiento de tu FFT, sea igual o menor al tiempo de captura de muestras, tal qué si tomas por ejemplo 32 muestras @ 50 uS = 1.6 milisegundos, ese sea el tiempo de procesamiento máximo de tu FFT, y al mismo tiempo, por interrupción o DMA, captures el siguiente bloque de muestras mientras procesas la FFT del bloque actual...

Coincido, lo ideal es no perder parte de la informacion de audio y tratar de optimizar todo para ello.


4.- La frecuencia de sampleo que tienes es muy baja, en realidad SI qué debería de ser de 40 Khz y hasta de 80 Khz para samplear el canal izquierdo y el derecho y promediarlos, más es posible que tu procesamiento de FFT tarde mucho (debido a los 12 bits del ADC) y hayas restringido el valor de frecuencia del ADC

Acá no coincido del todo... Hay que hubicarse en el contexto. El estandar de 22050Hz para audio existe y se usa. Un CD esta muestreado a 48KHz me parece que un equipo "regular" puede muestrear a 20KHz y perder los agudos (que es un porcentaje muy bajo de la señal).
De todos modos, coincido que si levantamos a 40KHz y encima muestreamos los dos canales, pues el resultado va a ser muchísimo mejor que el obtenido a 20K.

Ahora, te recomiendo separar en dos partes el procesamiento de FFT versus el refresco de la pantalla de LEDS, pues eso te va a permitir hacer cosas bastante interesantes:

Si estableces tu refresco de pantalla a 30 o 60 hz, lo que puedes hacer entre refresco y refresco es capturar N FFT y promediarlas u obtener los PICOS de intensidad más altos de ellas tal qué cuando procedas a graficar la señal puedas precisamente representar los picos de los bines capturados entre periodos de refresco...

Ejemplo: Si usas 30 Hz de refresco y capturas 32 puntos @ 50 uS puedes capturar 20.83 FFTs entre los tiempos de refresco de tu pantalla

Por otro lado, al tener un periodo de refresco de pantalla independiente a la captura de FFT, puedes "simular" capacitores qué hagan qué cada barrita baje suavemente en lugar de presentar a "secas" la FFT procesada, recuerda que un analizador de espectros es más un elemento estético qué un instrumento de medición...

Exclente, voy a ir tratando de mejorar el sistema para implementarlo de este modo.

Este es un ejemplo, pero en un PIC18, en una pantalla OLED de 256 x 64 pixeles en 16 tonos de grises, sampleando a 80 Khz para capturar canal derecho e izquierdo, obtener el valor RMS de cada canal de audio y promediarlos para generar la FFT de 128 puntos usando el ADC en modo de 8 bits.

Primero se captura el primer bloque de audio, y mientras se procesa se continua en paralelo la captura del siguiente bloque de audio, de tal manera qué al final se capturar 7 FFT, se obtienen los picos de señal de cada una y se simula un capacitor de decay para suavizar las barras...


 ((:-)) ((:-)) ((:-)) ((:-)) Ese Vúmetro con un PIC18!!!  ((:-)) ((:-)) ((:-))

El primer punto que me interesaría mejorar es el sistema analógico de entrada. Lo ideal es que sea un amplificadorcito al que le pueda regular la ganancia para ajustarlo a los niveles máximos de la pantalla...
Que has usado como acondicionador en la entrada?

Saludos!
-
Leonardo Garberoglio

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
Re:[Proyecto] Vúmetro con pantalla LED
« Respuesta #7 en: 11 de Mayo de 2017, 20:49:33 »
2.- Hay un punto muy importante y a la vez curioso cuando se desarrolla una FFT, el cual normalmente se menciona en un solo párrafo en los libros (DSP Proakis Manolakis por ejemplo) y tiene que ver con qué tanto es tantito la resolución necesaria en bits para la FFT. 

Este valor, tiene qué ver con el nivel señal ruido, el cual normalmente es de 60 dB.
¿Qué significa? Que dado un tamaño de puntos de FFT y su frecuencia, se puede determinar cuantos bits es necesario tener en el ADC para asegurar una relación señal-ruido de 60 dB, lo cual, para aplicarse en un display bien podrían ser 8 bits (12 bits está sobrado)

Me quedé pensando en este punto. Si la SNR es 60dB y pensamos que tenemos que pensar en representar señales que varíen en 60dB entonces me parece que 8 bits no alcanza.
60dB es una ganancia de 1000. O sea que si 256 bits representan la señal más grande, pues con no llego a poder representar una señal 1000 veces más chica. Solo una que es 256 veces más chica. una ganancia de 256 nos da 48dB.

Necesitaría al menos 10 bits para representar una señal con 60dB de variación.

Pienso bien? no encuentro el libro que citaste para ver como se calcula realmente.

Saludos!
-
Leonardo Garberoglio

Desconectado vixctor

  • PIC16
  • ***
  • Mensajes: 110
Re:[Proyecto] Vúmetro con pantalla LED
« Respuesta #8 en: 11 de Mayo de 2017, 22:38:19 »
Hola elgarbe

Creo saber qué es lo que pasa con el sampleo del ADC que tienes al momento de manipular los datos...

El tema está en el formato de los datos, veras:

Tu mencionas que simplemente acomodaste la señal para que quedara a la mitad, o sea, a 2047 - 2048 medido en el ADC...

Eso qué estas haciendo tiene un nombre: "positive straight offset binary code" o sea, es un OFFSET pero tus muestras NO tienen un formato numérico real...

Pues bien, te falta ese paso en ese punto, ya qué el sistema debe estar en complemento a 2 o con signo para leer de  + - 2048

La idea es simple: Negar el bit MSB de tus muestras:

En mi caso por ejemplo, usando 8 bits, el primer paso me entrega valores como...

0 1 2 .... 127 -  128 ... 129 ... 255, tomando la mitad es el offset positivo binario...

todo lo qué tengo que hacer es negar el bit MSB del ADC, para que me quede algo como...

128 129 130.... 255 ... 0 1 2 ... 127...  (viendolo como un valor entero)
 
Pero en realidad considerando el bit MSB como el signo, tengo...

- 128 -127 -126... -3 -2 - 1 0 1  2  3   4 ... 127

Y entonces ya la FFT puede procesar correctamente los valores, pues estarán con signo desde el inicio, de ahí que precisamente tu primer BIN salga hasta el tope...



Desconectado vixctor

  • PIC16
  • ***
  • Mensajes: 110
Re:[Proyecto] Vúmetro con pantalla LED
« Respuesta #9 en: 11 de Mayo de 2017, 23:52:54 »
Pasando a otro punto, una cosa es el ruido RMS y otra el rango dinámico de la FFT, el cual tiene una formula diferente a la que presentas:

SNR en la FFT, se refiere más a la relación señal ruido generada por los errores de cuantificación de las etapas intermedias de la FFT más que al propio SNR del ADC...

Por ejemplo, para la DFT directa SNR equivale a:

                                         (2b - 2N)
SNR = 10 Log (base 10) 2

Donde N es el numero de puntos expresados como potencia de 2

Pero, en la FFT, y cuando se usa escalación inicial SNR cambia a:

                                                                2b - v - 1
Signal Noise Ratio = 10 Log (base 10) 2

Log 2 base 10 x 10 es 3.010 (se puede tomar como 3)

SNR = 3 (2b - v -1)

Donde V es el numero de puntos expresados como potencia de 2

Ejemplo: (tomado de Proakis Manolakis): Se requiere una FFT de 1024 puntos con un SNR de 30 dB, cuantos bits son necesarios?

Para DFT:

3(2b - 20) = 30

2b - 20 = 30/3

2b = 10 + 20

b = 30 / 2 = 15 bits

Para el mismo ejemplo, pero usando FFT:

3 (2b - 10 - 1) = 30

2b - 11 = 30 / 3

2b = 10 + 11

b = 21 / 2 = 11 bits necesarios

Ciertamente 60 dB de SNR es mucho, y de hecho ese valor no lo recordaba del todo bien (30 dB lo mencionan mucho en el libro), pero aun con libro en mano no recuerdo en donde leí ese míni párrafo en donde venia el mínimo SNR necesario para que funcione bien la FFT, pero vamos, en realidad NO es un valor alto, porque el vúmetro al final del día, tiene una escala reducida en la AMPLITUD qué puede mostrar...

Por cierto, el libro es Digital Signal Processing, John G. Proakis y Dimitris G. Manolakis, editorial PRENTICE HALL, la edición que tengo es la 3ra

Y de hecho SNR en FFT depende del algoritmo usado, Radix 2 o 4, decimación en frecuencia o en tiempo, para lo cual hace años me metí muy a fondo pues implemente radix 2F decimación en frecuencia y leí al menos los dos capítulos relacionados al tema, y de ahí precisamente obtuve qué con 8 bits podía salir bien para FFT de 256 puntos o menos.


Desconectado vixctor

  • PIC16
  • ***
  • Mensajes: 110
Re:[Proyecto] Vúmetro con pantalla LED
« Respuesta #10 en: 12 de Mayo de 2017, 00:09:02 »
Ahora, de la última pregunta, use un amplificador inversor con divisor de tensión en la entrada no inversora, acoplado por capacitor a la entrada y salida directa al ADC, pero hay un punto importante relacionado con los otros dos puntos que ya escribí antes...

1.- Recuerdas que para adecuar el formato numérico del ADC simplemente niegas o complementas el bit MSB del ADC? Pues bien, si lo has cachado bien, hacer eso va a "invertir" la polaridad de las muestras tal que la señal quedará invertida, por eso se usa un amplificador inversor antes del ADC, para volver a la señal a su polaridad normal...

2.- El problema de samplear a menor frecuencia qué la máxima encontrada en AUDIO, es qué en la FFT, las frecuencias mayores a 10 Khz (en tu caso) te van a rebotar en la pared derecha, es decir, cuando la frecuencia de audio supera la frecuencia de sampleo, tus bines van a ir hacia el lado contrario...

Eso te obliga si o si al menos a poner un filtro de corte de frecuencias altas de alto Q en el amplificador operacional para evitar ese problema...

Desconectado vixctor

  • PIC16
  • ***
  • Mensajes: 110
Re:[Proyecto] Vúmetro con pantalla LED
« Respuesta #11 en: 12 de Mayo de 2017, 00:47:31 »
Este es un test interesante, es largo el video pero explica mucho:


Antes que nada, esta versión de player que tengo es la penúltima generación, no la última, el cual corre en un PIC18F67K22 @ 64 Mhz

El video consta de 3 minutos, en donde se reproducen 2 archivos de 1 minuto de audio x cada archivo...

El test consiste en:

Minuto 1: Una señal de 10 a 20 Khz que dura 30 segundos y de 20 Khz a 10Hz los siguientes 30 segundos. (eso significa que cada segundo hay un incremento de 666 Hz)

Observación: Esta versión del reproductor de audio, no tiene la capacidad de llegar a los 80 Khz de sampleo por canal, sino a 60 Khz, por lo cual, la escala máxima de la FFT es de aprox 15 Khz.
Pasando los 15 Khz (21 -22 seg x 666 = 14666 Hz) la frecuencia de audio continua incrementandose pero ahora los "bines" van al revés, es lo que te explicaba qué sucedia en la FFT cuando la frecuencia del audio supera a la del ADC, o sea, ahora va de "reversa"
Cuando regresa de 20 Khz a 15 Khz las bandas vuelven a la normalidad...

Minuto 2: Ahora se reproduce un archivo de 10 a 15 Khz (500 Hz x segundo), y como podrás observar, efectivamente la FFT cubre todo ese espectro de audio, lo cual corrobora el ancho de banda de la FFT...

Minuto 3: Es el mismo archivo de 10 a 20 Khz pero ésta vez incremente el volumen a 0 dB... Lo que puedes observar, es precisamente los componentes de distorsión armónica generados cuando la señal SATURA la entrada de voltaje del ADC y se nota en esas componentes qué van de un lado para el otro, o sea, los múltiplos de la frecuencia fundamental...

Ese pequeño tremulo o vaiven de toda la FFT conforme avanza, se genera cuando NO aplicas una ventana de acotación a la FFT (Hamming, cuadrática, Gauss, etc) ya que la FFT al correr en multiplos de potencias de 2, cuando se captura un segmento de señal, y su periodo NO COINCIDE con el número de muestras capturadas, genera pequeños errores que se ven reflejados como componentes de frecuencia, por ello normalmente se aplica el llamado acotamiento por ventana, en donde la idea, es hacer qué las muestras del inicio del sampleo empiecen en 0 y se amplifiquen hasta llegar a su máxima amplitud y luego se desvanezcan hasta llegar nuevamente al valor de 0, lo cual evita esas pequeñas variaciones...

Nota1: La versión 2.0 del reproductor, hace un overclock del PIC llevandolo a 75 Mhz lo cual obliga al ADC a trabajar más rápido y por consiguiente el ancho de banda de la FFT llega a los 19-20 Khz efectivos...

Nota 2: Como puedes observar en las barras de medición de señal RMS, estas se mantienen CONSTANTES a lo largo del barrido de audio, con excepción al momento de llegar a la frecuencia de Nyquist, en donde se generan errores por ser el valor medio cuadrático...

Nota 3: No se los demás pero al menos Yo si alcanzo a escuchar toda la escala de audio hasta los 20Khz, lo cual es un buen test para probar la salud de nuestros oidos...  :P

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
Re:[Proyecto] Vúmetro con pantalla LED
« Respuesta #12 en: 12 de Mayo de 2017, 20:19:16 »
Hola elgarbe

Creo saber qué es lo que pasa con el sampleo del ADC que tienes al momento de manipular los datos...

El tema está en el formato de los datos, veras:

Tu mencionas que simplemente acomodaste la señal para que quedara a la mitad, o sea, a 2047 - 2048 medido en el ADC...

Eso qué estas haciendo tiene un nombre: "positive straight offset binary code" o sea, es un OFFSET pero tus muestras NO tienen un formato numérico real...

Pues bien, te falta ese paso en ese punto, ya qué el sistema debe estar en complemento a 2 o con signo para leer de  + - 2048

La idea es simple: Negar el bit MSB de tus muestras:

En mi caso por ejemplo, usando 8 bits, el primer paso me entrega valores como...

0 1 2 .... 127 -  128 ... 129 ... 255, tomando la mitad es el offset positivo binario...

todo lo qué tengo que hacer es negar el bit MSB del ADC, para que me quede algo como...

128 129 130.... 255 ... 0 1 2 ... 127...  (viendolo como un valor entero)
 
Pero en realidad considerando el bit MSB como el signo, tengo...

- 128 -127 -126... -3 -2 - 1 0 1  2  3   4 ... 127

Y entonces ya la FFT puede procesar correctamente los valores, pues estarán con signo desde el inicio, de ahí que precisamente tu primer BIN salga hasta el tope...

Totalmente de acuerdo, anoche cuando termine de postear me quede investigando el formato Q15 y es como dices, no puedo almacenar un uint16 en un Q15 como si nada. Hay que acomodar el formato del número.
Como primer medida voy a pasar el buffer del ADC a 16 bits. y voy a ver si puedo hacer funcionar el Q15, luego seguimos con los ortos mensajes, que por cierto son muy interesantes!.

Saludos!
-
Leonardo Garberoglio

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
Re:[Proyecto] Vúmetro con pantalla LED
« Respuesta #13 en: 12 de Mayo de 2017, 22:36:54 »
Estoy bastante complicado con la tranformacion a Q15. El formato Q15 lo necesito porque la funcion FFT se hace sobre numeros en formato q15.
pero ya lo voy a destrabar...

saludos
-
Leonardo Garberoglio

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:[Proyecto] Vúmetro con pantalla LED
« Respuesta #14 en: 12 de Mayo de 2017, 23:02:23 »
Se puede saber que estas intentando convertir?