STM32: LCD显示

一、TFTLCD概述

1.1 TFTLCD

        TFT-LCD 即薄膜晶体管液晶显示器。其英文全称为:Thin Film Transistor-Liquid Crystal 
Display,该模块有如下特点 :

  • 2.4’、2.8’、3.5’、4.3’、7’,   5 种大小的屏幕可选;
  • 320×240 的分辨率(3.5’分辨率为:320*480,4.3’和 7’分辨率为:800*480)
  • 16 位真彩显示。
  • 自带触摸屏,可以用来作为控制输入。

        本文以2.8寸屏幕为例,进行介绍。2.8寸屏幕显示分辨率为320x240,接口为16位的80并口,自带触摸屏

  1.2 TFTLCD接口

         TFTLCD 模块采用 2*17 的 2.54 公排针与外部连接,接口定义如下

        如上图,可以看出,TFTLCD采用16位的并方式与外部连接,之所以不采用 8 位的方式,是因为彩屏的数据量比较大,尤其在显示图片的时候,如果用 8 位数据线,就会比 16 位方式慢一倍以上,我们当然希望速度越快越好,所以我们选择 16 位的接口。

        除了80并口数据线之外,该模块还有一些信号线

  •  RST:硬复位 TFTLCD
  •  CS:TFTLCD 片选信号
  •  RS:命令/数据标志(0,读写命令[控制寄存器];1,读写数据[数据寄存器])
  •  WR:向 TFTLCD 写入数据
  •  RD:从 TFTLCD 读取数据
  •  D[15:0]:16 位双向数据线

        TFTLCD 模块的驱动芯片有很多种类,本文以ILI9341控制器为例,进行介绍。ILI9341 液晶控制器自带显存,其显存总大小为 172800(240*320*18/8)字节,即 18 位模式(26万色)下的显存量。在 16 位模式下,ILI9341 采用 RGB565 格式存储颜色数据,此时 ILI9341的 18 位数据线与 MCU 的 16 位数据线以及 LCD GRAM 的对应关系如图所示:

        从图中可以看出,ILI9341 在16 位模式下面,数据线有用的是:D17~D13 和 D11~D1,D0和 D12 没有用到,实际上在我们 LCD 模块里面,ILI9341 的D0 和 D12 压根就没有引出来,这样,ILI9341 的 D17~D13 和 D11~D1 对应 MCU 的 D15~D0

        这样 MCU 的 16 位数据,最低 5 位代表蓝色,中间 6 位为绿色,最高 5 位为红色。数值越
大,表示该颜色越深。另外,特别注意 ILI9341 所有的指令都是 8 位的(高 8 位无效),且参数除了读写 GRAM 的时候是 16 位,其他操作参数,都是 8 位的

1.3 ILI9341指令

 1.3.1 0XD3指令

        指令0XD3个是读 ID4指令,用于读取 LCD 控制器的 ID,该指令如下表所示:

         从上表可以看出,0XD3 指令后面跟了 4 个参数,最后 2 个参数,读出来是 0X930X41,刚好是我们控制器 ILI9341 的数字部分,从而,通过该指令,即可判别所用的 LCD 驱动器是什么型号,这样,我们的代码,就可以根据控制器的型号去执行对应驱动 IC 的初始化代码,从而兼容不同驱动 IC 的屏,使得一个代码支持多款 LCD。 

 1.3.2 0X36指令

        0X36指令是存储访问控制指令可以控制 ILI9341 存储器的读写方向,简单的说,就是在连续写 GRAM 的时候,可以控制 GRAM 指针的增长方向,从而控制显示方式(读 GRAM 也是一样)。该指令如下表:

        从上表可以看出,0X36 指令后面,紧跟一个参数,这里我们主要关注:MY、MX、MV这三个位,通过这三个位的设置,我们可以控制整个 ILI9341 的全部扫描方向,如下表所示:

        这样,我们在利用 ILI9341 显示内容的时候,就有很大灵活性了。

1.3.3 0X2A指令

        0X2A是列地址设置指令,在从左到右,从上到下的扫描方式(默认)下面,该指令用于设置横坐标(x 坐标),该指令如表: 

         在默认扫描方式时,该指令用于设置 x 坐标该指令带有 4 个参数,实际上是 2 个坐标值:SC 和 EC,即列地址的起始值和结束值SC 必须小于等于 EC,且 0≤SC/EC≤239。一般在设置 x 坐标的时候,我们只需要带 2 个参数即可,也就是设置 SC 即可,因为如果 EC 没有变化,我们需要设置一次即可(在初始化 ILI9341 的时候设置),从而提高速度。

