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

stm32-LCD(液晶显示器)

LCD的介绍:

LCD(Liquid Crystal Display,液晶显示器)是一种利用液晶材料的电光效应来控制光线透过或反射的显示技术。LCD 广泛应用于各种电子设备中,如电视、电脑显示器、智能手机、平板电脑、汽车仪表盘、工业控制面板等。

LCD 的工作原理:
LCD 的核心是液晶材料,液晶在电场作用下会改变其分子排列,从而影响光的透过率。LCD 通过控制液晶分子的排列来控制光的透过或阻挡,进而形成图像。

基本结构:


LCD 的基本结构包括以下几层:
1. 背光层:提供光源,通常使用 LED 背光。
2. 偏光片:位于液晶层两侧,用于控制光的偏振方向。
3. 液晶层:液晶分子在电场作用下改变排列,控制光的透过率。
4. 彩色滤光片(仅彩色 LCD):用于生成彩色图像。
5. 透明电极:用于施加电场,控制液晶分子的排列。
6.玻璃基板:支撑整个结构。

液晶显示器(LCD)利用液晶分子的物理特性,配合电场调控,结合偏光片和彩色滤光片等组件,通过多层次的方式控制光线的透过与反射,从而显示出图像或文字。以下是液晶显示器的详细工作过程:

  1. 背光源发光: LCD 本身不能发光,所以它通常需要外部的 背光源。背光源一般使用 LED(发光二极管),它们发出的光被引导到屏幕的背面。

  2. 通过第一个偏光片: 背光源发出的光线是一种非偏振光。当这些光线通过 第一个偏光片(位于液晶面板的前面),偏光片会让光线只保持一个方向的振动。此时,光线已经变成了 偏振光,即光波在一个特定的平面内振动。

  3. 光线通过液晶层: 偏振光通过液晶层后,液晶分子的排列状态会影响光线的透过率。液晶分子本身是可以响应外部电场的,当给液晶施加电场时,液晶分子会调整其排列方式。

    • 在没有电场时,液晶分子一般排列成一定的模式(比如扭曲的状态)。这种排列会使光线的偏振方向发生旋转,从而允许光线通过液晶层。
    • 当电场施加到液晶分子上时,它们会发生重新排列,导致光线的偏振方向发生变化,进而影响光线的透过程度。
  4. 通过彩色滤光片生成像素: LCD 屏幕中的每一个小区域(像素)通常由 红、绿、蓝 三个子像素组成。每个子像素上面都有一个对应的 彩色滤光片,这些滤光片会选择性地通过不同颜色的光。

    • 通过调节液晶分子对于不同颜色光的透过率,屏幕能够显示不同颜色的像素。
  5. 通过第二个偏光片: 在液晶层之后,光线还需要通过第二个 偏光片。第二个偏光片的作用是将液晶层调整后的光线进行再偏振,从而最终形成图像。由于液晶屏的工作原理,只有通过正确排列的液晶分子才能使光线通过第二个偏光片,否则光线会被阻挡,显示为黑色或透明。

  6. 形成最终图像: 通过液晶分子的调控和光的偏振特性,屏幕最终显示的图像就由不同的 像素 组成。每个像素可以通过控制液晶的排列来显示特定的颜色,从而形成清晰的图像或文字。

  7. 沿光路方向描述:

  8. 背光发射的白光 → 偏光片(自然光转换成偏振光)→ 液晶层(控制电压大小,来改变液晶偏转方向,从而控制光穿过的“量”)→ 彩色滤光片(彩色光)

 LCD 的类型:
LCD 根据驱动方式和显示内容的不同,可以分为多种类型:

 1. 字符型 LCD
- 用于显示简单的字符和数字。
- 常见的规格有 16x2(16 列 2 行)、20x4 等。
- 通常通过并行接口(8 位或 4 位)或 I2C/SPI 接口与微控制器连接。
- 广泛应用于小型嵌入式设备,如计算器、家用电器等。

 2. **图形型 LCD**
- 用于显示图形和文本。
- 常见的分辨率有 128x64、240x64 等。
- 通常通过并行接口或 SPI 接口与微控制器连接。
- 适用于需要显示复杂信息的设备,如工业控制面板、医疗设备等。

3. TFT LCD(薄膜晶体管液晶显示器)**
- 彩色显示,支持高分辨率和高刷新率。
- 常见的分辨率有 320x240、480x272、800x480 等。
- 通常通过 RGB 接口、SPI 接口或 MIPI DSI 接口与处理器连接。
- 广泛应用于智能手机、平板电脑、汽车显示屏等。

4. IPS LCD(平面转换液晶显示器)**
- 一种改进的 TFT LCD,具有更广的视角和更好的色彩表现。
- 适用于高端智能手机、显示器等。

 5. OLED(有机发光二极管)**
- 虽然不是 LCD,但常与 LCD 对比。OLED 不需要背光,每个像素自发光。
- 具有更高的对比度、更广的视角和更快的响应速度。
- 广泛应用于高端智能手机和电视。

 LCD 的驱动方式
LCD 的驱动方式取决于其类型和接口:

1. 并行接口
- 使用多条数据线(如 8 位或 4 位)传输数据。
- 优点是传输速度快,缺点是占用引脚多。
- 常用于字符型 LCD 和图形型 LCD。

2. SPI 接口
- 使用串行外设接口(SPI)传输数据。
- 优点是占用引脚少,缺点是传输速度较慢。
- 常用于小型图形型 LCD 和 TFT LCD。

 3. I2C 接口
- 使用 I2C 总线传输数据。
- 优点是占用引脚极少,缺点是传输速度较慢。
- 常用于字符型 LCD。

4. RGB 接口
- 使用 RGB 信号线传输像素数据。
- 优点是传输速度快,适合高分辨率显示。
- 常用于 TFT LCD。

5. MIPI DSI 接口
- 使用移动行业处理器接口(MIPI)的显示串行接口(DSI)。
- 优点是传输速度快,功耗低。
- 常用于智能手机和平板电脑的高分辨率显示屏。

LCD 的优点和缺点
 优点
1. 功耗:相比 CRT 显示器,LCD 的功耗更低。
2. 轻薄:LCD 的结构紧凑,适合便携设备。
3. 无辐射:LCD 不会产生电磁辐射,对人体更安全。
4. 长寿命:LCD 的使用寿命较长,尤其是 LED 背光的 LCD。

 缺点
1. 视角受限:某些类型的 LCD 视角较窄,色彩和亮度会随视角变化。
2. *响应时间较慢:相比 OLED,LCD 的响应时间较慢,可能导致动态图像模糊。
3. **对比度较低**:LCD 的对比度通常不如 OLED。

LCD 的应用
LCD 广泛应用于以下领域:
1. 消费电子:智能手机、平板电脑、电视、电脑显示器等。
2. 工业控制:工业仪表、控制面板、医疗设备等。
3. 汽车电子:车载显示屏、仪表盘、导航系统等。
4. 家用电器:微波炉、洗衣机、空调等。

通过STM32利用ILI9341液晶显示存储控制器对LCD进行显示存储数据读写:

18080 并行接口时序:

**ILI9341** 是一款广泛应用于嵌入式系统中的 LCD 控制器芯片采用8080并行接口时序通信,主要用于驱动 **TFT LCD** 显示屏。它支持 **240x320** 分辨率(QVGA)的彩色显示屏,并通过 SPI 或并行接口与主控芯片(如 STM32、ESP32、Arduino 等)通信。ILI9341 具有低功耗、高集成度和易于驱动的特点,因此在嵌入式开发中非常受欢迎.

---

 ILI9341 的主要特性
1. 显示分辨率:
   - 支持 **240x320** 像素的 RGB 彩色显示。
   - 每个像素可显示 **18 位色深**(262K 颜色),通过 RGB 6-6-6 格式实现。

2. 接口支持:
   - SPI 接口:支持 4 线 SPI 通信,适合引脚资源有限的应用。
   - 并行接口:支持 8 位或 16 位并行接口,适合高速数据传输。
   - 支持 **IM[2:0]** 引脚配置接口模式。

