LCD1602

敲好聽的名字捏發表於2022-11-30
  • 功能描述:

  透過寫入指令或資料,控制內部的驅動晶片,從而實現在LCD螢幕上顯示字串:

\[{I\_❤LOVE❤\_BUAA!} \]

\[{1952--2022} \]

  • 設計方案:

  設計將以有限狀態機的形式完成功能設計,其中涉及的狀態及相關功能如下圖1所示,

LCD1602
圖1 狀態表

  CGRAM_ADDR前面均為LCD初始化狀態,不多贅述。CGRAM_ADDR狀態下,傳送指令:要寫入CGRAM,地址為00H;下面CGRAM_WRITE狀態下,傳送資料:其中字模資料為8行八列資料,不能一次傳入,故設計一計數器,若計數到7,則跳轉下一狀態ADDR1,否則繼續CGRAM_WRITE狀態直到完成一個字元的寫入;ADDR1和ADDR2狀態下均傳送指令:要寫入DDRAM,地址分別為00H和40H,完成後均跳轉到WRITE_DDRAM狀態;WRITE_DDRAM也需要一計數器來監控傳到了第幾行 以及是否傳送完畢,結束後跳轉至STOP狀態,維持當下顯示

  下面將說明具體實現。

  ①使能訊號的生成: 當LCD的時脈頻率設定錯誤時,LCD的顯示就會閃,或者沒有顯示,故選擇控制使能訊號的週期生成,來達到設計要求的500Hz,這裡選擇的是計數方法,已知我們的初始時鐘是CLOCK_50,若每逢上升沿計數,為生成500Hz的工作時鐘,需每逢計數99999,進行一次歸零進入下一計數週期,並設定使能訊號LCD_EN,每逢49999進行一次翻轉,即可對應得到使能訊號;

  ②延時15ms的實現: 在空閒IDLE狀態下,需延時15ms進入下一狀態,綜合考慮電路效能,選擇以計數的形式來實現,同上①;

  ③LCD的顯示: 在LCD上顯示字元的過程就是向DDRAM的相應地址寫入資料的過程,其中DDRAM地址與顯示位置的對應關係如下圖2所示。若想要在LCD的第一行最左顯示字母A,則過程為:傳指令DDRAM地址00H\({→}\)傳資料字母A(41H)

LCD1602
圖2 DDRAM地址與顯示對照表

  ④自定義字元的實現: 在晶片中內建了192個常用字元的字模,存於CGROM中,可以實現基本數字和字母的直接呼叫顯示(如上文中字母A),此外還有8個允許使用者自定義字元的CGRAM,即為下圖3中綠框中所示,我們可以透過先將自定義字元寫入CGRAM,再調入寫入DDRAM的方法實現自定義字元的顯示,下面以字元\({❤}\)為例加以說明:

LCD1602
圖3 CGRAM
  • 取字模: 將圖形轉換為二進位制數,顯示出來的部分置1,未顯示的部分置0,如下圖4所示,可以得到8列八位資料
LCD1602
圖4 字模結果
  • 寫入CGRAM: 寫入CGRAM的指令如下圖5所示,其中CGRAM的地址為00H-0FH,這裡我們選擇寫入地址為00H,將上面得到的8列八位資料依次寫入CGRAM,則CGROM中00H就代表了字元\({♥}\)
LCD1602
圖5 寫入CGRAN指令
  • 顯示: 即將地址指標指向DDRAM即可,寫入資料為00H。

  • 關鍵程式碼:

	/***main_FSM***/
	//Section1
	always@(posedge clk, negedge rstn)	begin
		if(!rstn)	cur_state <= IDLE;
		else if(cnt_500hz == 17'b01100001101001111)
			cur_state <= next_state;
	end

    //Section2
	always @(cur_state)	begin
		case(cur_state)
			IDLE:				begin
									if(flag)	next_state = CLEAR_LCD;
									else		next_state = IDLE;
								end
			CLEAR_LCD:		next_state = SET_DISP_MODE;
			SET_DISP_MODE:	next_state = DISP_ON;
			DISP_ON: 		next_state = SHIFT_DOWN;
			SHIFT_DOWN: 	next_state = CGRAM_ADDR;
			CGRAM_ADDR:		next_state = WRITE_CGRAM;
			WRITE_CGRAM:	begin
									if(cnt_cgram == 4'd7)		next_state = ADDR1;
									else								next_state = WRITE_CGRAM;
								end
			ADDR1:			next_state <= WRITE_DDRAM;
			WRITE_DDRAM:	begin
									if(cnt_char == 6'd15)		next_state = ADDR2;
									else if(cnt_char == 6'd31)	next_state = STOP;
									else								next_state = WRITE_DDRAM;
								end
			ADDR2:			next_state = WRITE_DDRAM;
			STOP:				next_state = STOP;
			default:			next_state = IDLE;
		endcase
	end

	//Section3
	always @(posedge clk, negedge rstn)	begin
		if(!rstn)	begin
			lcd_rs <= 1'b0;
			lcd_data <= 8'b00000000;
		end
		else	begin
			case(cur_state)	
				IDLE:				begin	lcd_rs<=1'b0; lcd_data<=8'b00000000;	end
				CLEAR_LCD:		begin lcd_rs<=1'b0; lcd_data<=8'b00000001;	end
				SET_DISP_MODE:	begin lcd_rs<=1'b0; lcd_data<=8'b00111000;	end
				DISP_ON: 		begin lcd_rs<=1'b0; lcd_data<=8'b00001100;	end
				SHIFT_DOWN: 	begin	lcd_rs<=1'b0; lcd_data<=8'b00000110;	end
				CGRAM_ADDR: 	begin lcd_rs<=1'b0; lcd_data<=8'b01000000;	end
				WRITE_CGRAM:	begin lcd_rs<=1'b1; lcd_data<=char_heart[cnt_cgram];		end
				ADDR1:			begin lcd_rs<=1'b0; lcd_data<=8'b10000000;	end
				WRITE_DDRAM:	begin lcd_rs<=1'b1; lcd_data<=char;				end
				ADDR2:			begin lcd_rs<=1'b0; lcd_data<=8'b11000000;	end
				STOP:				begin lcd_rs<=1'b0; lcd_data<=8'b00111000;	end
				default:			begin	lcd_rs<=1'b0; lcd_data<=8'b00111000;	end
			endcase
		end
	end
  • 綜合結果:

  頂層模組的綜合結果如下圖6所示,僅有一lcd1602模組;

LCD1602
圖6 頂層模組綜合結果

  lcd1602模組的綜合結果如下圖7所示,無LATCH;

LCD1602
圖7 lcd1602綜合結果

  得到的狀態轉移圖如下圖8所示,與設計思路相符合;

LCD1602
圖8 狀態轉移圖

  SUMMARY的結果如下圖9所示。

LCD1602
圖9 SUMMARY結果
  • 顯示結果: