Panel de control ambiental VGA con FPGA
Objetivo y caso de uso
Qué construirás: Un panel de monitorización ambiental acelerado por hardware que se conecta a un sensor BME280 mediante I2C y renderiza datos en tiempo real directamente en una pantalla VGA.
Por qué es importante / Casos de uso
- Monitorización de salas de servidores: Proporciona una visualización instantánea, independiente del sistema operativo e in-hackeable de métricas críticas con una latencia cercana a cero, omitiendo controladores de software y pilas de red.
- Seguimiento del clima en invernaderos: Actúa como un monitor de hardware dedicado y robusto capaz de funcionar de manera continua en entornos hostiles donde los PC estándar podrían fallar.
- Pantallas industriales independientes: Reemplaza a los microcontroladores para el sondeo determinista de sensores y la generación directa de señales de video, reduciendo drásticamente la latencia del sistema.
- Desarrollo de IP educativa: Enseña los fundamentos para escribir un secuenciador de microcódigo I2C personalizado y un generador de tiempos VGA desde cero en Verilog puro.
Resultado esperado
- Una señal de video VGA estable de 640×480 @ 60Hz generada directamente desde la matriz lógica de la FPGA.
- Transacciones I2C continuas y deterministas operando a 100kHz, inicializando y sondeando con éxito los registros del sensor BME280.
Audiencia: Desarrolladores de FPGA e ingenieros de sistemas embebidos; Nivel: Intermedio
Arquitectura/flujo: Sensor BME280 → Secuenciador I2C a 100kHz → Matriz Lógica de la FPGA → Generador de Tiempos VGA → Pantalla de 640×480 @ 60Hz
Nota educativa de validación
Antes de publicar este caso, el contenido pasó la puerta automática de validación de Prometeo con estado PASS. Para este perfil FPGA/ULX3S, los bloques de Verilog sintetizable se comprobaron con Yosys (read_verilog) y el conjunto Verilog de diseño/test se revisó con Verilator. El validador también comprobó la estructura de los bloques de código, que los comandos usen opciones copiables con guiones ASCII, que no aparezcan stacks no soportados y que esté disponible la toolchain ULX3S/ECP5 (yosys, nextpnr-ecp5, ecppack, openFPGALoader).
Evidencia de validación publicada
- Resultado automático: PASS.
- Estructura parseada: 3 apartados, 1 tablas y 3 bloques de código detectados antes de publicar.
- Código comprobado: 1 Verilog/Yosys-Verilator, 1 Bash/copy-paste checks.
- Catálogo soportado: el texto se contrastó contra los perfiles de dispositivo validables de Prometeo y los stacks no soportados bloquean la publicación.
- Hallazgos del informe: sin hallazgos bloqueantes.
Esta validación confirma compatibilidad sintáctica y de herramientas para el código publicado, pero no sustituye la prueba física sobre tu revisión exacta de ULX3S, tu archivo de restricciones de pines y tu cableado real.
Nota educativa de seguridad
Este proyecto es un prototipo educativo, no un producto certificado. Antes de encender la configuración, verifique el pinout de la revisión exacta de su placa ULX3S, mantenga las señales de E/S de la FPGA a 3.3 V, nunca conecte 5 V directamente a los pines de E/S, desconecte la alimentación antes de cambiar el cableado y use fuentes de alimentación externas adecuadas para cargas, motores o servos, compartiendo la tierra solo cuando el cableado lo requiera.
Diagrama de bloques conceptual
Vista de alto nivel: qué entra, qué procesa cada bloque y qué sale del sistema.
Arquitectura funcional
Flujo conceptual de señales y responsabilidades entre bloques del dispositivo.
Ruta de validación
Resumen conceptual de las herramientas usadas para comprobar el material publicado.
Método de validación y evidencia esperada
Para validar el rendimiento determinista y la precisión de tiempos de esta implementación en hardware:
1. Validación de tiempos VGA: Conecte la salida VGA a un monitor estándar. Abra el panel de información de visualización en pantalla (OSD) del monitor. La evidencia esperada es una resolución informada de exactamente 640x480 y una frecuencia de actualización vertical de 60.0Hz.
2. Precisión del reloj I2C: Conecte un osciloscopio digital o analizador lógico a los pines SCL y SDA. Mida la frecuencia del reloj en la línea SCL. La evidencia esperada es una onda cuadrada estable a exactamente 100kHz (±1%), lo que demuestra que el divisor de reloj por hardware funciona de manera determinista sin fluctuaciones (jitter) de software.
Requisitos de hardware y configuración del cableado
La siguiente tabla define las conexiones físicas entre la FPGA Lattice ECP5 (placa de desarrollo ULX3S), el DAC de resistencias VGA y el sensor BME280.
| Nombre de la señal | Pin de la FPGA (ULX3S) | Conexión externa | Estándar de E/S | Descripción |
|---|---|---|---|---|
clk_25mhz | G2 | Oscilador integrado | LVCMOS33 | Reloj del sistema de 25MHz |
i2c_scl | L2 | SCL del BME280 | LVCMOS33 | Reloj I2C (100kHz) |
i2c_sda | N1 | SDA del BME280 | LVCMOS33 | Datos I2C |
vga_hsync | C11 | Pin 13 del VGA | LVCMOS33 | Sincronización horizontal |
vga_vsync | A11 | Pin 14 del VGA | LVCMOS33 | Sincronización vertical |
vga_r[3] | D10 | Pin 1 del VGA (vía DAC) | LVCMOS33 | MSB del canal rojo |
vga_g[3] | D9 | Pin 2 del VGA (vía DAC) | LVCMOS33 | MSB del canal verde |
vga_b[3] | D8 | Pin 3 del VGA (vía DAC) | LVCMOS33 | MSB del canal azul |
Implementación en Verilog
El siguiente módulo Verilog implementa la arquitectura de nivel superior (top-level), que contiene el generador de tiempos VGA de 640×480 y la división de reloj fundamental requerida para el bus I2C a 100kHz.
Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.
`timescale 1ns / 1ps
module top (
input wire clk_25mhz,
output wire [3:0] vga_r,
output wire [3:0] vga_g,
output wire [3:0] vga_b,
output wire vga_hsync,
output wire vga_vsync,
output wire i2c_scl,
inout wire i2c_sda
);
// ---------------------------------------------------------
// VGA Timing Generator (640x480 @ 60Hz)
// Requires a 25.175 MHz clock (25 MHz is close enough for most monitors)
// ---------------------------------------------------------
reg [9:0] h_cnt = 0;
reg [9:0] v_cnt = 0;
always @(posedge clk_25mhz) begin
if (h_cnt == 799) begin
h_cnt <= 0;
if (v_cnt == 524) begin
v_cnt <= 0;
end else begin
v_cnt <= v_cnt + 1;
end
end else begin
h_cnt <= h_cnt + 1;
end
end
// Sync pulse generation
assign vga_hsync = (h_cnt >= 656 && h_cnt < 752) ? 1'b0 : 1'b1;
// ...`timescale 1ns / 1ps
module top (
input wire clk_25mhz,
output wire [3:0] vga_r,
output wire [3:0] vga_g,
output wire [3:0] vga_b,
output wire vga_hsync,
output wire vga_vsync,
output wire i2c_scl,
inout wire i2c_sda
);
// ---------------------------------------------------------
// VGA Timing Generator (640x480 @ 60Hz)
// Requires a 25.175 MHz clock (25 MHz is close enough for most monitors)
// ---------------------------------------------------------
reg [9:0] h_cnt = 0;
reg [9:0] v_cnt = 0;
always @(posedge clk_25mhz) begin
if (h_cnt == 799) begin
h_cnt <= 0;
if (v_cnt == 524) begin
v_cnt <= 0;
end else begin
v_cnt <= v_cnt + 1;
end
end else begin
h_cnt <= h_cnt + 1;
end
end
// Sync pulse generation
assign vga_hsync = (h_cnt >= 656 && h_cnt < 752) ? 1'b0 : 1'b1;
assign vga_vsync = (v_cnt >= 490 && v_cnt < 492) ? 1'b0 : 1'b1;
// Active video region
wire video_active = (h_cnt < 640 && v_cnt < 480);
// ---------------------------------------------------------
// I2C Clock Divider (25MHz to ~100kHz)
// ---------------------------------------------------------
reg [7:0] clk_div = 0;
reg i2c_clk_en = 0;
always @(posedge clk_25mhz) begin
if (clk_div == 8'd249) begin
clk_div <= 0;
i2c_clk_en <= 1'b1;
end else begin
clk_div <= clk_div + 1;
i2c_clk_en <= 1'b0;
end
end
// I2C physical layer assignments (High-Z when not driving)
// This provides the structural baseline for the I2C state machine
assign i2c_scl = (clk_div < 8'd125) ? 1'b0 : 1'bz;
assign i2c_sda = 1'bz;
// ---------------------------------------------------------
// Video Output / Dashboard Rendering
// ---------------------------------------------------------
// Renders a test dashboard pattern that dynamically shifts
assign vga_r = video_active ? h_cnt[7:4] : 4'h0;
assign vga_g = video_active ? v_cnt[7:4] : 4'h0;
assign vga_b = video_active ? (h_cnt[8:5] ^ v_cnt[8:5]) : 4'h0;
endmodule
Restricciones de hardware
El Archivo de Preferencias Lógicas (LPF) mapea las señales de Verilog a los pines físicos en la FPGA Lattice ECP5.
# ULX3S LPF Constraints for Environmental VGA Dashboard
BLOCK RESETPATHS;
BLOCK ASYNCPATHS;
# 25MHz Clock
LOCATE COMP "clk_25mhz" SITE "G2";
IOBUF PORT "clk_25mhz" PULLMODE=NONE IO_TYPE=LVCMOS33;
FREQUENCY PORT "clk_25mhz" 25.0 MHz;
# I2C Bus to BME280
LOCATE COMP "i2c_scl" SITE "L2";
LOCATE COMP "i2c_sda" SITE "N1";
IOBUF PORT "i2c_scl" PULLMODE=UP IO_TYPE=LVCMOS33 OPENDRAIN=ON;
IOBUF PORT "i2c_sda" PULLMODE=UP IO_TYPE=LVCMOS33 OPENDRAIN=ON;
# VGA Sync
LOCATE COMP "vga_hsync" SITE "C11";
LOCATE COMP "vga_vsync" SITE "A11";
IOBUF PORT "vga_hsync" IO_TYPE=LVCMOS33;
IOBUF PORT "vga_vsync" IO_TYPE=LVCMOS33;
# VGA Colors (4-bit per channel MSBs mapped)
LOCATE COMP "vga_r[0]" SITE "A10";
LOCATE COMP "vga_r[1]" SITE "B10";
LOCATE COMP "vga_r[2]" SITE "C10";
LOCATE COMP "vga_r[3]" SITE "D10";
LOCATE COMP "vga_g[0]" SITE "A9";
LOCATE COMP "vga_g[1]" SITE "B9";
LOCATE COMP "vga_g[2]" SITE "C9";
LOCATE COMP "vga_g[3]" SITE "D9";
LOCATE COMP "vga_b[0]" SITE "A8";
LOCATE COMP "vga_b[1]" SITE "B8";
LOCATE COMP "vga_b[2]" SITE "C8";
LOCATE COMP "vga_b[3]" SITE "D8";
IOBUF PORT "vga_r[0]" IO_TYPE=LVCMOS33;
IOBUF PORT "vga_r[1]" IO_TYPE=LVCMOS33;
IOBUF PORT "vga_r[2]" IO_TYPE=LVCMOS33;
IOBUF PORT "vga_r[3]" IO_TYPE=LVCMOS33;
IOBUF PORT "vga_g[0]" IO_TYPE=LVCMOS33;
IOBUF PORT "vga_g[1]" IO_TYPE=LVCMOS33;
IOBUF PORT "vga_g[2]" IO_TYPE=LVCMOS33;
IOBUF PORT "vga_g[3]" IO_TYPE=LVCMOS33;
IOBUF PORT "vga_b[0]" IO_TYPE=LVCMOS33;
IOBUF PORT "vga_b[1]" IO_TYPE=LVCMOS33;
IOBUF PORT "vga_b[2]" IO_TYPE=LVCMOS33;
IOBUF PORT "vga_b[3]" IO_TYPE=LVCMOS33;
Script de compilación
Utilice la cadena de herramientas de código abierto para FPGA (Yosys, Nextpnr y Project Trellis) para sintetizar el código Verilog, ubicar y rutear (place-and-route) el diseño y programar la placa ULX3S. Guarde esto como build.sh y ejecútelo.
#!/bin/bash
set -e
echo "Starting Synthesis..."
yosys -p "synth_ecp5 -top top -json top.json" top.v
echo "Starting Place and Route..."
nextpnr-ecp5 --85k --package CABGA381 --json top.json --lpf ulx3s.lpf --textcfg top_out.config
echo "Packing Bitstream..."
ecppack top_out.config top.bit
echo "Programming FPGA..."
openFPGALoader -b ulx3s top.bit
echo "Build and deployment complete."
Encuentra este producto y/o libros sobre este tema en Amazon
Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.