3. **内置显存**:
   - 内置 **172,800 字节** 的显存(GRAM),用于存储当前显示的图像数据。
   - 支持部分区域刷新,减少数据传输量。

4. **显示功能**:
   - 支持横屏和竖屏显示模式。
   - 支持图像旋转(0°、90°、180°、270°)。
   - 支持窗口地址设置,可局部刷新显示区域。

5. **低功耗**:
   - 支持睡眠模式和待机模式,适合电池供电的设备。

6. **驱动电压**:
   - 工作电压:**2.5V - 3.3V**。
   - 支持逻辑电平转换,兼容 3.3V 和 5V 系统。

7. **其他功能**:
   - 支持伽马校正。
   - 支持背光控制(通常通过 PWM 信号调节亮度)。

---

 ILI9341 的引脚功能
ILI9341 的引脚较多,以下是关键引脚的功能说明:

| 引脚名称       | 功能描述                                                                 |
|----------------|--------------------------------------------------------------------------|
| **VCC**        | 电源正极(2.5V - 3.3V)。                                                |
| **GND**        | 电源地。                                                                 |
| **RESET**      | 复位引脚,低电平有效。                                                   |
| **CS**         | 片选引脚,低电平有效。                                                   |
| **DC**         | 数据/命令选择引脚(高电平:数据;低电平:命令)。                        |
| **SCL/SCLK**   | SPI 时钟信号。                                                           |
| **SDA/MOSI**   | SPI 数据输入(主设备输出,从设备输入)。                                 |
| **MISO**       | SPI 数据输出(主设备输入,从设备输出),ILI9341 通常不需要此引脚。       |
| **D0-D15**     | 并行数据总线(8 位或 16 位模式)。                                       |
| **RD**         | 读信号(并行接口模式)。                                                 |
| **WR**         | 写信号(并行接口模式)。                                                 |
| **LEDK/LEDA**  | 背光控制引脚(通常接 PWM 信号调节亮度)。                                |

ILI9341 的通信协议
ILI9341 支持 **SPI** 和 **并行接口** 两种通信方式。以下是 SPI 模式下的通信流程:

 1. **SPI 模式**
- **CS**:片选信号,低电平有效。
- **DC**:数据/命令选择引脚。
  - **DC = 0**:发送命令。
  - **DC = 1**:发送数据。
- **SCLK**:时钟信号,数据在上升沿或下降沿采样。
- **MOSI**:主设备发送数据到 ILI9341。

 2. **并行接口模式**
- 使用 **D0-D15** 数据总线传输数据。
- **RD** 和 **WR** 信号用于控制读写操作。

---

ILI9341 的初始化流程
在使用 ILI9341 之前,需要对其进行初始化配置。以下是一个典型的初始化流程:

1. **硬件复位**:
   - 拉低 **RESET** 引脚一段时间(通常 10ms),然后拉高。

2. **发送初始化命令**:
   - 通过 SPI 或并行接口发送一系列命令和参数,配置 ILI9341 的工作模式。
   - 常用命令包括:
     - **0xCF**:电源控制 B。
     - **0xED**:电源控制 A。
     - **0xE8**:驱动时序控制 A。
     - **0xCB**:电源控制 A。
     - **0xF7**:泵比控制。
     - **0xEA**:驱动时序控制 B。
     - **0xC0**:电源控制 1。
     - **0xC1**:电源控制 2。
     - **0xC5**:VCOM 控制 1。
     - **0xC7**:VCOM 控制 2。
     - **0x36**:内存访问控制(设置显示方向)。
     - **0x3A**:像素格式设置(通常设置为 16 位色深)。
     - **0x11**:退出睡眠模式。
     - **0x29**:开启显示。

3. **设置显示区域**:
   - 使用 **0x2A** 和 **0x2B** 命令设置显示区域的起始和结束地址。
   - 使用 **0x2C** 命令开始写入显存数据。

---

 ILI9341 的常用命令
以下是一些常用的 ILI9341 命令:

| 命令(十六进制) | 功能描述                                                                 |
|------------------|--------------------------------------------------------------------------|
| **0x36**         | 内存访问控制(设置显示方向)。                                           |
| **0x2A**         | 设置列地址(X 方向)。                                                   |
| **0x2B**         | 设置行地址(Y 方向)。                                                   |
| **0x2C**         | 写入显存数据。                                                           |
| **0x3A**         | 设置像素格式(如 16 位 RGB565)。                                        |
| **0x11**         | 退出睡眠模式。                                                           |
| **0x29**         | 开启显示。                                                               |
| **0x20**         | 关闭显示。                                                               |

---

LCD引脚IO口
LCD_BL(背光控制)对应 PB0
LCD_CS(片选信号)对应 PG12 即 FSMC_NE4;
LCD _RS(数据/命令控制)对应 PG0 即 FSMC_A10
LCD _WR(写控制)对应 PD5 即 FSMC_NWE
LCD _RD(读控制)对应 PD4 即 FSMC_NOE
LCD _D[15:0]则直接连接在 FSMC_D15~FSMC_D0

使用 STM32 的 FSMC 模拟 8080 时序

FSMC(灵活静态内存控制器)是 STM32 系列微控制器提供的一个硬件外设,它允许微控制器与外部存储设备(如 SRAM、NAND Flash、LCD 等)进行高速数据交换。在模拟 8080 总线时序 时,FSMC 可以配置为与外部设备进行通信。

使用stm32FSMC外设的NORFLASH时序访问时序模拟8080

这里介绍一下关于FSMC模拟8080时序的两个结构体:

FSMC_NORSRAMTimingInitTypeDef 是 STM32 标准库中用于配置 FSMC(Flexible Static Memory Controller,灵活静态存储控制器)的 NOR Flash、SRAM 等存储器访问时序的结构体。

typedef struct
{
  uint32_t FSMC_AddressSetupTime;           /*!< 地址建立时间,单位为HCLK周期数 */
  uint32_t FSMC_AddressHoldTime;            /*!< 地址保持时间,单位为HCLK周期数 */
  uint32_t FSMC_DataSetupTime;              /*!< 数据建立时间,单位为HCLK周期数 */
  uint32_t FSMC_BusTurnAroundDuration;      /*!< 总线 turnaround 持续时间,单位为HCLK周期数 */
  uint32_t FSMC_CLKDivision;                /*!< 时钟分频因子 */
  uint32_t FSMC_DataLatency;                /*!< 数据延迟,单位为HCLK周期数 */
  uint32_t FSMC_AccessMode;                 /*!< 访问模式,可选值为FSMC_AccessMode_A、FSMC_AccessMode_B、FSMC_AccessMode_C、FSMC_AccessMode_D */
} FSMC_NORSRAMTimingInitTypeDef;
FSMC_NORSRAMInitTypeDef 是 STM32 标准库中用于配置 FSMC(Flexible Static Memory Controller,灵活静态存储控制器)连接的 NOR Flash 或 SRAM 存储器的初始化结构体。FSMC 可以让 STM32 微控制器与多种静态存储器(如 SRAM、NOR Flash 等)进行通信,该结构体可对这些存储器的各项参数进行详细设置。
typedef struct
{
  uint32_t FSMC_Bank;                     /*!< 指定要使用的FSMC存储块 */
  uint32_t FSMC_DataAddressMux;           /*!< 数据地址是否复用 */
  uint32_t FSMC_MemoryType;               /*!< 存储器类型 */
  uint32_t FSMC_MemoryDataWidth;          /*!< 存储器数据宽度 */
  uint32_t FSMC_BurstAccessMode;          /*!< 突发访问模式是否使能 */
  uint32_t FSMC_WaitSignalPolarity;       /*!< 等待信号的极性 */
  uint32_t FSMC_WrapMode;                 /*!< 回绕模式是否使能 */
  uint32_t FSMC_WaitSignalActive;         /*!< 等待信号激活状态 */
  uint32_t FSMC_WriteOperation;           /*!< 写操作是否使能 */
  uint32_t FSMC_WaitSignal;               /*!< 等待信号是否使能 */
  uint32_t FSMC_ExtendedMode;             /*!< 扩展模式是否使能 */
  uint32_t FSMC_WriteBurst;               /*!< 写突发模式是否使能 */
  FSMC_NORSRAMTimingInitTypeDef* FSMC_ReadWriteTimingStruct; /*!< 读写时序配置结构体指针 */
  FSMC_NORSRAMTimingInitTypeDef* FSMC_WriteTimingStruct;    /*!< 写时序配置结构体指针 */
} FSMC_NORSRAMInitTypeDef;
步骤 1:启用 FSMC 时钟并配置FSMC结构体步骤 ,配置 FSMC 为异步NORFlash模式以仿真8080时序

