此实验是在“基于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
激励文件(只对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
这里在把一段式状态机的仿真波形贴出来,可以好好的比较比较,这样做有什么不同,但要知道,三段式从输入到输出要比一段式要延时一个节拍。