Analizador de Redes AC

From Qi-Hardware
Jump to: navigation, search

Felipe Andres Navarro Murcia

César Augusto Pantoja Restrepo


Contents

[edit] Introducción

En una red eléctrica existen perturbaciones externas producidas por fenómenos naturales, por líneas de energía cercanas y por equipos conectados a la misma, tales disturbios pueden llegar a afectar a otros equipos que se encuentran conectados en la misma red; por esta razón los equipos medidores de parámetros de red AC, cobran mucha importancia hoy en dia, a tal punto que existen empresas especializadas en la operación y análisis de tales parámetros. A continuación presentamos el diseño e implementación de un dispositivo que mide uno de los parámetros de más importancia como son los armónicos. Este dispositivo fué diseñado para tomar muestras de tensión y corriente en AC y por medio de una FFT (transformada rápida de Fourier) implementada en una FPGA SPARTAN 3E montada en un sistema de desarrollo SIE, determinar los 16 primeros armónicos presentes y mostrarlos por medio de columnas en un display LCD.


[edit] Armónicos:

Una función periódica de determinada frecuencia, está compuesta por un número de ondas senoidales de distintas frecuencias, las cuales son múltiplos de la frecuencia de alimentación. En una red eléctrica, los armónicos son producidos por cargas no lineales, es decir que sus impedancias no son constantes, como por ejemplo, circuitos rectificadores, reguladores de tensión, hornos de arco, transformadores, etcétera. Un armónico se define con los dos parámetros que mejor lo caracterizan:

  • Amplitud
  • Orden

El orden(n) es la frecuencia del armónico con respecto a la fundamental, por ejemplo un armónico de orden 3 tiene una frecuencia 3 veces superior a la fundamental.
n=fn/f1

[edit] Efectos de los armónicos en una red AC

Efectos instantáneos

  • Falla de interruptores automáticos por efecto di/dt.
  • Malfuncionamiento en relés y contactores.
  • Interferencias con sistemas de comunicaciones.
  • Fallas en los equipos electrónicos de control.

Efectos de largo plazo

  • Daño en condensadores ya que su impedancia decrece con el orden de los armónicos
  • Sobrecalentamiento en transformadores y motores de inducción

[edit] Transformada Rapida de Fourier (FFT)

Para poder realizar el calculo de los armónicos de la red es necesario procesar muestras tomadas de la red eléctrica para encontrar su espectro en frecuencia, ello se realiza a través de la transformada discreta de Fourier (DFT). La DFT se calcula sobre una secuencia de n muestras complejas de una señal de interés, en nuestro caso el suministro de corriente alterna domiciliario (120 AC~60Hz), que devolverá una secuencia de n números complejos a través de la expresión

Eq1 analizador.png

Este representa un cálculo, si bien simple, de mucha extensión: asumiendo que vamos a emplear un DFT de 16 muestras se harían necesarias 16 multiplicaciones y 16 sumas complejas por cada una de las 16 muestras!; en terminos computacionales requiere de n2 operaciones . Por ello para calcular el DFT se suelen emplear algoritmos que reducen la cantidad de pasos necesarios para encontrar la transformada, entre estos el FFT(fast fourier transform) que será empleado en el proyecto. Este algoritmo se vale de la subdivisión del DFT en cálculos más pequeños de a dos muestras, por lo que es necesario que el numero de muestras sea una potencia de 2. La subdivision se realiza con el llamado Diezmado en el tiempo (decimation in time), que subdivide inicialmente las n muestras en 2 grupos que contienen las muestras con coeficiente par uno y con coeficiente impar el otro; este mismo procedimiento se realiza una y otra vez hasta obtener grupos de solo dos muestras después se multiplican las muestras impares por

Eq3 analizador.png

y se realiza el siguiente calculo, al que se le llama mariposa porque como se ve,se hace una especie de cruce entre las muestras

Eq2 analizador.png

Una vez realizado este procedimiento sobre todos los pares de muestras, se van haciendo mariposas como se muestra en la figura

Mariposa analizador.jpg

Para terminar tenemos el resultado de nuestro FFT, pero en un orden no natural al que se denomina "bit reversed order", que consiste en que el indice de las muestras visto como numero binario esta en orden inverso, es decir, que si quisiéramos poner las muestras en orden natural deberiamos tomar el indice de cada una en binario y tomando el bit menos significativo como aquel que esta en la parte más a la izquierda de este indice, reorganizar las muestras.

Además de estas consideraciones teóricas sobre el FFT, debemos saber que esperar de su resultadoː El algoritmo retornara un numero igual de muestras que las de la entrada, separadas entre si a una distancia (frecuencia de muestreo)/(numero de muestras), pero la segunda mitad de las muestras de salida es un reflejo de las primeras 8 muestras de salida, en otras palabras, solo la mitad de los valores de salida son significativos. Para nuestro proyecto decidimos mostrar los primeros 8 armónicos para lo cual requerimos entonces de 16 muestras de la red tomadas a una frecuencia de 960Hz(aprox. una muestra cada 1,042 ms). En principio esta velocidad de muestreo puede parecer un poco alta, sin embargo, si tenemos en cuenta que la pantalla se refrescará cada 30ms aprox. tenemos mas que tiempo de sobra para tomar las 16 muestras y procesarlas antes de que cambien los datos de la pantalla.

