lcd屏幕
液晶是一种介于固体和液体之间的特殊物质,它是一种有机化合物,常态下呈液态,但是它的分子排列却和固体晶体一样非常规则,因此取名液晶。如果给液晶施加电场,会改变它的分子排列,从而改变光线的传播方向,配合偏振光片,它就具有控制光线透过率的作用,再配合彩色滤光片,改变加给液晶电压大小,就能改变某一颜色透光量 的多少。用可控红、绿、蓝光输出强度的显示结构,把三种显示结构组成一个显示单位,通过控制红绿蓝的强度,可以使该单位混合输出不同的色彩,这样的一个显示单位就是我们所说的像素。
TFT-LCD(薄膜晶体管液晶显示器),其中TFT就是Thin Film Transistor的简称,指的是薄膜晶体管(矩阵),可以“主动的”对屏幕上的各个独立的像素进行控制,这也就是所谓的主动矩阵TFT(active matrix TFT)的来历。图像产生的基本原理很简单:显示屏由许多可以发出任意颜色的光线的像素组成,只要控制各个像素显示相应的颜色就能达到目的了。在TFT LCD中一般采用背光技术,为了能精确地控制每一个像素的颜色和亮度就需要在每一个像素之后安装一个半导体开关,以此做到完全的单独的控制一个像素点,液晶材料被夹在TFT玻璃层和颜色过滤层之间,通过改变刺激液晶的电压值就可以控制最后出现的光线强度与色彩。
lcd屏幕在代码中是通过对r7、g7、b7寄存器的读取来确定分辨率的。
采用RGB888的像素格式,一个像素点占用3个字节,共需要24位,bit23-bit16是RED通道,bit15-bit8是GREEN通道,bit7-bit0是BLUE通道。
HV同步模式下,图像的显示需要行同步信号(hsync)和场同步信号(vsync)来确定显示时序。此时,RGB接口的TFT-LCD液晶显示屏的显示时序和VGA时序标准类似。
时间参数:
行场扫描时序:
de:当为高电平时此时输出值到屏幕的对应像素点上。
LCD的显示和VGA的显示时序基本一致,都是从屏幕的左上角开始(从左往右,从上往下)经过Hor_sync_time和H_back_porch时间,屏幕开始显示,到H_front_porch时间后结束一行的显示,然后回到下一行左侧,循环到屏幕的最后一行扫描。在竖直方向上,经过 ver_sync_time和v_back_porch时间竖直方向屏幕开始显示,到ver_front_porch竖直方向显示结束。一帧显示完成。
在一个完整的行扫描周期中,RGB图像信息在HSync行同步信号的同步下完成一行图像的显示,在VSync行同步信号的同步下完成一帧图像的显示。RGB图像信息在有效图像阶段,图像信息有效,其他阶段图像信息无效;HSync和VSync行同步信号在同步阶段,维持高电平,其他阶段均保持低电平。在下一个行扫描周期的同步阶段,HSync和VSync行扫描信号拉高,其他阶段拉低,周而复始 。DE数据使能信号为高电平时,表示TFT显示屏扫描到了有效显示区域,此时输入到TFT显示屏的图像信息能够显示出来;当数据使能信号为低电平时,表示TFT显示屏未扫描到有效显示区域。
HSPW(行同步,是HSYNC信号持续时间。HSYNC信号不是一个脉冲,而是需要持续一段时间才是有效的,单位为CLK。)、HBP(行显示后沿)、HOZVAL(行有效显示区域,即显示一行数据所需的时间,假如屏幕分辨率为1024600,那么HOZVAL 就是1024,单位为CLK)、HFP(行显示前沿)、VSPW(场同步,帧同步信号宽度,也就是VSYNC信号持续时间,单位为1行的时间。)、VBP(场显示后沿)、LINE(场有效显示区域,显示一帧数据所需的时间,假如屏幕分辨率为1024600,那么LINE就是600行的时间)和VFP(场显示后沿)。
LCD屏(分辨率:1024*600)刷新一帧的时间
= (场同步+场显示后沿+场有效显示区域+场显示后沿) * (行同步 + 行显示后沿 + 行有效显示区域 + 行显示前沿)
先使用ZYNQ开发板上的RGB TFT-LCD接口,驱动RGB LCD液晶屏显示出彩条。
时钟模块:
module clk_div(input clk,input rstn,input [23:0]rgb_reg,
output reg lcd_pclk,output reg[15:0]lcd_id);
reg rd_flag,clk_25m,clk_12_5m,div_4_cnt;
always @(posedge clk or negedge rstn) begin
if(!rstn)
clk_25m<='d0;
else clk_25m<=~clk_25m;
end
always @(posedge clk or negedge rstn) begin
if(!rstn)
begin
div_4_cnt<='d0;
clk_12_5m<='d0;
end
else begin
div_4_cnt<=div_4_cnt+'d1;
if(div_4_cnt=='d1)
clk_12_5m=~clk_12_5m;
end
end
always @(*) begin
case(lcd_id)
16'h4342:lcd_pclk=clk_12_5m;
16'h7084:lcd_pclk=clk_25m;
16'h7016:lcd_pclk=clk;
16'h4384:lcd_pclk=clk_25m;
16'h1018:lcd_pclk=clk;
default:lcd_pclk='d0;
endcase
end
always @(posedge clk or negedge rstn) begin
if(!rstn) begin
rd_flag<='d0;
lcd_id<='d0;
end
else begin
if(rd_flag=='d0)begin
rd_flag<=~rd_flag;
case({rgb_reg[7],rgb_reg[15],rgb_reg[23]})
3'b000:lcd_id<=16'h4342;
3'b001:lcd_id<=16'h7084;
3'b010:lcd_id<=16'h7016;
3'b100:lcd_id<=16'h4384;
3'b101:lcd_id<=16'h1018;
default:lcd_id<='d0;
endcase
end
end
end
endmodule
lcd驱动模块:
module lcd_driver
(input pclk,input rstn,input [15:0]lcd_id,
input[23:0] display_data,output reg[10:0] h_disp,
output reg[10:0]v_disp,
output [10:0]xpos,output[10:0] ypos,
output de,hs,vs,bl,lcd_clk,lcd_rst,
output[23:0] lcd_rgb);
parameter H_SYNC_4342 = 11'd41; //行同步
parameter H_BACK_4342 = 11'd2; //行显示后沿
parameter H_DISP_4342 = 11'd480; //行有效数据
parameter H_FRONT_4342 = 11'd2; //行显示前沿
parameter H_TOTAL_4342 = 11'd525; //行扫描周期
parameter V_SYNC_4342 = 11'd10; //场同步
parameter V_BACK_4342 = 11'd2; //场显示后沿
parameter V_DISP_4342 = 11'd272; //场有效数据
parameter V_FRONT_4342 = 11'd2; //场显示前沿
parameter V_TOTAL_4342 = 11'd286; //场扫描周期
// 7' 800*480
parameter H_SYNC_7084 = 11'd128; //行同步
parameter H_BACK_7084 = 11'd88; //行显示后沿
parameter H_DISP_7084 = 11'd800; //行有效数据
parameter H_FRONT_7084 = 11'd40; //行显示前沿
parameter H_TOTAL_7084 = 11'd1056; //行扫描周期
parameter V_SYNC_7084 = 11'd2; //场同步
parameter V_BACK_7084 = 11'd33; //场显示后沿
parameter V_DISP_7084 = 11'd480; //场有效数据
parameter V_FRONT_7084 = 11'd10; //场显示前沿
parameter V_TOTAL_7084 = 11'd525; //场扫描周期
// 7' 1024*600
parameter H_SYNC_7016 = 11'd20; //行同步
parameter H_BACK_7016 = 11'd140; //行显示后沿
parameter H_DISP_7016 = 11'd1024; //行有效数据
parameter H_FRONT_7016 = 11'd160; //行显示前沿
parameter H_TOTAL_7016 = 11'd1344; //行扫描周期
parameter V_SYNC_7016 = 11'd3; //场同步
parameter V_BACK_7016 = 11'd20; //场显示后沿
parameter V_DISP_7016 = 11'd600; //场有效数据
parameter V_FRONT_7016 = 11'd12; //场显示前沿
parameter V_TOTAL_7016 = 11'd635; //场扫描周期
// 10.1' 1280*800
parameter H_SYNC_1018 = 11'd10; //行同步
parameter H_BACK_1018 = 11'd80; //行显示后沿
parameter H_DISP_1018 = 11'd1280; //行有效数据
parameter H_FRONT_1018 = 11'd70; //行显示前沿
parameter H_TOTAL_1018 = 11'd1440; //行扫描周期
parameter V_SYNC_1018 = 11'd3; //场同步
parameter V_BACK_1018 = 11'd10; //场显示后沿
parameter V_DISP_1018 = 11'd800; //场有效数据
parameter V_FRONT_1018 = 11'd10; //场显示前沿
parameter V_TOTAL_1018 = 11'd823; //场扫描周期
// 4.3' 800*480
parameter H_SYNC_4384 = 11'd128; //行同步
parameter H_BACK_4384 = 11'd88; //行显示后沿
parameter H_DISP_4384 = 11'd800; //行有效数据
parameter H_FRONT_4384 = 11'd40; //行显示前沿
parameter H_TOTAL_4384 = 11'd1056; //行扫描周期
parameter V_SYNC_4384 = 11'd2; //场同步
parameter V_BACK_4384 = 11'd33; //场显示后沿
parameter V_DISP_4384 = 11'd480; //场有效数据
parameter V_FRONT_4384 = 11'd10; //场显示前沿
parameter V_TOTAL_4384 = 11'd525; //场扫描周期
reg[10:0]h_sync,h_back,h_total,
v_sync,v_back,v_total,h_cnt,v_cnt;
wire en,data_req;
assign hs='d1,vs='d1,bl='d1,lcd_rst='d1;
assign lcd_clk=pclk,de=en,lcd_rgb=de?display_data:'d0;
assign xpos=data_req?(h_cnt-(h_sync+h_back-1)):'d0,
ypos=data_req?(v_cnt-(v_sync+v_back-1)):'d0;
assign en=((h_cnt>=h_sync+h_back)&&(h_cnt<h_sync+h_back+h_disp)&&
(v_cnt>=v_sync+v_back)&&(v_cnt<v_sync+v_back+v_disp));
assign data_req=((h_cnt>=h_sync+h_back-'d1)&&(h_cnt<h_sync+h_back+h_disp-'d1)
&&(v_cnt>=v_sync+v_back)&&(v_cnt<v_sync+v_back+v_disp));
always @(*) begin
case(lcd_id)
16'h4342 : begin
h_sync = H_SYNC_4342;
h_back = H_BACK_4342;
h_disp = H_DISP_4342;
h_total = H_TOTAL_4342;
v_sync = V_SYNC_4342;
v_back = V_BACK_4342;
v_disp = V_DISP_4342;
v_total = V_TOTAL_4342;
end
16'h7084 : begin
h_sync = H_SYNC_7084;
h_back = H_BACK_7084;
h_disp = H_DISP_7084;
h_total = H_TOTAL_7084;
v_sync = V_SYNC_7084;
v_back = V_BACK_7084;
v_disp = V_DISP_7084;
v_total = V_TOTAL_7084;
end
16'h7016 : begin
h_sync = H_SYNC_7016;
h_back = H_BACK_7016;
h_disp = H_DISP_7016;
h_total = H_TOTAL_7016;
v_sync = V_SYNC_7016;
v_back = V_BACK_7016;
v_disp = V_DISP_7016;
v_total = V_TOTAL_7016;
end
16'h4384 : begin
h_sync = H_SYNC_4384;
h_back = H_BACK_4384;
h_disp = H_DISP_4384;
h_total = H_TOTAL_4384;
v_sync = V_SYNC_4384;
v_back = V_BACK_4384;
v_disp = V_DISP_4384;
v_total = V_TOTAL_4384;
end
16'h1018 : begin
h_sync = H_SYNC_1018;
h_back = H_BACK_1018;
h_disp = H_DISP_1018;
h_total = H_TOTAL_1018;
v_sync = V_SYNC_1018;
v_back = V_BACK_1018;
v_disp = V_DISP_1018;
v_total = V_TOTAL_1018;
end
default : begin
h_sync = H_SYNC_4342;
h_back = H_BACK_4342;
h_disp = H_DISP_4342;
h_total = H_TOTAL_4342;
v_sync = V_SYNC_4342;
v_back = V_BACK_4342;
v_disp = V_DISP_4342;
v_total = V_TOTAL_4342;
end
endcase
end
always @(posedge pclk or negedge rstn) begin
if(!rstn)begin
h_cnt<='d0;
v_cnt<='d0;
end
else if(h_cnt==h_total-'d1)begin
h_cnt<='d0;
if(v_cnt==v_total-'d1)
v_cnt<='d0;
else v_cnt<=v_cnt+'d1;
end
else begin
h_cnt<=h_cnt+'d1;
end
end
endmodule
顶层模块:
module colorbar_top(input clk,input rstn,inout [23:0]lcd_rgb,
output de,hs,vs,bl,lcd_clk,lcd_rst);
wire pclk;
wire[23:0]display_data,lcd_rgb_d0,lcd_rgb_i;
wire[15:0]lcd_id;
wire[10:0]xpos, ypos,h_disp,v_disp;
assign lcd_rgb=de?lcd_rgb_d0:'dz,lcd_rgb_i=lcd_rgb;
clk_div clk_u(
.clk(clk),
.rstn(rstn),
.rgb_reg(lcd_rgb_i),
.lcd_pclk(pclk),
.lcd_id(lcd_id));
lcd_display lcd_u
(.pclk(pclk),
.rstn(rstn),
.xpos(xpos),
.ypos(ypos),
.h_disp(h_disp),
.v_disp(v_disp),
.display_data(display_data));
lcd_driver lcd_d_u
(.pclk(pclk),
.rstn(rstn),
.lcd_id(lcd_id),
.display_data(display_data),
.xpos(xpos),
.ypos(ypos),
.h_disp(h_disp),
.v_disp(v_disp),
.de(de),
.hs(hs),
.vs(vs),
.bl(bl),
.lcd_clk(lcd_clk),
.lcd_rst(lcd_rst),
.lcd_rgb(lcd_rgb_d0));
endmodule
项目结构:
上板效果:
然后我们让lcd显示图片和字符,首先要用pctlcd取字模,根据需求调整字宽和字高:
这种字符模式的数据用起来不方便,为了方便在LCD上显示,我们将汉字的点阵保存为.BMP格式的图片,再用图片模式取模:
其它的图片需要用pictolcd转换为coe文件放到rom中
添加rom ip
因为我们采用的是rgb888格式所以width=24,depth=图片分辨率(225*225)
信号时序与彩条输出时序一致,只有显示数据发生了变化,这里给出输出显示模块:
module lcd_display_2
(input pclk,input rstn,
input [10:0]xpos, ypos,
output reg [23:0]display_data);
localparam PIC_X_START=11'd1,
PIC_Y_START=11'd1,
PIC_WIDTH=11'D100,
PIC_HEIGHT=11'D100,
CHAR_X_START=11'D1,
CHAR_Y_START= PIC_HEIGHT+4'd10,
CHAR_WIDTH=11'D128,
CHAR_HEIGHT=11'D32,
BACK_COLOR=24'hE0FFFF,
CHAR_COLOR=24'hFF0100;
wire[10:0]x_cnt,y_cnt;
wire rd_en,pic_flag;
wire[23:0]rom_rd_data;
reg[13:0]rom_addr;
reg[127:0] char[31:0];
assign rd_en='d1,x_cnt=xpos-CHAR_X_START,
y_cnt=ypos-CHAR_Y_START,pic_flag=(xpos>=PIC_X_START)&&(xpos<PIC_X_START+PIC_WIDTH)
&&(ypos>=PIC_Y_START)&&(ypos<PIC_Y_START+PIC_HEIGHT);
blk_mem_gen_0 blk_mem_gen_0 (
.clka (pclk), // input wire clka
.ena (rd_en), // input wire ena
.addra (rom_addr), // input wire [13 : 0] addra
.douta (rom_rd_data) // output wire [23 : 0] douta
);
always @(posedge pclk or negedge rstn) begin
if(!rstn)begin
display_data<=BACK_COLOR;
end
else if(pic_flag)
begin
display_data<= rom_rd_data;
end
else
if((xpos>=CHAR_X_START)&&(xpos<CHAR_X_START+CHAR_WIDTH)
&&(ypos>=CHAR_Y_START)&&(ypos<CHAR_Y_START+CHAR_HEIGHT))
begin
if(char[y_cnt][CHAR_WIDTH-'D1-x_cnt])
display_data<=CHAR_COLOR;
else
display_data<=BACK_COLOR;
end
else
display_data<=BACK_COLOR;
end
always @(posedge pclk or negedge rstn) begin
if(!rstn)
rom_addr<='d0;
else if(pic_flag)
rom_addr<=rom_addr+'d1;
else if(ypos>=PIC_Y_START+PIC_HEIGHT)
rom_addr<='d0;
end
always @(posedge pclk) begin
char[0 ] <= 128'h00000000000000000000000000000000;
char[1 ] <= 128'h00000000000000000000000000000000;
char[2 ] <= 128'h00_01_00_00_00_40_04_00_00_00_02_00_02_00_00_00;
char[3 ] <= 128'h00_01_C0_00_00_20_0E_00_00_00_03_80_01_80_00_00;
char[4 ] <= 128'h00_01_80_00_00_38_0C_00_00_00_03_00_01_C0_00_10;
char[5 ] <= 128'h02_01_81_C0_00_18_08_00_00_20_03_00_00_CF_FF_F8;
char[6 ] <= 128'h01_81_81_C0_00_18_10_00_1F_F0_03_00_08_C0_00_30;
char[7 ] <= 128'h01_C1_83_00_00_08_20_18_18_30_03_00_0E_40_00_30;
char[8 ] <= 128'h00_E1_86_00_3F_FF_FF_FC_18_30_03_00_0C_00_00_30;
char[9 ] <= 128'h00_61_84_00_00_00_00_00_18_30_03_18_0C_00_00_30;
char[10] <= 128'h00_61_88_00_00_00_00_C0_18_3F_FF_FC_0C_00_18_30;
char[11] <= 128'h00_01_90_00_04_04_00_E0_18_30_03_00_0C_1F_FC_30;
char[12] <= 128'h00_01_80_30_07_FF_10_C0_18_30_03_00_0C_18_18_30;
char[13] <= 128'h0F_FF_FF_F8_06_06_18_C0_18_30_03_00_0C_18_18_30;
char[14] <= 128'h00_00_00_30_06_06_18_C0_18_32_03_00_0C_18_18_30;
char[15] <= 128'h00_00_00_30_06_06_18_C0_1F_F1_03_00_0C_18_18_30;
char[16] <= 128'h00_00_00_30_06_06_18_C0_18_31_C3_00_0C_1F_F8_30;
char[17] <= 128'h00_00_00_30_07_FE_18_C0_18_30_C3_00_0C_18_18_30;
char[18] <= 128'h00_00_00_30_06_06_18_C0_18_30_E3_00_0C_18_18_30;
char[19] <= 128'h00_00_00_30_06_06_18_C0_18_30_43_00_0C_18_18_30;
char[20] <= 128'h03_FF_FF_F0_06_06_18_C0_18_30_03_00_0C_18_18_30;
char[21] <= 128'h00_00_00_30_07_FE_18_C0_18_30_03_00_0C_18_18_30;
char[22] <= 128'h00_00_00_30_06_06_18_C0_18_30_03_00_0C_1F_F8_30;
char[23] <= 128'h00_00_00_30_06_06_18_C0_1F_F0_03_00_0C_18_18_30;
char[24] <= 128'h00_00_00_30_06_06_18_C0_18_30_03_00_0C_10_10_30;
char[25] <= 128'h00_00_00_30_06_06_18_C0_18_30_03_00_0C_00_00_30;
char[26] <= 128'h00_00_00_30_06_06_00_40_18_00_03_00_0C_00_00_30;
char[27] <= 128'h00_00_00_30_06_06_00_C0_00_00_03_00_0C_00_00_30;
char[28] <= 128'h1F_FF_FF_F0_06_3C_0F_C0_00_00_3F_00_0C_00_03_F0;
char[29] <= 128'h00_00_00_30_06_1C_03_80_00_00_07_00_0C_00_00_E0;
char[30] <= 128'h00_00_00_20_04_08_01_00_00_00_06_00_08_00_00_40;
char[31] <= 128'h00000000000000000000000000000000;
end
endmodule
上机运行效果:
rtc时钟
rtc时钟芯片:PCF8563 是PHILIPS 公司推出的一款工业级内含I2C 总线接口功能的具有极低功耗的多功能时钟/日历芯片。包括多种报警功能、定时器功能、时钟输出功能以及中断输出功能能完成各种复杂的定时服务,甚至可为单片机提供看门狗功能。
芯片上所有的数据和地址使用i2c总线传输加粗样式,在读写过程中,访问地址自动增加
芯片基本结构:
硬件原理图:
引脚包括晶振时钟相关osci、osco、电源vdd、地线vss、i2c相关sda、scl、输出时钟clkout、中断int
系统设计:
我们使用i2c协议驱动rtc时钟获得数据,并传递到lcd字符显示模块,最终在屏幕上显示出年月日、时分秒,而在PCF8563中这些都有对应的寄存器:
秒寄存器(地址02h)、分钟寄存器(地址03h)、小时寄存器(地址04h)、天寄存器(地址05h)、月/寄存器(地址07h)、年寄存器(地址08h)
rtc控制程序中定义了一个状态流控制计数器(flow_cnt),先将初始日期和时间:
module rtc_ctrl(
input clk,input rstn,
input [7:0]data_r,input done,
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]yr,
output reg en,output reg wl_rh,
output reg[15:0] device_addr,
output reg [7:0]data_w);
parameter init_time=48'h23_10_24_09_30_00;
reg[3:0]flow_cnt;
reg[12:0]wait_cnt;
always @(posedge clk or negedge rstn) begin
if(!rstn)
flow_cnt<='d0;
else if(flow_cnt%2==0)
begin
if(!flow_cnt&&wait_cnt==13'd8_000)//wait 16ms
flow_cnt<=flow_cnt+'d1;
else if(flow_cnt&&done=='d1)
begin
if(flow_cnt<4'd12)
flow_cnt<=flow_cnt+'d1;
else flow_cnt<=4'd1;
end
else ;
end
else
flow_cnt<=flow_cnt+'d1;
end
always @(posedge clk or negedge rstn) begin
if(!rstn)
begin
sec<='d0;
min<='d0;
hour<='d0;
day<='d0;
mon<='d0;
yr<='d0;
en<='d0;
wl_rh<='d0;
device_addr<='d0;
data_w<='d0;
flow_cnt<='d0;
wait_cnt<='d0;
end
else begin
en<='d0;
case(flow_cnt)
4'd0:begin
if(wait_cnt==13'd8_000)
wait_cnt<='d0;
else wait_cnt<=wait_cnt+'d1;
end
4'd1:begin
en<='d1;
device_addr<=8'h02;
data_w<=init_time[7:0];
end
4'd2:begin
if(done=='d1)
sec<=data_r[6:0];
else ;
end
4'd3:begin
en<='d1;
device_addr<=8'h03;
data_w<=init_time[15:8];
end
4'd4:begin
if(done=='d1)begin
min<=data_r[6:0];
end
else ;
end
4'd5:begin
en<='d1;
device_addr<=8'h04;
data_w<=init_time[23:16];
end
4'd6:begin
if(done=='d1)begin
hour<=data_r[6:0];
end
else ;
end
4'd7:begin
en<='d1;
device_addr<=8'h05;
data_w<=init_time[31:24];
end
4'd8:begin
if(done=='d1)begin
day<=data_r[6:0];
end
else ;
end
4'd9:begin
en<='d1;
device_addr<=8'h06;
data_w<=init_time[39:32];
end
4'd10:begin
if(done=='d1)begin
mon<=data_r[6:0];
end
else ;
end
4'd11:begin
en<='d1;
device_addr<=8'h06;
data_w<=init_time[47:40];
end
4'd12:begin
if(done=='d1)begin
yr<=data_r[6:0];
wl_rh<='d1;
end
else ;
end
default:;
endcase
end
end
endmodule
(TIME_INIT)写入PCF8563中,然后会循环从PCF8563中读出秒、分、时、日、月和年。在写操作是i2c_rh_wl(I2C读写控制信号)为低电平,读操作时拉高i2c_rh_wl信号。由于开始时是写使能,所以会向rtc中写入初始时间数据。然后切换读使能循环读取时间数据。
显示模块与之前的思路相同:
module lcd_display
(input pclk,input rstn,
input [10:0]xpos, ypos,
input [7:0]sec,min,hour,day,mon,yr,
output reg [23:0]display_data);
localparam PIC_X_START=11'd1,
PIC_Y_START=11'd1,
PIC_WIDTH=11'D100,
PIC_HEIGHT=11'D100,
CHAR_X_START=11'D1,
CHAR_Y_START= PIC_HEIGHT+4'd10,
CHAR_WIDTH=11'D128,
CHAR_HEIGHT=11'D32,
BACK_COLOR=24'hE0FFFF,
CHAR_COLOR=24'hFF0100,
NUM_POS_X_1 = CHAR_X_START+CHAR_WIDTH+4'D10, //第1行字符区域起始点横坐标
NUM_POS_Y_1 = CHAR_Y_START, //第1行字符区域起始点纵坐标
NUM_WIDTH_1 = 11'd80, //第1行字符区域的宽度,第1行共10个字符(加空格)
NUM_WIDTH_2 = 11'd64, //第2行字符区域的宽度,第2行共8个字符(加空格)
NUM_POS_X_2 = NUM_POS_X_1+NUM_WIDTH_1+4'd10, //第2行字符区域起始点横坐标
NUM_POS_Y_2 = CHAR_Y_START, //第2行字符区域起始点纵坐标
NUM_HEIGHT=11'D16;
wire[10:0]x_cnt,y_cnt;
wire rd_en,pic_flag;
wire[23:0]rom_rd_data;
reg[13:0]rom_addr;
reg[127:0] char[31:0];
reg [127:0] char_2 [10:0] ;
assign rd_en='d1,x_cnt=xpos-CHAR_X_START,
y_cnt=ypos-CHAR_Y_START,pic_flag=(xpos>=PIC_X_START)&&(xpos<PIC_X_START+PIC_WIDTH)
&&(ypos>=PIC_Y_START)&&(ypos<PIC_Y_START+PIC_HEIGHT);
blk_mem_gen_0 blk_mem_gen_0 (
.clka (pclk), // input wire clka
.ena (rd_en), // input wire ena
.addra (rom_addr), // input wire [13 : 0] addra
.douta (rom_rd_data) // output wire [23 : 0] douta
);
always @(posedge pclk or negedge rstn) begin
if(!rstn)begin
display_data<=BACK_COLOR;
end
else if(pic_flag)
begin
display_data<= rom_rd_data;
end
else
if((xpos>=CHAR_X_START)&&(xpos<CHAR_X_START+CHAR_WIDTH)
&&(ypos>=CHAR_Y_START)&&(ypos<CHAR_Y_START+CHAR_HEIGHT))
begin
if(char[y_cnt][CHAR_WIDTH-'D1-x_cnt])
display_data<=CHAR_COLOR;
else
display_data<=BACK_COLOR;
end
else if((xpos>=NUM_POS_X_1)&&(
xpos<NUM_POS_X_1+NUM_WIDTH_1/10*1)
&&(ypos>=NUM_POS_Y_1)&&(
ypos<NUM_POS_Y_1+NUM_HEIGHT))
begin
if(char_2[2] [(NUM_HEIGHT+NUM_POS_Y_1 - ypos)*8
- ((xpos-NUM_POS_X_1)%8) -1] )
display_data<=CHAR_COLOR;
else display_data<=BACK_COLOR;//year first
end
else if((xpos>=NUM_POS_X_1+NUM_WIDTH_1/10*1)&&(
xpos<NUM_POS_X_1+NUM_WIDTH_1/10*2)
&&(ypos>=NUM_POS_Y_1)&&(
ypos<NUM_POS_Y_1+NUM_HEIGHT))
begin
if(char_2[0] [(NUM_HEIGHT+NUM_POS_Y_1 - ypos)*8
- ((xpos-NUM_POS_X_1)%8) -1] )
display_data<=CHAR_COLOR;
else display_data<=BACK_COLOR;//year second
end
else if((xpos>=NUM_POS_X_1+NUM_WIDTH_1/10*2)&&(
xpos<NUM_POS_X_1+NUM_WIDTH_1/10*3)
&&(ypos>=NUM_POS_Y_1)&&(
ypos<NUM_POS_Y_1+NUM_HEIGHT))
begin
if(char_2[yr[7:4]] [(NUM_HEIGHT+NUM_POS_Y_1 - ypos)*8
- ((xpos-NUM_POS_X_1)%8) -1] )
display_data<=CHAR_COLOR;
else display_data<=BACK_COLOR;//year 3rd
end
else if((xpos>=NUM_POS_X_1+NUM_WIDTH_1/10*3)&&(
xpos<NUM_POS_X_1+NUM_WIDTH_1/10*4)
&&(ypos>=NUM_POS_Y_1)&&(
ypos<NUM_POS_Y_1+NUM_HEIGHT))
begin
if(char_2[yr[3:0]] [(NUM_HEIGHT+NUM_POS_Y_1 - ypos)*8
- ((xpos-NUM_POS_X_1)%8) -1] )
display_data<=CHAR_COLOR;
else display_data<=BACK_COLOR;//year 4th
end
else if((xpos>=NUM_POS_X_1+NUM_WIDTH_1/10*4)&&(
xpos<NUM_POS_X_1+NUM_WIDTH_1/10*5)
&&(ypos>=NUM_POS_Y_1)&&(
ypos<NUM_POS_Y_1+NUM_HEIGHT))
begin
if(char_2[11] [(NUM_HEIGHT+NUM_POS_Y_1 - ypos)*8
- ((xpos-NUM_POS_X_1)%8) -1] )
display_data<=CHAR_COLOR;
else display_data<=BACK_COLOR;//"-"
end
else if((xpos>=NUM_POS_X_1+NUM_WIDTH_1/10*5)&&(
xpos<NUM_POS_X_1+NUM_WIDTH_1/10*6)
&&(ypos>=NUM_POS_Y_1)&&(
ypos<NUM_POS_Y_1+NUM_HEIGHT))
begin
if(char_2[mon[7:4]] [(NUM_HEIGHT+NUM_POS_Y_1 - ypos)*8
- ((xpos-NUM_POS_X_1)%8) -1] )
display_data<=CHAR_COLOR;
else display_data<=BACK_COLOR;//mon 1st
end
else if((xpos>=NUM_POS_X_1+NUM_WIDTH_1/10*6)&&(
xpos<NUM_POS_X_1+NUM_WIDTH_1/10*7)
&&(ypos>=NUM_POS_Y_1)&&(
ypos<NUM_POS_Y_1+NUM_HEIGHT))
begin
if(char_2[mon[3:0]] [(NUM_HEIGHT+NUM_POS_Y_1 - ypos)*8
- ((xpos-NUM_POS_X_1)%8) -1] )
display_data<=CHAR_COLOR;
else display_data<=BACK_COLOR;//mon 2nd
end
else if((xpos>=NUM_POS_X_1+NUM_WIDTH_1/10*7)&&(
xpos<NUM_POS_X_1+NUM_WIDTH_1/10*8)
&&(ypos>=NUM_POS_Y_1)&&(
ypos<NUM_POS_Y_1+NUM_HEIGHT))
begin
if(char_2[11] [(NUM_HEIGHT+NUM_POS_Y_1 - ypos)*8
- ((xpos-NUM_POS_X_1)%8) -1] )
display_data<=CHAR_COLOR;
else display_data<=BACK_COLOR;//"-"
end
else if((xpos>=NUM_POS_X_1+NUM_WIDTH_1/10*8)&&(
xpos<NUM_POS_X_1+NUM_WIDTH_1/10*9)
&&(ypos>=NUM_POS_Y_1)&&(
ypos<NUM_POS_Y_1+NUM_HEIGHT))
begin
if(char_2[day[7:4]] [(NUM_HEIGHT+NUM_POS_Y_1 - ypos)*8
- ((xpos-NUM_POS_X_1)%8) -1] )
display_data<=CHAR_COLOR;
else display_data<=BACK_COLOR;//day 1st
end
else if((xpos>=NUM_POS_X_1+NUM_WIDTH_1/10*9)&&(
xpos<NUM_POS_X_1+NUM_WIDTH_1)
&&(ypos>=NUM_POS_Y_1)&&(
ypos<NUM_POS_Y_1+NUM_HEIGHT))
begin
if(char_2[day[3:0]] [(NUM_HEIGHT+NUM_POS_Y_1 - ypos)*8
- ((xpos-NUM_POS_X_1)%8) -1] )
display_data<=CHAR_COLOR;
else display_data<=BACK_COLOR;//day 2nd
end
else if((xpos>=NUM_POS_X_2)&&(
xpos<NUM_POS_X_2+NUM_WIDTH_2/8*1)
&&(ypos>=NUM_POS_Y_2)&&(
ypos<NUM_POS_Y_2+NUM_HEIGHT))
begin
if(char_2[hour[7:4]] [(NUM_HEIGHT+NUM_POS_Y_2 - ypos)*8
- ((xpos-NUM_POS_X_2)%8) -1] )
display_data<=CHAR_COLOR;
else display_data<=BACK_COLOR;//hour 1st
end
else if((xpos>=NUM_POS_X_2+NUM_WIDTH_2/8*1)&&(
xpos<NUM_POS_X_2+NUM_WIDTH_2/8*2)
&&(ypos>=NUM_POS_Y_2)&&(
ypos<NUM_POS_Y_2+NUM_HEIGHT))
begin
if(char_2[hour[3:0]] [(NUM_HEIGHT+NUM_POS_Y_2 - ypos)*8
- ((xpos-NUM_POS_X_2)%8) -1] )
display_data<=CHAR_COLOR;
else display_data<=BACK_COLOR;//hour 2nd
end
else if((xpos>=NUM_POS_X_2+NUM_WIDTH_2/8*2)&&(
xpos<NUM_POS_X_2+NUM_WIDTH_2/8*3)
&&(ypos>=NUM_POS_Y_2)&&(
ypos<NUM_POS_Y_2+NUM_HEIGHT))
begin
if(char_2[10] [(NUM_HEIGHT+NUM_POS_Y_2 - ypos)*8
- ((xpos-NUM_POS_X_2)%8) -1] )
display_data<=CHAR_COLOR;
else display_data<=BACK_COLOR;//":"
end
else if((xpos>=NUM_POS_X_2+NUM_WIDTH_2/8*3)&&(
xpos<NUM_POS_X_2+NUM_WIDTH_2/8*4)
&&(ypos>=NUM_POS_Y_2)&&(
ypos<NUM_POS_Y_2+NUM_HEIGHT))
begin
if(char_2[min[7:4]] [(NUM_HEIGHT+NUM_POS_Y_2 - ypos)*8
- ((xpos-NUM_POS_X_2)%8) -1] )
display_data<=CHAR_COLOR;
else display_data<=BACK_COLOR;//min 1st
end
else if((xpos>=NUM_POS_X_2+NUM_WIDTH_2/8*4)&&(
xpos<NUM_POS_X_2+NUM_WIDTH_2/8*5)
&&(ypos>=NUM_POS_Y_2)&&(
ypos<NUM_POS_Y_2+NUM_HEIGHT))
begin
if(char_2[min[3:0]] [(NUM_HEIGHT+NUM_POS_Y_2 - ypos)*8
- ((xpos-NUM_POS_X_2)%8) -1] )
display_data<=CHAR_COLOR;
else display_data<=BACK_COLOR;//min 2nd
end
else if((xpos>=NUM_POS_X_2+NUM_WIDTH_2/8*5)&&(
xpos<NUM_POS_X_2+NUM_WIDTH_2/8*6)
&&(ypos>=NUM_POS_Y_2)&&(
ypos<NUM_POS_Y_2+NUM_HEIGHT))
begin
if(char_2[10] [(NUM_HEIGHT+NUM_POS_Y_2 - ypos)*8
- ((xpos-NUM_POS_X_2)%8) -1] )
display_data<=CHAR_COLOR;
else display_data<=BACK_COLOR;//":"
end
else if((xpos>=NUM_POS_X_2+NUM_WIDTH_2/8*6)&&(
xpos<NUM_POS_X_2+NUM_WIDTH_2/8*7)
&&(ypos>=NUM_POS_Y_2)&&(
ypos<NUM_POS_Y_2+NUM_HEIGHT))
begin
if(char_2[sec[7:4]] [(NUM_HEIGHT+NUM_POS_Y_2 - ypos)*8
- ((xpos-NUM_POS_X_2)%8) -1] )
display_data<=CHAR_COLOR;
else display_data<=BACK_COLOR;//sec 1st
end
else if((xpos>=NUM_POS_X_2+NUM_WIDTH_2/8*7)&&(
xpos<NUM_POS_X_2+NUM_WIDTH_2)
&&(ypos>=NUM_POS_Y_2)&&(
ypos<NUM_POS_Y_2+NUM_HEIGHT))
begin
if(char_2[sec[3:0]] [(NUM_HEIGHT+NUM_POS_Y_2 - ypos)*8
- ((xpos-NUM_POS_X_2)%8) -1] )
display_data<=CHAR_COLOR;
else display_data<=BACK_COLOR;//sec 2nd
end
else display_data<=BACK_COLOR;
end
always @(posedge pclk or negedge rstn) begin
if(!rstn)
rom_addr<='d0;
else if(pic_flag)
rom_addr<=rom_addr+'d1;
else if(ypos>=PIC_Y_START+PIC_HEIGHT)
rom_addr<='d0;
end
always @(posedge pclk) begin
char[0 ] <= 128'h00000000000000000000000000000000;
char[1 ] <= 128'h00000000000000000000000000000000;
char[2 ] <= 128'h00_01_00_00_00_40_04_00_00_00_02_00_02_00_00_00;
char[3 ] <= 128'h00_01_C0_00_00_20_0E_00_00_00_03_80_01_80_00_00;
char[4 ] <= 128'h00_01_80_00_00_38_0C_00_00_00_03_00_01_C0_00_10;
char[5 ] <= 128'h02_01_81_C0_00_18_08_00_00_20_03_00_00_CF_FF_F8;
char[6 ] <= 128'h01_81_81_C0_00_18_10_00_1F_F0_03_00_08_C0_00_30;
char[7 ] <= 128'h01_C1_83_00_00_08_20_18_18_30_03_00_0E_40_00_30;
char[8 ] <= 128'h00_E1_86_00_3F_FF_FF_FC_18_30_03_00_0C_00_00_30;
char[9 ] <= 128'h00_61_84_00_00_00_00_00_18_30_03_18_0C_00_00_30;
char[10] <= 128'h00_61_88_00_00_00_00_C0_18_3F_FF_FC_0C_00_18_30;
char[11] <= 128'h00_01_90_00_04_04_00_E0_18_30_03_00_0C_1F_FC_30;
char[12] <= 128'h00_01_80_30_07_FF_10_C0_18_30_03_00_0C_18_18_30;
char[13] <= 128'h0F_FF_FF_F8_06_06_18_C0_18_30_03_00_0C_18_18_30;
char[14] <= 128'h00_00_00_30_06_06_18_C0_18_32_03_00_0C_18_18_30;
char[15] <= 128'h00_00_00_30_06_06_18_C0_1F_F1_03_00_0C_18_18_30;
char[16] <= 128'h00_00_00_30_06_06_18_C0_18_31_C3_00_0C_1F_F8_30;
char[17] <= 128'h00_00_00_30_07_FE_18_C0_18_30_C3_00_0C_18_18_30;
char[18] <= 128'h00_00_00_30_06_06_18_C0_18_30_E3_00_0C_18_18_30;
char[19] <= 128'h00_00_00_30_06_06_18_C0_18_30_43_00_0C_18_18_30;
char[20] <= 128'h03_FF_FF_F0_06_06_18_C0_18_30_03_00_0C_18_18_30;
char[21] <= 128'h00_00_00_30_07_FE_18_C0_18_30_03_00_0C_18_18_30;
char[22] <= 128'h00_00_00_30_06_06_18_C0_18_30_03_00_0C_1F_F8_30;
char[23] <= 128'h00_00_00_30_06_06_18_C0_1F_F0_03_00_0C_18_18_30;
char[24] <= 128'h00_00_00_30_06_06_18_C0_18_30_03_00_0C_10_10_30;
char[25] <= 128'h00_00_00_30_06_06_18_C0_18_30_03_00_0C_00_00_30;
char[26] <= 128'h00_00_00_30_06_06_00_40_18_00_03_00_0C_00_00_30;
char[27] <= 128'h00_00_00_30_06_06_00_C0_00_00_03_00_0C_00_00_30;
char[28] <= 128'h1F_FF_FF_F0_06_3C_0F_C0_00_00_3F_00_0C_00_03_F0;
char[29] <= 128'h00_00_00_30_06_1C_03_80_00_00_07_00_0C_00_00_E0;
char[30] <= 128'h00_00_00_20_04_08_01_00_00_00_06_00_08_00_00_40;
char[31] <= 128'h00000000000000000000000000000000;
end
always @(posedge pclk ) begin
char_2[0] <= 128'h00000018244242424242424224180000 ; // "0"
char_2[1] <= 128'h000000107010101010101010107C0000 ; // "1"
char_2[2] <= 128'h0000003C4242420404081020427E0000 ; // "2"
char_2[3] <= 128'h0000003C424204180402024244380000 ; // "3"
char_2[4] <= 128'h000000040C14242444447E04041E0000 ; // "4"
char_2[5] <= 128'h0000007E404040586402024244380000 ; // "5"
char_2[6] <= 128'h0000001C244040586442424224180000 ; // "6"
char_2[7] <= 128'h0000007E444408081010101010100000 ; // "7"
char_2[8] <= 128'h0000003C4242422418244242423C0000 ; // "8"
char_2[9] <= 128'h0000001824424242261A020224380000 ; // "9"
char_2[10] <= 128'h00000000000018180000000018180000 ;//":"
char_2[11] <= 128'h00000000000000007E00000000000000;//"-"
end
endmodule
顶层模块(i2c驱动模块之前博客有介绍):
module lcd_rgb(
input clk,input rstn,inout [23:0]lcd_rgb,inout sda,
output de,hs,vs,bl,lcd_clk,lcd_rst,scl);
parameter SLAVE_ADDR = 7'b101_0001 ; //器件地址(SLAVE_ADDR)
parameter BIT_CTRL = 1'b0 ; //字地址位控制参数(16b/8b)
parameter CLK_FREQ = 26'd50_000_000; //i2c_dri模块的驱动时钟频率(CLK_FREQ)
parameter I2C_FREQ = 18'd250_000 ; //I2C的SCL时钟频率
parameter init_time = 48'h23_10_26_09_30_00;//初始时间
wire pclk;
wire[23:0]display_data,lcd_rgb_d0,lcd_rgb_i;
wire[15:0]lcd_id;
wire[10:0]xpos, ypos,h_disp,v_disp;
wire dri_clk ; //I2C操作时钟
wire en ; //I2C触发控制
wire [15:0] device_addr ; //I2C操作地址
wire [ 7:0] data_w; //I2C写入的数据
wire [ 7:0] data_r; //I2C读出的数据
wire done ; //I2C操作结束标志
wire ack ; //I2C应答标志 0:应答 1:未应答
wire wl_rh ; //I2C读写控制
wire suce ; //E2PROM读写测试结果 0:失败 1:成功
assign lcd_rgb=de?lcd_rgb_d0:'dz,lcd_rgb_i=lcd_rgb;
wire [7:0]sec,min, hour,day,
mon , yr ; //年
clk_div clk_u(
.clk(clk),
.rstn(rstn),
.rgb_reg(lcd_rgb_i),
.lcd_pclk(pclk),
.lcd_id(lcd_id));
i2c_dri #(
.SLAVE_ADDR (SLAVE_ADDR), //EEPROM从机地址
.CLK_FREQ (CLK_FREQ ), //模块输入的时钟频率
.I2C_FREQ (I2C_FREQ ) //IIC_SCL的时钟频率
) u_i2c_dri(
.clk (clk ),
.rst_n (rstn ),
//i2c interface
.i2c_exec (en ),
.bit_ctrl (BIT_CTRL ),
.i2c_rh_wl (wl_rh ),
.i2c_addr (device_addr ),
.i2c_data_w (data_w),
.i2c_data_r (data_r),
.i2c_done (done ),
.i2c_ack (ack ),
.scl (scl ),
.sda (sda ),
//user interface
.dri_clk (dri_clk )
);
pcf8563_ctrl #(.TIME_INIT (init_time))
rtc_ct_u (
.clk(dri_clk),
.rstn(rstn) ,
.done(done),
.sec(sec) ,
.min(min) ,
.hour(hour) ,
.day(day),
.mon(mon),
.year(yr),
.exec(en),
.rh_wl(wl_rh),
.data_r(data_r),
.addr(device_addr),
.data_w(data_w)
);
//LCD显示模块
lcd_display u_lcd_display(
.pclk (pclk ),
.rstn (rstn),
.xpos (xpos),
.ypos (ypos),
.sec(sec) ,
.min(min) ,
.hour(hour) ,
.day(day),
.mon(mon),
.yr(yr),
.display_data (display_data)
);
//LCD驱动模块
lcd_driver lcd_d_u
(.pclk(pclk),
.rstn(rstn),
.lcd_id(lcd_id),
.display_data(display_data),
.xpos(xpos),
.ypos(ypos),
.h_disp(h_disp),
.v_disp(v_disp),
.de(de),
.hs(hs),
.vs(vs),
.bl(bl),
.lcd_clk(lcd_clk),
.lcd_rst(lcd_rst),
.lcd_rgb(lcd_rgb_d0));
endmodule
上板效果:
rtc实验