Analizador de Redes AC
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
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
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
Una vez realizado este procedimiento sobre todos los pares de muestras, se van haciendo mariposas como se muestra en la figura
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.
Fs = 958; % Sampling frequency
T = 1/Fs; % Sample time
L = 16; % Length of signal
t = (0:L-1)*T; % Time vector
% Sum of a 50 Hz sinusoid and a 120 Hz sinusoid
x = sin(2*pi*60*t)*1.75+0.5*sin(2*pi*120*t)+3*sin(2*pi*480*t);
%y = x + 2*randn(size(t)); % Sinusoids plus noise
stem(t(1:16),x(1:16))
NFFT = 2^nextpow2(L); % Next power of 2 from length of y
Y = fft(x,NFFT)/L;
f = Fs/2*linspace(0,1,NFFT/2+1);
% Plot single-sided amplitude spectrum.
stem(f,2*abs(Y(1:NFFT/2+1)))
[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.
[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:
ifndef __PLASMA_H__
define __PLASMA_H__
/*********** Hardware addesses ***********/define RAM_INTERNAL_BASE 0x00000000 //8KB
define RAM_EXTERNAL_BASE 0x10000000 //1MB
define RAM_EXTERNAL_SIZE 0x00100000define UART_BASE 0x20000000define UART_WRITE 0x20000000define UART_READ 0x20000000define UART_STATUS 0x20000010define IRQ_MASK 0x20000010define IRQ_STATUS 0x20000020define cs_lcd 0x20000030define cs_fft 0x20000040define GPIOA_IN 0x20000050define COUNTER_REG 0x20000060define ETHERNET_REG 0x20000070define FLASH_BASE 0x30000000/*********** GPIO out bits ***************/define ETHERNET_MDIO 0x00200000define ETHERNET_MDIO_WE 0x00400000define ETHERENT_MDC 0x00800000define ETHERNET_ENABLE 0x01000000/*********** Interrupt bits **************/define IRQ_UART_READ_AVAILABLE 0x01define IRQ_UART_WRITE_AVAILABLE 0x02define IRQ_COUNTER18_NOT 0x04define IRQ_COUNTER18 0x08define IRQ_ETHERNET_RECEIVE 0x10define IRQ_ETHERNET_TRANSMIT 0x20define IRQ_GPIO31_NOT 0x40define IRQ_GPIO31 0x80/*********** Ethernet buffers ************/define ETHERNET_RECEIVE 0x13ff0000define ETHERNET_TRANSMIT 0x13fe0000endif //__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.
[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:
[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:
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:
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:
Acorde a esto se deben generar unas señales de sincronización que deben seguir el siguiente esquema:
Los tiempos necesarios para mantener estas señales aparecen en la siguiente tabla:
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.
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.
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.
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.
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.
[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:
- Modulo ADC: Encargado de la comunicación y configuración con el ADC.
- Modulo RX: Encargado de recibir las muestras del ADC y enviarlas al siguiente modulo (Core_FFT) que realiza la transformada.
- Modulo Core_FFT: Realiza la transformada.
- 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
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
El siguiente código genera las señales de sincronización para la comunicación con el ADC
case(state)
0: begin
if(cs & rw)
beginstate<=1;
spi_rst<=0;
endelsebeginADC_CS<=1;
spi_rst<=0;
SPI_en<=0;
state<=0;
endend1: begin
if(n_muestra<1658)
beginADC_CS<=0;
spi_rst<=0;
SPI_en<=1;
state<=2;
endelsebeginADC_CS<=1;
spi_rst<=1;
SPI_en<=0;
state<=0;
endend2: begin
SPI_en<=0;
if(ADC_EOC & (tick_count>22))
beginADC_CS<=1;
endif(!busy)
beginstate<=1;
endend
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.
case (state_spi)
0: begin
if(SPI_en)
beginclkdiv_en<=1;
state_spi<=1;
busy<=1;
TX_en_reg<=1;
endend1: begin
TX_en_reg<=0;
if(tick)
tick_count <= tick_count+1;
elsebeginif(tick_count>55)
beginstate_spi<=0;
tick_count<=0;
clkdiv_en<=0;
busy<=0;
if(n_muestra<1659)
beginif(cont<102)
cont<=cont+1;
elsecont<=0;
n_muestra<=n_muestra+1;
if(cont==101)
fft_index<=fft_index+1;
endelsebeginn_muestra<=0;
fft_index<=0;
cont<=0;
endendendendendcase
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.
Transmisor TX (Envia la configuracion del ADC)
always@(posedge clk)
if(reset)
beginin_buffer<=0;
ADC_SDIN_buffer<=0;
endelsebeginif(TX_en_reg && (n_muestra<1))
beginin_buffer<=4'h9;
if((!posedge_SCLK) & tick)
beginADC_SDIN_buffer <= in_buffer[3];
in_buffer<=in_buffer<<1;
endendelsebeginif(TX_en_reg)
in_buffer<=4'h1;
if((!posedge_SCLK) & tick)
beginADC_SDIN_buffer <= in_buffer[3];
in_buffer<=in_buffer<<1;
endendendassign ADC_SDIN = ADC_SDIN_buffer;
//Receptor RX_ADC (Recibe los datos del ADC)always@(posedge clk)
beginif(cont==50)
begindata_av<=1;
if((!posedge_SCLK & tick) & (tick_count<21))
beginout_buffer<=(out_buffer<<1);
out_buffer[0]<=ADC_SDOUT;
endendelsedata_av<=0;
endassign data_out = out_buffer;
Los resultados de la simulación se muestran en las siguientes imagenes que corroboran el correcto funcionamiento de los modulos.
El bloque final del modulo se muestra en la siguiente figura
[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.
if(ADC_data_av & !ADC_busy & !core_busy)
case(data_index)
0: in_data0<=in_data;
1: in_data1<=in_data;
2: in_data2<=in_data;
3: in_data3<=in_data;
4: in_data4<=in_data;
5: in_data5<=in_data;
6: in_data6<=in_data;
7: in_data7<=in_data;
8: in_data8<=in_data;
9: in_data9<=in_data;
10: in_data10<=in_data;
11: in_data11<=in_data;
12: in_data12<=in_data;
13: in_data13<=in_data;
14: in_data14<=in_data;
15:
beginin_data15<=in_data;
done_reg<=1;
state<=0;
endendcase
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.
case(state2)
0: begin
fwd_inv<=0;
fwd_inv_we<=0;
if(done_reg)
beginstate2<=1;
start_fft<=1;
endend1: begin
start_fft<=0;
if(rfd)
begincase(rx_index)
0: out <= {1'b0,in_data0};
1: out <= {1'b0,in_data1};
2: out <= {1'b0,in_data2};
3: out <= {1'b0,in_data3};
4: out <= {1'b0,in_data4};
5: out <= {1'b0,in_data5};
6: out <= {1'b0,in_data6};
7: out <= {1'b0,in_data7};
8: out <= {1'b0,in_data8};
9: out <= {1'b0,in_data9};
10: out <= {1'b0,in_data10};
11: out <= {1'b0,in_data11};
12: out <= {1'b0,in_data12};
13: out <= {1'b0,in_data13};
14: out <= {1'b0,in_data14};
15:
beginout <= {1'b0,in_data15};
if(abs_done==0)
beginstate2 <= 0;
endflag_rx<=1;
endendcaseendendendcase
El diagrama de este modulo queda como se muestra en la siguiente imagen
[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,
---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
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.
parameter raiz1= 1'b0;//1
parameter raiz2= 1'b1;//2
parameter raiz3= 3'b100;//4
parameter raiz4= 5'b10000;//8
parameter raiz5= 7'b1000000;//16
parameter raiz6= 9'b100000000;//32
parameter raiz7= 11'b10000000000;//64
parameter raiz8= 13'b1000000000000;//128
parameter raiz9= 15'b100000000000000;//256
parameter raiz10= 17'b10000000000000000;//512
parameter raiz11= 19'b1000000000000000000;//1024
parameter raiz12= 21'b100000000000000000000;//2048
parameter raiz13= 23'b10000000000000000000000;//4096
reg [12:0]
out_data_abs0,out_data_abs1,out_data_abs2,out_data_abs3,out_data_abs4,out_data_abs5,out_data_abs6,out_data_abs7;//reg[22:0] abs2;reg[12:0] /*temp1=0,*/temp2;
reg[4:0] state=0,state2=0,state3=0;
reg flag_save=0,abs_flag=0;
reg[15:0] in_real_reg,in_imag_reg;
always@(posedge clk)
beginif(reset)
beginFFT_CE<=1;
temp2<=0;
temp1<=0;
state<=0;
enable_div<=0;
flag_save<=0;
unload<=0;
abs2<=0;
in_real_reg<=0;
in_imag_reg<=0;
endelsebegincase(state)
0:
beginif(done_in)
beginstate<=1;
unload<=1;
endend1:
beginunload<=0;
if(data_valid)
beginstate<=2;
endend2:
beginFFT_CE<=0;
flag_save<=0;
state<=3;
if(in_real[15]==1)
in_real_reg<=~in_real+1;
elsein_real_reg<=in_real;
if(in_imag[15]==1)
in_imag_reg<=~in_imag+1;
elsein_imag_reg<=in_imag;
end3:
beginabs2<=(in_real_reg[13:2]*in_real_reg[13:2])+(in_imag_reg[13:2]*in_imag_reg[13:2]);
state<=4;
end4:
beginif(abs2 > raiz13)
begintemp1<=4096;
endif(raiz12 < abs2 <= raiz13)
begintemp1<=2048;
endif(raiz11 < abs2 <= raiz12)
begintemp1<=1024;
endif(raiz10 < abs2 <= raiz11)
begintemp1<=512;
endif(raiz9 < abs2 <= raiz10)
begintemp1<=256;
endif(raiz8 < abs2 <= raiz9)
begintemp1<=128;
endif(raiz7 < abs2 <= raiz8)
begintemp1<=64;
endif(raiz6 < abs2 <= raiz7)
begintemp1<=32;
endif(raiz5 < abs2 <= raiz6)
begintemp1<=16;
endif(raiz4 < abs2 <= raiz5)
begintemp1<=8;
endif(raiz3 < abs2 <= raiz4)
begintemp1<=4;
endif(raiz2 < abs2 <= raiz3)
begintemp1<=2;
endif(/*raiz1 <*/abs2 <= raiz2)
begintemp1<=1;
endstate<=5;
end5:
beginenable_div<=1;
if(done_div)
beginstate<=6;
enable_div<=0;
endend6:
begintemp2<=(div+temp1)/2;
state<=7;
res_div<=1;
end7:
beginres_div<=0;
if(temp2 == temp1)
beginif(in_index < 10)
beginflag_save<=1;
FFT_CE<=1;
state<=2;
endelsebeginstate<=0;
FFT_CE<=1;
endendelsebegintemp1<=temp2;
state<=5;
endendendcaseendendalways@(posedge clk)
beginif(reset)
beginout_data_abs0<=0;
out_data_abs1<=0;
out_data_abs2<=0;
out_data_abs3<=0;
out_data_abs4<=0;
out_data_abs5<=0;
out_data_abs6<=0;
out_data_abs7<=0;
state2<=0;
abs_flag<=0;
endelsebegincase(state2)
0:
beginabs_flag<=0;
if(done_in)
state2<=1;
end1:
beginif(flag_save)
begincase(in_index-2)
0: out_data_abs0 <= temp2;
1: out_data_abs1 <= temp2;
2: out_data_abs2 <= temp2;
3: out_data_abs3 <= temp2;
4: out_data_abs4 <= temp2;
5: out_data_abs5 <= temp2;
6: out_data_abs6 <= temp2;
7:
beginout_data_abs7 <= temp2;
state2 <= 0;
abs_flag<=1;
endendcaseendendendcaseendend
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.
case(state3)
0:
beginif(abs_flag)
beginstate3<=1;
abs_done<=1;
endend1:
beginif(enable & rd)
begincase(data_index)
0: out_abs <= out_data_abs0;
1: out_abs <= out_data_abs1;
2: out_abs <= out_data_abs2;
3: out_abs <= out_data_abs3;
4: out_abs <= out_data_abs4;
5: out_abs <= out_data_abs5;
6: out_abs <= out_data_abs6;
7:
beginout_abs <= out_data_abs7;
state3 <= 0;
abs_done<=0;
endendcaseendendendcaseend
El bloque de este modulo se muestra en la siguiente imagen
El modulo completo (FFT) se presenta en la imagen siguiente
[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)).
#include "plasma.h"
#define MemoryRead(A) (*(volatile unsigned long*)(A))
#define MemoryWrite(A,V) *(volatile unsigned long*)(A)=(V)
int main(void)
{short i=0;
unsigned int done;MemoryRead(cs_lcd);
while(1)
{MemoryWrite(cs_fft,0x10);
//done=(MemoryRead(cs_fft));while(((MemoryRead(cs_fft)) & 0x2000) && 0x2000)
{for(i=0;i<=7;i++)
{MemoryWrite(cs_fft,0x20+i);
MemoryWrite(cs_lcd, (0x20000 | (1<<17)| /*(0x3E8+(0x3E8*i)*/((MemoryRead(cs_fft) & 0x1FFF))<<4|i));
}i=0;
}}return 0;
}
[edit] Plano de la PCB
| 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 |