El siguiente código representa una simulación realizada en Matlab de un modulo FFT con los mismos parámetros consignados arriba.

  1. Fs = 958;                    % Sampling frequency
  2. T = 1/Fs;                     % Sample time
  3. L = 16;                     % Length of signal
  4. t = (0:L-1)*T;                % Time vector
  5. % Sum of a 50 Hz sinusoid and a 120 Hz sinusoid
  6. x = sin(2*pi*60*t)*1.75+0.5*sin(2*pi*120*t)+3*sin(2*pi*480*t); 
  7. %y = x + 2*randn(size(t));     % Sinusoids plus noise
  8. stem(t(1:16),x(1:16))
  9. NFFT = 2^nextpow2(L); % Next power of 2 from length of y
  10. Y = fft(x,NFFT)/L;
  11. f = Fs/2*linspace(0,1,NFFT/2+1);
  12.  
  13. % Plot single-sided amplitude spectrum.
  14. stem(f,2*abs(Y(1:NFFT/2+1)))
Simulación realizada en matlab

[edit] Diseño del Circuito

[edit] Sección Análoga

Un Voltaje AC monofásico es atenuado en un factor de 10 por un divisor de voltaje implementado con 6 resistencias de precisión (tolerancia 1%), las cuales comprenden R5-R9, R11, (ver esquemático), las cuales reducen el voltaje de entrada máximo de 120VAC hasta un valor por debajo de 3.3V, el cual ingresa por la entrada no inversora del amplificador operacional U1B. En otro amplificador operacional (U1D), una señal DC de -1.7V es ajustada con el trimmer R46, para sumar una componente de DC a la señal del amplificador U1B, por medio de un amplificador sumador de ganancia unitaria U1C, con el fin de que la señal de salida del acondicioandor oscile entre 3.3V y 0V, todo esto para cumplir con los requerimientos de voltajes de entrada del ADC TLV1548, ubicado en la tarjeta de desarrollo SIE. Al mismo tiempo una resistencia shunt obtiene un voltaje equivalente a la corriente que está circulando en el circuito bajo prueba; este voltaje obtiene el mismo tratamiento descrito anteriormente.

[edit] Sección digital

La sección digital comprende un display LCD GPM940B0 marca Giantplus de 320x240 pixeles, con la circuiteria necesaria para generar las tensiones que son requeridas por la misma. Adicional a esto un buffer implementado con un circuito integrado 74HC244DW el cual es un dispositivo CMOS con una frecuencia de trabajo máxima de hasta 100 MHz.


Diagrama esquemático

Diagrama de bloques

[edit] Diseño de los módulos en la FPGA

El proyecto se centra en un procesador MIPS de 32 bits llamado PLASMA, un opencore escrito en VHDL por Steve Rhoads. Al procesador Plasma se le añadieron dos módulos periféricos, uno para el manejo de la LCD y el otro para el ADC y FFT.

[edit] Mapa de Memoria

Para los perifericos que se van a usar en el proyecto, se realizó un mapeo de la memoria de la siguiente manera:

  1. ifndef __PLASMA_H__
  2. define __PLASMA_H__
  3.  
  4. /*********** Hardware addesses ***********/
  5. define RAM_INTERNAL_BASE 0x00000000 //8KB
  6. define RAM_EXTERNAL_BASE 0x10000000 //1MB
  7. define RAM_EXTERNAL_SIZE 0x00100000
  8. define UART_BASE         0x20000000
  9. define UART_WRITE        0x20000000
  10. define UART_READ         0x20000000
  11. define UART_STATUS       0x20000010
  12. define IRQ_MASK          0x20000010
  13. define IRQ_STATUS        0x20000020
  14. define cs_lcd            0x20000030
  15. define cs_fft	         0x20000040
  16. define GPIOA_IN          0x20000050
  17. define COUNTER_REG       0x20000060
  18. define ETHERNET_REG      0x20000070
  19. define FLASH_BASE        0x30000000
  20.  
  21. /*********** GPIO out bits ***************/
  22. define ETHERNET_MDIO     0x00200000
  23. define ETHERNET_MDIO_WE  0x00400000
  24. define ETHERENT_MDC      0x00800000
  25. define ETHERNET_ENABLE   0x01000000
  26.  
  27. /*********** Interrupt bits **************/
  28. define IRQ_UART_READ_AVAILABLE  0x01
  29. define IRQ_UART_WRITE_AVAILABLE 0x02
  30. define IRQ_COUNTER18_NOT        0x04
  31. define IRQ_COUNTER18            0x08
  32. define IRQ_ETHERNET_RECEIVE     0x10
  33. define IRQ_ETHERNET_TRANSMIT    0x20
  34. define IRQ_GPIO31_NOT           0x40
  35. define IRQ_GPIO31               0x80
  36.  
  37. /*********** Ethernet buffers ************/
  38. define ETHERNET_RECEIVE  0x13ff0000
  39. define ETHERNET_TRANSMIT 0x13fe0000
  40.  
  41. endif //__PLASMA_H__

Este código corresponde al .h que se incluye como en los archivos ejecutables hechos en C. Vemos que al LCD (cs_lcd) se le ha asignado la dirección base 0x20000030 y al modulo FFT(cs_fft) 0x20000040.

Diagrama de perifericos

[edit] Periferico de manejo del display LCD:

Este proyecto cuenta con un display color LCD-TFT GPM940B0 Giantplus para la visualización de los armónicos. Dicho display cuenta con un bus de datos de 8 bits y 3 pines de configuración en modo serial. Para inicialzar el LCD se debe respetar la siguiente secuencia de encendido:

Init.jpg


[edit] Configuración del LCD

El display cuenta con 26 registros de configuración, de los cuales solo uno es de nuestro interés, ya que la configuración por default en el modo RGB 8 bits es suficiente para el tipo de imagen que se va a mostrar, este registro es el R05h:

Config1.jpg

al cual sólo se le modificará el bit STB (LSB) ya que cuando se pone en 1 enciende el display y genera las señales que son requeridas por el backlight. Las señales de configuración debe seguir el siguiente esquema:

Config2.jpg

El dato que se le enviará es el 0000010101011111b que corresponde al 55Fh, esto logrará el encendido del LCD completo. A la par de la configuración deben ir las señales de sincronización acorde al siguiente criterio:

Modo de funcionamiento 8 bit RGB:

Igual que la mayoría de displays, el área de la pantalla cuenta con zonas de front porch y back porch:

Screen1.jpg

Acorde a esto se deben generar unas señales de sincronización que deben seguir el siguiente esquema:


LCD1.jpeg


Los tiempos necesarios para mantener estas señales aparecen en la siguiente tabla:

Tabla1.jpeg

En la tabla anterior se aprecia que el back porch y el front porch cambia si se trata de una línea impar o una línea par. También hay que tener en cuenta que los datos deben enviarse en orden R-G-B si es una línea par o B-G-R si es una línea impar; en este proyecto esto no tiene importancia, ya que los datos serán blanco y negro, los cuales son todos "unos" o todos "ceros". Con base en estos datos se procede al diseño del módulo que debe generar tales señales.

[edit] Modulo de control LCD:

El modulo completo consta de 6 componentes, un módulo de control, un generador de sincronismos, divisor de frecuencia, un modulo de configuracion del LCD, un modulo para la secuencia de encendido y un modulo indice.

Diagrama general del modulo
Máquina de estados
RTL
RTL
Simulación 1
simulación 2

Funcionamiento El procesador PLASMA envia una señal de activación al modulo de control_LCD llamada CS_LCD, a través del decodificador de direcciones, la cual inicial el proceso activando el modulo de on-sequence, quien despues de 1ms habilita al modulo SYNC para iniciar las señales de sincronizacion Hsync y Vsync. Después de aproximadamente 15ms (ver inicializacion LCD) se habilita el modulo de configuracion del lcd, quien mediante una señales seriales configura y enciende el LCD, mientras al mismo tiempo el modulo SYNC continua con los sincronismos y evalua la posición del pixel para enviar el dato segun la ubicacion requerida.

Modulo on-sequence

Este modulo se encarga de producir un retardo de tiempo de 1ms para habilitar el modulo de generación de sincronismos y un retardo de 15ms para comenzar la configuración, siguiendo el diagrama de tiempos de encedido.

Máquina de estados
Diagrama de flujo

Modulo config_LCD Este modulo envia en forma serial, el dato de activación, escribiendo en el registro R05h de la LCD el reloj usado por este modulo proviene del divisor de frecuencia implementado en el modulo div_clk de aproximadamente 40khz.


Config lcd.jpg
Config LCD.png


Modulo SYNC Consta de tres partes principales: Generador de sincronismo horizontal, generador de sincronismo vertical y envio de datos. Durante el barrido horizontal se indaga la posición del pixel para determinar, mediante los wire c1, c2, c3, c4, c5, c6, c7, c8 el ancho de las columnas, es decir durante el tiempo que permanezca encendidos los wire, el modulo estará habilitado para el envio de datos, pero siempre y cuando el barrido vertical tambien se encuentre habilitado, esto lo hace comparando la posicion vertical con el dato ingresado desde el plasma, esto quiere decir que la altura de la columna depende del dato enviado desde el plasma. la altura de cada columna viene dada a traves de las entradas data_fft1, data_fft2,... y un registro index_abs que determina a cual columna corresponde cada dato.

Diagrama de flujo
SYNC.png


Modulo indice La función de este modulo es la de tomar el dato de entrada desde el plasma y organizarla para que el modulo SYNC la muestre. Desde el plasma proviene un dato de 18 bits organizados de la siguiente manera:

[rw][data_fft][index_abs]

donde:

[rw]= 1 bit, determina la habilitacion para la recepcion de datos.

[data_fft]= 13 bits contiene la magnitud del armonico y determina la altura de la columna.

[index_abs]= 4 bits contiene la informacion de a cual columna pertenece el data_fft.

el modulo toma el dato y la direccion y lo asigna a la salida correspondiente para el modulo SYNC.

Dia indice.jpg
Indice.png



[edit] Periferico FFT

Este periferico es el encargado de controlar al ADC, realizar la transformada FFT y calcular la magnitud de los datos obtenidos después de la transformación. De modo que el periferico agrupa varios sub-modulos que explicaremos más adelante, estos módulos son:

  1. Modulo ADC: Encargado de la comunicación y configuración con el ADC.
  2. Modulo RX: Encargado de recibir las muestras del ADC y enviarlas al siguiente modulo (Core_FFT) que realiza la transformada.
  3. Modulo Core_FFT: Realiza la transformada.
  4. Modulo Absoluto: Toma datos del Core_FFT y calcula la magnitud.

