当前位置: 首页 > news >正文

(42)STM32——LCD显示屏实验笔记

目录

学习目标

成果展示 

硬件知识

接口

并口时序

驱动时序

流程

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为低。

        然后:

  1. 读数据:在RD的上升沿,读取数据线上的数据(D[15:0]);
  2. 写数据:在WR的上升沿,使数据写入到ILI9341里面;

驱动时序

以下几个重点的时序值得我们去注意一下,因为与我们的读写操作有关:

  • 读ID低电平脉宽(trdl)
  • 读ID高电平脉宽(trdh)
  • 读FM低电平脉宽(trdlfm)
  • 读FM高电平脉宽(trdhfm)
  • 写控制低电平脉宽(twrl)
  • 写控制高电平脉宽(trwh)

注意:ID指LCD的ID号,FM指帧缓存,即:GRAM。

流程

        硬复位的话,我们是连接在复位按键上的,所以就省去了一个IO口;然后是初始化序列,这个一般是厂家提供的,不需要我们自己去写,然后设置一下坐标,就可以写/读GRAM指令了,这就是一个基本的驱动流程。

RGB565

        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

        0XD3指令:读ID4指令,用于读取LCD控制器的ID。因此,同一个代码,可以根据ID的不同,执行不同的LCD驱动初始化,以兼容不同的LCD屏幕。

        第一个参数是无效的,然后第二个是0X,第三个返回93,第四个返回41,对于不同的型号,就会返回不同的值,来达到兼容的效果。

0X36

        0X36指令:存储访问控制指令,控制ILI9341存储器的读写方向,即在连续写GRAM的时候,可以控制GRAM指针的增长方向,从而控制显示方式(读GRAM同此)。 

0X2A

        列地址指令,在从左到右,从上到下(默认)的扫描方式下,该指令可用于设置横坐标(X坐标)

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

0X2B

        0X2B指令:页地址设置指令,在从左到右,从上到下(默认)扫描方式下,该指令可用于设置纵坐标(Y坐标)

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

0X2C

        0X2C,该指令是写 GRAM 指令,在发送该指令之后,我们便可以往 LCD 的 GRAM 里面写入颜色数据了,该指令支持连续写(地址自动递增)。

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

0X2E

        读GRAM指令,用于读取ILI9341的显存(GRAM),同时0X2C指令支持连续读(地址自动递增)。

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

FSMC

        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

FSMC_BWTRx

FSMC_BCRx 和 FSMC_BTRx,组合成 BTCR[8]寄存器组,他们的对应关系如下:

  1. BTCR[0]对应 FSMC_BCR1,BTCR[1]对应 FSMC_BTR1
  2. BTCR[2]对应 FSMC_BCR2,BTCR[3]对应 FSMC_BTR2
  3. BTCR[4]对应 FSMC_BCR3,BTCR[5]对应 FSMC_BTR3
  4. BTCR[6]对应 FSMC_BCR4,BTCR[7]对应 FSMC_BTR4

FSMC_BWTRx 则组合成 BWTR[7],他们的对应关系如下:

  1. BWTR[0]对应 FSMC_BWTR1,BWTR[2]对应 FSMC_BWTR2,
  2. 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 

总结 

         这个东西,个人认为会用就行,原理部分稍稍了解一下,能熟练运用就行。

相关文章:

  • 全国青少年软件编程等级考试标准Python(1-6级)
  • Java语法基本概念
  • 一文搞懂CSS盒子模型
  • 【PAT甲级】1123 Is It a Complete AVL Tree
  • PWM实验(控制蜂鸣器,风扇,马达)
  • MySQL 从入门到入狱 rm - rf /* 咳咳~ 到精通
  • 回溯算法 - 二叉树中和为某一值的路径 字符串的排列
  • 纯C实现的贪吃蛇(无EasyX,详解)
  • JAVA计算机毕业设计SUNHome家政服务管理平台Mybatis+系统+数据库+调试部署
  • 【项目管理】Java离线版语音识别-语音转文字
  • HTML5标签+基础特性
  • git的相关操作
  • ES6--》读懂JS中—Class类
  • 机器学习笔记(三)
  • 【Java 面试题】经典 Java 面试题 200 问(下)
  • 【108天】Java——《Head First Java》笔记(第1-4章)
  • 【402天】跃迁之路——程序员高效学习方法论探索系列(实验阶段159-2018.03.14)...
  • 2017 前端面试准备 - 收藏集 - 掘金
  • 30秒的PHP代码片段(1)数组 - Array
  • Apache Zeppelin在Apache Trafodion上的可视化
  • es的写入过程
  • HTML-表单
  • JavaScript标准库系列——Math对象和Date对象(二)
  • Spark VS Hadoop:两大大数据分析系统深度解读
  • Stream流与Lambda表达式(三) 静态工厂类Collectors
  • Vue组件定义
  • 理解IaaS, PaaS, SaaS等云模型 (Cloud Models)
  • 消息队列系列二(IOT中消息队列的应用)
  • 职业生涯 一个六年开发经验的女程序员的心声。
  • #pragma multi_compile #pragma shader_feature
  • (1)(1.9) MSP (version 4.2)
  • (2)STM32单片机上位机
  • (Mirage系列之二)VMware Horizon Mirage的经典用户用例及真实案例分析
  • (十)T检验-第一部分
  • (一)spring cloud微服务分布式云架构 - Spring Cloud简介
  • (转)菜鸟学数据库(三)——存储过程
  • **Java有哪些悲观锁的实现_乐观锁、悲观锁、Redis分布式锁和Zookeeper分布式锁的实现以及流程原理...
  • .gitignore
  • .NET Micro Framework初体验(二)
  • .net websocket 获取http登录的用户_如何解密浏览器的登录密码?获取浏览器内用户信息?...
  • .NET 设计模式—适配器模式(Adapter Pattern)
  • .NET/C# 将一个命令行参数字符串转换为命令行参数数组 args
  • .net反编译的九款神器
  • ??javascript里的变量问题
  • [28期] lamp兄弟连28期学员手册,请大家务必看一下
  • [AHOI2009]中国象棋 DP,递推,组合数
  • [Apio2012]dispatching 左偏树
  • [C puzzle book] types
  • [C#]winform利用seetaface6实现C#人脸检测活体检测口罩检测年龄预测性别判断眼睛状态检测
  • [caffe(二)]Python加载训练caffe模型并进行测试1
  • [corCTF 2022] CoRJail: From Null Byte Overflow To Docker Escape
  • [C语言]——内存函数
  • [Delphi]一个功能完备的国密SM4类(TSM4)[20230329更新]
  • [Godot] 3D拾取
  • [GXYCTF2019]BabyUpload1 -- 题目分析与详解