1.3.4 0X2B指令

        与 0X2A 指令类似,指令:0X2B,是页地址设置指令在从左到右,从上到下的扫描方式(默认)下面,该指令用于设置纵坐标(y 坐标)

         在默认扫描方式时,该指令用于设置 y 坐标该指令带有 4 个参数,实际上是 2 个坐标值:SP 和 EP,即页地址的起始值和结束值SP 必须小于等于 EP,且 0≤SP/EP≤319,一般在设置
y 坐标的时候,我们只需要带 2 个参数即可,也就是设置 SP 即可,因为如果 EP 没有变化,我
们只需要设置一次即可(在初始化 ILI9341 的时候设置),从而提高速度。

1.3.5 0X2C指令

        0X2C指令是写 GRAM 指令,在发送该指令之后,我们便可以往 LCD的 GRAM 里面写入颜色数据了,该指令支持连续写,指令描述如下表所示: 

         从上表可知,在收到指令 0X2C 之后数据有效位宽变为 16 位我们可以连续写入 LCD 
GRAM 值
而 GRAM 的地址将根据 MY/MX/MV 设置的扫描方向进行自增。例如:假设设置的是从左到右,从上到下的扫描方式,那么设置好起始坐标(通过 SC,SP 设置)后每写入一个颜色值,GRAM 地址将会自动自增 1(SC++)如果碰到 EC,则回到 SC,同时 SP++,一直到坐标:EC,EP 结束,其间无需再次设置的坐标,从而大大提高写入速度。 

1.3.6 0X2E指令

         0X2E指令是读 GRAM 指令用于读取 ILI9341 的显存(GRAM),如下表所示:

         该指令用于读取 GRAM,如上表所示,ILI9341 在收到该指令后,第一次输出的是dummy 数据,也就是无效的数据,第二次开始,读取到的才是有效的 GRAM 数据(从坐标:SC,SP 开始),输出规律为:每个颜色分量占 8 个位,一次输出 2 个颜色分量。比如:第一次输出是 R1G1,随后的规律为:B1R2-G2B2-R3G3-B3R4-G4B4-R5G5... 以此类推。如果我们只需要读取一个点的颜色值,那么只需要接收到参数 3 即可,如果要连续读取(利用 GRAM地址自增,方法同上),那么就按照上述规律去接收颜色数据。

1.4 TFTLCD 使用流程:

        一般 TFTLCD 模块的使用流程如下图:

        任何 LCD,使用流程都可以简单的用以上流程图表示。其中硬复位和初始化序列,只需要执行一次即可。而画点流程就是:设置坐标-->写 GRAM 指令-->写入颜色数据,然后在 LCD 上
面,我们就可以看到对应的点显示我们写入的颜色了
。读点流程为:设置坐标-->读 GRAM 指令
-->读取颜色数据,这样就可以获取到对应点的颜色数据了。

1.5 TFTLCD 显示设置步骤

  • 设置 STM32F4 与 TFTLCD 模块相连接的 IO

        这一步,先将我们与 TFTLCD 模块相连的 IO 口进行初始化,以便驱动 LCD。这里我们用到的是 FSMC。

  • 初始化 TFTLCD 模块。 

        初始化步骤如1.4图中所示的初始化序列,初始化序列,就是向 LCD 控制器写入一系列的设置值(比如伽马校准),这些初始化序列一般 LCD 供应商会提供给客户,我们直接使用这些序列即可,不需要深入研究。在初始化之后,LCD 才可以正常使用。

  • 通过函数将字符和数字显示到 TFTLCD 模块上

        这一步则通过1.4左侧的流程,即:设置坐标-->写 GRAM 指令-->写 GRAM 来实现,但是这个步骤,只是一个点的处理,我们要显示字符/数字,就必须要多次使用这个步骤,从而达到显示字符/数字的目的。

