FPGA LCD1602驱动
0赞
发表于 2016/11/22 11:28:04
阅读(1529)
LCD1602是很常用的液晶显示器。用单片机驱动比较容易,相信大家都会的。
初学FPGA对状态机的构建和编程可能不太熟悉,1602的驱动可以作为一个很好的训练。
状态机划分:
分成若干状态,包括:上电等待(PowerOnWait)、模式设置(SetMode)、关闭显示(ShowOff)、清屏(Clear)、设置光标(Curcor)、开显示(ShowOn)、等待(Ready)、写第一行地址(WrAddr1)、写第一行数据(WrDat1)、写第二行地址(WrAddr2)、写第二行数据(WrDat2)。
Quartus自动生成状态机图如下所示:
主要verilog代码如下:
//============================================================== // LCM1602 CTL // Author : Deeply // DATE:2016-11-8 // Version :1.0 //============================================================== module lcm1602_ctl ( input i_clk_50m, input i_rst_n, input [7:0] i_data, input i_wr_addr1_trig, input i_wr_addr2_trig, input i_wr_data_trig, output reg [7:0] o_data, output reg o_en, output reg o_rs, output o_rw ); parameter PowerOnWait = 11'b00000000001; parameter SetMode = 11'b00000000010; parameter ShowOff = 11'b00000000100; parameter Clear = 11'b00000001000; parameter Curcor = 11'b00000010000; parameter ShowOn = 11'b00000100000; parameter Ready = 11'b00001000000; parameter WrAddr1 = 11'b00010000000; parameter WrDat1 = 11'b00100000000; parameter WrAddr2 = 11'b01000000000; parameter WrDat2 = 11'b10000000000; reg[10:0] crt_state; reg[10:0] next_state; //============================================================== //FSM Part1: State switching //============================================================== always @ ( posedge i_clk_50m or negedge i_rst_n) if(!i_rst_n) crt_state <= PowerOnWait; else crt_state <= next_state; //============================================================== //Part2: Next State Generation //============================================================== //5ms * 50MHz = 0.25M = 250000 d //parameter cnt_5ms_val = 18'd2500; // 18'd24_9999 reg[17:0] time_cnt; wire cnt_5ms; reg[2:0] cnt_15ms; // timer -- 5ms always @(posedge i_clk_50m or negedge i_rst_n) if(!i_rst_n) time_cnt <= 18'd0; else if(time_cnt <18'd24_9999 && crt_state == next_state)//18'd24_9999 time_cnt <= time_cnt + 18'd1; else time_cnt <= 18'd0; assign cnt_5ms = (time_cnt == 18'd24_9999);//18'd24_9999 // timer for poweron wait always @(posedge i_clk_50m or negedge i_rst_n) if(!i_rst_n) cnt_15ms <= 2'b00; else if(crt_state == PowerOnWait) if(cnt_5ms && cnt_15ms < 2'd3) cnt_15ms <= cnt_15ms + 1'b1; else cnt_15ms <= cnt_15ms; else cnt_15ms <= 2'b00; reg wr_addr1_trig_r; reg wr_addr2_trig_r; reg wr_data_trig_r; reg [11:0] cnt_4000; always @(posedge i_clk_50m or negedge i_rst_n) if(!i_rst_n) cnt_4000 <= 12'b0; else if(crt_state != next_state) cnt_4000 <= 12'b0; else if(!wr_data_trig_r & i_wr_data_trig) cnt_4000 <= 12'd0; else if(!wr_addr1_trig_r & i_wr_addr1_trig) cnt_4000 <= 12'd0; else if(!wr_addr2_trig_r & i_wr_addr1_trig) cnt_4000 <= 12'd0; else if(cnt_4000 < 12'd3999 ) cnt_4000 <= cnt_4000 + 12'd1; else cnt_4000 <= cnt_4000; always @ ( posedge i_clk_50m or negedge i_rst_n ) if(!i_rst_n) wr_addr1_trig_r <= 1'b0; else wr_addr1_trig_r <= i_wr_addr1_trig; always @ ( posedge i_clk_50m or negedge i_rst_n ) if(!i_rst_n) wr_addr2_trig_r <= 1'b0; else wr_addr2_trig_r <= i_wr_addr2_trig; always @ ( posedge i_clk_50m or negedge i_rst_n ) if(!i_rst_n) wr_data_trig_r <= 1'b0; else wr_data_trig_r <= i_wr_addr1_trig; // show data counter reg [3:0]data_cnt; always @( posedge i_clk_50m or negedge i_rst_n) if(!i_rst_n) data_cnt<= 1'b0; else if(!wr_data_trig_r && i_wr_data_trig) if( (crt_state == WrDat1 || crt_state == WrDat2) && data_cnt < 4'd15) data_cnt <= data_cnt + 4'd1; else data_cnt <= 4'd0; else data_cnt <= data_cnt; always @(*) case (crt_state) PowerOnWait: if(cnt_15ms == 2'd3) next_state = SetMode; else next_state = crt_state; SetMode: if(cnt_5ms) next_state = ShowOff; else next_state = crt_state; ShowOff: if(cnt_5ms) next_state = Clear; else next_state = crt_state; Clear: if(cnt_5ms) next_state = Curcor; else next_state = crt_state; Curcor: if(cnt_5ms) next_state = ShowOn; else next_state = crt_state; ShowOn: if(cnt_5ms) next_state = Ready; else next_state = crt_state; Ready: if(!wr_addr1_trig_r && i_wr_addr1_trig) next_state = WrAddr1; else if(!wr_addr2_trig_r && i_wr_addr2_trig) next_state = WrAddr2; else next_state = crt_state; WrAddr1: if(!wr_data_trig_r && i_wr_data_trig && cnt_4000 ==12'd3999) next_state = WrDat1; else next_state = crt_state; WrDat1: if(data_cnt == 4'd15 && cnt_4000 == 12'd3999) next_state = Ready; else next_state = crt_state; WrAddr2: if(!wr_data_trig_r && i_wr_data_trig && cnt_4000 == 12'd3999) next_state = WrDat2; else next_state = crt_state; WrDat2: if(data_cnt == 4'd15 && cnt_4000 == 12'd3999) next_state = Ready; else next_state = crt_state; default: next_state = PowerOnWait; endcase //====================================================================== // Part3: Output //====================================================================== //rs: 1 for data, 0 for command always @ (posedge i_clk_50m or negedge i_rst_n) if(!i_rst_n) o_rs <= 1'b0; else if((crt_state == WrDat1 || crt_state == WrDat2) && cnt_4000 < 12'd3900) o_rs <= 1'b1; else o_rs <= 1'b0; //en: always @ (posedge i_clk_50m or negedge i_rst_n) if(!i_rst_n) o_en <= 1'b0; else if(crt_state == Ready) o_en <= 1'b0; else if(cnt_4000 > 12'd100 && cnt_4000 < 12'd3900) o_en <= 1'b1; else o_en <= 1'b0; //rw: 1 for read, 0 for write assign o_rw = 1'b0; //o_data: always @ (*) case (crt_state) PowerOnWait : o_data = 8'h38; SetMode : o_data = 8'h38; ShowOff : o_data = 8'h08; Clear : o_data = 8'h01; Curcor : o_data = 8'h06; ShowOn : o_data = 8'h0C; WrAddr1 : o_data = 8'h80; WrAddr2 : o_data = 8'h80 | 8'h40; WrDat1 : o_data = i_data; WrDat2 : o_data = i_data; default : o_data = 8'hff; endcase endmodule