写在前面

本文是SDRAM系列文章的第九篇,前面八篇已经实现了一个简单的SDRAM控制器。正所谓光说不练云玩家,接下来我们搞搞实战,真正把SDRAM给用起来。

本文将结合UART模块、VGA模块、SDRAM模块(含PLL、FIFO)来做一个基于SDRAM缓存的串口传图实验,实现UART发送数据、SDRAM缓存数据、VGA显示数据这一过程。

其他博文链接:

相信我,SDRAM真的不难----汇总篇(电梯直达)


1、总体架构

期待实现的功能:在PC端使用串口助手发送一幅分辨率为 640*480 的图片数据给 FPGA,FPGA 以外接 SDRAM 做缓存,将接收到的图片数据通过 VGA 显示器显示出来。

总体架构示意图如下:

  • PLL模块:时钟生成模块。由于各个模块的时钟不尽相同,通过PLL统一生成时钟,全部接到全局时钟网络上
  • uart_rx:串口接收模块。将接收到的串行信号,转换成8bit并行信号
  • merge:数据拼接模块。将2个输入的8bit数据转换成16bit数据
  • sdram_top:sdram读写控制器。可以实现高速、大量的数据缓存
  • vga_driver:VGA接口驱动。实现图片(RGB565像素值)的VGA显示

这工程一看好像还挺多挺复杂的,那么我接下来就对整个图片的传送显示流程做一个讲解:

  • 首先将需要显示的图片,转为RGB565格式的txt文件(16bit像素值),然后通过PC端的串口发送上位机,将数据发送给FPGA
  • FPGA将通过串口接收模块uart_rx将串行线上的数据转成8bit的并行数据
  • 通过数据拼接模块merge将每2个8bit数据转换成16bit数据,然后,将其写入SDRAM缓存
  • 在FPGA接收数据的同时通过VGA接口,用屏幕将写入SDRAM的数据”读出“在屏幕上显示

2、设计实现

接下里就一步一步讲解具体的设计实现了。

2.1、图片预处理

图片格式:640 * 480。

图片内容:老婆。

显然我们无法直接通过串口发送图片,需要将图片转化成RGB565的像素值(其他RGB格式不讨论)才能使用VGA接口显示。我们可以先使用MATLAB将图片转换成RGB565,代码如下:

%--------------------------------------------------------------------------
%--           图片数据转换:1个像素转换成2个 8bit hex 数据
%--------------------------------------------------------------------------
clear all;
RGB24 = imread('laopo.bmp');            %读取图片文件fid = fopen('rgb565.txt','w+');         %打开文件
[ROW,COL,N] = size(RGB24);              %获得图片尺寸[高度,长度,维度]for i = 1:ROWfor j = 1:COLRG = bitand(RGB24(i,j,1),248) + bitshift(RGB24(i,j,2),-5); %{R[7:3],3'd0} + {5'd0,G[7:5]}G = bitand(RGB24(i,j,2),28);                               %{3'd0,G[4:2],2'd0}GB = bitshift( G,3) + bitshift(RGB24(i,j,3),-3);           %{G[4:2],5'd0} + {3'd0,B[7:3]}fprintf(fid,'%02x %02x ',RG,GB);%将字符打印到txt文件中end
end

2.2、子模块

接下来介绍各个子模块的功能及实现方式。这几个子模块,都是我之前的文章写过的模块,所以等下不会介绍的很详细,会贴出链接,感兴趣的朋友可以自己去看看。

2.2.1、串口接收模块

由于我们在PC端使用UART来发送图片信息,而UART是串行协议,所以我们需要一个模块来将接收到的串行数据转为并行数据。

串口模块将PC端通过UART接口(含协议)发送过来的图片的像素信息(RGB565格式)接收,并转化为8bit并行数据。串口接收模块的波特率在本次实验中选为115200。

供参考:串口(UART)的FPGA实现

2.2.2、数据拼接模块

由于目前的主流串口发送软件很少能支持16bit数据的发送,只能将16bit的像素信息拆成2个8bit数据来发送。所以需要一个模块将分别接收的2个8bit数据合并成1个16bit数据(完整的RGB565)。

数据拼接模块将串口接收模块发送的两个8bit数据转换成16bit数据。

供参考:一个简单方便的数据拼接模块(支持任意位宽、任意整数倍)

2.2.3、SDRAM控制模块

SDRAM控制模块可以将接收到串口发送的图片信息暂存到SDRAM芯片,同时显示器也在不停地通过VGA接口对SDRAM控制模块进行操作以读取SDRAM中存取的图片信息。在640*480@60Hz时序下的VGA接口,一幅图片的大小约为640*480*16bit /8 /1024 = 600KB,这个大小显然无法使用FPGA的片内资源来缓存,所以选择SDRAM这种大容量器件来作为接收的缓存。

