这两天都在学习怎样用verilog语言写LCD1602液晶的程序,其实,我对1602液晶还是蛮熟悉的,毕竟,在学单片机的时候,也用C51写过它的驱动程序,还是蛮简单的,但是由于FPGA是并行的,所以,写的驱动程序和单片机的不太一样,今天有幸在网上看到了Crazy Bingo用状态机写的1602液晶的驱动程序,突然觉得verilog语言中的状态机这么强大,竟然能像这样用状态机来写LCD1602液晶的驱动程序;自己写的代码太烂,没敢贴出来,所以,参考了他写的代码,然后修改了一下!

//程序实现的功能:在LCD1602液晶上显示字符;
module lcd_1602
        (
          sysclk,
          rst_n,
          lcd_en,
          lcd_rs,
          lcd_rw,
          lcd_data
           
        );

input   sysclk;         //系统时钟 50MHZ
input   rst_n;          //复位信号,低电平有效;

output       lcd_en;    //读写使能信号,高电平有效;
output       lcd_rs;    //数据命令选择端(H/L);
output       lcd_rw;    //读写选择端(H/L);
output[7:0]  lcd_data;  //8位数据口;

wire        sysclk;
wire        rst_n;
wire        lcd_en;
reg         lcd_rs;
wire        lcd_rw;
reg[7:0]    lcd_data;
  
parameter  [127:0]row1="hello     world!";      //因为lcd1602每一行可显示16个字符,一个字符占8位;
parameter  [127:0]row2="I   like   FPGA!";      //所以每一行一共有16*8=128位;

reg [15:0]   time_cnt;
always @(posedge sysclk or negedge rst_n) 
 begin
       if(!rst_n)
          time_cnt<=16'h0;
       else 
          time_cnt<=time_cnt+16'b1;  
 end  

assign  lcd_rw=1'b0;
assign  lcd_en=time_cnt[15]; 

wire  state_flag ;     //状态标志位 ,因为FPGA的运算速度比LCD1602要快的多,  
                      //所以必须要等到LCD1602稳定后才往里面写数据;
