⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 lcd.vhd

📁 基于FPGA液晶控制器设计与实现
💻 VHD
字号:
-- -----------------------------------------------------------------------------
--
-- 文件名 : lcd.vhd
--
-- 功能   : 液晶 1602 lcd 的模块.
--
-- 端口   : CLK_Z       : in     25MHz , 50% 占空比的时钟信号.
--          pulse1K     : in    1KHz 的 脉冲信号, 占空比很小.
--          pulse1M     : in    1MHz 的 脉冲信号, 占空比很小.
--          RESET       : in    大于671.08864ms的 复位 信号.
--
--          enable      : in    写显示寄存器的使能信号.
--          wren        : in    写显示寄存器的使能信号.
--          writeAddr   : in    写显示寄存器的地址.
--          writeData   : in    写显示寄存器的数据.
--
--          lcd_RS      : out   PIN_176, 1602 LCD 的 RS 管脚.
--          lcd_RW      : out   PIN_179, 1602 LCD 的 RW 管脚.
--          lcd_E       : out   PIN_178, 1602 LCD 的 E  管脚.
--          lcd_D0      : out   PIN_177, 1602 LCD 的 D0 管脚.
--          lcd_D1      : out   PIN_174, 1602 LCD 的 D1 管脚.
--          lcd_D2      : out   PIN_175, 1602 LCD 的 D2 管脚.
--          lcd_D3      : out   PIN_170, 1602 LCD 的 D3 管脚.
--          lcd_D4      : out   PIN_173, 1602 LCD 的 D4 管脚.
--          lcd_D5      : out   PIN_168, 1602 LCD 的 D5 管脚.
--          lcd_D6      : out   PIN_169, 1602 LCD 的 D6 管脚.
--          lcd_D7      : out   PIN_166, 1602 LCD 的 D7 管脚.
--
-- 笔记   : 1. LCD写操作的 状态机 采用 格雷码 是由于 常用实现方式的仿真 中 lcd_RS, lcd_RW 会产生毛刺.
--          2. 在 LCD 上显示8个数据, 每个数据占4个显示字符.
--               在LCD的显示起始地址值为: lcdWriteAddr. 分别为 0x00, 0x04, 0x08, 0x0C, 0x40, 0x44, 0x48, 0x4C.
--
-- Total logic elements:  206/12,060 ( 2%)
-- -----------------------------------------------------------------------------
-- 建立日期 : 2007/4/15
-- -----------------------------------------------------------------------------
-- 修改日期 :
-- 修改内容 :
-- -----------------------------------------------------------------------------

library ieee;
	use ieee.std_logic_1164.all;
	use ieee.std_logic_unsigned.all;

entity lcd is
	port (
		CLK_Z            : in  std_logic;
		pulse1K          : in  std_logic;
		pulse1M          : in  std_logic;

		RESET            : in  std_logic;

		enable           : in  std_logic;
		wren             : in  std_logic;
		writeAddr        : in  std_logic_vector(2 downto 0);
		writeData        : in  std_logic_vector(15 downto 0);

		lcd_RS           : out std_logic;
		lcd_RW           : out std_logic;
		lcd_E            : out std_logic;
		lcd_Data         : out std_logic_vector(7 downto 0)
	);
end lcd;

