一、相关概念介绍
1 光的三原色
RGB : 三原色(Red Green Blue) 在计算机中用16位进行表示 (5+ 6 +5)
2 嵌入式LCD显示模块接口类型
LCD的接口有多种,分类很细。主要看LCD的驱动方式和控制方式,目前彩色LCD的连接方式一般有这么几种:MCU模式、RGB模式、SPI模式、VSYNC模式、MDDI模式、DSI模式。
但应用比较多的就是M CU 模式 和RGB模式
2.1RGB模式
RGB模式是大屏采用较多的模式,比如我们电脑显示器。
对于RGB接口的LCM,主机输出的直接是每个像素的RGB数据,不需要进行变换(GAMMA校正等除外),对于这种接口,需要在主机部分有个LCD控制器(平常所说的显卡 ),以产生RGB数据和点、行、帧同步信号。

2.2MCU模式
因为主要针对单片机的领域在使用,因此得名,其主要特点是价格便宜。MCU-LCD接口的标准术语是Intel提出的8080总线标准(16位 并行),因此在很多文档中用I80来指MCU-LCD屏。
对于MCU接口的LCM(LCD Module),其内部的芯片就叫****LCD驱动器,****都带GRAM(显存)。主要功能是对主机发过的数据/命令,进行变换,变成每个像素的RGB数据,使之在屏上显示出来。

STM32需要发出特殊的指令,告知LCD芯片显示内容
这个指令分为两部分,(1)LCD的屏幕配置:分辨率,亮度(2)LCD屏幕的显示内容
传递数据有两种情况,(1)配置或内容(2)配置的数值
2.3总结对比两种模式的区别
- 控制主体的区别
- RGB 模式:也叫 RGB 接口模式,这种模式下,LCD 屏幕的显示时序(比如像素时钟、行同步、场同步等)是由外部的控制器来提供的,在 STM32 开发里,这个外部控制器一般就是 STM32 的 FMC(灵活存储控制器)或者专门的 LCD 控制器外设,屏幕只负责按照接收到的时序和数据来完成显示,自身不产生时序信号。
- MCU 模式:也叫 8080 并行接口模式,这种模式下,LCD 屏幕自身带有显存和显示控制器,STM32 只需要通过并行接口,把需要显示的数据写入到屏幕的显存中,之后的显示时序、数据的读取和屏幕刷新,都是由屏幕自己的控制器来完成的。
- 数据传输的区别
- RGB 模式:是一种同步的数据流传输,STM32 会按照屏幕要求的像素时钟,持续的向屏幕传输每一个像素的 RGB 数据,屏幕会实时的把接收到的数据显示出来,这种模式的延迟很低,适合显示动态的、高速变化的内容,比如视频。
- MCU 模式 :是一种异步的传输,STM32 只在需要更新显示内容的时候,才会把数据写入到屏幕的显存,写入完成之后,STM32 就可以去处理其他的任务,屏幕会从自己的显存中读取数据来完成显示,这种模式下,STM32 的资源占用会更低。


可以把这两种模式类比成:RGB 模式像是老师实时的把内容念给学生听,学生听完立刻就展示出来;而 MCU 模式像是老师把要讲的内容先写在学生的笔记本上,学生之后自己看笔记本的内容来展示。
3 液晶模块Z350IT002

