|
本帖最后由 正点原子 于 2021-1-23 15:35 编辑
1)实验平台:正点原子达芬奇FPGA开发板
2)购买链接:https://detail.tmall.com/item.htm?id=624335496505
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_dafenqi.html
4) 正点原子官方B站:https://space.bilibili.com/394620890
5)对正点原子FPGA感兴趣的同学可以加群讨论:905624739 点击加入:
第四十一章以太网传图片(LCD显示)
我们在“基于ROM的VGA图片显示实验”中利用FPGA片上存储资源存储图片,并通过VGA将图片显示到显示器屏幕上。但是由于FPGA片上存储资源有限,只能存储分辨率较小的图片。在本章,我们将学习如何利用以太网传输图片数据,使用DDR3来存储图片,并通过LCD接口显示。
本章包括以下几个部分:
4141.1简介
41.2实验任务
41.3硬件设计
41.4程序设计
41.5下载验证
41.1简介
利用LCD接口显示图片时,需要一个存储器用于存储图片数据。这个存储器可以采用FPGA片上存储资源,也可以使用片外存储设备,如DDR3、SD卡、FLASH等。
由于FPGA的片上存储资源有限,所以能够存储的图片大小也受到限制。达芬奇开发板上的FPGA芯片型号为XC7A35T-2FGG484C,它的片上存储资源为1.8Mbit,也就是说存储的图片大小不能超过1.8Mbit。对于分辨率为800*480的图片,当采用RGB565数据格式时,所需要的存储空间为800*480*16bit=6144000bit=6000Kbit=5.9 Mbit(1Kbit=1024bit,1 Mbit =1024 Kbit)。也就是说,如果是采用LCD显示分辨率800*480,FPGA的片上存储资源也远远不能够满足图片存储的需求。
达芬奇开发板上的DDR3存储容量为2048Mbit,大约可以存储340张上述格式的图片。另外,相比于开发板上的SD卡、FLASH等片外存储设备,DDR3还具有读写速度快的优点。因此在利用LCD显示图片时,DDR3是一种非常理想的存储设备。然而DDR3不像SD卡一样可以事先将图片导入,作为易失性存储器中的一种,DDR3中的数据在掉电后会丢失,因此用于LCD图片显示时需要向DDR3中写入图片数据。这里我们采用开发板上的网口接收上位机发送的图片数据,并将其写入DDR3,然后读出数据,最终通过LCD接口驱动LCD屏显示。
我们在“OV7725摄像头RGB-LCD显示”中对DDR3控制器作了详细的介绍,包括DDR3读写控制、寻址方法、用户接口等等。如果大家对这部分内容不是很熟悉的话,请参考“OV7725摄像头RGB-LCD显示”中的DDR3控制器部分的程序讲解。
41.2实验任务
本节实验任务是使用达芬奇开发板上的网口接收上位机传输的图片(分辨率为800*480),然后将图片存储在DDR3中并通过LCD接口在LCD屏幕上显示,支持4.3寸480*272、4.3寸800*480、7寸800*480、7寸1024*600、10.1寸1280*800这些尺寸/分辨率的屏幕。
41.3硬件设计
RGB TFT-LCD接口部分的硬件设计请参考“RGB TFT-LCD彩条显示实验”中的硬件设计部分。以太网原理图请参考“MDIO 接口读写测试实验”中的硬件设计部分,DDR3原理图则参考“DDR3读写测试”中的硬件设计部分。由于以太网、LCD接口和DDR3引脚数目较多且在前面相应的章节中已经给出它们的管脚列表,这里不再列出管脚分配。
41.4程序设计
下图是根据本章实验任务画出的系统框图。上位机通过网线将大小固定为800*480的图片以bin文件格式传输到开发板上,以太网顶层模块负责接收图片数据并且实现ARP功能,位宽转换模块负责将太网UDP模块接收到的32bit的数据转成16bit,并通过DDR3控制器存入DDR3中。当使用到4.3寸480*272的屏幕时,由于屏幕大小小于图片,我们将对图片进行裁剪后存入DDR3。LCD顶层模块从DDR3控制器读取DDR3中存储的图片数据并通过LCD接口显示在LCD屏上,另外当遇到屏幕大小大于图片大小时,需要在屏幕四周实现黑边填充。
图 41.4.1 系统框图
顶层模块的原理图如下图所示:
图 41.4.2 顶层模块原理图
FPGA顶层(eth_ddr3_lcd)例化了以下六个模块:时钟模块(clk_wiz_0)、以太网顶层模块(eth_top)、图片裁剪模块(picture_tailor)、32bit转16bit模块(udp_32_to_16bit)、DDR3控制器模块(ddr3_top)以及LCD顶层驱动模块(lcd_rgb_top)。
时钟模块(clk_wiz_0):该时钟模块产生两个时钟,分别是50M和200M,其中50M时钟供LCD顶层驱动模块使用,200M时钟供太网顶层模块和DDR3控制器模块使用。
以太网顶层模块(eth_top):太网顶层内部例化ARP顶层和UDP顶层,ARP主要是用来实现ARP协议的,避免用户每次使用以太网通信都去手动绑定IP,具体可以看以“以太网ARP测试”章节,UDP顶层则用来实现以太网通信的数据收发功能,该模块下面包含了以太网UDP接收模块(udp_rx)、以太网UDP发送模块(udp_tx)和CRC32校验模块(crc32_d8)。由于本章实验中网口只负责接收数据,所以以太网的UDP发送模块功能并没有用到。有关该UDP模块的详细介绍请大家参考“以太网UDP测试实验”章节。
裁剪模块(picture_tailor):裁剪模块用于把固定分辨率(800*480)的图片裁剪成大小为480*272的图片然后送到DDR3储存,从而适配正点原子480*272的LCD屏幕。
32bit转16bit模块(udp_32_to_16bit):该模块将UDP模块接收到的位宽为32bit的数据转换成16bit图像数据,这是因为图像数据需要写入DDR3,而DDR3控制器的图像数据接口位宽为16bit。
DDR3控制器模块(ddr3_top):DDR3控制器模块负责驱动DDR3片外存储器。该模块将DDR3复杂的读写操作封装成类似FIFO的用户接口,非常方便用户的使用。有关该模块的详细介绍请大家参考“OV7725摄像头RGB-LCD显示”章节。
LCD顶层模块(lcd_rgb_top):LCD顶层驱动模块根据LCD时序参数输出行、场同步信号;同时它还要输出数据请求信号用于读取DDR3中的图片数据,并将图片通过LCD接口显示出来。另外当遇到大于800*480的屏幕时,需要实现屏幕四周的黑边填充。
顶层模块的代码如下:
- 1 module eth_ddr3_lcd(
- 2 input sys_clk , //FPGA外部时钟,50MHz
- 3 input rst_n , //按键复位,低电平有效
- 4 //以太网接口
- 5 input eth_rxc , //RGMII接收数据时钟
- 6 input eth_rx_ctl , //RGMII输入数据有效信号
- 7 input [3:0] eth_rxd , //RGMII输入数据
- 8 output eth_txc , //RGMII发送数据时钟
- 9 output eth_tx_ctl , //RGMII输出数据有效信号
- 10 output [3:0] eth_txd , //RGMII输出数据
- 11 output eth_rst_n , //以太网芯片复位信号,低电平有效
- 12 // DDR3
- 13 inout [15:0] ddr3_dq , //DDR3 数据
- 14 inout [1:0] ddr3_dqs_n , //DDR3 dqs负
- 15 inout [1:0] ddr3_dqs_p , //DDR3 dqs正
- 16 output [13:0] ddr3_addr , //DDR3 地址
- 17 output [2:0] ddr3_ba , //DDR3 banck 选择
- 18 output ddr3_ras_n , //DDR3 行选择
- 19 output ddr3_cas_n , //DDR3 列选择
- 20 output ddr3_we_n , //DDR3 读写选择
- 21 output ddr3_reset_n , //DDR3 复位
- 22 output [0:0] ddr3_ck_p , //DDR3 时钟正
- 23 output [0:0] ddr3_ck_n , //DDR3 时钟负
- 24 output [0:0] ddr3_cke , //DDR3 时钟使能
- 25 output [0:0] ddr3_cs_n , //DDR3 片选
- 26 output [1:0] ddr3_dm , //DDR3_dm
- 27 output [0:0] ddr3_odt , //DDR3_odt
- 28 //lcd接口
- 29 output lcd_hs , //LCD 行同步信号
- 30 output lcd_vs , //LCD 场同步信号
- 31 output lcd_de , //LCD 数据输入使能
- 32 inout [23:0] lcd_rgb , //LCD 颜色数据
- 33 output lcd_bl , //LCD 背光控制信号
- 34 output lcd_rst , //LCD 复位信号
- 35 output lcd_pclk //LCD 采样时钟
- 36 );
- 37 //parameter define
- 38 //开发板MAC地址 00-11-22-33-44-55
- 39 parameter BOARD_MAC = 48'h00_11_22_33_44_55;
- 40 //开发板IP地址 192.168.1.10
- 41 parameter BOARD_IP = {8'd192,8'd168,8'd1,8'd10};
- 42 //目的MAC地址 ff_ff_ff_ff_ff_ff
- 43 parameter DES_MAC = 48'hff_ff_ff_ff_ff_ff;
- 44 //目的IP地址 192.168.1.102
- 45 parameter DES_IP = {8'd192,8'd168,8'd1,8'd102};
- 46 //输入数据IO延时,此处为0,即不延时(如果为n,表示延时n*78ps)
- 47 parameter IDELAY_VALUE = 0;
- 48
- 49 //wire define
- 50 wire gmii_rx_clk ; //以太网125M时钟
- 51 wire clk_50m ; //50mhz时钟,提供给lcd驱动时钟
- 52 wire locked ; //时钟锁定信号
- 53 wire rdata_req ; //DDR3控制器模块读使能
- 54 wire [15:0] rd_data ; //DDR3控制器模块读数据
- 55 wire init_calib_complete ; //DDR3初始化完成init_calib_complete
- 56 wire sys_init_done ; //系统初始化完成(DDR初始化+摄像头初始化)
- 57 wire clk_200m ; //ddr3参考时钟
- 58 wire [27:0] app_addr_rd_min ; //读DDR3的起始地址
- 59 wire [27:0] app_addr_rd_max ; //读DDR3的结束地址
- 60 wire [7:0] rd_bust_len ; //从DDR3中读数据时的突发长度
- 61 wire [27:0] app_addr_wr_min ; //写DDR3的起始地址
- 62 wire [27:0] app_addr_wr_max ; //写DDR3的结束地址
- 63 wire [7:0] wr_bust_len ; //从DDR3中写数据时的突发长度
- 64 wire lcd_clk ; //分频产生的LCD 采样时钟
- 65 wire [12:0] h_disp ; //LCD屏水平分辨率
- 66 wire [12:0] v_disp ; //LCD屏垂直分辨率
- 67 wire [10:0] h_pixel ; //存入ddr3的水平分辨率
- 68 wire [10:0] v_pixel ; //存入ddr3的屏垂直分辨率
- 69 wire [15:0] lcd_id ; //LCD屏的ID号
- 70 wire [27:0] ddr3_addr_max ; //存入DDR3的最大读写地址
- 71 wire i2c_rh_wl ; //I2C读写控制信号
- 72 wire [7:0] i2c_data_r ; //I2C读数据
- 73 wire [12:0] total_h_pixel ; //水平总像素大小
- 74 wire [12:0] total_v_pixel ; //垂直总像素大小
- 75 wire [11:0] pixel_ypos;
- 76 wire [11:0] pixel_xpos;
- 77 wire sys_rst_n ; //系统复位信号
- 78 wire [31:0] rec_data ; //以太网接收的数据
- 79
- 80 wire [15:0] picture_data ; //图片数据
- 81 wire picture_data_vld ; //图片数据有效使能信号
- 82 wire picture_data_vld0 ; //800x480图片像素有效使能信号
- 83 wire picture_data_vld1 ; //480x272图片像素有效使能信号
- 84 wire data_req_big ; //非480x272屏的LCD请求信号
- 85 wire data_req_small ; //480x272LCD屏请求信号
- 86
- 87 //*****************************************************
- 88 //** main code
- 89 //*****************************************************
- 90 //待PLL输出稳定之后,停止系统复位
- 91 assign sys_rst_n = rst_n & locked;
- 92 //系统初始化完成:DDR3初始化完成
- 93 assign sys_init_done = init_calib_complete;
- 94 assign ddr3_addr_max =(lcd_id == 16'h4342)? 130560:384000; //存入ddr3的最大读写地址
- 95 assign h_disp = (lcd_id == 16'h4342) ? 480:800; //存入ddr3的一行数据(突发长度)
- 96
- 97 //时钟产生模块
- 98 clk_wiz_0 u_clk_wiz_0
- 99 (
- 100 // Clock out ports
- 101 .clk_out1 (clk_200m),
- 102 .clk_out2 (clk_50m),
- 103 // Status and control signals
- 104 .reset (1'b0),
- 105 .locked (locked),
- 106 // Clock in ports
- 107 .clk_in1 (sys_clk)
- 108 );
- 109
- 110 //480x272屏图片裁剪模块
- 111 picture_tailor u_picture_tailor(
- 112 .gmii_rx_clk (gmii_rx_clk) , //图片输入像素时钟
- 113 .sys_rst_n (sys_rst_n) , //复位信号
- 114
- 115 .picture_data_vld0 (picture_data_vld0) , //800x480图片像素有效使能信号
- 116 .picture_data_vld1 (picture_data_vld1) //480x272图片像素有效使能信号
- 117 );
- 118
- 119 //DDR3控制器顶层模块
- 120 ddr3_top u_ddr3_top (
- 121 .clk_200m (clk_200m), //DDR3输入时钟
- 122 .sys_rst_n (sys_rst_n), //复位,低有效
- 123 .sys_init_done (sys_init_done), //系统初始化完成
- 124 .init_calib_complete (init_calib_complete), //ddr3初始化完成信号
- 125 //ddr3接口信号
- 126 .app_addr_rd_min (28'd0), //读DDR3的起始地址
- 127 .app_addr_rd_max (ddr3_addr_max[27:0]), //读DDR3的结束地址
- 128 .rd_bust_len (h_disp[10:3]), //从DDR3中读数据时的突发长度
- 129 .app_addr_wr_min (28'd0), //写DDR3的起始地址
- 130 .app_addr_wr_max (ddr3_addr_max[27:0]), //写DDR3的结束地址
- 131 .wr_bust_len (h_disp[10:3]), //从DDR3中写数据时的突发长度
- 132 // DDR3 IO接口
- 133 .ddr3_dq (ddr3_dq), //DDR3 数据
- 134 .ddr3_dqs_n (ddr3_dqs_n), //DDR3 dqs负
- 135 .ddr3_dqs_p (ddr3_dqs_p), //DDR3 dqs正
- 136 .ddr3_addr (ddr3_addr), //DDR3 地址
- 137 .ddr3_ba (ddr3_ba), //DDR3 banck 选择
- 138 .ddr3_ras_n (ddr3_ras_n), //DDR3 行选择
- 139 .ddr3_cas_n (ddr3_cas_n), //DDR3 列选择
- 140 .ddr3_we_n (ddr3_we_n), //DDR3 读写选择
- 141 .ddr3_reset_n (ddr3_reset_n), //DDR3 复位
- 142 .ddr3_ck_p (ddr3_ck_p), //DDR3 时钟正
- 143 .ddr3_ck_n (ddr3_ck_n), //DDR3 时钟负
- 144 .ddr3_cke (ddr3_cke), //DDR3 时钟使能
- 145 .ddr3_cs_n (ddr3_cs_n), //DDR3 片选
- 146 .ddr3_dm (ddr3_dm), //DDR3_dm
- 147 .ddr3_odt (ddr3_odt), //DDR3_odt
- 148 //用户
- 149 .ddr3_read_valid (1'b1), //DDR3 读使能
- 150 .ddr3_pingpang_en (1'b0), //DDR3 乒乓操作使能
- 151 .wr_clk (gmii_rx_clk), //写时钟
- 152 .wr_load (), //输入源更新信号
- 153 .datain_valid (picture_data_vld), //数据有效使能信号
- 154 .datain (picture_data), //有效数据
- 155 .rd_clk (lcd_clk), //读时钟
- 156 .rd_load (rd_vsync), //输出源更新信号
- 157 .dataout (rd_data), //rfifo输出数据
- 158 .rdata_req (rdata_req) //请求数据输入
- 159 );
- 160 //LCD 顶层
- 161 lcd_rgb_top u_lcd_rgb_top(
- 162 .sys_clk (clk_50m ), //时钟
- 163 .sys_rst_n (sys_rst_n ), //复位
- 164 .sys_init_done (sys_init_done), //初始化完成
- 165 //lcd接口
- 166 .lcd_id (lcd_id), //LCD屏的ID号
- 167 .lcd_hs (lcd_hs), //LCD 行同步信号
- 168 .lcd_vs (lcd_vs), //LCD 场同步信号
- 169 .lcd_de (lcd_de), //LCD 数据输入使能
- 170 .lcd_rgb (lcd_rgb), //LCD 颜色数据
- 171 .lcd_bl (lcd_bl), //LCD 背光控制信号
- 172 .lcd_rst (lcd_rst), //LCD 复位信号
- 173 .lcd_pclk (lcd_pclk), //LCD 采样时钟
- 174 .lcd_clk (lcd_clk), //LCD 驱动时钟
- 175 //用户接口
- 176 .out_vsync (rd_vsync), //lcd场信号
- 177 .h_disp (), //行分辨率 h_disp
- 178 .v_disp (), //场分辨率 v_disp
- 179 .pixel_xpos (pixel_xpos),
- 180 .pixel_ypos (pixel_ypos),
- 181 .data_in (rd_data), //rfifo输出数据
- 182 .data_req (rdata_req), //请求数据输入
- 183 .data_req_big (data_req_big), //非480x272屏的LCD请求信号
- 184 .data_req_small (data_req_small) //480x272LCD屏请求信号
- 185 );
- 186
- 187 //以太网顶层
- 188 eth_top #(
- 189 .BOARD_MAC (BOARD_MAC), //开发板MAC地址
- 190 .BOARD_IP (BOARD_IP), //开发板IP地址
- 191 .DES_MAC (DES_MAC), //目的MAC地址
- 192 .DES_IP (DES_IP) //目的IP地址
- 193 )
- 194 u_eth_top(
- 195 .clk_200m (clk_200m) , //DDR3输出时钟
- 196 .sys_rst_n (sys_rst_n) , //系统复位信号,低电平有效
- 197 .eth_rxc (eth_rxc) , //RGMII接收数据时钟
- 198 .eth_rx_ctl (eth_rx_ctl), //RGMII输入数据有效信号
- 199 .eth_rxd (eth_rxd) , //RGMII输入数据
- 200 .eth_txc (eth_txc) , //RGMII发送数据时钟
- 201 .eth_tx_ctl (eth_tx_ctl), //RGMII输出数据有效信号
- 202 .eth_txd (eth_txd) , //RGMII输出数据
- 203 .eth_rst_n (eth_rst_n) , //以太网芯片复位信号,低电平有效
- 204
- 205 .rec_en (rec_en), //以太网32位图片数据接收完成
- 206 .rec_data (rec_data), //以太网32位数据
- 207 .gmii_rx_clk (gmii_rx_clk) //以太网125M时钟
- 208 );
- 209
- 210 //以太网32位图片数像素据转16位
- 211 udp_32_to_16bit u_udp_32_to_16bit(
- 212 .clk (gmii_rx_clk), //时钟信号
- 213 .rst_n (sys_rst_n), //复位信号,低电平有效
- 214
- 215 .rec_en (rec_en), //UDP接收的数据使能信号
- 216 .picture_data_vld0 (picture_data_vld0), //非480x272屏图片像素有效
- 217 .picture_data_vld1 (picture_data_vld1), //480x272屏图片像素有效
- 218 .lcd_id (lcd_id), //LCD屏ID
- 219 .picture_data_vld (picture_data_vld), //图片像素有效
- 220 .rec_data (rec_data), //以太网32位图片像素数据
- 221 .picture_data (picture_data) //以太网16位图片像素数据
- 222 );
- 223 endmodule
复制代码
顶层模块主要实现了对各个子模块的例化,包括前文所提到的六个模块,还包括参数定义。绝大部分的内容我们在“OV772摄像头RGB-LCD显示实验”中有详细介绍。我们只介绍新添加的。
代码第78行,信号rec_data是从以太网模块直接接收到的32bit图片像素数据,本章没有进行缓存处理,注意它是gmii_rx_clk时钟域下的数据。
代码第80行,信号picture_data是欲存入DDR3的图片像素数据,包括裁剪的或者非裁剪的图片数据,16bit。
代码第81行,picture_data_vld,图片数据有效使能信号,该信号和picture_data对应,该信号拉高表明当前时钟下的picture_data是有效的。
代码第82行,picture_data_vld0,图片数据有效使能信号,该信号和picture_data_vld的区别是,它只是原始图片(800*480)数据的有效使能标记。
代码第82行,picture_data_vld1,图片数据有效使能信号,它是裁剪处理后图片(480*272)的有效使能标记。
以上信号的产生将在下文详细介绍。
代码第94行,定义了图片数据存入DDR3的最大地址。当遇到屏幕大小比图片大小(800*480)小的屏幕,也就是lcd_id等16'h4342时,我们需要对图片进行裁剪,此时的DDR3的最大地址就是按照实际屏幕大小来算的,也就是480*272等于130560,其他屏幕不需要裁剪,所以存入的地址是图片的大小都是800*480等于384000。
代码第95行,定义DDR3一次突发读写的长度,我们取所存入图片的一行作为突发长度,显然,对于lcd_id等16'h4342屏幕,存入DDR3的图片大小是480*272,所以突发长度我们取480,其他屏幕则选择800。
下文我们来介绍各个子模块,只介绍改动的部分。
首先是以太网顶层(eth_top),该部分代码我们基本移植了“以太网UDP测试实验”,我们所做的改动是去掉了FIFO缓存,直接把以太网UDP接收到的数据送给DDR3控制器模块,DDR3控制器有足够的速度和带宽接收这些数据,并不需额外的缓存。另外由于我们不需要发送以太网UDP数据,只是做一次性接收,所以发送模块并不需要。
图片裁剪(picture_tailor)模块,该模块是我们新加入的模块,实现把上位机发来的(800*480)的图片裁剪成适合小屏幕(480*272)显示的图片。实现方式有别于“OV772摄像头RGB-LCD显示实验”,但是异曲同工。下面来具体看代码。
- 1 module picture_tailor(
- 2 input gmii_rx_clk , //像素数据时钟
- 3 input sys_rst_n , //复位信号
- 4 //用户接口
- 5 input picture_data_vld0 , //整张图片数据有效使能信号
- 6 output picture_data_vld1 //裁剪后图片数据有效使能信号
- 7 );
- 8
- 9 //reg define
- 10 reg [10:0] picture_data_cntx; //整张图片横向像素点坐标
- 11 reg [10:0] picture_data_cnty; //整张图片纵向像素点坐标
- 12
- 13 //*****************************************************
- 14 //** main code
- 15 //*****************************************************
- 16
- 17 //产生裁剪后图片数据有效信号
- 18 assign picture_data_vld1 = ((160 <= picture_data_cntx) && (picture_data_cntx< 640)
- 19 && (104 <= picture_data_cnty) && (picture_data_cnty < 376)
- 20 && picture_data_vld0)?1'b1:1'b0;
- 21
- 22 //产生(800*480)图片横向像素点坐标
- 23 always@ (posedge gmii_rx_clk or negedge sys_rst_n) begin
- 24 if(!sys_rst_n)
- 25 picture_data_cntx <= 11'd0;
- 26 else if(picture_data_vld0)begin
- 27 if(picture_data_cntx == 800 - 1'b1)
- 28 picture_data_cntx <= 11'd0;
- 29 else
- 30 picture_data_cntx <= picture_data_cntx + 1'b1;
- 31 end
- 32 end
- 33
- 34 //产生(800*480)图片纵向像素点坐标
- 35 always@ (posedge gmii_rx_clk or negedge sys_rst_n) begin
- 36 if(!sys_rst_n)
- 37 picture_data_cnty <= 11'd0;
- 38 else begin
- 39 if(picture_data_cntx == 800 - 1'b1) begin
- 40 if(picture_data_cnty ==480 - 1'b1)
- 41 picture_data_cnty <= 11'd0;
- 42 else
- 43 picture_data_cnty <= picture_data_cnty + 1'b1;
- 44 end
- 45 end
- 46 end
- 47
- 48 endmodule
复制代码
该模块实际上不是直接对图片数据进行裁剪,而是裁剪数据有效使能信号,把“picture_data_vld0”裁剪成“picture_data_vld1”由于图片像素数据需要配合数据有效使能信号才能写入下一个DDR3模块,所以也就相当于裁剪了图片数据。
代码第18行到20行,产生裁剪后图片数据有效使能信号picture_data_vld1。原理不难理解,用原图片的像素坐标和四个固定值相比较,这四个固定值实际上就是裁剪后图片(480*272)的四个顶点,在坐标范围之内picture_data_vld1的值等于1,否则等于0。实质上就是从(800*480)的大图片中间抠出(480*272)的这张小图片。这四个顶点的值计算并如下。
左上顶点:160,等于(800-480)/2,
右上顶为:640,等于(800-480)/2+480,
左上顶点:104,等于(480-272)/2,
右上顶为:376,等于480-272)/2+272,
注意这里除了去比较坐标外还需要与上picture_data_vld0,因为只有picture_data_vld0有效时picture_data_vld1才可能有效,其他时候是无效的。
代码第23到32行,产生(800*480)图片横向像素点坐标,这里使用picture_data_vld0作为使能信号。代码第35到46行,产生(800*480)图片纵向素点坐标。
接着看32bit转16bit模块(udp_32_to_16bit)
- 1 module udp_32_to_16bit(
- 2 input clk, //时钟信号
- 3 input rst_n, //复位信号,低电平有效
- 4
- 5 input rec_en, //UDP接收的数据使能信号
- 6 output picture_data_vld0, //以太网800*480图片像素数据有效使能信号
- 7 input picture_data_vld1, //裁剪后480*272像素数据有效使能信号
- 8 input [15:0] lcd_id, //LCD ID
- 9 output picture_data_vld, //写入DDR3图片像素数据有效使能信号
- 10 input [31:0] rec_data, //以太网图片32位像素数据
- 11 output [15:0] picture_data //以太网图片16位像素数据
- 12 );
- 13 //wire define
- 14 wire [15:0] picture_data1 ; //以太网图片像素高16数据
- 15 wire [15:0] picture_data2 ; //以太网图片像素低16数据
- 16 //reg define
- 17 reg rec_en_d0; //接收数据有效延迟一拍
- 18
- 19 //*****************************************************
- 20 //** main code
- 21 //*****************************************************
- 22
- 23 //判断屏幕如果是480x272则将裁剪的图像数据有效使能赋值给像素数据有效信号,否则将不裁剪
- 24 //的图像数据有效使能赋给像素数据有效信号
- 25 assign picture_data_vld = (lcd_id==16'h4342)? picture_data_vld1:picture_data_vld0;
- 26 //产生以太网800*480图片像素数据有效使能信号,需要持续两个时钟周期
- 27 assign picture_data_vld0 = ((rec_en==1) ||(rec_en_d0==1 ))? 1'b1:1'b0;
- 28 //给以太网图片像素高16数据赋值
- 29 assign picture_data1 = rec_data[31:16];
- 30 //给以太网图片像素低16数据赋值
- 31 assign picture_data2 = rec_data[15:0];
- 32 //分别将太网图片像素高低16位赋值给下游数据
- 33 assign picture_data = rec_en? picture_data1:picture_data2;
- 34
- 35 //接收数据有效信号寄存一拍
- 36 always @(posedge clk or negedge rst_n) begin
- 37 if(~rst_n)begin
- 38 rec_en_d0 <=0;
- 39 end
- 40 else begin
- 41 rec_en_d0 <= rec_en;
- 42 end
- 43 end
- 44
- 45 endmodule
复制代码
该模块主要负责把以太网接收的32位UDP数据转为16位,同时根据不同的大小的屏幕产生16位数据有效使能信号。
代码第25行,产生写入DDR3的16bit使能信号,具体做法是判断lcd_id等于16'h4342时,此时将“picture_data_vld1”赋值给“picture_data_vld”,也就是此时使能裁剪图片的数据有效,反之,将“picture_data_vld0” 赋值给“picture_data_vld”,也就是使能完整(不裁剪)的图片数据。
代码第27行,产生800*480图片像素数据有效使能信号“picture_data_vld0”,以太网UDP接收到的32bit数据和有效信号rec_en是同步对应的,但是只占用了一个时钟,我们需要分前后两个时钟把32bit数据拆分成两个16bit传输出去,所以使能需要持续两个时钟进行,所以就有了((rec_en==1) ||(rec_en_d0==1 ))这个条件。
代码第29到31行,将32bit数据拆分成两个16bit数据。
代码第33行,分两个时钟先后把高低16bit赋值给图像像素picture_data。
代码第36行开始的always模块,对rec_en信号寄存一拍。
至此,我们代码讲解完毕。
41.5下载验证
用FPC排线将ATK-7 RGBLCD模块与开发板连接,然后将网线一端连接电脑网口,另一端与开发板上的网口连接;将下载器一端连电脑,另一端与开发板上对应端口连接,最后连接电源线并打开电源开关。
图 41.5.1 达芬奇开发板硬件连接
打开工程,并下载程序,程序下载完成后,由于还没有向DDR3中写入图片数据,所以DDR3中的数据是随机的,此时LCD显示器上显示的像素点颜色是杂乱无章的。接下来我们在上位机中将图片通过网口下载到开发板上的DDR3中。在此之前我们首先准备好图片,并制作成bin格式。具体操作如下:
打开工具Img2Lcd,该工具位于开发板所随附的资料“6_软件资料/1_软件/Img2Lcd”目录下,找到“Img2Lcd.exe”并双击打开,如下图所示:
图 41.5.2 Img2Lcd工具使用界面
点击“打开”,导入一幅分辨率为800*480的jpg格式图片,按照上图进行参数设置,可以看到我们输出的图像大小也800*480,确定后是并导出bin文件。在弹出的界面中选择bin文件的保存路径并输入文件名。
到这里我们已经成功地将图片转成了bin文件,接下来需要借助网口调试助手将bin文件下载到开发板中。但是需要注意的是,由于发送的文件较大,需要先对网络适配器属性进行配置。
打开设备管理器,然后在网络适配器中找到网卡并右键选择属性,如下图所示。
图 41.5.3 网络适配器界面
在属性界面中选择“高级”,在属性栏中找到“巨型帧(Jumbo Frame)”,然后在右侧值中选择“9KB MTU”,最后点击确定,如下图所示。
图 41.5.4 巨型帧设置
默认情况下,以太网的MTU(Maximum Transmission Unit,最大传输单元)是1500字节。而巨型帧的设置把以太网帧格式的数据段扩展到了9KB,从而在传输大文件时减少了数据帧的个数,减轻了网络设备处理帧头的额外开销,从而提高网络传输性能。
注意有时候可能找不到巨型帧选项,原因是网络适配器驱动软件过于老旧的问题,此时我们只需要更新驱动即可。还是在设备管理器的界面,操作如下:
图 41.5.5更新驱动
最后我们通过网络调试助手发送图片bin文件,如下图所示:
图 41.5.6 网络调试助手发送bin文件
网络调试助手的设置和前面的以太网通信相关例程一样,在远程主机一栏选择开发板的IP地址和端口号“192.168.1.102 :1234”;然后在发送区设置一栏勾选“启用文件数据源”,在弹出的界面中选择刚刚生成的bin文件;最后点击右下角的“发送”按钮,即可将由图片生成的bin文件通过网线发送到达芬奇开发板。
图片发送完成后液晶屏上显示的图片如下所示,说明基于DDR3的LCD显示实验下载验证成功:
图 41.5.7 LCD液晶屏显示的图片
|
|