首先,clk_div 从名字上就能看出,是分频后的时钟,
- //分频时钟
- defparam Gen_ClkDiv.divdFACTOR=24,Gen_ClkDiv.divdWIDTH=7;//分频出1M的时钟
- gen_divd Gen_ClkDiv(.reset(!resetin),.clkin(clkin),.clkout(clk_div));//端口名称关联
复制代码
这里 做了一个修改,把 240 改为了 24,这样 分配后 的时钟 是 1us。青创配套的程序,在 时序 上的注释 都是 有问题的,比如 等待>40us时间,实际操作上, >1000 us 时间。
程序的 核心 是 状态机 和 某一状态的时序操作。
parameter set_Func=8'b0000_0001, //功能设置
set_DispSwitch=8'b0000_0010, //--设置显示开关
set_EntryMd=8'b0000_0100, //--设置输入模式
clr_Disp=8'b1000_1000, //--刚开始时就需要清屏幕
set_DDAd1=8'b0001_0000, //--设置成DDRAM第一行的地址首地址为0x81
set_DDAd2=8'b0010_0000, //--设置DDRAM第二行的地址首地址为0xc2
Display1=8'b0100_0000, //--向第一行DDRAM写数据
Display2=8'b1000_0000, //--向第二行DDRAM写数据
Over=8'b0000_0000;
这些状态的编码是自己定义的,和指令的编码没有关系。
另外这些状态是 有 先后关系的,如下
看第一个状态 clr_Disp
- case (lcd_state)
- clr_Disp://--刚开始时就需要清屏幕
- begin
- delay_cnt<=delay_cnt+1;
- if (delay_cnt<=2)
- begin
- lcd_rs<=1'b0;
- lcd_rw<=1'b0;
- lcd_e<=1'b1;
- end
- else if (delay_cnt<=4)
- lcd_data<=8'h01;
- else if (delay_cnt<=6)
- lcd_e<=1'b0;//下降沿产生
- else if (delay_cnt>=50) //等待>40us时间,使指令写入完毕
- begin
- delay_cnt<=0;
- lcd_state<=set_Func;
- end
- end
复制代码
delay_cnt 是 每过 1us 加 1,用来产生延时。
在 2us 内, rs rw e 赋值;
超过2us时,写 命令,即给 lcd_data 赋值;
超过4 us时, e 产生下降沿,读入指令;
等待 >40 us 后,设置下一状态。
同样的方法,可以分析第二个状态,
- set_Func://功能设置
- begin
- delay_cnt<=delay_cnt+1;
- if (delay_cnt<=2)
- begin
- lcd_rs<=1'b0;//指令
- lcd_rw<=1'b0;//写
- lcd_e<=1'b1;
- end
- else if (delay_cnt<=4)
- lcd_data<=8'h38;//DL=1,8位,N=1,2行,F=0,5*7字体
- else if (delay_cnt<=6)
- lcd_e<=1'b0;//下降沿产生
- else if (delay_cnt>=50)//等待>40us时间,使指令写入完毕
- begin
- delay_cnt<=0;
- lcd_state<=set_EntryMd;
- end
- end
复制代码
此时,lcd_data <= 8'h38; 就是 C51 中常见的 写入指令 0x38。
时序分析省略。。。。
这样, 怎样理解 1602 在 FPGA 中实现,就有了一个大概的理解。