1在 STM32 的系统初始化中,需要使能 FSMC 的时钟。通常这在 RCC 的时钟使能部分进行配置:

//将FSMC配置成异步的NOR Flash使用的模式B以模拟8080时序
void  ILI9341_FSMC_Config()
{
	//配置FSMC外设时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE);
	FSMC_NORSRAMTimingInitTypeDef   readwriteTim;
	FSMC_NORSRAMInitTypeDef      FSMC_NORSRAM_Initsturt;
	//配置访问 NOR Flash时序结构体
	 readwriteTim.FSMC_AccessMode =FSMC_AccessMode_B ;
	readwriteTim.FSMC_AddressHoldTime =0x00;
	//(ADDSET)+1个HCLK,2/72MHZ=28ns;
	readwriteTim.FSMC_AddressSetupTime =0x01;//地址建立时间
	readwriteTim.FSMC_BusTurnAroundDuration=0x00;
	readwriteTim.FSMC_CLKDivision =0x00;
	readwriteTim.FSMC_DataLatency =0x00;
	//(DATASET)+1个HCLK=5/57MHZ=17ns
	readwriteTim.FSMC_DataSetupTime =0x04 ;//数据建立时间
	
	//配置访问NOR Flash初始化结构体
	FSMC_NORSRAM_Initsturt.FSMC_AsynchronousWait =FSMC_AsynchronousWait_Disable  ;
	FSMC_NORSRAM_Initsturt.FSMC_Bank =FSMC_Bank1_NORSRAM1;
	FSMC_NORSRAM_Initsturt.FSMC_BurstAccessMode =FSMC_BurstAccessMode_Disable ;
	FSMC_NORSRAM_Initsturt.FSMC_DataAddressMux =FSMC_DataAddressMux_Disable  ;
	FSMC_NORSRAM_Initsturt.FSMC_ExtendedMode =FSMC_ExtendedMode_Disable;
	FSMC_NORSRAM_Initsturt.FSMC_MemoryDataWidth =FSMC_MemoryDataWidth_16b;
	FSMC_NORSRAM_Initsturt.FSMC_MemoryType =FSMC_MemoryType_NOR;
	FSMC_NORSRAM_Initsturt.FSMC_ReadWriteTimingStruct =&readwriteTim;
	FSMC_NORSRAM_Initsturt.FSMC_WaitSignal =FSMC_WaitSignal_Disable ;  
	FSMC_NORSRAM_Initsturt.FSMC_WaitSignalActive =FSMC_WaitSignalActive_BeforeWaitState;
	FSMC_NORSRAM_Initsturt.FSMC_WaitSignalPolarity =FSMC_WaitSignalPolarity_Low ;
	FSMC_NORSRAM_Initsturt.FSMC_WrapMode =FSMC_WrapMode_Disable  ;
	FSMC_NORSRAM_Initsturt.FSMC_WriteBurst =FSMC_WriteBurst_Disable ;
	FSMC_NORSRAM_Initsturt.FSMC_WriteOperation =FSMC_WriteOperation_Enable;
	FSMC_NORSRAM_Initsturt.FSMC_WriteTimingStruct =&readwriteTim;
	FSMC_NORSRAMInit(&FSMC_NORSRAM_Initsturt);
	FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1, ENABLE );//使能结构体
	
}

步骤2: 配置 FSMC 的 GPIO 引脚并使能端口时钟

根据 ILI9341 的 8080 时序,需要将 STM32F103 的 GPIO 配置为 FSMC 使用的引脚。ILI9341 的 8080 接口通常使用 8 位数据总线多个控制信号。你需要将对应的 数据总线引脚(D0 到 D7)控制信号引脚(如 RS、WR、RD、CS 等) 配置为 复用功能

