Analizador Lógico
Universidad Nacional de Colombia
PROYECTO FINAL SISTEMAS EMBEBIDOS
|
[edit] Calificación
Porcentaje | Presentación 20% | Informe 30% | Funcionamiento 50% |
---|---|---|---|
1 Entrega 10% | 0.5 | 0.5 | 0.5 |
2 Entrega 30% | 1 | 1 | 1 |
3 Entrega 30% | 1 | 1 | 1
|
4 Entrega 30% | 3 | 3 | 3 |
[edit] Introducción
Este proyecto de sistemas embebidos consiste en desarrollar un analizador lógico, el cual es un instrumento que se utiliza para la medición de un gran número de señales digitales, utilizando la tarjeta SIE la cual es una adaptación del Ben Nanonote utilizada para desarrollar diferentes aplicaciones, ya sea de pequeña, mediana o alta complejidad debido a que posee integrada una FPGA Spartan3E y un procesador JZ4725, con el cual se pueden desarrollar diferentes aplicaciones multimedia, ya que cuenta con un nucleo incorporado XBurst CPU que permite alta integración, alto desempeño y bajo consumo energético, lo cual es ideal para sistemas embebidos. Ademas esta tarjeta también cuenta con una memoria SDRAM de 32MB y una NAND de 2GB las cuales permiten la inicialización del sistema.
[edit] Descripción del proyecto
Un analizador lógico es un instrumento que se usa para mediciones de señales digitales ya que su función principal es la detección de niveles altos o bajos de voltaje de un conjunto grande de señales, lo cual lo diferencia del osciloscopio porque este se utiliza para la visualización de señales de propósito general, además en su gran mayoria la disponibilidad de canales es de dos mientras que en el analizador se pueden implementar un número mucho mayor de canales.
El primer paso a seguir es entender la arquitectura y el funcionamiento completo de la tarjeta SIE para poder inicializarla de una manera adecuada, con el objetivo de integrar el analizador lógico mediante una tarjeta hija. Una vez hecha la adhesión del analizador se proseguirá con el envio de información adquirida por medio de las sondas puestas en cualquier circuito digital , luego se representará en una pantalla LCD las señales captadas. Para diferenciar las diferentes señales, se designaran diferentes colores a cada una de ellas de forma aleatoria, es decir, el canal 1 siempre será representado con el color amarillo, el 2 con el color rojo y asi sucesivamente hasta completar todos los canales de adquisición.
Como las señales son captadas en tiempo real, es de esperarse que cualquier cambio de tensión del circuito se vea representada inmediatamente cuando esta suceda. Es decir que el analizador lógico podrá medir tiempos entre cambios de nivel, número de estados lógicos, retrasos en la señal, trasitorios y sobre-picos. Con esto se tendrá la capacidad de verificar y depurar señales digitales, al la vez que detectar errores en estado transitorio.
[edit] Especificaciones del proyecto
Este analizador lógico presenta las siguientes características:
- Tendrá adquisición de 16 canales, los cuales serán presentados de a grupos de a 4 en el LCD, donde se tendrá la posibilidad de intercambiar grupos para la visualización del resto de las señales, esto controlado por un interruptor.
- La señal de máxima frecuencia que se puede medir es de 25MHz, para lo cual será necesario utilizar el reloj (CLK) de la FPGA, adquiriendo datos tanto en el flanco de subida como en el de bajada.
- Circuito de protección implementado por medio de 16-bit buffer de 3.3V de alimentación, con velocidad de respuesta del orden de nanosegundos, permitiendo que estos reacciones a una frecuencia alrededor de 100MHz.
- El modo de disparo se implementará con un edge trigger o disparo por pendiente.
- La resolución de la pantalla LCD será de 320 x 240 pixels.
- El analizador lógico está limitado a medir las señales de un solo circuito digital independiente, dado que las tierras deben ser comunes, para los 16 canales.
- La FPGA se encargará de las tareas hardware, ya que esta cumplirá la función de adquicisión/almacenamiento de los datos. La memoria RAM distribuida de este dispositivo es de 73K, mientras que la RAM de bloque es de 360K.
- Como entrada adicional contará con un botón para cambiar los canales en gupos de a 4, un botón de desplazamiento a la izquierda y otro a la derecha.
[edit] Descripción Hardware
Principalmente se utilizará la placa SIE, la cual nos permite realizar todo tipo de proyectos digitales, ya que cuenta con un microprocesador jz4725 de ingenic, 32MB de memoria SDRAM, 2GB de memoria NAND, una FPGA XC3S500E, pantalla LCD y varios GPIO's. Adicionalmente se desarrollará una tarjeta complementaria que optimizará la aplicación del analizador lógico.
Componente HW del proyecto
- Tarjeta SIE: SoCs, Memorias, Periféricos
- LCD
- Tarjeta Hija
- FPGA
[edit] Tareas Hardware
Para el manejo de la compoente software serán encesarias las siguientes tareas:
- Adquisición de datos
- Visualización en el LCD, refrescamiento y muestra de datos
- Almacenamiento en memoria
[edit] Descripción Software
Como herramientas software se utilizarán las cadenas de herramientas GNU, librerías, sistema operativo Linux, formato de archivos ELF, Makefile, creación de aplicaciones mediante Qt y Depuración (GDB). A continuación se describirá algunos de los paquetes de diseño a utilizar:
- GNU KiCAD: Con esta herramienta lo que se pretende es diseñar todos los circuitos electronicos que serán utilizados en el desarrollo del analizador lógico, entre los que se encuentra el PCB de la tarjeta hija desde la cual se enviarán las señales que se captarán y procesarán con la tarjeta SIE.
- Xilinx ISE: Ésta es la principal herramenta de programación de hardware utilziada hasta ahora y con ella lo que se pretende es poder programar la SIE para que capte las señales que provienen desde la tarjeta hija y las presente en la pantalla LCD.
- Qt Creator: Es un entorno de desarrollo integrado (IDE) que le proporciona herramientas para diseñar y desarrollar aplicaciones complejas para escritorios y plataformas móviles.
[edit] Tareas Software
Las tareas software que se manejarán en el proyecto son:
- Control de los datos adquiridos
- Manejo de la memoria
- Control de la visualización de los datos (Canales mostrados, tiempo de representación)
- Procesamiento de datos
- Medición de los parámetros importantes (estados lógicos, tiempo de subida, tiempo de bajada)
[edit] Tarjeta Hija
[edit] Esquemático y PCB
A continuación se presenta el esquematico de la tarjeta hija, la cual debe realizar el trabajo de acomple de impedancias y seguridad de la tarjeta SIE.
Para la adquisición de los datos se utilizará unas puntas de prueba que estarán conectadas al adaptador de la tarjeta hija. Como se mencionó anteriormente, para aislar la plataforma SIE del bloque de adquisición, se utilizará un buffer no-inversor 3-estados de 16 bits 74ALVCH162244, el cual permite controlar el flujo de señales ya que cuenta con cuatro señales ~OE (Ouput Enable), que cuando se encuentra en un nivle lógico ALTO, la salida asume un estado de alta impedancia. El voltaje de alimentación de este chip es de 3.3V y el tiempo de propagación de nAn a nYn es de 3,5 ns en condiciones normales.
La distribución de los pines es de la siguiente manera:
Número de Pin | Símbolo | Nombre y función |
---|---|---|
1 | 1 ~OE | Output Enable input (Active LOW) |
2, 3, 5, 6 | 1Y0 to 1Y3 | Data Outputs |
4, 10, 15, 21, 28, 34, 39, 45 | GND | Ground (0V) |
7, 18, 31, 42 | Vcc | Positive supply voltage |
8, 9, 11, 12 | 2Y0 to 2Y3 | Data outputs |
13, 14, 16, 17 | 3Y0 to 3Y3 | Data Outputs |
19, 20, 22, 23 | 4Y0 to 4Y3 | Data Outputs |
24 | 4 ~OE | Output Enable input (Active LOW) |
25 | 3 ~OE | Output Enable input (Active LOW) |
30, 29, 27, 26 | 4A0 to 4A3 | Data inputs |
36, 35, 33, 32 | 3A0 to 3A3 | Data Inputs |
41, 40, 38, 37 | 2A0 to 2A3 | Data Inputs |
47, 46, 44, 43 | 1A0 to 1A3 | Data Inputs |
48 | 2 ~OE | Output Enable input(Active LOW) |
[edit] Arquitectura del Analizador Lógico
En esta sección será presentada la arquitectura básica del proyecto en el que se demarcan las comunicaciones que se presentaran entre la FPGA y el procesador. Tal como se indica en la gráfica, se reciben todas las señales digitales mediante un arreglo de 4 bits que son representadas por medio de 1A, 2A,3A y 4A y dichas señales entrarán a el chip 74ALVCH162244, que es un buffer con el cual serán controlados los niveles lógicos de entrada al dispositivo. Cuando dichas señales salgan del chip, lo harán de la misma forma en la que ingresaron a él, es decir mediante arreglos de 4 bits, para de esa manera llegar a la RAM de la FPGA en la cual se almacenan los datos, cumpliendose de esta manera la función de escritura, para posteriormente llegar a las señales de control de los datos como lo son WritePulse Generator con la cual se accede a los datos almancenados, el Address decoder con la cual se le asigna un espacio de memoria especifico a los datos de entrada y estas dos ultimas señales han sido sincronizadas de tal forma que puedan llegar al procesador como instrucciones sincronicas. Adicionalmente, se establecen señales de enable para especificar cual es el momento en el que se empeiza la toma de datos y se asignan las funciones a los pulsadores.
[edit] Manejo de Memoria
En este moduló se controla el almacenamiento de los datos (El encargado directo de la memoria es el procesador), siendo simultáneos los procesos de lectura y de escritura. La memoria con la cual se va a trabajar en este proyecto sera la memoria RAM en bloque de la FPGA XC3S500E, la cual consta de 20 bloques de dicha memoria RAM doble puerto cuya capacidad de cada bloque es de 18 Kbits. Dado que se necesita realizar las tareas de lectura y escritura simultáneamente, la memoria debe tener la siguiente configuración.
Como se puede apreciar en la figura de arriba, se cuenta con las señales WE, EN, CLK, y SSR que cumplen las funciones de control de memoria. A continuación se presenta una tabla que describe la función de cada una de las señales:
Descripción de la señal | Nombre de la señal Puerto A | Nombre de la señal Puerto B | Tipo de señal | Función |
---|---|---|---|---|
Bus de direcciones | ADDRA | ADDRB | Entrada | El bus de direcciones selecciona una espacio de memoria para realizar las funciones de lectura y/o escritura. El ancho del puerto asociado al camino de datos determina el número de lineas de direcciones. Dichas funciones dependen de los habilitadores ENA y ENB ya que al realizar la transmisión de datos se deben tener en cuenta los tiempos setup y hold con respecto a CLKA y CLKB y deben tenerse en cuenta sin importar si se esta leyendo o escribiendo. |
Bus de entrada de datos | DIA | DIB | Entrada | Esta señal se encarga de escribir los datos provenientes del sistema analizado en la RAM cuando el Bus de direcciones establece su dirección en la memoria en cada flanco de subido del CLK y cuando el EN y WE esten activados. |
Entrada de dato de paridad | DIPA | DIPB | Entrada | La entrada de paridad repesenta un conjunto de bits adicionales incluidos en el camino de entrada de datos. No son de gran utilidad a la hora de revisar y generar la paridad de los bits de entrada y se colocan como datos adicionales del bus de direcciones. EL numero de bits de paridad 'p' estan incluidas en el dato de entrada. |
Salida de dato de paridad | DOPA | DOPB | Salida | El dato es escrito en el dus de direcciones de salida desde el espacio de memoria RAM especificado desde el bus de direcciones, ADDR. Configura el ancho del puerto de datos de salida con la description de la señal de datos de entrada. |
Write Enable | WEA | WEB | Entrada | Cuando se activa al mismo tiempo que el EN, esta entrada permite escribir los datos en la memoria RAM. Cuando esta deshabilitada, se habilita la función de lectura. Cuando este es el caso, un latch pasa datos desde el espacio de memoria hacia las salidas DO. |
Clock Enable | ENA | ENB | Entrada | Cuando se activa, esta entrada habilita la señal CLK para hacer operaciones de lectura y escritura en el bloque de memoria RAM. Cuando esta inactiva, el bloque de RAM ni escribe ni lee |
Set/Reset | SSRA | SSRB | Entrada | Cuando se activa, este pin hace que la salida DO tome el valor del atributo SRVAL. Se sincroniza con la señal CLK. |
Clock | CLKA | CLKB | Entrada | Esta entrada recibe la señal de reloj para sincronizar las operaciones de lectura y escritura. |
[edit] Definiciones de atributos de Memoria RAM
Un bloque de memoria tiene una serie de atributos que controlan su comportamiento.
Función | Atributo | Posibles Valores |
---|---|---|
Inicializar contenido de la memoria de datos, cargados durante la configuración | INITxx (INIT_00 hasta INIT3F) | Cada cadena de inicialización define 32 valores hexadecimales de 16384 bits de la memoria de datos del bloque de RAM. |
Inicializar contenido para el bit de paridad, cargados durante la configuración | INITPxx (INITP_00 hasta INITP0F) | Cada cadena de inicializacion define 32 valores hexadecimales de los 2048 bits de paridad de la memoria de datos de los bloque RAM. |
Latch de inicializacion de la salida de datos | INITA, INITB (dual-port) | Valor hexadecimal con el ancho del puerto seleccionado. |
Latch de sincronismo para la salida de datos | SRVAL_A, SRVAL_B (dual-port) | Valor hexadecimal con el ancho del puerto seleccionado. |
Latch de comportamiento de escritura de la salida de datos | WRITE_MODE | WRITE_FIRST, READ_FIRST, NO_CHANGE |
[edit] Operaciones de datos
Escribir datos y acceder a ellos desde el bloque de RAM es una operación síncrona que se realiza sin importar el puerto de cada bloque. La operación de lectura se realiza de la siguiente manera:
Tal y como se observa en esta gráfica, la operación de escritura para un solo puerto cumple con la descripción que se hizo anteriormente en la cual se escribe siempre y cuando la señal EN y la señal WE se encuentran en un nivel lógico 1. Por otro lado, para la lectura, se debe tener la señal EN activa y WE con un 0 lógico. Debido a que se necesita para el analizador lógico una memoria de doble puerto, se seleccionará el puerto A como de escritura y el puerto B como de lectura ya que cada una cuenta con un bus de direcciones que funcionan de forma independiente.
Instanciación de la memoria:
//***********************************************************************************************
// Memory
//***********************************************************************************************
always @ (wd or CS)
WdRam = wd && (CS == TRIG_TRIG);
RAMB16_S9_S9 RAM1 (
.DOA(dout[7:0]), // Port A 8-bit Data Output
.DOB(), // Port B 8-bit Data Output
.DOPA(), // Port A 1-bit Parity Output
.DOPB(), // Port B 1-bit Parity Output
.ADDRA(WdCntr), // Port A 11-bit Address Input
.ADDRB(RdCntr[10:0]), // Port B 11-bit Address Input
.CLKA(~clk), // Port A Clock
.CLKB(~clk), // Port B Clock
.DIA(Ireg[7:0]),// Port A 8-bit Data Input
.DIB(8'b0), // Port B 8-bit Data Input
.DIPA(1'b0), // Port A 1-bit parity Input
.DIPB(1'b0), // Port-B 1-bit parity Input
.ENA(1'b1), // Port A RAM Enable Input
.ENB(1'b1), // Port B RAM Enable Input
.SSRA(1'b0), // Port A Synchronous Set/Reset Input
.SSRB(1'b0), // Port B Synchronous Set/Reset Input
.WEA(WdRam), // Port A Write Enable Input
.WEB(1'b0) // Port B Write Enable Input
);
RAMB16_S9_S9 RAM2 (
.DOA(dout[15:8]), // Port A 8-bit Data Output
.DOB(), // Port B 8-bit Data Output
.DOPA(), // Port A 1-bit Parity Output
.DOPB(), // Port B 1-bit Parity Output
.ADDRA(WdCntr), // Port A 11-bit Address Input
.ADDRB(RdCntr[10:0]), // Port B 11-bit Address Input
.CLKA(~clk), // Port A Clock
.CLKB(~clk), // Port B Clock
.DIA(Ireg[15:8]),// Port A 8-bit Data Input
.DIB(8'b0), // Port B 8-bit Data Input
.DIPA(1'b0), // Port A 1-bit parity Input
.DIPB(1'b0), // Port-B 1-bit parity Input
.ENA(1'b1), // Port A RAM Enable Input
.ENB(1'b1), // Port B RAM Enable Input
.SSRA(1'b0), // Port A Synchronous Set/Reset Input
.SSRB(1'b0), // Port B Synchronous Set/Reset Input
.WEA(WdRam), // Port A Write Enable Input
.WEB(1'b0) // Port B Write Enable Input
);
[edit] Diagrama De Bloques
[edit] Modulo Trigger
El trabajo principal de este modulo es el de controlar la adquisición de los datos a la memoria. Como se puede observar en el código a continuación, se utilizó un modulo de comparación realmente sencillo, el cual simplemente compara el dato de entrada con el trigger dado por el usuario con el fin de obtener la señal de control que será utilizada en la maquina de control mostrado anteriormente.
Debido a que la plataforma con la que se cuenta no posee los suficientes números de entradas digitales para facilitarle al usuario el poder escoger el trigger de una manera sencilla, se opto por realizar una maquina de estados con dos entradas de control trigger, tal y como se muestra al principio del código.
A continuacion se presenta un diagrama de bloques del trigger en el que se puede observar como es el funcionamiento de la maquina de estados que lo conforman. Es necesario anotar que se ha omitido la senal de reset dado que es un estado adicional controlado por un pulsador y no interfiere en el nivel logico del trigger.
parameter [1:0] //synopsys enum STATE_TYPE
TRIG_IDLE = 2'b00, //do nothing
TRIG_ARMED = 2'b01, //arm trigger mechanism
TRIG_TRIG = 2'b10; //record data
//----------------------------------------------------------
// Trigger compare logic
//----------------------------------------------------------
TrigCompare T0 (.A(Ireg[ 0]),.B(treg[ 0]),.T(TCout[ 0]));
TrigCompare T1 (.A(Ireg[ 1]),.B(treg[ 1]),.T(TCout[ 1]));
TrigCompare T2 (.A(Ireg[ 2]),.B(treg[ 2]),.T(TCout[ 2]));
TrigCompare T3 (.A(Ireg[ 3]),.B(treg[ 3]),.T(TCout[ 3]));
TrigCompare T4 (.A(Ireg[ 4]),.B(treg[ 4]),.T(TCout[ 4]));
TrigCompare T5 (.A(Ireg[ 5]),.B(treg[ 5]),.T(TCout[ 5]));
TrigCompare T6 (.A(Ireg[ 6]),.B(treg[ 6]),.T(TCout[ 6]));
TrigCompare T7 (.A(Ireg[ 7]),.B(treg[ 7]),.T(TCout[ 7]));
TrigCompare T8 (.A(Ireg[ 8]),.B(treg[ 8]),.T(TCout[ 8]));
TrigCompare T9 (.A(Ireg[ 9]),.B(treg[ 9]),.T(TCout[ 9]));
TrigCompare T10(.A(Ireg[10]),.B(treg[10]),.T(TCout[10]));
TrigCompare T11(.A(Ireg[11]),.B(treg[11]),.T(TCout[11]));
TrigCompare T12(.A(Ireg[12]),.B(treg[12]),.T(TCout[12]));
TrigCompare T13(.A(Ireg[13]),.B(treg[13]),.T(TCout[13]));
TrigCompare T14(.A(Ireg[14]),.B(treg[14]),.T(TCout[14]));
TrigCompare T15(.A(Ireg[15]),.B(treg[15]),.T(TCout[15]));
always @ (TCout)
TC = TCout[15] & TCout[14] & TCout[13] & TCout[12] &
TCout[11] & TCout[10] & TCout[9] & TCout[8] & TCout[7] &
TCout[6] & TCout[5] & TCout[4] & TCout[3] & TCout[2] & TCout[1] & TCout[0];
//----------------------------------------------------------------
// Trigger State machine
//----------------------------------------------------------------
always @ (posedge clk or posedge reset)
begin
if(reset)
CS <= 0; //reset triggered status
else
CS <= NS; //goto next state
end
always @(CS or Done or TC or arm)
begin
case (CS)
TRIG_IDLE: begin
if(arm)
begin
NS = TRIG_ARMED; //arm system?
ClearCntr = 1; //clear address counters
clearready = 1; //put into not ready state
setready = 0;
end
else
begin
NS = TRIG_IDLE;
ClearCntr = 0;
clearready = 0;
setready = 0;
end
end
TRIG_ARMED: begin
if(TC)
begin
NS = TRIG_TRIG; //triggered...record
ClearCntr = 0;
clearready = 0;
setready = 0;
end
else
begin
ClearCntr = 0;
NS = TRIG_ARMED;
clearready = 0;
setready = 0;
end
end
TRIG_TRIG: begin
if(Done)
begin
NS = TRIG_IDLE; //done, stop
ClearCntr = 0;
clearready = 0;
setready = 1; //put into ready state
end
else
begin
NS = TRIG_TRIG;
ClearCntr = 0;
clearready = 0;
setready = 0;
end
end
default:
begin
NS = TRIG_IDLE;
ClearCntr = 0;
clearready = 1;
setready = 0;
end
endcase
end
//-------------------------------------------------
//Trigger compare
//-------------------------------------------------
module TrigCompare(A,B,T);
input A; //data input
input B; //tirgger input
output T; //output
reg T;
always @ (A or B)
begin
if(A == B)
T = 1; //triggered
else
T = 0; //not triggered
end
endmodule
[edit] Banco de Registros de Configuración
Para la configuración de las funciones de escritura y lectura del periférico de adquisición se establecieron los siguientes registros y funciones:
// parameter:
// clk.............system clock
// reset...........system reset
// data_in.............this is the data to record
// enable..........causes the data to be recorded
// data............output data port
// arm.............arm trigger unit
// rd..............read data from recording
// ready...........indicates that memory is full
// trigger.........trigger word
//-----------------------------------------------------------
input clk;
input reset;
input [15:0] data_in;
input [12:0] addr;
reg enable=0;
output reg [7:0] rdBus;
reg [7:0] data=0;
reg arm=0;
reg rd=0;
input [7:0] wrBus;
output ready;
reg [15:0] trigger;
//writes data to trigger registers
reg wdtrig=0;
reg [15:0] treg=0; //contains bit 0 of trigger for each bit (polarity)
reg [15:0] Ireg=0; //data input reg
reg [8:0] WdCntr=0; //address counter for writing data
reg [10:0] RdCntr=0; //address counter for reading data
wire [15:0] dout; //data output register
reg [1:0] CS=0; //state indicates that the system was triggered
reg [1:0] NS=0;
reg Done=0; //indicates memory is full
reg TC=0;
reg ClearCntr=0; //clear the address counters
wire [15:0] TCout;
reg wd,WdRam=0;
reg ready,setready,clearready=0;
input we,cs;
//Write control
always @(negedge clk)
begin
if(reset)
{enable, arm,rd,wdtrig,trigger} <= 0;
else if(we & cs) begin
case (addr)
0: begin trigger[7:0] <= wrBus; end
1: begin trigger[15:8] <= wrBus; end
2: begin enable <= wrBus[0];
arm <= wrBus[1];
rd <= wrBus[2];
wdtrig <= wrBus[3];
end
default: begin enable <= 1; end
endcase
end
else
begin
{enable, arm,rd,wdtrig,trigger} <= 0;
end
end
//Read control
always @(posedge clk)
if(reset)
{rdBus} <= 0;
else begin
case (addr)
0: begin rdBus <= trigger[7:0]; end
1: begin rdBus <= trigger[15:8]; end
2: begin rdBus <= {enable, arm,rd,wdtrig};end
default: begin rdBus <= data; end
endcase
end
[edit] Interfaz con el Usuario utilizando QT
La interfaz pensada para el analizador consta de la pantalla lcd en la que seran mostrados los canales a partir de las decisiones que tome el usuario por medio de los 8 botones laterales, los cuales controlaran una lista de opciones que estara presente en la parte derecha de la pantalla, permitiendole al usuario controlar el tipo de visualizacion, la referencia del triger, la frecuencia de captura y la posición.
Lo primero que hay que hay que hacer es agregar en el encabezado csv.h los encabezados necesarios para que QT pueda leer y escribir de la memoria RAM
#ifndef MYQTAPP_H #define MYQTAPP_H #include "ui_csv.h" #include "jz47xx_mmap.h" #include "jz47xx_gpio.h" #define CS2_PORT JZ_GPIO_PORT_B #define CS2_PIN 26 class csv : public QWidget, private Ui::find_inout { Q_OBJECT public: csv(); public slots://funciones void principal(); void graf(); void change(); JZ_REG *initFPGA_RAM(); private: JZ_REG *RAM; }; #endif
Posteriormente se modifican la clase csv y la función cpp de tal manera que se grafiquen los 16 canales, se introduzcan los bits de control necesarios para el periférico y se puedan hacer las transiciones entre los diferentes QspinBox necesarios para realizar las elecciones de las opciones y la parte de visualización de la siguietne manera:
#include <QtGui> #include <QFile> #include <QString> #include <QStringList> #include <QList> #include <QTextStream> #include "csv.h" #include <iostream> #include <stdio.h> #include <QImage> #include <QPainter> #include <QtGui> // if we include <QtGui> there is no need to include every class used: <QString>, <QFileDialog>,... QImage imagen; int N; csv::csv() { setupUi(this); // this sets up GUI printf ("Acaba de iniciar el proceso"); RAM=initFPGA_RAM(); N=2000; spinBox1->setRange(1,16); spinBox2->setRange(1,16); spinBox3->setRange(0,25000000); spinBox3->setSingleStep(5000); connect(spinBox1, SIGNAL(valueChanged(int)),this,SLOT(graf()));//Esto es lo importante para graficar connect(spinBox3, SIGNAL(valueChanged(int)),this,SLOT(change()));//Esto es lo importante para cambiar las propiedades del QsinBox3 } void csv::principal() { spinBox3->setValue(1000); printf ("Comienza el proceso: Grafica sin nada"); graf(); } void csv::graf(){ int c,t,f; c=spinBox1->value();c=c-1;//lee canal guado los primeros 4 bits para eso t=spinBox2->value();//lee trigger guardo los demas 4 bits f=spinBox3->value();//lee frecuencia dejamos 20 posiciones para la frecuencia printf ("Se tomaron los valores de canal, trigger y frecuencia"); //Comienzo a escribir en la memoria //Reservados para trigger switch(t) { case 0:{RAM[10]=0 ;RAM[11]=0 ;RAM[12]=0 ;RAM[13]=0;} break; case 1:{RAM[10]=0;RAM[11]=0;RAM[12]=0;RAM[13]=1;} break; case 2:{RAM[10]=0;RAM[11]=0;RAM[12]=1;RAM[13]=0;} break; case 3:{RAM[10]=0;RAM[11]=0;RAM[12]=1;RAM[13]=1;} break; case 4:{RAM[10]=0;RAM[11]=1;RAM[12]=0;RAM[13]=0;} break; case 5:{RAM[10]=0;RAM[11]=1;RAM[12]=0;RAM[13]=1;} break; case 6:{RAM[10]=0;RAM[11]=1;RAM[12]=1;RAM[13]=0;} break; case 7:{RAM[10]=0;RAM[11]=1;RAM[12]=1;RAM[13]=1;} break; case 8:{RAM[10]=1;RAM[11]=0;RAM[12]=0;RAM[13]=0;} break; case 9:{RAM[10]=1;RAM[11]=0;RAM[12]=0;RAM[13]=1;} break; case 10:{RAM[10]=1;RAM[11]=0;RAM[12]=1;RAM[13]=0;} break; case 11:{RAM[10]=1;RAM[11]=0;RAM[12]=1;RAM[13]=1;} break; case 12:{RAM[10]=1;RAM[11]=1;RAM[12]=0;RAM[13]=0;} break; case 13:{RAM[10]=1;RAM[11]=1;RAM[12]=0;RAM[13]=1;} break; case 14:{RAM[10]=1;RAM[11]=1;RAM[12]=1;RAM[13]=0;} break; case 15:{RAM[10]=1;RAM[11]=1;RAM[12]=1;RAM[13]=1;} break; default:break; } printf ("Se guardaron los bits de control del trigger"); //Reservados para frecuencia switch(f) { case 1000:{RAM[14]=0 ;RAM[15]=0 ;RAM[16]=0 ;RAM[17]=0;} break; case 5000:{RAM[14]=0;RAM[15]=0;RAM[16]=0;RAM[17]=1;} break; case 10000:{RAM[14]=0;RAM[15]=0;RAM[16]=1;RAM[17]=0;} break; case 50000:{RAM[14]=0;RAM[15]=0;RAM[16]=1;RAM[17]=1;} break; case 100000:{RAM[14]=0;RAM[15]=1;RAM[16]=0;RAM[17]=0;} break; case 500000:{RAM[14]=0;RAM[15]=1;RAM[16]=0;RAM[17]=1;} break; case 1000000:{RAM[14]=0;RAM[15]=1;RAM[16]=1;RAM[17]=0;} break; case 5000000:{RAM[14]=0;RAM[15]=1;RAM[16]=1;RAM[17]=1;} break; case 10000000:{RAM[14]=1;RAM[15]=0;RAM[16]=0;RAM[17]=0;} break; case 15000000:{RAM[14]=1;RAM[15]=0;RAM[16]=0;RAM[17]=1;} break; case 20000000:{RAM[14]=1;RAM[15]=0;RAM[16]=1;RAM[17]=0;} break; case 25000000:{RAM[14]=1;RAM[15]=0;RAM[16]=1;RAM[17]=1;} break; default:break; } printf ("Se guardaron los bits de control de las frecuencias"); RAM[18]=1;//Este es el bit de start int ramaux = RAM[18]; int value[N*16]; printf ("Se va a comenzar a leer la memoria"); //lee de la memoria if(ramaux==1 && RAM[18]==0) {//Es necesario cambiar el star de 1 a 0 cuando se termine de escribir en la memoria para comenzar a leer for (int i4=0;i4<N*16;i4++){ value[i4]=RAM[i4]; } } imagen=QImage("fondo.png"); QPainter painter(&imagen); painter.setFont(QFont("Arial", 14)); int dx=20,dy=20,n=0,aux1=0,aux2=0; textEdit->clear(); painter.eraseRect(0,0,640,400); painter.setPen(Qt::blue); textEdit->textCursor().insertImage(imagen); //label->setPixmap(QPixmap("fondo.png")); painter.drawText(100,10,"Analizador Logico"); painter.setFont(QFont("Arial", 12)); painter.drawText(10,30,"Canal 1"); painter.drawText(10,50,"Canal 2"); painter.drawText(10,70,"Canal 3"); painter.drawText(10,90,"Canal 4"); painter.drawText(10,110,"Canal 5"); painter.drawText(10,130,"Canal 6"); painter.drawText(10,150,"Canal 7"); painter.drawText(10,170,"Canal 8"); painter.drawText(10,190,"Canal 9"); painter.drawText(10,210,"Canal 10"); painter.drawText(10,230,"Canal 11"); painter.drawText(10,250,"Canal 12"); painter.drawText(10,270,"Canal 13"); painter.drawText(10,290,"Canal 14"); painter.drawText(10,310,"Canal 15"); painter.drawText(10,330,"Canal 16"); painter.setPen(Qt::green); for(int i2=0;i2<N;i2++)//Dibujo la cuadricula { painter.drawLine(70+(dx*(i2)),15,70+(dx*(i2)),335);//Dibuja las lineas verticales for(int i3=0;i3<=16;i3++) { painter.drawLine(70,15+(i3*20),640,15+(i3*20));//Dibuja las lineas horizontales } } painter.setPen(Qt::black); //Dibujo ceros y unos para cualquier canal for(int i4=0;i4<c+1;i4++) { int c2=i4; n=0; for(int i=0;i<N;i++)//Las primeras 9 posiciones del canal 1 me indican los bits de control { aux1=value[i+N*c2];//Para las otras muestras: value[i+N*c2] con N tamano de cada muestra aux2=value[i+1+N*c2];//Para las otras muestras: value[i+1+N*c2] con N tamano de cada muestra if (aux1==0){ painter.drawLine(70+(dx*n),30+(c2*dy),70+(dx*(n+1)),30+(c2*dy));}//Dibuja un 0 if (aux1==1){ painter.drawLine(70+(dx*n),20+(c2*dy),70+(dx*(n+1)),20+(c2*dy));}//Dibuja un 1 if (aux1!=aux2){ painter.drawLine(70+(dx*(n+1)),30+(c2*dy),70+(dx*(n+1)),20+(c2*dy));}//Dibuja una linea vertical n=n+1; } } //textEdit->verticalScrollBar()->setValue(50); //textEdit->horizontalScrollBar()->setValue(50); } void csv::change(){ if (spinBox3->value()==0){ spinBox3->setValue(1000); } if (spinBox3->value()==6000){ spinBox3->setValue(5000); } if (spinBox3->value()==60000){ spinBox3->setValue(50000); } if (spinBox3->value()==600000){ spinBox3->setValue(500000); } if (spinBox3->value()==6000000){ spinBox3->setValue(5000000); } else if (spinBox3->value()<10000){ spinBox3->setSingleStep(5000); } else if (spinBox3->value()<100000){ spinBox3->setSingleStep(50000); } else if (spinBox3->value()<1000000){ spinBox3->setSingleStep(500000); } else if (spinBox3->value()<25000000){ spinBox3->setSingleStep(5000000); } } JZ_REG * csv::initFPGA_RAM() { JZ_PIO *pio; JZ_REG *virt_addr; // pio = jz_gpio_map (CS2_PORT); // jz_gpio_as_func (pio, CS2_PIN, 0); virt_addr = (JZ_REG *) (jz_mmap(0x13010000) + 0x18); if (*virt_addr != 0x0FFF7700) { *virt_addr = 0x0FFF7700; printf ("ADC: Configuring CS2 8 bits and 0 WS: %08X\n", *virt_addr); } else printf ("ADC: CS2, already configured: %08X\n", *virt_addr); virt_addr = (JZ_REG *) jz_mmap (0x14000000); return virt_addr; }
[edit] Driver
Un device driver es un programa que le permite al sistema operativo interactuar con un periférico, proporcionando una interfaz para utilizarlo. Para la realización de este driver fue necesario modificar el driver del irq y el de un teclado de una consola de juegos , con el fin de implementar los botones que controlan las funciones principales del analizador.
#include <linux/module.h> #include <linux/init.h> #include <linux/input.h> #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/kernel.h> /* Needed for KERN_INFO */ #include <linux/ioport.h> #include <linux/device.h> #include <linux/irq.h> /* We want an interrupt */ #include <linux/platform_device.h> #include <linux/fs.h> #include <asm/uaccess.h> #include <asm/io.h> #include <linux/gpio.h> #include <asm/mach-jz4740/gpio.h> #define FPGA_IRQ_PIN JZ_GPIO_PORTC(15) #define FPGA_BASE 0x15000000 #define BASE 0xB5000000 MODULE_AUTHOR(" "); MODULE_DESCRIPTION("Botones Analizador Lógico keyboard driver"); MODULE_LICENSE("GPL"); unsigned int irq; static unsigned char atakbd_keycode[0x5] = { /* American layout */ [0] = KEY_UP,[1] = KEY_TAB,[2] = KEY_LEFT,[3] = KEY_RIGHT,[4] = KEY_DOWN, }; static struct input_dev *atakbd_dev; static irqreturn_t irq_handler(int irq, void *dev_id)//función que toma las interrupciones y define las acciones a seguir { unsigned char opciones=0; opciones=inb(BASE); printk("Se oprimio la tecla\n"); switch(opciones){ case 0: input_report_key(atakbd_dev,atakbd_keycode[0],1); input_report_key(atakbd_dev,atakbd_keycode[0],0); break; case 1: input_report_key(atakbd_dev,atakbd_keycode[1],1); input_report_key(atakbd_dev,atakbd_keycode[1],0); break; case 4: input_report_key(atakbd_dev,atakbd_keycode[2],1); input_report_key(atakbd_dev,atakbd_keycode[2],0); break; case 8: input_report_key(atakbd_dev,atakbd_keycode[3],1); input_report_key(atakbd_dev,atakbd_keycode[3],0); break; case 16: input_report_key(atakbd_dev,atakbd_keycode[4],1); input_report_key(atakbd_dev,atakbd_keycode[4],0); break; default: input_report_key(atakbd_dev,0,1); input_report_key(atakbd_dev,0,0); break; } input_sync(atakbd_dev); return IRQ_HANDLED; } static int __init atakbd_init(void) { int i, error,res; atakbd_dev = input_allocate_device(); if (!atakbd_dev) return -ENOMEM; atakbd_dev->name = "Analizador Keyboard"; atakbd_dev->phys = "analizador/input0"; atakbd_dev->id.bustype = BUS_HOST; atakbd_dev->id.vendor = 0x0001; atakbd_dev->id.product = 0x0001; atakbd_dev->id.version = 0x0100; atakbd_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); atakbd_dev->keycode = atakbd_keycode; atakbd_dev->keycodesize = sizeof(unsigned char); atakbd_dev->keycodemax = ARRAY_SIZE(atakbd_keycode); for (i = 0; i < 0x6; i++) { set_bit(atakbd_keycode[i], atakbd_dev->keybit); } /* error check-->indica al sistema el nuevo dispositivo de entrada */ error = input_register_device(atakbd_dev); if (error) { input_free_device(atakbd_dev); return error; } irq = gpio_to_irq(FPGA_IRQ_PIN);//asigna el numero del irq al pin fisico del procesador res = request_irq(irq, irq_handler, IRQF_DISABLED | IRQF_TRIGGER_RISING, "FPGA - IRQ", NULL); // IRQF_TRIGGER_FALLING --> asignar el valor del irq y la funcion que lo utiliza return 0; } static void __exit atakbd_exit(void) { input_unregister_device(atakbd_dev); } module_init(atakbd_init); module_exit(atakbd_exit);
[edit] Análisis Económico
Los costos asociados al proyecto son los siguientes:
Se trabajaron 2 horas diarias a lo largo de las 14 semanas del semestre, con lo que por cada uno de los integrantes se tiene en total 140, lo que suma finalmente 420 horas de trabajo las cuales son avaluadas en $40.000 pesos cada una, para un costo total de mano de obra de $16'800.000.
En cuanto a los materiales utilizados y demás derivados se tienen:
- SIE ---------------> $200.000 pesos.
- Tarjeta hija----------------> $182.000 pesos.
- Sondas, resistencias y conectores -----------------------------------> $100.000 pesos.
- Servicios públicos $20.000 pesos mes -> $80.000 pesos
- Total prototipo -------------------------------------------> $562.000 pesos.
[edit] Cronograma de Actividades
A continuación se presenta el cronograma de actividades a desarrollar a lo largo del semestre:
Semana | Actividad a realizar |
---|---|
4 | Presentación especificaciones proyecto, diagrama de bloques y PCB. |
5 | Pruebas PCB de la tarjeta hija (circuito de protección) |
6 | Diseño comunicación entre tarjeta hija y SIE |
7 | Desarrollo de drivers en Linux |
8 | Segunda entrega proyecto |
9 | Diseño interfaz hardware/software |
10 | Diseño interfaz hardware/software |
11 | Prueba de funcionalidad básica de la tarjeta |
12 | Tercera entrega proyecto |
13 | Pruebas finales modulo memorias y adquisición |
14 | Pruebas finales modulo control |
15 | Pruebas finales modulo vizualización |
16 | Entrega final |
[edit] Bibliografía
[1] "The XYZs of Logic Analyzers", Nota de aplicación de Tektronix.2001.
[2] "Feeling Confortable with Logic Analyzers”, Nota de aplicación 1337. Agilent Technologies.
[3] "ANALIZADORES LÓGICOS", lección 9.