此实验是在“基于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产生的优先权导致,一不小心就范这样的错误。

 /***************************************************/
reg [:] c_state,n_state;
reg [:] num;
reg lcd_rs;
always @(posedge lcd_clk or negedge rst_n)
if(!rst_n)
lcd_rs <= 'b0;
else if((c_state == WRITE_DATA0)||(c_state == WRITE_DATA1)||(c_state == WRITE_DATA2)||(c_state == WRITE_DATA3))
lcd_rs <= 'b1; //data mode
else
lcd_rs <= 'b0; //cmd mode
/***************************************************/ /***********************************************************/
always @(posedge lcd_clk or negedge rst_n)
if(!rst_n)
num <= 'd0;
else if((c_state == WRITE_DATA0) || (c_state == WRITE_DATA1))
num <= num + 'b1;
else if(num == 'd20) //不能放在状态判断后面,否则num不会归零
num <= 'd0;
/***********************************************************/

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

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

 /***************************************************/
reg [:] c_state,n_state;
reg [:] num;
reg lcd_rs;
always @(posedge lcd_clk or negedge rst_n)
if(!rst_n)
lcd_rs <= 'b0;
else if((n_state == WRITE_DATA0)||(n_state == WRITE_DATA1)||(n_state == WRITE_DATA2)||(n_state == WRITE_DATA3))
lcd_rs <= 'b1; //data mode
else
lcd_rs <= 'b0; //cmd mode
/***************************************************/ /***********************************************************/
always @(posedge lcd_clk or negedge rst_n)
if(!rst_n)
num <= 'd0;
else if(num == 'd20)
num <= 'd0;
else if((c_state == WRITE_DATA0) || (c_state == WRITE_DATA1))
num <= num + 'b1;
/***********************************************************/

注意看上面两种仿真波形,右边框的位置上是有区别的,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 <= 'b1;
dis_data <= 'h00;
end
else case(num)
//I2C閫氫俊瀹為獙
'd0: dis_data <= "I";
'd1: dis_data <= "2";
.
.
.
/***********************************************************/ assign lcd_en = en ? lcd_clk : '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抽出来,我这里是简单的处理下。

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

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

代码实现:

LCD12864.v

 module LCD12864(
//input
sys_clk,
rst_n,
dis_data_low,
dis_data_hig, //output
lcd_rs,
lcd_rw,
lcd_en,
lcd_data,
lcd_psb
);
input sys_clk;// 50MHZ
input rst_n;
input [:] dis_data_low;
input [:] dis_data_hig; output lcd_rs;//H:data L:command
output lcd_rw;//H:read module L:write module
output lcd_en;//H active
output [:]lcd_data;
output lcd_psb;//H:parallel module L:SPI module /***************************************************/
parameter T3MS = 'd149_999;
parameter IDLE = 'd0,
INIT_FUN_SET1 = 'd1,
INIT_FUN_SET2 = 'd2,
INIT_DISPLAY = 'd3,
INIT_CLEAR = 'd4,
INIT_DOT_SET = 'd5,
SET_DDRAM = 'd6,
WRITE_DATA0 = 'd7,
SET_DDRAM2 = 'd8,
WRITE_DATA1 = 'd9,
WRITE_DATA2 = 'd10,
WRITE_DATA3 = 'd11;
/***************************************************/
//产生周期为6MS的lcd_clk给LCD
reg [:] cnt;
reg lcd_clk;
always @(posedge sys_clk or negedge rst_n)
if(!rst_n) begin
cnt <= 'd0;
lcd_clk <= 'b0;
end
else if(cnt == T3MS)begin
cnt <= 'd0;
lcd_clk <= ~lcd_clk;
end
else
cnt <= cnt + 'b1;
/***************************************************/
reg lcd_rs;
always @(posedge lcd_clk or negedge rst_n)
if(!rst_n)
lcd_rs <= 'b0;
else if((n_state == WRITE_DATA0)||(n_state == WRITE_DATA1)||(n_state == WRITE_DATA2)||(n_state == WRITE_DATA3))
lcd_rs <= 'b1; //data mode
else
lcd_rs <= 'b0; //cmd mode
/***************************************************/
reg [:] c_state,n_state;
always @(posedge lcd_clk or negedge rst_n)
if(!rst_n)
c_state <= IDLE;
else
c_state <= n_state;
/***************************************************/
always @(*)
case(c_state)
IDLE: n_state = INIT_FUN_SET1;
INIT_FUN_SET1: n_state = INIT_FUN_SET2;
INIT_FUN_SET2: n_state = INIT_DISPLAY;
INIT_DISPLAY: n_state = INIT_CLEAR;
INIT_CLEAR: n_state = INIT_DOT_SET;
INIT_DOT_SET: n_state = SET_DDRAM;
SET_DDRAM: n_state = WRITE_DATA0;
WRITE_DATA0: if(num == 'd11) n_state = SET_DDRAM2;
else n_state = WRITE_DATA0;
SET_DDRAM2: n_state = WRITE_DATA1;
WRITE_DATA1: if(num == 'd20) n_state = WRITE_DATA2;
else n_state = WRITE_DATA1;
WRITE_DATA2: n_state = WRITE_DATA3;
WRITE_DATA3: n_state = SET_DDRAM;
default: n_state = IDLE;
endcase
/***************************************************/
reg [:] lcd_data;
reg [:] dis_data;
always @(posedge lcd_clk or negedge rst_n)
if(!rst_n) lcd_data <= 'h00;
else case(n_state)
IDLE: lcd_data <= 'hzz;
INIT_FUN_SET1: lcd_data <= 'h30; //function setting
INIT_FUN_SET2: lcd_data <= 'h30; //function setting
INIT_DISPLAY: lcd_data <= 'h0c; //display setting
INIT_CLEAR: lcd_data <= 'h01; //clear setting
INIT_DOT_SET: lcd_data <= 'h06; //dot setting
SET_DDRAM: lcd_data <= 'h91; //2 line
WRITE_DATA0: lcd_data <= dis_data;
SET_DDRAM2: lcd_data <= 'h89; //3 line
WRITE_DATA1: lcd_data <= dis_data;
WRITE_DATA2: lcd_data <= dis_data_hig; //high byte
WRITE_DATA3: lcd_data <= dis_data_low; //low byte
default: ;
endcase
/***********************************************************/
reg [:] num;
always @(posedge lcd_clk or negedge rst_n)
if(!rst_n)
num <= 'd0;
else if(num == 'd20)
num <= 'd0;
else if((n_state == WRITE_DATA0) || (n_state == WRITE_DATA1))
num <= num + 'b1;
/***********************************************************/
always @(posedge sys_clk or negedge rst_n)
if(!rst_n)
dis_data <= 'h00;
else case(num)
'd0: dis_data <= "I";
'd1: dis_data <= "2";
'd2: dis_data <= "C";
'd3: dis_data <= " ";//8'hcd;
'd4: dis_data <= "E";//8'ha8;
'd5: dis_data <= "P";//8'hd0;
'd6: dis_data <= "P";//8'hc5;
'd7: dis_data <= "R";//8'hca;
'd8: dis_data <= "O";//8'hb5;
'd9: dis_data <= "M";//8'hd1;
'd10: dis_data <= " ";//8'he9;
'd11: dis_data <= "0";
'd12: dis_data <= "3";
'd13: dis_data <= 8'hb5;
'd14: dis_data <= 8'hd8;
'd15: dis_data <= 8'hd6;
'd16: dis_data <= 8'hb7;
'd17: dis_data <= 8'hd6;
'd18: dis_data <= 8'hb5;
'd19: dis_data <= " ";
default:dis_data <= 'h00;
endcase
/***************************************************/
assign lcd_rw = 'b0;//只有写模式
assign lcd_psb = 'b1;//并口模式
wire en;
assign en = 'b1;//(n_state == STOP) ? 1'b0 : 'b1;
assign lcd_en = en ? lcd_clk : 'b0;
/***************************************************/
endmodule

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

lcd_top.v

 `timescale 1ns/10ps
module lcd_top;
/*************************************************************/
reg sys_clk;
reg rst_n;
wire start_cnt;
/*************************************************************/
initial begin
sys_clk = 'b0;
rst_n = 'b0;
#;
rst_n = 'b1;
end
/*************************************************************/
always # sys_clk = ~sys_clk;
/*************************************************************/
wire lcd_rs;//H:data L:command
wire lcd_rw;//H:read module L:write module
wire lcd_en;//H active
wire [:] lcd_data;
wire lcd_psb;//H:parallel module L:SPI module
LCD12864 LCD12864_u1(
//input
.sys_clk(sys_clk),
.rst_n(rst_n),
.dis_data_low(""),
.dis_data_hig("c"), //output
.lcd_rs(lcd_rs),
.lcd_rw(lcd_rw),
.lcd_en(lcd_en),
.lcd_data(lcd_data),
.lcd_psb(lcd_psb)
);
endmodule

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

简单三段式状态机实验2-LCD12864的更多相关文章

  1. 简单三段式状态机实验1-SOS

    一直想从一段式状态机切换到三段式状态机,从书上和网上不断搜寻三段式案例及方法,感觉很简单,就想拿之前做过的实验把一段式改成三段式,可是写起来并非那么简单,很棘手,改完后也没有成功,尤其状态机里面的计数 ...

  2. 简单三段式状态机实验3-Sequence Detect(序列检测)

    1.序列检测器的逻辑功能描述:序列检测指的就是将一个指定的序列从数字码流中识别出来.本例中,我们将设计一个"10010”序列的检测器.设x_in为数字码流输入,z_out为检出标记输出,高电 ...

  3. Verilog笔记.三段式状态机

    之前都是用的一段式状态机,逻辑与输出混在一起,复杂点的就比较吃力了. 所以就开始着手三段式状态机. 组合逻辑与时序逻辑分开,这样就能简单许多了. 但是两者在思考方式上也有着很大的区别. 三段式,分作: ...

  4. FPGA三段式状态机的思维陷阱

    用三段式描述状态机的好处,国内外各位大牛都已经说的很多了,大致可归为以下三点: 1.将组合逻辑和时序逻辑分开,利于综合器分析优化和程序维护; 2.更符合设计的思维习惯; 3.代码少,比一段式状态机更简 ...

  5. 10010序列检测器的三段式状态机实现(verilog)

    序列检测器是时序数字电路设计中经典的教学范例,夏宇闻的<verilog数字系统设计教程>一书中有这个例子,用verilog设计一个“10010”序列的检测器.看完后我觉得F和G两个状态多余 ...

  6. 基于FPGA的三段式状态机

    状态机分类: 通常, 状态机的状态数量有限, 称为有限状态机(FSM) .由于状态机所有触发器的时钟由同一脉冲边沿触发, 故也称之为同步状态机. 根据状态机的输出信号是否与电路的输入有关分为 Meal ...

  7. (原创)Verilog三段式状态机

    下面以上图一个简单的FSM说明三段式Verilog状态机范式: `timescale 1ns / 1ps module FSM( clk,rst_n, in1,in2, out1,out2, CS,N ...

  8. Verilog三段式状态机描述

    时序电路的状态是一个状态变量集合,这些状态变量在任意时刻的值都包含了为确定电路的未来行为而必需考虑的所有历史信息. 状态机采用VerilogHDL语言编码,建议分为三个always段完成. 三段式建模 ...

  9. verilog 三段式状态机的技巧

    三段式代码多,但是有时钟同步,延时少,组合逻辑跟时序逻辑分开并行出错少. (1)同步状态转移 (2)当前状态判断接下来的状态 (3)动作输出 如果程序复杂可以不止三个always   .always ...

随机推荐

  1. [转]关于Socket粘包问题

    这两天看csdn有一些关于socket粘包,socket缓冲区设置的问题,发现自己不是很清楚,所以查资料了解记录一下: 一两个简单概念长连接与短连接:1.长连接 Client方与Server方先建立通 ...

  2. nginx php mysql 集成安装包

    经过多次的苦苦寻找,终于找到一款 集成了nginx 和mysql php的包.只需一键安装即可. 且可以修改其中的nginx mysql 与php的版本. 解压此文件lnmp???ɰ?n-1.6p-5 ...

  3. 阿里云Linux挂载数据盘

    Linux 系统挂载数据盘   适用系统:Linux(Redhat , CentOS,Debian,Ubuntu) *  Linux的云服务器数据盘未做分区和格式化,可以根据以下步骤进行分区以及格式化 ...

  4. Androidndk开发打包时我们应该如何注意平台的兼容(x86,arm,arm-v7a)

    很多朋友在开发Android JNI的的时候,会遇到findlibrary returned null的错误,因为某种原因,so没有打包到apk中.下面浅析下引起该错误的原因以及平台兼容性问题. 一. ...

  5. JNI调用问题(部分机型崩溃)

    1.今日测试发现在部分手机上游戏会崩溃,通过logcat日志发现是jni调用问题(我猜测) 错误日志中有如下语句: trying to work around app JNI bugs, but di ...

  6. DISUBSTR - Distinct Substrings

    DISUBSTR - Distinct Substrings no tags  Given a string, we need to find the total number of its dist ...

  7. Android中自定义veiw使用Java中的回调方法

    //------------------MainActivity----中---------------------------------- import android.os.Bundle;imp ...

  8. FZU 2140 Forever 0.5(将圆离散化)

    主要就是将圆离散化,剩下的都好办 #include<iostream> #include<cstdio> #include<cstring> #include< ...

  9. Oracle Sql优化之范围处理

    1.表中字段自关联与分析函数的性能比较,自关联需要扫描表两次,分析函数扫描一次即可 ----自关联 select v1.proj_id,v1.proj_start,v1.proj_end from v ...

  10. Image File Execution Options(转)

    今天公司的一台计算机无法正常工作送来维修,经简单判断是感染了很多病毒,即使在安全模式下也无法清除:于是将硬盘摘下挂到另外一台机器上,用卡巴斯基对病毒进行了查杀,再次启动计算机后发现很多系统维护程序以及 ...