此实验是在“基于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. JavaScript 错误 - Throw、Try 和 Catch

    http://www.w3school.com.cn/js/js_errors.asp try 语句测试代码块的错误.catch 语句处理错误.throw 语句创建自定义错误. 错误一定会发生 当 J ...

  2. jquery datatable 参数api

    jQuery 的插件 dataTables 是一个优秀的表格插件,提供了针对表格的排序.浏览器分页.服务器分页.筛选.格式化等功能.dataTables 的网站上也提供了大量的演示和详细的文档进行说明 ...

  3. android:onTouch()和onTouchEvent()的区别?看完这篇文章就知道了

    Android Touch Screen 与传统Click Touch Screen不同,会有一些手势(Gesture),例如Fling,Scroll等等.这些Gesture会使用户体验大大提升. A ...

  4. MVC动态生成表单

    1*书写方式 一.using语句可以不写结束标记,自动加上 服务端 客户端 默认提交当前控制器和操作方法 二.开始与结束代码都写 服务端 客户端 三.一些常用的重载方法 (1)要提交的控制器,和操作方 ...

  5. THE ROAD TO PROGRAM

    <The C Programming Language> <The Practice of Programming><The Art of Computer Progra ...

  6. Oracle 锁

    select  for update对某行加锁之后: select语句可以执行: select  for update 这行不可以: 会一直等待锁释放 select for update wait 3 ...

  7. MySql-时间格式转换之转换为时分秒格式的日期

    select date_format(create_datetime,'%Y-%m-%d %k:%i:%s') from busi_repairitem_category MySQL毫秒值和日期的指定 ...

  8. 网址组成与特殊ip小解

    网址 https://www.baidu.com:8010/a/html/a.html?tn=monline_3_dg#part1 注解: 网址= 当前url协议+域名+端口号+路径名+参数+hash ...

  9. ALAssetsLibrary 照片相关 浅析

    ALAssetsLibrary 提供了访问iOS设备下”照片”应用下所有照片和视频的接口: 从 ALAssetsLibrary 中可读取所有的相册数据,即 ALAssetsGroup 对象列表: 从每 ...

  10. margin叠加相邻两个元素的上下margin是叠加在一起

    <!doctype html><html><head><meta charset="utf-8"><title>无标题文 ...