此实验是在“基于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. [Lua]Lua高级教程Metatables

    什么是Metatable metatable是Lua中的重要概念,每一个table都可以加上metatable,以改变相应的table的行为. Metatables举例 -- 声明一个正常的关系变量 ...

  2. angular跨域访问的问题

    CORS跨域资源共享 跨域资源共享(CORS )是一种网络浏览器的技术规范,它为Web服务器定义了一种方式,允许网页从不同的域访问其资源. Form responseHeaders = (Form) ...

  3. Hadoop2.6.0 动态增加节点

    本文主要从基础准备,添加DataNode和添加NodeManager三个部分详细说明在Hadoop2.6.0环境下,如何动态新增节点到集群中. 基础准备 在基础准备部分,主要是设置hadoop运行的系 ...

  4. 批量删除实现js+springmvc

    前台的控件 <input type='checkbox' name='isSelect' value='"+data[i].id+"' ></input>& ...

  5. linux下如何开放80端口

    linux清屏命令:clear linux版本:CentOS6.5 1.开启80端口命令:/sbin/iptables -I INPUT -p tcp --dport 80 -j ACCEPT2.保存 ...

  6. perl-cgi基础

    来源: http://www.cnblogs.com/itech/archive/2012/09/22/2698553.html 代码: http://xxxxx/cgi/perl-cgi.cgi?n ...

  7. zookeeper集群实例

    zookeeper是什么 Zookeeper,一种分布式应用的协作服务,是Google的Chubby一个开源的实现,是Hadoop的分布式协调服务,它包含一个简单的原语集,应用于分布式应用的协作服务, ...

  8. 倒计时demo

    #import <UIKit/UIKit.h> @interface ViewController : UIViewController @property (strong,nonatom ...

  9. springmvc中的几个问题

    一   url-pattern的问题: <!-- No mapping found for HTTP request with URI [/WEB-INF/jsp/homePage.jsp] i ...

  10. Spring Boot 系列教程4-JDBC

    JDBC Java Data Base Connectivity,是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成.不管是Hibe ...