STM32-LCD液晶显示
创始人
2024-12-28 08:35:02
0

目录

LCD液晶显示

ILI9341液晶控制器简介

液晶屏的信号线和8080时序

使用STM32的FSMC外设模拟8080接口时序

FSMC

功能框图

通讯引脚

存储器控制器

时钟控制逻辑

FSMC的地址映射

FSMC控制异步Nor Flash的时序

FSMC模拟8080时序

FSMC结构体

NOR FLASH时序结构体

FSMC初始化结构体(针对NOR FLASH)

实验环节(野火例程-HAL库液晶显示)

硬件原理图

基础配置

FSMC初始化(结构体)

访问地址 

初始化ILI9341寄存器

总体初始化

设置液晶显示窗口和光标位置

填充像素点和清窗口屏

获取某一个坐标点的像素数据

画线

画矩形

画圆形

显示字符和字符串

设置或获取当前字体类型

设置或获取LCD前景和背景颜色

清除某行文字

测试代码1:液晶屏幕显示

实验现象

测试代码2:液晶坐标方向

实验现象


LCD液晶显示

针对野火指南者配套资料:3.2寸 LCD电阻屏,屏幕里自带ILI9341液晶控制器芯片该控制器芯片中存在GRAM(即显存)该液晶控制器使用8080接口与单片机通讯,液晶面板引出来的FPC信号线为8080接口,单片机把要显示的数据通过引出的8080接口发送到液晶控制器,要显示的数据存储到它内部的显存中,然后液晶控制器不断把显存的内容刷新到液晶面板,显示内容。

还有个电阻触摸屏的控制器XPT2046,实际上是一个ADC芯片,通过检测电压值来计算触摸坐标。

液晶屏的每个像素点都是数据,在实际应用中需要把每个像素点的数据缓存起来,再传输给液晶屏,一般会使用SRAM和SDRAM性质的存储器,而这些专门用于存储显示数据的存储器,被称为显存。显存一般至少要能存储一帧显示数据,如分辨率为800*480的液晶屏,若使用RGB888格式显示,一帧数据大小 = 3 * 800 * 480 = 1152000字节;若使用RGB565格式显示,一帧数据大小 = 2 * 800 * 480 = 768000字节.

一般来说,外置的液晶控制器会自带显存,而像STM32F429等集成液晶控制器的芯片可使用内部SRAM或外扩SDRAM用于显存空间。

ILI9341液晶控制器简介

内部结构复杂。芯片中含有GRAM(即显存),GRAM中每个存储单元都对应液晶面板的一个像素点。通过液晶控制器内部各种模块共同作用把GRAM存储单元的数据转化为液晶面板的控制信号,使像素点呈现特定的颜色,而像素点组合起来则成为了一幅完整的图像。

ILI9341液晶控制器根据自身的IM[3:0]信号线电平决定了它与MCU的通讯方式,支持SPI、8080通讯方式。野火指南者中固定搭配8080(内部硬件电路处理),使用16根数据线的RGB565格式。即当IM[3:0]=0x8时,MCU接口模式为8080 MCU 16-bit bus interface II

液晶屏的信号线和8080时序

信号线ILI9341对应的信号线说明
LCD_DB[15:0]D[15:0]数据信号
LCD_RDRDX读数据信号,低电平有效
LCD_RSD/CX

数据/命令信号。

高电平时,D[15:0]表示的是数据(RGB像素数据或命令数据)

低电平时,D[15:0]表示的是控制命令

LCD_RESETRESX复位信号,低电平有效
LCD_WRWRX写数据信号,低电平有效
LCD_CSCSX片选信号,低电平有效
LCD_BK-背光信号,低电平有效
GPIO[5:1]-触摸屏的控制信号线

这些数据线为8080通讯接口,带X的表示低电平有效。

由图知:

写命令时序由片选信号线CSX拉低开始,数据/命令选择信号线D/CX拉低表示写入的是命令地址。

写信号线WRX拉低、读信号线RDX拉高时,传输方向为写入,同时数据线D[17:0](或D[15:0])输出命令地址。

在第二个传输阶段传输的是命令参数,所以D/CX信号线要置高电平,表示写入的是命令数据。

当需要把像素数据写入GRAM时,把CSX拉低,再把D/CX置高,这时D[17:0](或D[15:0])传输的数据则会被ILI9341保存到它的GRAM里。

使用STM32的FSMC外设模拟8080接口时序

ILI9341的8080通讯接口时序可以由STM32使用普通IO口进行模拟(效率低),也可以由STM32使用FSMC外设模拟8080接口时序。

STM32F1系列芯片使用FSMC外设来管理扩展的存储器,可以用来驱动SRAM、Nor Flash和Nand Flash类型的存储器,不能驱动SDRAM这种动态的存储器(STM32F429系列的控制器中的FMC外设才支持控制SDRAM存储器)。

由于FSMC外设可以用于控制扩展的外部存储器,而MCU对液晶屏的操作实际上是把显示数据写入到显存中,与控制存储器类似,且8080接口的通讯时序完全可以使用FSMC外设产生,因此非常适合使用FSMC控制液晶屏。

FSMC

功能框图

通讯引脚

框图右侧是FSMC外设相关的控制引脚,只有share signals是共用引脚,其他引脚要看控制的存储器类型。

本篇中控制LCD时是使用FSMC的NOR/PSRAM类型的存储器,而且控制LCD时使用的是Nor Flash类型的模式B,所以主要分析Nor Flash控制信号线部分。

FSMC控制Nor Flash的信号线信号方向功能
FSMC_NE[4:1]输出片选
FSMC_NL(FSMC_NADV)输出地址、数据线复用时作锁存信号
FSMC_CLK输出时钟(同步突发模式使用)
FSMC_A[25:0]输出地址总线
FSMC_D[15:0]输入/输出双向数据总线
FSMC_NOE输出输出使能
FSMC_NWE输出写使能
FSMC_NWAIT输入NOR闪存要求FSMC等待的信号

在控制LCD时,使用的是类似异步、地址和数据线独立的Nor Flash控制方式,所以实际上FSMC_CLK、FSMC_NWAIT和FSMC_NL(FSMC_NADV)引脚没有使用到。

STM32具有FSMC_NE1/2/3/4号引脚,不同的引脚对应STM32内部不同的地址区域。例如,当STM32访问0x6800 0000~0x6bff ffff地址空间时,FSMC_NE3引脚会自动设置为低电平,由于它一般连接到外部存储器的片选信号且低电平有效,所以外部存储器的片选被使能,而访问0x6000 0000~0x63ff ffff地址空间时,FSMC_NE1会输出低电平。

当使用不同的FSMC_NE引脚连接外部存储器时,STM32访问外部存储的地址不一样,从而达到控制多个外部存储器芯片的目的。

存储器控制器

控制Nor Flash的有FSMC_BCR1/2/3/4控制寄存器、FSMC_BTR1/2/3/4片选时序寄存器和FSMC_BWTR1/2/3/4写时序寄存器。每种寄存器都有4个,对应不同存储区域。

FSMC_BCRx寄存器:配置要控制的存储器类型、数据线宽度和信号有效极性等参数。

FSMC_BTRx寄存器:配置SRAM访问时的各种时间延迟,如数据保持时间、地址保持时间等。

FSMC_BWTR寄存器:和FSMC_BTRx寄存器类似,专门用于控制写时序的时间参数。