[edit] Modulo ADC

El ADC utilizado es el TLV1548 el cual es un dispositivo que genera un valor discreto de 10 bits, de una señal análoga, este modulo tiene salida de datos y configuración serial mediante el protocolo SPI. La siguiente figura muestra el diagrama de tiempos para la comunicación con el ADC

Time ADC.png

Como vemos, un ciclo completo del modulo toma 10us (configurado en transformación rapida). El ADC requiere un reloj de 8.2Mhz que es empleado para la comunicación SPI, como ya se dijo el tiempo de muestreo es de 10us y al inicio de cada ciclo se envían 10 pulsos del clock mencionado en los cuales se envían los registros de configuración del ADC, ademas en cada uno de estos pulsos se recibe cada uno de los bits que ha convertido el ADC (recibiendo el resultado de la configuración anterior). Dado que para la realización del FFT requerimos un tiempo de muestreo de 1,040ms debemos tomar un dato cada 105 ciclos del ADC, por tanto para obtener las 16 muestras requerimos de 1664 ciclos del ADC; Por ello el control del modulo esta configurado para que al obtener un pulso de "enable" obtenga las 16 muestras y espere hasta que los demás módulos del FFT terminen sus procedimientos, antes de tomas las siguientes 16 muestras. Su algoritmo de funcionamiento las maquinas de estados quedan entonces como se muestran en las siguientes figuras

ADC diagrama.jpg
ADC Es1.jpg
ADC ES2.jpg

El siguiente código genera las señales de sincronización para la comunicación con el ADC

  1. case(state)
  2. 			0:	begin
  3. 				if(cs & rw)
  4. 					begin
  5. 					state<=1;
  6. 					spi_rst<=0;
  7. 					end
  8. 				else
  9. 					begin
  10. 					ADC_CS<=1;
  11. 					spi_rst<=0;
  12. 					SPI_en<=0;
  13. 					state<=0;					
  14. 					end			  	
  15. 				end
  16. 			1:	begin
  17. 				if(n_muestra<1658)
  18. 					begin
  19. 					ADC_CS<=0;
  20. 					spi_rst<=0;
  21. 					SPI_en<=1;
  22. 					state<=2;
  23. 					end
  24. 				else
  25. 					begin
  26. 					ADC_CS<=1;
  27. 					spi_rst<=1;
  28. 					SPI_en<=0;
  29. 					state<=0;
  30. 					end
  31. 				end
  32. 			2:	begin
  33. 				SPI_en<=0;
  34. 				if(ADC_EOC & (tick_count>22))
  35. 					begin
  36. 					ADC_CS<=1;
  37. 					end
  38. 				if(!busy)
  39. 					begin
  40. 					state<=1;
  41. 					end
  42. 				end

El siguiente código controla los tiempos de sincronización de la comunicación SPI. La señal tick representa un cambio de flanco (positivo a negativo o negativo a positivo) del reloj generado para la comunicación SPI, a partir de esta señal se generan contadores que sirven para recibir los datos del ADC en el tiempo correcto(10 clocks del SPI o 22 ticks) y luego el momento en el que se acaba el ciclo de transformación del ADC (55 ticks). Ademas contando cada vez que tick_count==55, se lleva un registro del numero de muestras tomadas y un indice (fft_index) que indica al modulo RX en que posición debe guardar el dato que recibe.

  1. case (state_spi)
  2. 		0:	begin 
  3. 				if(SPI_en)
  4. 				begin
  5. 				clkdiv_en<=1;
  6. 				state_spi<=1;
  7. 				busy<=1; 
  8. 				TX_en_reg<=1;
  9. 				end 
  10. 			end
  11. 		1:	begin
  12. 				TX_en_reg<=0;
  13. 				if(tick)
  14. 					tick_count <= tick_count+1;
  15. 				else
  16. 				begin
  17. 					if(tick_count>55)
  18. 					begin
  19. 					state_spi<=0;
  20. 					tick_count<=0;
  21. 					clkdiv_en<=0;
  22. 					busy<=0;
  23. 						if(n_muestra<1659)
  24. 							begin
  25. 							if(cont<102)
  26. 								cont<=cont+1;
  27. 								else
  28. 								cont<=0;	
  29. 							n_muestra<=n_muestra+1;
  30. 							if(cont==101)
  31. 							fft_index<=fft_index+1;
  32. 							end
  33. 						else
  34. 							begin
  35. 							n_muestra<=0;
  36. 							fft_index<=0;
  37. 							cont<=0;
  38. 							end
  39. 					end
  40. 				end	
  41. 			end
  42. 		endcase

