<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
(续首次Verilog 操作液晶手记(1))
下面的代码是对上述的完善,预期功能是在液晶的第一行显示时间,第二行显示日期,并且时间可调。调节方式:有一个快调按键,调节小时,一个慢调按键,用来调节分钟。一个确定按键,当调到要求的时间时按一下该按键。
// ms12232.v
module lcd(clk, reset, incs, incf, inc, e, rs, rw, db); //这个分号容易丢掉
input clk, reset;
output e, rs, rw;
output [7:0] db;
input inc, incs, incf; // inc为确定键,incs为慢调键,incf为快调键。
wire clk, reset;
reg e, rs, rw;
reg [7:0] db;
wire inc, incs, incf;
reg [10:0] sectime; //调节时间快慢的系数。
reg [13:0] fenpin; //对系统时钟分频获得液晶的操作周期。
reg [7:0] state;
reg [10:0] fenpin2; //对液晶的操作周期继续分频获得周期1秒的信号secflag。
//周期分别为1秒,1分钟,1小时。
reg secflag;
reg minflag;
reg hourflag;
reg [7:0] year3;
reg [7:0] year2;
reg [7:0] year1;
reg [7:0] year0;
reg [7:0] mon1;
reg [7:0] mon0;
reg [7:0] day1;
reg [7:0] day0;
reg [7:0] hour1;
reg [7:0] hour0;
reg [7:0] min1;
reg [7:0] min0;
reg [7:0] sec1;
reg [7:0] sec0;
parameter Idle = 1;
parameter S1 = 2; // 发指令0x30
parameter S2 = 3; // 发指令0x30
parameter S3 = 4; // 发指令0x0e
parameter S4 = 5; // 发指令0x01
parameter S5 = 6; // 发指令0x02
parameter S6 = 7; // 发指令0x04
parameter S7 = 8; // 发指令0x82
parameter S8 = 9; // 发指令0x82
parameter Shour1 = 10;
parameter Shour0 = 11;
parameter Smaohao0 = 12;
parameter Smin1 = 13;
parameter Smin0 = 14;
parameter Smaohao1 = 15;
parameter Ssec1 = 16;
parameter Ssec0 = 17;
parameter Sspace = 18;
parameter Syearadd = 20;
parameter Syear3 = 21;
parameter Syear2 = 22;
parameter Syear1 = 23;
parameter Syear0 = 24;
parameter Sgang0 = 25;
parameter Smon1 = 26;
parameter Smon0 = 27;
parameter Sgang1 = 28;
parameter Sday1 = 29;
parameter Sday0 = 30;
// 获取液晶操作周期
always @(negedge clk or negedge reset)
begin
if(!reset)
begin
fenpin <= 0;
end
else fenpin <= fenpin + 1;
end
always @(fenpin)
begin
if(fenpin==0) e <= 1;
else if(fenpin == 4096) e <= 0;
else e <= e;
end
// 液晶显示操作
always @(posedge e or negedge reset)
begin
if(!reset)
begin
state <= Idle;
rs <= 0;
rw <= 0;
db <= 0;
year3 <= 2;
year2 <= 0;
year1 <= 0;
year0 <= 9;
mon1 <= 0;
mon0 <= 6;
day1 <= 0;
day0 <= 3;
end
else case(state)
Idle:
begin
state <= S1;
end
S1:
begin
rs <= 0;
db <= 8'h30;
state <= S2;
end
S2:
begin
db <= 8'h30;
state <= S3;
end
S3:
begin
db <= 8'he;
state <= S4;
end
S4:
begin
rs <= 0;
db <= 8'h1;
state <= S5;
end
S5:
begin
db <= 8'h2;
state <= S6;
end
S6:
begin
db <= 8'h4;
state <= S7;
end
S7:
begin
rs <= 0;
db <= 8'h81;
state <= S8;
end
S8:
begin
rs <= 0;
db <= 8'h81;
state <= Sspace;
end
Sspace:
begin
rs <= 1;
db <= 32; // 空格
state <= Shour1;
end
Shour1:
begin
db <= hour1 + 48;
state <= Shour0;
end
Shour0:
begin
db <= hour0 + 48;
state <= Smaohao0;
end
Smaohao0:
begin
db <= 58; // 冒号
state <= Smin1;
end
Smin1:
begin
db <= min1 + 48;
state <= Smin0;
end
Smin0:
begin
db <= min0 + 48;
state <= Smaohao1;
end
Smaohao1:
begin
db <= 58; // 冒号
state <= Ssec1;
end
Ssec1:
begin
db <= sec1 + 48;
state <= Ssec0;
end
Ssec0:
begin
db <= sec0 + 48;
state <= Syearadd;
end
Syearadd:
begin
rs <= 0;
db <= 8'h91;
state <= Syear3;
end
Syear3:
begin
rs <= 1;
db <= year3 + 48;
state <= Syear2;
end
Syear2:
begin
db <= year2 + 48;
state <= Syear1;
end
Syear1:
begin
db <= year1 + 48;
state <= Syear0;
end
Syear0:
begin
db <= year0 + 48;
state <= Sgang0;
end
Sgang0:
begin
db <= 45; // 横杠
state <= Smon1;
end
Smon1:
begin
db <= mon1 + 48;
state <= Smon0;
end
Smon0:
begin
db <= mon0 + 48;
state <= Sgang1;
end
Sgang1:
begin
db <= 45; // 横杠
state <= Sday1;
end
Sday1:
begin
db <= day1 + 48;
state <= Sday0;
end
Sday0:
begin
db <= day0 + 48;
state <= S8;
end
endcase
end
// 调节时间
/*
当综合出现某一个变量与标准的D触发器不匹配的错误时,通常是由于if…else…或者if…else if…else语句造成的。所以verilog中的if…else…语句真是值得深刻的推敲和尤其的注意啊!
注意1:if…else…结构要完整,不能只有if没有else。
注意2:对条件的判断要完整。
*/
always @(negedge reset or negedge inc or negedge incs or negedge incf)
begin
if(!reset)
begin
sectime <= 1463;
end
else if(!inc)
sectime <= 1463; // 正常值,该值下时间正常走动。
else if(!incs) // 慢调
sectime <= 24;
else if(!incf) // 快调
sectime <= 0;
else
sectime <= 1463;
end
// 获取秒信号
always @(negedge e or negedge reset)
begin
if(!reset)
begin
fenpin2 <= 0;
secflag <= 0;
end
else if(fenpin2 == sectime)
begin
fenpin2 <= 0;
secflag <= ~secflag;
end
else fenpin2 <= fenpin2 + 1;
end
// 秒加1,同时获取分钟信号
/*
这里的敏感电平列表中不能用电平触发的形式,要用边沿触发。因为最开始就是使用电平触发的形式(always @(secflag or reset)),仿真综合都没问题,但一旦烧进芯片中液晶工作不正长,会显示一些预期之外的字符,后改为边沿触发就正常了。以下的分钟加1和小时加1都是同样的道理,至于为何如此,我尚今不知。
另外发现的一个现象是:verilog的除法的除数必须是2的整次幂,否则不能综合。发现的原因是曾试过用整除和取余的方法来进行秒的加1和进位的实现,综合时出现错误,有了上述的提示。
还有一个需要注意的地方是:敏感电平列表中不能同时出现边沿和电平触发的形式,要么都是边沿触发,要么都是电平触发,并且不能在同一个敏感列表中检测信号的上升边沿和下降边沿。
*/
always @(posedge secflag or negedge reset)
begin
if(!reset)
begin
sec1 <= 5;
sec0 <= 6;
minflag <= 0;
end
else
begin
minflag <= 0;
if(sec0 == 9)
begin
sec0 <= 0;
if(sec1 == 5)
begin
sec1 <= 0;
minflag <= 1;
end
else
sec1 <= sec1 + 1;
end
else
sec0 <= sec0 + 1;
end
end
// 分钟加1,同时获取小时信号
always @(posedge minflag or negedge reset)
begin
if(!reset)
begin
min1 <= 5;
min0 <= 8;
hourflag <= 0;
end
else
begin
hourflag <= 0;
if(min0 == 9)
begin
min0 <= 0;
if(min1 == 5)
begin
min1 <= 0;
hourflag <= 1;
end
else
min1 <= min1 + 1;
end
else
min0 <= min0 + 1;
end
end
// 小时加1
always @(posedge hourflag or negedge reset)
begin
if(!reset)
begin
hour1 <= 1;
hour0 <= 9;
end
else
begin
if(hour0 == 3)
begin
if(hour1 == 2)
begin
hour0 <= 0;
hour1 <= 0;
// one day over,这里可以给出一天之后的处理代码。
end
else
begin
hour0 <= hour0 + 1;
end
end
else if(hour0 == 9)
begin
hour0 <= 0;
hour1 <= hour1 + 1;
end
else
begin
hour0 <= hour0 + 1;
end
end
end
endmodule
参考综合后的记录:
Core Cells : 489 of 768 (64%)
IO Cells : 16 of 83 (19%)
可见,该代码综合后占用了489个D触发器(A3P030总共768个),占用了16个I/O口线(总共可用I/O 83个)。本来是想增加可调闹铃功能,但估计D触发器不够用了。
完毕!
用户417217 2012-5-8 22:55
tengjingshu_112148725 2009-6-8 15:54