FPGA--1602
程序员文章站
2022-04-30 11:33:04
...
介绍顺序:时序、设计思路、代码实现;
- 时序:
市面上大部分1602芯片为HD44780,1602时序也就是HD44780的时序,直接看图:
这是写入的时序图,读取的时序图用不上,不分析,与写入类似。
可以看到共有四种线,RS指令数据选择,0代表指令,1代表数据,R/W读取控制,0代表写入,1代表读取,E代表使能,下降沿写入,高电平读取,DB[7:0]为并行8位数据线。
Tas:RS,RW变化开始,到E变化开始的时间,最少140ns;
Pweh:E变化开始,到E复位,最少450ns;
Tdsw:数据变化开始,到E下降沿,最少195ns,实际操作中,数据和E可以同时变化,这样可以省略tdsw参数;
Th/tah:E复位开始,到RS,RW复位(数据可以不复位),最少10ns;
Tcycle:一次操作时间,最少1000ns;
- 设计思路:
按照上述时序图,可以实现指令或数据的写入,具体写入指令或数据的顺序如下:
HD44780共有11种指令,建议看看芯片手册;
下面只说平常用到的指令,按照执行顺序说明:
- 功能设置:0x38,延时40us
- 显示开关控制:0x0c,延时40us;
- 输入方式设置:0x06,延时40us;
- 清屏:0x01,延时1.64ms;
- DDRAM地址设置:0x80或0xc0;
- 写数据:待显示数据;
有几点需要注意:
- 功能设置指令最少执行两次,否则可能导致第二行不能显示;
- 延时时间一定要保证,特别是清屏指令,否则不显示;
- 代码实现:
写入以及显示各用一个状态机实现:
- 初始化:
module lcd1602(clk,rst,rs,rw,e,db);
input clk;//系统时钟
input rst;//系统复位
output reg rs;//液晶--rs
output reg rw;//液晶--rw
output reg e;//液晶--e
output reg [7:0] db;//液晶--db
reg write_en;//写入--启动,1启动,0停止
reg write_rs;//写入--指令or数据,1数据,0指令
reg [3:0] write_state;//写入--状态机
reg [5:0] write_cnt;//写入--延时控制
reg [5:0] lcd_state;//lcd状态机
reg [7:0] lcd_db_r;//lcd待显示数据
reg [16:0] lcd_delay;//lcd操作间延时
reg [3:0] lcd_init_cnt;//lcd初始化次数
parameter tas = 10;//140ns-->200ns
parameter pweh = 25;//450ns-->500ns
parameter th = 1;//10ns-->20ns
parameter tcycle = 50;//1000ns-->1000ns
parameter delay_1640 = 100000;//1.64ms-->2ms
parameter delay_40 = 5000;//40us-->100us
- 写入:
always @ (posedge clk or negedge rst) begin//指令、数据写入
if(!rst) begin
write_state <= 4'd0;
write_cnt <= 8'd0;
rs <= 1'b0;
rw <= 1'b0;
e <= 1'b0;
db <= 8'd0;
end
else begin
case(write_state)
4'd0 : if(write_en == 1'b1) write_state <= 4'd1;//等待
4'd1 : begin//rs,rw置位,延时tas
rs <= write_rs;
rw <= 1'b0;
if(write_cnt == (tas - 1)) begin
write_state <= 4'd2;
write_cnt <= 8'd0;
end
else write_cnt <= write_cnt + 1;
end
4'd2 : begin//e,data置位,延时pweh
e <= 1'b1;
db <= lcd_db_r;
if(write_cnt == (pweh - 1)) begin
write_state <= 4'd3;
write_cnt <= 8'd0;
end
else write_cnt <= write_cnt + 1'b1;
end
4'd3 : begin//e复位,延时th
e <= 1'b0;
if(write_cnt == (th - 1)) begin
write_state <= 4'd4;
write_cnt <= 8'd0;
end
else write_cnt <= write_cnt + 1'b1;
end
4'd4 : begin//rs,rw复位,延时tcycle,写入完毕
rs <= 1'b0;
rw <= 1'b0;
if(write_cnt == (tcycle - 1)) begin
write_state <= 4'd0;
write_cnt <= 8'd0;
end
else write_cnt <= write_cnt + 1'b1;
end
default : ;
endcase
end
end
- 显示:
always @ (posedge clk or negedge rst) begin//显示
if(!rst) begin
lcd_state <= 6'd0;
lcd_db_r <= 8'd0;
lcd_delay <= 17'd0;
lcd_init_cnt <= 4'd0;
write_en <= 1'b0;
write_rs <= 1'b0;
end
else begin
case(lcd_state)
6'd0 : begin//功能设置(6)
lcd_db_r <= 8'h38;//8为数据接口,两行显示,5*7点阵字符
write_rs <= 1'b0;//指令
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd1 : begin//功能设置--延时(40us)
if(lcd_delay == delay_40) begin
if(lcd_init_cnt == 4'd1) begin
lcd_state <= lcd_state + 1;
lcd_init_cnt <= 4'd0;
end
else begin
lcd_state <= 6'd0;
lcd_init_cnt <= lcd_init_cnt + 1;
end
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd2 : begin//显示开关控制(4)
lcd_db_r <= 8'h0c;//开显示,关光标,关闪烁
write_rs <= 1'b0;//指令
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd3 : begin//显示开关控制--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd4 : begin//输入方式设置(3)
lcd_db_r <= 8'h06;//AC++,画面不动
write_rs <= 1'b0;//指令
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd5 : begin//输入方式设置--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd6 : begin//清屏(1)
lcd_db_r <= 8'h01;//固定,注意延时1.64ms
write_rs <= 1'b0;//指令
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd7 : begin//清屏--延时(1.64ms)
if(lcd_delay == delay_1640) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd8 : begin//DDRAM地址设置(8)
lcd_db_r <= 8'h80;//地址:第一行第一个
write_rs <= 1'b0;//指令
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd9 : begin//DDRAM地址设置--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd10 : begin//写数据(10)
lcd_db_r <= "2";//待显示数据
write_rs <= 1'b1;//数据
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd11 : begin//写数据--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd12 : begin//写数据(10)
lcd_db_r <= "0";//待显示数据
write_rs <= 1'b1;//数据
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd13 : begin//写数据--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd14 : begin//写数据(10)
lcd_db_r <= "1";//待显示数据
write_rs <= 1'b1;//数据
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd15 : begin//写数据--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd16 : begin//写数据(10)
lcd_db_r <= "8";//待显示数据
write_rs <= 1'b1;//数据
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd17 : begin//写数据--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd18 : begin//写数据(10)
lcd_db_r <= "/";//待显示数据
write_rs <= 1'b1;//数据
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd19 : begin//写数据--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd20 : begin//写数据(10)
lcd_db_r <= "0";//待显示数据
write_rs <= 1'b1;//数据
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd21 : begin//写数据--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd22 : begin//写数据(10)
lcd_db_r <= "8";//待显示数据
write_rs <= 1'b1;//数据
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd23 : begin//写数据--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd24 : begin//写数据(10)
lcd_db_r <= "/";//待显示数据
write_rs <= 1'b1;//数据
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd25 : begin//写数据--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd26 : begin//写数据(10)
lcd_db_r <= "3";//待显示数据
write_rs <= 1'b1;//数据
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd27 : begin//写数据--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd28 : begin//写数据(10)
lcd_db_r <= "0";//待显示数据
write_rs <= 1'b1;//数据
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd29 : begin//写数据--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd30 : begin//DDRAM地址设置(8)
lcd_db_r <= 8'hc0;//地址:第二行第一个
write_rs <= 1'b0;//指令
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd31 : begin//DDRAM地址设置--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd32 : begin//写数据(10)
lcd_db_r <= "1";//待显示数据
write_rs <= 1'b1;//数据
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd33 : begin//写数据--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd34 : begin//写数据(10)
lcd_db_r <= "5";//待显示数据
write_rs <= 1'b1;//数据
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd35 : begin//写数据--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd36 : begin//写数据(10)
lcd_db_r <= ":";//待显示数据
write_rs <= 1'b1;//数据
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd37 : begin//写数据--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd38 : begin//写数据(10)
lcd_db_r <= "1";//待显示数据
write_rs <= 1'b1;//数据
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd39 : begin//写数据--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
6'd40 : begin//写数据(10)
lcd_db_r <= "0";//待显示数据
write_rs <= 1'b1;//数据
write_en <= 1'b1;
if(write_cnt == (tcycle - 1)) begin
lcd_state <= lcd_state + 1;
write_en <= 1'b0;
end
end
6'd41 : begin//写数据--延时(40us)
if(lcd_delay == delay_40) begin
lcd_state <= lcd_state + 1;
lcd_delay <= 17'd0;
end
else lcd_delay <= lcd_delay + 1;
end
endcase
end
end
上一篇: BlueCMS 新手代码审计
推荐阅读