assign  state_flag=(time_cnt==16'h7fff)?1'b1:1'b0 ;    //lcd_en最小值500ns ,所以lcd_en的频率应维持在2MHZ以内; 

parameter  IDLE=8'h00;
//lcd1602 initial;
parameter  INI_SET=8'h01;      //显示工作模式设置;
    
parameter  INI_CLR=8'h02;      //清屏显示;

parameter  CURSOR_SET1=8'h03;  // 光标设置1;

parameter  CURSOR_SET2=8'h04;  //光标设置2;     
//display line 1;
parameter  LINE1_ADDER=8'h05;

parameter  LINE1_0=8'h06;

parameter  LINE1_1=8'h07;

parameter  LINE1_2=8'h08;

parameter  LINE1_3=8'h09;

parameter  LINE1_4=8'h0A;

parameter  LINE1_5=8'h0B;

parameter  LINE1_6=8'h0C;

parameter  LINE1_7=8'h0D;

parameter  LINE1_8=8'h0E;

parameter  LINE1_9=8'h0F;

parameter  LINE1_A=8'h10;

parameter  LINE1_B=8'h11;

parameter  LINE1_C=8'h12;

parameter  LINE1_D=8'h13;

parameter  LINE1_E=8'h14;

parameter  LINE1_F=8'h15;
// display line 2;
parameter  LINE2_ADDER=8'h16;

parameter  LINE2_0=8'h17;

parameter  LINE2_1=8'h18;

parameter  LINE2_2=8'h19;

parameter  LINE2_3=8'h1A;

parameter  LINE2_4=8'h1B;

parameter  LINE2_5=8'h1C;

parameter  LINE2_6=8'h1D;

parameter  LINE2_7=8'h1E;

parameter  LINE2_8=8'h1F;

parameter  LINE2_9=8'h20;

parameter  LINE2_A=8'h21;

parameter  LINE2_B=8'h22;

parameter  LINE2_C=8'h23;

parameter  LINE2_D=8'h24;

parameter  LINE2_E=8'h25;

parameter  LINE2_F=8'h26;
//------------------------------------------------//
reg[7:0] state;
reg[7:0] next_state;
always @ (posedge sysclk or negedge rst_n)
  begin
        if(!rst_n)
          state<=IDLE;
        else if(state_flag)
          state<=next_state;  
  end   
     
always @ (*)
  begin
       case(state)
         //display line1;
         IDLE        :   next_state=INI_SET;
         
         INI_SET     :   next_state=INI_CLR;
         
         INI_CLR     :   next_state=CURSOR_SET1;
         
         CURSOR_SET1 :   next_state=CURSOR_SET2;
         
         CURSOR_SET2 :   next_state=LINE1_ADDER;
         
         LINE1_ADDER :   next_state=LINE1_0;
         
         LINE1_0     :   next_state=LINE1_1;
         
         LINE1_1     :   next_state=LINE1_2;
         
         LINE1_2     :   next_state=LINE1_3;
         
         LINE1_3     :   next_state=LINE1_4;
         
         LINE1_4     :   next_state=LINE1_5; 
         
         LINE1_5     :   next_state=LINE1_6;
         
         LINE1_6     :   next_state=LINE1_7;
         
         LINE1_7     :   next_state=LINE1_8;
         
         LINE1_8     :   next_state=LINE1_9;
         
         LINE1_9     :   next_state=LINE1_A;
         
         LINE1_A     :   next_state=LINE1_B; 
         
         LINE1_B     :   next_state=LINE1_C;
         
         LINE1_C     :   next_state=LINE1_D;
         
         LINE1_D     :   next_state=LINE1_E;
         
         LINE1_E     :   next_state=LINE1_F; 
         
         LINE1_F     :   next_state=LINE2_ADDER; 
         //display line2;
         LINE2_ADDER :   next_state=LINE2_0;
         
         LINE2_0     :   next_state=LINE2_1;
         
         LINE2_1     :   next_state=LINE2_2;
         
         LINE2_2     :   next_state=LINE2_3;
         
         LINE2_3     :   next_state=LINE2_4;
         
         LINE2_4     :   next_state=LINE2_5; 
         
         LINE2_5     :   next_state=LINE2_6;
         
         LINE2_6     :   next_state=LINE2_7;
         
         LINE2_7     :   next_state=LINE2_8;
         
         LINE2_8     :   next_state=LINE2_9;
         
         LINE2_9     :   next_state=LINE2_A;
         
         LINE2_A     :   next_state=LINE2_B;
          
         LINE2_B     :   next_state=LINE2_C;
         
         LINE2_C     :   next_state=LINE2_D;
         
         LINE2_D     :   next_state=LINE2_E;
         
         LINE2_E     :   next_state=LINE2_F; 
         
         LINE2_F     :   next_state=LINE1_ADDER;
         
         default     :   next_state=IDLE; 
       endcase 
  end  

always @ (posedge sysclk or negedge rst_n)   
  begin
        if(!rst_n)
          begin 
           lcd_rs<=1'h0;
           lcd_data<=8'hxx;
          end 
        else if(state_flag)
          begin
            case (next_state)
               IDLE        :   lcd_rs<=1'h0;
               
               INI_SET     :   lcd_rs<=1'h0;
                              
               INI_CLR     :   lcd_rs<=1'h0;
               
               CURSOR_SET1 :   lcd_rs<=1'h0;
               
               CURSOR_SET2 :   lcd_rs<=1'h0;
        //     line1       
               LINE1_ADDER :   lcd_rs<=1'h0; 
               
               LINE1_0     :   lcd_rs<=1'h1;
               
               LINE1_1     :   lcd_rs<=1'h1;
               
               LINE1_2     :   lcd_rs<=1'h1; 
               
               LINE1_3     :   lcd_rs<=1'h1;
               
               LINE1_4     :   lcd_rs<=1'h1;
               
               LINE1_5     :   lcd_rs<=1'h1;
               
               LINE1_6     :   lcd_rs<=1'h1; 
               
               LINE1_7     :   lcd_rs<=1'h1;
               
               LINE1_8     :   lcd_rs<=1'h1;
               
               LINE1_9     :   lcd_rs<=1'h1;
               
               LINE1_A     :   lcd_rs<=1'h1; 
               
               LINE1_B     :   lcd_rs<=1'h1;
               
               LINE1_C     :   lcd_rs<=1'h1;
               
               LINE1_D     :   lcd_rs<=1'h1;
               
               LINE1_E     :   lcd_rs<=1'h1; 
               
               LINE1_F     :   lcd_rs<=1'h1;
        //     line2 
               LINE2_ADDER :   lcd_rs<=1'h0; 
               
               LINE2_0     :   lcd_rs<=1'h1;
               
               LINE2_1     :   lcd_rs<=1'h1;
               
               LINE2_2     :   lcd_rs<=1'h1; 
               
               LINE2_3     :   lcd_rs<=1'h1;
               
               LINE2_4     :   lcd_rs<=1'h1;
               
               LINE2_5     :   lcd_rs<=1'h1;
               
               LINE2_6     :   lcd_rs<=1'h1; 
               
               LINE2_7     :   lcd_rs<=1'h1;
               
               LINE2_8     :   lcd_rs<=1'h1;
               
               LINE2_9     :   lcd_rs<=1'h1;
               
               LINE2_A     :   lcd_rs<=1'h1; 
               
               LINE2_B     :   lcd_rs<=1'h1;
               
               LINE2_C     :   lcd_rs<=1'h1;
               
               LINE2_D     :   lcd_rs<=1'h1;
               
               LINE2_E     :   lcd_rs<=1'h1; 
               
               LINE2_F     :   lcd_rs<=1'h1;     
             endcase 
             
             
             case(next_state) 
                IDLE        :  lcd_data<=8'hxx;
                
                INI_SET     :  lcd_data<=8'h38;   //设置16*2显示,5*7点阵,8位数据接口;
                
                INI_CLR     :  lcd_data<=8'h01;   //清屏显示;
                
                CURSOR_SET1 :  lcd_data<=8'h06;   //写一个字符后地址指针加一; 
                   
                CURSOR_SET2 :  lcd_data<=8'h0c;   //设置开显示,不显示光标;
               //line1 
                LINE1_ADDER :  lcd_data<=8'h80;   //LCD1602第一行 首地址; 
                
                LINE1_0     :  lcd_data<=row1[127:120];
                
                LINE1_1     :  lcd_data<=row1[119:112];
                
                LINE1_2     :  lcd_data<=row1[111:104];
                
                LINE1_3     :  lcd_data<=row1[103:96];  
                
                LINE1_4     :  lcd_data<=row1[95:88];
                
                LINE1_5     :  lcd_data<=row1[87:80];
                
                LINE1_6     :  lcd_data<=row1[79:72];
                
                LINE1_7     :  lcd_data<=row1[71:64]; 
                 
                LINE1_8     :  lcd_data<=row1[63:56];
                
                LINE1_9     :  lcd_data<=row1[55:48];
                
                LINE1_A     :  lcd_data<=row1[47:40];
                
                LINE1_B     :  lcd_data<=row1[39:32];  
                
                LINE1_C     :  lcd_data<=row1[31:24];
                
                LINE1_D     :  lcd_data<=row1[23:16];
                
                LINE1_E     :  lcd_data<=row1[15:8];
                
                LINE1_F     :  lcd_data<=row1[7:0];  
              //line2
                LINE2_ADDER :  lcd_data<=8'hC0;   //LCD1602第二行首地址(8'h80+8'h40=8'hC0)
                
                LINE2_0     :  lcd_data<=row2[127:120];
                
                LINE2_1     :  lcd_data<=row2[119:112];
                
                LINE2_2     :  lcd_data<=row2[111:104];
                
                LINE2_3     :  lcd_data<=row2[103:96];  
                
                LINE2_4     :  lcd_data<=row2[95:88];
                
                LINE2_5     :  lcd_data<=row2[87:80];
                
                LINE2_6     :  lcd_data<=row2[79:72];
                
                LINE2_7     :  lcd_data<=row2[71:64];  
                
                LINE2_8     :  lcd_data<=row2[63:56];
                
                LINE2_9     :  lcd_data<=row2[55:48];
                
                LINE2_A     :  lcd_data<=row2[47:40];
                
                LINE2_B     :  lcd_data<=row2[39:32];  
                
                LINE2_C     :  lcd_data<=row2[31:24];
                
                LINE2_D     :  lcd_data<=row2[23:16];
                
                LINE2_E     :  lcd_data<=row2[15:8];
                
                LINE2_F     :  lcd_data<=row2[7:0];
                
               endcase     
                   
                
          end    
             
  end 
 endmodule