void ILI9341_GPIO_Config()
{
	//配置GPIO的时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOD, ENABLE);
	GPIO_InitTypeDef  ILI9341_GPIO_Inissturt;
	
	//配置FSMC数据线 D0-D15
	//D0
  ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	ILI9341_GPIO_Inissturt.GPIO_Pin=ILI9341_D0_PIN ;
  ILI9341_GPIO_Inissturt.GPIO_Speed =  GPIO_Speed_50MHz;
  GPIO_Init(ILI9341_D0_PORT, &ILI9341_GPIO_Inissturt);
	//D1
	ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	ILI9341_GPIO_Inissturt.GPIO_Pin=   ILI9341_D1_PIN ;
  ILI9341_GPIO_Inissturt.GPIO_Speed =  GPIO_Speed_50MHz;
  GPIO_Init(ILI9341_D1_PORT,&ILI9341_GPIO_Inissturt);
	//D2
	ILI9341_GPIO_Inissturt.GPIO_Mode =ILI9341_D2_PIN ;
  ILI9341_GPIO_Inissturt.GPIO_Speed =  GPIO_Speed_50MHz;
  GPIO_Init(ILI9341_D2_PORT, &ILI9341_GPIO_Inissturt);
	//D3
	ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	ILI9341_GPIO_Inissturt.GPIO_Pin=  ILI9341_D3_PIN ; 
  ILI9341_GPIO_Inissturt.GPIO_Speed =  GPIO_Speed_50MHz;
  GPIO_Init(ILI9341_D3_PORT, &ILI9341_GPIO_Inissturt);
	//D4
	ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	ILI9341_GPIO_Inissturt.GPIO_Pin=   ILI9341_D4_PIN ;
  ILI9341_GPIO_Inissturt.GPIO_Speed =  GPIO_Speed_50MHz;
  GPIO_Init(ILI9341_D4_PORT, &ILI9341_GPIO_Inissturt);
	//D5
	ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	ILI9341_GPIO_Inissturt.GPIO_Pin=   ILI9341_D5_PIN ;
  ILI9341_GPIO_Inissturt.GPIO_Speed =  GPIO_Speed_50MHz;
  GPIO_Init(ILI9341_D5_PORT, &ILI9341_GPIO_Inissturt);
	//D6
	ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	ILI9341_GPIO_Inissturt.GPIO_Pin= ILI9341_D6_PIN ;
  ILI9341_GPIO_Inissturt.GPIO_Speed =  GPIO_Speed_50MHz;
  GPIO_Init(ILI9341_D6_PORT, &ILI9341_GPIO_Inissturt);
	//D7
	ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	ILI9341_GPIO_Inissturt.GPIO_Pin=      ILI9341_D7_PIN ;
  ILI9341_GPIO_Inissturt.GPIO_Speed =  GPIO_Speed_50MHz;
  GPIO_Init(ILI9341_D7_PORT, &ILI9341_GPIO_Inissturt);
	//D8
	ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	ILI9341_GPIO_Inissturt.GPIO_Pin=   ILI9341_D8_PIN ;
  ILI9341_GPIO_Inissturt.GPIO_Speed =  GPIO_Speed_50MHz;
  GPIO_Init(ILI9341_D8_PORT, &ILI9341_GPIO_Inissturt);
	//D9
	ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	ILI9341_GPIO_Inissturt.GPIO_Pin=    ILI9341_D9_PIN ;
  ILI9341_GPIO_Inissturt.GPIO_Speed =  GPIO_Speed_50MHz;
  GPIO_Init(ILI9341_D9_PORT, &ILI9341_GPIO_Inissturt);
	//D10
	ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	ILI9341_GPIO_Inissturt.GPIO_Pin=  ILI9341_D10_PIN ;
  ILI9341_GPIO_Inissturt.GPIO_Speed =  GPIO_Speed_50MHz;
  GPIO_Init(ILI9341_D10_PORT, &ILI9341_GPIO_Inissturt);
	//D11
	ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	ILI9341_GPIO_Inissturt.GPIO_Pin=  ILI9341_D11_PIN ; 
  ILI9341_GPIO_Inissturt.GPIO_Speed =  GPIO_Speed_50MHz;
  GPIO_Init(ILI9341_D11_PORT, &ILI9341_GPIO_Inissturt);
	//D12
	ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	ILI9341_GPIO_Inissturt.GPIO_Pin=   ILI9341_D12_PIN ;
  ILI9341_GPIO_Inissturt.GPIO_Speed =  GPIO_Speed_50MHz;
  GPIO_Init(ILI9341_D12_PORT, &ILI9341_GPIO_Inissturt);
	//D13
	ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	ILI9341_GPIO_Inissturt.GPIO_Pin=  ILI9341_D13_PIN  ;
  ILI9341_GPIO_Inissturt.GPIO_Speed =  GPIO_Speed_50MHz;
  GPIO_Init(ILI9341_D13_PORT, &ILI9341_GPIO_Inissturt);
	//D14
	ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	ILI9341_GPIO_Inissturt.GPIO_Pin=   ILI9341_D14_PIN ;
  ILI9341_GPIO_Inissturt.GPIO_Speed =  GPIO_Speed_50MHz;
  GPIO_Init(ILI9341_D14_PORT, &ILI9341_GPIO_Inissturt);
	//D15
	ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	ILI9341_GPIO_Inissturt.GPIO_Pin=  ILI9341_D15_PIN ;
  ILI9341_GPIO_Inissturt.GPIO_Speed =  GPIO_Speed_50MHz;
  GPIO_Init(ILI9341_D15_PORT, &ILI9341_GPIO_Inissturt);
	
	
	//FSMC相对应的控制线
	//RD
	ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	ILI9341_GPIO_Inissturt.GPIO_Speed =GPIO_Speed_50MHz;
	ILI9341_GPIO_Inissturt.GPIO_Pin =  ILI9341_RD_PIN ;
	GPIO_Init(ILI9341_RD_PORT, &ILI9341_GPIO_Inissturt);
	//WR
		ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	  ILI9341_GPIO_Inissturt.GPIO_Speed =GPIO_Speed_50MHz;
	ILI9341_GPIO_Inissturt.GPIO_Pin =ILI9341_WR_PIN ;
	GPIO_Init(ILI9341_WR_PORT, &ILI9341_GPIO_Inissturt);
	
	//CS
		ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	ILI9341_GPIO_Inissturt.GPIO_Speed =GPIO_Speed_50MHz;
	ILI9341_GPIO_Inissturt.GPIO_Pin =ILI9341_CS_PIN ;
	GPIO_Init(ILI9341_CS_PORT, &ILI9341_GPIO_Inissturt);
	
	
	//DC
		ILI9341_GPIO_Inissturt.GPIO_Mode =GPIO_Mode_AF_PP;
	ILI9341_GPIO_Inissturt.GPIO_Speed =GPIO_Speed_50MHz;
	ILI9341_GPIO_Inissturt.GPIO_Pin =ILI9341_DC_PIN ;
	GPIO_Init(ILI9341_DC_PORT, &ILI9341_GPIO_Inissturt);
	
	
	//RST
		ILI9341_GPIO_Inissturt.GPIO_Mode = GPIO_Mode_Out_PP;
	ILI9341_GPIO_Inissturt.GPIO_Speed =GPIO_Speed_50MHz;
	ILI9341_GPIO_Inissturt.GPIO_Pin =ILI9341_RST_PIN ;
	GPIO_Init(ILI9341_RST_PORT, &ILI9341_GPIO_Inissturt);
	
	
	//BK
		ILI9341_GPIO_Inissturt.GPIO_Mode = GPIO_Mode_Out_PP;
	ILI9341_GPIO_Inissturt.GPIO_Speed =GPIO_Speed_50MHz;
	ILI9341_GPIO_Inissturt.GPIO_Pin =ILI9341_BK_PIN ;
	GPIO_Init(ILI9341_BK_PORT, &ILI9341_GPIO_Inissturt);
	
	
}

#ifndef      __BSP_ILI9341_LCD_H
#define	     __BSP_ILI9341_LCD_H

#include "stm32f10x.h"
#include "./font/fonts.h"

/***************************************************************************************
2^26 =0X0400 0000 = 64MB, 每个 BANK 有 4*64MB = 256MB
64MB:FSMC_Bank1_NORSRAM1:0X6000 0000 ~ 0X63FF FFFF
64MB:FSMC_Bank1_NORSRAM2:0X6400 0000 ~ 0X67FF FFFF
64MB:FSMC_Bank1_NORSRAM3:0X6800 0000 ~ 0X6BFF FFFF
64MB:FSMC_Bank1_NORSRAM4:0X6C00 0000 ~ 0X6FFF FFFF

选择 BANK1-BORSRAM1 连接 TFT, 地址范围为 0X6000 0000 ~ 0X63FF FFFF
FSMC_A16 连接 LCD 的 DC(命令/数据选择)引脚 命令地址基地址 = 0X60000000
RAM 基地址 = 0X60020000 = 0X60000000+2^16*2 = 0X60000000 + 0X20000 = 0X60020000
当选择不同的地址区域时, 地址需要相应调整
****************************************************************************************/

/******************************* ILI9341 显示屏的 FSMC 地址定义 ***************************/
//FSMC_Bank1_NORSRAM 映射到 LCD 命令的地址
#define      FSMC_Addr_ILI9341_CMD         ( ( uint32_t ) 0x60000000 )

//FSMC_Bank1_NORSRAM 映射到 LCD 数据的地址
#define      FSMC_Addr_ILI9341_DATA        ( ( uint32_t ) 0x60020000 )

// 选择使用的 NOR/SRAM 块 定义为 FSMC_Bank1_NORSRAM1
#define      FSMC_Bank1_NORSRAMx           FSMC_Bank1_NORSRAM1

/******************************* ILI9341 显示屏 8080 通信接口定义 ***************************/
/******引脚定义*****/
// 片选,选择 NOR/SRAM 块 定义时钟使能
#define      ILI9341_CS_CLK                RCC_APB2Periph_GPIOD   
#define      ILI9341_CS_PORT               GPIOD
#define      ILI9341_CS_PIN                GPIO_Pin_7

//DC 引脚,用于选择 FSMC 的地址是命令还是数据,本定义为连接 LCD 时使用的地址
//PD11 为 FSMC_A16
#define      ILI9341_DC_CLK                RCC_APB2Periph_GPIOD   
#define      ILI9341_DC_PORT               GPIOD
#define      ILI9341_DC_PIN                GPIO_Pin_11

// 写使能
#define      ILI9341_WR_CLK                RCC_APB2Periph_GPIOD   
#define      ILI9341_WR_PORT               GPIOD
#define      ILI9341_WR_PIN                GPIO_Pin_5

// 读使能
#define      ILI9341_RD_CLK                RCC_APB2Periph_GPIOD   
#define      ILI9341_RD_PORT               GPIOD
#define      ILI9341_RD_PIN                GPIO_Pin_4

// 复位引脚
#define      ILI9341_RST_CLK               RCC_APB2Periph_GPIOE
#define      ILI9341_RST_PORT              GPIOE
#define      ILI9341_RST_PIN               GPIO_Pin_1

// 背光引脚
#define      ILI9341_BK_CLK                RCC_APB2Periph_GPIOD    
#define      ILI9341_BK_PORT               GPIOD
#define      ILI9341_BK_PIN                GPIO_Pin_12

/********数据引脚定义**************/
#define      ILI9341_D0_CLK                RCC_APB2Periph_GPIOD   
#define      ILI9341_D0_PORT               GPIOD
#define      ILI9341_D0_PIN                GPIO_Pin_14