二、FSMC概述

        FSMC,即灵活的静态存储控制器能够与同步或异步存储器和 16 位 PC 存储器卡连接,STM32F4 的FSMC 接口支持包括 SRAMNAND FLASHNOR FLASHPSRAM 等存储器。FSMC 的框图如下图:

         从图中可以看出,FSMC将外部设备分为两类,NOR/PSRAM 设备、NAND/PC 卡设备。LCD是被视为SRAM来控制的

        FSMC驱动外部SRAM时,外部SRAM的控制一般有:地址线(如A0~A25)数据线(如D0~D15)写信号(WE,即WR)读信号(OE,即RD)片选信号(CS),如果SRAM支持字节控制,那么还有UB/LB信号。

        在介绍TFTLCD的时候,介绍过其接口,有RSD0~D15WRRDCS,其操作时序和SRAM的控制完全类似,唯一不同的是TFTLCK有RS信号,而没有地址信号

        TFTLCD 通过 RS 信号来决定传送的数据是数据还是命令,本质上可以理解为一个地址信
号,比如我们把 RS 接在 A0 上面,那么当 FSMC 控制器写地址 0 的时候会使得 A0 变为 0,对 TFTLCD 来说,就是写命令。而 FSMC 写地址1 的时候,A0 将会变为 1,对 TFTLCD 来说,就是写数据了这样,就把数据和命令区分开了,他们其实就是对应 SRAM 操作的两个连续地址。

        因此,可以把TFTLCD当成一个SRAM来用,只不过这个SRAM只有两个地址,这就是FSMC驱动LDC的原理。

        STM32的FSMC支持8、16、32位数据宽度,我们此处用的是16位,因为LCD是16位的宽度。FSMC将外部存储器划分为固定大小为256M字节的四个存储块,如下图所示:

         从上图可以看出,FSMC 总共管理 1GB 空间,拥有 4 个存储块(Bank),我们用到的是SRAM也就是块1。FSMC 存储块 1(Bank1)被分为 4 个区,每个区管理 64M 字节空间,每个区都有独立的寄存器对所连接的存储器进行配置。Bank1 的 256M 字节空间由 28 根地址线(HADDR[27:0])寻址。

         这里 HADDR 是内部 AHB 地址总线,其中 HADDR[25:0] 来自外部存储器地址FSMC_A[25:0],而 HADDR[26:27]对4 个区进行寻址,如下表所示

当 Bank1 接的是 16 位宽度存储器的时候:HADDR[25:1]-->FSMC_A[24:0]

当 Bank1 接的是 8 位宽度存储器的时候:HADDR[25:0]--> FSMC_A[25:0]

不论外部接 8 位/16 位宽设备,FSMC_A[0]永远接在外部设备地址 A[0]

        此处的数据位宽度很重要。此处的HADDR是按照字节进行寻址的,而我们外接的存储器不一定是按照8位字节进行寻址的,因此,根据存储器数据宽度不同,实际向存储器发送的地址也将不同。如下图:

         当存储器宽度为8位的时候,那门正好和HADDR按字节寻址的规则对应起来,也就是8位一个字节一个地址,HADDR地址A0~A25都被使用到了。存储器可以有的最大容量是,64MB(A0~A25地址最多表示64MB地址)*8bit = 512Mb。

        但是,当存储器数据宽度是16位的时候,这时,对于同样的512Mb的存储空间,其需要的地址为512Mb/16bit = 32MB,而A0~A25是可以表示64MB的地址的,因此,当数据位宽度为16位的时候,真正使用到的地址位是HADDR[25:1],即不用HADDR[0]位,这样正好寻址32Mb,但是每个地址上有16位数据,因此存储器容量还是64MB/2 * 16 = 512Mb。真正的操作时,会将地址位向右移动一位,即 HADDR[1]对应A[0],而HADDR[25]对应A[24]。这样也保证了,即使数据位宽度为16位,FSMC_A[0]接的还是外部设备地址A[0]。

        以A12接RS线来分析:A12接RS线,则指令操作时A12应为0,此时FSMC_A地址二进制表示为:0 1111 1111 1111,也就是说,HADDR[1] 对应A[0],而HADDR[0]不用,为0,则其地址的二进制表示为:01 1111 1111 1110转换为16进制就是0X1FFE。而RS为1,也就是下一个地址,表示的是数据操作,这时,只需给之前0X1FFE+0X02,就可以了,之所以加2,是因为存储器是16位的,其下一个地址横跨了16位数据,而HADDR是以8位寻址的,因此其地址要加2。也就是0x2000

二、软件设计

2.1 LCD.H文件:

/
//LCD硬件参数
typedef struct  
{										    
	u16 width;			//LCD 宽度 320
	u16 height;			//LCD 高度 640
	u16 id;				//LCD ID
	u8  dir;			//横屏还是竖屏: 0 横批,1 竖屏
	u16	 wramcmd;		//开始写GRAM指令
	u16  setxcmd;		//设置x坐标指令
	u16  setycmd;		//设置y坐标指令
}_lcd_dev;

//LCD参数
extern _lcd_dev lcddev;	//管理LCD重要参数
//LCD 的画笔颜色和背景色	   
extern u16  POINT_COLOR; 
extern u16  BACK_COLOR;  

//-----------------LCD背光端口定义---------------- 
#define	LCD_BACK PFout(10)  //LCD背光    	PF10 	


// A12(RS) 作为数据命令区分线
// RS线是用来决定传输的是数据还是命令。
// 由于此处FSMC驱动LCD使用的NOR/SRAM的Bank1的sector4,其基地址为0x6C000000
// 而A12为数据命令区分线,其表示的偏移量为0x00001FFE,
// 分析:0X00001FFE转为二进制为1 1111 1111 1110,16位地址时,地址要右移一位,
// 即为0 1111 1111 1111;这样正好第12位A12为0。
//      但是,如果16位地址再加1(对应到8位地址就是加2,即0x00001FFE+0x02),
// 即为0x00002000,即1 0000 0000 0000
//      此时,A12就是1,即实现了对RS的0和1控制。
#define  CMD_BASE     ((u32)(0x6C000000 | 0x00001FFE))     //0x00001FFE为相对于基地址的偏移量
#define  DATA_BASE    ((u32)(0x6C000000 | 0x00002000))

#define LCD_CMD       ( * (u16 *) CMD_BASE )      //此时RS==0     
#define LCD_DATA      ( * (u16 *) DATA_BASE)      //此时RS==1

//扫描方向定义
#define  L2R_U2D  0 //从左到右,从上到下
#define  L2R_D2U  1 //从左到右,从下到上
#define  R2L_U2D  2 //从右到左,从上到下
#define  R2L_D2U  3 //从右到左,从下到上

#define  U2D_L2R  4 // 从上到下,从左到右
#define  U2D_R2L  5 // 从下到上,从左到右
#define  D2U_L2R  6 // 从上到下,从右到左
#define  D2U_R2L  7 // 从下到上,从右到左


// 颜色定义
#define  WHITE        0xFFFF
#define  BLACK        0x0000	
#define  RED          0xF800
#define  GREEN        0x07E0
#define  BLUE         0x001F 
#define  BRED         0XF81F
#define  GRED 			  0XFFE0
#define  GBLUE			  0X07FF
#define  BROWN 			  0XBC40  
#define  BRRED 			  0XFC07  
#define  GRAY  			  0X8430  
#define  MAGENTA      0xF81F
#define  CYAN         0x7FFF
#define  YELLOW       0xFFE0

void LCD_WriteReg(u16 LCD_Reg, u16 LCD_Value);
u16 LCD_ReadReg(u16 LCD_Reg);
void LCD_WriteGRAM(void);

void LCD_Init(void);				                //初始化
void LCD_DisplayOn(void);							//开显示
void LCD_DisplayOff(void);							//关显示
void LCD_Clear(u16 Color);	 						//清屏
void LCD_SetCursor(u16 Xpos, u16 Ypos);				//设置光标
void LCD_DrawPoint(u16 x,u16 y);					//画点
void LCD_Color_DrawPoint(u16 x,u16 y,u16 color);	//颜色画点
u16  LCD_GetPoint(u16 x,u16 y); 					//读点

void LCD_Open_Window(u16 X0,u16 Y0,u16 width,u16 height); 	
void Set_Scan_Direction(u8 direction);					 
void Set_Display_Mode(u8 mode);						 

void LCD_Fill_onecolor(u16 sx,u16 sy,u16 ex,u16 ey,u16 color);		//填充单个颜色
void LCD_Draw_Picture(u16 sx,u16 sy,u16 ex,u16 ey,u16 *color);		//填充指定颜色
void LCD_DisplayChar(u16 x,u16 y,u8 word,u8 size);					//显示一个字符
void LCD_DisplayString(u16 x,u16 y,u8 size,u8 *p);		            //显示一个12/16/24字体字符串
void LCD_DisplayString_color(u16 x,u16 y,u8 size,u8 *p,u16 brushcolor,u16 backcolor); //显示一个12/16/24字体自定义颜色的字符串
void LCD_DisplayNum(u16 x,u16 y,u32 num,u8 len,u8 size,u8 mode);	//显示数字
void LCD_DisplayNum_color(u16 x,u16 y,u32 num,u8 len,u8 size,u8 mode,u16 brushcolor,u16 backcolor); //显示自定义颜色的数字  	   																			 

2.2 LCD.C文件介绍:

        硬件连接中,我们使用FSMC的Bank1的第四区来控制TFTLCD,程序的流程如下所示:

  • 初始化TFTLCD对应的GPIO口,即初始化FSMC
  • TFTLCD初始化,包含初始化序列
  • 编写TFTLCD显示函数 

2.1.1 FSMC初始化:

//配置FSMC
void LCD_FSMC_Config()
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	FSMC_NORSRAMInitTypeDef  FSMC_NORSRAMInitStructure;
    FSMC_NORSRAMTimingInitTypeDef  readWriteTiming; 
	FSMC_NORSRAMTimingInitTypeDef  writeTiming;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD|RCC_AHB1Periph_GPIOE|RCC_AHB1Periph_GPIOF|RCC_AHB1Periph_GPIOG, ENABLE);
  RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC,ENABLE);//使能FSMC时钟  
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;        //PF10 推挽输出,控制背光
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;     //输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;    //推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;      //上拉
  GPIO_Init(GPIOF, &GPIO_InitStructure);            //初始化PF10 
	
  GPIO_InitStructure.GPIO_Pin = (3<<0)|(3<<4)|(7<<8)|(3<<14); 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;      //复用输出
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;    //推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;      //上拉
  GPIO_Init(GPIOD, &GPIO_InitStructure);            //初始化  
	
  GPIO_InitStructure.GPIO_Pin = (0X1FF<<7);         //PE7~15,AF OUT
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;      //复用输出
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;    //推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;      //上拉
  GPIO_Init(GPIOE, &GPIO_InitStructure);            //初始化  

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;         //PG2
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;      //复用输出
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;    //推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;      //上拉
  GPIO_Init(GPIOG, &GPIO_InitStructure);            //初始化  

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;        //PG12
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;      //复用输出
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;    //推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;      //上拉
  GPIO_Init(GPIOG, &GPIO_InitStructure);            //初始化 

  GPIO_PinAFConfig(GPIOD,GPIO_PinSource0,GPIO_AF_FSMC); 
  GPIO_PinAFConfig(GPIOD,GPIO_PinSource1,GPIO_AF_FSMC); 
  GPIO_PinAFConfig(GPIOD,GPIO_PinSource4,GPIO_AF_FSMC);
  GPIO_PinAFConfig(GPIOD,GPIO_PinSource5,GPIO_AF_FSMC); 
  GPIO_PinAFConfig(GPIOD,GPIO_PinSource8,GPIO_AF_FSMC); 
  GPIO_PinAFConfig(GPIOD,GPIO_PinSource9,GPIO_AF_FSMC);
  GPIO_PinAFConfig(GPIOD,GPIO_PinSource10,GPIO_AF_FSMC);
  GPIO_PinAFConfig(GPIOD,GPIO_PinSource14,GPIO_AF_FSMC);
  GPIO_PinAFConfig(GPIOD,GPIO_PinSource15,GPIO_AF_FSMC); 
 
  GPIO_PinAFConfig(GPIOE,GPIO_PinSource7,GPIO_AF_FSMC);  
  GPIO_PinAFConfig(GPIOE,GPIO_PinSource8,GPIO_AF_FSMC);
  GPIO_PinAFConfig(GPIOE,GPIO_PinSource9,GPIO_AF_FSMC);
  GPIO_PinAFConfig(GPIOE,GPIO_PinSource10,GPIO_AF_FSMC);
  GPIO_PinAFConfig(GPIOE,GPIO_PinSource11,GPIO_AF_FSMC);
  GPIO_PinAFConfig(GPIOE,GPIO_PinSource12,GPIO_AF_FSMC);
  GPIO_PinAFConfig(GPIOE,GPIO_PinSource13,GPIO_AF_FSMC);
  GPIO_PinAFConfig(GPIOE,GPIO_PinSource14,GPIO_AF_FSMC);
  GPIO_PinAFConfig(GPIOE,GPIO_PinSource15,GPIO_AF_FSMC); 
 
  GPIO_PinAFConfig(GPIOG,GPIO_PinSource2,GPIO_AF_FSMC); 
  GPIO_PinAFConfig(GPIOG,GPIO_PinSource12,GPIO_AF_FSMC);   

  readWriteTiming.FSMC_AddressSetupTime = 0XF;	 //地址建立时间(ADDSET) 16个HCLK 1/168M=6ns*16=96ns	
  readWriteTiming.FSMC_AddressHoldTime = 0x00;	 //地址保持时间(ADDHLD)
  readWriteTiming.FSMC_DataSetupTime = 60;			 //数据保存时间 60个HCLK	= 6*60=360ns
  readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
  readWriteTiming.FSMC_CLKDivision = 0x00;
  readWriteTiming.FSMC_DataLatency = 0x00;
  readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A;	 
    
	writeTiming.FSMC_AddressSetupTime =8;	     //地址建立时间(ADDSET)9个HCLK =54ns 
  writeTiming.FSMC_AddressHoldTime = 0x00;	 //地址保持时间 
  writeTiming.FSMC_DataSetupTime = 7;		     //数据保存时间 6ns*9个HCLK=54ns
  writeTiming.FSMC_BusTurnAroundDuration = 0x00;
  writeTiming.FSMC_CLKDivision = 0x00;
  writeTiming.FSMC_DataLatency = 0x00;
  writeTiming.FSMC_AccessMode = FSMC_AccessMode_A;	 

  FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM4;  
  FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; 
  FSMC_NORSRAMInitStructure.FSMC_MemoryType =FSMC_MemoryType_SRAM;
  FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;   //数据宽度为16bit   
  FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode =FSMC_BurstAccessMode_Disable;
  FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
	FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait=FSMC_AsynchronousWait_Disable; 
  FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;   
  FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;  
  FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;	 //写使能
  FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;   
  FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Enable;      //读写使用不同的时序
  FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable; 
  FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &readWriteTiming;     //读写时序
  FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &writeTiming;             //写时序
  FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure);  //初始化FSMC
  FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE);  //使能Bank1			 
  delay_ms(50); 
}