时钟控制逻辑

FSMC外设挂载在AHB总线上,时钟信号来源于HCLK(默认72MHz),控制器的同步时钟输出就是由它分配得到。例如NOR控制器的FSMC_CLK引脚输出的时钟,可用于与同步类型的Nor Flash芯片进行同步通讯,它的时钟频率可通过FSMC_BTRx:CLKDIV位配置,可以配置为HCLK的1/2或1/3,即若它与同步类型的Nor Flash芯片进行同步通讯时,同步时钟最高频率为36MHz。本篇的Nor Flash为异步类型的存储器,不适用同步时钟信号,所以时钟分频配置没用。

FSMC的地址映射

FSMC连接好外部存储器并初始化后就可以直接通过访问地址来读写数据。这种地址访问与I2C EEPROM、SPI FLASH不一样,后两种方式都需要控制I2C或SPI总线给存储器发送地址,然后获取数据。而使用FSMC外接存储器时,其存储单元是映射到STM32的内部寻址空间的在程序上,定义一个执行这些地址的指针,然后就可以通过指针直接修改该存储单元的内容,FSMC外设会自动完成数据访问过程,读写命令的操作不需要程序控制

如:

#define Bank1_SRAM_ADDR    ((uint32_t)(0x68000000))  // 写入16位数据0xAA55到地址0x68000010 *(uint16_t *)(Bank1_SRAM_ADDR + 10) = (uint16_t)0xAA55;  // 从地址0x68000010读取16位数据 temp = *(uint16_t *)(Bank1_SRAM_ADDR + 10);

以上是标准的C语言对特定地址的指针式访问,只是由于该地址被STM32映射到FSMC外设,所以访问这些地址时,FSMC会自动输出地址、数据等访问时序。

如FSMC_NE[4:1]信号线可用于选择BANK1内部的4小块地址区域,当STM32访问0x6C00 0000 ~ 0x6FFF FFFF地址空间时,会访问到Bank1的第一个小块区域,相应的FSMC_NE1信号线会输出控制信号。

FSMC控制异步Nor Flash的时序

FSMC外设支持输出多种不同的时序以便于控制不同的存储器,具有ABCD四种模式。

当内核发出访问某个指向外部存储器地址时,FSMC外设会根据配置控制信号线产生时序访问存储器。

以读时序为例,该图表示一个存储器操作周期由地址建立周期(ADDSET)、数据建立周期(DATAST)和2个HCLK周期组成。

        在地址建立周期中,地址线发出要访问的地址,数据掩码信号线指示出要读取地址的高、低字节部分,片选信号使能存储器芯片。

        地址建立周期结束后读使能信号线发出读使能信号,然后存储器通过数据信号线把目标数据传输给FSMC,FSMC把它交给内核。

写时序类似,区别是它的一个存储器操作周期仅由地址建立周期(ADDSET)、数据建立周期(DATAST)组成,且在数据建立周期期间写使能信号线发出写信号,然后FSMC把数据通过数据线传输到存储器中。

当FSMC外设被配置为正常工作,并且外部接了Nor Flash时,若向0x6000 0000地址写入数据(如0xABCD),FSMC会自动在各信号线上产生相应的电平信号,写入数据。FSMC会控制片选型FSMC_NE1选择相应的Nor芯片,然后使用地址线FSMC_A[25:0]输出0x6000 0000,在FSMC_NWE写使能信号线上发出低电平的写使能信号,而要写入的数据信号(0xABCD)则从数据线FSMC_D[15:0]输出,然后数据就被保存到了Nor Flash。

FSMC模拟8080时序

对比FSMC NOR/PSRAM中的模式B时序和ILI9341液晶控制器芯片使用的8080时序,这两个时序是非常相似的(除了FSMC的地址线A和8080的D/CX线外,可以说是完全一样的)。

FSMC-NOR信号线功能8080信号线功能
FSMC_NEx片选信号CSX片选信号
FSMC_NWR写使能WRX写使能
FSMC_NOE读使能RDX读使能
FSMC_D[15:0]数据信号D[15:0]数据信号
FSMC_A[25:0]地址信号D/CX数据/命令选择

对于FSMC和8080接口,前四种信号线都是完全一样的,仅仅是FSMC的地址信号A[25:0]和8080的数据/命令选择线D/CX有区别。对于8080的数据/命令选择线D/CX,高电平表示数值,低电平表示命令,如果能使用FSMC的A地址线根据不同情况来产生对应的电平,那么完全可以使用FSMC来产生8080接口需要的时序了。

为了模拟8080时序,可以把FSMC_A0地址线(也可以是其他地址线,如A1、A2等)与ILI9341芯片8080接口的数据/命令选择线D/CX相连接。那么A0高电平时,数据线FSMC_D[15:0]的信号会被ILI9341理解为数值,反之为命令。

由于FSMC会自动产生地址信号,当使用FSMC向0x6xxx xxx1、0x6xxx xxx3、0x6xxx xxx5等奇数地址写入数据时,地址最低位的值为1,FSMC会控制FSMC_A0地址线输出高电平,此时数据线FSMC_D[15:0]的信号会被ILI9341理解为数值;当使用FSMC向0x6xxx xxx0、0x6xxx xxx2、0x6xxx xxx4等偶数地址写入数据时,地址最低位的值为0,FSMC会控制FSMC_A0地址线输出低电平,此时数据线FSMC_D[15:0]的信号会被ILI9341理解为命令。

因此,只有配置好FSMC外设,然后在代码中利用指针变量,向不同地址单元写入数据就能由FSMC模拟出的8080接口向ILI9341写入控制命令或GRAM的数据。

FSMC结构体

与控制SRAM一样,控制FSMC使用NOR FLASH存储器时主要是配置时序寄存器和控制寄存器。在HAL中,主要由时序结构体和初始化结构体来体现。

NOR FLASH时序结构体

typedef struct {     uint32_t AddressSetupTime;       /* 地址建立时间,0-0xF个HCLK周期 */     uint32_t AddressHoldTime;        /* 地址保持时间,0-0xF个HCLK周期*/     uint32_t DataSetupTime;          /* 数据建立时间,0-0xF个HCLK周期*/     uint32_t BusTurnAroundDuration;  /* 总线转换周期,0-0xF个HCLK周期,在NOR FLASH */     uint32_t CLKDivision;  /* 时钟分频因子,1-0xF,若控制异步存储器,本参数无效 */     uint32_t DataLatency;  /* 数据延迟时间,若控制异步存储器,本参数无效 */     uint32_t AccessMode;   /* 设置访问模式 */ }FSMC_NORSRAMTimingInitTypeDef;

AddressSetupTime:地址建立时间

即FSMC读写时序图的ADDSET值,它可以被设置为0~0x0f个HCLK周期数。按STM32 HAL库的默认配置,HCLK的时钟频率为72MHz,即一个HCLK周期为1/72微秒。

DataSetupTime:数据建立时间

即FSMC读写时序图的DATAST值,它可以被设置为0~0x0f个HCLK周期数。

AccessMode:存储器访问模式

不同模式下FSMC访问存储器地址时引脚输出的时序不一样,可选FSMC_ACCESS_MODE_A/B/C/D模式。控制异步NOR FLASH时使用B模式。