architecture RTL of lcd is
			constant Tstart        : std_logic_vector(2 downto 0) := "000";
			constant T1            : std_logic_vector(2 downto 0) := "001";
			constant Tshow0        : std_logic_vector(2 downto 0) := "011";
			constant Tshow1        : std_logic_vector(2 downto 0) := "010";
			constant Tshow2        : std_logic_vector(2 downto 0) := "110";
			constant Tshow3        : std_logic_vector(2 downto 0) := "100";
	type stateS_TYPE is (Sinit0, Sinit1, Sinit2, Sinit3, Sinit4, Sinit5, Saddr, Sdata0, Sdata1, Sdata2, Sdata3);

	signal stateS,  next_stateS                             : stateS_TYPE;
	signal stateT,  next_stateT                             : std_logic_vector(2 downto 0);

	signal lcdRS, lcdRW, lcdE,  tempLcdRS, writeLCD         : std_logic;
	signal tempLcdData                                      : std_logic_vector(7 downto 0);

	signal ramData0, tempRamData0, ramData1, tempRamData1   : std_logic_vector(15 downto 0);
	signal ramData2, tempRamData2, ramData3, tempRamData3   : std_logic_vector(15 downto 0);
	signal ramData4, tempRamData4, ramData5, tempRamData5   : std_logic_vector(15 downto 0);
	signal ramData6, tempRamData6, ramData7, tempRamData7   : std_logic_vector(15 downto 0);

	signal lcdAddr, tempLcdAddr                             : std_logic_vector(2 downto 0);
	signal showData                                         : std_logic_vector(15 downto 0);

	signal dataASIC3, dataASIC2, dataASIC1, dataASIC0       : std_logic_vector(3 downto 0);
	signal tempDataASIC3, tempDataASIC2                     : std_logic_vector(2 downto 0);
	signal tempDataASIC1, tempDataASIC0                     : std_logic_vector(2 downto 0);

	signal lcdWriteAddr                                     : std_logic_vector(6 downto 0);
	signal lcdWriteData3, lcdWriteData2                     : std_logic_vector(7 downto 0);
	signal lcdWriteData1, lcdWriteData0                     : std_logic_vector(7 downto 0);