供参考:相信我,SDRAM真的不难----汇总篇(电梯直达)

2.2.4、VGA驱动模块

VGA驱动模块将SDRAM中缓存的图片信息以VGA时序输出,从而在显示器上显示图片。

供参考:如何用VGA接口乳法?

2.2.5、PLL模块

使用PLL模块统一生成时钟,有利于统一管理时钟,约束时序。各模块需要用到的时钟如下:

  • 50M:串口接收模块、数据拼接模块使用
  • 100M:SDRAM控制模块使用
  • 100M(偏移-30°):SDRAM芯片使用
  • 25M:VGA驱动模块使用

2.3、顶层模块

顶层模块主要实现化上述几个子模块,并设定一些参数:

  • 突发长度:256
  • SDRAM起始地址:0;SDRAM停止地址:640*480(刚好缓存一幅图片)
  • 串口波特率:115200bps

顶层模块完整代码如下:

module pic_uart_sdram #(parameter            BPS     = 115200               //发送波特率
)
(       input               sys_clk         ,               //50M系统时钟input              sys_rst_n       ,               //系统复位input                 uart_rxd        ,               //串口接收output                h_sync          ,               //VGA行同步output              v_sync          ,               //VGA场同步output  [15:0]      rgb_data        ,               //在VGA时序下的RGB数据
//SDRAM相关信号output               sdram_clk_out   ,output             sdram_cke       ,output             sdram_cs_n      ,output             sdram_ras_n     ,output             sdram_cas_n     ,output             sdram_we_n      ,output [1:0]       sdram_bank      ,output [12:0]      sdram_addr      ,output [1:0]       sdram_dqm       ,inout  [15:0]      sdram_dq
);
localparam  CLK_FRE = 50_000_000   ;                       //输入时钟频率
localparam  H_PIXEL = 24'd640         ;                       //水平方向像素个数,用于设置 SDRAM 缓存大小
localparam  V_PIXEL = 24'd480         ;                       //垂直方向像素个数,用于设置 SDRAM 缓存大小//8bit转16bit模块
localparam  integer DATA_WIDTH  = 8    ;                       //输入数据位宽
localparam  integer MERGE_STAGE = 2    ;                       //合并级数,如2则将两个数据合并为一个 //PLL
wire    rst_n           ;                                   //低电平有效的复位信号,除PLL模块外均使用该信号
wire    locked          ;                                   //PLL输出稳定信号,高电平有效
wire    pll_50M         ;                                   //用于串口模块
wire    pll_100M        ;                                   //用于SDRAM
wire    pll_100M_shift  ;                                   //用于SDRAM
wire    pll_25M         ;                                   //用于25M
//uart
wire    [7:0]   rx_data;                                    //接收到的串口数据
wire    rx_flag;                                            //串口数据有效
//VGA
wire            pix_data_req;                               //RGB数据请求信号,用作SDRAM的读请求
wire    [15:0]  sdram_rd_data;                              //从SDRAM中读出的数据
//merge
wire    [15:0]  data_16bit;                                 //8bit转16bit后的数据
wire            data_16bit_valid;                           //16bit数据有效信号assign rst_n = sys_rst_n & locked;                            //PLL未稳定视为复位状态//例化PLL
clk_gen clk_gen_inst (.areset               (~sys_rst_n         ),          //.inclk0               (sys_clk            ),          //50M输入.c0                  (pll_50M            ),          //50M.c1                    (pll_100M           ),          //100M.c2                   (pll_100M_shift     ),          //100M_SHIFT.c3                     (pll_25M            ),          //25M.locked                (locked             )           //PLL输出稳定信号,高电平有效
);
//例化VGA驱动模块(640*480@60,时钟25M)
vag_driver  vag_driver_inst(    .vga_clk                (pll_25M            ),          //VGA时钟.sys_rst_n               (rst_n              ),          //复位信号、低电平有效.pixel_data             (sdram_rd_data      ),          //输入RGB数据   .h_sync                 (h_sync             ),          //行同步信号     .v_sync                 (v_sync             ),          //场同步信号.pixel_x             (                   ),          //行坐标,不需要使用.pixel_y             (                   ),          //场坐标,不需要使用.pix_data_req            (pix_data_req       ),          //RGB数据请求信号,比rgb_valid提前一拍.rgb_data              (rgb_data           )           //在VGA时序下的RGB数据
);
//例化8bit转16bit模块
merge #(.DATA_WIDTH             (DATA_WIDTH         ),          //输入数据位宽.MERGE_STAGE            (MERGE_STAGE        )           //合并级数,如2则将两个数据合并为一个。
)
merge_inst( .sys_clk                (pll_50M            ),          //50M系统时钟.sys_rst_n             (rst_n              ),          //系统复位.data_in              (rx_data            ),  .data_in_valid          (rx_flag            ),          .data_out               (data_16bit         ),.data_out_valid           (data_16bit_valid   )
);//例化串口接收模块
uart_rx
#(.BPS                  (BPS                ),          //发送波特率.CLK_FRE             (CLK_FRE            )           //输入时钟频率
)
uart_rx_inst(       .sys_clk                (pll_50M            ),          //50M系统时钟.sys_rst_n             (rst_n              ),          //系统复位                          .uart_rxd               (uart_rxd           ),          //接收数据线                             .uart_rx_done           (rx_flag            ),          //数据接收完成标志,当其为高电平时,代表接收数据有效.uart_rx_data          (rx_data            )           //接收到的数据,在uart_rx_done为高电平时有效
);//例化SDRAM控制模块
sdram_top   sdram_top_inst
(.sdram_clk             (pll_100M           ),          //sdram时钟.sdram_rst_n           (rst_n              ),          //sdram复位信号 .clk_out                (pll_100M_shift     ),          //sdram相位偏移时钟(直接给SDRAM芯片)
//写FIFO信号       .wr_fifo_rst            (~rst_n             ),          //写FIFO复位信号                                .wr_fifo_wr_clk          (pll_50M            ),          //写FIFO写时钟  .wr_fifo_wr_req         (data_16bit_valid   ),          //写FIFO写请求.wr_fifo_wr_data      (data_16bit         ),          //写FIFO写数据.sdram_wr_b_addr      (0                  ),          //写SDRAM首地址.sdram_wr_e_addr         (H_PIXEL*V_PIXEL    ),          //写SDRAM末地址.wr_burst_len            (256                ),          //写SDRAM数据突发长度.wr_fifo_num          (                   ),          //写fifo中的数据量
//读FIFO信号                                           .rd_fifo_rd_clk         (pll_25M            ),          //读FIFO读时钟,与VGA时钟一致.rd_fifo_rst         (~rst_n             ),          //读复位信号 .rd_fifo_rd_req         (pix_data_req       ),          //读FIFO读请求.sdram_rd_b_addr      (0                  ),          //读SDRAM首地址.sdram_rd_e_addr         (H_PIXEL*V_PIXEL    ),          //读SDRAM末地址.rd_burst_len            (256                ),          //读SDRAM数据突发长度.rd_fifo_rd_data      (sdram_rd_data      ),          //读FIFO读数据,用于VGA显示的数据.rd_fifo_num           (                   ),          //读fifo中的数据量
//功能信号                                              .read_valid             (1'b1              ),          //SDRAM读使能.init_end             (                   ),          //SDRAM初始化完成标志
//SDRAM接口信号                                         .sdram_clk_out          (sdram_clk_out      ),          //SDRAM芯片时钟.sdram_cke               (sdram_cke          ),          //SDRAM时钟有效信号.sdram_cs_n            (sdram_cs_n         ),          //SDRAM片选信号.sdram_ras_n             (sdram_ras_n        ),          //SDRAM行地址选通脉冲.sdram_cas_n          (sdram_cas_n        ),          //SDRAM列地址选通脉冲.sdram_we_n           (sdram_we_n         ),          //SDRAM写允许位.sdram_bank              (sdram_bank         ),          //SDRAM的L-Bank地址线.sdram_addr            (sdram_addr         ),          //SDRAM地址总线.sdram_dqm               (sdram_dqm          ),          //SDRAM数据掩码.sdram_dq                (sdram_dq           )           //SDRAM数据总线
);endmodule

