【【RTC实时时钟实验 -- 在HDMI上显示-FPGA 小实验】】
2024-01-07 20:45:48
RTC实时时钟实验 – 在HDMI上显示
top.v
module RTS_TOP#(
parameter TIME_INIT = 48'h24_01_06_11_08_00 ,
parameter WAIT_TIME = 13'd8000 ,
parameter SLAVE_ADDR = 7'b1010001 , // E2PROM 浠庢満鍦板潃
parameter CLK_FREQ = 26'd50_000_000 , // 50MHz 鐨勬椂閽熼锟�?
parameter I2C_FREQ = 18'd250_000 // SCL 鐨勬椂閽熼锟�?
)(
input sys_clk ,
input rst_n ,
// to HDMI
output tmds_clk_p , // TMDS 鏃堕挓閫氶亾
output tmds_clk_n ,
output [2:0] tmds_data_p , // TMDS 鏁版嵁閫氶亾
output [2:0] tmds_data_n ,
// I2C 閫氶亾
output scl ,
inout sda
);
//-----------------------------------------------------
//-----------------------------------------------------
// next is wire and reg define
wire [15 : 0] i2c_addr ;
wire [7 : 0] i2c_data_w ;
wire i2c_rh_wl ;
wire i2c_exec ;
wire dri_clk ;
wire [7 : 0] i2c_data_r ;
wire i2c_ack ;
wire i2c_done ;
wire [7 : 0 ] sec ;
wire [7 : 0 ] min ;
wire [7 : 0 ] hour ;
wire [7 : 0 ] day ;
wire [7 : 0 ] mon ;
wire [7 : 0 ] year ;
//--------------------------------------------------------
//--------------------------------------------------------
IIC_CONTROL#(
.SLAVE_ADDR ( 7'b1010001 ),
.CLK_FREQ ( 26'd50_000_000 ),
.I2C_FREQ ( 18'd250_000 )
)u_IIC_CONTROL(
.clk ( sys_clk ),
.rst_n ( rst_n ),
.i2c_addr ( i2c_addr ),
.i2c_data_w ( i2c_data_w ),
.i2c_rh_wl ( i2c_rh_wl ),
.bit_control ( 0 ),
.i2c_exec ( i2c_exec ),
.dri_clk ( dri_clk ),
.i2c_data_r ( i2c_data_r ),
.i2c_ack ( i2c_ack ),
.i2c_done ( i2c_done ),
.scl ( scl ),
.sda ( sda )
);
PCF8563#(
.TIME_INIT ( TIME_INIT ),
.WAIT_TIME ( WAIT_TIME )
)u_PCF8563(
.clk ( dri_clk ),
.rst_n ( rst_n ),
.i2c_done ( i2c_done ),
.i2c_data_r ( i2c_data_r ),
.i2c_rh_wl ( i2c_rh_wl ),
.i2c_exec ( i2c_exec ),
.i2c_addr ( i2c_addr ),
.i2c_data_w ( i2c_data_w ),
.sec ( sec ),
.min ( min ),
.hour ( hour ),
.day ( day ),
.mon ( mon ),
.year ( year )
);
hdmi_top u_hdmi_top(
.sys_clk ( sys_clk ),
.sys_rst_n ( rst_n ),
.tmds_clk_p ( tmds_clk_p ),
.tmds_clk_n ( tmds_clk_n ),
.tmds_data_p ( tmds_data_p ),
.tmds_data_n ( tmds_data_n ),
.sec ( sec ),
.min ( min ),
.hour ( hour ),
.day ( day ),
.mon ( mon ),
.year ( year )
);
endmodule
dvi_transmitter_top.v
module dvi_transmitter_top(
input pclk ,
input sys_rst_n ,
input pclk_x5 ,
input video_hsync ,
input video_vsync ,
input video_de ,
input [23 : 0] video_din ,
output tmds_clk_p ,
output tmds_clk_n ,
output [2 : 0] tmds_data_p ,
output [2 : 0] tmds_data_n ,
output tmds_oen
);
assign tmds_oen = 1 ;
// next is define
wire reset ;
wire [9:0] blue_10bit ;
wire [9:0] green_10bit ;
wire [9:0] red_10bit ;
wire [2:0] tmds_data_serial ;
wire tmds_clk_serial ;
reset_syn u_reset_syn(
.pclk ( pclk ),
.reset_n ( sys_rst_n ),
.reset ( reset )
);
dvi_encoder u_dvi_encoder_blue(
.clkin ( pclk ),
.rstin ( reset ),
.din ( video_din[7:0] ),
.c0 ( video_hsync ),
.c1 ( video_vsync ),
.de ( video_de ),
.dout ( blue_10bit )
);
dvi_encoder u_dvi_encoder_green(
.clkin ( pclk ),
.rstin ( reset ),
.din ( video_din[15:8] ),
.c0 ( 1'b0 ),
.c1 ( 1'b0 ),
.de ( video_de ),
.dout ( green_10bit )
);
dvi_encoder u_dvi_encoder_red(
.clkin ( pclk ),
.rstin ( reset ),
.din ( video_din[23:16] ),
.c0 ( 1'b0 ),
.c1 ( 1'b0 ),
.de ( video_de ),
.dout ( red_10bit )
);
serializer10 u_serializer10_blue(
.reset ( reset ),
.paralell_clk ( pclk ),
.serial_clk_5x ( pclk_x5 ),
.paralell_data ( blue_10bit ),
.serial_data_out ( tmds_data_serial[0] )
);
serializer10 u_serializer10_green(
.reset ( reset ),
.paralell_clk ( pclk ),
.serial_clk_5x ( pclk_x5 ),
.paralell_data ( green_10bit ),
.serial_data_out ( tmds_data_serial[1] )
);
serializer10 u_serializer10_red(
.reset ( reset ),
.paralell_clk ( pclk ),
.serial_clk_5x ( pclk_x5 ),
.paralell_data ( red_10bit ),
.serial_data_out ( tmds_data_serial[2] )
);
serializer10 u_serializer10_clk(
.reset ( reset ),
.paralell_clk ( pclk ),
.serial_clk_5x ( pclk_x5 ),
.paralell_data ( 10'b1111100000 ),
.serial_data_out ( tmds_clk_serial )
);
//转换差分信号
OBUFDS #(
.IOSTANDARD ("TMDS_33") // I/O电平标准为TMDS
) TMDS0 (
.I (tmds_data_serial[0]),
.O (tmds_data_p[0]),
.OB (tmds_data_n[0])
);
OBUFDS #(
.IOSTANDARD ("TMDS_33") // I/O电平标准为TMDS
) TMDS1 (
.I (tmds_data_serial[1]),
.O (tmds_data_p[1]),
.OB (tmds_data_n[1])
);
OBUFDS #(
.IOSTANDARD ("TMDS_33") // I/O电平标准为TMDS
) TMDS2 (
.I (tmds_data_serial[2]),
.O (tmds_data_p[2]),
.OB (tmds_data_n[2])
);
OBUFDS #(
.IOSTANDARD ("TMDS_33") // I/O电平标准为TMDS
) TMDS3 (
.I (tmds_clk_serial),
.O (tmds_clk_p),
.OB (tmds_clk_n)
);
endmodule
encoder.v
module dvi_encoder (
input clkin, // pixel clock input
input rstin, // async. reset input (active high)
input [7:0] din, // data inputs: expect registered
input c0, // c0 input
input c1, // c1 input
input de, // de input
output reg [9:0] dout // data outputs
);
// Counting number of 1s and 0s for each incoming pixel
// component. Pipe line the result.
// Register Data Input so it matches the pipe lined adder
// output
reg [3:0] n1d; //number of 1s in din
reg [7:0] din_q;
//计算像素数据中“1”的个数
always @ (posedge clkin) begin
n1d <=#1 din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7];
din_q <=#1 din;
end
///
// Stage 1: 8 bit -> 9 bit
// Refer to DVI 1.0 Specification, page 29, Figure 3-5
///
wire decision1;
assign decision1 = (n1d > 4'h4) | ((n1d == 4'h4) & (din_q[0] == 1'b0));
wire [8:0] q_m;
assign q_m[0] = din_q[0];
assign q_m[1] = (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]);
assign q_m[2] = (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]);
assign q_m[3] = (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]);
assign q_m[4] = (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]);
assign q_m[5] = (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]);
assign q_m[6] = (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]);
assign q_m[7] = (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]);
assign q_m[8] = (decision1) ? 1'b0 : 1'b1;
/
// Stage 2: 9 bit -> 10 bit
// Refer to DVI 1.0 Specification, page 29, Figure 3-5
/
reg [3:0] n1q_m, n0q_m; // number of 1s and 0s for q_m
always @ (posedge clkin) begin
n1q_m <=#1 q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7];
n0q_m <=#1 4'h8 - (q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]);
end
parameter CTRLTOKEN0 = 10'b1101010100;
parameter CTRLTOKEN1 = 10'b0010101011;
parameter CTRLTOKEN2 = 10'b0101010100;
parameter CTRLTOKEN3 = 10'b1010101011;
reg [4:0] cnt; //disparity counter, MSB is the sign bit
wire decision2, decision3;
assign decision2 = (cnt == 5'h0) | (n1q_m == n0q_m);
/
// [(cnt > 0) and (N1q_m > N0q_m)] or [(cnt < 0) and (N0q_m > N1q_m)]
/
assign decision3 = (~cnt[4] & (n1q_m > n0q_m)) | (cnt[4] & (n0q_m > n1q_m));
// pipe line alignment
reg de_q, de_reg;
reg c0_q, c1_q;
reg c0_reg, c1_reg;
reg [8:0] q_m_reg;
always @ (posedge clkin) begin
de_q <=#1 de;
de_reg <=#1 de_q;
c0_q <=#1 c0;
c0_reg <=#1 c0_q;
c1_q <=#1 c1;
c1_reg <=#1 c1_q;
q_m_reg <=#1 q_m;
end
///
// 10-bit out
// disparity counter
///
always @ (posedge clkin or posedge rstin) begin
if(rstin) begin
dout <= 10'h0;
cnt <= 5'h0;
end else begin
if (de_reg) begin
if(decision2) begin
dout[9] <=#1 ~q_m_reg[8];
dout[8] <=#1 q_m_reg[8];
dout[7:0] <=#1 (q_m_reg[8]) ? q_m_reg[7:0] : ~q_m_reg[7:0];
cnt <=#1 (~q_m_reg[8]) ? (cnt + n0q_m - n1q_m) : (cnt + n1q_m - n0q_m);
end else begin
if(decision3) begin
dout[9] <=#1 1'b1;
dout[8] <=#1 q_m_reg[8];
dout[7:0] <=#1 ~q_m_reg[7:0];
cnt <=#1 cnt + {q_m_reg[8], 1'b0} + (n0q_m - n1q_m);
end else begin
dout[9] <=#1 1'b0;
dout[8] <=#1 q_m_reg[8];
dout[7:0] <=#1 q_m_reg[7:0];
cnt <=#1 cnt - {~q_m_reg[8], 1'b0} + (n1q_m - n0q_m);
end
end
end else begin
case ({c1_reg, c0_reg})
2'b00: dout <=#1 CTRLTOKEN0;
2'b01: dout <=#1 CTRLTOKEN1;
2'b10: dout <=#1 CTRLTOKEN2;
default: dout <=#1 CTRLTOKEN3;
endcase
cnt <=#1 5'h0;
end
end
end
endmodule
hdmi_display.v
module video_display(
input pixel_clk ,
input sys_rst_n ,
input [ 11 : 0 ] pixel_xpos_w ,
input [ 11 : 0 ] pixel_ypos_w ,
output reg [ 23 : 0 ] pixel_data_w ,
// display
input [ 7 : 0] sec ,
input [ 7 : 0] min ,
input [ 7 : 0] hour ,
input [ 7 : 0] day ,
input [ 7 : 0] mon ,
input [ 7 : 0] year
);
// 我不想显示年月日?? 直接存起??
wire [7 : 0] year1 ;
wire [7 : 0] mon1 ;
wire [7 : 0] day1 ;
assign day1 = day ;
assign year1 = year ;
assign mon1 = mon ;
// 暂存起来
//parameter define
localparam CHAR_X_START = 11'd50; //字符起始点横坐标
localparam CHAR_Y_START = 11'd100; //字符起始点纵坐标
localparam CHAR_WIDTH = 10'd88; //字符宽度 32*11 = 352
localparam CHAR_HEIGHT = 10'd16; //字符高度
//棰滆??
localparam BACK_COLOR = 24'hE0FFFF; //背景色,浅蓝??
localparam CHAR_COLOR = 24'hff0000; //字符颜色,红??
reg [127:0] char[10:0]; //字符数组
// 这里是字符的显示 我想做的??
//----------------------
// 09 : 20 00
// 两个+空格+冒号+空格+两个+空格+空格+两个 11??
//
//
//----------------------
always @(posedge pixel_clk)
begin
char[0] = 128'h00000018244242424242424224180000;/*"0",0*/
char[1] = 128'h000000083808080808080808083E0000;/*"1",1*/
char[2] = 128'h0000003C4242420204081020427E0000;/*"2",2*/
char[3] = 128'h0000003C4242020418040242423C0000;/*"3",3*/
char[4] = 128'h000000040C0C142424447F04041F0000;/*"4",4*/
char[5] = 128'h0000007E404040784402024244380000;/*"5",5*/
char[6] = 128'h000000182440405C62424242221C0000;/*"6",6*/
char[7] = 128'h0000007E420404080810101010100000;/*"7",7*/
char[8] = 128'h0000003C4242422418244242423C0000;/*"8",8*/
char[9] = 128'h0000003844424242463A020224180000;/*"9",9*/
char[10] = 128'h00000000000018180000000018180000;/*":",10*/
end
// 准备显示
always@( posedge pixel_clk or negedge sys_rst_n)
begin
if( sys_rst_n == 0)
begin
pixel_data_w <=BACK_COLOR ;
end
else
// //09 : 20 00
// 两个+空格+冒号+空格+两个+空格+空格+两个 11??
// 小时的十??
if( (pixel_xpos_w >= CHAR_X_START + CHAR_WIDTH/11*0)
&& (pixel_xpos_w < CHAR_X_START + CHAR_WIDTH/11*1)
&& (pixel_ypos_w >= CHAR_Y_START)
&& (pixel_ypos_w < CHAR_Y_START + CHAR_HEIGHT) )
begin
if( char[hour[7 : 4]][ (CHAR_HEIGHT + CHAR_Y_START - pixel_ypos_w) * 8
-((pixel_xpos_w - (CHAR_X_START)) % 8) -1 ] )
pixel_data_w <=CHAR_COLOR ;
else
pixel_data_w <=BACK_COLOR ;
end
// 小时的个??
else if( (pixel_xpos_w >= CHAR_X_START + CHAR_WIDTH/11*1)
&& (pixel_xpos_w < CHAR_X_START + CHAR_WIDTH/11*2)
&& (pixel_ypos_w >= CHAR_Y_START)
&& (pixel_ypos_w < CHAR_Y_START + CHAR_HEIGHT) )
begin
if( char[hour[3 : 0]][ (CHAR_HEIGHT + CHAR_Y_START - pixel_ypos_w) * 8
-((pixel_xpos_w - (CHAR_X_START)) % 8) -1 ] )
pixel_data_w <=CHAR_COLOR ;
else
pixel_data_w <=BACK_COLOR ;
end
// 空格
else if( (pixel_xpos_w >= CHAR_X_START + CHAR_WIDTH/11*2)
&& (pixel_xpos_w < CHAR_X_START + CHAR_WIDTH/11*3)
&& (pixel_ypos_w >= CHAR_Y_START)
&& (pixel_ypos_w < CHAR_Y_START + CHAR_HEIGHT) )
begin
pixel_data_w <=BACK_COLOR ;
end
// 冒号
else if( (pixel_xpos_w >= CHAR_X_START + CHAR_WIDTH/11*3)
&& (pixel_xpos_w < CHAR_X_START + CHAR_WIDTH/11*4)
&& (pixel_ypos_w >= CHAR_Y_START)
&& (pixel_ypos_w < CHAR_Y_START + CHAR_HEIGHT) )
begin
if( char[10][ (CHAR_HEIGHT + CHAR_Y_START - pixel_ypos_w) * 8
-((pixel_xpos_w - (CHAR_X_START)) % 8) -1 ] )
pixel_data_w <=CHAR_COLOR ;
else
pixel_data_w <=BACK_COLOR ;
end
// 空格
else if( (pixel_xpos_w >= CHAR_X_START + CHAR_WIDTH/11*4)
&& (pixel_xpos_w < CHAR_X_START + CHAR_WIDTH/11*5)
&& (pixel_ypos_w >= CHAR_Y_START)
&& (pixel_ypos_w < CHAR_Y_START + CHAR_HEIGHT) )
begin
pixel_data_w <=BACK_COLOR ;
end
// 分钟的十??
else if( (pixel_xpos_w >= CHAR_X_START + CHAR_WIDTH/11*5)
&& (pixel_xpos_w < CHAR_X_START + CHAR_WIDTH/11*6)
&& (pixel_ypos_w >= CHAR_Y_START)
&& (pixel_ypos_w < CHAR_Y_START + CHAR_HEIGHT) )
begin
if( char[min[7 : 4]][ (CHAR_HEIGHT + CHAR_Y_START - pixel_ypos_w) * 8
-((pixel_xpos_w - (CHAR_X_START)) % 8) -1 ] )
pixel_data_w <=CHAR_COLOR ;
else
pixel_data_w <=BACK_COLOR ;
end
// 分钟的个??
else if( (pixel_xpos_w >= CHAR_X_START + CHAR_WIDTH/11*6)
&& (pixel_xpos_w < CHAR_X_START + CHAR_WIDTH/11*7)
&& (pixel_ypos_w >= CHAR_Y_START)
&& (pixel_ypos_w < CHAR_Y_START + CHAR_HEIGHT) )
begin
if( char[min[3 : 0]][ (CHAR_HEIGHT + CHAR_Y_START - pixel_ypos_w) * 8
-((pixel_xpos_w - (CHAR_X_START)) % 8) -1 ] )
pixel_data_w <=CHAR_COLOR ;
else
pixel_data_w <=BACK_COLOR ;
end
//空格
// 空格
else if( (pixel_xpos_w >= CHAR_X_START + CHAR_WIDTH/11*7)
&& (pixel_xpos_w < CHAR_X_START + CHAR_WIDTH/11*8)
&& (pixel_ypos_w >= CHAR_Y_START)
&& (pixel_ypos_w < CHAR_Y_START + CHAR_HEIGHT) )
begin
pixel_data_w <=BACK_COLOR ;
end
// 空格
else if( (pixel_xpos_w >= CHAR_X_START + CHAR_WIDTH/11*8)
&& (pixel_xpos_w < CHAR_X_START + CHAR_WIDTH/11*9)
&& (pixel_ypos_w >= CHAR_Y_START)
&& (pixel_ypos_w < CHAR_Y_START + CHAR_HEIGHT) )
begin
pixel_data_w <=BACK_COLOR ;
end
// 秒的十位
else if( (pixel_xpos_w >= CHAR_X_START + CHAR_WIDTH/11*9)
&& (pixel_xpos_w < CHAR_X_START + CHAR_WIDTH/11*10)
&& (pixel_ypos_w >= CHAR_Y_START)
&& (pixel_ypos_w < CHAR_Y_START + CHAR_HEIGHT) )
begin
if( char[sec[7 : 4]][ (CHAR_HEIGHT + CHAR_Y_START - pixel_ypos_w) * 8
-((pixel_xpos_w - (CHAR_X_START)) % 8) -1 ] )
pixel_data_w <=CHAR_COLOR ;
else
pixel_data_w <=BACK_COLOR ;
end
// 分钟的个??
else if( (pixel_xpos_w >= CHAR_X_START + CHAR_WIDTH/11*10)
&& (pixel_xpos_w < CHAR_X_START + CHAR_WIDTH/11*11)
&& (pixel_ypos_w >= CHAR_Y_START)
&& (pixel_ypos_w < CHAR_Y_START + CHAR_HEIGHT) )
begin
if( char[sec[3 : 0]][ (CHAR_HEIGHT + CHAR_Y_START - pixel_ypos_w) * 8
-((pixel_xpos_w - (CHAR_X_START)) % 8) -1 ] )
pixel_data_w <=CHAR_COLOR ;
else
pixel_data_w <=BACK_COLOR ;
end
else
begin
pixel_data_w <= BACK_COLOR; //屏幕背景为白??
end
end
endmodule
HDMI_top.v
module hdmi_top(
input sys_clk,
input sys_rst_n,
output tmds_clk_p, // TMDS 时钟通道
output tmds_clk_n,
output [2:0] tmds_data_p, // TMDS 数据通道
output [2:0] tmds_data_n ,
// next is from PCF
input [ 7 : 0] sec ,
input [ 7 : 0] min ,
input [ 7 : 0] hour ,
input [ 7 : 0] day ,
input [ 7 : 0] mon ,
input [ 7 : 0] year
);
//wire define
wire pixel_clk;
wire pixel_clk_5x;
wire clk_locked;
wire [10:0] pixel_xpos_w;
wire [10:0] pixel_ypos_w;
wire [23:0] pixel_data_w;
wire video_hs;
wire video_vs;
wire video_de;
wire [23:0] video_rgb;
// next is main code
clk_wiz_0 instance_name1
(
// Clock out ports
.clk_out1(pixel_clk), // output clk_out1
.clk_out2(pixel_clk_5x), // output clk_out2
// Status and control signals
.reset(~sys_rst_n), // input reset
.locked(clk_locked), // output locked
// Clock in ports
.clk_in1(sys_clk)
);
video_driver u_video_driver(
.pixel_clk ( pixel_clk ),
.rst_n ( sys_rst_n ),
.pixel_data ( pixel_data_w ),
.video_rgb ( video_rgb ),
.video_hs ( video_hs ),
.video_vs ( video_vs ),
.video_de ( video_de ),
.pixel_xpos ( pixel_xpos_w ),
.pixel_ypos ( pixel_ypos_w )
);
video_display u_video_display(
.pixel_clk ( pixel_clk ),
.sys_rst_n ( sys_rst_n ),
.pixel_xpos_w ( pixel_xpos_w ),
.pixel_ypos_w ( pixel_ypos_w ),
.pixel_data_w ( pixel_data_w ),
.sec ( sec ),
.min ( min ),
.hour ( hour ),
.day ( day ),
.mon ( mon ),
.year ( year )
);
dvi_transmitter_top u_dvi_transmitter_top(
.pclk ( pixel_clk ),
.sys_rst_n ( sys_rst_n & clk_locked ),
.pclk_x5 ( pixel_clk_5x ),
.video_hsync ( video_hs ),
.video_vsync ( video_vs ),
.video_de ( video_de ),
.video_din ( video_rgb ),
.tmds_clk_p ( tmds_clk_p ),
.tmds_clk_n ( tmds_clk_n ),
.tmds_data_p ( tmds_data_p ),
.tmds_data_n ( tmds_data_n ),
.tmds_oen ( )
);
endmodule
I2c_dri.v
module IIC_CONTROL #(
parameter SLAVE_ADDR = 7'b1010001 , // E2PROM 从机地址
parameter CLK_FREQ = 26'd50_000_000 , // 50MHz 的时钟频率
parameter I2C_FREQ = 18'd250_000 // SCL 的时钟频率
)
(
input clk ,
input rst_n ,
// ---------------------------------------------- //
input [15 : 0] i2c_addr , // 地址
input [7 : 0] i2c_data_w , // 数据
input i2c_rh_wl , // 判断 是 read or write
input bit_control , // 1是 16位 0 是 8位
input i2c_exec ,
// ------------------------------------------------ //
output reg dri_clk ,
output reg [7 : 0] i2c_data_r ,
output reg i2c_ack ,
output reg i2c_done ,
// -------------------------------------------------- //
output reg scl ,
inout sda
);
// --------------------------------------------------------//
// next is define //
// --------------------------------------------------------//
reg [9 : 0] clk_cnt ;
wire [8 : 0] dri_cnt ;
reg [2 : 0] state ;
reg [2 : 0] next_state ;
reg st_done ; // 在 状态机里面用来提示数据完成可以跳转
reg sda_dir ; // sda方向控制器
reg sda_out ; // 选择FPGA输入模式之后赋予sda线上
wire sda_in ; // sda输入信号
reg [6 : 0] cnt ; // 我们为了第三部分状态机而准备的
reg [15: 0] addr_save ; // 地址存储
reg [7 : 0] data_w_save ; // 数据写的暂存
reg wr_flag ; // 0 是 写 1 是 读
// 这三个是 暂存的方便调度的
reg [7 : 0] data_r_save ; // 读到的数据存储方便整合
// --------------------------------------------------------- //
// parameter define //
parameter st_idle = 3'b000 ; // 空闲状态
parameter st_sladdr = 3'b001 ; // 发送器件地址
parameter st_addr16 = 3'b010 ; // 发送高八位地址
parameter st_addr8 = 3'b011 ; // 发送低八位地址
parameter st_data_wr = 3'b100 ; // 写数据
parameter st_addr_rd = 3'b101 ; // 再次发送器件地址读
parameter st_data_rd = 3'b110 ; // 读数据
parameter st_stop = 3'b111 ; // 结束操作停止位
// ---------------------------------------------------- //
// next is main code //
// -------------------------------------------------------//
assign dri_cnt = (CLK_FREQ/I2C_FREQ ) >> 2 ;
always@(posedge clk or negedge rst_n )
begin
if(rst_n == 0)
begin
dri_clk <= 0 ;
clk_cnt <= 0 ;
end
else if( clk_cnt == dri_cnt[8:1] - 1)
begin
clk_cnt <= 0 ;
dri_clk <= ~dri_clk ;
end
else
begin
dri_clk <= dri_clk ;
clk_cnt <= clk_cnt + 1 ;
end
end
// 下面开始状态机的叙述
// 同步时序描述状态转移
always@(posedge dri_clk or negedge rst_n)
begin
if(rst_n == 0)
begin
state <= st_idle ;
end // 处于空闲状态
else
begin
state <= next_state ;
end
end
// 组合逻辑判断状态转移条件
always@(*)
begin
next_state <= st_idle ;
case(state)
st_idle :
begin
if(i2c_exec == 1)
begin
next_state <= st_sladdr ;
end
else
begin
next_state <= st_idle ;
end
end
// 当触发了i2c_exec 时候 可以由 空闲状态转移到
st_sladdr :
begin
if(st_done == 1)
begin
if(bit_control == 1)
next_state <= st_addr16 ;
else
next_state <= st_addr8 ;
end
else
begin
next_state <= st_sladdr ;
end
end
// 当 触发了 st_done 之后 通过 bit_control 选择是低八位 还是高八位的传输
st_addr16 :
begin
if(st_done == 1)
begin
next_state <= st_addr8 ;
end
else
begin
next_state <= st_addr16 ;
end
end
// 高位 用完 轮到 低位的 传输
st_addr8 :
begin
if(st_done == 1)
begin
if(wr_flag == 0)
next_state <= st_data_wr ;
else
next_state <= st_addr_rd ;
end
else
begin
next_state <= st_addr8 ;
end
end
// 先来判断 写数据的 st_data_wr 数据代号是 4
st_data_wr :
begin
if(st_done == 1)
begin
next_state <= st_stop ;
end
else
begin
next_state <= st_data_wr ;
end
end
//
st_addr_rd :
begin
if(st_done == 1)
begin
next_state <= st_data_rd ;
end
else
begin
next_state <= st_addr_rd ;
end
end
//
st_data_rd :
begin
if(st_done == 1)
begin
next_state <= st_stop ;
end
else
begin
next_state <= st_data_rd ;
end
end
//
st_stop :
begin
if(st_done == 1)
begin
next_state <= st_idle ;
end
else
begin
next_state <= st_stop ;
end
end
default:
next_state <= st_idle ;
endcase
end
/ 下面来考虑另一个状态机的第三部分 --- 时序电路描述状态输出
// 设置一个变量 来控制 SDA的朝向
assign sda = sda_dir ? sda_out : 1'bz ; // sda_dir 为1 FPGA控制
assign sda_in = sda ; // 把sda当成了输出
always@(posedge dri_clk or negedge rst_n )
begin
if( rst_n == 0)
begin
//首先根据输入输出 来判断 SCL 与 SDA 必须都为高
scl <= 1 ;
sda_dir <= 1 ;
sda_out <= 1 ;
// 剩下的输出 i2c_data_r(输出) == data_r_save
i2c_data_r <= 0 ;
data_r_save <= 0 ;
// 下面是端口的另外两个输出 i2c_ack 和 i2c_done
i2c_ack <= 0 ;
i2c_done <= 0 ;
// 接下里是 内部信号的调节 这两个一个是内部后续的计数 还有一个本次case完成的结束信号
cnt <= 0 ;
st_done <= 0 ;
// 下面是三个暂存信号一个是 读写标志位 还有 传入的地址暂存 传入的数据暂存
wr_flag <= 0 ;
addr_save <= 0 ;
data_w_save <= 0 ;
end
else
begin
st_done <= 0 ; // 脉冲信号
cnt <= cnt + 1 ;
//这里写在了 case之前就代表了 不用刻意在内部去调配 st_done 或是cnt
case(state)
st_idle :
begin
scl <= 1 ;
sda_dir <= 1 ;
sda_out <= 1 ;
//这两个写不写不所谓 因为根本没用到
i2c_data_r <= 0 ;
data_r_save <= 0 ;
i2c_done <= 0 ;
//
cnt <= 0 ;
st_done <= 0 ;
// 开始
if( i2c_exec == 1) begin
wr_flag <= i2c_rh_wl ;
addr_save <= i2c_addr ;
data_w_save <= i2c_data_w ;
i2c_ack <= 0 ;
end
end
// 这里先传递的是
st_sladdr :
begin
case(cnt)
7'd1 :
sda_out <= 0 ;
7'd3 :
scl <= 0 ;
7'd4 :
sda_out <= SLAVE_ADDR[6] ;
7'd5 :
scl <= 1'b1 ;
7'd7 :
scl <= 1'b0 ;
7'd8 :
sda_out <= SLAVE_ADDR[5] ;
7'd9 :
scl <= 1'b1 ;
7'd11 :
scl <= 1'b0 ;
7'd12 :
sda_out <= SLAVE_ADDR[4] ;
7'd13 :
scl <= 1'b1 ;
7'd15 :
scl <= 1'b0 ;
7'd16 :
sda_out <= SLAVE_ADDR[3] ;
7'd17 :
scl <= 1'b1 ;
7'd19 :
scl <= 1'b0 ;
7'd20 :
sda_out <= SLAVE_ADDR[2] ;
7'd21 :
scl <= 1'b1 ;
7'd23 :
scl <= 1'b0 ;
7'd24 :
sda_out <= SLAVE_ADDR[1] ;
7'd25 :
scl <= 1'b1 ;
7'd27 :
scl <= 1'b0 ;
7'd28 :
sda_out <= SLAVE_ADDR[0] ;
7'd29 :
scl <= 1'b1 ;
7'd31 :
scl <= 1'b0 ;
7'd32 :
sda_out <= 1'b0 ;
// 此处完成了 数据的传递 接下来的任务是 反馈
7'd33 :
scl <= 1'b1 ;
7'd35 :
scl <= 1'b0 ;
7'd36 :
sda_dir <= 1'b0 ; // 下放控制权给从机端口
7'd37 :
scl <= 1'b1 ;
// 下一时刻判断是否 有正确的反馈拉低 并确定 st_done = 1
7'd38 :
begin
st_done <= 1'b1 ;
if( sda_in == 1)
i2c_ack <= 1'b1 ;
end
7'd39 :
begin
scl <= 1'b0 ;
cnt <= 7'b0 ;
end
default :
;
endcase
end
//发送高8位字节
st_addr16 :
begin
case(cnt)
7'd0 :
begin // 39之后移动一格就是0 0 此处即可以开始
//把使能交还给FPGA端
sda_dir <= 1'b1 ;
sda_out <= addr_save[15] ;
end
// 第一个转换有点时序差距 后面都是 每隔4 sda变化一次
7'd1 :
scl <= 1'b1 ;
7'd3 :
scl <= 1'b0 ;
7'd4 :
sda_out <= addr_save[14] ;
7'd5 :
scl <= 1'b1 ;
7'd7 :
scl <= 1'b0 ;
7'd8 :
sda_out <= addr_save[13] ;
7'd9 :
scl <= 1'b1 ;
7'd11 :
scl <= 1'b0 ;
7'd12 :
sda_out <= addr_save[12] ;
7'd13 :
scl <= 1'b1 ;
7'd15 :
scl <= 1'b0 ;
7'd16 :
sda_out <= addr_save[11] ;
7'd17 :
scl <= 1'b1 ;
7'd19 :
scl <= 1'b0 ;
7'd20 :
sda_out <= addr_save[10] ;
7'd21 :
scl <= 1'b1 ;
7'd23 :
scl <= 1'b0 ;
7'd24 :
sda_out <= addr_save[9] ;
7'd25 :
scl <= 1'b1 ;
7'd27 :
scl <= 1'b0 ;
7'd28 :
sda_out <= addr_save[8] ;
// 29 拉升 31下降 32放控制权 33拉升 34结束并作判断 35 拉低 cnt归零为下一状态准备
7'd29 :
scl <= 1'b1 ;
7'd31 :
scl <= 1'b0 ;
7'd32 :
sda_dir <= 1'b0 ;
7'd33 :
scl <= 1'b1 ;
7'd34 :
begin
st_done <= 1'b1 ; //完成
if(sda_in == 1)
i2c_ack <= 1'b1 ; // scl拉高时 反馈 i2c_ack = 1 表示有错误
end
7'd35 :
begin
scl <= 1'b0 ;
cnt <= 7'b0 ;
end
default :
;
endcase
end
//发送低8位字节
st_addr8 :
begin
// 和上面这个写法是一样的 对于cnt = 0 sda_dir 交回FPGA控制权 并立刻赋值
case(cnt)
7'd0:
begin
sda_dir <= 1'b1 ;
sda_out <= addr_save[7]; //字地址
end
7'd1 :
scl <= 1'b1;
7'd3 :
scl <= 1'b0;
7'd4 :
sda_out <= addr_save[6];
7'd5 :
scl <= 1'b1;
7'd7 :
scl <= 1'b0;
7'd8 :
sda_out <= addr_save[5];
7'd9 :
scl <= 1'b1;
7'd11 :
scl <= 1'b0;
7'd12 :
sda_out <= addr_save[4];
7'd13 :
scl <= 1'b1;
7'd15 :
scl <= 1'b0;
7'd16 :
sda_out <= addr_save[3];
7'd17 :
scl <= 1'b1;
7'd19 :
scl <= 1'b0;
7'd20 :
sda_out <= addr_save[2];
7'd21 :
scl <= 1'b1;
7'd23 :
scl <= 1'b0;
7'd24 :
sda_out <= addr_save[1];
7'd25 :
scl <= 1'b1;
7'd27 :
scl <= 1'b0;
7'd28 :
sda_out <= addr_save[0];
7'd29 :
scl <= 1'b1 ;
7'd31 :
scl <= 1'b0 ;
7'd32 :
sda_dir <= 1'b0 ;
7'd33 :
scl <= 1'b1 ;
7'd34 :
begin
st_done <= 1'b1 ; //完成
if(sda_in == 1)
i2c_ack <= 1'b1 ; // scl拉高时 反馈 i2c_ack = 1 表示有错误
end
7'd35 :
begin
scl <= 1'b0 ;
cnt <= 7'b0 ;
end
default :
;
endcase
end
//
st_data_wr :
begin
// 和上面这个写法是一样的 对于cnt = 0 sda_dir 交回FPGA控制权 并立刻赋值
case(cnt)
7'd0:
begin
sda_dir <= 1'b1 ;
sda_out <= data_w_save[7]; //字地址
end
7'd1 :
scl <= 1'b1;
7'd3 :
scl <= 1'b0;
7'd4 :
sda_out <= data_w_save[6];
7'd5 :
scl <= 1'b1;
7'd7 :
scl <= 1'b0;
7'd8 :
sda_out <= data_w_save[5];
7'd9 :
scl <= 1'b1;
7'd11 :
scl <= 1'b0;
7'd12 :
sda_out <= data_w_save[4];
7'd13 :
scl <= 1'b1;
7'd15 :
scl <= 1'b0;
7'd16 :
sda_out <= data_w_save[3];
7'd17 :
scl <= 1'b1;
7'd19 :
scl <= 1'b0;
7'd20 :
sda_out <= data_w_save[2];
7'd21 :
scl <= 1'b1;
7'd23 :
scl <= 1'b0;
7'd24 :
sda_out <= data_w_save[1];
7'd25 :
scl <= 1'b1;
7'd27 :
scl <= 1'b0;
7'd28 :
sda_out <= data_w_save[0];
// 29 拉升 31下降 32放控制权 33拉升 34结束并作判断 35 拉低 cnt归零为下一状态准备
7'd29 :
scl <= 1'b1 ;
7'd31 :
scl <= 1'b0 ;
7'd32 :
sda_dir <= 1'b0 ;
7'd33 :
scl <= 1'b1 ;
7'd34 :
begin
st_done <= 1'b1 ; //完成
if(sda_in == 1)
i2c_ack <= 1'b1 ; // scl拉高时 反馈 i2c_ack = 1 表示有错误
end
7'd35 :
begin
scl <= 1'b0 ;
cnt <= 7'b0 ;
end
default :
;
endcase
end
// 读控制信号 可以开始读了
st_addr_rd :
begin
// 这里的过程应该和上面的那个 st_sladdr一样 先写地址
// 一样又不太一样
case(cnt)
7'd0 :
begin
sda_dir <= 1'b1;
sda_out <= 1'b1;
end
7'd1 :
scl <= 1'b1;
7'd2 :
sda_out <= 1'b0; //重新开始
7'd3 :
scl <= 1'b0;
7'd4 :
sda_out <= SLAVE_ADDR[6]; //传送器件地址
7'd5 :
scl <= 1'b1;
7'd7 :
scl <= 1'b0;
7'd8 :
sda_out <= SLAVE_ADDR[5];
7'd9 :
scl <= 1'b1;
7'd11:
scl <= 1'b0;
7'd12:
sda_out <= SLAVE_ADDR[4];
7'd13:
scl <= 1'b1;
7'd15:
scl <= 1'b0;
7'd16:
sda_out <= SLAVE_ADDR[3];
7'd17:
scl <= 1'b1;
7'd19:
scl <= 1'b0;
7'd20:
sda_out <= SLAVE_ADDR[2];
7'd21:
scl <= 1'b1;
7'd23:
scl <= 1'b0;
7'd24:
sda_out <= SLAVE_ADDR[1];
7'd25:
scl <= 1'b1;
7'd27:
scl <= 1'b0;
7'd28:
sda_out <= SLAVE_ADDR[0];
7'd29:
scl <= 1'b1;
7'd31:
scl <= 1'b0;
7'd32:
sda_out <= 1'b1; //1:读
7'd33:
scl <= 1'b1;
7'd35:
scl <= 1'b0;
7'd36:
begin
sda_dir <= 1'b0;
sda_out <= 1'b1;
end
7'd37:
scl <= 1'b1;
7'd38:
begin //从机应答
st_done <= 1'b1;
if(sda_in == 1'b1) //高电平表示未应答
i2c_ack <= 1'b1; //拉高应答标志位
end
7'd39:
begin
scl <= 1'b0;
cnt <= 7'b0;
end
default :
;
endcase
end
st_data_rd :
begin //读取数据(8 bit)
case(cnt)
7'd0:
sda_dir <= 1'b0;
7'd1:
begin
data_r_save[7] <= sda_in;
scl <= 1'b1;
end
7'd3:
scl <= 1'b0;
7'd5:
begin
data_r_save[6] <= sda_in ;
scl <= 1'b1 ;
end
7'd7:
scl <= 1'b0;
7'd9:
begin
data_r_save[5] <= sda_in;
scl <= 1'b1 ;
end
7'd11:
scl <= 1'b0;
7'd13:
begin
data_r_save[4] <= sda_in;
scl <= 1'b1 ;
end
7'd15:
scl <= 1'b0;
7'd17:
begin
data_r_save[3] <= sda_in;
scl <= 1'b1 ;
end
7'd19:
scl <= 1'b0;
7'd21:
begin
data_r_save[2] <= sda_in;
scl <= 1'b1 ;
end
7'd23:
scl <= 1'b0;
7'd25:
begin
data_r_save[1] <= sda_in;
scl <= 1'b1 ;
end
7'd27:
scl <= 1'b0;
7'd29:
begin
data_r_save[0] <= sda_in;
scl <= 1'b1 ;
end
7'd31:
scl <= 1'b0;
7'd32:
begin
sda_dir <= 1'b1;
sda_out <= 1'b1;
end
7'd33:
scl <= 1'b1;
7'd34:
st_done <= 1'b1; //非应答
7'd35:
begin
scl <= 1'b0;
cnt <= 7'b0;
i2c_data_r <= data_r_save;
end
default :
;
endcase
end
st_stop:
begin //结束I2C操作
case(cnt)
7'd0:
begin
sda_dir <= 1'b1; //结束I2C
sda_out <= 1'b0;
end
7'd1 :
scl <= 1'b1;
7'd3 :
sda_out <= 1'b1;
7'd15:
st_done <= 1'b1;
7'd16:
begin
cnt <= 7'b0;
i2c_done <= 1'b1; //向上层模块传递I2C结束信号
end
default :
;
endcase
end
endcase
end
end
endmodule
PCF8563.v
module PCF8563#(
parameter TIME_INIT = 48'h24_01_06_14_30_00 ,
parameter WAIT_TIME = 13'd8000
)(
input clk ,
input rst_n ,
input i2c_done ,
input [7 : 0] i2c_data_r , // this thing from i2c to HDMI
//
output reg i2c_rh_wl ,
output reg i2c_exec ,
output reg [15 : 0] i2c_addr ,
output reg [ 7 : 0] i2c_data_w , // this is give to i2c
//
output reg [ 7 : 0] sec ,
output reg [ 7 : 0] min ,
output reg [ 7 : 0] hour ,
output reg [ 7 : 0] day ,
output reg [ 7 : 0] mon ,
output reg [ 7 : 0] year
);
// parameter and define
reg [3 : 0] reg_cnt ;
reg [12 : 0] wait_cnt ;
//
always@(posedge clk or negedge rst_n)
begin
if(rst_n == 0 )
begin
i2c_rh_wl <= 0 ;
i2c_exec <= 0 ;
i2c_addr <= 0 ;
i2c_data_w <= 0 ;
sec <= 0 ;
min <= 0 ;
hour <= 0 ;
day <= 0 ;
mon <= 0 ;
year <= 0 ;
reg_cnt <= 0 ;
wait_cnt <= 0 ;
end
else
begin
i2c_exec <= 0 ;
case(reg_cnt)
4'd0 :
begin // ��???
i2c_exec <= 0 ;
if(wait_cnt == WAIT_TIME )
begin
wait_cnt <= 0 ;
reg_cnt <= reg_cnt +1 ;
end
else
wait_cnt <= wait_cnt + 1 ;
end
//---------------------------------------------------------------------
4'd1 :
begin // ?? ??
i2c_exec <= 1 ;
i2c_addr <= 8'h02 ;
reg_cnt <= reg_cnt + 1 ;
i2c_data_w <= TIME_INIT[7 : 0] ;
end
4'd2 :
begin // ?? ??
if(i2c_done == 1)
begin
sec <= i2c_data_r[6 : 0] ;
reg_cnt <= reg_cnt + 1 ;
end
end
//--------------------------------------------------------------------------
4'd3 : // ?? ����
begin
i2c_exec <= 1 ;
i2c_addr <= 8'h03 ;
reg_cnt <= reg_cnt + 1 ;
i2c_data_w <= TIME_INIT[15: 8] ;
end
4'd4 : // ?? ����
begin //
if(i2c_done == 1)
begin
min <= i2c_data_r[6 : 0] ;
reg_cnt <= reg_cnt + 1 ;
end
end
//---------------------------------------------------------------
4'd5 : // ?? С?
begin
i2c_exec <= 1 ;
i2c_addr <= 8'h04 ;
reg_cnt <= reg_cnt + 1 ;
i2c_data_w <= TIME_INIT[23: 16] ;
end
4'd6 : // ?? С?
begin //
if(i2c_done == 1)
begin
hour <= i2c_data_r[5 : 0] ;
reg_cnt <= reg_cnt + 1 ;
end
end
//---------------------------------------------------------------
4'd7 : // ?? ??
begin
i2c_exec <= 1 ;
i2c_addr <= 8'h05 ;
reg_cnt <= reg_cnt + 1 ;
i2c_data_w <= TIME_INIT[31: 24] ;
end
4'd8 : // ?? ??
begin //
if(i2c_done == 1)
begin
day <= i2c_data_r[5 : 0] ;
reg_cnt <= reg_cnt + 1 ;
end
end
//----------------------------------------------------------------------
4'd9 : // ?? ??
begin
i2c_exec <= 1 ;
i2c_addr <= 8'h07 ;
reg_cnt <= reg_cnt + 1 ;
i2c_data_w <= TIME_INIT[39: 32] ;
end
4'd10 : // ?? ??
begin //
if(i2c_done == 1)
begin
mon <= i2c_data_r[4 : 0] ;
reg_cnt <= reg_cnt + 1 ;
end
end
//------------------------------------------------------------------------
4'd11 : // ?? ??
begin
i2c_exec <= 1 ;
i2c_addr <= 8'h08 ;
reg_cnt <= reg_cnt + 1 ;
i2c_data_w <= TIME_INIT[47: 40] ;
end
4'd12 : // ?? ??
begin //
if(i2c_done == 1)
begin
year <= i2c_data_r[7 : 0] ;
i2c_rh_wl <= 1 ;
reg_cnt <= 1 ;
end
end
default : reg_cnt <= 0 ;
endcase
end
end
endmodule
reset_syn.v
module reset_syn(
input pclk ,
input reset_n ,
output reg reset
);
reg reset1 ;
always@( posedge pclk or negedge reset_n)
begin
if( reset_n == 0)
begin
reset1 <= 1 ;
end
else
begin
reset1 <= 0 ;
reset <= reset1 ;
end
end
endmodule
serializer.v
module serializer10 (
input reset , // 复位,高有效
input paralell_clk , // 输入并行数据时钟
input serial_clk_5x , // 输入串行数据时钟
input [9 : 0] paralell_data , // 输入并行数据
output serial_data_out // 输出串行数据
);
//wire define
wire cascade1 ; //用于两个 OSERDESE2 级联的信号
wire cascade2 ;
// 此处的代码 来自 vivado的 原语 和 正点原子的同时调配
// 这是 master接口
OSERDESE2 #(
.DATA_RATE_OQ("DDR"), // 设置双倍数据速率
.DATA_RATE_TQ("DDR"), // DDR, BUF, SDR
.DATA_WIDTH(10), // 输入的并行数据宽度为 10bit
// .INIT_OQ(1'b0), // Initial value of OQ output (1'b0,1'b1)
// .INIT_TQ(1'b0), // Initial value of TQ output (1'b0,1'b1)
.SERDES_MODE("MASTER"), // MASTER, SLAVE
//.SRVAL_OQ(1'b0), // OQ output value when SR is used (1'b0,1'b1)
// .SRVAL_TQ(1'b0), // TQ output value when SR is used (1'b0,1'b1)
.TBYTE_CTL("FALSE"), // Enable tristate byte operation (FALSE, TRUE)
.TBYTE_SRC("FALSE"), // Tristate byte source (FALSE, TRUE)
.TRISTATE_WIDTH(1) // 3-state converter width (1,4)
)
OSERDESE2_MASTER (
.OFB(), // 未使用
.OQ(serial_data_out), // 串行输出数据
// SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
.SHIFTOUT1(), // SHIFTIN1 用于位宽扩展
.SHIFTOUT2(), // SHIFTIN2 用于位宽扩展
.TBYTEOUT(), // 未使用
.TFB(), // 未使用
.TQ(), // 未使用
.CLK(serial_clk_5x), // 串行数据时钟,5 倍时钟频率
.CLKDIV(paralell_clk), // 并行数据时钟
// D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
.D1(paralell_data[0]),
.D2(paralell_data[1]),
.D3(paralell_data[2]),
.D4(paralell_data[3]),
.D5(paralell_data[4]),
.D6(paralell_data[5]),
.D7(paralell_data[6]),
.D8(paralell_data[7]),
.OCE(1'b1), // 1-bit input: Output data clock enable
.RST(reset), // 1-bit input: Reset
// SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
.SHIFTIN1(cascade1), // SHIFTIN1 用于位宽扩展
.SHIFTIN2(cascade2), // SHIFTIN2 用于位宽扩展
// T1 - T4: 1-bit (each) input: Parallel 3-state inputs
.T1(1'b0), // 未使用
.T2(1'b0), // 未使用
.T3(1'b0), // 未使用
.T4(1'b0), // 未使用
.TBYTEIN(1'b0), // 未使用
.TCE(1'b0) // 未使用
);
// slave接口
OSERDESE2 #(
.DATA_RATE_OQ("DDR"), // 设置双倍数据速率
.DATA_RATE_TQ("DDR"), // DDR, BUF, SDR
.DATA_WIDTH(10), // 输入的并行数据宽度为 10bit
// .INIT_OQ(1'b0), // Initial value of OQ output (1'b0,1'b1)
// .INIT_TQ(1'b0), // Initial value of TQ output (1'b0,1'b1)
.SERDES_MODE("SLAVE"), // MASTER, SLAVE
//.SRVAL_OQ(1'b0), // OQ output value when SR is used (1'b0,1'b1)
// .SRVAL_TQ(1'b0), // TQ output value when SR is used (1'b0,1'b1)
.TBYTE_CTL("FALSE"), // Enable tristate byte operation (FALSE, TRUE)
.TBYTE_SRC("FALSE"), // Tristate byte source (FALSE, TRUE)
.TRISTATE_WIDTH(1) // 3-state converter width (1,4)
)
OSERDESE2_SLAVE (
.OFB(), // 未使用
.OQ(), // 串行输出数据
// SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
.SHIFTOUT1(cascade1), // SHIFTIN1 用于位宽扩展
.SHIFTOUT2(cascade2), // SHIFTIN2 用于位宽扩展
.TBYTEOUT(), // 未使用
.TFB(), // 未使用
.TQ(), // 未使用
.CLK(serial_clk_5x), // 串行数据时钟,5 倍时钟频率
.CLKDIV(paralell_clk), // 并行数据时钟
// D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
.D1(1'b0),
.D2(1'b0),
.D3(paralell_data[8]),
.D4(paralell_data[9]),
.D5(1'b0),
.D6(1'b0),
.D7(1'b0),
.D8(1'b0),
.OCE(1'b1), // 1-bit input: Output data clock enable
.RST(reset), // 1-bit input: Reset
// SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
.SHIFTIN1(), // SHIFTIN1 用于位宽扩展
.SHIFTIN2(), // SHIFTIN2 用于位宽扩展
// T1 - T4: 1-bit (each) input: Parallel 3-state inputs
.T1(1'b0), // 未使用
.T2(1'b0), // 未使用
.T3(1'b0), // 未使用
.T4(1'b0), // 未使用
.TBYTEIN(1'b0), // 未使用
.TCE(1'b0) // 未使用
);
endmodule
video_driver.v
module video_driver
(
input pixel_clk ,
input rst_n ,
input [ 23 : 0 ] pixel_data ,
output [ 23 : 0 ] video_rgb ,
output video_hs , // 行同步信号
output video_vs , // 场同步信号
output video_de , // 数据使能
output [ 11 : 0 ] pixel_xpos , // 像素点横坐标 1280
output [ 11 : 0 ] pixel_ypos // 像素点横坐标 720
);
//parameter define
//1280*720 分辨率时序参数 时钟频率74.25
parameter H_SYNC = 12'd40; //行同步
parameter H_BACK = 12'd220; //行显示后沿
parameter H_DISP = 12'd1280; //行有效数据
parameter H_FRONT = 12'd110; //行显示前沿
parameter H_TOTAL = 12'd1650; //行扫描周期
parameter V_SYNC = 12'd5; //场同步
parameter V_BACK = 12'd20; //场显示后沿
parameter V_DISP = 12'd720; //场有效数据
parameter V_FRONT = 12'd5; //场显示前沿
parameter V_TOTAL = 12'd750; //场扫描周期
// reg define
reg [11 : 0] cnt_h ;
reg [11 : 0] cnt_v ;
wire data_reg ;
// define
// next is main code
always@(posedge pixel_clk or negedge rst_n)
begin
if( rst_n == 0)
begin
cnt_h <= 0 ;
end
else
begin
if(cnt_h == H_TOTAL - 1)
begin
cnt_h <= 0 ;
end
else
cnt_h <= cnt_h + 1 ;
end
end
always@(posedge pixel_clk or negedge rst_n)
begin
if( rst_n == 0)
begin
cnt_v = 0 ;
end
else
begin
if( cnt_h == H_TOTAL - 1)
begin
if(cnt_v == V_TOTAL - 1)
begin
cnt_v <= 0 ;
end
else
begin
cnt_v <= cnt_v + 1 ;
end
end
end
end
// =======================main code============\\
// video_rgb
// video_hs
// video_vs
// video_de
// pixel_xpos
// pixel_ypos
assign video_hs = 1 ;
assign video_vs = 1 ;
assign video_rgb = video_de ? pixel_data : 24'b0 ;
assign video_de = (((cnt_h >= H_SYNC+H_BACK) && (cnt_h < H_SYNC+H_BACK+H_DISP))
&&((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
? 1'b1 : 1'b0;
assign data_reg = (((cnt_h >= H_SYNC+H_BACK - 1) && (cnt_h < H_SYNC+H_BACK+H_DISP - 1))
&&((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
? 1'b1 : 1'b0;
assign pixel_xpos = data_reg ? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 0;
assign pixel_ypos = data_reg ? (cnt_v - (V_SYNC + V_BACK - 1'b1)) : 0;
endmodule
README.md
## RTC实时时钟实验 -- 在HDMI上显示
### 将整体设计分为3部分
( HDMI部分 中间模块 IIC 转接口
其实 HDMI的部分 只要在于修改 display 的显示
对于 IIC 转接口 直接使用上一个项目的 示例 )
先了解难度最大的中间模块的书写
在第一次上电 将初始值赋予i2c_dri 这是写 部分 接下来都是进入循环的读
其实我觉得他这个思路挺好的 就是记录一个 i2c_done 如果没有接收到done 信号 就一直执行
接收到了
这里有一个值得思考的地方为什么正点原子把 i2c_addr,明明是 16位 在赋值的时候只搞8位
难道不会出现问题嘛?可恶!!!
在完成中间模块 和 IIC的模块之后
我们接下来考虑的是 HDMI的接口
HDMI 下属又分为几个小的模块 主要修改的
dvi_transmitter_top
encoder
reset_syn
serializer
top // 修改
video_display // 修改
video_driver
文章来源:https://blog.csdn.net/weixin_50965981/article/details/135438332
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!