小工具知识网

当前位置: 首页 > 行业知识 > 【STM32篇】驱动LCD显示屏

行业知识

【STM32篇】驱动LCD显示屏

2026-11-04 22:39:09 来源:互联网转载

���次使用的硬件设备为野火的霸道V2开发板,显示器控制芯片型号为ILI9341,实际型号为ST7789V。在编写代码时参考的是ILI9341数据手册,二者差别不大,都是240*320分辨率。

1. 简介

        ILI9341是一个用于TFT液晶显示的单芯片控制驱动器,具有262144色的240RGB x 320像素显示解决方案。ILI9341支持8/9/16/18位数据总线的MCU接口,6/16/18位数据总线RGB接口以及3/4线的SPI接口。移动图像区域可以通过窗口地址功能再内部GRAM来指定。指定的窗口区域可以选择性地更新,因此,可以在图像区域同时独立的显示移动图像。

系统接口:

        8080-Ⅰ/8080-Ⅱ系列MCU的8/9/16/18位接口。

        图形控制的6/16/18位RGB接口。

        3/4线的SPI接口。

        在控制方式上,一般采用16位控制方式(RGB565)。可详读ILI9341数据手册,在3AH寄存器中可进行配置。

 2. 引脚连接

LCD显示屏处的排针:

 开发板上对应的LCD接口:(FSMC_8080模式)

 对应引脚连接:

控制引脚
CSPG12FSMC_NE4
#RSPE2 FSMC_A23
#WRPD5FSMC_NWE
#RDPD4 FSMC_NOE
复位和背光引脚
RESPG11复位
#BKPG6背光
数据引脚
DB0PD14FSMC_D0
DB1PD15FSMC_D1
DB2PD0FSMC_D2
DB3PD1FSMC_D3
DB4PE7FSMC_D4
DB5PE8FSMC_D5
DB6PE9FSMC_D6
DB7PE10FSMC_D7
DB8PE11FSMC_D8
DB9PE12FSMC_D9
DB10PE13FSMC_D10
DB11PE14FSMC_D11
DB12PE15FSMC_D12
DB13PD8FSMC_D13
DB14PD9FSMC_D14
DB15PD10FSMC_D15

        在引脚连接时,特地将LCD的控制引脚和数据引脚与MCU的FSMC外设连接,在使用FSMC模拟8080时序时,这些引脚便可交由FSMC控制,只需将FSMC配置好就可以了。当然,也可使用模拟SPI对这些引脚进行控制。所以在编写代码时,除了读写接口函数配置不同以外,两种控制方式的其他带啊吗都可相同。

3. FSMC与“8080”

如果说你问我:你怎么知道FSMC可以模拟8080?

我只能回答:我也是听别人说的。

首先我们先对比8080与FSMC二者时序的异同。

LCD 8080时序

 注:①写命令;②写数据。

FSMC写NOR时序(模式B)
LCD 8080时序FSMC 写NOR
#CS片选#NEx片选
RDX读使能#NOE读使能
WRX写使能#NWE写使能
D/CX数据#命令A[25:0]地址线
D[17:0]数据引脚D[15:0]数据引脚

如图可见,LCD的8080时序与FSMC写NOR(模式B)时序近乎相同,数据引脚选用16位模式。

不同的则是FSMC没有数据命令选择引脚,只有地址线,我们可选择地址线中的一根地址线充当数据命令控制引脚即可(0表示命令模式,1表示数据模式)。因此,只要配置好FSMC,便可模拟8080时序驱动LCD屏幕实现数据显示。

3.1 FSMC设备地址

FSMC 存储块

         如图所示,NOR/PSRAM的地址范围为 0x60000000~0x6FFFFFFF。NOR/PSRAM又分为4个存储块,如下图所示,存储块的选择由地址的26和27位控制。

60000000H二进制表示为:0110 0000 0000 0000 0000 0000 0000 0000

                                          [27:26]↑↑

HADDR[27:26]=00表示选择了NOR/PSRAM 1,即起始地址为60000000H

HADDR[27:26]=01表示选择了NOR/PSRAM 2,即起始地址为64000000H

HADDR[27:26]=10表示选择了NOR/PSRAM 3,即起始地址为68000000H

HADDR[27:26]=11表示选择了NOR/PSRAM 4,即起始地址为6C000000H

注意:NOR存储区划分了四个区并有四个专用的片选FSMC_NE[4:1]

那么就以为着我选择NOR/PSRAM 1就需要使用FSMC_NE1对应的片选线。

 外部存储地址:地址位对应地址线

         对于控制LCD屏而言,我们采取的数据宽度为16位即【RGB565】,地址线FSMC_A[24:0]对应着存储器地址HADDR[25:1]。假设我们使用FSMC_A0地址线作为数据命令控制线,选择的存储块为NOR/PSRAM 1 时,地址设置为0x60000000,地址线A0上的电平输出为低电平,表示命令模式;地址设置为0x60000002,地址线A0上的电平输出为高电平,表示数据模式。

        而本次使用的开发板与8080数据命令引脚连接的引脚为PE2(FSMC_A23),对应地址位为HADDR[24]。片选引脚为FSMC_NE4,HADDR[27:26]=11。存储块为NOR/PSRAM 4 ,令地址位的第24位为0时表示命令模式,即0x6C000000;数据模式:0x6D000000。

         当我们在该地址上写入数据时,FSMC便会控制数据线输出对应的数据,读取数据时,也可直接读取对应的地址即可。