2.1.2  设置LCD重要参数

        设置_lcd_dev结构体:

/****************************************************************************
* 名    称: void Set_Display_Mode(u8 mode)
* 功    能:设置LCD显示方向
* 入口参数:mode: 0,竖屏
                  1,横屏
* 返回参数:无
* 说    明:
****************************************************************************/
void Set_Display_Mode(u8 mode)
{
	if(mode==0)		  //竖屏
	{
		lcddev.dir=0;	

        if(lcddev.id==0X9341)
		{
			lcddev.wramcmd=0X2C;  //GRAM的指令 
			lcddev.setxcmd=0X2A;  //写X坐标指令
			lcddev.setycmd=0X2B;  //写Y坐标指令
		    lcddev.width=240;     //设置宽度240
		    lcddev.height=320;	  //设置高度320 		
		}	
		else if(lcddev.id==0X1963)
		{
			lcddev.wramcmd=0X2C;	 
			lcddev.setxcmd=0X2B;	 
		    lcddev.setycmd=0X2A;	 
			lcddev.width=480;		 
			lcddev.height=800;		  
		}
	}
	else 				  //横屏
	{	  				
        lcddev.dir=1;
		
		if(lcddev.id==0X9341)
		{
			lcddev.wramcmd=0X2C;
			lcddev.setxcmd=0X2A;
			lcddev.setycmd=0X2B; 
		    lcddev.width=240;
		    lcddev.height=320;					
		}	
		else if(lcddev.id==0X1963)
		{
			lcddev.wramcmd=0X2C;	 //GRAM的指令 
			lcddev.setxcmd=0X2B;	 //写X坐标指令
		    lcddev.setycmd=0X2A;	 //写Y坐标指令
			lcddev.width=480;		 //设置宽度480
			lcddev.height=800;		 //设置高度800  
		}
	} 
	Set_Scan_Direction(L2R_U2D);	//设置扫描方向   从左到右,从下到上
}	

2.1.3 写寄存器值

/****************************************************************************
* 名    称: void LCD_WriteReg(u16 LCD_Reg, u16 LCD_Value)
* 功    能:LCD写寄存器
* 入口参数:LCD_Reg: 寄存器地址
*           LCD_RegValue: 要写入的数据
* 返回参数:无
* 说    明:       
****************************************************************************/				   
void LCD_WriteReg(u16 LCD_Reg, u16 LCD_Value)
{	
	LCD_CMD = LCD_Reg;		 //写入要写的寄存器序号	 
	LCD_DATA = LCD_Value;  //向寄存器写入的数据	    		 
}

