当前位置:网站首页>STM32: LCD显示
STM32: LCD显示
2022-04-23 18:27:00 【溪午闻璐】
一、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。
- 初始化 TFTLCD 模块。
初始化步骤如1.4图中所示的初始化序列,初始化序列,就是向 LCD 控制器写入一系列的设置值(比如伽马校准),这些初始化序列一般 LCD 供应商会提供给客户,我们直接使用这些序列即可,不需要深入研究。在初始化之后,LCD 才可以正常使用。
- 通过函数将字符和数字显示到 TFTLCD 模块上
这一步则通过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
版权声明
本文为[溪午闻璐]所创,转载请带上原文链接,感谢
https://blog.csdn.net/sssxlxwbwz/article/details/124306195
边栏推荐
- Robocode Tutorial 4 - robocode's game physics
- Daily CISSP certification common mistakes (April 13, 2022)
- Pointers in rust: box, RC, cell, refcell
- Crawler for querying nicknames and avatars based on qqwebapi
- Cygwin64 right click to add menu, and open cygwin64 here
- In shell programming, the shell file with relative path is referenced
- PowerDesigner various font settings; Preview font setting; SQL font settings
- Log4j2 cross thread print traceid
- MySQL auto start settings start with systemctl start mysqld
- Daily CISSP certification common mistakes (April 19, 2022)
猜你喜欢
Promote QT default control to custom control
From source code to executable file
解决报错max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
logstash 7. There is a time problem in X. the difference between @ timestamp and local time is 8 hours
Jeecg boot microservice architecture
Win1远程出现“这可能是由于credssp加密oracle修正”解决办法
Nodejs安装
Creation and use of QT dynamic link library
MySQL auto start settings start with systemctl start mysqld
WiFi ap6212 driver transplantation and debugging analysis technical notes
随机推荐
kettle庖丁解牛第17篇之文本文件输出
Domestic GD chip can filter
In shell programming, the shell file with relative path is referenced
【ACM】376. Swing sequence
Daily CISSP certification common mistakes (April 18, 2022)
Test post and login function
Realization of consumer gray scale
C language to achieve 2048 small game direction merging logic
硬核解析Promise對象(這七個必會的常用API和七個關鍵問題你都了解嗎?)
Nodejs安装
Pyppeter crawler
【ACM】509. Fibonacci number (DP Trilogy)
Using transmittablethreadlocal to realize parameter cross thread transmission
ESP32 LVGL8. 1 - anim animation (anim 16)
Analysez l'objet promise avec le noyau dur (Connaissez - vous les sept API communes obligatoires et les sept questions clés?)
14个py小游戏源代码分享第二弹
CANopen usage method and main parameters of object dictionary
WIN1 remote "this may be due to credssp encryption Oracle correction" solution
Robocode Tutorial 4 - robocode's game physics
Refcell in rust