基于FPGA的串口传图SRAM缓存VGA显示
简介
在DE2开发板上使用串口接收PC发送的640*480分辨率灰度图,存入SRAM,通过VGA进行显示。
开发板:DE2
型号:EP2C35F672C6
开发工具:Quartus II 13.0 + Modelsim 10.5 SE
全局时钟: 50M
VGA时钟:25M
SRAM大小:256k*16bit,总共512kb
目前还不会用SDRAM,先从简单的SRAM入手。
由于640*480分辨率的24位彩色图片需要900kb,SRAM装不下,所以使用8位灰度图(300kb)。
该SRAM为256k*16bit,为了存下300kb的灰度图,我将其包装为512k*8bit
RTL视图
一共五个模块:PLL、串口接收、写入地址计数、SRAM驱动、VGA驱动
PLL模块为VGA驱动模块提供25M时钟。
串口接收模块接收来自PC发送的图片数据。
写入地址计数模块根据接收到的数据,产生写入地址,并在接收完整张图片后,输出receive_done信号,允许VGA从SRAM读取数据。
SRAM驱动模块对SRAM芯片进行读写。
VGA驱动模块产生VGA时序显示图片。
串口接收模块
波特率:115200
数据位:8
校验:无
停止位:1
module uart_rx(// clk, rst_ninput clk,input rst_n,// uart rxinput rx,// valid data outputoutput reg valid,output reg [7:0] data
);parameter CLK_FRE = 50_000_000;parameter BAUD_RATE = 115200; // 默认115200波特率parameter cnt_baud_max = CLK_FRE / BAUD_RATE - 1; // 115200波特率下是434时钟周期一波特// 打两拍防止亚稳态,rx_3用于检测rx下降沿reg rx_1;reg rx_2;reg rx_3;// 工作使能reg work_ena;// 波特计数器 比特计数器reg [8:0] cnt_baud;reg [4:0] cnt_bit;// 计数器使能、清零wire cnt_baud_ena;wire cnt_baud_end;wire cnt_bit_ena;wire cnt_bit_end;// data移位寄存器reg [7:0] data_shift;// valid打一拍再输出,和data同步输出reg valid_reg;// 打两拍防止亚稳态always @(posedge clk, negedge rst_n) beginif(!rst_n) beginrx_1 <= 0;rx_2 <= 0;rx_3 <= 0;end else beginrx_1 <= rx;rx_2 <= rx_1;rx_3 <= rx_2; endend// rx下降沿检测,出现下降沿开始工作,接收完数据结束工作always @(posedge clk, negedge rst_n) beginif(!rst_n) beginwork_ena <= 0;end else beginif(~rx_2 & rx_3)work_ena <= 1;else if(cnt_bit_ena && cnt_bit_end)work_ena <= 0;endend// 波特计数器always @(posedge clk, negedge rst_n) beginif(!rst_n)cnt_baud <= 0;else if(cnt_baud_ena) beginif(cnt_baud_end)cnt_baud <= 0;elsecnt_baud <= cnt_baud + 1;end elsecnt_baud <= 0;endassign cnt_baud_ena = work_ena;assign cnt_baud_end = (cnt_baud == cnt_baud_max);// 比特计数器always @(posedge clk, negedge rst_n) beginif(!rst_n)cnt_bit <= 0;else if(cnt_bit_ena) beginif(cnt_bit_end)cnt_bit <= 0;elsecnt_bit <= cnt_bit + 1;endendassign cnt_bit_ena = (cnt_baud == cnt_baud_max / 2);assign cnt_bit_end = (cnt_bit == 8);// data移位寄存器always @(posedge clk, negedge rst_n) beginif(!rst_n)data_shift <= 0;else if(cnt_bit_ena)data_shift <= {rx_2, data_shift[7:1]}; end// 内部valid_reg有效信号always @(posedge clk, negedge rst_n) beginif(!rst_n) beginvalid_reg <= 0;end else if(cnt_bit_ena && cnt_bit_end)valid_reg <= 1;elsevalid_reg <= 0;end// valid_reg 打一拍再输出,和data同步always @(posedge clk, negedge rst_n) beginif(!rst_n)valid <= 0;elsevalid <= valid_reg;end// 输出dataalways @(posedge clk, negedge rst_n) beginif(!rst_n)data <= 0;else if(valid_reg)data <= data_shift;endendmodule
写入地址计数模块
根据串口接收模块接收到的数据,输出该数据需要写入SRAM的地址,接收完整张图片后,拉高receive_done信号,允许VGA模块读取SRAM中的数据。
module uart_rx_addr(input clk,input rst_n,// uart_rx sideinput valid,// sram_ctrl sideoutput reg receive_done,output reg [18:0] addr
);wire cnt_end = (addr == 640 * 480);always @(posedge clk, negedge rst_n) beginif(!rst_n)addr <= 0;else if(valid) beginif(cnt_end)addr <= 0;elseaddr <= addr + 1;endend// 串口传完整张图后,拉高receive_done,允许vga从sram中读取数据always @(posedge clk, negedge rst_n) beginif(!rst_n)receive_done <= 0;else if(cnt_end)receive_done <= 1;endendmodule
SRAM驱动模块
该SRAM为256k*16bit,为了存下300kb的灰度图,我将其包装为512k*8bit,写优先。
包装方法:地址低于256k的数据写入SRAM的数据位低8位,地址高于高256kb数据写入SRAM的数据位高8位
module sram_ctrl(input clk,input rst_n,// wrinput wr_req,input [18:0] wr_addr,input [7:0] wr_data,// rdinput rd_req,input [18:0] rd_addr,output reg [7:0] rd_data,// sram sideoutput [17:0] SRAM_ADDR,inout [15:0] SRAM_DQ,output SRAM_WE_N,output SRAM_OE_N,output SRAM_UB_N,output SRAM_LB_N,output SRAM_CE_N
);assign SRAM_CE_N = 0;assign SRAM_OE_N = 0;assign SRAM_DQ = wr_req ? (SRAM_LB_N ? {wr_data, 8'hzz} : {8'hzz, wr_data}) : 16'hzzzz;assign SRAM_WE_N = ~wr_req;assign SRAM_ADDR = wr_req ? wr_addr[17:0] : rd_addr[17:0];// 将地址最高位作为256k分界线// 地址低于256k的数据写入SRAM的低8位,地址高于高256kb数据写入SRAM的高8位assign SRAM_LB_N = wr_req ? wr_addr[18] : rd_addr[18];assign SRAM_UB_N = ~SRAM_LB_N;// rd_dataalways @(posedge clk, negedge rst_n) beginif(!rst_n)rd_data <= 0;else if(rd_req) beginif(SRAM_LB_N)rd_data <= SRAM_DQ[15:8];elserd_data <= SRAM_DQ[7:0];endendendmodule
VGA驱动模块
使用友晶官方的VGA驱动模块,稍作修改,将输入输出的RGB信号从10位宽改为8位宽。
module VGA_Ctrl ( // Host SideiRed,iGreen,iBlue,oCurrent_X,oCurrent_Y,oAddress,oRequest,// VGA SideoVGA_R,oVGA_G,oVGA_B,oVGA_HS,oVGA_VS,oVGA_SYNC,oVGA_BLANK,oVGA_CLOCK,// Control SignaliCLK,iRST_N );
// Host Side
input [7:0] iRed;
input [7:0] iGreen;
input [7:0] iBlue;
output [21:0] oAddress;
output [10:0] oCurrent_X;
output [10:0] oCurrent_Y;
output oRequest;
// VGA Side
output [7:0] oVGA_R;
output [7:0] oVGA_G;
output [7:0] oVGA_B;
output reg oVGA_HS;
output reg oVGA_VS;
output oVGA_SYNC;
output oVGA_BLANK;
output oVGA_CLOCK;
// Control Signal
input iCLK;
input iRST_N;
// Internal Registers
reg [10:0] H_Cont;
reg [10:0] V_Cont;
// Horizontal Parameter
parameter H_FRONT = 16;
parameter H_SYNC = 96;
parameter H_BACK = 48;
parameter H_ACT = 640;
parameter H_BLANK = H_FRONT+H_SYNC+H_BACK;
parameter H_TOTAL = H_FRONT+H_SYNC+H_BACK+H_ACT;
// Vertical Parameter
parameter V_FRONT = 11;
parameter V_SYNC = 2;
parameter V_BACK = 31;
parameter V_ACT = 480;
parameter V_BLANK = V_FRONT+V_SYNC+V_BACK;
parameter V_TOTAL = V_FRONT+V_SYNC+V_BACK+V_ACT;
assign oVGA_SYNC = 1'b1; // This pin is unused.
assign oVGA_BLANK = ~((H_Cont<H_BLANK)||(V_Cont<V_BLANK));
assign oVGA_CLOCK = ~iCLK;
assign oVGA_R = iRed;
assign oVGA_G = iGreen;
assign oVGA_B = iBlue;
assign oAddress = oCurrent_Y*H_ACT+oCurrent_X;
assign oRequest = ((H_Cont>=H_BLANK && H_Cont<H_TOTAL) &&(V_Cont>=V_BLANK && V_Cont<V_TOTAL));
assign oCurrent_X = (H_Cont>=H_BLANK) ? H_Cont-H_BLANK : 11'h0 ;
assign oCurrent_Y = (V_Cont>=V_BLANK) ? V_Cont-V_BLANK : 11'h0 ;// Horizontal Generator: Refer to the pixel clock
always@(posedge iCLK or negedge iRST_N)
beginif(!iRST_N)beginH_Cont <= 0;oVGA_HS <= 1;endelsebeginif(H_Cont<H_TOTAL)H_Cont <= H_Cont+1'b1;elseH_Cont <= 0;// Horizontal Syncif(H_Cont==H_FRONT-1) // Front porch endoVGA_HS <= 1'b0;if(H_Cont==H_FRONT+H_SYNC-1) // Sync pulse endoVGA_HS <= 1'b1;end
end// Vertical Generator: Refer to the horizontal sync
always@(posedge oVGA_HS or negedge iRST_N)
beginif(!iRST_N)beginV_Cont <= 0;oVGA_VS <= 1;endelsebeginif(V_Cont<V_TOTAL)V_Cont <= V_Cont+1'b1;elseV_Cont <= 0;// Vertical Syncif(V_Cont==V_FRONT-1) // Front porch endoVGA_VS <= 1'b0;if(V_Cont==V_FRONT+V_SYNC-1) // Sync pulse endoVGA_VS <= 1'b1;end
end
endmodule
顶层模块
顶层模块例化所有模块进行连线,和上面的RTL图一致
module uart_sram_vga(input clk,input rst_n,// uart sideinput rx,// sram sideoutput [17:0] SRAM_ADDR,inout [15:0] SRAM_DQ,output SRAM_WE_N,output SRAM_OE_N,output SRAM_UB_N,output SRAM_LB_N,output SRAM_CE_N,// vga sideoutput VGA_HS,output VGA_VS,output [9:0] VGA_R,output [9:0] VGA_G,output [9:0] VGA_B,output VGA_CLK,output VGA_BLANK,output VGA_SYNC
);// 用不上assign {VGA_R[1:0], VGA_G[1:0], VGA_B[1:0]} = 0;// pll输出25M时钟给vga_ctrlwire clk_25;// uart接收到的数据和有效位wire valid;wire [7:0] data;// wr_addr:数据的写入地址,uart接收完数据后拉高receive_donewire [18:0] wr_addr;wire receive_done;// vga_ctrl输出的坐标、地址、请求、rgbwire [9:0] x;wire [9:0] y;wire [21:0] address;wire oRequest;wire [7:0] R,G,B;// 从sram中读取的数据wire [7:0] rd_data;vga_pll inst_vga_pll (.inclk0 (clk),.c0 (clk_25));uart_rx inst_uart_rx (.clk (clk),.rst_n (rst_n),.rx (rx),.valid (valid),.data (data));uart_rx_addr inst_uart_rx_addr (.clk (clk), .rst_n (rst_n), .valid (valid), .receive_done (receive_done),.addr (wr_addr));sram_ctrl inst_sram_ctrl (.clk (clk),.rst_n (rst_n),.wr_req (valid),.wr_addr (wr_addr),.wr_data (data),.rd_req (receive_done & oRequest),.rd_addr (address),.rd_data (rd_data),.SRAM_ADDR (SRAM_ADDR),.SRAM_DQ (SRAM_DQ),.SRAM_WE_N (SRAM_WE_N),.SRAM_OE_N (SRAM_OE_N),.SRAM_UB_N (SRAM_UB_N),.SRAM_LB_N (SRAM_LB_N),.SRAM_CE_N (SRAM_CE_N));VGA_Ctrl inst_VGA_Ctrl (.iRed (rd_data),.iGreen (rd_data),.iBlue (rd_data),.oCurrent_X (x),.oCurrent_Y (y),.oAddress (address),.oRequest (oRequest),.oVGA_R (VGA_R[9:2]),.oVGA_G (VGA_G[9:2]),.oVGA_B (VGA_B[9:2]),.oVGA_HS (VGA_HS),.oVGA_VS (VGA_VS),.oVGA_SYNC (VGA_SYNC),.oVGA_BLANK (VGA_BLANK),.oVGA_CLOCK (VGA_CLK),.iCLK (clk_25),.iRST_N (rst_n));
endmodule
结果分析
图片选择友晶资料盘里的图片,分辨率640*480,将其转灰度图用于实验。
仿真就不放了,直接放上板验证:
用正点原子的XCOM串口软件发送bmp图片,实验结果如下:
图像上下反转了,而且左右也有错位,说明直接使用串口软件发送bmp文件是不对的。
网上查了很多FPGA串口传图的代码,他们都喜欢自己写一个串口程序来传图,但我太菜了,不会写,他们的程序我也不好用,因为我要传灰度图。
解决方法:将图片转存为hex文件,然后用串口软件发送hex文件。
方式1:matlab
gray = imread('gray_640_480.bmp');
f = fopen('gray_640_480.hex', 'w+');
for y = 1:480for x = 1:640fprintf(f, '%x', gray(y, x));end
end
fclose(f);
方式2:python+opencv
import cv2 as cv
gray = cv.imread('gray_640_480.bmp', cv.IMREAD_GRAYSCALE)
f = open("gray_640_480.hex", "w+")
for y in range(0, 480):for x in range(0, 640):# 记得去掉0x开头hex_data = hex(gray[y][x])[2:4]f.write(hex_data)
f.close()
执行上述代码后,只是将bmp文件转化为了hex文本,其内容还是ASCII字符,2位十六进制数对应1字节,此时的hex文件大小(600kb)是bmp文件大小(300kb)的2倍。
接下来需要执行最后一步:以十六进制编码保存文件,我用的是sublime编辑器,打开hex文件,选择文件->以…编码保存->十六进制
执行完最后一步,hex文件大小变成300kb了,转hex完毕。
之后再用串口软件发送该hex文件即可,实验结果如下,与原图一致:
基于FPGA的串口传图SRAM缓存VGA显示相关推荐
- 基于FPGA的串口传图SDRAM缓存VGA显示
简介 在DE2开发板上,使用串口接收PC上的Qt程序发送的640*480彩色图片,以RGB565格式存入SDRAM,通过VGA显示在屏幕上. 开发板:DE2 开发工具:Quartus II 13.0 ...
- 基于 DDR3 的串口传图帧缓存系统设计实现(整体设计)
文章目录 前言 一.串口传图顶层系统设计框图 二.各模块说明 三.系统工程及 IP 创建 四.uart_ddr3_tft模块 五.uart_ddr3_tft模块仿真文件 六.传图展示 前言 结合串口接 ...
- UART串口传图LCD显示----图像处理
UART串口传图LCD显示----图像处理 设计介绍 首先需要准备一个txt文本,里面存储一个16进制200* 200的图片数据,通过串口调试助手使用串口传输一个200* 200图片,然后通过开发板上 ...
- 基于 DDR3 的串口传图帧缓存系统设计实现(fifo2mig_axi )
文章目录 前言 一.接口转换模块设计 二.fifo2mig_axi 模块 二.接口转换模块仿真 四.fifo2mig_axi_tb 五.仿真展示 前言 结合串口接收模块和 tft 显示屏控制模块,设计 ...
- 基于FPGA的视频图像直方图均衡 图像处理 图像增强 VGA对比度增强CLAHE
基于FPGA的视频图像直方图均衡 图像处理 图像增强 VGA对比度增强CLAHE 本设计是基于FPGA的视频图像直方图均衡,实现的效果是可以实时地将摄像头采集的图像进行直方图均衡,具体过程是FPGA控 ...
- 基于FPGA的数字钟——(三)时钟显示模块(数码管)
基于FPGA的数字钟--(三)数码管显示模块 一.硬件原理 本设计中使用 6 个共阳数码管,可以显示 6 个数字(包含小数点) .电路用 PNP管来反向驱动并且控制列扫描信号来选择哪个数码管.而且所有 ...
- FPGA基础设计(一):VGA显示方法(文字、图形、波形)
概述 VGA是一种学习FPGA最常见的基础实验.虽然现在的显示屏大多已经采用DVI和HDMI方案,但其实VGA在另一个地方还有应用,那就是大屏的LCD.目前4.3寸以上的TFT基本都是VGA接口, ...
- FPGA 20个例程篇:15.VGA显示八种颜色的彩条
第六章 图像显示处理,经典再现 15.VGA显示八种颜色的彩条 图像和视频处理可以说是FPGA中又一个经典地应用,使用FPGA做图像处理最核心的优势就在于:FPGA能进行实时流水线运算,从而达到更高的 ...
- 基于DDR3的串口传图帧缓存系统设计实现
整体设计框图如图所示 模块名称 模块功能 uart_byte_rx模块 负责串口图像数据的接收 bit8_trans_bit16模块 将串口接收的每两个 8bit 数据转换成一个 16bit 数据(图 ...
最新文章
- 看别人的C/C++代码时发现自己所不知道的语法~
- 测试 MySQL 性能的几款工具
- mysql部署策略_MySQL延迟问题和数据刷盘策略流程分析
- (王道408考研操作系统)第五章输入/输出(I/O)管理-第一节3:I/O控制方式
- 自适应滤波器设计及matlab实现,(终稿)自适应滤波器设计及Matlab实现.doc(OK版)...
- GeeksForGeeks 翻译计划 | ApacheCN
- SpringSecurity-1-前言,登录原理
- 借给朋友两万块钱,已经两年,每次要钱都各种借口,我该怎么办?
- JavaScript 预解析机制
- java 读取excel wps_安装WPS引发的excel上传问题
- js中for循环的优化写法
- 【优化算法】孪生支持向量机(TWSVM)【含Matlab源码 1257期】
- 电力拖动自动控制系统复习(一)
- 使用Scala实现Either数据结构
- typora用Pandoc导出html,typora使用pandoc导出功能
- 零基础学习PS——#photoshop# 的167个技能!
- 8、TM4单片机的滴答定时器,及利用定时器精确延时
- matlab中xpcapi库的调用,关于MATLAB中xpc实时控制平台搭建的心得
- PyAutoGUI应用
- 万维网发布服务(w3svc)已停止。除非万维网发布服务(w3svc)正在运行,否则无法启动网站。