初学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