这两天都在学习怎样用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