外贸网站友情链接,网站文件权限设置,网站服务器配置,南通高端网站建设公司实验目标#xff1a;驱动HdMI显示十色等宽彩条。
本实验的重点是#xff1a;
1掌握TMDS通信协议。
2rgb565转rgb888。
3编写HDMI驱动程序。
4学会看流程图编写代码。
值得注意的事情
1注意数据与解析数据的信号#xff08;比如传入的数据中0或者1的个数#xff09;驱动HdMI显示十色等宽彩条。
本实验的重点是
1掌握TMDS通信协议。
2rgb565转rgb888。
3编写HDMI驱动程序。
4学会看流程图编写代码。
值得注意的事情
1注意数据与解析数据的信号比如传入的数据中0或者1的个数要在时序上与数据进行对齐。
如果解析数据的信号是时序信号那么它将会滞后数据一个clk。如果后面要用到数据与对应的解析数据的信号同时做条件那么需要对数据进行打一拍处理。用打拍过后的数据与解析数据的信号作为条件去做判断。
2如果一个数据是有符号位的那么判断它的正负就不能用简单的 0 来判断。
比如本实验中cnt就是5位宽的有符号数判断其正负cnt[4] 1 为负数cnt[4] 0 为正数。如果是cnt 0 表述负数的话那么cnt永远不会被判定为负数。
看仿真波形的经验之谈
1在本实验与官方的编码模块代码那么在通过功能方正验证自己编写的编码模块代码是否正确时可以把官方的模块例化进工程然后仿真对比波形是否一致即可判断自己编写的代码是否正确。
2在遇到不一致的时候比如输入信号都一致某个输出信号不一致。
先不要着急看去检查哪些信号会直接影响这个输出信号赋值因为前面的一些信号会间接影响输出信号赋值。比如影响直接影响输出信号赋值的信号。
那么此时应该先检查一些直接受输入信号影响的信号是否正确是否和官方给的代码波形一致
然后再看一些”条件“波形是否正确
也就是说由输入信号逐渐到输出信号这个过程中一次产生你编写代码的顺序去检查这些信号是否正确。自顶向下。慢慢检查。
3实在检查不出来看看自己的代码和官方的代码编写的差在哪里。
TMDS通信协议
一种传输视频/音频/辅助数据的传输技术最小化传输差分信号。
采用DIV编码。附上几张图
8bit像素信息到最小化传输直流均衡10bit数据编码流程 信源端与接收端的数据传输框图由图可知3路差分信号传输由rgb888转10bit的数据。一路差分信号传输clock。
下面两路ddc信号是从接收端读取硬件工作模式相关信息的本实验没有用到。IIC通信协议。 模块框图 时序图 代码
/*********************功能 将8位rgb信号转换成10位信号。编码cnt 当前时钟的值cnt(t - 1) 相对于当前时钟上一个时钟周期的值
*/module encode(input wire clk_25 ,input wire rst_n ,input wire [7:0] data_in ,input wire hsync , input wire vsync ,input wire DE ,output wire [9:0] data_out
);reg [7:0] data_in_reg1 ;reg [3:0] data_in_n1 ; // 保存传入数据1的个数.wire crtl_1 ; // 一个判断条件data_in中1的个数大于4 or 个数等于4且data_in[0] 0wire [8:0] q_m ;reg [8:0] q_m_reg1 ;reg DE_reg1 ;reg DE_reg2 ;reg C0_reg1 ; // 行同步信号,打拍为了和数据对齐。reg C1_reg1 ; // 场同步信号,打拍为了和数据对齐。reg C0_reg2 ; reg C1_reg2 ; reg [9:0] q_out ; // 最终要输出的10为数据经过一系列编码。wire ctrl_2 ;wire ctrl_3 ;reg [4:0] cnt ; // 这是一个用于跟踪数据流差异的寄存器。正值表示已传输的“1”的超额数量。负值表示已传输的“0”的超额数量。reg [3:0] q_m_n1 ; // q_m中1的个数。cnt的最高位是符号位。有符号位的数据不能简单通过是否大于小于0来判断正负应该判断最高位符号位的0/11负数0正数reg [3:0] q_m_n0 ; // q_m中0的个数。/**********************************************************************************/// 产生第一个条件计算输入数据中1的个数。always (posedge clk_25 or negedge rst_n) begin // 计算传入数据1的个数.if(~rst_n) begindata_in_n1 4d0 ;end else begindata_in_n1 data_in[0] data_in[1] data_in[2] data_in[3] data_in[4] data_in[5] data_in[6] data_in[7] ;endend// 对输入数据打一拍以对齐数据data_in_n1。always (posedge clk_25 or negedge rst_n) beginif(~rst_n) begindata_in_reg1 8d0 ;end else begindata_in_reg1 data_in ;endend// 根据data_in_n1与输入数据的打拍信号描述出crtl_1判断条件。assign crtl_1 ( data_in_n1 4 || (data_in_n1 4 data_in_reg1[0] 1b0) ) ? 1b1 : 1b0 ;// 8bit 到 9bit 编码逻辑。assign q_m[0] data_in_reg1[0] ;assign q_m[1] (crtl_1) ? q_m[0]~^data_in_reg1[1] : q_m[0]^data_in_reg1[1] ;assign q_m[2] (crtl_1) ? q_m[1]~^data_in_reg1[2] : q_m[1]^data_in_reg1[2] ;assign q_m[3] (crtl_1) ? q_m[2]~^data_in_reg1[3] : q_m[2]^data_in_reg1[3] ;assign q_m[4] (crtl_1) ? q_m[3]~^data_in_reg1[4] : q_m[3]^data_in_reg1[4] ;assign q_m[5] (crtl_1) ? q_m[4]~^data_in_reg1[5] : q_m[4]^data_in_reg1[5] ;assign q_m[6] (crtl_1) ? q_m[5]~^data_in_reg1[6] : q_m[5]^data_in_reg1[6] ;assign q_m[7] (crtl_1) ? q_m[6]~^data_in_reg1[7] : q_m[6]^data_in_reg1[7] ;assign q_m[8] (crtl_1) ? 1b0 : 1b1 ;/********************* 对q_out和cnt赋值用到的条件 ***************************/// 数据q_m中1的个数和0的个数。always (posedge clk_25 or negedge rst_n) begin if(~rst_n) beginq_m_n1 4d0 ;q_m_n0 4d0 ;end else beginq_m_n1 q_m[0] q_m[1] q_m[2] q_m[3] q_m[4] q_m[5] q_m[6] q_m[7] ;// q_m_n0 ~q_m[0] ~q_m[1] ~q_m[2] ~q_m[3] // ~q_m[4] ~q_m[5] ~q_m[6] ~q_m[7] ;q_m_n0 4d8 - (q_m[0] q_m[1] q_m[2] q_m[3] q_m[4] q_m[5] q_m[6] q_m[7]) ;endend// 根据cnt和q_m_n1 q_m_n2 产生判断条件ctrl_2ctrl_3。assign ctrl_2 ((cnt 5d0) || (q_m_n1 q_m_n0)) ? 1b1 : 1b0 ; // 其实可以直接写逻辑运算关系式。assign ctrl_3 (((~cnt[4]) (q_m_n1 q_m_n0)) || ((cnt[4]) (q_m_n1 q_m_n0))) ? 1b1 : 1b0 ;// reg [8:0] q_m_reg1 ;// 由于q_m_n1 q_m_n0 是时序逻辑相对于q_m就延后了一个时钟周期为了保证qm数据与q_m_n1 q_m_n0 对齐所以要对qm打一拍。always (posedge clk_25 or negedge rst_n) beginif(~rst_n) beginq_m_reg1 8d0 ;end else beginq_m_reg1 q_m ;endend// reg DE_reg1 ;// reg DE_reg2 ;// DE 逐渐的要与q_m_reg1对齐always (posedge clk_25 or negedge rst_n) beginif(~rst_n) beginDE_reg1 1b0 ;DE_reg2 1b0 ; end else beginDE_reg1 DE ;DE_reg2 DE_reg1 ; endend// reg C0_reg1 ; // 行同步信号,打拍为了和数据对齐。// reg C0_reg2 ; // 行场同步信号也是要与q_m_reg1对齐。always (posedge clk_25 or negedge rst_n) beginif(~rst_n) beginC0_reg1 1b0 ;C0_reg2 1b0 ; end else beginC0_reg1 hsync ;C0_reg2 C0_reg1 ; endend// reg C1_reg1 ; // 场同步信号,打拍为了和数据对齐。// reg C1_reg2 ;always (posedge clk_25 or negedge rst_n) beginif(~rst_n) beginC1_reg1 1b0 ;C1_reg2 1b0 ; end else beginC1_reg1 vsync ;C1_reg2 C1_reg1 ; endend// 对q_out 与 cnt 赋值// q_outalways (posedge clk_25 or negedge rst_n) beginif(~rst_n) beginq_out 10d0 ;cnt 5d0 ;end else beginif(DE_reg2 1b0) begincase ({C1_reg2 ,C0_reg2})2b00: q_out[9:0] 10b11010_10100 ;2b01: q_out[9:0] 10b00101_01011 ;2b10: q_out[9:0] 10b01010_10100 ;2b11: q_out[9:0] 10b10101_01011 ;default: q_out[9:0] 10b11010_10100 ; // 随便挑一个。endcasecnt 5d0 ;end else beginif(ctrl_2 1b1) beginq_out[9] ~q_m_reg1[8] ; // q_out[9] ~q_m_reg1[8] ;q_out[8] q_m_reg1[8] ; // q_out[8] q_m_reg1[8] ;q_out[7:0] (q_m_reg1[8] 1b1) ? q_m_reg1[7:0] : ~q_m_reg1[7:0] ; // q_out[7:0] (q_m_reg1[8] 1b1) ? q_m_reg1[7:0] : ~q_m_reg1[7:0] ;if(q_m_reg1[8] 1b0) begincnt cnt q_m_n0 - q_m_n1 ;end else begincnt cnt - q_m_n0 q_m_n1 ;endend else beginif(ctrl_3 1b1) beginq_out[9] 1b1 ;q_out[8] q_m_reg1[8] ;q_out[7:0] ~q_m_reg1[7:0] ;cnt cnt {q_m_reg1[8], 1b0} q_m_n0 - q_m_n1 ; // 2 乘以 1位宽的数据相当于把该数据左移一位。 {q_m_reg1[8],0} end else beginq_out[9] 1b0 ;q_out[8] q_m_reg1[8] ;q_out[7:0] q_m_reg1[7:0] ;cnt cnt - {~q_m_reg1[8], 1b0} - q_m_n0 q_m_n1 ;endendendendend/************output signal descrable **************/assign data_out q_out ;
endmodule
// 10位并行数据信号转换为串行差分信号。
module par_to_ser(input wire clk_125 ,input wire rst_n ,input wire [9:0] data_out, // 应该在一个周期内把并行转串行应该用到流水线技术output wire ser_p ,output wire ser_n
);/**********************************************************************************// // wire signal define
// wire dataout_flag ;
// wire [3:0] datain_h_num ;
// wire [3:0] datain_l_num ;
// // reg signal define
// reg datain_h ;
// reg datain_l ;
// reg [3:0] cnt_bit ;
// reg [9:0] data_out_reg1 ;//
// // wire dataout_flag ;
// assign dataout_flag (data_out ! data_out_reg1) ? 1b1 : 1b0 ;
// assign datain_h_num 2d2*cnt_bit ;
// assign datain_l_num 2d2*cnt_bit 1b1 ;
// // reg datain_h ;
// always (posedge clk_125 or negedge rst_n) begin
// if(~rst_n) begin
// datain_h 1b0 ;
// end else begin
// datain_h data_out_reg1[datain_h_num] ; // 其实这里可能有一个问题datain_h_num如果计算慢了那么datain_h将会滞后一个clk_125.
// end
// end
// // reg datain_l ;
// always (posedge clk_125 or negedge rst_n) begin
// if(~rst_n) begin
// datain_l 1b0 ;
// end else begin
// datain_l data_out_reg1[datain_l_num] ;
// end
// end
// // reg [3:0] cnt_bit ;
// always (posedge clk_125 or negedge rst_n) begin
// if(~rst_n) begin
// cnt_bit 4d0 ;
// end else begin
// if(dataout_flag 1b1 || cnt_bit 4d4) begin
// cnt_bit 4d0 ;
// end else begin
// cnt_bit cnt_bit 1b1 ;
// end
// end
// end
// // reg [9:0] data_out_reg1 ;
// always (posedge clk_125 or negedge rst_n) begin
// if(~rst_n) begin
// data_out_reg1 10d0 ;
// end else begin
// data_out_reg1 data_out ;
// end
// end
************************************************************************************//***********************************************************************************/
// 野火教程里的方法10bit并行数据转串行数据。// wire signal deginewire dataout_flag ;wire [4:0] data_rise ;wire [4:0] data_fall ;// reg signal definereg [3:0] cnt_shift ;reg [9:0] data_out_reg1 ;reg [4:0] data_rise_s ;reg [4:0] data_fall_s ;// [4:0] data_rise ;assign data_rise {data_out[8],data_out[6],data_out[4],data_out[2],data_out[0]} ;// [4:0] data_fall ;assign data_fall {data_out[9],data_out[7],data_out[5],data_out[3],data_out[1]} ;// wire dataout_flag;assign dataout_flag (data_out ! data_out_reg1) ? 1b1 : 1b0 ;// reg [9:0] data_out_reg1 ;always (posedge clk_125 or negedge rst_n) beginif(~rst_n) begindata_out_reg1 10d0 ;end else begindata_out_reg1 data_out ;endend// reg [3:0] cnt_shift ;always (posedge clk_125 or negedge rst_n) beginif(~rst_n) begincnt_shift 4d0 ;end else beginif(dataout_flag 1b1 || cnt_shift 4d4) begincnt_shift 4d0 ;end else begincnt_shift cnt_shift 1b1 ; endendend// reg [4:0] data_rise_s ;// reg [4:0] data_fall_s ;always (posedge clk_125 or negedge rst_n) beginif(~rst_n) begindata_rise_s 5d0 ;data_fall_s 5d0 ;end else beginif(cnt_shift 4d4) begindata_rise_s data_rise ;data_fall_s data_fall ;end else begindata_rise_s data_rise_s[4:1] ; // 相当于右移1位。data_fall_s data_fall_s[4:1] ;endendend// 单沿到双沿采样通过调用ddio_out实现。相当于把采样时钟翻倍。
ddio_out ddio_out_inst0 (.datain_h ( data_rise_s[0] ), // 在outclk上升沿对datain_h采样传给data_out。1bit的串行数据。.datain_l ( data_fall_s[0] ), // 在outclk下降沿对datain_l采样传给data_out。1bit的串行数据。.outclock ( ~clk_125 ), // 采样时钟。.dataout ( ser_p )
);ddio_out ddio_out_inst1 (.datain_h ( ~data_rise_s[0] ), // 单端到差分之间的转换。.datain_l ( ~data_fall_s[0] ), .outclock ( ~clk_125 ), .dataout ( ser_n )
);endmodule
只放了两个新模块的代码。
timescale 1ns/1ns
module test();reg sys_clk ;reg sys_rst_n ;wire clk_p ;wire clk_n ;wire r_p ;wire r_n ;wire g_p ;wire g_n ;wire b_p ;wire b_n ;wire DDC_SCL ;wire DDC_SDA ;top top_insert(.sys_clk ( sys_clk ) ,.sys_rst_n ( sys_rst_n ) ,.clk_p ( clk_p ) ,.clk_n ( clk_n ) ,.r_p ( r_p ) ,.r_n ( r_n ) ,.g_p ( g_p ) ,.g_n ( g_n ) ,.b_p ( b_p ) ,.b_n ( b_n ) ,.DDC_SCL ( DDC_SCL ) ,.DDC_SDA ( DDC_SDA )
);parameter CYCLE 20 ;initial beginsys_clk 1b1 ;sys_rst_n 1b0 ;#( CYCLE ) ;sys_rst_n 1b1 ;endalways #( CYCLE / 2 ) sys_clk ~sys_clk ;
endmodule