简单三段式状态机实验2-LCD12864

  此实验是在“基于I2C EPPRPM(AT24C02B) + LCD12864实验”基础上,把LCD模块里的一段式状态机改成三段式,I2C EPPROM模块暂时未改出来,一步一步来吧,改完后代码下载到板子上验证是OK的。

    三段式状态机里面要注意的是,抽出来reg 如计数器num、lcd_rs,在利用状态作为判断条件时,得注意是用n_state呢还是用c_state,对于我这样的初学者,一时半会弄不清是用哪个作为判断条件好,怎么办,每种情况都试一次吧。结果用n_state能正常显示,用c_state显示乱码。

用c_state作为判断条件的仿真波形如下:

好奇怪哦,左边框中num为什么没有清零呢?这是因为if else产生的优先权导致,一不小心就范这样的错误。

 1 /***************************************************/
 2 reg [4:0] c_state,n_state;
 3 reg [6:0] num;
 4 reg lcd_rs;
 5 always @(posedge lcd_clk or negedge rst_n)
 6 if(!rst_n)
 7     lcd_rs <= 1'b0;
 8 else if((c_state == WRITE_DATA0)||(c_state == WRITE_DATA1)||(c_state == WRITE_DATA2)||(c_state == WRITE_DATA3))
 9     lcd_rs <= 1'b1;        //data mode
