一、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 个参数,读出来是 0X93 和 0X41,刚好是我们控制器 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。
? ? ? ? 初始化步骤如1.4图中所示的初始化序列,初始化序列,就是向 LCD 控制器写入一系列的设置值(比如伽马校准),这些初始化序列一般 LCD 供应商会提供给客户,我们直接使用这些序列即可,不需要深入研究。在初始化之后,LCD 才可以正常使用。
????????这一步则通过1.4左侧的流程,即:设置坐标-->写 GRAM 指令-->写 GRAM 来实现,但是这个步骤,只是一个点的处理,我们要显示字符/数字,就必须要多次使用这个步骤,从而达到显示字符/数字的目的。
二、FSMC概述
????????FSMC,即灵活的静态存储控制器,能够与同步或异步存储器和 16 位 PC 存储器卡连接,STM32F4 的FSMC 接口支持包括 SRAM、NAND FLASH、NOR FLASH 和 PSRAM 等存储器。FSMC 的框图如下图:
? ? ? ? ?从图中可以看出,FSMC将外部设备分为两类,NOR/PSRAM 设备、NAND/PC 卡设备。LCD是被视为SRAM来控制的。
????????FSMC驱动外部SRAM时,外部SRAM的控制一般有:地址线(如A0~A25)、数据线(如D0~D15)、写信号(WE,即WR)、读信号(OE,即RD)、片选信号(CS),如果SRAM支持字节控制,那么还有UB/LB信号。
? ? ? ? 在介绍TFTLCD的时候,介绍过其接口,有RS、D0~D15、WR、RD、CS,其操作时序和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???????
|