OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 10018|回复: 0
打印 上一主题 下一主题

[XILINX] 【正点原子FPGA连载】第四十八章以太网传图片(LCD 显示)--摘自【正点原子】超越者之FPGA开发指南

[复制链接]

1308

主题

1324

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
5598
金钱
5598
注册时间
2019-5-8
在线时间
1493 小时
跳转到指定楼层
楼主
发表于 2021-1-25 16:36:08 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
1)实验平台:正点原子超越者FPGA开发板
2)平台购买地址:https://item.taobao.com/item.htm?&id=631660290421
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz-chaoyuezhe.html
4)本章实例源码下载: 41_eth_ddr3_lcd.rar (7.79 MB, 下载次数: 6)


5)正点原子FPGA交流群:905624739点击加入:
6)关注正点原子公众号,获取最新资料更新




第四十八章以太网传图片(LCD 显示)




我们在“基于 ROM 的LCD图片显示实验”中利用 FPGA 片上存储资源存储图片,并通过 LCD接口 将图片显示到LCD屏幕上。但是由于 FPGA 片上存储资源有限,只能存储分辨率较小的图片。在本章,我们将学习如何利用以太网传输图片数据,使用 DDR3 来存储图片,并通过 LCD 接口显示。
本章包括以下几个部分:
4848.1简介
48.2实验任务
48.3硬件设计
48.4程序设计
48.5下载验证

48.1简介
利用 LCD 接口显示图片时,需要一个存储器用于存储图片数据。这个存储器可以采用 FPGA 片上存储资源,也可以使用片外存储设备,如 DDR3、SD 卡、FLASH 等。由于 FPGA 的片上存储资源有限,所以能够存储的图片大小也受到限制。超越者开发板上的 FPGA 芯片型号为 XC6SLX16,它的片上存储资源为576Kbit,也就是说存储的图片大小不能超过576Kbit。对于分辨率为 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 屏显示。
48.2实验任务
本节实验任务是使用超越者开发板上的网口接收上位机传输的图片(分辨率为 800*480),然后将图片存储在 DDR3 中并通过 LCD 接口在 LCD 屏幕上显示,支持 4.3 寸 480*272、 4.3 寸 800*480、 7 寸 800*480、7 寸 1024*600、 10.1 寸 1280*800 这些尺寸/分辨率的屏幕。
48.3硬件设计
RGB TFT-LCD接口部分的硬件设计请参考“RGB TFT-LCD彩条显示实验”中的硬件设计部分。以太网原理图请参考“MDIO 接口读写测试实验”中的硬件设计部分,DDR3原理图则参考“DDR3读写测试”中的硬件设计部分。由于以太网、LCD接口和DDR3引脚数目较多且在前面相应的章节中已经给出它们的管脚列表,这里不再列出管脚分配。
48.4程序设计
下图是根据本章实验任务画出的系统框图。上位机通过网线将大小固定为800*480的图片以bin文件格式传输到开发板上,以太网顶层模块负责接收图片数据并且实现ARP功能,位宽转换模块负责将太网UDP模块接收到的32bit的数据转成16bit,并通过DDR3控制器存入DDR3中。当使用到4.3寸480*272的屏幕时,由于屏幕大小小于图片,我们将对图片进行裁剪后存入DDR3。LCD顶层模块从DDR3控制器读取DDR3中存储的图片数据并通过LCD接口显示在LCD屏上,另外当遇到屏幕大小大于图片大小时,需要在屏幕四周实现黑边填充。


图 48.4.1 顶层系统框图