Finalmente tenemos los módulos que reciben los datos del ADC y envían los datos de configuración(inicio en modo rápido de muestreo 0x9 y elección del canal 1 ). TX_ADC envía la configuración; cuando el numero de muestras del ADC es 0 envía el comando 0x9 que inicializa el modulo y 0x1 que indica que estamos trabajando sobre el canal 1 del ADC.

  1. Transmisor TX (Envia la configuracion del ADC)
  2. 	always@(posedge clk)
  3. 	if(reset)
  4. 	begin
  5. 	in_buffer<=0;
  6. 	ADC_SDIN_buffer<=0;
  7. 	end
  8. 	else
  9. 	begin
  10. 		if(TX_en_reg && (n_muestra<1))
  11. 		begin
  12. 		in_buffer<=4'h9;
  13. 		if((!posedge_SCLK) & tick)
  14. 			begin
  15. 			ADC_SDIN_buffer <= in_buffer[3];
  16. 			in_buffer<=in_buffer<<1;
  17. 			end
  18. 		end
  19. 		else
  20. 		begin
  21. 		if(TX_en_reg)
  22. 		in_buffer<=4'h1;
  23. 			if((!posedge_SCLK) & tick)
  24. 			begin
  25. 			ADC_SDIN_buffer <= in_buffer[3];
  26. 			in_buffer<=in_buffer<<1;
  27. 			end
  28. 		end
  29. 	end	
  30.  
  31. 	assign	ADC_SDIN = ADC_SDIN_buffer;


  1. //Receptor RX_ADC (Recibe los datos del ADC)
  2. 	always@(posedge clk)
  3. 	begin
  4. 	if(cont==50)
  5. 	begin
  6. 	data_av<=1;
  7. 		if((!posedge_SCLK & tick) & (tick_count<21))
  8. 		begin
  9. 		out_buffer<=(out_buffer<<1);
  10. 		out_buffer[0]<=ADC_SDOUT;
  11. 		end
  12. 	end
  13. 	else
  14. 	data_av<=0;
  15. 	end
  16.  
  17. 	assign data_out = out_buffer;

Los resultados de la simulación se muestran en las siguientes imagenes que corroboran el correcto funcionamiento de los modulos.

ADC FFT.jpg
ADC2 FFT.jpg

El bloque final del modulo se muestra en la siguiente figura

Circuito RTL

[edit] Modulo RX

Este es un sencillo modulo que se encarga de guardar las 16 muestras tomadas por el ADC y iniciar la comunicación y transmisión hacia el Core_FFT para procesar los datos. La primera parte del código se muestra a continuación, aqui se guardan los datos provenientes del ADC en 16 registros, esta recepción se habilita cuando el modulo ADC envía una señal de dato valido (ADC_data_av) y cuando el ADC no esta ocupado, ademas debe esperar a que el modulo FFT general termine(core_busy). Cuando el dato guardado es el 16 se enciende un flag(done_reg) que habilita el envío de datos al Core.

  1.  
  1. if(ADC_data_av & !ADC_busy & !core_busy)
  2. 						case(data_index)
  3. 							0: in_data0<=in_data;
  4. 							1: in_data1<=in_data;
  5. 							2: in_data2<=in_data;
  6. 							3: in_data3<=in_data;
  7. 							4: in_data4<=in_data;
  8. 							5: in_data5<=in_data;
  9. 							6: in_data6<=in_data;
  10. 							7: in_data7<=in_data;
  11. 							8: in_data8<=in_data;
  12. 							9: in_data9<=in_data;
  13. 							10: in_data10<=in_data;
  14. 							11: in_data11<=in_data;
  15. 							12: in_data12<=in_data;
  16. 							13: in_data13<=in_data;
  17. 							14: in_data14<=in_data;
  18. 							15: 
  19. 								begin 
  20. 								in_data15<=in_data;
  21. 								done_reg<=1;
  22. 								state<=0; 
  23. 								end
  24. 							endcase

