FPGA学习心得——LCD1602文字滚动显示(4位传输模式、ST7066U控制芯片)
2012-08-03 10:15
681 查看
目前市场上的LCD1602液晶,其控制芯片主要有Samsung S6A0069X or KS0066U,Hitachi HD44780,SMOS SED1278。Digilent的Spartan-3E开发板上配置的LCD1602的控制芯片是Sitronix的ST7066U,而且开发板配套的开发说明书上写“为了减少使用I/O口,采用4位传输模式”,真心蛮坑的,选择4位或者8位传输模式应该让用户选择嘛,呵呵……废话不说了,开始正文了。
开发板的字符型LCD的接口情况如Figure5-1所示,其中,SF_D<11-8>为数据传输信号,LCD_E是LCD的读写使能信号。LCD_RS是LCD的片选寄存器控制信号,在进行写指令操作的时候,应该将其置0,而在进行读写数据的时候,应该置1。LCD_RW为读写控制位信号,为0时表示写数据(LCD接收来自FPGA的数据),为1时表示读数据(LCD显示寄存器中的数据)。关于LCD接口的详细说明如Table5-1所示。
![](http://my.csdn.net/uploads/201208/03/1343957760_9295.png)
![](http://my.csdn.net/uploads/201208/03/1343958855_3052.png)
液晶控制芯片内部有三组存储器,分别为显示数据存储器(DD RAM)、字符发生器存储器(CG ROM)和字符产生器存储器(CG
RAM)。关于这三组存储器的功能以及相应的概念,网上有很多,这里不再赘述。百度空间里有这么一篇文章可以帮助对这三组存储器的理解:http://hi.baidu.com/%C4%E1%BF%A8njord/blog/item/f070efcbc55f6fe753664f2e.html。
![](http://my.csdn.net/uploads/201208/03/1343959426_4293.png)
Figure5-6的上半部分说明了LCD采用4位数据总线时输入命令和数据的时序情况,所有的命令和数据均以8位形式传送给字符显示屏控制芯片,采用4位数据总线传输时,每一次8位传送操作必须分两次4位传输操作才能完成,先传送高4位,再传送低4位,其间隔时间至少1μs。待完成一个8位的传输操作后,与下一次传输操作的时间间隔至少要超过40 μs。而在传输清屏指令之后,与下一次传输操作的时间间隔至少要超过1.64
ms。上电后,必须向液晶显示屏控制芯片传送初始化命令。由于接通电源后,液晶显示屏控制芯片默认为第一次写数据操作是8位数据传输,而实验开发板上的液晶显示屏控制芯片的DB3~DB0没有连接,仅连接了4位数据总线DB7~DB4,这就需要重复写入功能控制字0X3后再写入采用4位数据总线传输方式,传输功能设置控制字中的DL=0,以及写入功能控制字0X2。操作步骤如下:
(1) 电源接通后,等待15 ms以上,当时钟频率为50 MHz时,15 ms就等于750 000个时钟周期。
(2) 传送控制字SF_D<11:8>=0x3,数据稳定后,LCD_E变成高电平,并且保持高电平12个时钟周期。
(3) 等待4.1 ms或更长时间,当时钟为50 MHz时,即为205 000个时钟周期。
(4) 传送控制字SF_D<11:8>=0x3,LCD_E变成高电平,并且保持高电平12个时钟周期。
(5) 等待100 μs或更长时间,当时钟为50 MHz时,即为5000个时钟周期。
(6) 传送控制字SF_D<11:8>=0x2,LCD_E保持高电平12个时钟周期。
(7) 等待40 μs或更长时间,当时钟为50 MHz时,即为2000个时钟周期。
(8) 传送功能设置命令控制字0X28,DL=0时,采用4位数据总线DB7~DB4;N=1时,双行显示;F =0时,5×7点阵。
(9) 传送模式设置命令,0X06,设置显示屏自动增加地址指针。
(10) 传送显示打开命令,0X0c,打开显示屏,关闭光标显示。
(11) 等待至少1.64 ms(82 000个时钟周期)。
初始化完成后,再传输指定地址计数器地址和显示的数据。当地址计数器配置为自动增1和显示多个字符时,依次传输多个字符编码,每个字符自动存储并显示在下个位置。
以上就是在LCD上显示字符的介绍了,我第一次按照上面的步骤写下来一共整了54个状态,满屏的代码看着真心不舒服。后来采用分频的方法,分频到100us,这样就可以大大减少状态机的状态个数啦。在这个基础上,如果要想实现LCD上的字符滚动显示,我最初的想法就是,通过改变数据的DDRAM地址来实现,即完整的一个字符串写完后,清屏一下,然后将原来的DDRAM地址+1,再写一次,这样就相当于将整个字符串向右移动一位了,如果整个字符串移动到LCD最右端,让DDRAM地址回到最初的位置,如此反复。为了实现动态的效果,在写完一次字符串后,延时一段时间(我这里用的是1s),否则移动太快,液晶上什么也看不到。
下面给出源程序:
开发板的字符型LCD的接口情况如Figure5-1所示,其中,SF_D<11-8>为数据传输信号,LCD_E是LCD的读写使能信号。LCD_RS是LCD的片选寄存器控制信号,在进行写指令操作的时候,应该将其置0,而在进行读写数据的时候,应该置1。LCD_RW为读写控制位信号,为0时表示写数据(LCD接收来自FPGA的数据),为1时表示读数据(LCD显示寄存器中的数据)。关于LCD接口的详细说明如Table5-1所示。
![](http://my.csdn.net/uploads/201208/03/1343957760_9295.png)
![](http://my.csdn.net/uploads/201208/03/1343958855_3052.png)
液晶控制芯片内部有三组存储器,分别为显示数据存储器(DD RAM)、字符发生器存储器(CG ROM)和字符产生器存储器(CG
RAM)。关于这三组存储器的功能以及相应的概念,网上有很多,这里不再赘述。百度空间里有这么一篇文章可以帮助对这三组存储器的理解:http://hi.baidu.com/%C4%E1%BF%A8njord/blog/item/f070efcbc55f6fe753664f2e.html。
![](http://my.csdn.net/uploads/201208/03/1343959426_4293.png)
Figure5-6的上半部分说明了LCD采用4位数据总线时输入命令和数据的时序情况,所有的命令和数据均以8位形式传送给字符显示屏控制芯片,采用4位数据总线传输时,每一次8位传送操作必须分两次4位传输操作才能完成,先传送高4位,再传送低4位,其间隔时间至少1μs。待完成一个8位的传输操作后,与下一次传输操作的时间间隔至少要超过40 μs。而在传输清屏指令之后,与下一次传输操作的时间间隔至少要超过1.64
ms。上电后,必须向液晶显示屏控制芯片传送初始化命令。由于接通电源后,液晶显示屏控制芯片默认为第一次写数据操作是8位数据传输,而实验开发板上的液晶显示屏控制芯片的DB3~DB0没有连接,仅连接了4位数据总线DB7~DB4,这就需要重复写入功能控制字0X3后再写入采用4位数据总线传输方式,传输功能设置控制字中的DL=0,以及写入功能控制字0X2。操作步骤如下:
(1) 电源接通后,等待15 ms以上,当时钟频率为50 MHz时,15 ms就等于750 000个时钟周期。
(2) 传送控制字SF_D<11:8>=0x3,数据稳定后,LCD_E变成高电平,并且保持高电平12个时钟周期。
(3) 等待4.1 ms或更长时间,当时钟为50 MHz时,即为205 000个时钟周期。
(4) 传送控制字SF_D<11:8>=0x3,LCD_E变成高电平,并且保持高电平12个时钟周期。
(5) 等待100 μs或更长时间,当时钟为50 MHz时,即为5000个时钟周期。
(6) 传送控制字SF_D<11:8>=0x2,LCD_E保持高电平12个时钟周期。
(7) 等待40 μs或更长时间,当时钟为50 MHz时,即为2000个时钟周期。
(8) 传送功能设置命令控制字0X28,DL=0时,采用4位数据总线DB7~DB4;N=1时,双行显示;F =0时,5×7点阵。
(9) 传送模式设置命令,0X06,设置显示屏自动增加地址指针。
(10) 传送显示打开命令,0X0c,打开显示屏,关闭光标显示。
(11) 等待至少1.64 ms(82 000个时钟周期)。
初始化完成后,再传输指定地址计数器地址和显示的数据。当地址计数器配置为自动增1和显示多个字符时,依次传输多个字符编码,每个字符自动存储并显示在下个位置。
以上就是在LCD上显示字符的介绍了,我第一次按照上面的步骤写下来一共整了54个状态,满屏的代码看着真心不舒服。后来采用分频的方法,分频到100us,这样就可以大大减少状态机的状态个数啦。在这个基础上,如果要想实现LCD上的字符滚动显示,我最初的想法就是,通过改变数据的DDRAM地址来实现,即完整的一个字符串写完后,清屏一下,然后将原来的DDRAM地址+1,再写一次,这样就相当于将整个字符串向右移动一位了,如果整个字符串移动到LCD最右端,让DDRAM地址回到最初的位置,如此反复。为了实现动态的效果,在写完一次字符串后,延时一段时间(我这里用的是1s),否则移动太快,液晶上什么也看不到。
下面给出源程序:
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; ---- Uncomment the following library declaration if instantiating ---- any Xilinx primitives in this code. --library UNISIM; --use UNISIM.VComponents.all; entity LCD1602_Update is Port ( Clk : in STD_LOGIC; Reset : in STD_LOGIC; LCD_DATA : out STD_LOGIC_VECTOR (7 downto 4); LCD_EN : out STD_LOGIC; LCD_RS : out STD_LOGIC; LCD_RW : out STD_LOGIC); end LCD1602_Update; architecture Behavioral of LCD1602_Update is Signal Clk_scan : STD_LOGIC := '0'; Signal Clk_100us : STD_LOGIC ; --Signal Reset : STD_LOGIC := '0'; --Method 2 Type string1 is array(0 to 5) of STD_LOGIC_VECTOR(7 downto 0); constant data1 : string1 := (x"48",x"45",x"4c",x"4c",x"4f",x"21"); --"HELLO!" Type State_Display is ( st_disp1,st_disp2,st_disp3,st_disp4,st_disp5, st_disp6,st_disp7,st_disp8,st_disp9,st_disp10, st_disp11,st_disp12,st_disp13,st_disp14,st_disp15, st_disp16,st_disp17,st_disp18,st_disp19,st_disp20, st_disp21 ); Signal Current_Disp : State_Display := st_disp1; begin Proc_CLK_100us:process(Clk) --Frequency division to 10KHz,which is 100us variable cnt_clk : integer range 0 to 5000 := 0; begin if(rising_edge(Clk)) then if(cnt_clk < 2500) then cnt_clk := cnt_clk + 1; Clk_scan <= '0'; elsif(cnt_clk < 4999) then cnt_clk := cnt_clk + 1; Clk_scan <= '1'; else cnt_clk := 0; end if; Clk_100us <= Clk_scan; end if; end process Proc_CLK_100us; Proc_Display:process(Clk_100us,Reset) variable cnt_disp : integer range 0 to 10000; variable cnt_rw : integer range 0 to 6 := 0; variable cnt_loop : integer range 0 to 16 := 0; begin -- Method 1 : Use a button(always High) to set the signal port "Reset" if(Reset = '0') then LCD_RS <= '0'; LCD_RW <= '0'; LCD_EN <= '0'; LCD_DATA <= "0000"; cnt_disp := 0; cnt_rw := 0; cnt_loop := 0; Current_Disp <= st_disp1; -- Method 2 : Use delay to set the signal "Reset" to 1 -- if(cnt_disp < 50000) then -- cnt_disp := cnt_disp + 1; -- else -- cnt_disp := 0; -- end if; -- Reset <= '1'; elsif(rising_edge(CLK_100us)) then case Current_Disp is when st_disp1 => if(cnt_disp < 150) then --Wait for 15ms or longer cnt_disp := cnt_disp + 1; Current_Disp <= st_disp1; else cnt_disp := 0; Current_Disp <= st_disp2; end if; when st_disp2 => --Write SF_D<11:8>=0x3, pulse LCD_E High for 240ns. LCD_DATA <= x"3"; LCD_EN <= '1'; if(cnt_disp < 41) then --Wait for 4.1ms or longer cnt_disp := cnt_disp + 1; --实验发现这样写没有问题!!!但最好还是再用一个状态 Current_Disp <= st_disp2; else cnt_disp := 0; LCD_EN <= '0'; Current_Disp <= st_disp3; end if; when st_disp3 => --Write SF_D<11:8>=0x3, pulse LCD_E High for 240ns. if(cnt_disp < 1) then --Wait 100 μs or longer cnt_disp := cnt_disp + 1; LCD_DATA <= x"3"; LCD_EN <= '1'; Current_Disp <= st_disp3; else cnt_disp := 0; LCD_EN <= '0'; Current_Disp <= st_disp4; end if; when st_disp4 => --Write SF_D<11:8>=0x3, pulse LCD_E High for 240ns. if(cnt_disp < 1) then --Wait 40 μs or longer cnt_disp := cnt_disp + 1; LCD_DATA <= x"3"; LCD_EN <= '1'; Current_Disp <= st_disp4; else cnt_disp := 0; LCD_EN <= '0'; Current_Disp <= st_disp5; end if; when st_disp5 => --Write SF_D<11:8>=0x2, pulse LCD_E High for 240ns. if(cnt_disp < 1) then --Wait 40 μs or longer cnt_disp := cnt_disp + 1; LCD_DATA <= x"2"; LCD_EN <= '1'; Current_Disp <= st_disp5; else cnt_disp := 0; LCD_EN <= '0'; Current_Disp <= st_disp6; end if; when st_disp6 => -- Function Set 0x28 if(cnt_disp < 1) then --Wait 40 μs or longer cnt_disp := cnt_disp + 1; LCD_DATA <= x"2"; LCD_EN <= '1'; Current_Disp <= st_disp6; else cnt_disp := 0; LCD_EN <= '0'; Current_Disp <= st_disp7; end if; when st_disp7 => if(cnt_disp < 1) then --Wait 40 μs or longer cnt_disp := cnt_disp + 1; LCD_DATA <= x"8"; LCD_EN <= '1'; Current_Disp <= st_disp7; else cnt_disp := 0; LCD_EN <= '0'; Current_Disp <= st_disp8; end if; when st_disp8 => --Entry Mode Set 0x06 if(cnt_disp < 1) then --Wait 40 μs or longer cnt_disp := cnt_disp + 1; LCD_DATA <= x"0" ; LCD_EN <= '1'; Current_Disp <= st_disp8; else cnt_disp := 0; LCD_EN <= '0'; Current_Disp <= st_disp9; end if; when st_disp9 => if(cnt_disp < 1) then --Wait 40 μs or longer cnt_disp := cnt_disp + 1; LCD_DATA <= x"6"; LCD_EN <= '1'; Current_Disp <= st_disp9; else cnt_disp := 0; LCD_EN <= '0'; Current_Disp <= st_disp10; end if; when st_disp10 => --Display On/Off 0x0C if(cnt_disp < 1) then --Wait 40 μs or longer cnt_disp := cnt_disp + 1; LCD_DATA <= x"0"; LCD_EN <= '1'; Current_Disp <= st_disp10; else cnt_disp := 0; LCD_EN <= '0'; Current_Disp <= st_disp11; end if; when st_disp11 => if(cnt_disp < 1) then --Wait 40 μs or longer cnt_disp := cnt_disp + 1; LCD_DATA <= x"C"; LCD_EN <= '1'; Current_Disp <= st_disp11; else cnt_disp := 0; LCD_EN <= '0'; Current_Disp <= st_disp12; end if; when st_disp12 => --Clear Display 0x01 if(cnt_disp < 1) then --Wait 40 μs or longer cnt_disp := cnt_disp + 1; LCD_DATA <= x"0"; LCD_EN <= '1'; Current_Disp <= st_disp12; else cnt_disp := 0; LCD_EN <= '0'; Current_Disp <= st_disp13; end if; when st_disp13 => if(cnt_disp < 1) then --Wait 40 μs or longer cnt_disp := cnt_disp + 1; LCD_DATA <= x"1"; LCD_EN <= '1'; Current_Disp <= st_disp13; else cnt_disp := 0; LCD_EN <= '0'; Current_Disp <= st_disp14; end if; when st_disp14 => if(cnt_disp < 17) then --Wait 1.64ms or longer cnt_disp := cnt_disp + 1; Current_Disp <= st_disp14; else cnt_disp := 0; Current_Disp <= st_disp15; end if; when st_disp15 => --Set DDRAM Address 0x00+0x80 if(cnt_disp < 1) then --Wait 40 μs or longer cnt_disp := cnt_disp + 1; LCD_DATA <= x"8"; LCD_EN <= '1'; Current_Disp <= st_disp15; else cnt_disp := 0; LCD_EN <= '0'; Current_Disp <= st_disp16; end if; when st_disp16 => --Shift the DDRAM Address if(cnt_disp < 1) then --Wait 40 μs or longer cnt_disp := cnt_disp + 1; LCD_DATA <= x"0" + conv_std_logic_vector(cnt_loop,4); if(cnt_loop < 15) then cnt_loop := cnt_loop + 1; else cnt_loop := 0; end if; LCD_EN <= '1'; Current_Disp <= st_disp16; else cnt_disp := 0; LCD_EN <= '0'; Current_Disp <= st_disp17; end if; when st_disp17 => --Write Data to DDRAM(st_disp17-st_disp20) if(cnt_rw < 6) then LCD_DATA <= data1(cnt_rw)(7 downto 4); --Write the Upper Nibble LCD_EN <= '1'; LCD_RS <= '1'; Current_Disp <= st_disp18; else cnt_rw := 0; Current_Disp <= st_disp21; end if; when st_disp18 => LCD_EN <= '0'; Current_Disp <= st_disp19; when st_disp19 => LCD_DATA <= data1(cnt_rw)(3 downto 0); --Write the Lower Nibble LCD_EN <= '1'; LCD_RS <= '1'; Current_Disp <= st_disp20; when st_disp20 => LCD_EN <= '0'; cnt_rw := cnt_rw + 1; Current_Disp <= st_disp17; when st_disp21 => if(cnt_disp < 10000) then --Delay 1 min to display the next string cnt_disp := cnt_disp + 1; Current_Disp <= st_disp21; else cnt_disp := 0; LCD_RS <= '0'; Current_Disp <= st_disp12; end if; when others => null; end case; end if; end process Proc_Display; end Behavioral;
相关文章推荐
- unity3D学习【功能实现】之四:滚动文字显示,类似公告面板
- 设计模式之工厂模式的学习心得
- fpga控制vga显示黑白图片
- GridView自动生成列时,利用DIV控制显示长宽 控件数据行和表头文字不换行
- C#跑马灯,图片滚动,后台获取图片地址。动态绑定图片,imag显示文字
- 电力项目七--js控制文字内容过长的显示和文本字数的显示
- MapXtreme 2005 学习心得 在地图上创建点/线并显示标注(五)
- 菜鸟学习设计模式的心得
- 设计模式学习心得——one
- android - TextView单行显示...或者文字左右滚动(走马灯效果)
- 文字滚动应用,禁止鼠标控制暂停或滚动
- js控制”回到顶部“按钮滚动一屏后再显示和滚动条平滑滚动
- android 文字超出控件宽度时,自动滚动显示,类似跑马灯效果
- javascript 控制 table tr display block 显示模式时,只对第一个单元格有效
- 学习设计模式心得
- 设计模式之装饰模式,学习心得。如有错误,请多指教,谢谢。
- Java设计模式学习心得总结
- 如何让一个方框栏内的文字滚动显示?
- 文字滚动显示效果
- jQuery [jQuery视频教程--->Jq网页开发精解04] 设计列表 文字滚动 图片按比例显示