STM32-LCD液晶显示

目录

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.8nsTiming.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;// 存储器是在等待周期之前的一个时钟周期使能NWAITSRAM_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 GammaILI9341_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 GammaILI9341_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);              //2ILI9341_SetPointPixel(usX_Center - sCountY,   usY_Center + sCurrentX);            //3ILI9341_SetPointPixel(usX_Center - sCountY,   usY_Center - sCurrentX);            //4ILI9341_SetPointPixel(usX_Center - sCurrentX, usY_Center - sCountY);              //5ILI9341_SetPointPixel(usX_Center + sCurrentX, usY_Center - sCountY);              //6ILI9341_SetPointPixel(usX_Center + sCountY,   usY_Center - sCurrentX);            //7ILI9341_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);                //2ILI9341_SetPointPixel(usX_Center - sCurrentY, usY_Center + sCurrentX);                //3ILI9341_SetPointPixel(usX_Center - sCurrentY, usY_Center - sCurrentX);                //4ILI9341_SetPointPixel(usX_Center - sCurrentX, usY_Center - sCurrentY);                //5ILI9341_SetPointPixel(usX_Center + sCurrentX, usY_Center - sCurrentY);                //6ILI9341_SetPointPixel(usX_Center + sCurrentY, usY_Center - sCurrentX);                //7ILI9341_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*32LCD_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时,观看最佳。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/113998.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

由Django-Session配置引发的反序列化安全问题

漏洞成因 漏洞成因位于目标配置文件settings.py下 关于这两个配置项 SESSION_ENGINE&#xff1a; 在Django中&#xff0c;SESSION_ENGINE 是一个设置项&#xff0c;用于指定用于存储和处理会话&#xff08;session&#xff09;数据的引擎。 SESSION_ENGINE 设置项允许您选择不…

Python基础教程:内置函数之字典函数的使用方法

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 len(字典名)&#xff1a; 返回键的个数&#xff0c;即字典的长度 # len(字典名)&#xff1a; # 返回键的个数&#xff0c;即字典的长度dic {a:123,b:456,c:789…

大疆智图(PC):新一代高效率高精度摄影测量软件

大疆智图是一款以二维正射影像与三维模型重建为主的软件&#xff0c;同时提供二维多光谱重建、激光雷达点云处理、精细化巡检等功能。它能够将无人机采集的数据可视化&#xff0c;实时生成高精度、高质量三维模型&#xff0c;满足事故现场、工程监测、电力巡线等场景的展示与精…

抖音热搜榜:探索热门话题的奥秘

抖音热搜榜是抖音平台根据用户观看、点赞、评论、分享等行为数据&#xff0c;综合计算得出的热门话题排行榜。它反映了当前平台上最热门、最受欢迎的话题和内容。抖音热搜榜有以下几个作用和意义&#xff1a; 1. 满足用户需求&#xff1a;抖音热搜榜为用户提供了丰富的热门话题…

华为云云耀云服务器L实例评测|使用clickhouse-benchmark工具对ClickHouse的性能测试

目录 引言 1 ClickHouse简介 2 利用docker安装ClickHouse 2.1 安装Docker 2.2 下载ClickHouse Docker镜像 2.3 创建ClickHouse容器 2.4 访问ClickHouse 3 创建测试表 4 运行 clickhouse-benchmark 5 分析结果 结语 引言 利用华为云的云耀云服务器L实例&#xff0c…

【Objective-C】浅析Block及其捕获机制

目录 Block的基本使用Block的声明Block的实现Block的调用 Block作为形参使用Block作为属性使用给Block起别名Block的copy Block的捕获机制auto类型的局部变量__block浅析static类型的局部变量全局变量 其他问题 Block的基本使用 什么是Block&#xff1f; Block &#xff08;块…

【技术分享】RK356X Ubuntu 推流USB摄像头

本文适用与触觉智能所有RK356X ubuntu系统的主板。 IDO-SBC3566基于瑞芯微RK3566研发的一款高性能低功耗的智能主板&#xff0c;采用四核A55,主频高达1.8GHz&#xff0c;专为个人移动互联网设备和AIOT设备而设计&#xff0c;内置了多种功能强大的嵌入式硬件引擎&#xff0c;为…

2020年亚太杯APMCM数学建模大赛A题激光标记舱口轮廓生成求解全过程文档及程序

