目录
学习目标
成果展示
硬件知识
接口
并口时序
驱动时序
流程
RGB565
编辑 指令
0XD3
0X36
0X2A
0X2B
0X2C
0X2E
FSMC
外设接口
读写时序
寄存器
FSMC_BCRx
FSMC_BTRx
FSMC_BWTRx
硬件连接
代码
总结
今天我们要讲解的是有关LCD显示屏的知识,其实这部分知识之前就学习过了,但是因为没有LCD显示屏,就没有做实验,所以我们今天就当是来复习一下之前的知识点了。主要的内容就是关于LCD的原理,与学会使用LCD。
LCD显示屏
TFT-LCD 即薄膜晶体管液晶显示器。其英文全称为:Thin Film Transistor-Liquid Crystal Display。TFT-LCD 与无源 TN-LCD、STN-LCD 的简单矩阵不同,它在液晶显示屏的每一个像素上都设置有一个薄膜晶体管(TFT),可有效地克服非选通时的串扰,使显示液晶屏的静态特性与扫描线数无关,因此大大提高了图像质量。TFT-LCD 也被叫做真彩液晶显示器。
- LCD_CS:LCD片选信号
- LCD_WR:LCD写信号
- LCD_RD:LCD读信号
- DB[17:1]:16位双向数据线
- LCD_RST:硬复位LCD信号
- LCD_RS:命令/数据标志(0:命令,1:数据)
- BL_CTR:背光控制信号
- T_MISO/T_MOSI/T_PEN/T_CS/T_CLK,触摸屏接口信号
注意:DB1~DB8,DB10~DB17,总是按顺序连接MCU的D0~D15
模块的 8080 并口读/写的过程为:
- 先根据要写入/读取的数据类型,设置RS为高(数据)/低(命令),然后拉低片选,选中ILI9341,接着根据读数据/写数据置RD/WR为低。
然后:
- 读数据:在RD的上升沿,读取数据线上的数据(D[15:0]);
- 写数据:在WR的上升沿,使数据写入到ILI9341里面;
以下几个重点的时序值得我们去注意一下,因为与我们的读写操作有关:
- 读ID低电平脉宽(trdl)
- 读ID高电平脉宽(trdh)
- 读FM低电平脉宽(trdlfm)
- 读FM高电平脉宽(trdhfm)
- 写控制低电平脉宽(twrl)
- 写控制高电平脉宽(trwh)
注意:ID指LCD的ID号,FM指帧缓存,即:GRAM。
硬复位的话,我们是连接在复位按键上的,所以就省去了一个IO口;然后是初始化序列,这个一般是厂家提供的,不需要我们自己去写,然后设置一下坐标,就可以写/读GRAM指令了,这就是一个基本的驱动流程。
RGB565其实就是将一个16位数据的高5位编码为红色,后6位编码为绿色,低5位编码为蓝色。
比如:0XF800为红色(D15~D11五个位都是1显示红色,其它位都是0没有颜色)
ILI9341所有指令都是8位的(高8位无效),且参数除了读写GRAM的时候是16位,其它操作参数都是8位。
ILI9341的指令很多,我们重点讲解下面6条指令:0XD3 0X36 0X2B 0X2A 0X2C 0X2E。
0XD3指令:读ID4指令,用于读取LCD控制器的ID。因此,同一个代码,可以根据ID的不同,执行不同的LCD驱动初始化,以兼容不同的LCD屏幕。
第一个参数是无效的,然后第二个是0X,第三个返回93,第四个返回41,对于不同的型号,就会返回不同的值,来达到兼容的效果。
0X36指令:存储访问控制指令,控制ILI9341存储器的读写方向,即在连续写GRAM的时候,可以控制GRAM指针的增长方向,从而控制显示方式(读GRAM同此)。
列地址指令,在从左到右,从上到下(默认)的扫描方式下,该指令可用于设置横坐标(X坐标)
在默认扫描方式时,该指令用于设置X坐标,该指令带有4个参数,实际上是2个坐标值:SC和EC,即列地址的起始值和结束值,SC必须小于等于EC,且0<=SC/EC<=239(4.3寸屏幕不一样)。一般在设置X坐标的时候,我们只需带两个参数即可,也就是设置SC即可,因为如果EC没有变化,我们只需设置一次即可,从而提高速度。
0X2B指令:页地址设置指令,在从左到右,从上到下(默认)扫描方式下,该指令可用于设置纵坐标(Y坐标)
在默认扫描方式时,该指令用于设置 y 坐标,该指令带有 4 个参数,实际上是 2 个坐标值:SP 和 EP,即页地址的起始值和结束值,SP 必须小于等于 EP,且 0≤SP/EP≤319。一般在设置 y 坐标的时候,我们只需要带 2 个参数即可,也就是设置 SP 即可,因为如果 EP 没有变化,我们只需要设置一次即可,从而提高速度。
0X2C,该指令是写 GRAM 指令,在发送该指令之后,我们便可以往 LCD 的 GRAM 里面写入颜色数据了,该指令支持连续写(地址自动递增)。
从上表可知,在收到指令 0X2C 之后,数据有效位宽变为 16 位,我们可以连续写入 LCD GRAM 值,而 GRAM 的地址将根据 MY/MX/MV 设置的扫描方向进行自增。例如:假设设置的是从左到右,从上到下的扫描方式,那么设置好起始坐标(通过 SC,SP 设置)后,每写入一个颜色值,GRAM 地址将会自动自增 1(SC++),如果碰到 EC,则回到 SC,同时 SP++,一直到坐标:EC,EP 结束,其间无需再次设置的坐标,从而大大提高写入速度。
读GRAM指令,用于读取ILI9341的显存(GRAM),同时0X2C指令支持连续读(地址自动递增)。
该指令用于读取 GRAMILI9341 在收到该指令后,第一次输出的是 dummy 数据,也就是无效的数据,第二次开始,读取到的才是有效的 GRAM 数据(从坐标:SC,SP 开始),输出规律为:每个颜色分量占 8 个位,一次输出 2 个颜色分量。比如:第一次输出是 R1G1,随后的规律为:B1R2->G2B2->R3G3->B3R4->G4B4->R5G5... 以此类推。如果我们只需要读取一个点的颜色值,那么只需要接收到参数 3 即可,如果要连续读取(利用 GRAM地址自增,方法同上),那么就按照上述规律去接收颜色数据。
FSMC,即灵活的静态存储控制器,能够与同步或异步存储器和16位PC存储器卡连接,STM32的FSMC接口支持包括SRAM、NAND FLASH、NOR FLASHSH和PSRAM等存储器(不支持SD RAM,只有429、439支持,407、103不支持)。FSMC的框图如下图所示:
FSMC驱动外部SRAM时,外部SRAM的控制一般有:地址线(如A0~A25)、数据线(如D0~D15)、写信号(WE,即WR)、读信号(OE,即RD)、片选信号(CS),如果SRAM支持字节控制,还有UB/LB信号。
TFTLCD的信号包括:RS、D0~D15、WR、RD、CS、RST和BL等,其中真正在操作LCD的时候需要用到的只有:RS、D0~D15、WR、RD和CS。其操作时序和SRAM的控制完全类似,唯一不同的就是TFTLCD有RS信号,但是没有地址信号。
TFTLCD通过RS信号来决定传送的数据是数据还是命令,本质上可以理解为一个地址信号,比如我们把RS接在A0上面,那么当FSMC控制器写地址0的时候,会使得A0变为0,对TFTLCD来说,就是写命令,而FSMC控制器写地址1的时候,A0将会变为1,对于TFTLCD来说就是写数据了。这样,就把数据和命令区分开了,它们其实就是对应SRAM操作的两个连续地址。当然RS也可以接在其他地址线上,STM32F4把RS接在A6上面。
因此可以把TFTLCD当成一个SRAM来用(将TFTLCD的RS连接到地址线的时候可以当作SRAM),只不过这个SRAM有2个地址,这就是FSMC可以驱动LCD的原理。
STM32的FSMC支持8/16/32位数据宽度。FSMC的外部设备地址映像,STM32的FSMC将外部存储器划分为固定大小为256M字节的四个存储块。
这个我们来简单理解一下,8位相当于一个字节,而每次加8刚好能读出地址,但是16位就相当于两个字节,每次加2,所以需要除2才能读出正确读数。
FSMC_BCRx 和 FSMC_BTRx,组合成 BTCR[8]寄存器组,他们的对应关系如下:
- BTCR[0]对应 FSMC_BCR1,BTCR[1]对应 FSMC_BTR1
- BTCR[2]对应 FSMC_BCR2,BTCR[3]对应 FSMC_BTR2
- BTCR[4]对应 FSMC_BCR3,BTCR[5]对应 FSMC_BTR3
- BTCR[6]对应 FSMC_BCR4,BTCR[7]对应 FSMC_BTR4
FSMC_BWTRx 则组合成 BWTR[7],他们的对应关系如下:
- BWTR[0]对应 FSMC_BWTR1,BWTR[2]对应 FSMC_BWTR2,
- BWTR[4]对应 FSMC_BWTR3,BWTR[6]对应 FSMC_BWTR4,
BWTR[1]、BWTR[3]和 BWTR[5]保留,没有用到。
- LCD_BL(背光控制)对应 PB0;
- LCD_CS 对应 PG12 即 FSMC_NE4;
- LCD _RS 对应 PF12 即 FSMC_A6;
- LCD _WR 对应 PD5 即 FSMC_NWE;
- LCD _RD 对应 PD4 即 FSMC_NOE;
- LCD _D[15:0]则直接连接在 FSMC_D15~FSMC_D0;
代码太多了,就简单介绍一下FSMC的配置。
vu32 i=0;
GPIO_InitTypeDef GPIO_InitStructure;
FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
FSMC_NORSRAMTimingInitTypeDef readWriteTiming;
FSMC_NORSRAMTimingInitTypeDef writeTiming;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOD|RCC_AHB1Periph_GPIOE|RCC_AHB1Periph_GPIOF|RCC_AHB1Periph_GPIOG, ENABLE);//使能PD,PE,PF,PG时钟
RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC,ENABLE);//使能FSMC时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//PB15 推挽输出,控制背光
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(GPIOB, &GPIO_InitStructure);//初始化 //PB15 推挽输出,控制背光
GPIO_InitStructure.GPIO_Pin = (3<<0)|(3<<4)|(7<<8)|(3<<14);//PD0,1,4,5,8,9,10,14,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(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_12;//PF12,FSMC_A6
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(GPIOF, &GPIO_InitStructure);//初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//PF12,FSMC_A6
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);//PD0,AF12
GPIO_PinAFConfig(GPIOD,GPIO_PinSource1,GPIO_AF_FSMC);//PD1,AF12
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);//PD15,AF12
GPIO_PinAFConfig(GPIOE,GPIO_PinSource7,GPIO_AF_FSMC);//PE7,AF12
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);//PE15,AF12
GPIO_PinAFConfig(GPIOF,GPIO_PinSource12,GPIO_AF_FSMC);//PF12,AF12
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)模式A未用到
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; //模式A
writeTiming.FSMC_AddressSetupTime =3; //地址建立时间(ADDSET)为3个HCLK =18ns ,9
writeTiming.FSMC_AddressHoldTime = 0x00; //地址保持时间(A
writeTiming.FSMC_DataSetupTime = 2; //数据保存时间为6ns*3个HCLK=18ns,8
writeTiming.FSMC_BusTurnAroundDuration = 0x00;
writeTiming.FSMC_CLKDivision = 0x00;
writeTiming.FSMC_DataLatency = 0x00;
writeTiming.FSMC_AccessMode = FSMC_AccessMode_A; //模式A
FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM4;// 这里我们使用NE4 ,也就对应BTCR[6],[7]。
FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; // 不复用数据地址
FSMC_NORSRAMInitStructure.FSMC_MemoryType =FSMC_MemoryType_SRAM;// FSMC_MemoryType_SRAM; //SRAM
FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;//存储器数据宽度为16bit
FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode =FSMC_BurstAccessMode_Disable;// 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
这个东西,个人认为会用就行,原理部分稍稍了解一下,能熟练运用就行。