由上面的框图可知FPGA顶层(eth_ddr3_lcd)例化了以下六个模块:时钟模块(pll)、以太网顶层模块(eth_udp_loop)、图片裁剪模块(picture_tailor)、32bit转16bit模块(udp_32_to_16bit)、DDR3控制器模块(ddr3_control)以及LCD顶层模块(lcd_rgb_top)。
时钟模块(pll):该时钟模块产生3个时钟,分别是50M、100M和300M,其中50M时钟和100M时钟供LCD顶层模块使用,300M时钟供DDR3控制器模块使用。
以太网顶层模块(eth_udp_loop):太网顶层内部例化ARP顶层、UDP顶层和GMII接口转RGMII接口模块,ARP主要是用来实现ARP协议的,避免用户每次使用以太网通信都去手动绑定IP,具体可以看以“以太网ARP测试”章节,UDP顶层则用来实现以太网通信的数据收发功能,该模块下面包含了以太网UDP接收模块(udp_rx)、以太网UDP发送模块(udp_tx)和CRC32校验模块(crc32_d8)。有关该UDP模块的详细介绍请大家参考“以太网UDP测试实验”章节。GMII接口转RGMII接口模块主要是将GMII接口转化为RGMII接口。
裁剪模块(picture_tailor):裁剪模块用于把固定分辨率(800*480)的图片裁剪成大小为480*272的图片然后送到DDR3储存,从而适配正点原子480*272的LCD屏幕。
32bit转16bit模块(udp_32_to_16bit):该模块将UDP模块接收到的位宽为32bit的数据转换成16bit图像数据,这是因为图像数据需要写入DDR3,而DDR3控制器的图像数据接口位宽为16bit。
DDR3控制器模块(ddr3_control):DDR3控制器模块负责驱动DDR3片外存储器。该模块将DDR3复杂的读写操作封装成类似FIFO的用户接口,非常方便用户的使用。有关该模块的详细介绍请大家参考“DDR读写测试实验”章节。
LCD顶层模块(lcd_rgb_top):LCD顶层驱动模块根据LCD时序参数输出行、场同步信号;同时它还要输出数据请求信号用于读取DDR3中的图片数据,并将图片通过LCD接口显示出来。另外当遇到大于800*480的屏幕时,需要实现屏幕四周的黑边填充。
顶层模块的代码如下:
  1. 1   module eth_ddr3_lcd(
  2. 2       input                  sys_clk     ,    //FPGA外部时钟,50MHz
  3. 3       input                  rst_n       ,    //按键复位,低电平有效
  4. 4       //以太网接口                           
  5. 5       input                  eth_rxc     ,    //RGMII接收数据时钟
  6. 6       input                  eth_rx_ctl  ,    //RGMII输入数据有效信号
  7. 7       input       [3:0]      eth_rxd     ,    //RGMII输入数据
  8. 8       output                 eth_txc     ,    //RGMII发送数据时钟   
  9. 9       output                 eth_tx_ctl  ,    //RGMII输出数据有效信号
  10. 10      output      [3:0]      eth_txd     ,    //RGMII输出数据         
  11. 11      output                 eth_rst_n   ,    //以太网芯片复位信号,低电平有效   
  12. 12    //ddr3 port
  13. 13      inout   [15:0]         mcb3_dram_dq,     
  14. 14      output  [13:0]         mcb3_dram_a,      
  15. 15      output  [2:0]          mcb3_dram_ba,     
  16. 16      output                 mcb3_dram_ras_n,  
  17. 17      output                 mcb3_dram_cas_n,   
  18. 18      output                 mcb3_dram_we_n,   
  19. 19      output                 mcb3_dram_reset_n,
  20. 20      output                 mcb3_dram_cke,   
  21. 21      output                 mcb3_dram_dm,     
  22. 22      inout                  mcb3_dram_udqs,   
  23. 23      inout                  mcb3_dram_udqs_n,
  24. 24      inout                  mcb3_rzq,         
  25. 25      inout                  mcb3_zio,         
  26. 26      output                 mcb3_dram_udm,   
  27. 27      inout                  mcb3_dram_dqs,   
  28. 28      inout                  mcb3_dram_dqs_n,  
  29. 29      output                 mcb3_dram_ck,     
  30. 30      output                 mcb3_dram_ck_n,      
  31. 31                             
  32. 32      output                 init_calib_complete,                                    
  33. 33      //lcd接口                           
  34. 34      output                 lcd_hs       ,   //LCD 行同步信号
  35. 35      output                 lcd_vs       ,   //LCD 场同步信号
  36. 36      output                 lcd_de       ,   //LCD 数据输入使能
  37. 37      inout       [23:0]     lcd_rgb      ,   //LCD 颜色数据
  38. 38      output                 lcd_bl       ,   //LCD 背光控制信号
  39. 39      output                 lcd_pclk         //LCD 采样时钟
  40. 40      );
  41. 41  //parameter define
  42. 42  //开发板MAC地址 00-11-22-33-44-55
  43. 43  parameter  BOARD_MAC = 48'h00_11_22_33_44_55;     
  44. 44  //开发板IP地址 192.168.1.10
  45. 45  parameter  BOARD_IP  = {8'd192,8'd168,8'd1,8'd10};  
  46. 46  //目的MAC地址 ff_ff_ff_ff_ff_ff
  47. 47  parameter  DES_MAC   = 48'hff_ff_ff_ff_ff_ff;   
  48. 48  //目的IP地址 192.168.1.102     
  49. 49  parameter  DES_IP    = {8'd192,8'd168,8'd1,8'd102};  
  50. 50                                                            
  51. 51  //wire define   
  52. 52  wire         gmii_rx_clk               ;  //以太网125M时钟            
  53. 53  wire         clk_50m                   ;  //50mhz时钟,提供给lcd驱动时钟
  54. 54  wire         locked                    ;  //时钟锁定信号                                      
  55. 55  wire         rdata_req                 ;  //DDR3控制器模块读使能
  56. 56  wire  [15:0] rd_data                   ;  //DDR3控制器模块读数据
  57. 57  wire         init_calib_complete       ;  //DDR3初始化完成init_calib_complete
  58. 58  wire         lcd_clk                   ;  //分频产生的LCD 采样时钟
  59. 59  wire  [12:0] h_disp                    ;  //LCD屏水平分辨率
  60. 60  wire  [15:0] lcd_id                    ;  //LCD屏的ID号
  61. 61  wire  [27:0] ddr3_addr_max             ;  //存入DDR3的最大读写地址
  62. 62  wire         sys_rst_n                 ;  //系统复位信号
  63. 63  wire  [31:0] rec_data                  ;  //以太网接收的数据
  64. 64  wire  [15:0] picture_data              ;  //图片数据
  65. 65  wire         picture_data_vld          ;  //图片数据有效信号
  66. 66  wire         picture_data_vld0         ;  //非480x272屏图片像素有效信号
  67. 67  wire         picture_data_vld1         ;  //480x272屏图片像素有效信号
  68. 68  wire         data_req_big              ;  //非480x272屏的LCD请求信号
  69. 69  wire         data_req_small            ;  //480x272LCD屏请求信号
  70. 70  
  71. 71  //*****************************************************
  72. 72  //**                    main code
  73. 73  //*****************************************************
  74. 74  
  75. 75  //待PLL输出稳定之后,停止系统复位
  76. 76  assign sys_rst_n = rst_n & locked;
  77. 77  //系统初始化完成:DDR3初始化完成
  78. 78  assign  sys_init_done = init_calib_complete;
  79. 79  //存入ddr3的最大读写地址
  80. 80  assign  ddr3_addr_max =(lcd_id == 16'h4342)? 130560:384000;
  81. 81  //存入ddr3的一行数据(突发长度)
  82. 82  assign  h_disp =  (lcd_id == 16'h4342) ? 480:800;           
  83. 83  
  84. 84  //时钟产生模块
  85. 85  pll u_pll
  86. 86   (// Clock in ports
  87. 87    .CLK_IN1    (sys_clk),      
  88. 88    // Clock out ports
  89. 89    .CLK_OUT1   (clk_ddr),   
  90. 90    .CLK_OUT2   (clk_50m),     
  91. 91    .CLK_OUT3   (clk_100m),        
  92. 92    // Status and control signals
  93. 93    .LOCKED     (locked)
  94. 94    );  
  95. 95      
  96. 96  //480x272屏图片裁剪模块   
  97. 97  picture_tailor u_picture_tailor(
  98. 98      .gmii_rx_clk           (gmii_rx_clk)          ,  //图片输入像素时钟
  99. 99      .sys_rst_n             (sys_rst_n)            ,  //复位信号                    
  100. 100     
  101. 101     .picture_data_vld0     (picture_data_vld0)    ,  //480x272屏图片像素有效信号                     
  102. 102     .picture_data_vld1     (picture_data_vld1)       //非480x272屏图片像素有效信号
  103. 103     );
  104. 104     
  105. 105 //ddr控制器
  106. 106 ddr3_control
  107. 107  #(
  108. 108    .C3_NUM_DQ_PINS         (16),
  109. 109    .C3_MEM_ADDR_WIDTH      (14),
  110. 110    .C3_MEM_BANKADDR_WIDTH  (3)
  111. 111    )
  112. 112  u_ddr3_control(
  113. 113   .clk_ddr                (clk_ddr),                              
  114. 114   .clk_100m               (gmii_rx_clk),                             
  115. 115   .rst_n                  (sys_rst_n),                           
  116. 116   .mcb3_dram_dq           (mcb3_dram_dq     ),                    
  117. 117   .mcb3_dram_a            (mcb3_dram_a      ),                    
  118. 118   .mcb3_dram_ba           (mcb3_dram_ba     ),                    
  119. 119   .mcb3_dram_ras_n        (mcb3_dram_ras_n  ),                    
  120. 120   .mcb3_dram_cas_n        (mcb3_dram_cas_n  ),                    
  121. 121   .mcb3_dram_we_n         (mcb3_dram_we_n   ),                    
  122. 122   .mcb3_dram_reset_n      (mcb3_dram_reset_n),                    
  123. 123   .mcb3_dram_cke          (mcb3_dram_cke    ),                    
  124. 124   .mcb3_dram_dm           (mcb3_dram_dm     ),                    
  125. 125   .mcb3_dram_udqs         (mcb3_dram_udqs   ),                    
  126. 126   .mcb3_dram_udqs_n       (mcb3_dram_udqs_n ),                    
  127. 127   .mcb3_rzq               (mcb3_rzq         ),                    
  128. 128   .mcb3_zio               (mcb3_zio         ),                    
  129. 129   .mcb3_dram_udm          (mcb3_dram_udm    ),                    
  130. 130   .mcb3_dram_dqs          (mcb3_dram_dqs    ),                    
  131. 131   .mcb3_dram_dqs_n        (mcb3_dram_dqs_n  ),                    
  132. 132   .mcb3_dram_ck           (mcb3_dram_ck     ),                    
  133. 133   .mcb3_dram_ck_n         (mcb3_dram_ck_n   ),                    
  134. 134   .c3_calib_done          (init_calib_complete),                  
  135. 135                                                                  
  136. 136   .ddr3_read_valid        (1'b1 ),                                
  137. 137   .ddr3_pingpang_en       (1'b0),                                 
  138. 138                                                                  
  139. 139   //用户接口                                                        
  140. 140   .clk_write              (gmii_rx_clk),           //写时钟                          
  141. 141   .wr_en                  (picture_data_vld),      //写使能                        
  142. 142   .wr_data                (picture_data),          //写数据  
  143. 143   .wr_load                (0),                     //写端更新信号   
  144. 144   .wr_length              (h_disp[10:3]),          //一次写长度                          
  145. 145   .wr_max_addr            (ddr3_addr_max),         //写最大地址   
  146. 146   .wr_min_addr            (0),                     //写最小地址                        
  147. 147                                                                             
  148. 148   .clk_read               (lcd_clk),               //读时钟   
  149. 149   .rd_load                (out_vsync),             //读端更新信号   
  150. 150   .rd_en                  (rdata_req),             //读使能                        
  151. 151   .rd_max_addr            (ddr3_addr_max),         //读最大地址   
  152. 152   .rd_min_addr            (0),                     //读最小地址                        
  153. 153   .rd_length              (h_disp[10:3]),          //一次读长度                        
  154. 154   .rd_data                (rd_data)                //读数据                        
  155. 155                                                                  
  156. 156   );  
  157. 157      
  158. 158 //LCD顶层模块
  159. 159 lcd_rgb_top  u_lcd_rgb_top(
  160. 160     .sys_clk               (clk_50m  ),
  161. 161     .clk_100m              (clk_100m),
  162. 162     .sys_rst_n             (sys_rst_n ),
  163. 163     .sys_init_done         (sys_init_done),     
  164. 164                           
  165. 165     //lcd接口                           
  166. 166     .lcd_id                (lcd_id),                //LCD屏的ID号
  167. 167     .lcd_hs                (lcd_hs),                //LCD 行同步信号
  168. 168     .lcd_vs                (lcd_vs),                //LCD 场同步信号
  169. 169     .lcd_de                (lcd_de),                //LCD 数据输入使能
  170. 170     .lcd_rgb               (lcd_rgb),               //LCD 颜色数据
  171. 171     .lcd_bl                (lcd_bl),                //LCD 背光控制信号
  172. 172     .lcd_rst               (lcd_rst),               //LCD 复位信号
  173. 173     .lcd_pclk              (lcd_pclk),              //LCD 采样时钟
  174. 174     .lcd_clk               (lcd_clk),               //LCD 驱动时钟
  175. 175     //用户接口                     
  176. 176     .h_disp                (),                      //行分辨率  
  177. 177     .v_disp                (),                      //场分辨率
  178. 178     .pixel_xpos            (),
  179. 179     .pixel_ypos            (),
  180. 180     .out_vsync             (out_vsync),
  181. 181     .data_in               (rd_data),               //rfifo输出数据
  182. 182     .data_req              (rdata_req)              //请求数据输入
  183. 183     );  
  184. 184
  185. 185 //以太网顶层
  186. 186 eth_udp_loop #(
  187. 187     .BOARD_MAC          (BOARD_MAC),                //开发板MAC地址
  188. 188     .BOARD_IP           (BOARD_IP),                 //开发板IP地址
  189. 189     .DES_MAC            (DES_MAC),                  //目的MAC地址
  190. 190     .DES_IP             (DES_IP)                    //目的IP地址
  191. 191     )
  192. 192 u_eth_udp_loop(
  193. 193     .sys_rst_n          (sys_rst_n) ,               //系统复位信号,低电平有效
  194. 194     .eth_rxc            (eth_rxc)   ,               //RGMII接收数据时钟
  195. 195     .eth_rx_ctl         (eth_rx_ctl),               //RGMII输入数据有效信号
  196. 196     .eth_rxd            (eth_rxd)   ,               //RGMII输入数据
  197. 197     .eth_txc            (eth_txc)   ,               //RGMII发送数据时钟   
  198. 198     .eth_tx_ctl         (eth_tx_ctl),               //RGMII输出数据有效信号
  199. 199     .eth_txd            (eth_txd)   ,               //RGMII输出数据         
  200. 200     .eth_rst_n          (eth_rst_n) ,               //以太网芯片复位信号,低电平有效
  201. 201                                                     
  202. 202     .rec_en             (rec_en),                   //以太网32位图片数据接收完成
  203. 203     .rec_data           (rec_data),                 //以太网32位数据
  204. 204     .gmii_rx_clk        (gmii_rx_clk)               //以太网125M时钟
  205. 205     );
  206. 206     
  207. 207 //以太网32位图片数像素据转16位
  208. 208 udp_32_to_16bit u_udp_32_to_16bit(
  209. 209   .clk                 (gmii_rx_clk),               //时钟信号
  210. 210   .rst_n               (sys_rst_n),                 //复位信号,低电平有效
  211. 211                                                     
  212. 212   .rec_en              (rec_en),                    //UDP接收的数据使能信号
  213. 213   .picture_data_vld0   (picture_data_vld0),         //非480x272屏图片像素有效
  214. 214   .picture_data_vld1   (picture_data_vld1),         //480x272屏图片像素有效  
  215. 215   .lcd_id              (lcd_id),                    //LCD屏ID
  216. 216   .picture_data_vld    (picture_data_vld),          //图片像素有效  
  217. 217   .rec_data            (rec_data),                  //以太网32位图片像素数据
  218. 218   .picture_data        (picture_data)               //以太网316位图片像素数据
  219. 219     );  
  220. 220     
  221. 221 endmodule
复制代码

顶层模块主要实现了对各个子模块的例化,包括前文所提到的六个模块,还包括参数定义。绝大部分的内容我们在“OV7725摄像头RGB-LCD显示实验”中有详细介绍。我们只介绍新添加的。
代码第63行,信号rec_data是从以太网模块直接接收到的32bit图片像素数据,本章没有进行缓存处理,注意它是gmii_rx_clk时钟域下的数据。
代码第64行,信号picture_data是欲存入DDR3的图片像素数据,包括裁剪的或者非裁剪的图片数据,16bit。
代码第65行,picture_data_vld,图片数据有效使能信号,该信号和picture_data对应,该信号拉高表明当前时钟下的picture_data是有效的。
代码第66行,picture_data_vld0,图片数据有效使能信号,该信号和picture_data_vld的区别是,它只是原始图片(800*480)数据的有效使能标记。
代码第67行,picture_data_vld1,图片数据有效使能信号,它是裁剪处理后图片(480*272)的有效使能标记。
以上信号的产生将在下文详细介绍。
代码第80行,定义了图片数据存入DDR3的最大地址。当遇到屏幕大小比图片大小(800*480)小的屏幕,也就是lcd_id等16'h4342时,我们需要对图片进行裁剪,此时的DDR3的最大地址就是按照实际屏幕大小来算的,也就是480*272等于130560,其他屏幕不需要裁剪,所以存入的地址是图片的大小都是800*480等于384000。
代码第82行,定义DDR3一次突发读写的长度,我们取所存入图片的一行作为突发长度,显然,对于lcd_id等16'h4342屏幕,存入DDR3的图片大小是480*272,所以突发长度我们取480,其他屏幕则选择800。
下文我们来介绍各个子模块,只介绍改动的部分。
首先是以太网顶层(eth_udp_loop),该部分代码我们基本移植了“以太网UDP测试实验”,将以太网UDP接收到的数据送给DDR3控制器模块,DDR3控制器有足够的速度和带宽接收这些数据,并不需额外的缓存。
        图片裁剪(picture_tailor)模块,该模块是我们新加入的模块,实现把上位机发来的(800*480)的图片裁剪成适合小屏幕(480*272)显示的图片。实现方式有别于“OV772摄像头RGB-LCD显示实验”,但是异曲同工。下面来具体看代码。
  1. 1  module picture_tailor(
  2. 2      input                 gmii_rx_clk          ,  //像素数据时钟
  3. 3      input                 sys_rst_n            ,  //复位信号                    
  4. 4     //用户接口
  5. 5      input                 picture_data_vld0    ,  //整张图片数据有效使能信号                              
  6. 6      output                picture_data_vld1       //裁剪后图片数据有效使能信号   
  7. 7      );
  8. 8  
  9. 9  //reg define                     
  10. 10 reg [10:0] picture_data_cntx;                     //整张图片横向像素点坐标
  11. 11 reg [10:0] picture_data_cnty;                     //整张图片纵向像素点坐标
  12. 12
  13. 13 //*****************************************************
  14. 14 //**                    main code
  15. 15 //*****************************************************
  16. 16
  17. 17 //产生裁剪后图片数据有效信号
  18. 18 assign picture_data_vld1 = ((160 <= picture_data_cntx) && (picture_data_cntx< 640)
  19. 19                           && (104 <= picture_data_cnty) && (picture_data_cnty < 376)
  20. 20                           && picture_data_vld0)?1'b1:1'b0;
  21. 21
  22. 22 //产生(800*480)图片横向像素点坐标
  23. 23 always[url=home.php?mod=space&uid=95564]@[/url] (posedge gmii_rx_clk or negedge sys_rst_n) begin
  24. 24     if(!sys_rst_n)
  25. 25         picture_data_cntx <= 11'd0;
  26. 26     else if(picture_data_vld0)begin
  27. 27         if(picture_data_cntx == 800 - 1'b1)
  28. 28             picture_data_cntx <= 11'd0;
  29. 29         else
  30. 30             picture_data_cntx <= picture_data_cntx + 1'b1;           
  31. 31     end
  32. 32 end
  33. 33
  34. 34 //产生(800*480)图片纵向像素点坐标
  35. 35 always@ (posedge gmii_rx_clk or negedge sys_rst_n) begin
  36. 36     if(!sys_rst_n)
  37. 37         picture_data_cnty <= 11'd0;
  38. 38     else begin
  39. 39         if(picture_data_cntx == 800 - 1'b1) begin
  40. 40             if(picture_data_cnty ==480 - 1'b1)
  41. 41                 picture_data_cnty <= 11'd0;
  42. 42             else
  43. 43                 picture_data_cnty <= picture_data_cnty + 1'b1;   
  44. 44         end
  45. 45     end   
  46. 46 end
  47. 47   
  48. 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. 1  module udp_32_to_16bit(
  2. 2      input               clk,                    //时钟信号
  3. 3      input               rst_n,                  //复位信号,低电平有效
  4. 4      
  5. 5      input               rec_en,                 //UDP接收的数据使能信号
  6. 6      output              picture_data_vld0,      //以太网800*480图片像素数据有效使能信号
  7. 7      input               picture_data_vld1,      //裁剪后480*272像素数据有效使能信号
  8. 8      input  [15:0]       lcd_id,                 //LCD ID
  9. 9      output              picture_data_vld,       //写入DDR3图片像素数据有效使能信号
  10. 10     input  [31:0]       rec_data,               //以太网图片32位像素数据
  11. 11     output [15:0]       picture_data            //以太网图片16位像素数据
  12. 12     );
  13. 13  //wire define
  14. 14 wire [15:0] picture_data1 ;                     //以太网图片像素高16数据
  15. 15 wire [15:0] picture_data2 ;                     //以太网图片像素低16数据
  16. 16  //reg define      
  17. 17 reg rec_en_d0;                                  //接收数据有效延迟一拍
  18. 18
  19. 19 //*****************************************************
  20. 20 //**                    main code
  21. 21 //*****************************************************
  22. 22
  23. 23 //判断屏幕如果是480x272则将裁剪的图像数据有效使能赋值给像素数据有效信号,否则将不裁剪
  24. 24 //的图像数据有效使能赋给像素数据有效信号
  25. 25 assign picture_data_vld = (lcd_id==16'h4342)? picture_data_vld1:picture_data_vld0;
  26. 26 //产生以太网800*480图片像素数据有效使能信号,需要持续两个时钟周期
  27. 27 assign picture_data_vld0 = ((rec_en==1) ||(rec_en_d0==1 ))? 1'b1:1'b0;
  28. 28 //给以太网图片像素高16数据赋值
  29. 29 assign picture_data1 = rec_data[31:16];
  30. 30 //给以太网图片像素低16数据赋值
  31. 31 assign picture_data2 = rec_data[15:0];
  32. 32 //分别将太网图片像素高低16位赋值给下游数据
  33. 33 assign picture_data = rec_en? picture_data1:picture_data2;
  34. 34
  35. 35 //接收数据有效信号寄存一拍
  36. 36 always @(posedge clk or negedge rst_n) begin
  37. 37     if(~rst_n)begin
  38. 38        rec_en_d0 <=0;
  39. 39     end
  40. 40      else begin
  41. 41         rec_en_d0 <= rec_en;
  42. 42      end
  43. 43 end   
  44. 44      
  45. 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信号寄存一拍。
至此,我们代码讲解完毕
48.5下载验证
用FPC排线将RGB-LCD屏与开发板连接,然后将网线一端连接电脑网口,另一端与开发板上的网口连接;将下载器一端连电脑,另一端与开发板上对应端口连接,最后连接电源线。

图 48.5.1 超越者开发板硬件连接

打开电源并下载程序,程序下载完成后,由于还没有向DDR3中写入图片数据,所以DDR3中的数据是随机的,此时LCD显示器上显示的像素点颜色是杂乱无章的。接下来我们在上位机中将图片通过网口下载到开发板上的DDR3中。在此之前我们首先准备好图片,并制作成bin格式。具体操作如下:
打开工具Img2Lcd,该工具位于开发板所随附的资料“6_软件资料/1_软件/Img2Lcd”目录下,找到“Img2Lcd.exe”并双击打开,如下图所示:

图 48.5.2 Img2Lcd工具使用界面

点击“打开”,导入一幅分辨率为800*480的jpg格式图片,按照上图进行参数设置,可以看到我们输出的图像大小也800*480,确定后是并导出bin文件。在弹出的界面中选择bin文件的保存路径并输入文件名。
到这里我们已经成功地将图片转成了bin文件,接下来需要借助网口调试助手将bin文件下载到开发板中。但是需要注意的是,由于发送的文件较大,需要先对网络适配器属性进行配置。
打开设备管理器,然后在网络适配器中找到网卡并右键选择属性,如下图所示。


图 48.5.3 网络适配器界面

在属性界面中选择“高级”,在属性栏中找到“巨型帧(Jumbo Frame)”,然后在右侧值中选择“9KB MTU”,最后点击确定,如下图所示。

图 48.5.4 巨型帧设置

默认情况下,以太网的MTU(Maximum Transmission Unit,最大传输单元)是1500字节。而巨型帧的设置把以太网帧格式的数据段扩展到了9KB,从而在传输大文件时减少了数据帧的个数,减轻了网络设备处理帧头的额外开销,从而提高网络传输性能。
注意有时候可能找不到巨型帧选项,原因是网络适配器驱动软件过于老旧的问题,此时我们只需要更新驱动即可。还是在设备管理器的界面,操作如下:

图 48.5.5更新驱动

        最后我们通过网络调试助手发送图片bin文件,如下图所示:

图 48.5.6 网络调试助手发送bin文件

网络调试助手的设置和前面的以太网通信相关例程一样,在远程主机一栏选择开发板的IP地址和端口号“192.168.1.102 :1234”;然后在发送区设置一栏勾选“启用文件数据源”,在弹出的界面中选择刚刚生成的bin文件;最后点击右下角的“发送”按钮,即可将由图片生成的bin文件通过网线发送到超越者开发板。
图片发送完成后液晶屏上显示的图片如下所示,说明基于以太网的LCD显示实验下载验证成功:


图 48.5.7  LCD液晶屏显示的图片


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

如发现本坛存在违规或侵权内容, 请点击这里发送邮件举报 (或致电020-38271790)。请提供侵权说明和联系方式。我们将及时审核依法处理,感谢配合。

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-4-18 01:58

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表