begin
	lcd_RS   <= lcdRS;
	lcd_RW   <= lcdRW;
	lcd_E    <= lcdE;
	lcd_Data <= tempLcdData;

	----------- 接收 到 显示寄存器(ramData0 .. ramData7) --------------------------------------
	--  CLK_Z       enable   wren          out
	-- 非上升沿       *        *            *
	--  上升沿        1        *            *
	--  上升沿        1        H          write

	ramData0 <= tempRamData0;         ramData1 <= tempRamData1;
	ramData2 <= tempRamData2;         ramData3 <= tempRamData3;
	ramData4 <= tempRamData4;         ramData5 <= tempRamData5;
	ramData6 <= tempRamData6;         ramData7 <= tempRamData7;

	writeData_pro: process (RESET, CLK_Z, enable, wren, writeAddr, writeData, ramData0,
	                            ramData1, ramData2, ramData3, ramData4, ramData5, ramData6, ramData7)
	begin
		if (RESET = '1') then
			tempRamData0 <= (others => '0');        tempRamData1 <= (others => '0');
			tempRamData2 <= (others => '0');        tempRamData3 <= (others => '0');
			tempRamData4 <= (others => '0');        tempRamData5 <= (others => '0');
			tempRamData6 <= (others => '0');        tempRamData7 <= (others => '0');

		elsif (CLK_Z'event and CLK_Z = '1' and enable = '1' and wren = '1') then --上升沿
			tempRamData0 <= ramData0;        tempRamData1 <= ramData1;
			tempRamData2 <= ramData2;        tempRamData3 <= ramData3;
			tempRamData4 <= ramData4;        tempRamData5 <= ramData5;
			tempRamData6 <= ramData6;        tempRamData7 <= ramData7;

			case writeAddr is
				when "000" => tempRamData0 <= writeData;
				when "001" => tempRamData1 <= writeData;
				when "010" => tempRamData2 <= writeData;
				when "011" => tempRamData3 <= writeData;
				when "100" => tempRamData4 <= writeData;
				when "101" => tempRamData5 <= writeData;
				when "110" => tempRamData6 <= writeData;
				when "111" => tempRamData7 <= writeData;
				when others => null;
			end case;
		end if;
	end process;

	----------- 选择 显示数据 showData ------------------------------------------------------
	showData_pro: process (RESET, CLK_Z, pulse1K, lcdAddr, ramData0, ramData1,
	                            ramData2, ramData3, ramData4, ramData5, ramData6, ramData7)
	begin
		if (RESET = '1') then
			showData <= "0000000000000000";
		elsif (CLK_Z'event and CLK_Z = '1' and pulse1K = '1') then --上升沿
			case lcdAddr is
				when "000" => showData  <=  ramData0;
				when "001" => showData  <=  ramData1;
				when "010" => showData  <=  ramData2;
				when "011" => showData  <=  ramData3;
				when "100" => showData  <=  ramData4;
				when "101" => showData  <=  ramData5;
				when "110" => showData  <=  ramData6;
				when "111" => showData  <=  ramData7;
				when others => null;
			end case;
		end if;
	end process;

	------- 将 showData 转换为 4个 对应显示的ASIC码(lcdWriteData3 .. lcdWriteData0). --------------------
	dataASIC3 <= showData(15 downto 12);        tempDataASIC3 <= dataASIC3(2 downto 0) + "111";
	dataASIC2 <= showData(11 downto  8);        tempDataASIC2 <= dataASIC2(2 downto 0) + "111";
	dataASIC1 <= showData( 7 downto  4);        tempDataASIC1 <= dataASIC1(2 downto 0) + "111";
	dataASIC0 <= showData( 3 downto  0);        tempDataASIC0 <= dataASIC0(2 downto 0) + "111";

	lcdWriteData3 <= ("01000" & tempDataASIC3) when((dataASIC3(3) and (dataASIC3(2) or dataASIC3(1))) = '1') else
	                 ("0011" & dataASIC3);
	lcdWriteData2 <= ("01000" & tempDataASIC2) when((dataASIC2(3) and (dataASIC2(2) or dataASIC2(1))) = '1') else
	                 ("0011" & dataASIC2);
	lcdWriteData1 <= ("01000" & tempDataASIC1) when((dataASIC1(3) and (dataASIC1(2) or dataASIC1(1))) = '1') else
	                 ("0011" & dataASIC1);
	lcdWriteData0 <= ("01000" & tempDataASIC0) when((dataASIC0(3) and (dataASIC0(2) or dataASIC0(1))) = '1') else
	                 ("0011" & dataASIC0);

	----------------- LCD 初始化, 并 向其发送 数据. ---------------------------------------------
	stateS_mooreState: process (RESET, CLK_Z, pulse1K, next_stateS, tempLcdAddr)
	begin
		if (RESET = '1') then
			stateS   <=  Sinit0;
			lcdAddr  <=  "000";
		elsif (CLK_Z'event and CLK_Z = '1' and pulse1K = '1') then --上升沿
			stateS      <=  next_stateS;
			lcdAddr     <=  tempLcdAddr;
		end if;
	end process;

	lcdWriteAddr <= lcdAddr(2) & "00" & lcdAddr(1 downto 0) & "00"; --发送到 LCD的地址.
	stateS_pro : process(stateS, lcdAddr, lcdWriteAddr, lcdWriteData0, lcdWriteData1, lcdWriteData2, lcdWriteData3)
	begin
		writeLCD    <= '1';     --默认: 对LCD进行写操作.(除了 延时,其他状态都要置一)
		tempLcdRS   <= '0';
		tempLcdData <= "00000001";
		tempLcdAddr <= lcdAddr; --显示数据的地址.

		case stateS is
			when Sinit0 =>  --初始化
					next_stateS <= Sinit1;

			when Sinit1 =>  --功能设置. DL:1(8位数据接口); N:1(两行显示); F:0(5 * 7点阵字符)
				tempLcdRS   <= '0';
				tempLcdData <= "00111000";--0x38;

					next_stateS <= Sinit2;

			when Sinit2 =>  --显示开关控制.D:1(表示显示开); C:0(表示光标关); B:0(表示闪烁关)
				tempLcdRS   <= '0';
				tempLcdData <= "00001100";--0x0C;

					next_stateS <= Sinit3;

			when Sinit3 =>  --清屏(要延时 1.53ms).
				tempLcdRS   <= '0';
				tempLcdData <= "00000001";--0x01;

					next_stateS <= Sinit4;

			when Sinit4 =>  --延时.
				writeLCD    <= '0';       --不对LCD操作.

					next_stateS <= Sinit5;

			when Sinit5 =>  --输入方式设置.:I/D:1(数据读、写操作后,AC自动增一); SH:0(数据读、写操作,画面不动)
				tempLcdRS   <= '0';
				tempLcdData <= "00000110";--0x06;

					next_stateS <= Saddr;

			when Saddr =>   -- DDRAM 地址设置. RS:0, R/W:0, DB7:1.
				tempLcdRS   <= '0';
				tempLcdData <= '1' & lcdWriteAddr;--lcdWriteAddr 7bit
				tempLcdAddr <= lcdAddr + 1;       --地址 加1(改变显示位置)

					next_stateS <= Sdata0;

			when Sdata0 =>  -- 显示数据0.
				tempLcdRS   <= '1';
				tempLcdData <= lcdWriteData3;    --显示数据高位

					next_stateS <= Sdata1;

			when Sdata1 =>  -- 显示数据1.
				tempLcdRS   <= '1';
				tempLcdData <= lcdWriteData2;

					next_stateS <= Sdata2;

			when Sdata2 =>  -- 显示数据2.
				tempLcdRS   <= '1';
				tempLcdData <= lcdWriteData1;

					next_stateS <= Sdata3;

			when Sdata3 =>  -- 显示数据3.
				tempLcdRS   <= '1';
				tempLcdData <= lcdWriteData0;    --显示数据低位

					next_stateS <= Saddr;

			when others => next_stateS <= Sinit0; -- 返回 开始状态
		end case;
	end process;

	----------------- LCD 写操作 时序. -----------------------------------------------------
	stateT_mooreState: process (RESET, CLK_Z, pulse1M, next_stateT)
	begin
		if (RESET = '1') then
			stateT      <=  Tstart;
		elsif (CLK_Z'event and CLK_Z = '1' and pulse1M = '1') then --上升沿
			stateT      <=  next_stateT;
		end if;
	end process;

	stateT_pro : process(stateT, pulse1K, writeLCD, tempLcdRS)
	begin
		lcdRS    <= '0';
		lcdRW    <= '0';
		lcdE     <= '0';

		case stateT is
			when Tstart =>  --开始,等待 pulse1K 信号
				lcdRS    <= '0';
				lcdRW    <= '1';
				if (pulse1K = '1') then
					next_stateT <= T1;
				else
					next_stateT <= Tstart;
				end if;

			when T1 =>      --在 pulse1K 脉冲后的第一个 pulse1M 脉冲 处查看是否需要发送数据到LCD
				if (writeLCD = '1') then --发送数据到 LCD
					next_stateT <= Tshow0;
				else
					next_stateT <= Tshow3;
				end if;

			when Tshow0 =>  --KS0066 写操作时序0
				lcdRS    <= tempLcdRS;
				lcdRW    <= '0';

					next_stateT <= Tshow1;

			when Tshow1 =>  --KS0066 写操作时序1
				lcdRS    <= tempLcdRS;
				lcdRW    <= '0';
				lcdE     <= '1';

					next_stateT <= Tshow2;

			when Tshow2 =>  --KS0066 写操作时序2
				lcdRS    <= tempLcdRS;
				lcdRW    <= '0';
				lcdE     <= '0';

					next_stateT <= Tshow3;

			when Tshow3 =>  --KS0066 写操作时序3
				lcdRS    <= '0';
				lcdRW    <= '1';
				lcdE     <= '0';

					next_stateT <= Tstart;        -- 返回 开始状态

			when others => next_stateT <= Tstart; -- 返回 开始状态
		end case;
	end process;

end RTL;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -