7.4 MCU接口LCD控制器
7.4.1 MCU接口LCD控制器介绍
MCU接口LCD的特点是其输入输出接口是标准的8位或16位单片机总线,其屏幕刷新和行列扫描过程在其内部完成,因此也被称作智能LCD(Smart LCD)。
这里以MzT24-1 TFT LCD为例。MzT24-1是一块高画质的TFT真彩LCD模块,具有丰富多样的接口、编程方便、易于扩展等良好性能。MzT24-1内置专用驱动和控制IC(SPFD5408),并且驱动IC自己集成显示缓存,无须外部显示缓存。
MzT24-01彩色TFT LCD显示模块的基本参数如表7-3所示。
表7-3 MzT24-1彩色TFT LCD显示模块的基本参数
MzT24-1彩色TFT LCD显示模块的输入输出如表7-4所示。
表7-4 MzT24-1彩色TFT LCD显示模块的引脚说明
MzT24-1彩色TFT LCD显示模块的LCD驱动控制IC为SPFD5408,用户在对MzT24-1模块进行操作时,实际上是对SPFD5408进行相关的控制寄存器、显示数据存储器进行操作。
MzT24-1模块的2.4英寸TFT-LCD显示面板上,共分布着240×320个像素点,而模块内部的TFT-LCD驱动控制芯片内置有与这些像素点对应的显示数据RAM(简称显存)。模块中每个像素点需要16位的数据(即2字节长度)来表示该点的RGB颜色信息,所以模块内置的显存共有240×320×16bit的空间,通常以字节(byte)来描述其大小。
MzT24-1模块的显示操作非常简便,需要改变某一个像素点的颜色时,只需要对该点所对应的2个字节的显存进行操作即可。而为了便于索引操作,模块将所有的显存地址分为X轴地址(X Address)和Y轴地址(Y Address),分别可以寻址的范围为X Address=0~239,Y Address= 0~319,X Address和Y Address交叉对应着一个显存单元(2byte);这样只要索引到了某一个X、Y轴地址时,并对该地址的寄存器进行操作,便可对TFT-LCD显示器上对应的像素点进行操作了。
MzT24-1模块的像素点与显存对应关系如图7-6所示:
图7-6 显存与像素点的对应关系
MzT24-1模块内部有一个显存地址累加器AC,用于在读写显存时对显存地址进行自动的累加,这在连续对屏幕显示数据操作时非常有用,特别是应用在图形显示、视频显示时。此外,AC累加器可以设置为各种方向的累加方式,如通常情况下为对X Address累加方式,具体为当累加到一行的尽头时,会切换到下一行的开始累加;还可以为对Y Address累加方式,具体为当累加到一列(垂直方向)的尽头时,会切换到下一个X Address所对应的列开始累加。
MzT24-1模块支持标准Intel8080总线,总线的最高速度可达8MHz,可支持视频。其接口时序如图7-7所示。
图7-7 MzT24-1模块接口时序
接口时序中的主要参数如表7-5所示。
表7-5 MzT24-1模块接口时序的主要时间参数
对MzT24-1模块的操作主要分为两种,一是对控制寄存器的读写操作,二是对显存的读写操作;而这两种操作实际上都是通过对LCD控制器(SPFD5408)的寄存器(Register)进行操作完成的,SPFD5408提供了一个索引寄存器(Index Register),对该Index Register寄存器的写入操作可以指定操作的寄存器索引,以便于完成控制寄存器、显存操作寄存器的读写操作。MzT24-1提供了RS(有些资料称A0)控制线,并以此线的高低电平状态来区别这对Index Register操作还是对所指向的寄存器进行操作,当RS为低电平时,表示当前的总线操作是对Index Register进行操作,即指明接下去的寄存器操作是针对哪一个寄存器的;当RS为高电平时,表示为对寄存器操作。
MzT24-1模块内部有控制寄存器,用户在使用MzT24-1之前及对其进行操作过程当中,需要对一些寄存器进行写操作以完成对LCD的初始化,或者是完成某些功能的设置(如当前显存操作地址设置等)。
对控制寄存器进行操作前,需要先对索引寄存器进行定入操作,以指明接下去的寄存器读写操作是针对哪一个寄存器的。操作的步骤如下:
①在RS为低电平的状态下,写入两个字节的数据,第一个字节为零,第二字节为寄存器索引值。
②然后在RS为主电平的状态下,写入两个字节数据,第一字节为高8位,第二字节为低8位;如要读出指定寄存器的数据,则需要连续三次读操作方能完成一次读出操作,第一个字节为无效数据,第二字节为高8位,第三字节为低8位。
MzT24-1的显存操作也是通过寄存器操作来完成的,即对0x22寄存器进行操作时,就是对当前位置点的显示进行读写操作。
MzT24-1模块的控制寄存器当中,最常被调用的是寄存器除了对显存操作的0x22寄存器外,还有当前显存地址的寄存器display RAM bus address counter(AC),一共由两个的寄存器组成,分别存放有X Address和Y Adderss,表示当前对显存数据的读写操作是针对于该地址所指向的显存单元;而每一个显存单元在前面已经用图示意过,每个单元有16位,最高的5位为R(红)的分量,最低的5位为B(蓝)的分量,中间6位为G(绿)分量。如图7-8所示。
图7-8 显存单元分量示意图
当需要对LCD显示面板上某一个坐标点(X,Y)进行操作时,需要先设置AC,以指向需要操作的点所对应的显存地址,然后连续写入或读出数据,才完成对该点的显存单元的数据操作。
而当对某一个显存单元完成写入数据操作后,AC会自动地进行调整,或者是不进行调整(根据控制寄存器中的设置而决定)保持原来指向。AC的这个特性对于MzT24-1模块来说非常有用,可以根据此特性设计出快速的LCD显示操作功能函数,以适应不同用户的需求。
关于该屏幕的更多信息请参考其用户手册。
7.4.2 MCU接口LCD控制器源代码分析
本章的MCU接口LCD针对MzT24-1而设计。其主体结构是一个状态机,该状态机的主要任务是完成对LCD指定寄存器的写操作。
查阅MzT24-1的模块编程手册可知,该模块内部有控制寄存器,用户在使用MzT24-1之前及对其进行操作过程中,需要对寄存器进行写操作以完成对LCD的初始化,或者是完成某些功能的设置(如当前显存操作地址设置等)。
对控制寄存器进行操作前,需要先对索引寄存器进行写入操作,以指明接下去的寄存器写操作是针对哪一个寄存器的。操作的步骤如下:
①在RS为低电平的状态下,写入两个字节的数据,第一个字节为零,第二个字节为寄存器索引。
②后在RS为高电平的状态下,写入两个字节数据,第一个字节为高8位,第二个字节为低8位。
smart_lcd完成以上4个字节的写操作及中间的等待过程。其源代码如下:
module smart_lcd(clk,rst,ack_o,err_o,rty_o,dat_i,adr_i,dat_o,sel_i,stb_i,cyc_i,we_i, LCD_DATA, LCD_CS, LCD_RS, LCD_RST, LCD_WR,LCD_RD); ……//此处忽略了输入输出定义 reg [7:0] lcd_data_out; reg lcd_data_out_ena; assign LCD_DATA = lcd_data_out_ena?lcd_data_out:8'bzzzz_zzzz; wire write_op = adr_i == 32'hC0000000 && cyc_i && stb_i && we_i && sel_i == 4'hf; wire read_op = adr_i == 4 && cyc_i && stb_i && sel_i == 4'hf; parameter IDLE = 1, WRITE_INDEX_HIGH = 2, WAIT_1 = 3, WRITE_INDEX_HIGH_CANCEL_WR = 4, WRITE_INDEX_HIGH_END = 5, WRITE_INDEX_LOW = 6, WAIT_2 = 7, WRITE_INDEX_LOW_CANCEL_WR = 8, WRITE_INDEX_LOW_END = 9, WRITE_DATA_HIGH = 10, WAIT_3 = 11, WRITE_DATA_HIGH_CANCEL_WR = 12, WRITE_DATA_HIGH_END = 13, WRITE_DATA_LOW = 14, WAIT_4 = 15, WRITE_DATA_LOW_CANCEL_WR = 16, WRITE_DATA_LOW_END = 17, END_OPERATION = 18; always @ (posedge clk or posedge rst) begin if(rst) begin ack_o <= 1'b0; LCD_RS <= 1;LCD_CS <= 1;LCD_WR <= 1;LCD_RD <= 1; lcd_data_out_ena <= 0;lcd_data_out <= 0;wait_cnt <= 0;state <= IDLE; end else begin case(state) IDLE:begin if(write_op) begin //开始进行写操作 state <= WRITE_INDEX_HIGH;LCD_RS <= 0; LCD_CS <= 0; lcd_data_out <= 0; end else begin //空闲 ack_o <= 1'b0; LCD_RS <= 1;LCD_CS <= 1;LCD_WR <= 1; LCD_RD <= 1;lcd_data_out_ena <= 0; lcd_data_out <= 0; wait_cnt <= 0; end end WRITE_INDEX_HIGH:begin //写INDEX高字节 ack_o <= 1;data_buf <= dat_i[23:0]; LCD_WR<=0;LCD_RD<=1;state<=WAIT_1;wait_cnt<=0;lcd_data_out_ena <= 1; end WAIT_1:begin ack_o <= 0;wait_cnt <= wait_cnt + 6'b1; if(wait_cnt > 3) state <= WRITE_INDEX_HIGH_CANCEL_WR; end WRITE_INDEX_HIGH_CANCEL_WR:begin LCD_WR <= 1;wait_cnt <= 0; state <= WRITE_INDEX_HIGH_END; end WRITE_INDEX_HIGH_END:begin wait_cnt <= wait_cnt + 1; if(wait_cnt > 4) begin state <= WRITE_INDEX_LOW; lcd_data_out_ena <= 0; end end WRITE_INDEX_LOW:begin LCD_WR <= 0; LCD_RD <= 1; lcd_data_out <= data_buf[23:16]; lcd_data_out_ena <= 1; wait_cnt <= 0; state <= WAIT_2; end WAIT_2:begin wait_cnt <= wait_cnt + 1; if(wait_cnt > 3)state <= WRITE_INDEX_LOW_CANCEL_WR; end WRITE_INDEX_LOW_CANCEL_WR:begin LCD_WR <= 1;wait_cnt <= 0;state <= WRITE_INDEX_LOW_END; end WRITE_INDEX_LOW_END:begin wait_cnt <= wait_cnt + 1; if(wait_cnt > 4) begin state <= WRITE_DATA_HIGH;lcd_data_out_ena<=0;end end WRITE_DATA_HIGH:begin LCD_RS <= 1; LCD_WR <= 0; lcd_data_out <= data_buf[15:8]; lcd_data_out_ena <= 1; wait_cnt <= 0; state <= WAIT_3; end WAIT_3:begin wait_cnt <= wait_cnt + 1; if(wait_cnt > 3) state <= WRITE_DATA_HIGH_CANCEL_WR; end WRITE_DATA_HIGH_CANCEL_WR:begin LCD_WR <= 1;wait_cnt <= 0; state <= WRITE_DATA_HIGH_END; end WRITE_DATA_HIGH_END:begin wait_cnt <= wait_cnt + 1; if(wait_cnt > 4) begin state <= WRITE_DATA_LOW;lcd_data_out_ena <= 0;end end WRITE_DATA_LOW:begin LCD_RS <= 1;LCD_WR <= 0;lcd_data_out <= data_buf[7:0]; lcd_data_out_ena <= 1;wait_cnt <= 0;state <= WAIT_4; end WAIT_4:begin wait_cnt <= wait_cnt + 1; if(wait_cnt > 3) state <= WRITE_DATA_LOW_CANCEL_WR; end WRITE_DATA_LOW_CANCEL_WR:begin LCD_WR <= 1; wait_cnt <= 0; state <= WRITE_DATA_LOW_END; end WRITE_DATA_LOW_END:begin wait_cnt <= wait_cnt + 1; if(wait_cnt > 4) begin state <= END_OPERATION; lcd_data_out_ena <= 0;end end END_OPERATION:begin state <= IDLE; ack_o <= 1'b0;end default:begin state <= IDLE;ack_o <= 1'b0;end endcase end//else end//always endmodule
7.4.3 MCU接口LCD控制器的验证
MCU接口LCD控制器设计已经通过FPGA验证,本书将在第20章中讲述如何在屏幕上显示图片、实时时钟。