2.1.4 写GRAM指令

//开始写GRAM
//0X2c为写GRAM指令。在发送完该指令之后,就可以往LCD的GRAM写入颜色数据了。
void LCD_WriteGRAM(void)
{
 	LCD_CMD=lcddev.wramcmd;	  // 0X2C
}

2.1.5 设置光标位置

/****************************************************************************
* 名    称: void LCD_SetCursor(u16 Xaddr, u16 Yaddr)
* 功    能:设置光标位置
* 入口参数:x:x坐标
            y:y坐标
* 返回参数:无
* 说    明:     
****************************************************************************/
void LCD_SetCursor(u16 Xaddr, u16 Yaddr)
{	 		    
			LCD_CMD=lcddev.setxcmd; 
			LCD_DATA=(Xaddr>>8); 
			LCD_DATA=(Xaddr&0XFF);	 
			LCD_CMD=lcddev.setycmd; 
			LCD_DATA=(Yaddr>>8); 
			LCD_DATA=(Yaddr&0XFF);
} 

2.1.6 画点

/****************************************************************************
* 名    称: void LCD_DrawPoint(u16 x,u16 y)
* 功    能:画点(在该点写入画笔的颜色)
* 入口参数:x:x坐标
            y:y坐标
* 返回参数:无
* 说    明RUSH_COLOR:此点的颜色值
****************************************************************************/
void LCD_DrawPoint(u16 x,u16 y)
{
	LCD_SetCursor(x,y);		    //设置光标位置 
	LCD_WriteGRAM();	        //开始写入GRAM
	LCD_DATA=POINT_COLOR; 
}
/****************************************************************************
* 名    称: void LCD_Color_DrawPoint(u16 x,u16 y,u16 color)
* 功    能:在设置的坐标处画相应颜色(在该点写入自定义颜色)
* 入口参数:x:x坐标
            y:y坐标
            color 此点的颜色值
* 返回参数:无
* 说    明:color:写入此点的颜色值   GUI调用该函数
****************************************************************************/
void LCD_Color_DrawPoint(u16 x,u16 y,u16 color)
{	       
	 LCD_SetCursor(x,y);		    //设置光标位置 
	 LCD_WriteGRAM();	        //开始写入GRAM
	 LCD_DATA=color;  
}	

2.1.7 清屏函数

/****************************************************************************
* 名    称: void LCD_Clear(u16 color)
* 功    能:清屏函数
* 入口参数:color: 要清屏的填充色
* 返回参数:无
* 说    明:
****************************************************************************/
void LCD_Clear(u16 color)
{
	u32 i=0;      
	u32 pointnum=0;
	
	pointnum=lcddev.width*lcddev.height; 	 //得到LCD总点数
	LCD_SetCursor(0x00,0x00);	       //设置光标位置 
	LCD_WriteGRAM();     		 //开始写入GRAM	 	  
	for(i=0;i<pointnum;i++)
	{
		LCD_DATA=color;	   
	}
}

2.1.8 读取某点颜色

/****************************************************************************
* 名    称: u16 LCD_GetPoint(u16 x,u16 y)
* 功    能:读取某点的颜色值	
* 入口参数:x:x坐标
            y:y坐标
* 返回参数:此点的颜色
* 说    明:     
****************************************************************************/
u16 LCD_GetPoint(u16 x,u16 y)
{
 	vu16 r=0,g=0,b=0;
   
	LCD_SetCursor(x,y);	
	
	LCD_CMD=0X2E;   //9341与1963读GRAM指令一样 
	r=LCD_DATA; 	
	
 	if(lcddev.id==0X1963)
		 return r;		   //1963直接读出来就是16位颜色值
	
	else	 //其他驱动就是9341
 	{
		 lcdm_delay(2);	  
		 b=LCD_DATA;    //9341要读2次
		 g=r&0XFF;		  //9341第一次读取的是RG的值,R在前,G在后,各占8位
		 g<<=8;
		 return (((r>>11)<<11)|((g>>10)<<5)|(b>>11)); //9341需公式转换
	} 		  	
}	

三、结束

        工程源码:https://download.csdn.net/download/sssxlxwbwz/85205397​​​​​​​

  • 22
    点赞
  • 220
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值