2020年亚太杯APMCM数学建模大赛 A题 激光标记舱口轮廓生成 原题再现&#xff1a; 激光是20中的一项重要发明世纪&#xff0c;它被称为“最锋利的刀”、“最精确的尺子”和“最不寻常的光”。 激光已越来越多地应用于工业加工&#xff0c; 其中可以是就业在各种加工业务例如作…

「Qt中文教程指南」如何创建基于Qt Widget的应用程序(三)

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 本文描述了如何使用…

学信息系统项目管理师第4版系列34_10大管理49过程ITTO

整合管理 组 过程 输入 工具和技术 输出 启动 制定项目章程 立项管理文件协议事业环境因素组织过程资产 专家判断数据收集人际关系与团队技能会议 项目章程假设日志 计划 2.制定项目管理计划 项目章程其他知识领域规划过程的输出事业环境因素组织过程资产 专家…

设计链表复习

设计链表 class ListNode {int val;ListNode next;public ListNode() {}public ListNode(int val) {this.val val;}public ListNode(int val, ListNode next) {this.val val;this.next next;}}class MyLinkedList {//size存储链表元素的个数int size;//虚拟头节点ListNode…

花生好车基于 KubeSphere 的微服务架构实践

公司简介 花生好车成立于 2015 年 6 月&#xff0c;致力于打造下沉市场汽车出行解决方案第一品牌。通过自建直营渠道&#xff0c;瞄准下沉市场&#xff0c;现形成以直租、批售、回租、新能源汽车零售&#xff0c;四大业务为核心驱动力的汽车新零售平台&#xff0c;目前拥有门店…

nuxt使用i18n进行中英文切换

中文效果图&#xff1a; 英文效果图&#xff1a; 版本&#xff1a; 安装&#xff1a; npm install --save nuxtjs/i18n 新建en.js与zh.js两个文件进行切换显示 en.js内容 import globals from ./../js/global_valexport default {/******* 公共内容开始* *****/seeMore: &quo…

基于食肉植物优化的BP神经网络(分类应用) - 附代码

基于食肉植物优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于食肉植物优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.食肉植物优化BP神经网络3.1 BP神经网络参数设置3.2 食肉植物算法应用 4.测试结果…

系统设计 - 我们如何通俗的理解那些技术的运行原理 - 第四部分:微服务架构

本心、输入输出、结果 文章目录 系统设计 - 我们如何通俗的理解那些技术的运行原理 - 第四部分&#xff1a;微服务架构前言典型的微服务架构是什么样的微服务的优势 微服务最佳实践在开发微服务时&#xff0c;我们需要遵循以下最佳实践&#xff1a; 微服务通常使用什么技术堆栈…

笔记:绘图进阶

主要功能&#xff1a; 双坐标轴多子图共用一个横坐标横坐标时间刻度设置&#xff08;简便方法&#xff09;自定义时间坐标轴起止时间 # -*- coding: utf-8 -*-import numpy as np import pandas as pd import matplotlib.pyplot as plt import matplotlib.dates as mdatesif …

网站技术查看

当打开一个网页感觉很好奇&#xff0c;他使用了什么框架和什么技术&#xff1f; 常用的网页技术分析网站。 1. w3techs Check web technologies used by a website - Site InfoW3Techs identifies which web technologies such as CMS, programming language, web server an…

算法学习之 背包01问题 , 备战leecode

来看题目 我们分析一下题目&#xff0c;首先我们要排序&#xff0c;这有助于我们得到最大的值&#xff0c;我们要得到一个递推公式 代码如下: class Solution { public:int maxSatisfaction(vector<int>& satisfaction) {int n satisfaction.size();vector<v…

微软官方推出的四款工具,太实用了,值得收藏

目录 一、Officeplus——丰富的办公资源库 二、微软数学求解器 三、微软内置edge浏览器 四、Microsoft To-Do 办公待办神器 所以今天小编给大家分享4个微软官方推出的实用工具&#xff0c;每一个都非常好用&#xff0c;对于大家日常办公&#xff0c;非常有必要&#xff0c;感兴…

C语言--冒泡排序和简答选择排序

冒泡排序 一种典型的交换排序 类似水冒泡&#xff0c;大元素经不断的交换由水底慢慢的浮出 从头到尾&#xff0c;循环比较两相邻的元素 大的元素移到后面&#xff0c;小的放前面-每次循环&#xff0c;大的元素会排到最后 代码如下&#xff1a; #include<stdio.h> …