当前位置:   article > 正文

LVDS接口(3)LVDS发送端设计_lvds接口lcd显示彩图测试fpga工程

lvds接口lcd显示彩图测试fpga工程


 

--- 副标题:『FPGA通信接口』图像显示(4)LVDS接口

目录

1.LVDS 发送端程序设计

2.屏幕简介

3.RGB、LVDS、VGA、HDMI、MIPI DSI接口的区别

4.代码分析与效果

5.传送门


1.LVDS 发送端程序设计

根据前两篇文章的描述,搭建LVDS发送端程序首先要明确以下三个事项,①明确属性,包括通道个数,LVDS传输线个数,串化因子的大小,训练数据pattern值,以及SDR或DDR模式,随路时钟的大小;②明确当前硬件环境,随路时钟应该接MRCC或者SRCC管脚,否则vivado编译会不通过;差分数据管脚应该连接在FPGA上的PN差分对,并要确认好管脚所在bank的供电电压;还应该查看FPGA芯片数据手册判断是否支持需求的传输速率。③根据应用场景,例如LVDS发送端发送数据到LVDS液晶显示屏,则要遵守液晶显示屏时序的相关要求。在上述事项明确的前提下,LVDS发送端要解决的问题便是,将来自其他模块的应用数据按照既定的传输模型并转串后,接入到FPGA内部的LVDS发送器完成数据的发送。为此,xilinx 7系列提供了OBUFDS原语实现单端信号转LVDS差分信号的转换(可理解为该原语指向FPGA内部的LVDS发送器),提供了OSERDES2原语实现逻辑中应用并行数据的串行化处理。

说明:本文第五节注意在提供的源码链接与文章开始位置的链接不是一个代码,文章开始的链接是与本文相关的LVDS屏幕点亮的程序;传送门提供的源码链接是LVDS接口系列对应的源码链接。在其中有一个vivado工程是专门是LVDS发送端,在那个例程中:8位传化因子,1个通道,2个LVDS,DDR模式,随路时钟为200MHz,并行时钟为50MHz,pattern值为0xe9,LVDS相关引脚连接某A7芯片Bank16,bank电压为2.5V。所发送的应用数据为测试数据为1-255自增,发送端逻辑框图如下。但是本篇是以LVDS接口的屏幕为例介绍LVDS发送端程序设计的。在那个例程LVDS发送端在中介绍了使用serdes原语串并转换的方式,本文lvds_tx模块使用另外一种方式完成串并转换,在此过程中将数据也重组成符合该屏幕操作时序的序列。

2.屏幕简介

屏幕是由厂家提供的TFT显示模组和屏幕PCB背板组成。PCB的作用是提供LCD背光所需的电压、用于屏幕显示的电压、与其他设备相连的排针或者其他连接器形式。当模组支持触摸功能时还可以接上触摸转换或触摸控制芯片,通过SPI或IIC等方式向控制端提供触摸信息。液晶屏提供的常见的视频接入口有RGB、LVDS、VGA、HDMI、MIP-DSI接口。本文点亮一块LVDS接口的液晶屏介绍发送LVDS接口时序。

一块屏幕,在编程之前,应该搞清pin定义,屏幕时序参数,和接口时序(即LVDS发送端需要清楚的事项)三部分内容,在屏幕手册中均可找到。手头的这块板子pin定义如下。搞清每一个管脚的作用,并且根据硬件连接在FPGA工程中为每一个管脚添加约束。该屏幕支持触摸功能(即PCB背板上有触摸控制芯片且屏幕模组支持触摸功能。),但本次开发并未涉及,所以无需关注。

该屏幕的时序参数如下,这与其他接口的显示参数是一致的,只是没有前沿和后沿的概念,这是因为LVDS接口时序中只有de信号,而没有行场同步信号。而这个de信号是 RGB 时序中才有,对于 VGA 时序是不需要的。

该屏幕支持RGB888格式的图片,在此模式下,其操作时序如下所示,图中PINC即为屏幕时序参数中的时钟频率转换成差分信号所得,可以看到该时钟占空比为4:3,参数中为51.2MHz,实操中给50MHz即可。图中的”-”在编程时给0即可。