#define      ILI9341_D1_CLK                RCC_APB2Periph_GPIOD   
#define      ILI9341_D1_PORT               GPIOD
#define      ILI9341_D1_PIN                GPIO_Pin_15

#define      ILI9341_D2_CLK                RCC_APB2Periph_GPIOD   
#define      ILI9341_D2_PORT               GPIOD
#define      ILI9341_D2_PIN                GPIO_Pin_0

#define      ILI9341_D3_CLK                RCC_APB2Periph_GPIOD  
#define      ILI9341_D3_PORT               GPIOD
#define      ILI9341_D3_PIN                GPIO_Pin_1

#define      ILI9341_D4_CLK                RCC_APB2Periph_GPIOE   
#define      ILI9341_D4_PORT               GPIOE
#define      ILI9341_D4_PIN                GPIO_Pin_7

#define      ILI9341_D5_CLK                RCC_APB2Periph_GPIOE   
#define      ILI9341_D5_PORT               GPIOE
#define      ILI9341_D5_PIN                GPIO_Pin_8

#define      ILI9341_D6_CLK                RCC_APB2Periph_GPIOE   
#define      ILI9341_D6_PORT               GPIOE
#define      ILI9341_D6_PIN                GPIO_Pin_9

#define      ILI9341_D7_CLK                RCC_APB2Periph_GPIOE  
#define      ILI9341_D7_PORT               GPIOE
#define      ILI9341_D7_PIN                GPIO_Pin_10

#define      ILI9341_D8_CLK                RCC_APB2Periph_GPIOE   
#define      ILI9341_D8_PORT               GPIOE
#define      ILI9341_D8_PIN                GPIO_Pin_11

#define      ILI9341_D9_CLK                RCC_APB2Periph_GPIOE   
#define      ILI9341_D9_PORT               GPIOE
#define      ILI9341_D9_PIN                GPIO_Pin_12

#define      ILI9341_D10_CLK                RCC_APB2Periph_GPIOE   
#define      ILI9341_D10_PORT               GPIOE
#define      ILI9341_D10_PIN                GPIO_Pin_13

#define      ILI9341_D11_CLK                RCC_APB2Periph_GPIOE   
#define      ILI9341_D11_PORT               GPIOE
#define      ILI9341_D11_PIN                GPIO_Pin_14

#define      ILI9341_D12_CLK                RCC_APB2Periph_GPIOE   
#define      ILI9341_D12_PORT               GPIOE
#define      ILI9341_D12_PIN                GPIO_Pin_15

#define      ILI9341_D13_CLK                RCC_APB2Periph_GPIOD   
#define      ILI9341_D13_PORT               GPIOD
#define      ILI9341_D13_PIN                GPIO_Pin_8

#define      ILI9341_D14_CLK                RCC_APB2Periph_GPIOD   
#define      ILI9341_D14_PORT               GPIOD
#define      ILI9341_D14_PIN                GPIO_Pin_9

#define      ILI9341_D15_CLK                RCC_APB2Periph_GPIOD   
#define      ILI9341_D15_PORT               GPIOD
#define      ILI9341_D15_PIN                GPIO_Pin_10

/*************************************** 调试宏定义 ******************************************/
#define      DEBUG_DELAY()                

/***************************** ILI934 显示屏相关的参数定义 ***************************/
#define      ILI9341_DispWindow_X_Star		    0     // 起始显示窗口 X 坐标
#define      ILI9341_DispWindow_Y_Star		    0     // 起始显示窗口 Y 坐标
#define 			ILI9341_LESS_PIXEL	  							240			// 显示屏较小尺寸方向的像素数
#define 			ILI9341_MORE_PIXEL	 								320			// 显示屏较大尺寸方向的像素数

// 根据显示屏扫描方向而变化的 Y 坐标相关
// 调用 ILI9341_GramScan 函数设置扫描方向时会用到 这里声明为外部变量
extern uint16_t LCD_X_LENGTH,LCD_Y_LENGTH; 

// 显示屏扫描模式
// 定义取值为 0-7
extern uint8_t LCD_SCAN_MODE;

/******************************* 定义 ILI934 显示屏常用颜色 ********************************/
#define      BACKGROUND		                BLACK   // 默认背景颜色为黑色

#define      WHITE		 		           0xFFFF	   // 白色
#define      BLACK                         0x0000	   // 黑色 
#define      GREY                          0xF7DE	   // 灰色 
#define      BLUE                          0x001F	   // 蓝色 
#define      BLUE2                         0x051F	   // 浅蓝色 
#define      RED                           0xF800	   // 红色 
#define      MAGENTA                       0xF81F	   // 品红色,即洋红色 
#define      GREEN                         0x07E0	   // 绿色 
#define      CYAN                          0x7FFF	   // 青色,即蓝绿色 
#define      YELLOW                        0xFFE0	   // 黄色 
#define      BRED                          0xF81F
#define      GRED                          0xFFE0
#define      GBLUE                         0x07FF

/******************************* 定义 ILI934 常用命令 *******************************/
#define      CMD_SetCoordinateX		 		    0x2A	     // 设置 X 坐标命令
#define      CMD_SetCoordinateY		 		    0x2B	     // 设置 Y 坐标命令
#define      CMD_SetPixel		 		        0x2C	     // 设置像素命令

/* 定义 LCD 驱动芯片 ID */
#define     LCDID_UNKNOWN             0
#define     LCDID_ILI9341             0x9341
#define     LCDID_ST7789V             0x8552

/********************************** 声明 ILI934 函数 ***************************************/
void                     ILI9341_Init                    ( void );
uint16_t                 ILI9341_ReadID                 ( void );
void                     ILI9341_Rst                     ( void );
void                     ILI9341_BackLed_Control         ( FunctionalState enumState );
void                     ILI9341_GramScan                ( uint8_t ucOtion );
void                     ILI9341_OpenWindow              ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight );
void                     ILI9341_Clear                   ( uint16_t usX, uint16_t usY, uint16_t usWidth, uint16_t usHeight );
void                     ILI9341_SetPointPixel           ( uint16_t usX, uint16_t usY );
uint16_t                 ILI9341_GetPointPixel           ( uint16_t usX , uint16_t usY );
void                     ILI9341_DrawLine                ( uint16_t usX1, uint16_t usY1, uint16_t usX2, uint16_t usY2 );
void                     ILI9341_DrawRectangle           ( uint16_t usX_Start, uint16_t usY_Start, uint16_t usWidth, uint16_t usHeight,uint8_t ucFilled );
void                     ILI9341_DrawCircle              ( uint16_t usX_Center, uint16_t usY_Center, uint16_t usRadius, uint8_t ucFilled );
void                     ILI9341_DispChar_EN             ( uint16_t usX, uint16_t usY, const char cChar );
void                     ILI9341_DispStringLine_EN      ( uint16_t line, char * pStr );
void                     ILI9341_DispString_EN      			( uint16_t usX, uint16_t usY, char * pStr );
void 					 ILI9341_DispString_EN_YDir 		(   uint16_t usX,uint16_t usY ,  char * pStr );

void 											LCD_SetFont											(sFONT *fonts);
sFONT 										*LCD_GetFont											(void);
void 											LCD_ClearLine										(uint16_t Line);
void 											LCD_SetBackColor								(uint16_t Color);
void 											LCD_SetTextColor								(uint16_t Color)	;
void 											LCD_SetColors										(uint16_t TextColor, uint16_t BackColor);
void 											LCD_GetColors										(uint16_t *TextColor, uint16_t *BackColor);


