用verilog HDL实现LCD液晶显示代码

初学verilog,写得不好请多指教
/*在LCD上显示12580 yi an wo bang ni

内部显示地址
  1   2  3  4   5  6   7  8  9  10  11  12  13  14  15  16
 00  01 02  03  04 05 06 07 08   09 0A  0B  0C  0D  0E  0F   第一行
 40  41 42  43  44 45 46 47 48   49 4A  4B  4C  4D  4E  4F   第二行
比如第二行第一个字符的地址是40H,那么是否直接写入40H就可以
将光标定位在第二行第一个字符的位置呢?这样不行,因为写入显
示地址时要求最高位D7恒定为高电平1所以实际写入的数据应该是
01000000B(40H)+10000000B(80H)=11000000B(C0H)
*/
module lcdcnt(clk,rst_n,en,rs,rw,data);
input clk,rst_n;//50MHZ clock,reset
output[7:0]data;//8 bit output data
output rs,rw,en;
reg rs,rw,en;
reg[7:0]data;


parameter
IDLE =10'b0000000000,//闲置
CLEARSCREEN =10'b0000000001,//清屏
CURSORHOME =10'b0000000010,//光标归位
SETMODE =10'b0000000100,//模式设置
DISPCTRL =10'b0000001000,//显示设置
SHIFTCTRL =10'b0000100000,//移位设置
SETFUNC =10'b0001000000,//功能设置
SETCGRAM =10'b0010000000,//设置CGRAM
SETDDRAM1 =10'b0100000001,//DDRAM1
SETDDRAM2 =10'b1000000000,//DDRAM2
// BF =10'b1000000001,//繁忙位设置
WRITERAM1 =10'b1000000010,//写data
WRITERAM2 =10'b1000000011;//写data
reg[15:0] cnt;     //时钟计数


always@(posedge clk)
begin
if(!rst_n) cnt<=0;
else cnt<=cnt+1'b1;
end
wire tc_cnt;//25000个时钟周期时,tc_cn保持一个时钟的高电平,再25000个低电平,有assign设置成wire?
assign tc_cnt=(cnt==16'd25000)?1'b1:0;
reg clk_div;//在tc_cnt的上升沿clk_div取反
always@(posedge tc_cnt)
begin
if(!rst_n) clk_div<=0;
else clk_div<=~clk_div;
end
reg clk_int;//在clk_div上升沿clk_int取反
always@(posedge clk_div)
begin
if(!rst_n) clk_int<=0;
else clk_int<=~clk_int;
end
always@(negedge clk_div)//在clk_div下降沿使能信号取反
begin
if(!rst_n) en=0;
else en=~en;
end


reg[9:0] state;//10位状态,前2位为rs,rw,后面为d0-d7
wire[15:0]data_in;
assign data_in=16'd12580;
reg[7:0] disp_data[5:0];
reg[2:0] flag;
reg[3:0] data_flag;


always@(posedge clk or negedge rst_n)//为什么这里要用阻塞式赋值,用非阻塞式显示01258?
begin
if(!rst_n)
begin
flag=0;
data_flag=0;
end
else
begin
if((data_in/10000)>0)
begin
flag=flag+3'd1;
if(flag==3'd1)
data_flag=data_in/10000;
if(flag==3'd2)
data_flag=(data_in%10000)/1000;
if(flag==3'd3)
data_flag=(data_in%1000)/100;
if(flag==3'd4)
data_flag=(data_in%100)/10;
if(flag==3'd5)
data_flag=data_in%10;
if(flag==3'd6)
flag=3'd0;
end
end
end
always@(posedge clk)
begin
case(data_flag)
4'd0:disp_data[flag]="0";
4'd1:disp_data[flag]="1";
4'd2:disp_data[flag]="2";
4'd3:disp_data[flag]="3";
4'd4:disp_data[flag]="4";
4'd5:disp_data[flag]="5";
4'd6:disp_data[flag]="6";
4'd7:disp_data[flag]="7";
4'd8:disp_data[flag]="8";
4'd9:disp_data[flag]="9";
endcase
end
reg[5:0]addr;
always@(posedge clk_int or negedge rst_n)
begin
if(!rst_n)
begin
state=IDLE;
addr=6'b000000;
data=8'b00000000;
rs=0;
rw=0;
end
else
begin
case(state)
IDLE:
begin
data=8'bzzzz_zzzz;
state=CLEARSCREEN;
end
CLEARSCREEN:
begin 
rs=0;
rw=0;
data=8'b00000001;
state=CURSORHOME;
end
CURSORHOME:
begin
rs=0;
rw=0;
data=8'b00000010;
state=SETMODE;
end
SETMODE:
begin
rs=0;
rw=0;
data=8'b00000110;
state=DISPCTRL;
end
DISPCTRL:
begin
rs=0;
rw=0;
data=8'b00001110;
state=SHIFTCTRL;
end
SHIFTCTRL:
begin
rs=0;
rw=0;
data=8'b00110000;
state=SETFUNC;
end
SETFUNC:
begin
rs=0;
rw=0;
data=8'b00111000;//这里设置显示2行还是一行,之前设成一行,搞半天没明白问题
state=SETCGRAM;
end
SETCGRAM:
begin
rs=0;
rw=0;
data=8'b01000000;
state=SETDDRAM1;
end
SETDDRAM1:
begin
addr=0;
rs=0;
rw=0;
data=8'b10000000;//设置起始地址,第一行0x00H+0x80H
state=WRITERAM1;
end
SETDDRAM2:
begin
addr=0;
rs=0;
rw=0;
data=8'b11000000;//设置起始地址,第二行0x40H+0x80H
state=WRITERAM2;
end
/* BF:
begin
rs=0;
rw=1;
data=8'b00000000;
state=WRITERAM1;
end*/
WRITERAM1:
begin
rs=1;//写数据打开
rw=0;
case(addr)//addr只是一个计数用的,不是地址,地址写完一个会自动加一,前面都有设置的,那怎么实现计数变化呢?
6'b000_000:data=disp_data[1];
6'b000_001:data=disp_data[2];
6'b000_010:data=disp_data[3];
6'b000_011:data=disp_data[4];
6'b000_100:data=disp_data[5];
6'b000_101:data=" ";
6'b000_110:data=" ";
6'b000_111:data="m";
6'b001_000:data="o";
6'b001_001:data="s";
6'b001_010:data="i";
6'b001_011:data="m";
6'b001_100:data="o";
6'b001_101:data="s";
6'b001_110:data="i";
6'b001_111:data=" ";
endcase
if(addr==6'b001_111)
begin
addr=0;
state=SETDDRAM2;
end
else addr=addr+6'b1;
end
WRITERAM2:
begin
rs=1;
rw=0;
case(addr)
6'b000_000:data="y";
6'b000_001:data="i";
6'b000_010:data=" ";
6'b000_011:data="a";
6'b000_100:data="n";
6'b000_101:data=" ";
6'b000_110:data="w";
6'b000_111:data="o";
6'b001_000:data=" ";
6'b001_001:data="b";
6'b001_010:data="a";
6'b001_011:data="n";
6'b001_100:data="g";
6'b001_101:data=" ";
6'b001_110:data="n";
6'b001_111:data="i";
endcase
if(addr==6'b001_111)
begin
addr=0;
state=SETDDRAM1;
end
else addr=addr+6'b1;
end
default:state=IDLE;
endcase
end
end
endmodule

你可能感兴趣的:(fpga)