Deeply

FPGA LCD1602驱动

0
阅读(1529)

LCD1602是很常用的液晶显示器。用单片机驱动比较容易,相信大家都会的。

初学FPGA对状态机的构建和编程可能不太熟悉,1602的驱动可以作为一个很好的训练。

状态机划分:

分成若干状态,包括:上电等待(PowerOnWait)、模式设置(SetMode)、关闭显示(ShowOff)、清屏(Clear)、设置光标(Curcor)、开显示(ShowOn)、等待(Ready)、写第一行地址(WrAddr1)、写第一行数据(WrDat1)、写第二行地址(WrAddr2)、写第二行数据(WrDat2)。

Quartus自动生成状态机图如下所示:


fsm.png


主要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