#endif /* __BSP_ILI9341_ILI9341_H */
```

 
步骤 3:配置 FSMC 读取/写入时序并向液晶屏发送命令及数据

在 8080 总线中,时序很重要,尤其是 读取写入操作。FSMC 可以配置成模拟这种时序。你需要设置适当的 地址设置时间数据设置时间读写周期等。上面的代码中的 FSMC_NORSRAMInitTypeDef 结构体就是用来配置这些时序参数的。根据《STM32参考手册》对FSu 问NOR Flash的说明 (见图26-30),STM32内部访问地址时使用的是内部HADDR点它是需要转换到外部存储器的內部AHB地址线,它是字节地址(8位),而存储器访问是按字节访问的,因此接到存储器的地址线按照存储器的数据宽度有所不同。

数据宽度(位)地址线连接对应关系最大访问存储器空间
8 位HADDR [25:0] 与 FSMC_A [25:0] 对应相连64M 字节 ×8 = 512M 位
16 位HADDR [25:1] 与 FSMC_A [24:0] 对应相连,HADDR [0] 未接64M 字节 ×16 = 1024M 位
(注:原内容 “64M 字节 12×16=512M 位” 可能有误,按照逻辑推测为 64M 字节 ×16 = 1024M 位)

另外,对于 16 位宽度的外部存储器补充说明:FSMC 将在内部使用 HADDR [25:1] 产生外部存储器的地址 FSMC_A [24:0],不论外部存储器的宽度是多少(16 位或 8 位),FSMC_A [0] 始终应该连到外部存储器的地址线 A0。

新调整计算公式。
·要使 FSMC_A16地址线为高电平,实质是访问内部HADDR地址的第(16+1)位为1即可,使用 0X6000 0000~0X63FF FFPF 内的任意地址,作如下运算:
使FSMC_A16地址线为高电平:0X6000 0000 /= (1<<(16+1))=0x6002 0000
·要使 FSMC_A16地址线为低电平,实质是访问内部HADDR地址的第(16+1)位为0即可,使用 0X6000 0000~0X63FF FFFF 内的任意地址,作如下运算:
使FSMC_A16地址线为低电平:0X6000 0000 &= ~(1<<(16+1)) =0x6000 0000



__inline void ILI9341_Write_Cmd ( uint16_t usCmd )
{
	* ( __IO uint16_t * ) ( FSMC_Addr_ILI9341_CMD ) = usCmd;
	
}

/**
  * @brief  向ILI9341写入数据
  * @param  usData :要写入的数据
  * @retval 无
  */	
__inline void ILI9341_Write_Data ( uint16_t usData )
{
	* ( __IO uint16_t * ) ( FSMC_Addr_ILI9341_DATA ) = usData;
	
}


/**
  * @brief  从ILI9341读取数据
  * @param  无
  * @retval 读取到的数据
  */	
__inline uint16_t ILI9341_Read_Data ( void )
{
	return ( * ( __IO uint16_t * ) ( FSMC_Addr_ILI9341_DATA ) );
	
}
/**
  * @brief  用于 ILI9341 简单延时函数
  * @param  nCount :延时计数值
  * @retval 无
  */	
//向液晶屏写入初始化配置
static void  ILI9341_REG_Config()
{
	//包括上电,背光源,伽马参数,分辨率,像素格式具体参考《ILI9431》数据手册
	delay_ms (10);
	ILI9341_Write_Cmd(0xCF);
  ILI9341_Write_Data(0x00);
  ILI9341_Write_Data(0x81);
	ILI9341_Write_Data(0x30);
	
	delay_ms (10);
	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) */
   delay_ms (10);
    ILI9341_Write_Cmd ( 0xE8 );
    ILI9341_Write_Data ( 0x85 );
    ILI9341_Write_Data ( 0x10 );
    ILI9341_Write_Data ( 0x78 );
    
    /*  Power control A (CBh) */
    delay_ms (10);
    ILI9341_Write_Cmd ( 0xCB );
    ILI9341_Write_Data ( 0x39 );
    ILI9341_Write_Data ( 0x2C );
    ILI9341_Write_Data ( 0x00 );
    ILI9341_Write_Data ( 0x34 );
    //ILI9341_Write_Data ( 0x02 );
    ILI9341_Write_Data ( 0x06 ); //原来是0x02改为0x06可防止液晶显示白屏时有条纹的情况
    
    /* Pump ratio control (F7h) */
   delay_ms (10);
    ILI9341_Write_Cmd ( 0xF7 );
    ILI9341_Write_Data ( 0x20 );
    
    /* Driver timing control B */
    delay_ms (10);
    ILI9341_Write_Cmd ( 0xEA );
    ILI9341_Write_Data ( 0x00 );
    ILI9341_Write_Data ( 0x00 );
    
    /* Frame Rate Control (In Normal Mode/Full Colors) (B1h) */
   delay_ms (10);
    ILI9341_Write_Cmd ( 0xB1 );
    ILI9341_Write_Data ( 0x00 );
    ILI9341_Write_Data ( 0x1B );
    
    /*  Display Function Control (B6h) */
 delay_ms (10);
    ILI9341_Write_Cmd ( 0xB6 );
    ILI9341_Write_Data ( 0x0A );
    ILI9341_Write_Data ( 0xA2 );
    
    /* Power Control 1 (C0h) */
   delay_ms (10);
    ILI9341_Write_Cmd ( 0xC0 );
    ILI9341_Write_Data ( 0x35 );
    
    /* Power Control 2 (C1h) */
   delay_ms (10);
    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 );
   delay_ms (10);
    
    /* 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 */
   delay_ms (10);
    ILI9341_Write_Cmd ( 0x36 ); 	
    ILI9341_Write_Data ( 0xC8 );    /*竖屏  左上角到 (起点)到右下角 (终点)扫描方式*/
    delay_ms (10);
    
    /* 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 */
    delay_ms (10);
    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 ); 
	}
  
  else if(lcdid == LCDID_ST7789V)
  {
    /*  Power control B (CFh)  */
    DEBUG_DELAY  ();
    ILI9341_Write_Cmd ( 0xCF  );
    ILI9341_Write_Data ( 0x00  );
    ILI9341_Write_Data ( 0xC1  );
    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 );
    
    /* 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 );
    
    
    /* Power Control 1 (C0h) */
    DEBUG_DELAY ();
    ILI9341_Write_Cmd ( 0xC0 );   //Power control
    ILI9341_Write_Data ( 0x21 );  //VRH[5:0]
    
    /* Power Control 2 (C1h) */
    DEBUG_DELAY ();
    ILI9341_Write_Cmd ( 0xC1 );   //Power control
    ILI9341_Write_Data ( 0x11 );  //SAP[2:0];BT[3:0]
    
    /* VCOM Control 1 (C5h) */
    ILI9341_Write_Cmd ( 0xC5 );
    ILI9341_Write_Data ( 0x2D );
    ILI9341_Write_Data ( 0x33 );
    
    /*  VCOM Control 2 (C7h)  */
//	ILI9341_Write_Cmd ( 0xC7 );
//	ILI9341_Write_Data ( 0XC0 );
    
    /* memory access control set */
    DEBUG_DELAY ();
    ILI9341_Write_Cmd ( 0x36 );   //Memory Access Control
    ILI9341_Write_Data ( 0x00 );  /*竖屏  左上角到 (起点)到右下角 (终点)扫描方式*/
    DEBUG_DELAY ();
    
    ILI9341_Write_Cmd(0x3A);   
    ILI9341_Write_Data(0x55); 
    
      /* Frame Rate Control (In Normal Mode/Full Colors) (B1h) */
    DEBUG_DELAY ();
    ILI9341_Write_Cmd ( 0xB1 );
    ILI9341_Write_Data ( 0x00 );
    ILI9341_Write_Data ( 0x17 );
    
    /*  Display Function Control (B6h) */
    DEBUG_DELAY ();
    ILI9341_Write_Cmd ( 0xB6 );
    ILI9341_Write_Data ( 0x0A );
    ILI9341_Write_Data ( 0xA2 );
    
    ILI9341_Write_Cmd(0xF6);    			
    ILI9341_Write_Data(0x01); 
    ILI9341_Write_Data(0x30); 
    
    /* 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); //Positive gamma
    ILI9341_Write_Data(0xd0);         
    ILI9341_Write_Data(0x00); 
    ILI9341_Write_Data(0x02); 
    ILI9341_Write_Data(0x07); 
    ILI9341_Write_Data(0x0b); 
    ILI9341_Write_Data(0x1a); 
    ILI9341_Write_Data(0x31); 
    ILI9341_Write_Data(0x54); 
    ILI9341_Write_Data(0x40); 
    ILI9341_Write_Data(0x29); 
    ILI9341_Write_Data(0x12); 
    ILI9341_Write_Data(0x12); 
    ILI9341_Write_Data(0x12); 
    ILI9341_Write_Data(0x17);

    /* Negative Gamma Correction (E1h) */
    ILI9341_Write_Cmd(0xe1); //Negative gamma
    ILI9341_Write_Data(0xd0); 
    ILI9341_Write_Data(0x00); 
    ILI9341_Write_Data(0x02); 
    ILI9341_Write_Data(0x07); 
    ILI9341_Write_Data(0x05); 
    ILI9341_Write_Data(0x25); 
    ILI9341_Write_Data(0x2d); 
    ILI9341_Write_Data(0x44); 
    ILI9341_Write_Data(0x45); 
    ILI9341_Write_Data(0x1c); 
    ILI9341_Write_Data(0x18); 
    ILI9341_Write_Data(0x16); 
    ILI9341_Write_Data(0x1c); 
    ILI9341_Write_Data(0x1d); 
  
	
	/* 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 );
	
	
    /* Sleep Out (11h)  */
    ILI9341_Write_Cmd ( 0x11 );	  //Exit Sleep
    ILI9341_Delay ( 0xAFFf<<2 );
    DEBUG_DELAY ();
    
    /* Display ON (29h) */
    ILI9341_Write_Cmd ( 0x29 );   //Display on
    
    ILI9341_Write_Cmd(0x2c);
  }
	
}


/**
 * @brief  ILI9341初始化函数,如果要用到lcd,一定要调用这个函数
 * @param  无
 * @retval 无
 */

}
步骤 4:使用 FSMC发送控制命令并初始化液晶屏

一旦 FSMC 配置完成,你就可以通过它与外部设备交换数据了。假设你有一个外部存储设备连接到 FSMC 上,并且你使用 8080 时序进行访问,数据将通过如下方式传输:

void ILI9341_Init ( void )
{
	ILI9341_GPIO_Config ();
	ILI9341_FSMC_Config ();
	
	ILI9341_BackLed_Control ( ENABLE );      //点亮LCD背光灯
	ILI9341_Rst ();
	ILI9341_REG_Config ();
	
	//设置默认扫描方向,其中 6 模式为大部分液晶例程的默认显示方向  
	ILI9341_GramScan(LCD_SCAN_MODE);
}


/**
 * @brief  ILI9341背光LED控制
 * @param  enumState :决定是否使能背光LED
  *   该参数为以下值之一:
  *     @arg ENABLE :使能背光LED
  *     @arg DISABLE :禁用背光LED
 * @retval 无
 */
void ILI9341_BackLed_Control ( FunctionalState enumState )
{
	if ( enumState )
		GPIO_ResetBits ( ILI9341_BK_PORT, ILI9341_BK_PIN );	
	else
		GPIO_SetBits ( ILI9341_BK_PORT, ILI9341_BK_PIN );
		
}


/**
 * @brief  读取LCD驱动芯片ID函数,可用于测试底层的读写函数
 * @param  无
 * @retval 正常时返回值为LCD驱动芯片ID: LCDID_ILI9341/LCDID_ST7789V
 *         否则返回: LCDID_UNKNOWN
 */
uint16_t ILI9341_ReadID(void)
{
	uint16_t id = 0;
	
	ILI9341_Write_Cmd(0x04);
	ILI9341_Read_Data();
	ILI9341_Read_Data();
	id = ILI9341_Read_Data();
	id <<= 8;
	id |= ILI9341_Read_Data();
	
  if(id == LCDID_ST7789V)
  {
    return id;
  }
  else
  {
    ILI9341_Write_Cmd(0xD3);
    ILI9341_Read_Data();
    ILI9341_Read_Data();
    id = ILI9341_Read_Data();
    id <<= 8;
    id |= ILI9341_Read_Data();
    if(id == LCDID_ILI9341)
    {
      return id;
    }
  }
  
	return LCDID_UNKNOWN;
}


/**
 * @brief  ILI9341 软件复位
 * @param  无
 * @retval 无
 */
void ILI9341_Rst ( void )
{			
	GPIO_ResetBits ( ILI9341_RST_PORT, ILI9341_RST_PIN );	 //低电平复位

	ILI9341_Delay ( 0xAFF ); 					   

	GPIO_SetBits ( ILI9341_RST_PORT, ILI9341_RST_PIN );		 	 

	ILI9341_Delay ( 0xAFF ); 	
	
}




/**
 * @brief  设置ILI9341的GRAM的扫描方向 
 * @param  ucOption :选择GRAM的扫描方向 
 *     @arg 0-7 :参数可选值为0-7这八个方向
 *
 *	!!!其中0、3、5、6 模式适合从左至右显示文字,
 *				不推荐使用其它模式显示文字	其它模式显示文字会有镜像效果			
 *		
 *	其中0、2、4、6 模式的X方向像素为240,Y方向像素为320
 *	其中1、3、5、7 模式下X方向像素为320,Y方向像素为240
 *
 *	其中 6 模式为大部分液晶例程的默认显示方向
 *	其中 3 模式为摄像头例程使用的方向
 *	其中 0 模式为BMP图片显示例程使用的方向
 *
 * @retval 无
 * @note  坐标图例:A表示向上,V表示向下,<表示向左,>表示向右
					X表示X轴,Y表示Y轴

											 LCDID_ILI9341
------------------------------------------------------------
模式0:				.		模式1:		.	模式2:			.	模式3:					
					A		.					A		.		A					.		A									
					|		.					|		.		|					.		|							
					Y		.					X		.		Y					.		X					
					0		.					1		.		2					.		3					
	<--- X0 o		.	<----Y1	o		.		o 2X--->  .		o 3Y--->	
------------------------------------------------------------	
模式4:				.	模式5:			.	模式6:			.	模式7:					
	<--- X4 o		.	<--- Y5 o		.		o 6X--->  .		o 7Y--->	
					4		.					5		.		6					.		7	
					Y		.					X		.		Y					.		X						
					|		.					|		.		|					.		|							
					V		.					V		.		V					.		V		
---------------------------------------------------------				
											 LCD屏示例
								|-----------------|
								|			Logo		|
								|									|
								|									|
								|									|
								|									|
								|									|
								|									|
								|									|
								|									|
								|-----------------|
								屏幕正面(宽240,高320)
								
								
								
								
								      LCDID_ST7789V							
------------------------------------------------------------
模式0:				.		模式1:		.	模式2:			.	模式3:					
	o 0X--->  	.		o 1Y--->  .	<--- X2 o		.	<--- Y3 o				
	0						.		1					.					2		.					3			
	Y						.		X					.					Y		.					X		
	|						.		|					.					|		.					|			
	V								V					.					V		.					V		
------------------------------------------------------------	
模式4:				.	模式5:			.	模式6:		.	模式7:					
	A						.		A					.					A	.					A	
	|						.		|					.					|	.					|
	Y						.		X					.					Y	.					X				
	4						.		5					.					6	.					7				
	o 4X--->  	.		o 5Y--->  .	<--- X6 o	.	<--- Y7 o	
---------------------------------------------------------				
											 LCD屏示例
								|-----------------|
								|			Logo		|
								|									|
								|									|
								|									|
								|									|
								|									|
								|									|
								|									|
								|									|
								|-----------------|
								屏幕正面(宽240,高320)		

 *******************************************************/
void ILI9341_GramScan ( uint8_t ucOption )
{	
	//参数检查,只可输入0-7
	if(ucOption >7 )
		return;
	
	//根据模式更新LCD_SCAN_MODE的值,主要用于触摸屏选择计算参数
	LCD_SCAN_MODE = ucOption;
	
	//根据模式更新XY方向的像素宽度
	if(ucOption%2 == 0)	
	{
		//0 2 4 6模式下X方向像素宽度为240,Y方向为320
		LCD_X_LENGTH = ILI9341_LESS_PIXEL;
		LCD_Y_LENGTH =	ILI9341_MORE_PIXEL;
	}
	else				
	{
		//1 3 5 7模式下X方向像素宽度为320,Y方向为240
		LCD_X_LENGTH = ILI9341_MORE_PIXEL;
		LCD_Y_LENGTH =	ILI9341_LESS_PIXEL; 
	}

	//0x36命令参数的高3位可用于设置GRAM扫描方向	
	ILI9341_Write_Cmd ( 0x36 );
  if(lcdid == LCDID_ILI9341)
  {
    ILI9341_Write_Data ( 0x08 |(ucOption<<5));//根据ucOption的值设置LCD参数,共0-7种模式
  }
  else if(lcdid == LCDID_ST7789V)
  {
    ILI9341_Write_Data ( 0x00 |(ucOption<<5));//根据ucOption的值设置LCD参数,共0-7种模式
  }
	ILI9341_Write_Cmd ( CMD_SetCoordinateX ); 
	ILI9341_Write_Data ( 0x00 );		/* x 起始坐标高8位 */
	ILI9341_Write_Data ( 0x00 );		/* x 起始坐标低8位 */
	ILI9341_Write_Data ( ((LCD_X_LENGTH-1)>>8)&0xFF ); /* x 结束坐标高8位 */	
	ILI9341_Write_Data ( (LCD_X_LENGTH-1)&0xFF );				/* x 结束坐标低8位 */

	ILI9341_Write_Cmd ( CMD_SetCoordinateY ); 
	ILI9341_Write_Data ( 0x00 );		/* y 起始坐标高8位 */
	ILI9341_Write_Data ( 0x00 );		/* y 起始坐标低8位 */
	ILI9341_Write_Data ( ((LCD_Y_LENGTH-1)>>8)&0xFF );	/* y 结束坐标高8位 */	 
	ILI9341_Write_Data ( (LCD_Y_LENGTH-1)&0xFF );				/* y 结束坐标低8位 */

	/* write gram start */
	ILI9341_Write_Cmd ( CMD_SetPixel );	
}



/**
 * @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 )
{	
	ILI9341_Write_Cmd ( CMD_SetCoordinateX ); 				 /* 设置X坐标 */
	ILI9341_Write_Data ( usX >> 8  );	 /* 先高8位,然后低8位 */
	ILI9341_Write_Data ( usX & 0xff  );	 /* 设置起始点和结束点*/
	ILI9341_Write_Data ( ( usX + usWidth - 1 ) >> 8  );
	ILI9341_Write_Data ( ( usX + usWidth - 1 ) & 0xff  );

	ILI9341_Write_Cmd ( CMD_SetCoordinateY ); 			     /* 设置Y坐标*/
	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 );
		
	
}
/**
 * @brief  对ILI9341显示器的某一窗口以某种颜色进行清屏
 * @param  usX :在特定扫描方向下窗口的起点X坐标
 * @param  usY :在特定扫描方向下窗口的起点Y坐标
 * @param  usWidth :窗口的宽度
 * @param  usHeight :窗口的高度
 * @note 可使用LCD_SetBackColor、LCD_SetTextColor、LCD_SetColors函数设置颜色
 * @retval 无
步骤 5:编写液晶屏的绘制像素点函数

为了使 STM32 的 FSMC 与 8080 总线兼容,你需要确保 地址总线数据总线 的时序与 8080 标准保持一致。STM32 的 FSMC 提供了 地址设置时间数据设置时间 等配置选项,可以帮助你模拟 8080 的时序。

步骤 7:利用描点函数制作各种不同的液晶显示应用:

可以借助一下字摸软件辅助显示:

字符显示定制

  • 生成字符字模:能根据用户输入的字符或字符串,按照特定的字体、字号和编码规则,生成对应的字模数据。这些数据定义了每个字符在 LCD 屏幕上的像素点显示方式,比如哪些像素点应该点亮以形成特定的字符形状。
  • 支持多种字体和字号:可以提供多种不同风格的字体选择,如宋体、黑体、楷体等,还能支持不同大小的字号,以满足不同应用场景对字符显示效果的要求。在制作电子菜单时,可能需要较大的字号来显示菜单项名称,而在显示一些辅助信息时,则可以使用较小的字号。
  • 自定义字符创建:允许用户创建自定义的字符或符号。在一些特殊的应用中,标准字符集中的字符可能无法满足需求,用户可以通过字模工具绘制自己需要的特殊字符,如特定的商标标识、特殊的箭头符号等,然后生成相应的字模数据,在 LCD 上进行显示。

图形显示支持

  • 生成图形字模:除了字符字模,还能生成图形的字模数据。用户可以通过工具绘制简单的图形,如线条、矩形、圆形等,或者导入已有的图形文件,工具会将图形转换为 LCD 能够识别的字模数据,控制 LCD 上的像素点显示,从而呈现出所需的图形。
  • 图像显示处理:对于一些复杂的图像,字模工具可以进行处理,将其转换为适合 LCD 显示的字模数据。在显示图片时,工具会根据 LCD 的分辨率和色彩模式,对图像进行采样、量化等操作,生成对应的字模数据,使 LCD 能够尽可能清晰地显示图像。

与硬件开发集成

  • 代码生成:字模工具通常可以生成与特定硬件平台和编程语言兼容的代码。在嵌入式系统开发中,将生成的字模数据与底层驱动代码相结合,就能实现 LCD 对字符和图形的显示功能。以 C 语言为例,字模工具可以生成包含字模数据的 C 数组,开发人员可以直接将这些数组嵌入到项目代码中,通过调用相关函数来控制 LCD 显示相应的内容。
  • 硬件适配:不同型号的 LCD 具有不同的接口和显示特性,字模工具可以根据具体的 LCD 硬件参数进行配置,生成与之匹配的字模数据和控制代码。针对不同分辨率、色彩深度和通信接口(如 SPI、I2C 等)的 LCD,工具能够调整字模数据的格式和传输方式,确保数据能够正确地发送到 LCD 并实现正确显示。

提高开发效率

  • 可视化操作:大多数字模工具都具有可视化的操作界面,用户可以通过直观的图形化操作来输入字符、绘制图形、设置参数等,无需手动编写复杂的字模数据生成代码,大大降低了开发难度,提高了开发效率。
  • 快速预览和调试:提供实时预览功能,用户在生成字模数据的过程中,可以随时查看字符或图形在 LCD 上的显示效果,及时发现问题并进行调整。在开发过程中,如果发现显示效果不理想,可以立即返回字模工具中修改参数或重新生成字模数据,而无需重新编译和下载整个项目代码,节省了大量的调试时间。

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;
}


/**
  * @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);	/* 清屏,显示全黑 */

}
/*********************end of file*************************/

http://www.kler.cn/a/562494.html

相关文章:

  • 【Matlab仿真】Matlab Function中如何使用静态变量?
  • rust笔记10-多线程
  • MySQL 8.0 Enterprise Backup (MEB) 备份与恢复实践指南
  • 力扣hot100 —— 电话号码字母组合; 子集 (非回溯做法)简单易懂
  • ctfshow做题笔记—栈溢出—pwn57~pwn60
  • 基数排序:独特的排序之道
  • C++和OpenGL实现3D游戏编程【连载23】——几何着色器和法线可视化
  • Vue3 + Vite + TS,使用 配置项目别名属性:resolve
  • 补题A-E Codeforces Round 953 (Div. 2)
  • 【Java】数据类型
  • Vue中环境配置的若干问题解决
  • opencv边缘检测
  • mysql大数量表添加索引方案
  • 《AI 大模型 ChatGPT 的传奇》
  • uni-app 开发 App 、 H5 横屏签名(基于lime-signature)
  • 优选算法大集合(待更新)
  • Python 3.11 69 个内置函数(完整版)
  • 《Keras 3 使用 PointNet 进行点云分段》:此文为AI自动翻译
  • ktransformers 上的 DeepSeek-R1 671B open-webui
  • 7种内外网数据交换方案全解析 哪种安全、高效、合规?