Z350IT002。这是一款 TFT LCD(薄膜晶体管液晶显示器)模块。TFT LCD 以其优秀的显示性能和低功耗特性而广泛应用于各种电子设备。
3.1分辨率
模块的分辨率为320RGB × 480点阵,横向380像素点,纵向480像素点,像素是由三原色组成。
3.2构造
它由960个源(source)和480个门(gate)构成,源是横向的,门是控制纵向的像素点驱动。
3.3微控制器接口
Z350IT002 设计了易于通过微控制器访问和控制的接口。这使得它非常适合于嵌入式系统或其他需要直接由微控制器控制显示屏的应用。
3.4应用领域
Z350IT002 特别适用于中小尺寸的显示需求,如便携式设备、工业控制面板、小型嵌入式系统等。
3.5显示效果
作为一款TFT LCD,Z350IT002可以提供良好的颜色对比度和亮度,适合于需要中等分辨率和高色彩质量的应用。一款适用于多种中小型电子设备的TFT LCD显示模块,其易于微控制器集成的特点使其成为许多嵌入式应用和工业产品的理想选择。
4 控制器芯片ILI9486
液晶模块Z350IT002内部使用的控制芯片是:ILI9486。
ILI9486 是一款流行的 LCD 控制器芯片,由 Ilitek 公司生产,通常用于驱动中小尺寸的 TFT(薄膜晶体管)LCD 显示屏。具**有320RGBx480点的分辨率。**它包括960通道的源驱动器和480通道的门驱动器,以及用于320RGBx480点图形数据的345600字节GRAM和电源供应电路。
另外ILI9486支持多种接口类型,包括:
- 1并行CPU数据总线接口,支持8位、9位、16位和18位。
- 线和4线串行外设接口(SPI)。
- 符合RGB(16位/18位)数据总线,用于视频图像显示。
- 高速串行接口,提供一个数据和时钟通道,支持最高达500Mbps的MIPI DSI链路。
- 支持MDDI接口。

4 使用STM32的FSMC来实现8080时序


二、代码介绍
需求介绍:使用FSMC控制LCD显示我们想要的字符。
硬件电路图

- D0-D15是16位数据总线接口。分别接FSMC的D0-D15。
- RST是LCD复位引脚,低电平复位。接LCD-RST(PG15)。
- RD是读控制引脚,上升沿时读数据。接FSMC-NOE(PD4)。
- WR是写控制引脚,上升沿时写数据。接FSMC-NWE(PD5)。
- RS是数据或命令选择引脚RS=1写数据,RS=0写命令。接FSMC-A10(PG0)。
- CS是片选引脚,低电平有效。接FSMC-NE4(PG12)。
- LEDA是背光电源(3.0V-3.4V)引脚。
- LEDK是背光亮度控制引脚。通过LCD-BG(PB0)来驱动MOS管Q5的导通电流。可以通过给LCD-BG输出PWM波来控制背光的亮度。占空比越大,背光就会越亮。
- YD、XL、YU、XR是触摸屏控制引脚。

扩展LCD的时候,使用的是块1的地址,一共4*64MB = 256MB。
NE4, 所以地址范围是:0X6C00 0000 ~ 0X6FFF FFFF,寄存器的基地址是6C000000。