3、仿真

略。因为VGA的时序如果不改动参数仿真,那么时间就太长了;改了吧,看起来意义又不大。干脆不仿真了,直接看现象吧。

4、上板验证

验证环境:

  • 串口上位机:野火多功能调试助手
  • MATLAB:MATLAB R2014a
  • Quartus II:Quartus II 13.1 (64-bit)
  • FPGA:EP4CE10F17C8
  • SDRAM:W9825G6KH-6

需要注意的是因为串口的发送速度太慢了,而VGA读取的速度又相对较快,所以看起来,图片是一行、一行的显示出来的。实验结果如下:

5、其他

  • 创作不易,如果本文对您有帮助,还请多多点赞、评论和收藏。GAKKI都来了,你好意思不给我点赞?
  • 关于本文,您有什么想法均可在评论区留言交流。如果需要整个工程,请在评论留下邮箱或者私信我邮箱(注意保护隐私)。
  • 自身能力不足,如有错误还请多多指出!

版本信息

文件:V1.0

编号:69

Vivado:无

Modelsim:无

Quartus II:Quartus II 13.1 (64-bit)

相信我,SDRAM真的不难(九)----基于SDRAM缓存的串口传图综合实战(UART + SDRAM + VGA)相关推荐

  1. 相信我,SDRAM真的不难(一)----初识SDRAM

    写在前面 本文是SDRAM系列文章的第一篇,旨在对SDRAM做一个初步的介绍与认识. 其他博文链接:相信我,SDRAM真的不难----汇总篇(电梯直达) 1.什么是SDRAM SDRAM(Synchr ...

  2. 相信我,SDRAM真的不难(二)----初始化操作

    写在前面 本文是SDRAM系列文章的第二篇,对SDRAM的初始化操作进行了详细介绍.代码编写与仿真验证. 其他博文链接:相信我,SDRAM真的不难----汇总篇(电梯直达) 1.初始化 SDRAM 的 ...

  3. 相信我,SDRAM真的不难(四)----写操作(页突发模式)

    写在前面 本文是SDRAM系列文章的第四篇,对SDRAM的突发写操作进行了详细介绍.代码编写与仿真验证. 其他博文链接:相信我,SDRAM真的不难----汇总篇(电梯直达) 1.写操作 SDRAM提供 ...

  4. 基于 DDR3 的串口传图帧缓存系统设计实现(整体设计)

    文章目录 前言 一.串口传图顶层系统设计框图 二.各模块说明 三.系统工程及 IP 创建 四.uart_ddr3_tft模块 五.uart_ddr3_tft模块仿真文件 六.传图展示 前言 结合串口接 ...

  5. 基于 DDR3 的串口传图帧缓存系统设计实现(fifo2mig_axi )

    文章目录 前言 一.接口转换模块设计 二.fifo2mig_axi 模块 二.接口转换模块仿真 四.fifo2mig_axi_tb 五.仿真展示 前言 结合串口接收模块和 tft 显示屏控制模块,设计 ...

  6. 相信我,SDRAM真的不难----汇总篇

  7. 基于DDR3的串口传图帧缓存系统设计实现

    整体设计框图如图所示 模块名称 模块功能 uart_byte_rx模块 负责串口图像数据的接收 bit8_trans_bit16模块 将串口接收的每两个 8bit 数据转换成一个 16bit 数据(图 ...

  8. FPGA uart+sdram+vga传图

    项目介绍 注意事项 这个练手项目本来在七月份就完成了,但是其中有个bug我怎么改也改不了,其中的sdram看了下b站上明德扬的sdram视频,然后借鉴了他们的摄像头采集模块的sdram代码,这里的主要 ...

  9. Jquery真的不难~第八回 JS的闭包问题

    百度百科中对闭包的定义: 闭包是可以包含自由(未绑定到特定对象)变量的代码块:这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义."闭包" 一词来源 ...

最新文章

  1. mlcc激光雷达与相机外参标定初体验
  2. 怎样判断子进程已经结束 process.waitFor();的问题
  3. pytorch 笔记:torch.nn.Conv2d
  4. EL表达式 JSTL(详解)
  5. 【模拟】游戏(jzoj 1614)
  6. 云计算-从基础到应用架“.NET研究”构系列-云计算的演进
  7. 百度BCC云解析配置(新旧文档对比) - (文档篇)
  8. iOS 接入微信 支付宝 参数设置
  9. Linux系统文件管理以及连接文件和inode简介
  10. MFC界面开发帮助文档:BCG可视化设计器使用指南
  11. 蓝宝石rx470d原版bios_狼神矿卡烤机89°C!强刷蓝宝石RX570超白金显卡BIOS降温75°教程...
  12. 【科普】数字货币的基石--区块链
  13. matlab二项式,动态规划 – 计算二项式系统 —MATLAB代码 – 算法网
  14. MySQL 报OperationalError: (1130, “XX‘ is not allowed to connect to this MySQL server“)的正确解决方法
  15. Apache Log4j2 漏洞解决办法
  16. 【考试题解】 递归递推
  17. 智科模式识别期末大课设:多种方法对数据集进行手写数字识别(数据集:MINIST)
  18. DoIP协议从入门到精通系列——车载网络拓扑
  19. DICOM:DICOM万能编辑工具之Sante DICOM Editor
  20. secureCRT,永久设置,保护眼睛,配色方案

热门文章

  1. 陷波滤波器(Notch filter)
  2. 一个不错的源代码语法高亮插件dp.SyntaxHighlighter
  3. 十进制转换十六进制的高低八位合并分解
  4. 什么是动态DNS(DDNS)?
  5. 常犇_《河神2》变味了吗?
  6. 同贺!广西北部湾银行新一代系统群建设项目成功投产上线
  7. 数据库系统概论期末总结(核心考点)
  8. 利用Python发短信
  9. PoE交换机能代替普通工业交换机使用吗?
  10. java swing 多层_java swing布局嵌套