Este es uno de los ejercicios que hicimos durante el curso, trasladado a una placa FPGA mucho más barata que las de Celoxica que usamos allí. Un generador de pantalla VGA con un pequeño sprite (un fantasmita del Pacman) dando botes de un lado a otro. Recuerdo que era muuuy fácil modificar el código y hacer que el sprite fuera el doble o el cuádruple de grande, o en general, hacer que cada uno de sus píxeles se ampliara en un factor de una potencia de 2. Código fuente incluido.
Código: Seleccionar todo
/* Ejemplo de uso de la salida VGA de la tarjeta BASYS en Handel-C
El programa es una adaptación de uno de los ejercicios propuestos
en el curso de Procesado de Imagen con FPGA.
(C)2010 Miguel Angel Rodriguez Jodar.
Departamento de Arquitectura y Tecnologia de Computadores.
Universidad de Sevilla. */
#define ClockPin "P54"
#define ClockRate 25
#define VGARedPins {"P67","P68","P70"}
#define VGAGreenPins {"P50","P51","P52"}
#define VGABluePins {"P43","P44"}
#define VGASyncHPins {"P39"}
#define VGASyncVPins {"P35"}
set clock=external ClockPin with { rate=ClockRate, standard="LVCMOS33" };
#define TSH 800
#define TSV 520
#define TPWH 95
#define TPWV 2
#define TFPH 16
#define TFPV 10
#define TBPH 48
#define TBPV 29
#define TDISPH 639
#define TDISPV 480
static unsigned 10 contador_x=0;
static unsigned 10 contador_y=0;
static unsigned 1 HS=1;
static unsigned 1 VS=1;
unsigned 1 enable_video;
rom unsigned 24 Sprite [16*16] = { /* fantasmita del PacMan */
0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,
0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,
0x00FF00,0x00FF00,0x00FF00,0x00FF00,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0x00FF00,0x00FF00,0x00FF00,0x00FF00,
0x00FF00,0x00FF00,0x00FF00,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0x00FF00,0x00FF00,0x00FF00,
0x00FF00,0x00FF00,0xFF0000,0x000000,0x000000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0x000000,0x000000,0xFF0000,0xFF0000,0xFF0000,0x00FF00,0x00FF00,
0x00FF00,0x00FF00,0x000000,0x000000,0x000000,0x000000,0xFF0000,0xFF0000,0x000000,0x000000,0x000000,0x000000,0xFF0000,0xFF0000,0x00FF00,0x00FF00,
0x00FF00,0x00FF00,0xFFFFFF,0xFFFFFF,0x000000,0x000000,0xFF0000,0xFF0000,0xFFFFFF,0xFFFFFF,0x000000,0x000000,0xFF0000,0xFF0000,0x00FF00,0x00FF00,
0x00FF00,0xFF0000,0xFFFFFF,0xFFFFFF,0x000000,0x000000,0xFF0000,0xFF0000,0xFFFFFF,0xFFFFFF,0x000000,0x000000,0xFF0000,0xFF0000,0xFF0000,0x00FF00,
0x00FF00,0xFF0000,0xFF0000,0x000000,0x000000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0x000000,0x000000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0x00FF00,
0x00FF00,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0x00FF00,
0x00FF00,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0x00FF00,
0x00FF00,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0x00FF00,
0x00FF00,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0xFF0000,0x00FF00,
0x00FF00,0xFF0000,0xFF0000,0x00FF00,0xFF0000,0xFF0000,0xFF0000,0x00FF00,0x00FF00,0xFF0000,0xFF0000,0xFF0000,0x00FF00,0xFF0000,0xFF0000,0x00FF00,
0x00FF00,0xFF0000,0x00FF00,0x00FF00,0x00FF00,0xFF0000,0xFF0000,0x00FF00,0x00FF00,0xFF0000,0xFF0000,0x00FF00,0x00FF00,0x00FF00,0xFF0000,0x00FF00,
0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00,0x00FF00
} with {block = "BlockRAM"};
void RunVGAOutput();
void SetRGB (unsigned 8, unsigned 8, unsigned 8);
#define TAMCUADRO 16
void main()
{
static unsigned 10 posicion_x=0;
static unsigned 10 posicion_y=16;
static unsigned 1 delta_x=0;
static unsigned 1 delta_y=0;
unsigned 24 color;
unsigned 1 dibujar_cuadro_h, dibujar_cuadro_v, dibujar_cuadro;
par /* 3 máquinas de estado en paralelo */
{
/************************************************************************************************/
RunVGAOutput(); /* la primera maquina de estado, produce la temporización VGA
y mantiene los contadores x,y*/
/************************************************************************************************/
while(1) /* La segunda maquina de estado dibuja un punto */
{ /* del color apropiado, según la posición de dicho punto */
if (enable_video) /* si estamos en zona de pixeles... */
{
par /* este bloque dura 2 ciclos */
{
color=Sprite[(contador_y-posicion_y)[3:0]@(contador_x-posicion_x)[3:0]]; /* color del sprite que tocaria pintar */
dibujar_cuadro_h = (contador_x==posicion_x)? 1 : (contador_x==posicion_x+TAMCUADRO || contador_x==639)? 0 : dibujar_cuadro_h; /* si la posicion X está en la zona en la que debe pintarse el sprite, indicarlo */
dibujar_cuadro_v = (contador_y==posicion_y)? 1 : (contador_y==posicion_y+TAMCUADRO || contador_y==479)? 0 : dibujar_cuadro_v; /* si la posicion Y está en la zona en la que debe pintarse el sprite, indicarlo */
dibujar_cuadro = dibujar_cuadro_h & dibujar_cuadro_v; /* Si ambos X e Y están en la zona del sprite, indicarlo */
if (dibujar_cuadro && color!=0x00ff00) /* si toca pintar un pixel del sprite y éste no es de color verde... (=transparente) */
SetRGB(color[23:16],color[15:8],color[7:0]);
else /* si no, pintar el fondo de pantalla (una trama mu mona que me he inventado) */
SetRGB(contador_x[7:0]^posicion_y[7:0],contador_y[7:0]^posicion_x[7:0],contador_x[7]@contador_y[7]@0b000000);
}
}
else /* si no estamos en zona de pÃxeles, apaga la generación de color RGB */
SetRGB(0,0,0);
}
/************************************************************************************************/
while(1) /* la tercera máquina de estados actualiza la posición del sprite, una vez cada cuadro (60Hz)*/
{
while(VS) /* esperamos a que baje la señal VS (retrazo vertical) */
delay;
while (!VS) /* y ahora esperamos a que suba */
delay;
par
{
posicion_x = (delta_x)? posicion_x -1 : posicion_x +1;
posicion_y = (delta_y)? posicion_y -1 : posicion_y +1;
}
par
{
if (posicion_x==639-TAMCUADRO || posicion_x==0)
delta_x = ~delta_x;
else
delay;
if (posicion_y==479-TAMCUADRO || posicion_y==0)
delta_y = ~delta_y;
else
delay;
}
}
/************************************************************************************************/
}
}
void RunVGAOutput()
{
static unsigned 10 contador_h=0;
static unsigned 10 contador_v=0;
unsigned 1 enable_video_h,enable_video_v;
interface bus_out () HSync (unsigned 1 out=HS) with {data = VGASyncHPins, standard = "LVCMOS33"};
interface bus_out () VSync (unsigned 1 out=VS) with {data = VGASyncVPins, standard = "LVCMOS33"};
while(1)
{
par
{
if (contador_h==TSH)
contador_h=0;
else
contador_h++;
if (contador_h==0)
{
par
{
HS=0;
if (contador_v==TSV)
contador_v=0;
else
contador_v++;
if (enable_video_v)
contador_y++;
else
delay;
}
}
else if (contador_h==TPWH)
HS=1;
else if (contador_h==TPWH+TBPH)
{
par
{
enable_video_h=1;
contador_x=0;
}
}
else if (contador_h==TPWH+TBPH+TDISPH)
enable_video_h=0;
else
delay;
if (contador_v==0)
VS=0;
else if (contador_v==TPWV)
VS=1;
else if (contador_v==TPWV+TBPV)
{
par
{
enable_video_v=1;
contador_y=0;
}
}
else if (contador_v==TPWV+TBPV+TDISPV)
enable_video_v=0;
else
delay;
enable_video = enable_video_h & enable_video_v;
if (enable_video_h)
contador_x++;
else
delay;
}
}
}
void SetRGB (unsigned 8 r, unsigned 8 g, unsigned 8 b)
{
unsigned 3 Red, Green;
unsigned 2 Blue;
interface bus_out () VGARed (unsigned 3 out=Red) with {data = VGARedPins, standard = "LVCMOS33"};
interface bus_out () VGAGreen (unsigned 3 out=Green) with {data = VGAGreenPins, standard = "LVCMOS33"};
interface bus_out () VGABlue (unsigned 2 out=Blue) with {data = VGABluePins, standard = "LVCMOS33"};
par
{
Red = r[7:5];
Green = g[7:5];
Blue = b[7:6];
}
}
phpBB [media]
Efecto "Knight Rider" (seguro que sabeis a qué me refiero) usando los leds de la placa Basys (una placa basada en una Spartan 3 pequeña)
phpBB [media]
Contador de 4 dígitos para la placa Basys3, en Handel-C (incluyo el código fuente)
Código: Seleccionar todo
/* Ejemplo de uso del display de 4 dÃgitos de 7 segmentos de la
tarjeta BASYS, en Handel-C.
(C)2010 Miguel Angel Rodriguez Jodar.
Departamento de Arquitectura y Tecnologia de Computadores.
Universidad de Sevilla. */
#define ClockPin "P54"
#define ClockRate 100
#define LEDPins {"P2","P3","P4","P5","P7","P8","P14","P15"}
#define Segmentos {"P83","P17","P20","P21","P23","P16","P25"}
#define Anodos {"P26","P32","P33","P34"}
set clock=external ClockPin with { rate=ClockRate, standard="LVCMOS33" };
void SetLEDs(unsigned 8);
void RunDisplay();
static unsigned 4 numerobcd[4]={0,0,0,0};
void main()
{
unsigned 23 retardo;
static unsigned 8 valor=1;
static unsigned 1 direccion=0;
par
{
RunDisplay();
while(1)
{
numerobcd[3]++;
if (numerobcd[3]==10)
{
numerobcd[3]=0;
numerobcd[2]++;
if (numerobcd[2]==10)
{
numerobcd[2]=0;
numerobcd[1]++;
if (numerobcd[1]==10)
{
numerobcd[1]=0;
numerobcd[0]++;
if (numerobcd[0]==10)
numerobcd[0]=0;
}
}
}
SetLEDs (valor);
if (direccion==0)
valor=valor<<1;
else
valor=valor>>1;
if (valor==1 || valor==128)
direccion=~direccion;
while (retardo)
retardo--;
retardo--;
}
}
}
void SetLEDs(unsigned 8 v) /* 2 ciclos de reloj: uno para asignar el parámetro a V y otro para asignarlo a LED */
{
unsigned 8 LED;
interface bus_out () IntLED (unsigned 8 out=LED ) with {data = LEDPins, standard = "LVCMOS33"};
LED=v;
}
void RunDisplay()
{
static rom unsigned 7 Digitos[10]={0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10}; /* conversor BCD a 7 segmentos */
static rom unsigned 4 Deco2a4[4]={0b0111,0b1011,0b1101,0b1110}; /* sencillo deco de 2 a 4 para los ánodos */
unsigned 7 CatSegments;
unsigned 4 AnSegments;
interface bus_out () DisplayCatodos (unsigned 7 out=CatSegments) with {data = Segmentos, standard = "LVCMOS33"};
interface bus_out () DisplayAnodos (unsigned 4 out=AnSegments) with {data = Anodos, standard = "LVCMOS33"};
unsigned 2 iDigito; /* contador de 2 bits para recorrer los 4 digitos */
unsigned 16 retardo; /* contador de 16 bits (65536 ciclos de reloj) para implementar un retardo
que dé tiempo a que se vea el digito */
while(1) /* esta maquina de estados debe arrancarse en paralelo con el resto de cosas */
{
par
{
AnSegments = Deco2a4[iDigito]; /* activamos un ánodo de los 4 */
CatSegments = Digitos[numerobcd[iDigito]]; /* mostramoss el dÃgito */
iDigito++; /* siguiente display */
retardo--; /* preparamos el retardo igual a 0xFFFF (¿serÃa menos costoso poner retardo=0xffff? */
}
while (retardo) /* bucle de retardo. Gasta "retardo" ciclos de reloj */
retardo--;
}
}
phpBB [media]
Primera versión de una "ULA" de Spectrum, o sea, un circuito que genera video (señal VGA en este caso) con la misma disposición de pantalla que el Spectrum.
En este caso en particular, un proceso en paralelo va rellenando lentamente la memoria de video con un patrón. En el video comparo el comportamiento de este diseño con un emulador de Spectrum ejecutando un programa que rellena la memoria de video de la misma forma.
Incluyo el código fuente en primicia total.
Código: Seleccionar todo
/* Generación de imágenes en color con un framebuffer reducido, de 6912 bytes
He escogido este esquema porque permite imágenes a color con un framebuffer
lo suficientemente pequeño como para que quepa en la memoria BlockRAM de una
Spartan3-100, como la que lleva la tarjeta BASYS.
El formato es un remedo del formato de imagen que genera la ULA del
Sinclair ZX Spectrum.
Ver demo en: http://www.youtube.com/watch?v=ronoUVZ3LRo
(C)1982 Sinclair Research Ltd / Amstrad plc.
(C)2010 Miguel Angel Rodriguez Jodar.
Departamento de Arquitectura y Tecnologia de Computadores.
Universidad de Sevilla. */
#define ClockPin "P54"
#define ClockRate 25
#define VGAColorPins {"P67","P68","P70","P50","P51","P52","P43","P44"}
#define VGASyncHPins {"P39"}
#define VGASyncVPins {"P35"}
set clock=external ClockPin with { rate=ClockRate, standard="LVCMOS33" };
#define TSH 800
#define TSV 520
#define TPWH 95
#define TPWV 2
#define TFPH 16
#define TFPV 10
#define TBPH 48
#define TBPV 29
#define TDISPH 639
#define TDISPV 480
static unsigned 10 contador_x=0;
static unsigned 10 contador_y=0;
static unsigned 1 HS=1;
static unsigned 1 VS=1;
unsigned 1 enable_video;
unsigned 8 Borde;
mpram
{
wom unsigned 8 CPU[6912];
rom unsigned 8 ULA[6912];
} Framebuffer with {block="BlockRAM"};
void RunVGAOutput();
void ZXVideoULA();
void main()
{
unsigned 13 i;
unsigned 15 retardo;
par
{
ZXVideoULA();
/* máquina de estados que rellena el framebuffer lentamente, para que
se vea el formato usado, usando los valores 0,1,2,...,254,255,0,1,2,3... */
seq
{
Borde=7;
for (i=6144;i!=6912;i++)
Framebuffer.CPU[i]=56;
for (i=0;i!=6912;i++)
{
Framebuffer.CPU[i]=i[7:0];
while(retardo)
retardo--;
retardo--;
}
}
}
}
macro expr ULA_x=(contador_x-64)>>1;
macro expr ULA_y=(contador_y-48)>>1;
/*
En alta resolución podemos ponerlo asÃ:
010 BB SSS FFF CCCCC
Coordenadas: X,Y
X/8 = CCCCC
X%8 = bit dentro del byte apuntado por CCCCC
Y = BBFFFSSS
*/
macro expr GetAddrBitmap(x,y)=(y[7:6]@y[2:0]@y[5:3]@x[7:3]);
macro expr GetAddrAttr(x,y)=(0b110@y[7:3]@x[7:3]);
void ZXVideoULA()
{
unsigned 8 Color;
interface bus_out () VGAColor (unsigned 8 out=Color) with {data = VGAColorPins, standard = "LVCMOS33"};
static rom unsigned 8 Paleta[16]={0x00,0b00000010,0b10000000,0b10000010,0b00010000,0b00010010,0b10010000,0b10010010,
0x00,0b00000011,0b11000000,0b11000011,0b00011000,0b00011011,0b11011000,0b11011011};
unsigned 1 estoy_en_paper_h,estoy_en_paper_v,estoy_en_paper;
unsigned 8 bitmap,bitmap2,atributo;
unsigned 13 dirbitmap,dirattr;
unsigned 4 contbitsbitmap;
static unsigned 5 contflash=0;
unsigned 1 fflash;
par
{
RunVGAOutput();
while(1)
{
while(VS)
delay;
while(!VS)
delay;
contflash++;
if (contflash==19)
{
fflash=~fflash;
contflash=0;
}
}
while(1)
{
if (enable_video)
{
par
{
estoy_en_paper_h = (contador_x==64)? 1 : (contador_x==576)? 0 : estoy_en_paper_h;
estoy_en_paper_v = (contador_y==48)? 1 : (contador_y==432)? 0 : estoy_en_paper_v;
estoy_en_paper = estoy_en_paper_h & estoy_en_paper_v;
if (!estoy_en_paper)
{
par
{
Color=Paleta[0b0@Borde[2:0]];
contbitsbitmap=0;
if (estoy_en_paper_v && contador_x==61)
{
par
{
dirbitmap = GetAddrBitmap(0,ULA_y);
dirattr = GetAddrAttr(0,ULA_y);
}
}
else if (estoy_en_paper_v && contador_x==62)
bitmap = Framebuffer.ULA[dirbitmap];
else if (estoy_en_paper_v && contador_x==63)
atributo = Framebuffer.ULA[dirattr];
else
delay;
}
}
else
{
par
{
switch (contbitsbitmap)
{
case 13:
par
{
dirbitmap++;
dirattr++;
}
break;
case 14:
bitmap2 = Framebuffer.ULA[dirbitmap];
break;
case 15:
par
{
bitmap = bitmap2;
atributo = Framebuffer.ULA[dirattr];
}
break;
default:
delay;
}
contbitsbitmap++;
if (contbitsbitmap[0])
bitmap = bitmap<<1;
else
delay;
Color=Paleta[ (bitmap[7])? atributo[6]@((atributo[7]&fflash)? atributo[5:3] : atributo[2:0]) : atributo[6]@((atributo[7]&fflash)? atributo[2:0] : atributo[5:3]) ];
}
}
}
}
else
Color=0x00;
}
}
}
void RunVGAOutput()
{
static unsigned 10 contador_h=0;
static unsigned 10 contador_v=0;
unsigned 1 enable_video_h,enable_video_v;
interface bus_out () HSync (unsigned 1 out=HS) with {data = VGASyncHPins, standard = "LVCMOS33"};
interface bus_out () VSync (unsigned 1 out=VS) with {data = VGASyncVPins, standard = "LVCMOS33"};
while(1)
{
par
{
if (contador_h==TSH)
contador_h=0;
else
contador_h++;
if (contador_h==0)
{
par
{
HS=0;
if (contador_v==TSV)
contador_v=0;
else
contador_v++;
if (enable_video_v)
contador_y++;
else
delay;
}
}
else if (contador_h==TPWH)
HS=1;
else if (contador_h==TPWH+TBPH)
{
par
{
enable_video_h=1;
contador_x=0;
}
}
else if (contador_h==TPWH+TBPH+TDISPH)
enable_video_h=0;
else
delay;
if (contador_v==0)
VS=0;
else if (contador_v==TPWV)
VS=1;
else if (contador_v==TPWV+TBPV)
{
par
{
enable_video_v=1;
contador_y=0;
}
}
else if (contador_v==TPWV+TBPV+TDISPV)
enable_video_v=0;
else
delay;
enable_video = enable_video_h & enable_video_v;
if (enable_video_h)
contador_x++;
else
delay;
}
}
}
phpBB [media]
Para meterle una imagen predefinida, cambiar en el código anterior esto:
Código: Seleccionar todo
mpram
{
wom unsigned 8 CPU[6912];
rom unsigned 8 ULA[6912];
} Framebuffer with {block="BlockRAM"};
Código: Seleccionar todo
static rom unsigned 8 Framebuffer[6912]=
{
#include "contenido_rom.hch"
/* Se incluye una aplicación en C (file2data.c) que permite
coger un fichero conteniendo una imagen de memoria de la VRAM del
Spectrum (o una pantalla de carga) y convertirla a un
formato adecuado para incluirla en este punto del programa.
Se incluyen unos cuantos de estos ficheros, con extensión SCR */
} with {block="BlockRAM"};
Código: Seleccionar todo
#include <stdio.h>
#define SCREENFILE "phantis.scr" // o cualquier otro...
void main (void)
{
FILE *fi, *fo;
unsigned char buffer[6912];
int i;
fi=fopen(SCREENFILE,"rb");
fread (buffer, 1, 6912, fi);
fclose(fi);
fo=fopen("contenido_rom.hch","wt");
for (i=0;i<6912;i++)
{
fprintf (fo,"0x%02.2X",buffer[i]);
if (i!=6911)
fprintf (fo,", ");
if (i%16==15)
fprintf (fo,"\n");
}
fclose(fo);
}