El siguiente código es el encargado de enviar al Core_FFT, para ello le envía un pulso de start, al cual el core responde con una señal llamada rfd que permanece prendida mientras el core recibe los datos, ademas el core envía un indice (rx_index) que indica la muestra que debe recibir, hay que notar que esta entrada de datos se da como un burst.

  1. 		case(state2)
  2. 			0:	begin
  3. 				fwd_inv<=0;
  4. 				fwd_inv_we<=0;
  5. 				if(done_reg)
  6. 				begin
  7. 				state2<=1;
  8. 				start_fft<=1;
  9. 				end
  10. 				end
  11. 			1:	begin	
  12. 					start_fft<=0;
  13. 					if(rfd)
  14. 					begin
  15. 						case(rx_index)
  16. 							0: out <= {1'b0,in_data0};
  17. 							1: out <= {1'b0,in_data1};
  18. 							2: out <= {1'b0,in_data2};
  19. 							3: out <= {1'b0,in_data3};
  20. 							4: out <= {1'b0,in_data4};
  21. 							5: out <= {1'b0,in_data5};
  22. 							6: out <= {1'b0,in_data6};
  23. 							7: out <= {1'b0,in_data7};
  24. 							8: out <= {1'b0,in_data8};
  25. 							9: out <= {1'b0,in_data9};
  26. 							10: out <= {1'b0,in_data10};
  27. 							11: out <= {1'b0,in_data11};
  28. 							12: out <= {1'b0,in_data12};
  29. 							13: out <= {1'b0,in_data13};
  30. 							14: out <= {1'b0,in_data14};
  31. 							15: 
  32. 								begin 
  33. 								out <= {1'b0,in_data15};
  34. 								if(abs_done==0)
  35. 									begin
  36. 									state2 <= 0;
  37. 									end
  38. 								flag_rx<=1;
  39. 								end
  40. 							endcase
  41. 					end
  42. 				end
  43. 		endcase

El diagrama de este modulo queda como se muestra en la siguiente imagen

RX rtl.png

[edit] Core FFT

Este modulo es el directamente encargado de transformar los datos tomados del ADC en el dominio del tiempo al dominio de la frecuencia. Dada la complejidad general del proyecto, se nos permitió el uso de un Core generado por Xilinx. Este tipo de implementación suele ser muy efectiva y eficiente pero nos cuesta el no tener control sobre los procesos internos del modulo, y ni siquiera podemos ver el modelo de comportamiento en VHDL, en cierta forma se trabaja con una caja negra; por otro lado hay que tener en cuenta que este tipo de Cores no pueden ser empleados en FPGAs de fabricantes diferentes a Xilinx, por lo cual la portabilidad se hace limitada. Las opciones elegidas para la generación del core fueron:

  • Entradas de 11 bits, ello porque el core recibe datos en complemento a 2 y como nuestros datos son enteros positivos de 10 bits, podemos simplemente usar los registros de ce
  • Se configuro en Radix-2_lite, que optimiza el core para emplear la menor cantidad de logica de la FPGA.
  • Se configuro sin Escalado de los datos, salida en orden natural y un pin que puede pausar toda la lógica del core al ser activado.
  • La memoria a emplear es Ram distribuida, que se eligió de esta manera para no chocar con las memorias de bloque empleadas por Plasma.

El bloque del Core se muestra en la siguiente imagen,

Circuito RTL generado

---Si algún lector requiere del uso de algún core, este debe instanciarse en el modulo más general, es decir , no intanciado en otro modulo que haga parte del general.---

[edit] Modulo Absolute

Este es el modulo final del periférico FFT, inicialmente saca los datos del Core y uno por uno calcula la magnitud, luego los pone en 8 registros(como se habia dicho, solo la mitad de los resultados del FFT sirven pues la otra mitad es un espejo). Estos registros son a los que puede acceder el procesador para enviar a la pantalla, para ello se dispone de un indice de entrada que sacara el dato requerido por el procesador.

El algoritmo empleado para el calculo de la magnitud, mas precisamente de la raiz cuadrada necesaria para calcularlo es el algoritmo babilonio que se muestra en el siguiente diagrama de flujo

Alg bab.jpg

Inicialmente asignamos un valor de una raiz conocida (en este caso una potencia de 2) como primera aproximación a la raíz, luego se le asigna a la raíz el valor (a/raíz+raíz)/2 donde a es el numero al que queremos sacarle la raiz, y volvemos a hacer este procedimiento matemático hasta que raíz= (a/raíz+raíz)/2, cuando se cumple esta condición hemos encontrado una aproximación relativamente buena.

El siguiente código es el encargado de sacar los datos del Core, para ello el modulo debe esperar a la señal de done del core, a la cual el modulo(Absolute) envía un pulso de unload hacia el core. Al recibir la señal de unload, el Core responde con una señal de "data valid" y empieza a sacar los datos en burst uno por cada señal del reloj. El modulo Absolute espera a el primer dato (parte real y parte imaginaria) y pausa la lógica del core con la señal CE mientras saca la magnitud. Se toman las partes real e imaginaria y se sigue al pie de la letra le algoritmo para la raíz mencionado anteriormente, cuando se halla la raíz el valor es almacenado en un registro para que el procesador pueda leerlos mas tarde.

  1. parameter raiz1=	 1'b0;//1
  2. parameter raiz2=	 1'b1;//2	
  3. parameter raiz3=	 3'b100;//4
  4. parameter raiz4=	 5'b10000;//8
  5. parameter raiz5=	 7'b1000000;//16
  6. parameter raiz6=	 9'b100000000;//32
  7. parameter raiz7=	11'b10000000000;//64
  8. parameter raiz8=	13'b1000000000000;//128
  9. parameter raiz9=	15'b100000000000000;//256
  10. parameter raiz10=	17'b10000000000000000;//512
  11. parameter raiz11=	19'b1000000000000000000;//1024
  12. parameter raiz12=	21'b100000000000000000000;//2048
  13. parameter raiz13=	23'b10000000000000000000000;//4096
  14.  
  15. reg [12:0]
  16. 		out_data_abs0,
  17. 		out_data_abs1,
  18. 		out_data_abs2,
  19. 		out_data_abs3,
  20. 		out_data_abs4,
  21. 		out_data_abs5,
  22. 		out_data_abs6,
  23. 		out_data_abs7;
  24.  
  25. //reg[22:0] abs2;
  26. reg[12:0] /*temp1=0,*/temp2;
  27. reg[4:0] state=0,state2=0,state3=0;
  28. reg flag_save=0,abs_flag=0;
  29. reg[15:0] in_real_reg,in_imag_reg;
  30.  
  31. always@(posedge clk)
  32. begin
  33. 	if(reset)
  34. 	begin
  35. 	FFT_CE<=1;
  36. 	temp2<=0;
  37. 	temp1<=0;
  38. 	state<=0;
  39. 	enable_div<=0;
  40. 	flag_save<=0;
  41. 	unload<=0;
  42. 	abs2<=0;
  43. 	in_real_reg<=0;
  44. 	in_imag_reg<=0;
  45. 	end
  46. 	else
  47. 	begin
  48. 		case(state)
  49. 		0:
  50. 			begin
  51. 				if(done_in)
  52. 				begin
  53. 				state<=1;
  54. 				unload<=1;
  55. 				end
  56. 			end
  57. 		1:
  58. 			begin
  59. 			unload<=0;
  60. 				if(data_valid)
  61. 				begin
  62. 				state<=2;
  63. 				end
  64. 			end	
  65. 		2:
  66. 			begin
  67. 				FFT_CE<=0;
  68. 				flag_save<=0;
  69. 				state<=3;
  70. 				if(in_real[15]==1)
  71. 					in_real_reg<=~in_real+1;
  72. 					else
  73. 					in_real_reg<=in_real;
  74. 				if(in_imag[15]==1)
  75. 					in_imag_reg<=~in_imag+1;
  76. 					else
  77. 					in_imag_reg<=in_imag;
  78. 			end	
  79. 		3:
  80. 			begin
  81. 			abs2<=(in_real_reg[13:2]*in_real_reg[13:2])+(in_imag_reg[13:2]*in_imag_reg[13:2]);
  82. 			state<=4;
  83. 			end
  84. 		4:
  85. 			begin
  86. 				if(abs2 > raiz13)
  87. 					begin
  88. 					temp1<=4096;
  89. 					end
  90. 				if(raiz12 < abs2 <= raiz13)
  91. 					begin
  92. 					temp1<=2048;
  93. 					end
  94. 				if(raiz11 < abs2 <= raiz12)
  95. 					begin
  96. 					temp1<=1024;
  97. 					end
  98. 				if(raiz10 < abs2 <= raiz11)
  99. 					begin
  100. 					temp1<=512;
  101. 					end
  102. 				if(raiz9 < abs2 <= raiz10)
  103. 					begin
  104. 					temp1<=256;
  105. 					end
  106. 				if(raiz8 < abs2 <= raiz9)
  107. 					begin
  108. 					temp1<=128;
  109. 					end
  110. 				if(raiz7 < abs2 <= raiz8)
  111. 					begin
  112. 					temp1<=64;
  113. 					end
  114. 				if(raiz6 < abs2 <= raiz7)
  115. 					begin
  116. 					temp1<=32;
  117. 					end
  118. 				if(raiz5 < abs2 <= raiz6)
  119. 					begin
  120. 					temp1<=16;
  121. 					end
  122. 				if(raiz4 < abs2 <= raiz5)
  123. 					begin
  124. 					temp1<=8;
  125. 					end
  126. 				if(raiz3 < abs2 <= raiz4)
  127. 					begin
  128. 					temp1<=4;
  129. 					end
  130. 				if(raiz2 < abs2 <= raiz3)
  131. 					begin
  132. 					temp1<=2;
  133. 				end
  134. 				if(/*raiz1 <*/abs2 <= raiz2)
  135. 					begin
  136. 					temp1<=1;
  137. 					end
  138. 				state<=5;	
  139. 			end
  140. 			5:
  141. 				begin
  142. 					enable_div<=1;
  143. 					if(done_div)
  144. 						begin
  145. 						state<=6;
  146. 						enable_div<=0;
  147. 						end
  148. 				end
  149. 			6:
  150. 				begin
  151. 					temp2<=(div+temp1)/2;
  152. 					state<=7;
  153. 					res_div<=1;
  154. 				end
  155. 			7:
  156. 				begin
  157. 					res_div<=0;
  158. 					if(temp2 == temp1)
  159. 						begin
  160. 							if(in_index < 10)
  161. 								begin
  162. 								flag_save<=1;
  163. 								FFT_CE<=1;
  164. 								state<=2;
  165. 								end
  166. 							else
  167. 								begin
  168. 								state<=0;
  169. 								FFT_CE<=1;
  170. 								end
  171. 						end
  172. 					else
  173. 						begin
  174. 						temp1<=temp2;
  175. 						state<=5;
  176. 						end
  177. 				end
  178. 		endcase	
  179. 	end
  180. end
  181.  
  182. always@(posedge clk)
  183. begin
  184. 	if(reset)
  185. 	begin
  186. 		out_data_abs0<=0;
  187. 		out_data_abs1<=0;
  188. 		out_data_abs2<=0;
  189. 		out_data_abs3<=0;
  190. 		out_data_abs4<=0;
  191. 		out_data_abs5<=0;
  192. 		out_data_abs6<=0;
  193. 		out_data_abs7<=0;
  194. 		state2<=0;
  195. 		abs_flag<=0;
  196. 	end
  197. 	else
  198. 	begin
  199. 		case(state2)
  200. 		0:
  201. 			begin
  202. 			abs_flag<=0;
  203. 			if(done_in)
  204. 			state2<=1;
  205. 			end
  206. 		1:
  207. 			begin
  208. 			if(flag_save)
  209. 				begin
  210. 					case(in_index-2)
  211. 					0: out_data_abs0 <= temp2;
  212. 					1: out_data_abs1 <= temp2;
  213. 					2: out_data_abs2 <= temp2;
  214. 					3: out_data_abs3 <= temp2;
  215. 					4: out_data_abs4 <= temp2;
  216. 					5: out_data_abs5 <= temp2;
  217. 					6: out_data_abs6 <= temp2;
  218. 					7: 	
  219. 						begin 
  220. 						out_data_abs7 <= temp2;
  221. 						state2 <= 0;
  222. 						abs_flag<=1;
  223. 						end
  224. 					endcase	
  225. 				end
  226. 			end
  227. 		endcase	
  228. 	end
  229. end

Una vez tomados todos los datos, se envia al procesador una señal llamada abs_done, que sera mapeada en el bus de datos despues de el dato sacado por este modulo, queda entonces el bit 13 de cpu_data_r para abs_done y del 12 al 0 la magnitud de salida del modulo Absolute. Cabe notar que abs_done permanecerá prendida mientras el procesador no pida el ultimo dato disponible (el 8, ademas a partir de este punto se empiezan a tomar de nuevo datos en el ADC y los subsiguientes procedimientos). El siguiente código pode un dato en el bus de salida dependiendo de la señal enviada por el procesador.

  1.   case(state3)
  2. 		0:
  3. 			begin
  4. 				if(abs_flag)
  5. 				begin
  6. 				state3<=1;
  7. 				abs_done<=1;
  8. 				end
  9. 			end
  10. 		1:
  11. 			begin
  12. 			if(enable & rd)
  13. 				begin
  14. 				case(data_index)
  15. 				0: out_abs <= out_data_abs0;
  16. 				1: out_abs <= out_data_abs1;
  17. 				2: out_abs <= out_data_abs2;
  18. 				3: out_abs <= out_data_abs3;
  19. 				4: out_abs <= out_data_abs4;
  20. 				5: out_abs <= out_data_abs5;
  21. 				6: out_abs <= out_data_abs6;
  22. 				7: 	
  23. 						begin 
  24. 						out_abs <= out_data_abs7;
  25. 						state3 <= 0;
  26. 						abs_done<=0;
  27. 						end
  28. 				endcase	
  29. 				end
  30. 			end
  31. 		endcase	
  32. 	end

El bloque de este modulo se muestra en la siguiente imagen

Abs rtl.png

El modulo completo (FFT) se presenta en la imagen siguiente

FFT general.png

[edit] Tareas Software

Las tareas software en nuestro proyecto se encargan de inicializar los periféricos FFT y LCD, y ademas los comunica transfiriendo los datos obtenidos del FFT hacia la pantalla, esto se realiza siempre que el modulo FFT tenga la condición abs_done encendida, para luego volver a tomar nuevas muestras. El siguiente codigo representa estas tareas: con MemRead inicializamos los periféricos , leemos abs_done para luego enviar con un "for loop" el dato al LCD, para ello se hace primero un MemoryWrite al FFT especificando el indice del valor (a traves de la variable i) y luego escribimos el LCD (se ve una trama coformada por el dato y ciertas posiciones que usamos en el bus de datos como señales de control del LCD(necesarias para que reciba los datos)).

  1. #include "plasma.h"
  2. #define MemoryRead(A) (*(volatile unsigned long*)(A))
  3. #define MemoryWrite(A,V) *(volatile unsigned long*)(A)=(V)
  4.  
  5.  
  6. int main(void)
  7. {
  8.   short i=0;
  9.   unsigned int done;
  10.   MemoryRead(cs_lcd);
  11.   while(1)
  12.   {
  13.   MemoryWrite(cs_fft,0x10);
  14.   //done=(MemoryRead(cs_fft));  
  15.   while(((MemoryRead(cs_fft)) & 0x2000) && 0x2000)  
  16.   {
  17.   for(i=0;i<=7;i++) 
  18.    {
  19.     MemoryWrite(cs_fft,0x20+i); 
  20.     MemoryWrite(cs_lcd, (0x20000 | (1<<17)| /*(0x3E8+(0x3E8*i)*/((MemoryRead(cs_fft) & 0x1FFF))<<4|i));
  21.     }
  22.     i=0; 
  23.    }
  24.   }
  25.   return 0;
  26. }


[edit] Plano de la PCB

AC2-Cobre.jpg
AC2-Comp.jpg
pcb lado componentes
pcb lado inferior
circuito completo conectado al SIE
IMG0083A.jpg
Prueba analizador1.JPG
Prueba analizador2.jpg


Semana Fecha Actividad
S1 3-8 de ago Definición del proyecto
S2 10-15 de ago Investigación
S3 17-22 de ago Establecimiento de las especificaciones
S4 25-29 de ago Diseño del circuito de adquisición
S5 31 ago-5 sept Diseño de la PCB
S6 7-12 de sept Diseño del algoritmo del FFT
S7 14-19de sept Fabricación de la PCB del circuito de adquisición
S8 28 sept - 3 de oct Pruebas y ajustes de PCB, programación hardware y software
S9 5-10 de oct Programación módulo VGA
S10 12-17 de oct Programación módulo VGA
S11 19-24 de oct Programación y pruebas modulo VGA
S12 26-31 de oct Programación FFT
S13 2-7 de nov Pruebas del algoritmo FFT
S14 9-14 de nov Integración del sistema completo
S15 16-21 de nov Pruebas y ajustes
S16 23-28 de nov Entrega proyecto final


Costos relacionados al proyecto
Elemento Descripción Costo total una unidad Costo total 100 und
Componentes Resistencias, condensadores, circuitos integrados, conectores $72000 $6500000
Circuito impreso Doble cara, silkscreen $120000 $2000000
Sie Board qi-hardware project $200000 $19000000
Diseño $8000000
Impuestos iva, fletes $50000 $5500000
Total $442000 $41000000
Personal tools
Namespaces
Variants
Actions
Navigation
interactive
Toolbox
Print/export