LCD我们选择的是16位宽度的,选择地址线时,我们选择的是A10接LCD的D/CX(数据/命令引脚)。
注意:当使用16位宽的外部存储器时,用HADDR[25:1]表示外部的FSMC_A[24:0],内部地址相当于左移了1位,所以计算地址的时候要注意。
当A10=0时,表示写命令 ,所以地址是:0x6C00 0000。
当A10=1时,表示写数据 ,所以地址是:0x6C00 0000 + 1<<11 = 0x6C00 0800
FSMC.c代码配置
cpp
/**
* FSMC - LCD引脚初始化
*/
void Dri_FSMC_LCD_GPIO_Init(void) {
// 1. 开启时钟
// F, G, D, E
RCC->APB2ENR |= RCC_APB2ENR_IOPDEN;
RCC->APB2ENR |= RCC_APB2ENR_IOPEEN;
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
RCC->APB2ENR |= RCC_APB2ENR_IOPGEN;
// 2. 配置引脚
// 工作模式: 复用推挽输出
// MODE(11), CNF(10)
// A10-A15
// !PG0-PG5
GPIOG->CRL |= GPIO_CRL_MODE0;
GPIOG->CRL |= GPIO_CRL_CNF0_1;
GPIOG->CRL &= ~GPIO_CRL_CNF0_0;
// D0-D1
// !PD14, PD15
GPIOD->CRH |= GPIO_CRH_MODE14;
GPIOD->CRH |= GPIO_CRH_CNF14_1;
GPIOD->CRH &= ~GPIO_CRH_CNF14_0;
GPIOD->CRH |= GPIO_CRH_MODE15;
GPIOD->CRH |= GPIO_CRH_CNF15_1;
GPIOD->CRH &= ~GPIO_CRH_CNF15_0;
// D2, D3
// !PD0, PD1
GPIOD->CRL |= GPIO_CRL_MODE0;
GPIOD->CRL |= GPIO_CRL_CNF0_1;
GPIOD->CRL &= ~GPIO_CRL_CNF0_0;
GPIOD->CRL |= GPIO_CRL_MODE1;
GPIOD->CRL |= GPIO_CRL_CNF1_1;
GPIOD->CRL &= ~GPIO_CRL_CNF1_0;
// D4-D12
// PE7-PE15
GPIOE->CRL |= GPIO_CRL_MODE7;
GPIOE->CRL |= GPIO_CRL_CNF7_1;
GPIOE->CRL &= ~GPIO_CRL_CNF7_0;
GPIOE->CRH |= GPIO_CRH_MODE8;
GPIOE->CRH |= GPIO_CRH_CNF8_1;
GPIOE->CRH &= ~GPIO_CRH_CNF8_0;
GPIOE->CRH |= GPIO_CRH_MODE9;
GPIOE->CRH |= GPIO_CRH_CNF9_1;
GPIOE->CRH &= ~GPIO_CRH_CNF9_0;
GPIOE->CRH |= GPIO_CRH_MODE10;
GPIOE->CRH |= GPIO_CRH_CNF10_1;
GPIOE->CRH &= ~GPIO_CRH_CNF10_0;
GPIOE->CRH |= GPIO_CRH_MODE11;
GPIOE->CRH |= GPIO_CRH_CNF11_1;
GPIOE->CRH &= ~GPIO_CRH_CNF11_0;
GPIOE->CRH |= GPIO_CRH_MODE12;
GPIOE->CRH |= GPIO_CRH_CNF12_1;
GPIOE->CRH &= ~GPIO_CRH_CNF12_0;
GPIOE->CRH |= GPIO_CRH_MODE13;
GPIOE->CRH |= GPIO_CRH_CNF13_1;
GPIOE->CRH &= ~GPIO_CRH_CNF13_0;
GPIOE->CRH |= GPIO_CRH_MODE14;
GPIOE->CRH |= GPIO_CRH_CNF14_1;
GPIOE->CRH &= ~GPIO_CRH_CNF14_0;
GPIOE->CRH |= GPIO_CRH_MODE15;
GPIOE->CRH |= GPIO_CRH_CNF15_1;
GPIOE->CRH &= ~GPIO_CRH_CNF15_0;
// D13-D15
// !PD8-PD10
GPIOD->CRH |= GPIO_CRH_MODE8;
GPIOD->CRH |= GPIO_CRH_CNF8_1;
GPIOD->CRH &= ~GPIO_CRH_CNF8_0;
GPIOD->CRH |= GPIO_CRH_MODE9;
GPIOD->CRH |= GPIO_CRH_CNF9_1;
GPIOD->CRH &= ~GPIO_CRH_CNF9_0;
GPIOD->CRH |= GPIO_CRH_MODE10;
GPIOD->CRH |= GPIO_CRH_CNF10_1;
GPIOD->CRH &= ~GPIO_CRH_CNF10_0;
// CS(NE4), NWE, NOE
// !PG12, PD5, PD4
GPIOG->CRH |= GPIO_CRH_MODE12;
GPIOG->CRH |= GPIO_CRH_CNF12_1;
GPIOG->CRH &= ~GPIO_CRH_CNF12_0;
GPIOD->CRL |= GPIO_CRL_MODE4;
GPIOD->CRL |= GPIO_CRL_CNF4_1;
GPIOD->CRL &= ~GPIO_CRL_CNF4_0;
GPIOD->CRL |= GPIO_CRL_MODE5;
GPIOD->CRL |= GPIO_CRL_CNF5_1;
GPIOD->CRL &= ~GPIO_CRL_CNF5_0;
// LCD_RST, LCD_BG
// 通用推挽输出
// !PG15, PB0
GPIOG->CRH |= GPIO_CRH_MODE15;
GPIOG->CRH &= ~GPIO_CRH_CNF15_1;
GPIOG->CRH &= ~GPIO_CRH_CNF15_0;
GPIOB->CRL |= GPIO_CRL_MODE0;
GPIOB->CRL &= ~GPIO_CRL_CNF0_1;
GPIOB->CRL &= ~GPIO_CRL_CNF0_0;
}
/**
* FSMC - LCD寄存器初始化
*/
void Dri_FSMC_LCD_Register_Init(void) {
// 1. 开启时钟
RCC->AHBENR |= RCC_AHBENR_FSMCEN;
// 2. 配置寄存器
// BCR -> 4
// BTR -> 5
FSMC_Bank1->BTCR[6] |= FSMC_BCR4_WREN; // 写使能
FSMC_Bank1->BTCR[6] &= ~FSMC_BCR4_FACCEN; // 不访问FLASH
FSMC_Bank1->BTCR[6] &= ~FSMC_BCR4_MWID_1; // 数据宽度
FSMC_Bank1->BTCR[6] |= FSMC_BCR4_MWID_0;
FSMC_Bank1->BTCR[6] &= ~FSMC_BCR4_MTYP; // 存储类型:SRAM
FSMC_Bank1->BTCR[6] &= ~FSMC_BCR4_MUXEN; // 地址、数据线不复用
FSMC_Bank1->BTCR[6] |= FSMC_BCR4_MBKEN; // Bank使能
FSMC_Bank1->BTCR[7] &= ~FSMC_BTR4_ADDSET;
FSMC_Bank1->BTCR[7] |= (71 << 8); // 71
}
/**
* FSMC - LCD液晶初始化
*/
void Dri_FSMC_LCD_Init(void) {
// 配置引脚
Dri_FSMC_LCD_GPIO_Init();
// 配置FSMC(寄存器)
Dri_FSMC_LCD_Register_Init();
}
LCD.c代码
cpp
#define LCD_ADDRESS_CMD (uint16_t *)(0x6C000000)
#define LCD_ADDRESS_DATA (uint16_t *)(0x6C000800)
/**
* LCD液晶屏 - 开启背光
*/
void Int_LCD_Background(void) {
// PB0 -> 拉高电平
GPIOB->ODR |= GPIO_ODR_ODR0;
SysTick_DelayMs(5);
}
/**
* LCD液晶屏 - 复位
*/
void Int_LCD_Reset(void) {
// PG15 -> 拉低电平
GPIOG->ODR &= ~GPIO_ODR_ODR15;
// 5ms
SysTick_DelayMs(5);
// PG15 -> 拉高电平
GPIOG->ODR |= GPIO_ODR_ODR15;
// 5ms
SysTick_DelayMs(5);
}
void Int_LCD_WriteCmd(uint16_t cmd) {
*LCD_ADDRESS_CMD = cmd;
}
void Int_LCD_WriteData(uint16_t data) {
*LCD_ADDRESS_DATA = data;
}
/**
* LCD液晶屏 - 配置初始化
*/
void Int_LCD_Config_Init(void) {
/* 1. 设置灰阶电压以调整TFT面板的伽马特性, 正校准。一般出厂就设置好了 */
Int_LCD_WriteCmd(0xE0);
Int_LCD_WriteData(0x00);
Int_LCD_WriteData(0x07);
Int_LCD_WriteData(0x10);
Int_LCD_WriteData(0x09);
Int_LCD_WriteData(0x17);
Int_LCD_WriteData(0x0B);
Int_LCD_WriteData(0x41);
Int_LCD_WriteData(0x89);
Int_LCD_WriteData(0x4B);
Int_LCD_WriteData(0x0A);
Int_LCD_WriteData(0x0C);
Int_LCD_WriteData(0x0E);
Int_LCD_WriteData(0x18);
Int_LCD_WriteData(0x1B);
Int_LCD_WriteData(0x0F);
/* 2. 设置灰阶电压以调整TFT面板的伽马特性,负校准 */
Int_LCD_WriteCmd(0XE1);
Int_LCD_WriteData(0x00);
Int_LCD_WriteData(0x17);
Int_LCD_WriteData(0x1A);
Int_LCD_WriteData(0x04);
Int_LCD_WriteData(0x0E);
Int_LCD_WriteData(0x06);
Int_LCD_WriteData(0x2F);
Int_LCD_WriteData(0x45);
Int_LCD_WriteData(0x43);
Int_LCD_WriteData(0x02);
Int_LCD_WriteData(0x0A);
Int_LCD_WriteData(0x09);
Int_LCD_WriteData(0x32);
Int_LCD_WriteData(0x36);
Int_LCD_WriteData(0x0F);
/* 3. Adjust Control 3 (F7h) */
/*LCD_WriteCmd(0XF7);
Int_LCD_WriteData(0xA9);
Int_LCD_WriteData(0x51);
Int_LCD_WriteData(0x2C);
Int_LCD_WriteData(0x82);*/
/* DSI write DCS command, use loose packet RGB 666 */
/* 4. 电源控制1*/
Int_LCD_WriteCmd(0xC0);
Int_LCD_WriteData(0x11); /* 正伽马电压 */
Int_LCD_WriteData(0x09); /* 负伽马电压 */
/* 5. 电源控制2 */
Int_LCD_WriteCmd(0xC1);
Int_LCD_WriteData(0x02);
Int_LCD_WriteData(0x03);
/* 6. VCOM控制 */
Int_LCD_WriteCmd(0XC5);
Int_LCD_WriteData(0x00);
Int_LCD_WriteData(0x0A);
Int_LCD_WriteData(0x80);
/* 7. Frame Rate Control (In Normal Mode/Full Colors) (B1h) */
Int_LCD_WriteCmd(0xB1);
Int_LCD_WriteData(0xB0);
Int_LCD_WriteData(0x11);
/* 8. Display Inversion Control (B4h) (正负电压反转,减少电磁干扰)*/
Int_LCD_WriteCmd(0xB4);
Int_LCD_WriteData(0x02);
/* 9. Display Function Control (B6h) */
Int_LCD_WriteCmd(0xB6);
Int_LCD_WriteData(0x0A);
Int_LCD_WriteData(0xA2);
/* 10. Entry Mode Set (B7h) */
Int_LCD_WriteCmd(0xB7);
Int_LCD_WriteData(0xc6);
/* 11. HS Lanes Control (BEh) */
Int_LCD_WriteCmd(0xBE);
Int_LCD_WriteData(0x00);
Int_LCD_WriteData(0x04);
/* 12. Interface Pixel Format (3Ah) */
Int_LCD_WriteCmd(0x3A);
Int_LCD_WriteData(0x55); /* 0x55 : 16 bits/pixel */
/* 13. Sleep Out (11h) 关闭休眠模式 */
Int_LCD_WriteCmd(0x11);
/* 14. 设置屏幕方向和RGB */
Int_LCD_WriteCmd(0x36);
Int_LCD_WriteData(0x08);
SysTick_DelayMs(120);
/* 14. display on */
Int_LCD_WriteCmd(0x29);
}
/**
* LCD液晶屏 - 初始化
*/
void Int_LCD_Init(void) {
// 1. 通过FSMC进行连接
Dri_FSMC_LCD_Init();
// 2. 复位
Int_LCD_Reset();
// 3. 开启背光
Int_LCD_Background();
// 4. 液晶屏初始化
Int_LCD_Config_Init();
}
/**
* LCD液晶屏 - 清屏(使用单一的颜色填满整个屏幕: 背景色)
* 向LCD发出设置颜色的请求:所有的像素点全部都是一个颜色
*/
void Int_LCD_ClearScreen(uint16_t bgcolor) {
Int_LCD_WriteCmd(0x2C); // 发送颜色
for (uint32_t i = 0; i < 320 * 480; i++)
{
Int_LCD_WriteData(bgcolor);
}
}
/**
* LCD液晶屏 - 设定显示区域
*/
void Int_LCD_SetDisplayArea( uint16_t x, uint16_t y, uint16_t width, uint16_t height ) {
Int_LCD_WriteCmd(0x2A);
Int_LCD_WriteData(x >> 8); // SC
Int_LCD_WriteData( x & 0x00FF ); // SC
Int_LCD_WriteData( (x + width - 1) >> 8 ); // EC
Int_LCD_WriteData( (x + width - 1) & 0x00FF ); // EC
Int_LCD_WriteCmd(0x2B);
Int_LCD_WriteData( y >> 8); // SC
Int_LCD_WriteData( y & 0x00FF ); // SC
Int_LCD_WriteData( (y + height - 1) >> 8 ); // EC
Int_LCD_WriteData( (y + height - 1) & 0x00FF ); // EC
}
/**
* LCD液晶屏 - 显示图案
*/
void Int_LCD_DisplaySymbol1(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t fcolor) {
Int_LCD_SetDisplayArea(x, y, width, height);
Int_LCD_WriteCmd(0x2C); // 发送颜色
for (uint32_t i = 0; i < width * height; i++)
{
Int_LCD_WriteData(fcolor);
}
}
/**
* LCD液晶屏 - 显示图案
*/
void Int_LCD_DisplaySymbol(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t fcolor, uint16_t bgcolor) {
Int_LCD_SetDisplayArea(x, y, width, height);
Int_LCD_WriteCmd(0x2C); // 发送颜色
uint16_t buffer[100] = {
bgcolor, bgcolor, bgcolor, bgcolor, fcolor, fcolor, bgcolor, bgcolor, bgcolor, bgcolor,
bgcolor, bgcolor, bgcolor, fcolor, fcolor, fcolor, fcolor, bgcolor, bgcolor, bgcolor,
bgcolor, bgcolor, fcolor, fcolor, fcolor, fcolor, fcolor, fcolor, bgcolor, bgcolor,
bgcolor, fcolor, fcolor, fcolor, fcolor, fcolor, fcolor, fcolor, fcolor, bgcolor,
fcolor, fcolor, fcolor, fcolor, fcolor, fcolor, fcolor, fcolor, fcolor, fcolor,
fcolor, fcolor, fcolor, fcolor, fcolor, fcolor, fcolor, fcolor, fcolor, fcolor,
bgcolor, fcolor, fcolor, fcolor, fcolor, fcolor, fcolor, fcolor, fcolor, bgcolor,
bgcolor, bgcolor, fcolor, fcolor, fcolor, fcolor, fcolor, fcolor, bgcolor, bgcolor,
bgcolor, bgcolor, bgcolor, fcolor, fcolor, fcolor, fcolor, bgcolor, bgcolor, bgcolor,
bgcolor, bgcolor, bgcolor, bgcolor, fcolor, fcolor, bgcolor, bgcolor, bgcolor, bgcolor
};
for (uint32_t i = 0; i < 100; i++)
{
Int_LCD_WriteData(buffer[i]);
}
}
main.c的代码
cpp
/**
* 需求 : 使用LCD液晶屏显示符号,图案或文字
*/
int main(void)
{
Int_LCD_Init();
Int_LCD_ClearScreen(BLUE);
Int_LCD_DisplaySymbol(10, 10, 10, 10, RED, GREEN);
while(1)
{
}
}
