新闻  |   论坛  |   博客  |   在线研讨会
首次Verilog 操作液晶手记(2)
0750long | 2009-06-13 12:28:54    阅读:2151   发布文章

首次Verilog 操作液晶手记(2)

 

(续首次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触发器不够用了。

完毕!

*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
推荐文章
最近访客