参数访问模式单位最小值最大值
地址建立时间异步HCLK116
地址保持时间异步复用I/OHCLK216
数据建立时间异步HCLK2256
总线转换周期异步和同步 读写HCLK116
时钟分频因子同步HCLK216
数据延迟时间同步CLK217

FSMC初始化结构体(针对NOR FLASH)

/**  * @brief  FSMC NOR/SRAM Init structure definition  */ typedef struct {     uint32_t NSBank;              /* 设置要控制的Bank区域 */     uint32_t DataAddressMux;      /* 设置地址总线与数据总线是否复用 */     uint32_t MemoryType;          /* 设置存储器的类型 */     uint32_t MemoryDataWidth;     /* 设置存储器的数据宽度 */     uint32_t BurstAccessMode;     /* 设置是否支持突发访问模式,只支持同步类型的存储器 */     uint32_t WaitSignalPolarity;  /* 设置等待信号的极性 */     uint32_t WrapMode;            /* 设置是否支持对齐的突发模式 */     uint32_t WaitSignalActive;    /* 配置等待信号在等待前有效还是等待期间有效 */     uint32_t WriteOperation;      /* 设置是否写使能 */     uint32_t WaitSignal;          /* 设置是否使能等待状态插入 */     uint32_t ExtendedMode;        /* 设置是否使能扩展模式 */     uint32_t WriteBurst;          /* 设置是否使能写突发操作 */     uint32_t AsynchronousWait;    /* 设置是否使能等待信号 */     uint32_t ContinuousClock;     /* 设置是否使能FMC时钟输出到外部存储设备 */     uint32_t WriteFifo;           /* 设置是否使能FIFO */     uint32_t PageSize;            /* 指定页的大小 */      /* 当不使用扩展模式时,本参数用于配置读写时序,否则用于配置读时序 */     FSMC_NORSRAM_TimingTypeDef* FSMC_ReadWriteTimingStruct;      /* 当使用扩展模式时,本参数用于配置写时序 */     FSMC_NORSRAM_TimingTypeDef * FSMC_WriteTimingStruct;   } FSMC_NORSRAM_InitTypeDef;

NSBank:bank地址区域

地址区域
FSMC_NORSRAM_BANK10x6000 0000~0x63FF FFFF
FSMC_NORSRAM_BANK20x6400 0000~0x67FF FFFF
FSMC_NORSRAM_BANK30x6800 0000~0x6BFF FFFF
FSMC_NORSRAM_BANK40x6C00 0000~0x6FFF FFFF

MemoryType:存储器类型

FSMC_MEMORY_TYPE_SRAMSRAM
FSMC_MEMORY_TYPE_PSRAMPSRAM
FSMC_MEMORY_TYPE_NORNOR FLASH

MemoryDataWidth:存储器的数据宽度

FSMC_NORSRAM_MEM_BUS_WIDTH_88位
FSMC_NORSRAM_MEM_BUS_WIDTH_1616位
FSMC_NORSRAM_MEM_BUS_WIDTH_3232位

WaitSignalActive:配置等待信号在等待前有效还是在等待期间有效

FSMC_WAIT_TIMING_BEFORE_WS等待信号在等待前有效
FSMC_WAIT_TIMING_DURING_WS等待信号在等待期间有效

ExtendedMode:是否使用扩展模式

在非扩展模式下,对存储器读写的时序都只使用FSMC_BCD寄存器配置;

在扩展模式下,对存储器读写的时序可以分开配置,读时序使用FSMC_BCD寄存器配置,写时序使用FSMC_BWTR寄存器配置。

实验环节(野火例程-HAL库液晶显示)

硬件原理图

原理图中可看出一些信息:

PD7使用了FSMC_NE1,该引脚对应这STM32内部的地址区域,访问的是0x6000 0000~0x63FF FFFF地址空间,FSMC_NE1会输出低电平。

PD11使用了FSMC_A16,为命令/数据选择引脚。高电平传输数据,低电平传输命令。

使用了FSMC_D0~FSMC_D15,使用了16个数据引脚,选择16bit数据宽度。

基础配置

LED(推挽上拉,默认高电平灭灯状态):

RLED-PB5、GLED-PB0、BLED-PB1。

串口:使用USART1,9600波特率,8-N-1。使用重定向printf输出。勾选使用C库。

FSMC_GPIO:

        LCD_RST:LCD复位引脚,低电平有效。推挽上拉。

        LCD_BL:LCD背光引脚,低电平有效。推挽上拉。

FSMC初始化(结构体)

