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

2009-6-8 14:00 3542 8 9 分类: FPGA/CPLD

<?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…语句真是值得深刻的推敲和尤其的注意啊!


注意1if…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%)


可见,该代码综合后占用了489D触发器(A3P030总共768个),占用了16I/O口线(总共可用I/O 83个)。本来是想增加可调闹铃功能,但估计D触发器不够用了。




完毕!

PARTNER CONTENT

文章评论2条评论)

登录后参与讨论

用户417217 2012-5-8 22:55

很好的程序代码

tengjingshu_112148725 2009-6-8 15:54

好,谢谢分享
相关推荐阅读
用户104380 2009-06-08 13:49
首次Verilog 操作液晶手记(1)
语言:Verilog<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />开发环境:Lib...
用户104380 2008-08-19 17:02
OEM与ODM
OEM OEM的英语全称是“Original Equipment Manufacture”(原始设备生产商),它的出现与经济和工业化的发展有着密切的联系。随着经济全球化的发展,著名品牌的商品销售数量迅...
用户104380 2008-08-19 16:34
JFET可变电阻仿真
JFET可变电阻的仿真<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />fyw 以N沟...
用户104380 2008-08-14 15:46
二极管,BJT和JFET的SPICE模型参数
SPICE中二极管模型的参数<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />IS  ...
用户104380 2008-08-14 12:45
从叠加到戴维南
戴维南分析应该算得上是所有电路分析中用的最频繁的手法之一。这是一份从叠加到戴维南定理的分析过程,以一个最简单的电路,演示了戴维南的过程和应用,值得一看。...
EE直播间
更多
我要评论
2
8
关闭 站长推荐上一条 /3 下一条