3.RGB、LVDS、VGA、HDMI、MIPI DSI接口的区别

 LVDS涉及领域很多,单在图像接口领域,LVDS用在很多CMOS图像传感器上的采集接口,用在LVDS屏幕上用作显示接口。RGB TTL接口信号类型是TTL电平,信号的内容是RGB666或者RGB888还有行场同步和时钟,以及DE信号;LVDS接口信号类型是LVDS信号(低电压差分对),信号的内容是RGB数据和DE信号和时钟,本质是RGB的信号转换成差分串行的数据,不同的屏幕位组合通道组合的方式有所不同;VGA接口与RGB接口信号基本一致,但他是模拟信号,是传统的接口,现在也已基本淘汰。HDMI接口是速度比较快的接口,活跃于生活的各个角落,接口类型丰富,常用的HDMI1.4、2.0和2.1,分别支持最高2k60hz、4K60hz和4K120hz(8K60hz),采用TMDS电平标准,本质是RGB接口经过TMDS编码,该过程可以用编码芯片实现,也可通过支持TMDS标准的FPGA实现。另外,HDMI还可作为输入接口应用。MIPI-DSI接口信号类型是LVDS信号,信号的内容是视频流数据和控制指令,是消费类电子中常见的接口,例如手机玩具等。此外MIPI还有用于视频采集的CSI接口,常见于各类CMOS图像传感器中。

4.代码分析与效果

顶层模块Top例化了三个模块,其中clk ip生成各模块所用的时钟,lvds_driver模块完成RGB数据到LVDS数据的转换(满足该屏幕的操作时序),并且对外留下数据同步信号lcd_de和RGB数据进入信号,即在lcd_de有效的时候按照像素顺序输入RGB数据即可。Rgb888_data模块是自定义的图片数据源。每秒切换一张图,一共8张不同颜色的图,在实际应用中数据替换即可。

另外上电之后lcd_de信号的生成应该等其他位置就绪之后在进行,这里将原复位信号延时后作为模块的复位信号做到了二者之间先后启动。