static SRAM_HandleTypeDef  SRAM_Handler; static FSMC_NORSRAM_TimingTypeDef Timing;  static void ILI9341_FSMC_Config(void) {     SRAM_Handler.Instance = FSMC_NORSRAM_DEVICE;     SRAM_Handler.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;      /* SRAM device configuration */ 	// B模式下读写时序只考虑ADDSET的值和DATAST的值 	// 单位为1个HCLK的时钟周期,即1/72us = 0.0138us = 13.8ns     Timing.AddressSetupTime      = 0x00;	// ADDSET的值     Timing.AddressHoldTime       = 0x00;     Timing.DataSetupTime         = 0x01;	// DATAST的值     Timing.BusTurnAroundDuration = 0x00;     Timing.CLKDivision           = 0x00;     Timing.DataLatency           = 0x00;     Timing.AccessMode            = FSMC_ACCESS_MODE_B;      SRAM_Handler.Init.NSBank 			= FSMC_NORSRAM_BANK1;     SRAM_Handler.Init.DataAddressMux 	= FSMC_DATA_ADDRESS_MUX_DISABLE;     SRAM_Handler.Init.MemoryType 		= FSMC_MEMORY_TYPE_NOR;     SRAM_Handler.Init.MemoryDataWidth 	= FSMC_NORSRAM_MEM_BUS_WIDTH_16;    // FSMC_D0~FSMC_D15     // 是否使能突发访问,仅对同步突发存储器有效,此处未用到     SRAM_Handler.Init.BurstAccessMode 	= FSMC_BURST_ACCESS_MODE_DISABLE;     // 等待信号的极性,仅在突发模式访问下有用     SRAM_Handler.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;     // 存储器是在等待周期之前的一个时钟周期使能NWAIT     SRAM_Handler.Init.WaitSignalActive 	= FSMC_WAIT_TIMING_BEFORE_WS;     SRAM_Handler.Init.WriteOperation 	= FSMC_WRITE_OPERATION_ENABLE;     SRAM_Handler.Init.WaitSignal 		= FSMC_WAIT_SIGNAL_DISABLE;         // 等待使能位,此处未用到     SRAM_Handler.Init.ExtendedMode 		= FSMC_EXTENDED_MODE_DISABLE;       // 读写使用相同的时序     // 是否使能同步传输模式下的等待信号,此处未用到     SRAM_Handler.Init.AsynchronousWait 	= FSMC_ASYNCHRONOUS_WAIT_DISABLE;     SRAM_Handler.Init.WriteBurst 		= FSMC_WRITE_BURST_DISABLE;         // 禁止突发写      /* SRAM controller initialization */     ILI9341_GPIO_Config();    // FSMC的相关GPIO配置     /* 读写使用相同的时序 */     HAL_SRAM_Init(& SRAM_Handler, &Timing, &Timing); }

初始化FSMC后,当访问特定地址时,FSMC会产生相应的模拟8080时序(控制FSMC地址线输出要访问的内存地址,使用FSMC数据信号线接收或发送数据,其它FSMC_NE片选信号、读使能信号FSMC_NOE、写使能信号FSMC_NWE辅助产生完整的时序),所以FSMC产生的信号被ILI9341接收,并且使用FSMC_A16控制命令/数据选择引脚RS(即D/CX)。切记,FSMC_A16引脚(看硬件原理图得知)输出高电平表示传输数据,低电平表示传输命令。

访问地址 

关于访问地址:

由于结构体配置了FSMC_NORSRAM_BANK1,即使用了FSMC_NE1作为8080时序CS的片选信号,所以当访问0x6000 0000~0x63FF FFFF时,FSMC才对外产生片选有效的访问时序

由于硬件上选择了FSMC_A16地址线作为命令/数据选择RS信号线,所以0x6000 0000~0x63FF FFFF地址的范围内,再选择地址的第16位输出1表示产生数据,选择地址的第16位输出0表示产生命令。例程中选择了0x6000 0000 &= ~(0<<16)  ==>  0x6000 0000,0x6000 0000 |= (1<<16)  ==>  0x6001 0000。即0x6000 000可表示命令地址,0x6001 0000可表示数据地址。

但是这样选择访问地址不是对的,根据《STM32参考手册》对FSMC访问NOR FLASH的说明,STM32内部访问地址使用的是HADDR总线,HADDR总线是需要转换到外部存储器的内部AHB地址线,HADDR总线是字节地址,而存储器访问不都是按字节访问,因此接到存储器的地址线依照存储器的数据宽度而有所不同。

内存宽度发给存储器的数据地址最大内存容量(位)
8位HADDR[25:0]64MByte * 8 = 512Mbit
16位HADDR[25:1] >> 164MByte/2 * 16 = 512Mbit

        在16位外部存储器宽度的情况下,FSMC将在内部使用HADR[25:1]来生成外部存储器FSMC_A[24:0]

        无论外部存储器宽度是8位还是16位,FSMC_A[0]都应该连接到外部存储器地址A[0]。

也就是说,HADDR1与FSMC_A0对应,以此类推。因此当FSMC_A16为1时,内部地址是第17位为1,所以命令访问地址计算应该为0x6000 0000 &= ~(0<<(16+1))  ==>  0x6000 0000,数据访问地址计算应该为0x6000 0000 |= (1<<(16+1))  ==> 0x6002 0000。因此0x6000 0000可表示命令地址,0x6002 0000可表示数据地址。

当STM32访问内部的0x6002 0000地址时,FSMC自动输出时序,且使得与液晶屏的数据/命令选择线RS(即D/CX)相连的FSMC_A16输出高电平,使得液晶屏会把传输过程理解为数据传输;当STM32访问内部的0x6000 0000地址时,FSMC自动输出时序,且使得与液晶屏的数据/命令选择线RS(即D/CX)相连的FSMC_A16输出低电平,使得液晶屏会把传输过程理解为命令传输。

//FSMC_Bank1_NORSRAM用于LCD命令操作的地址 #define      FSMC_Addr_ILI9341_CMD         ( ( uint32_t ) 0x60000000 )  //FSMC_Bank1_NORSRAM用于LCD数据操作的地址       #define      FSMC_Addr_ILI9341_DATA        ( ( uint32_t ) 0x60020000 )  /**   * @brief  向ILI9341写入命令   * @param  usCmd :要写入的命令(寄存器地址)   * @retval 无   */ void ILI9341_Write_Cmd(uint16_t usCmd) {     * (__IO uint16_t *)(FSMC_Addr_ILI9341_CMD) = usCmd; }  /**   * @brief  向ILI9341写入数据   * @param  usData :要写入的数据   * @retval 无   */ void ILI9341_Write_Data(uint16_t usData) {     * (__IO uint16_t *)(FSMC_Addr_ILI9341_DATA) = usData; }  /**   * @brief  从ILI9341读取数据   * @param  无   * @retval 读取到的数据   */ uint16_t ILI9341_Read_Data(void) {     return (* (__IO uint16_t *)(FSMC_Addr_ILI9341_DATA)); }

初始化ILI9341寄存器

厂家写好的,直接用。

static void ILI9341_REG_Config(void) {     lcdid = ILI9341_ReadID();      if (lcdid == LCDID_ILI9341)     {         /*  Power control B (CFh)  */         DEBUG_DELAY();         ILI9341_Write_Cmd(0xCF);         ILI9341_Write_Data(0x00);         ILI9341_Write_Data(0x81);         ILI9341_Write_Data(0x30);          /*  Power on sequence control (EDh) */         DEBUG_DELAY();         ILI9341_Write_Cmd(0xED);         ILI9341_Write_Data(0x64);         ILI9341_Write_Data(0x03);         ILI9341_Write_Data(0x12);         ILI9341_Write_Data(0x81);          /*  Driver timing control A (E8h) */         DEBUG_DELAY();         ILI9341_Write_Cmd(0xE8);         ILI9341_Write_Data(0x85);         ILI9341_Write_Data(0x10);         ILI9341_Write_Data(0x78);          /*  Power control A (CBh) */         DEBUG_DELAY();         ILI9341_Write_Cmd(0xCB);         ILI9341_Write_Data(0x39);         ILI9341_Write_Data(0x2C);         ILI9341_Write_Data(0x00);         ILI9341_Write_Data(0x34); //        ILI9341_Write_Data ( 0x02 ); 		// 原来是0x02,改为0x06可防止液晶显示白屏时有条纹的情况         ILI9341_Write_Data(0x06);              /* Pump ratio control (F7h) */         DEBUG_DELAY();         ILI9341_Write_Cmd(0xF7);         ILI9341_Write_Data(0x20);          /* Driver timing control B */         DEBUG_DELAY();         ILI9341_Write_Cmd(0xEA);         ILI9341_Write_Data(0x00);         ILI9341_Write_Data(0x00);          /* Frame Rate Control (In Normal Mode/Full Colors) (B1h) */         DEBUG_DELAY();         ILI9341_Write_Cmd(0xB1);         ILI9341_Write_Data(0x00);         ILI9341_Write_Data(0x1B);          /*  Display Function Control (B6h) */         DEBUG_DELAY();         ILI9341_Write_Cmd(0xB6);         ILI9341_Write_Data(0x0A);         ILI9341_Write_Data(0xA2);          /* Power Control 1 (C0h) */         DEBUG_DELAY();         ILI9341_Write_Cmd(0xC0);         ILI9341_Write_Data(0x35);          /* Power Control 2 (C1h) */         DEBUG_DELAY();         ILI9341_Write_Cmd(0xC1);         ILI9341_Write_Data(0x11);          /* VCOM Control 1 (C5h) */         ILI9341_Write_Cmd(0xC5);         ILI9341_Write_Data(0x45);         ILI9341_Write_Data(0x45);          /*  VCOM Control 2 (C7h)  */         ILI9341_Write_Cmd(0xC7);         ILI9341_Write_Data(0xA2);          /* Enable 3G (F2h) */         ILI9341_Write_Cmd(0xF2);         ILI9341_Write_Data(0x00);          /* Gamma Set (26h) */         ILI9341_Write_Cmd(0x26);         ILI9341_Write_Data(0x01);         DEBUG_DELAY();          /* Positive Gamma Correction */         ILI9341_Write_Cmd(0xE0);    //Set Gamma         ILI9341_Write_Data(0x0F);         ILI9341_Write_Data(0x26);         ILI9341_Write_Data(0x24);         ILI9341_Write_Data(0x0B);         ILI9341_Write_Data(0x0E);         ILI9341_Write_Data(0x09);         ILI9341_Write_Data(0x54);         ILI9341_Write_Data(0xA8);         ILI9341_Write_Data(0x46);         ILI9341_Write_Data(0x0C);         ILI9341_Write_Data(0x17);         ILI9341_Write_Data(0x09);         ILI9341_Write_Data(0x0F);         ILI9341_Write_Data(0x07);         ILI9341_Write_Data(0x00);          /* Negative Gamma Correction (E1h) */         ILI9341_Write_Cmd(0XE1);    //Set Gamma         ILI9341_Write_Data(0x00);         ILI9341_Write_Data(0x19);         ILI9341_Write_Data(0x1B);         ILI9341_Write_Data(0x04);         ILI9341_Write_Data(0x10);         ILI9341_Write_Data(0x07);         ILI9341_Write_Data(0x2A);         ILI9341_Write_Data(0x47);         ILI9341_Write_Data(0x39);         ILI9341_Write_Data(0x03);         ILI9341_Write_Data(0x06);         ILI9341_Write_Data(0x06);         ILI9341_Write_Data(0x30);         ILI9341_Write_Data(0x38);         ILI9341_Write_Data(0x0F);          /* memory access control set */         DEBUG_DELAY();         ILI9341_Write_Cmd(0x36);         ILI9341_Write_Data(0xC8);       /*竖屏  左上角到 (起点)到右下角 (终点)扫描方式*/         DEBUG_DELAY();          /* column address control set */         ILI9341_Write_Cmd(CMD_SetCoordinateX);         ILI9341_Write_Data(0x00);         ILI9341_Write_Data(0x00);         ILI9341_Write_Data(0x00);         ILI9341_Write_Data(0xEF);          /* page address control set */         DEBUG_DELAY();         ILI9341_Write_Cmd(CMD_SetCoordinateY);         ILI9341_Write_Data(0x00);         ILI9341_Write_Data(0x00);         ILI9341_Write_Data(0x01);         ILI9341_Write_Data(0x3F);          /*  Pixel Format Set (3Ah)  */         DEBUG_DELAY();         ILI9341_Write_Cmd(0x3a);         ILI9341_Write_Data(0x55);          /* Sleep Out (11h)  */         ILI9341_Write_Cmd(0x11);         ILI9341_Delay(0xAFFf << 2);         DEBUG_DELAY();          /* Display ON (29h) */         ILI9341_Write_Cmd(0x29);     } }

总体初始化

也就是把上述的一些初始化集合在一起。

void ILI9341_Init(void) {     ILI9341_GPIO_Config();				// FSMC_GPIO的配置     ILI9341_FSMC_Config();				// FSMC相关配置      ILI9341_BackLed_Control(ENABLE);	// 点亮LCD背光灯     ILI9341_Rst();						// 复位     ILI9341_REG_Config();				// 初始化ILI9341寄存器          // 其中0、3、5、6 模式适合从左至右显示文字,     // 不推荐使用其它模式显示文字	其它模式显示文字会有镜像效果     // 其中 6 模式为大部分液晶例程的默认显示方向     ILI9341_GramScan(LCD_SCAN_MODE); }

设置液晶显示窗口和光标位置

根据液晶屏的要求,在发送显示数据前需要先设置窗口,确定发送像素数据的显示区域。

/**  * @brief  在ILI9341显示器上开辟一个窗口  * @param  usX :在特定扫描方向下窗口的起点X坐标  * @param  usY :在特定扫描方向下窗口的起点Y坐标  * @param  usWidth :窗口的宽度  * @param  usHeight :窗口的高度  * @retval 无  */ void ILI9341_OpenWindow(uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight) { 	/* 设置X坐标,设置起始点和结束点 */     ILI9341_Write_Cmd(CMD_SetCoordinateX); 				     ILI9341_Write_Data(usX >> 8);     ILI9341_Write_Data(usX & 0xff);     ILI9341_Write_Data((usX + usWidth - 1) >> 8);     ILI9341_Write_Data((usX + usWidth - 1) & 0xff);  	/* 设置Y坐标,设置起始点和结束点 */     ILI9341_Write_Cmd(CMD_SetCoordinateY);     ILI9341_Write_Data(usY >> 8);     ILI9341_Write_Data(usY & 0xff);     ILI9341_Write_Data((usY + usHeight - 1) >> 8);     ILI9341_Write_Data((usY + usHeight - 1) & 0xff); }  /**  * @brief  设定ILI9341的光标坐标  * @param  usX :在特定扫描方向下光标的X坐标  * @param  usY :在特定扫描方向下光标的Y坐标  * @retval 无  */ static void ILI9341_SetCursor(uint16_t usX, uint16_t usY) {     ILI9341_OpenWindow(usX, usY, 1, 1); }

填充像素点和清窗口屏

/**  * @brief  在ILI9341显示器上以某一颜色填充像素点  * @param  ulAmout_Point :要填充颜色的像素点的总数目  * @param  usColor :颜色  * @retval 无  */ static __inline void ILI9341_FillColor(uint32_t ulAmout_Point, uint16_t usColor) {     uint32_t i = 0;      /* memory write */     ILI9341_Write_Cmd(CMD_SetPixel);      for (i = 0; i < ulAmout_Point; i ++)     {         ILI9341_Write_Data(usColor);     } }  static uint16_t CurrentTextColor = BLACK;	// 前景色 static uint16_t CurrentBackColor = WHITE;	// 背景色  /**  * @brief  对ILI9341显示器的某一窗口以某种颜色进行清屏  * @param  usX :在特定扫描方向下窗口的起点X坐标  * @param  usY :在特定扫描方向下窗口的起点Y坐标  * @param  usWidth :窗口的宽度  * @param  usHeight :窗口的高度  * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色  * @retval 无  */ void ILI9341_Clear(uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight) {     ILI9341_OpenWindow(usX, usY, usWidth, usHeight);     ILI9341_FillColor(usWidth * usHeight, CurrentBackColor); }  /**  * @brief  对ILI9341显示器的某一点以某种颜色进行填充  * @param  usX :在特定扫描方向下该点的X坐标  * @param  usY :在特定扫描方向下该点的Y坐标  * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色  * @retval 无  */ void ILI9341_SetPointPixel(uint16_t usX, uint16_t usY) {     if ((usX < LCD_X_LENGTH) && (usY < LCD_Y_LENGTH))     {         ILI9341_SetCursor(usX, usY);         ILI9341_FillColor(1, CurrentTextColor);     } }

获取某一个坐标点的像素数据

/**  * @brief  读取 GRAM 的一个像素数据  * @param  无  * @retval 像素数据  */ static uint16_t ILI9341_Read_PixelData(void) {     uint16_t usRG = 0, usB = 0 ;      ILI9341_Write_Cmd(0x2E); /* 读数据 */     // 去掉前一次读取结果     ILI9341_Read_Data(); 	 /* FIRST READ OUT DUMMY DATA */      // 获取红色通道与绿色通道的值     usRG = ILI9341_Read_Data();  	/*READ OUT RED AND GREEN DATA  */     usB = ILI9341_Read_Data();  	/*READ OUT BLUE DATA*/      return ((usRG & 0xF800) | ((usRG << 3) & 0x7E0) | (usB >> 11)); }  /**  * @brief  获取 ILI9341 显示器上某一个坐标点的像素数据  * @param  usX :在特定扫描方向下该点的X坐标  * @param  usY :在特定扫描方向下该点的Y坐标  * @retval 像素数据  */ uint16_t ILI9341_GetPointPixel(uint16_t usX, uint16_t usY) {     uint16_t usPixelData; 	     ILI9341_SetCursor(usX, usY);     usPixelData = ILI9341_Read_PixelData();      return usPixelData; }

画线

/**  * @brief  在 ILI9341 显示器上使用 Bresenham 算法画线段  * @param  usX1 :在特定扫描方向下线段的一个端点X坐标  * @param  usY1 :在特定扫描方向下线段的一个端点Y坐标  * @param  usX2 :在特定扫描方向下线段的另一个端点X坐标  * @param  usY2 :在特定扫描方向下线段的另一个端点Y坐标  * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色  * @retval 无  */ void ILI9341_DrawLine(uint16_t usX1, uint16_t usY1, uint16_t usX2, uint16_t usY2) {     uint16_t us;     uint16_t usX_Current, usY_Current;      int32_t lError_X = 0, lError_Y = 0, lDelta_X, lDelta_Y, lDistance;     int32_t lIncrease_X, lIncrease_Y;      //计算坐标增量     lDelta_X = usX2 - usX1;     lDelta_Y = usY2 - usY1;      usX_Current = usX1;     usY_Current = usY1;      if (lDelta_X > 0)     {         lIncrease_X = 1;    //设置单步方向     }     else if (lDelta_X == 0)     {         lIncrease_X = 0;    //垂直线     }     else     {         lIncrease_X = -1;         lDelta_X = - lDelta_X;     }      if (lDelta_Y > 0)     {         lIncrease_Y = 1;     }     else if (lDelta_Y == 0)     {         lIncrease_Y = 0;    //水平线     }     else     {         lIncrease_Y = -1;         lDelta_Y = - lDelta_Y;     }      if (lDelta_X > lDelta_Y)     {         lDistance = lDelta_X;    //选取基本增量坐标轴     }     else     {         lDistance = lDelta_Y;     }      for (us = 0; us <= lDistance + 1; us ++)  //画线输出     {         ILI9341_SetPointPixel(usX_Current, usY_Current);   //画点          lError_X += lDelta_X ;         lError_Y += lDelta_Y ;          if (lError_X > lDistance)         {             lError_X -= lDistance;             usX_Current += lIncrease_X;         }          if (lError_Y > lDistance)         {             lError_Y -= lDistance;             usY_Current += lIncrease_Y;         }     } }

画矩形

/**  * @brief  在 ILI9341 显示器上画一个矩形  * @param  usX_Start :在特定扫描方向下矩形的起始点X坐标  * @param  usY_Start :在特定扫描方向下矩形的起始点Y坐标  * @param  usWidth:矩形的宽度(单位:像素)  * @param  usHeight:矩形的高度(单位:像素)  * @param  ucFilled :选择是否填充该矩形   *   该参数为以下值之一:   *     @arg 0 :空心矩形   *     @arg 1 :实心矩形  * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色  * @retval 无  */ void ILI9341_DrawRectangle(uint16_t usX_Start, uint16_t usY_Start, uint16_t usWidth, uint16_t usHeight,                            uint8_t ucFilled) {     if (ucFilled)     {         ILI9341_OpenWindow(usX_Start, usY_Start, usWidth, usHeight);         ILI9341_FillColor(usWidth * usHeight, CurrentTextColor);     }     else     {         ILI9341_DrawLine(usX_Start, usY_Start, usX_Start + usWidth - 1, usY_Start);         ILI9341_DrawLine(usX_Start, usY_Start + usHeight - 1, usX_Start + usWidth - 1, usY_Start + usHeight - 1);         ILI9341_DrawLine(usX_Start, usY_Start, usX_Start, usY_Start + usHeight - 1);         ILI9341_DrawLine(usX_Start + usWidth - 1, usY_Start, usX_Start + usWidth - 1, usY_Start + usHeight - 1);     } }

画圆形

/**  * @brief  在 ILI9341 显示器上使用 Bresenham 算法画圆  * @param  usX_Center :在特定扫描方向下圆心的X坐标  * @param  usY_Center :在特定扫描方向下圆心的Y坐标  * @param  usRadius:圆的半径(单位:像素)  * @param  ucFilled :选择是否填充该圆   *   该参数为以下值之一:   *     @arg 0 :空心圆   *     @arg 1 :实心圆  * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色  * @retval 无  */ void ILI9341_DrawCircle(uint16_t usX_Center, uint16_t usY_Center, uint16_t usRadius, uint8_t ucFilled) {     int16_t sCurrentX, sCurrentY;     int16_t sError;      sCurrentX = 0;     sCurrentY = usRadius;      sError = 3 - (usRadius << 1);       //判断下个点位置的标志      while (sCurrentX <= sCurrentY)     {         int16_t sCountY;          if (ucFilled) 		{             for (sCountY = sCurrentX; sCountY <= sCurrentY; sCountY ++)             {                 ILI9341_SetPointPixel(usX_Center + sCurrentX, usY_Center + sCountY);              //1,研究对象                 ILI9341_SetPointPixel(usX_Center - sCurrentX, usY_Center + sCountY);              //2                 ILI9341_SetPointPixel(usX_Center - sCountY,   usY_Center + sCurrentX);            //3                 ILI9341_SetPointPixel(usX_Center - sCountY,   usY_Center - sCurrentX);            //4                 ILI9341_SetPointPixel(usX_Center - sCurrentX, usY_Center - sCountY);              //5                 ILI9341_SetPointPixel(usX_Center + sCurrentX, usY_Center - sCountY);              //6                 ILI9341_SetPointPixel(usX_Center + sCountY,   usY_Center - sCurrentX);            //7                 ILI9341_SetPointPixel(usX_Center + sCountY,   usY_Center + sCurrentX);            //0             } 		}         else         {             ILI9341_SetPointPixel(usX_Center + sCurrentX, usY_Center + sCurrentY);                //1,研究对象             ILI9341_SetPointPixel(usX_Center - sCurrentX, usY_Center + sCurrentY);                //2             ILI9341_SetPointPixel(usX_Center - sCurrentY, usY_Center + sCurrentX);                //3             ILI9341_SetPointPixel(usX_Center - sCurrentY, usY_Center - sCurrentX);                //4             ILI9341_SetPointPixel(usX_Center - sCurrentX, usY_Center - sCurrentY);                //5             ILI9341_SetPointPixel(usX_Center + sCurrentX, usY_Center - sCurrentY);                //6             ILI9341_SetPointPixel(usX_Center + sCurrentY, usY_Center - sCurrentX);                //7             ILI9341_SetPointPixel(usX_Center + sCurrentY, usY_Center + sCurrentX);                //0         }          sCurrentX ++;          if (sError < 0)         {             sError += 4 * sCurrentX + 6;         }         else         {             sError += 10 + 4 * (sCurrentX - sCurrentY);             sCurrentY --;         }     } }

显示字符和字符串

/**  * @brief  在 ILI9341 显示器上显示一个英文字符  * @param  usX :在特定扫描方向下字符的起始X坐标  * @param  usY :在特定扫描方向下该点的起始Y坐标  * @param  cChar :要显示的英文字符  * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色  * @retval 无  */ void ILI9341_DispChar_EN(uint16_t usX, uint16_t usY, const char cChar) {     uint8_t  byteCount, bitCount, fontLength;     uint16_t ucRelativePositon;     uint8_t *Pfont;      //对ascii码表偏移(字模表不包含ASCII表的前32个非图形符号)     ucRelativePositon = cChar - ' ';      //每个字模的字节数     fontLength = (LCD_Currentfonts->Width * LCD_Currentfonts->Height) / 8;      //字模首地址     /*ascii码表偏移值乘以每个字模的字节数,求出字模的偏移位置*/     Pfont = (uint8_t *)&LCD_Currentfonts->table[ucRelativePositon * fontLength];      //设置显示窗口     ILI9341_OpenWindow(usX, usY, LCD_Currentfonts->Width, LCD_Currentfonts->Height);      ILI9341_Write_Cmd(CMD_SetPixel);      //按字节读取字模数据     //由于前面直接设置了显示窗口,显示数据会自动换行     for (byteCount = 0; byteCount < fontLength; byteCount++)     {         //一位一位处理要显示的颜色         for (bitCount = 0; bitCount < 8; bitCount++)         {             if (Pfont[byteCount] & (0x80 >> bitCount))             {                 ILI9341_Write_Data(CurrentTextColor);             }             else             {                 ILI9341_Write_Data(CurrentBackColor);             }         }     } }  /**  * @brief  在 ILI9341 显示器上显示英文字符串  * @param  line :在特定扫描方向下字符串的起始Y坐标   *   本参数可使用宏LINE(0)、LINE(1)等方式指定文字坐标,   *   宏LINE(x)会根据当前选择的字体来计算Y坐标值。 	*		显示中文且使用LINE宏时,需要把英文字体设置成Font8x16  * @param  pStr :要显示的英文字符串的首地址  * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色  * @retval 无  */ void ILI9341_DispStringLine_EN(uint16_t line,  char *pStr) {     uint16_t usX = 0;      while (* pStr != '\0')     {         if ((usX - ILI9341_DispWindow_X_Star + LCD_Currentfonts->Width) > LCD_X_LENGTH)         {             usX = ILI9341_DispWindow_X_Star;             line += LCD_Currentfonts->Height;         }          if ((line - ILI9341_DispWindow_Y_Star + LCD_Currentfonts->Height) > LCD_Y_LENGTH)         {             usX = ILI9341_DispWindow_X_Star;             line = ILI9341_DispWindow_Y_Star;         }          ILI9341_DispChar_EN(usX, line, * pStr);         pStr ++;         usX += LCD_Currentfonts->Width;     } }  /**  * @brief  在 ILI9341 显示器上显示英文字符串  * @param  usX :在特定扫描方向下字符的起始X坐标  * @param  usY :在特定扫描方向下字符的起始Y坐标  * @param  pStr :要显示的英文字符串的首地址  * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色  * @retval 无  */ void ILI9341_DispString_EN(uint16_t usX, uint16_t usY,  char *pStr) {     while (* pStr != '\0')     {         if ((usX - ILI9341_DispWindow_X_Star + LCD_Currentfonts->Width) > LCD_X_LENGTH)         {             usX = ILI9341_DispWindow_X_Star;             usY += LCD_Currentfonts->Height;         }          if ((usY - ILI9341_DispWindow_Y_Star + LCD_Currentfonts->Height) > LCD_Y_LENGTH)         {             usX = ILI9341_DispWindow_X_Star;             usY = ILI9341_DispWindow_Y_Star;         }          ILI9341_DispChar_EN(usX, usY, * pStr);         pStr ++;         usX += LCD_Currentfonts->Width;     } }  /**  * @brief  在 ILI9341 显示器上显示英文字符串(沿Y轴方向)  * @param  usX :在特定扫描方向下字符的起始X坐标  * @param  usY :在特定扫描方向下字符的起始Y坐标  * @param  pStr :要显示的英文字符串的首地址  * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色  * @retval 无  */ void ILI9341_DispString_EN_YDir(uint16_t usX, uint16_t usY,  char *pStr) {     while (* pStr != '\0')     {         if ((usY - ILI9341_DispWindow_Y_Star + LCD_Currentfonts->Height) > LCD_Y_LENGTH)         {             usY = ILI9341_DispWindow_Y_Star;             usX += LCD_Currentfonts->Width;         }          if ((usX - ILI9341_DispWindow_X_Star + LCD_Currentfonts->Width) >  LCD_X_LENGTH)         {             usX = ILI9341_DispWindow_X_Star;             usY = ILI9341_DispWindow_Y_Star;         }          ILI9341_DispChar_EN(usX, usY, * pStr);         pStr ++;         usY += LCD_Currentfonts->Height;     } }

设置或获取当前字体类型

/**   * @brief  设置英文字体类型   * @param  fonts: 指定要选择的字体 	*		参数为以下值之一   * 	@arg:Font24x32;   * 	@arg:Font16x24;   * 	@arg:Font8x16;   * @retval None   */ void LCD_SetFont(sFONT *fonts) {     LCD_Currentfonts = fonts; }  /**   * @brief  获取当前字体类型   * @param  None.   * @retval 返回当前字体类型   */ sFONT *LCD_GetFont(void) {     return LCD_Currentfonts; }

设置或获取LCD前景和背景颜色

/**   * @brief  设置LCD的前景(字体)及背景颜色,RGB565   * @param  TextColor: 指定前景(字体)颜色   * @param  BackColor: 指定背景颜色   * @retval None   */ void LCD_SetColors(uint16_t TextColor, uint16_t BackColor) {     CurrentTextColor = TextColor;     CurrentBackColor = BackColor; }  /**   * @brief  获取LCD的前景(字体)及背景颜色,RGB565   * @param  TextColor: 用来存储前景(字体)颜色的指针变量   * @param  BackColor: 用来存储背景颜色的指针变量   * @retval None   */ void LCD_GetColors(uint16_t *TextColor, uint16_t *BackColor) {     *TextColor = CurrentTextColor;     *BackColor = CurrentBackColor; }  /**   * @brief  设置LCD的前景(字体)颜色,RGB565   * @param  Color: 指定前景(字体)颜色   * @retval None   */ void LCD_SetTextColor(uint16_t Color) {     CurrentTextColor = Color; }  /**   * @brief  设置LCD的背景颜色,RGB565   * @param  Color: 指定背景颜色   * @retval None   */ void LCD_SetBackColor(uint16_t Color) {     CurrentBackColor = Color; }

清除某行文字

/**   * @brief  清除某行文字   * @param  Line: 指定要删除的行   *   本参数可使用宏LINE(0)、LINE(1)等方式指定要删除的行,   *   宏LINE(x)会根据当前选择的字体来计算Y坐标值,并删除当前字体高度的第x行。   * @retval None   */ void LCD_ClearLine(uint16_t Line) {     ILI9341_Clear(0, Line, LCD_X_LENGTH, ((sFONT *)LCD_GetFont())->Height);	/* 清屏,显示全黑 */ }

测试代码1:液晶屏幕显示

void LCD_Test(void) {     /*演示显示变量*/     static uint8_t testCNT = 0;     char dispBuff[100];      testCNT++;      LCD_SetFont(&Font8x16);								// 设置字体类型:8*16,16*24,24*32     LCD_SetColors(RED, BLACK);							// 设置前景和背景色     ILI9341_Clear(0, 0, LCD_X_LENGTH, LCD_Y_LENGTH);	// 清屏,显示全黑 	     /********显示字符串示例*******/ 	ILI9341_DispString_EN(24, 0,  "  ____           _____"); 	ILI9341_DispString_EN(24, 16, " ______         _______"); 	ILI9341_DispString_EN(24, 32, "_________      _________"); 	ILI9341_DispString_EN(24, 48, "  _____   Love   _____"); 	ILI9341_DispString_EN(24, 64, "    _____      _____"); 	ILI9341_DispString_EN(24, 80, "      _____  _____"); 	ILI9341_DispString_EN(24, 96, "        ________"); 	ILI9341_DispString_EN(24, 112, "          ____");  	/********显示变量示例*******/ 	sprintf(dispBuff, "%04d ", testCNT); 	ILI9341_DispString_EN(104, 32, dispBuff);      /*******显示图形示例******/     LCD_SetFont(&Font24x32); 	     /* 画直线 */     LCD_ClearLine(LINE(4));		/* LINE4 = 32*4 = 128,清除单行文字 */     LCD_SetTextColor(BLUE);     ILI9341_DispStringLine_EN(LINE(4), "Draw line:");      LCD_SetTextColor(RED);     ILI9341_DrawLine(0, 160, 239, 319);     ILI9341_DrawLine(239, 160, 0, 319);      LCD_SetTextColor(YELLOW);     ILI9341_DrawLine(0, 180, 239, 180);     ILI9341_DrawLine(0, 300, 239, 300);      LCD_SetTextColor(BLUE);     ILI9341_DrawLine(20, 160, 20, 319);     ILI9341_DrawLine(220, 160, 220, 319);      HAL_Delay(2000);     ILI9341_Clear(0, 16 * 8, LCD_X_LENGTH, LCD_Y_LENGTH - 16 * 8);	/* 清中下屏,显示全黑 */      /*画矩形*/     LCD_ClearLine(LINE(4));		/* LINE4 = 32*4 = 128,清除单行文字 */     LCD_SetTextColor(BLUE);     ILI9341_DispStringLine_EN(LINE(4), "Draw Rect:");      LCD_SetTextColor(RED);     ILI9341_DrawRectangle(0, 160, 240, 160, 1);      LCD_SetTextColor(YELLOW);     ILI9341_DrawRectangle(80, 200, 120, 100, 0);      LCD_SetTextColor(BLUE);     ILI9341_DrawRectangle(120, 170, 100, 50, 1);  	HAL_Delay(2000);     ILI9341_Clear(0, 16 * 8, LCD_X_LENGTH, LCD_Y_LENGTH - 16 * 8);	/* 清中下屏,显示全黑 */      /* 画圆 */     LCD_ClearLine(LINE(4));		/* LINE4 = 32*4 = 128,清除单行文字 */     LCD_SetTextColor(BLUE);     ILI9341_DispStringLine_EN(LINE(4), "Draw Cir:");      LCD_SetTextColor(RED);     ILI9341_DrawCircle(120, 240, 78, 0);      LCD_SetTextColor(YELLOW);     ILI9341_DrawCircle(120, 240, 50, 1);      LCD_SetTextColor(BLUE);     ILI9341_DrawCircle(120, 240, 20, 1);      HAL_Delay(2000);     ILI9341_Clear(0, 16 * 8, LCD_X_LENGTH, LCD_Y_LENGTH - 16 * 8);	/* 清屏,显示全黑 */ }

实验现象

测试代码2:液晶坐标方向

液晶屏幕显示的功能例程中,使用了ILI9341_GramScan(6)固定了X和Y轴的方向。在该例城,主要对ILI9341_GramScan函数参数取不同值,改变X和Y轴的方向。

/*用于展示LCD的八种方向模式*/ void LCD_Direction_Show(void) {     uint8_t i = 0;     char dispBuff[100];      //轮流展示各个方向模式     for (i = 0; i < 8; i++)     {         LCD_SetFont(&Font16x24);         LCD_SetColors(RED, BLACK);          ILI9341_Clear(0, 0, LCD_X_LENGTH, LCD_Y_LENGTH);	/* 清屏,显示全黑 */          //其中0、3、5、6 模式适合从左至右显示文字,         //不推荐使用其它模式显示文字	其它模式显示文字会有镜像效果         //其中 6 模式为大部分液晶例程的默认显示方向         ILI9341_GramScan(i);          sprintf(dispBuff, "o --->X");         ILI9341_DispStringLine_EN(LINE(0), dispBuff); // 沿X方向显示文字 														          sprintf(dispBuff, "o ||VY");                              ILI9341_DispString_EN_YDir(0, 0, dispBuff);   // 沿Y方向显示文字  		LCD_SetFont(&Font24x32); 		LCD_SetTextColor(BLUE); 		sprintf(dispBuff, "%d", i); 		ILI9341_DispString_EN(50, 50, dispBuff); 		         HAL_Delay(2000);     } }

实验现象

由此看出,参数为6时,观看最佳。

相关内容

热门资讯

第三了解!云扑克内置外挂透明挂... 自定义新版云扑克内置系统规律,只需要输入自己想要的开挂功能,一键便可以生成出云扑克内置专用辅助器,不...
第四了解!微扑克透明挂外挂透明... 第四了解!微扑克透明挂外挂透明挂辅助透视挂,wpk ai机器人(有挂助手)-哔哩哔哩是一款可以让一直...
第2实锤!微扑克必备外挂辅助器... 第2实锤!微扑克必备外挂辅助器脚本,wpk德州插件(有挂透视)-哔哩哔哩;1、这是跨平台的微扑克必备...
第六了解!wpk软件透明挂辅助... 第六了解!wpk软件透明挂辅助插件,微扑克智能辅助器(有挂分享)-哔哩哔哩;人气非常高,ai更新快且...
第8个了解!wepoke苹果版... 第8个了解!wepoke苹果版软件透明挂辅助透视挂,wepoke有科技的(有挂猫腻)-哔哩哔哩;科技...
第一了解!wepoke透明挂外... 第一了解!wepoke透明挂外挂辅助插件,wpk外挂被实锤(确实有挂)-哔哩哔哩;wepoke透明挂...
第9个了解!线上德州软件透明挂... 第9个了解!线上德州软件透明挂辅助神器,微扑克真的有挂存在的(有挂辅助)-哔哩哔哩是一款可以让一直输...
第一实锤!wePoKe外挂辅助... 自定义新版wePoKe系统规律,只需要输入自己想要的开挂功能,一键便可以生成出wePoKe专用辅助器...
第9个了解!微扑克脚本外挂辅助... 第9个了解!微扑克脚本外挂辅助器插件,微扑克辅助钻石(有挂解密)-哔哩哔哩;1、很好的工具软件,可以...
第9实锤!wepoke挂软件透... 第9实锤!wepoke挂软件透明挂辅助插件,wpk如何才能稳定长期收益(有挂透视)-哔哩哔哩;1、这...