10 else
11     lcd_rs <= 1'b0;        //cmd mode
12 /***************************************************/
13 
14 
15 /***********************************************************/
16 always @(posedge lcd_clk or negedge rst_n)
17 if(!rst_n)
18     num <= 7'd0;
19 else if((c_state == WRITE_DATA0) || (c_state == WRITE_DATA1))
20     num <= num + 1'b1;
21 else if(num == 7'd20) //不能放在状态判断后面,否则num不会归零
22     num <= 7'd0;
23 /***********************************************************/

用n_state作为判断条件的仿真波形如下:

虽然把c_state改成n_state ,但是这个num能清零,好奇怪哦,有时间在好好研究吧,为了保险起见还是改成如下形式:

 1 /***************************************************/
 2 reg [4:0] c_state,n_state;
 3 reg [6:0] num;
 4 reg lcd_rs;
 5 always @(posedge lcd_clk or negedge rst_n)
 6 if(!rst_n)
 7     lcd_rs <= 1'b0;
 8 else if((n_state == WRITE_DATA0)||(n_state == WRITE_DATA1)||(n_state == WRITE_DATA2)||(n_state == WRITE_DATA3))
 9     lcd_rs <= 1'b1;        //data mode
10 else
11     lcd_rs <= 1'b0;        //cmd mode
12 /***************************************************/
13 
14 
15 /***********************************************************/
16 always @(posedge lcd_clk or negedge rst_n)
17 if(!rst_n)
18     num <= 7'd0;
19 else if(num == 7'd20)
20     num <= 7'd0;
21 else if((c_state == WRITE_DATA0) || (c_state == WRITE_DATA1))
22     num <= num + 1'b1;
23 /***********************************************************/

注意看上面两种仿真波形,右边框的位置上是有区别的,c_state要比n_state慢一节拍,但对应输出数据值是c_state,比如当c_state=7时,是对LCD写数据,故lcd_rs=1,输出lcd_data="I",很明显,用c_state作为判断条件的是不对的,输出已经延长一拍。

三段式状态机里还要注意一个地方,在状态机里面既要作为输出,又要作为其他输出信号的判断条件,如下面的en,会产生警告,报出间接指出latch。

/***********************************************************/
reg en;
always @(posedge sys_clk or negedge rst_n)
if(!rst_n) begin
    en <= 1'b1;
    dis_data <= 8'h00;
end
else case(num)
        //I2C閫氫俊瀹為獙
        7'd0:    dis_data <= "I";
        7'd1:    dis_data <= "2"; 
        .
        .
        .
/***********************************************************/

assign    lcd_en = en ?  lcd_clk : 1'b0;
/***********************************************************/

“Warning (10240): Verilog HDL Always Construct warning at LCD12864.v(121): inferring latch(es) for variable "en", which holds its previous value in one or more paths through the always construct”

虽然有些警告无关紧要的,但还是应尽量避免这样写,办法也是要把en抽出来,我这里是简单的处理下。

1 /***************************************************/
2 wire    en;
3 assign    en = 1'b1;//(n_state == STOP) ? 1'b0 : 1'b1;
4 assign    lcd_en = en ?  lcd_clk : 1'b0;
5 /***************************************************/

其实这里还有个问题,程序中在IDLE时,lcd_data <= 8'hzz;但仿真波形中没有看到,跟踪调试时,发现这语句永远不执行,自己屡了屡,确实执行不到,一时半会我也想不明白。比较简单的状态机,差不多先这样吧。

代码实现:

LCD12864.v

  1 module LCD12864(
  2                     //input 
  3                     sys_clk,
  4                     rst_n,
  5                     dis_data_low,
  6                     dis_data_hig,
  7                     
  8                     //output 
  9                     lcd_rs,
 10                     lcd_rw,
 11                     lcd_en,
 12                     lcd_data,
 13                     lcd_psb
 14                 );
 15 input         sys_clk;// 50MHZ
 16 input         rst_n;
 17 input [7:0] dis_data_low;
 18 input [7:0] dis_data_hig;
 19 
 20 output         lcd_rs;//H:data    L:command
 21 output         lcd_rw;//H:read module    L:write module
 22 output         lcd_en;//H active
 23 output [7:0]lcd_data;
 24 output        lcd_psb;//H:parallel    module    L:SPI module
 25 
 26 /***************************************************/
 27 parameter     T3MS            = 18'd149_999;
 28 parameter    IDLE            = 5'd0,
 29             INIT_FUN_SET1    = 5'd1,
 30             INIT_FUN_SET2    = 5'd2,
 31             INIT_DISPLAY    = 5'd3,
 32             INIT_CLEAR        = 5'd4,
 33             INIT_DOT_SET    = 5'd5,
 34             SET_DDRAM        = 5'd6,
 35             WRITE_DATA0        = 5'd7,
 36             SET_DDRAM2        = 5'd8,
 37             WRITE_DATA1        = 5'd9,
 38             WRITE_DATA2        = 5'd10,
 39             WRITE_DATA3        = 5'd11;
 40 /***************************************************/
 41 //产生周期为6MS的lcd_clk给LCD
 42 reg [17:0] cnt;
 43 reg lcd_clk;
 44 always @(posedge sys_clk or negedge rst_n)
 45 if(!rst_n) begin
 46     cnt <= 18'd0;
 47     lcd_clk <= 1'b0;
 48 end
 49 else if(cnt == T3MS)begin
 50     cnt <= 18'd0;
 51     lcd_clk <= ~lcd_clk;
 52 end
 53 else
 54     cnt <= cnt + 1'b1;
 55 /***************************************************/
 56 reg lcd_rs;
 57 always @(posedge lcd_clk or negedge rst_n)
 58 if(!rst_n)
 59     lcd_rs <= 1'b0;
 60 else if((n_state == WRITE_DATA0)||(n_state == WRITE_DATA1)||(n_state == WRITE_DATA2)||(n_state == WRITE_DATA3))
 61     lcd_rs <= 1'b1;        //data mode
 62 else
 63     lcd_rs <= 1'b0;        //cmd mode
 64 /***************************************************/
 65 reg [4:0] c_state,n_state;
 66 always @(posedge lcd_clk or negedge rst_n)
 67 if(!rst_n)
 68     c_state <= IDLE;
 69 else
 70     c_state <= n_state;
 71 /***************************************************/
 72 always @(*)
 73 case(c_state)
 74     IDLE:             n_state = INIT_FUN_SET1;
 75     INIT_FUN_SET1:    n_state = INIT_FUN_SET2;
 76     INIT_FUN_SET2:    n_state = INIT_DISPLAY;
 77     INIT_DISPLAY:    n_state = INIT_CLEAR; 
 78     INIT_CLEAR:     n_state = INIT_DOT_SET;
 79     INIT_DOT_SET:   n_state = SET_DDRAM;    
 80     SET_DDRAM:        n_state = WRITE_DATA0;
 81     WRITE_DATA0:    if(num == 7'd11) n_state = SET_DDRAM2;
 82                     else n_state = WRITE_DATA0;
 83     SET_DDRAM2:     n_state = WRITE_DATA1;
 84     WRITE_DATA1:     if(num == 7'd20) n_state = WRITE_DATA2;
 85                     else n_state = WRITE_DATA1;
 86     WRITE_DATA2:    n_state = WRITE_DATA3;
 87     WRITE_DATA3:    n_state = SET_DDRAM;
 88     default:         n_state = IDLE;
 89 endcase    
 90 /***************************************************/
 91 reg [7:0] lcd_data;
 92 reg [7:0] dis_data;
 93 always @(posedge lcd_clk or negedge rst_n)
 94 if(!rst_n)    lcd_data <= 8'h00;
 95 else case(n_state)
 96         IDLE:            lcd_data <= 8'hzz;
 97         INIT_FUN_SET1:    lcd_data <= 8'h30;    //function setting    
 98         INIT_FUN_SET2:    lcd_data <= 8'h30;    //function setting        
 99         INIT_DISPLAY:    lcd_data <= 8'h0c;    //display setting    
100         INIT_CLEAR:        lcd_data <= 8'h01;    //clear setting
101         INIT_DOT_SET:    lcd_data <= 8'h06;    //dot setting    
102         SET_DDRAM:        lcd_data <= 8'h91; //2 line
103         WRITE_DATA0:    lcd_data <= dis_data;
104         SET_DDRAM2:        lcd_data <= 8'h89; //3 line
105         WRITE_DATA1:    lcd_data <= dis_data;
106         WRITE_DATA2:    lcd_data <= dis_data_hig;    //high byte
107         WRITE_DATA3:    lcd_data <= dis_data_low; //low byte
108         default: ;
109     endcase
110 /***********************************************************/
111 reg [6:0] num;
112 always @(posedge lcd_clk or negedge rst_n)
113 if(!rst_n)
114     num <= 7'd0;
115 else if(num == 7'd20)
116     num <= 7'd0;
117 else if((n_state == WRITE_DATA0) || (n_state == WRITE_DATA1))
118     num <= num + 1'b1;
119 /***********************************************************/
120 always @(posedge sys_clk or negedge rst_n)
121 if(!rst_n)
122     dis_data <= 8'h00;
123 else case(num)
124         7'd0:    dis_data <= "I";
125         7'd1:    dis_data <= "2"; 
126         7'd2:    dis_data <= "C";
127         7'd3:    dis_data <= " ";//8'hcd;
128         7'd4:    dis_data <= "E";//8'ha8;
129         7'd5:    dis_data <= "P";//8'hd0;
130         7'd6:    dis_data <= "P";//8'hc5;
131         7'd7:    dis_data <= "R";//8'hca;
132         7'd8:    dis_data <= "O";//8'hb5;
133         7'd9:    dis_data <= "M";//8'hd1;
134         7'd10:    dis_data <= " ";//8'he9;
135         7'd11:    dis_data <= "0";
136         7'd12:    dis_data <= "3";
137         7'd13:    dis_data <= 8'hb5;
138         7'd14:    dis_data <= 8'hd8; 
139         7'd15:    dis_data <= 8'hd6; 
140         7'd16:    dis_data <= 8'hb7; 
141         7'd17:    dis_data <= 8'hd6;
142         7'd18:    dis_data <= 8'hb5;
143         7'd19:    dis_data <= " ";
144         default:dis_data <= 8'h00;
145     endcase
146 /***************************************************/
147 assign    lcd_rw = 1'b0;//只有写模式
148 assign    lcd_psb = 1'b1;//并口模式
149 wire    en;
150 assign    en = 1'b1;//(n_state == STOP) ? 1'b0 : 1'b1;
151 assign    lcd_en = en ?  lcd_clk : 1'b0;
152 /***************************************************/
153 endmodule
View Code

激励文件(只对LCD模块进行仿真):

lcd_top.v

 1 `timescale 1ns/10ps
 2 module lcd_top;
 3 /*************************************************************/
 4 reg          sys_clk;
 5 reg          rst_n;
 6 wire         start_cnt;
 7 /*************************************************************/
 8 initial begin
 9     sys_clk = 1'b0;
10     rst_n = 1'b0;
11     #100;
12     rst_n = 1'b1;
13 end
14 /*************************************************************/
15 always #10 sys_clk = ~sys_clk;
16 /*************************************************************/
17 wire         lcd_rs;//H:data    L:command
18 wire         lcd_rw;//H:read module    L:write module
19 wire         lcd_en;//H active
20 wire [7:0]    lcd_data;
21 wire        lcd_psb;//H:parallel    module    L:SPI module 
22 LCD12864    LCD12864_u1(
23                     //input 
24                     .sys_clk(sys_clk),
25                     .rst_n(rst_n),
26                     .dis_data_low("8"),
27                     .dis_data_hig("c"),
28                     
29                     //output 
30                     .lcd_rs(lcd_rs),
31                     .lcd_rw(lcd_rw),
32                     .lcd_en(lcd_en),
33                     .lcd_data(lcd_data),
34                     .lcd_psb(lcd_psb)
35                 );
36 endmodule
View Code

这里在把一段式状态机的仿真波形贴出来,可以好好的比较比较,这样做有什么不同,但要知道,三段式从输入到输出要比一段式要延时一个节拍。

原文地址:https://www.cnblogs.com/wen2376/p/3448116.html