lvds_driver模块

  1. module rgbtolvds(
  2. input rst_n, // 复位信号,低电平有效
  3. input clk_350MHz, // 350MHz
  4. input clk_50MHz, // 50MHz
  5. output lcd_de, // LCD数据使能信号,高电平有效
  6. input [7:0] R, // LCD R分量数据
  7. input [7:0] G, // LCD G分量数据
  8. input [7:0] B, // LCD B分量数据
  9. // LVDS接口信号
  10. output lcd_clk_p, // LVDS接口时钟,50MHz
  11. output lcd_clk_n,
  12. output lcd_data0_p, // {G[0], R[5:0]}
  13. output lcd_data0_n, // {G[0], R[5:0]}
  14. output lcd_data1_p, // {B[1:0], G[5:1]}
  15. output lcd_data1_n, // {B[1:0], G[5:1]}
  16. output lcd_data2_p, // {DE, 2'b0, B[5:2]}
  17. output lcd_data2_n, // {DE, 2'b0, B[5:2]}
  18. output lcd_data3_p, // {1'b0, B[7:6], G[7:6], R[7:6]}
  19. output lcd_data3_n // {1'b0, B[7:6], G[7:6], R[7:6]}
  20. );
  21. //----------------------------------------------------------------------
  22. // 典型LVDS时序
  23. wire [6:0] lvds_ind0 = {G[0], R[5], R[4], R[3], R[2], R[1], R[0]};
  24. wire [6:0] lvds_ind1 = {B[1], B[0], G[5], G[4], G[3], G[2], G[1]};
  25. wire [6:0] lvds_ind2 = {lcd_de, 1'b0, 1'b0, B[5], B[4], B[3], B[2]};
  26. wire [6:0] lvds_ind3 = {1'b0, B[7], B[6], G[7], G[6], R[7], R[6]};
  27. //----------------------------------------------------------------------
  28. // LVDS数据发送模块例化
  29. wire lcd_clk;
  30. wire lcd_data3,lcd_data2,lcd_data1,lcd_data0;
  31. lvds_tx u_lvds_tx
  32. (
  33. .rst_n (rst_n ),
  34. .clk_50MHz (clk_50MHz ),
  35. .clk_350MHz (clk_350MHz ),
  36. .din ({lvds_ind3,lvds_ind2,lvds_ind1,lvds_ind0} ),
  37. .dout ({lcd_data3,lcd_data2,lcd_data1,lcd_data0} ),
  38. .outclock (lcd_clk )
  39. );
  40. // 单端信号转差分信号
  41. OBUFDS #(.IOSTANDARD("DEFAULT"),.SLEW("SLOW"))u_clk_OBUFDS(.O(lcd_clk_p),.OB(lcd_clk_n),.I(lcd_clk));
  42. OBUFDS #(.IOSTANDARD("DEFAULT"),.SLEW("SLOW"))u_d0_OBUFDS (.O(lcd_data0_p),.OB(lcd_data0_n),.I(lcd_data0));
  43. OBUFDS #(.IOSTANDARD("DEFAULT"),.SLEW("SLOW"))u_d1_OBUFDS (.O(lcd_data1_p),.OB(lcd_data1_n),.I(lcd_data1));
  44. OBUFDS #(.IOSTANDARD("DEFAULT"),.SLEW("SLOW"))u_d2_OBUFDS (.O(lcd_data2_p),.OB(lcd_data2_n),.I(lcd_data2));
  45. OBUFDS #(.IOSTANDARD("DEFAULT"),.SLEW("SLOW"))u_d3_OBUFDS (.O(lcd_data3_p),.OB(lcd_data3_n),.I(lcd_data3));
  46. //屏幕参数生成de信号
  47. reg [11:0] hcnt = 12'd0;
  48. reg [11:0] vcnt = 12'd0;
  49. reg [2 :0] rst_cnt = 3'd0;
  50. localparam H_ActiveSize= 1024;
  51. localparam V_ActiveSize= 600;
  52. localparam V_FrameSize = 635;
  53. localparam H_FrameSize= 1344;
  54. wire hs_valid = hcnt < H_ActiveSize;
  55. wire vs_valid = vcnt < V_ActiveSize;
  56. assign lcd_de = hs_valid && vs_valid;
  57. wire rst_sync = rst_cnt[2];
  58. always @(posedge clk_50MHz)begin
  59. if(!rst_n)
  60. rst_cnt <= 3'd0;
  61. else if(rst_cnt[2] == 1'b0)
  62. rst_cnt <= rst_cnt + 1'b1;
  63. end
  64. always @(posedge clk_50MHz)begin
  65. if(rst_sync == 1'b0)
  66. hcnt <= 12'd0;
  67. else if(hcnt < (H_FrameSize - 1'b1))
  68. hcnt <= hcnt + 1'b1;
  69. else
  70. hcnt <= 12'd0;
  71. end
  72. always @(posedge clk_50MHz)begin
  73. if(rst_sync == 1'b0)
  74. vcnt <= 12'd0;
  75. else if(hcnt == (H_ActiveSize - 1'b1)) begin
  76. vcnt <= (vcnt == (V_FrameSize - 1'b1)) ? 12'd0 : vcnt + 1'b1;
  77. end
  78. end
  79. endmodule

 rgb888_data模块

  1. module rgb888_data(
  2. input rst_n,
  3. input clk_pixel,
  4. input lcd_de,
  5. output [23:0]RGB
  6. );
  7. reg [25:0]time_cnt;//1s计数
  8. reg [2:0]pic_cnt;//图片切换
  9. reg [23:0]RGB_i;
  10. assign RGB = lcd_de ? RGB_i : 24'd0;
  11. always @(posedge clk_pixel or negedge rst_n) begin
  12. if(!rst_n)begin
  13. time_cnt <= 26'd0;
  14. pic_cnt <= 0;
  15. RGB_i <= 0;
  16. end else begin
  17. time_cnt <= time_cnt + 1;
  18. if(time_cnt==26'd50_000_000)begin
  19. pic_cnt <= pic_cnt + 1;
  20. case (pic_cnt)
  21. 0:RGB_i<= {8'd0,8'd0,8'd0};//
  22. 1:RGB_i<= {8'hff,8'hff,8'hff};//白
  23. 2:RGB_i<= {8'hff,8'd0,8'd0};//
  24. 3:RGB_i<= {8'd0,8'hff,8'd0};//绿
  25. 4:RGB_i<= {8'hff,8'hdc,8'd0};//
  26. 5:RGB_i<= {8'd0,8'h2c,8'h35};//hei绿
  27. 6:RGB_i<= {8'hda,8'h70,8'hb3};//分红
  28. 7:RGB_i<= {8'hb5,8'hce,8'ha8};//黑
  29. default: RGB_i <= 0;
  30. endcase
  31. end
  32. end
  33. end
  34. endmodule

lvds_tx模块

  1. module lvds_tx(
  2. input rst_n, // 复位信号,低电平有效
  3. input clk_50MHz, // 50MHz
  4. input clk_350MHz, // 350MHz
  5. input [27:0]din,
  6. output reg [3:0] dout,
  7. output reg outclock // 50MHz,占空比4/7
  8. );
  9. //----------------------------------------------------------------------
  10. // 350MHz对50MHz进行同步处理
  11. reg clk_50MHz_r1;
  12. reg clk_50MHz_r2;
  13. reg clk_50MHz_r3;
  14. always @(posedge clk_350MHz)
  15. begin
  16. clk_50MHz_r1 <= clk_50MHz;
  17. clk_50MHz_r2 <= clk_50MHz_r1;
  18. clk_50MHz_r3 <= clk_50MHz_r2;
  19. end
  20. wire clk_50MHz_pos = (~clk_50MHz_r3 & clk_50MHz_r2) ? 1'b1 : 1'b0;
  21. reg sync_done;
  22. always @(posedge clk_350MHz)
  23. begin
  24. if(rst_n == 1'b0)
  25. sync_done <= 1'b0;
  26. else
  27. begin
  28. if(clk_50MHz_pos == 1'b1)
  29. sync_done <= 1'b1;
  30. else
  31. sync_done <= sync_done;
  32. end
  33. end
  34. //----------------------------------------------------------------------
  35. // 生成LVDS时序
  36. reg [3:0] bit_cnt;
  37. reg [27:0] din_tmp;
  38. always @(posedge clk_350MHz)
  39. begin
  40. if(sync_done == 1'b1)
  41. begin
  42. case(bit_cnt)
  43. 4'd0 :
  44. begin
  45. dout[0] <= din_tmp[ 1];
  46. dout[1] <= din_tmp[ 8];
  47. dout[2] <= din_tmp[15];
  48. dout[3] <= din_tmp[22];
  49. outclock <= 1'b1;
  50. bit_cnt <= 4'd1;
  51. end
  52. 4'd1 :
  53. begin
  54. dout[0] <= din_tmp[ 0];
  55. dout[1] <= din_tmp[ 7];
  56. dout[2] <= din_tmp[14];
  57. dout[3] <= din_tmp[21];
  58. outclock <= 1'b1;
  59. bit_cnt <= 4'd2;
  60. end
  61. 4'd2 :
  62. begin
  63. dout[0] <= din[ 6];
  64. dout[1] <= din[13];
  65. dout[2] <= din[20];
  66. dout[3] <= din[27];
  67. outclock <= 1'b1;
  68. bit_cnt <= 4'd3;
  69. din_tmp <= din;
  70. end
  71. 4'd3 :
  72. begin
  73. dout[0] <= din_tmp[ 5];
  74. dout[1] <= din_tmp[12];
  75. dout[2] <= din_tmp[19];
  76. dout[3] <= din_tmp[26];
  77. outclock <= 1'b1;
  78. bit_cnt <= 4'd4;
  79. end
  80. 4'd4 :
  81. begin
  82. dout[0] <= din_tmp[ 4];
  83. dout[1] <= din_tmp[11];
  84. dout[2] <= din_tmp[18];
  85. dout[3] <= din_tmp[25];
  86. outclock <= 1'b0;
  87. bit_cnt <= 4'd5;
  88. end
  89. 4'd5 :
  90. begin
  91. dout[0] <= din_tmp[ 3];
  92. dout[1] <= din_tmp[10];
  93. dout[2] <= din_tmp[17];
  94. dout[3] <= din_tmp[24];
  95. outclock <= 1'b0;
  96. bit_cnt <= 4'd6;
  97. end
  98. 4'd6 :
  99. begin
  100. dout[0] <= din_tmp[ 2];
  101. dout[1] <= din_tmp[ 9];
  102. dout[2] <= din_tmp[16];
  103. dout[3] <= din_tmp[23];
  104. outclock <= 1'b0;
  105. bit_cnt <= 4'd0;
  106. end
  107. default :
  108. begin
  109. dout <= 4'b0;
  110. outclock <= 1'b0;
  111. bit_cnt <= 4'd0;
  112. din_tmp <= 28'b0;
  113. end
  114. endcase
  115. end
  116. else
  117. begin
  118. dout <= 4'b0;
  119. outclock <= 1'b0;
  120. bit_cnt <= 4'd0;
  121. din_tmp <= 28'b0;
  122. end
  123. end
  124. endmodule

5.传送门

我的主页 

FPGA通信接口汇总导航

下一篇:LVDS接口(4)LVDS接收端设计 

源码链接
                                                                         END                                                                         


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/858331
推荐阅读
相关标签