3.2 FSMC-NOR/PSRAM配置

API(接口函数):

void FSMC_NORSRAMInit(FSMC_NORSRAMInitTypeDef* FSMC_NORSRAMInitStruct)

FSMC_NORSRAMInitTypeDef 初始化结构体

结构体原型:

/**   * @brief  FSMC NOR/SRAM Init structure definition  */typedef struct{  uint32_t FSMC_Bank;              //选择控制存储块                           uint32_t FSMC_DataAddressMux;    //地址总线与数据总线是否复用  uint32_t FSMC_MemoryType;        //存储器类型  uint32_t FSMC_MemoryDataWidth;   //设置存储器数据宽度  uint32_t FSMC_BurstAccessMode;   //设置是否支持突发访问模式  uint32_t FSMC_AsynchronousWait;  //设置同步等待传输时的等待信号  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;

FSMC_NORSRAMTimingInitTypeDef

时序结构体:

/**   * @brief  Timing parameters For NOR/SRAM Banks    */typedef struct{  uint32_t FSMC_AddressSetupTime;      //地址建立时间  uint32_t FSMC_AddressHoldTime;       //地址保持时间  uint32_t FSMC_DataSetupTime;         //数据建立时间  uint32_t FSMC_BusTurnAroundDuration; //总线转换周期  uint32_t FSMC_CLKDivision;           //时钟分频因子(异步模式下无效)  uint32_t FSMC_DataLatency;           //数据延迟时间(异步模式下无效)  uint32_t FSMC_AccessMode;            //设置访问模式}FSMC_NORSRAMTimingInitTypeDef;

3.3 配置FSMC

时钟和中断优先级的配置都在main.c中做统一配置。

引脚配置:除了RES和BK引脚以为,其他引脚都配置为复用输出模式。这里为了减少代码行数,就直接使用16进制代替GPIO_Pin。

void ILI9341_GPIO_Config(void){//复位和背光引脚:通用推挽输出GPIO_InitTypeDef ILI9341_GPIO;ILI9341_GPIO.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_11;ILI9341_GPIO.GPIO_Mode = GPIO_Mode_Out_PP;ILI9341_GPIO.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOG,&ILI9341_GPIO);//数据引脚和控制引脚:复用推挽输出//GPIODILI9341_GPIO.GPIO_Pin = 0xC733;ILI9341_GPIO.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_Init(GPIOD,&ILI9341_GPIO);//GPIOEILI9341_GPIO.GPIO_Pin = 0xFF84;//PE 2、7~15GPIO_Init(GPIOE,&ILI9341_GPIO);//GPIOGILI9341_GPIO.GPIO_Pin = GPIO_Pin_12;GPIO_Init(GPIOG,&ILI9341_GPIO);}

FSMC配置:此处就不做过多解释,详情参考STM32F10x用户手册。

void ILI9341_FSMC_Config(void){FSMC_NORSRAMDeInit(FSMC_Bank1_NORSRAM4);//复位存储块NOR/PSRAM 4FSMC_NORSRAMInitTypeDef ili9341_FSMC={0};//NOR初始化结构体/*时序配置*/FSMC_NORSRAMTimingInitTypeDef FSMC_ReadWrite_Timing={0};//时序结构体FSMC_ReadWrite_Timing.FSMC_AddressSetupTime = 0x01;//地址建立时间FSMC_ReadWrite_Timing.FSMC_DataSetupTime = 0x04;//数据建立时间FSMC_ReadWrite_Timing.FSMC_AccessMode = FSMC_AccessMode_B;//访问模式:模式B/*以下配置与模式B无关*/FSMC_ReadWrite_Timing.FSMC_AddressHoldTime = 0x00;//地址保持时间//仅适用于总线复用模式的NOR闪存操作FSMC_ReadWrite_Timing.FSMC_BusTurnAroundDuration = 0x00;//总线转换周期//在访问异步NOR闪存、SRAM或ROM时,这个参数不起作用FSMC_ReadWrite_Timing.FSMC_CLKDivision = 0x00;//时钟分频因子FSMC_ReadWrite_Timing.FSMC_DataLatency = 0x00;//数据延迟时间/*NOR初始化配置*/ili9341_FSMC.FSMC_Bank = FSMC_Bank1_NORSRAM4;// NOR/PSRAM 4ili9341_FSMC.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;//地址数据总线不复用ili9341_FSMC.FSMC_MemoryType = FSMC_MemoryType_NOR;ili9341_FSMC.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;ili9341_FSMC.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;//同步突发模式ili9341_FSMC.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;//不使能等待ili9341_FSMC.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;ili9341_FSMC.FSMC_WrapMode = FSMC_WrapMode_Disable;//不支持对齐突发模式ili9341_FSMC.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;//等待信号在等待前有效ili9341_FSMC.FSMC_WriteOperation = FSMC_WriteOperation_Enable;//写使能ili9341_FSMC.FSMC_WaitSignal = FSMC_WaitSignal_Disable;//不使能等待状态插入ili9341_FSMC.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;//不使能扩展模式ili9341_FSMC.FSMC_WriteBurst = FSMC_WriteBurst_Disable;//不使能写突发模式ili9341_FSMC.FSMC_ReadWriteTimingStruct = &FSMC_ReadWrite_Timing;ili9341_FSMC.FSMC_WriteTimingStruct = &FSMC_ReadWrite_Timing;FSMC_NORSRAMInit(&ili9341_FSMC);FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4,ENABLE);//使能FSMC}

        接下来就是写数据、写命令和读数据函数。由于使用了FSMC外设,所以读写数据都可直接对地址操作。这里我定义的地址为:

#define FSMC_ADDR_CMD()     *(volatile uint16_t *)0x6C000000

#define FSMC_ADDR_DATA()    *(volatile uint16_t *)0x6D000000

改地址为32位地址,因为我们读取的数据位数为16位,所以做了个地址对齐并使用volatile对这段地址进行防止被优化。再取个*表示值,可读取和改变这个值。

/*\brief:写指令\param:cmd: ili9341控制指令\retval:none*/void ILI9341_WriteCmd(uint16_t cmd){FSMC_ADDR_CMD() = cmd;}/*\brief:写数据\param:data: 写入的数据\retval:none*/void ILI9341_WriteData(uint16_t data){FSMC_ADDR_DATA() = data;}/*\brief:读数据\param:none\retval:none*/uint16_t ILI9341_ReadData(void){return FSMC_ADDR_DATA();}

在此,对FSMC的操作已经结束,重要的就是用到这三个函数对ILI9341进行读写操作,换言之,使用SPI也是用到读写函数。

4. 4线SPI

此处先略。

5. LCD配置

        驱动LCD屏的关键是在屏幕任意位置画一个点,相对于OLED的画点只是一个位表示亮和不亮,这里的画点,一个点表示一个16位的RGB像素点。

5.1 获取LCD显示屏ID

         读取ID指令为04H,在未对屏幕进行任何配置前,可用该指令验证编写好的读写函数是否可行。

/*\brief:读显示ID信息\param:none\retval:ID信息*/uint16_t Read_LCD_ID(void){uint16_t id=0;ILI9341_WriteCmd(0x04);//读显示ID信息ILI9341_ReadData();ILI9341_ReadData();//LCD制造商IDid = ILI9341_ReadData();//驱动版文号IDid 8);//高位ILI9341_WriteData(x&0xFF);//结束地址(与起始地址相同)ILI9341_WriteData(x>>8);//高位ILI9341_WriteData(x&0xFF);ILI9341_WriteCmd(0x2B);//设置页地址//起始地址ILI9341_WriteData(y>>8);//高位ILI9341_WriteData(y&0xFF);//结束地址(与起始地址相同)ILI9341_WriteData(y>>8);//高位ILI9341_WriteData(y&0xFF);}

        在设置行列地址时,将起始地址和结束地址设置为同一只,行列交叉的点便是设置的坐标点。

5.3.2 画点函数

        在画一个点前,首先需要给定一个坐标点,然后再把像素点画上去。当然,不能直接使用写数据函数直接把像素点画上去,还得发送写存储器指令。

/*\brief:画点函数(在屏幕的任意位置画一个像素)\param:x: 横坐标y: 列坐标colour: 颜色(RGB565)\retval:none*/void LCD_DrawDot(uint16_t x,uint16_t y,uint16_t colour){if(x+1>LCD_WIDTH || y+1>LCD_HIGH) return ;//超出屏幕范围LCD_SetCoord(x,y);//设置坐标ILI9341_WriteCmd(0x2C);ILI9341_WriteData(colour);//绘制一个像素点}

5.4 清屏函数

        将屏幕清成一个颜色,这里使用的画点函数,将屏幕上的240x320个像素点逐一画上一个点。

void LCD_Clear(uint16_t colour){uint16_t i,j;for(i=0;i8);//高位ILI9341_WriteData(0&0xFF);//结束地址ILI9341_WriteData(239>>8);//高位ILI9341_WriteData(239&0xFF);ILI9341_WriteCmd(0x2B);//设置页地址//起始地址ILI9341_WriteData(0>>8);//高位ILI9341_WriteData(0&0xFF);//结束地址ILI9341_WriteData(319>>8);//高位ILI9341_WriteData(319&0xFF);ILI9341_WriteCmd(0x2C);//写存储for(i=0;i

上一篇:宝塔面板服务器安装全攻略,轻松搭建服务器,必备指南助你上手!

下一篇:[CISCN2019 华北赛区 Day2 Web1]Hack World 1 题目分析与详解