> 技术文档 > STM32 使用FSMC驱动ILI9341LCD显示屏(16bit 8080)_ili9341中文手册

STM32 使用FSMC驱动ILI9341LCD显示屏(16bit 8080)_ili9341中文手册


一、前期准备

        1.1 资料简介

                文档中有关于2.8寸屏ILI9341的驱动手册以及网络获取的中文手册供大家参考

        1.2 硬件环境

                M3开发板

                ST-Link调试器

                ILI9341 2.8寸LCD屏

二、FSMC与LCD

        2.1 8080接口

        一般情况下,在嵌入式行业中常用的屏幕(OLED、TFT-LCD)会提供多个接口,其中比较常见的为SPI、IIC、8080、6800接口等,可硬件选择接口,选择方式参考官方手册,本次课程中使用屏幕硬件接口选择方式如下:

        串行通讯SPI与IIC这里我们不做过多介绍,基本都是一些常规的底层硬件接口。对于屏幕显示而言,串行通信数据传输较慢,对于很多情况而言,我们更倾向于使用并行通信。并行通讯接口在屏幕中一般提供了6800与8080接口,6800总线又叫做摩托罗拉总线、8080时序也叫做英特尔总线。

        在底层MCU驱动环境下,我们比较多的会使用MCU接口中的8080时序,在本次课程中我们使用了16位的8080接口,8080接口需要如下驱动管脚:

管脚名称

管脚功能

其他说明

CS

片选

低电平有效

RES

复位

低电平有效

WR

写使能

低电平有效

RD

读使能

低电平有效

DC

数据命令选择端

低电平命令、高电平数据

D[15:0]

16位并行数据线

        8080-I/8080-II串并接口,通过D[17:0]数据引脚实现寄存器的存取

        8080写数据时序图:

        8080读数据时序图:

        2.2 MCU中的FSMC

        FSMC是嵌入式单片机MCU提供的一个接口:灵活的静态存储器控制器

        FSMC可外接外部RAM到存储器上,地址空间为0X60000000~0X9FFFFFFF,共1G空间。

        而这1G空间由分为了16个区,其中4个区为1个块。即一个区为64M大小,一个块为256M大小。

                存储块1用于访问NOR闪存或PSRAM存储设备。

                存储块2和3用于访问NAND闪存设备,每个存储块连接一个NAND闪存。

                存储块4用于访问PC卡设备

        根据以上内容,结合如下FSMC存储块示意图,我们可以计算出每一个区的空间首地址,此操作在此次操作中尤为重要。

比如如何计算出块1的第4个区空间首地址

  addr = 0X60000000 + 3*64M

  即空间首地址为基地址加上前面三个区的总空间大小

  那么addr = 0X60000000 + 192M

      addr = 0X60000000 + 201326592字节

      addr = 0X60000000 + 0XC000000

      addr = 0X6C000000

        接下来我们就需要关注如何接入将外部存储器,并且FSMC如何控制该控制块

        其中HADDR是需要转换到外部存储器的内部AHB地址线。

        当外部存储器的数宽为8位时,FSMC的地址线将与MCU内存空间地址线从0开始依次对应连接。

        当外部存储器的数宽为16位时,FSMC的地址线将与MCU内存空间地址线从1开始依次错位连接。

        此时,我们会发现FSMC的模式A与LCD屏的8080接口时序是非常相似的:

                 FSMC模式A读操作、读操作:

        2.3 FSMC与8080时序

        此时,我们可以将FSMC于8080时序在硬件接口上先做一个对比

8080时序

FSMC

CS

片选信号,低电平有效

NEx

片选,低电平有效(x=1...4)

WR

写使能

NWR

写使能

RD

读使能

NOE

输出使能

D[15:0]

并行数据线

D[15:0]

并行数据线

D/C

数据命令选择端

A[25:0]

地址线

        而通讯的时序也可以做一个简单的对比:

        经过对比我们将会发现8080接口的硬件管脚定义与FSMC接口的硬件管脚定义基本吻合,但是8080时序需要有一个数据命令选择管脚,而FSMC没有。但是FSMC接口也多出了26根地址线供我们使用,那么我们是否可以将地址线其中的一根线作为数据命令选择端与LCD屏连接呢?

        此时我们必须注意D/C管脚为低电平时代表传输的信号为command,高电平是data。此时我们便发现了一个巧妙的方法使用FSMC地址线其中的一条线作为我们8080接口的D/C管脚(任意一条地址线都可以)。那么本次课程中使用的硬件设计如下所示:

LCD管脚

MCU IO

FSMC功能

管脚功能

配置模式

BL

PD12

背光板

通用推挽输出

CS

PD7

FSMC_NE1

片选

复用推挽输出

RD

PD4

FSMC_NOE

读使能

复用推挽输出

WR

PD5

FSMC_NWE

写使能

复用推挽输出

RS(D/C)

PD11

FSMC_A16

数据命令选择

复用推挽输出

D0

PD14

FSMC_D0

并行数据线0

复用推挽输出

D1

PD15

FSMC_D1

并行数据线1

复用推挽输出

D2

PD0

FSMC_D2

并行数据线2

复用推挽输出

D3

PD1

FSMC_D3

并行数据线3

复用推挽输出

D4

PE7

FSMC_D4

并行数据线4

复用推挽输出

D5

PE8

FSMC_D5

并行数据线5

复用推挽输出

D6

PE9

FSMC_D6

并行数据线6

复用推挽输出

D7

PE10

FSMC_D7

并行数据线7

复用推挽输出

D8

PE11

FSMC_D8

并行数据线8

复用推挽输出

D9

PE12

FSMC_D9

并行数据线9

复用推挽输出

D10

PE13

FSMC_D10

并行数据线10

复用推挽输出

D11

PE14

FSMC_D11

并行数据线11

复用推挽输出

D12

PE15

FSMC_D12

并行数据线12

复用推挽输出

D13

PD8

FSMC_D13

并行数据线13

复用推挽输出

D14

PD9

FSMC_D14

并行数据线14

复用推挽输出

D15

PD10

FSMC_D15

并行数据线15

复用推挽输出

        2.4 代码实现

        关于LCD屏显示原理在此我们不做过多的赘述,本文我们重点关注FSMC如何驱动LCD屏,本文我们以逐日开发板(ARM Cortex-M3内核STM32F103VET6)与ILI9341 LCD屏为硬件平台(各位使用市面上常见的开发板即可,了解原理即可灵活修改),驱动代码初始化框架为:

/************************函数名称:LCD_Config函数作用:LCD 初始化函数入口:无函数出口:无函数作者:WYC创建时间:2021.05.25修改时间:2021.05.25************************/void LCD_Config(void){LCD_PortConfig();LCD_FSMCConfig();LCD_9341Config();LCD_Clear(WHITE);}

在此我们对此代码按步骤分析:

  • 底层接口初始化—LCD_PortConfig

                初始化所有IO口,包含FSMC接口与背光板模式,并将背光板关闭。

  • FSMC初始化—LCD_FSMCConfig

                初始化FSMC工作模式为模式A

                选择FSMC_Bank 为FSMC_Bank1_NORSRAM1,即块1的区1(因为我们CS管脚连接的PD7为FSMC_NE1)

                配置数宽为16位,因为我们LCD使用16位8080并行接口

  • ILI9341驱动初始化—LCD_9341Config

                        对于ILI9341驱动来说(其他屏幕也基本类似),驱动的初始化主要靠写命令与写数据两个函数,将我们的配置写入

                LCD屏对应的配置寄存器。

那么我们在此使用了一个巧妙的方式提供了这两个基本函数:

//LCD在FSMC中寄存器操作//使用NOR/SRAM的 Bank1.sector1,地址位HADDR[27,26]=16 A16作为数据命令区分线 //1 1111 1111 1111 1110//01100000 000000X1 11111111 11111110//01100000 00000010 00000000 00000000//注意设置时STM32内部会右移一位对其!#define LCD_BASE ((u32)(0x60000000 | 0x0001FFFE))#define TFTLCD ((LCD_TypeDef *) LCD_BASE)//LCD寄存器结构体typedef struct{u16 LCD_REG;//命令寄存器u16 LCD_RAM;} LCD_TypeDef;
/************************函数名称:LCD_WR_REG函数作用:LCD 读写命令函数函数入口:data写入指令函数出口:无函数作者:WYC创建时间:2021.05.25修改时间:2021.05.25************************/void LCD_WR_REG(volatile uint16_t regval){ regval=regval;//使用-O2优化的时候,必须插入的延时 TFTLCD->LCD_REG=regval;//写入要写的寄存器序号}/************************函数名称:LCD_WR_DATA函数作用:LCD 读写数据函数函数入口:data写入数据函数出口:无函数作者:WYC创建时间:2021.05.25修改时间:2021.05.25************************/void LCD_WR_DATA(volatile uint16_t data){ data=data;//使用-O2优化的时候,必须插入的延时 TFTLCD->LCD_RAM=data;//0110 1100 0000 0000 0000 1000 0000 0000}

                 实现命令与数据传输的关键其实就是数据命令选择端管脚的状态,我们参照单片机内部寄存器的方式制作了一个类似于

        GPIO的外设:TFTLCD

                 TFTLCD是一个类似于GPIO、USART1、ADC1之类的外设,其类型为结构体指针,数据为存储器空间的基地址,我们异

        步一步解决这个问题。

                 结构体指针:

                我们定义了TFTLCD的结构体指针为LCD_TypeDef类型,内含两个寄存器—LCD_REG寄存器与LCD_RAM寄存器。两个

        变量都为u16类型,也就是说当我操作TFTLCD->RAM寄存器时,会在TFTLCD基地址的基础上地址偏移两个字节,这个也是我

        们寄存器偏移量的原理。

               所以最终我们将TFTLCD宏定义为((LCD_TypeDef *) LCD_BASE)

               此处我们给出寄存器GPIOA的两张截图,大家可以对比参考,领悟其中奥妙

(PS:大家可以借此机会领悟寄存器空间的魅力^_^)

基地址:

        基地址计算就要我们关注上面的一些知识了,整个FSMC的基地址为0X60000000,我们接入的是SRAM即块1。我们连接的片选是FSMC_NE1,所以我们此时基地址偏移量为0。

        但是如果我们使用的是块2并且片选为NE2的话,基地址则为0X70000000+64M,得到0X74000000。

        然后就是如何将DC管脚切换的问题了,在驱动LCD屏时,FSMC所有的地址线是不影响我们的操作的,所以我们将DC接入了其中任意一个地址线上,我们在硬件设计时接入的是A16。

        也就是说当我使用TFTLCD->REG时,A16应该是低电平,此时地址在基地址基础上不变化

        当我使用TFTLCD->RAM时,A16应该是高电平,此时地址在基地址基础上偏移两个字节

并且上文提到,当数宽为16时,HADDR与FSMC_A引脚是错位相连的,即HADDR[0]未使用

       由此我们可以得到,当地址偏移两字节时A16=1,地址线数据为10 0000 0000 0000 0000(十六进制0X20000)

       反推不偏移两字节时,A16=0,地址数据线为01 1111 1111 1111 1110(十六进制0X1FFFE)

        所以最终我们得到最终的基地址LCD_BASE为0x60000000 | 0x0001FFFE

  • 刷新屏幕—LCD_Clear

                此代码大家需要去详细了解LCD屏幕的工作模式,本文我们制作测试使用。如果前面配置没有问题,那么屏幕初始化后将

        变为大家想使用的颜色。

本文到此便结束了,如有不对之处,欢迎大家的批评与指正!!!

LCD源文件

#include \"tft_lcd.h\"/************************函数名称:LCD_Config函数作用:LCD 初始化函数入口:无函数出口:无函数作者:WYC创建时间:2021.05.25修改时间:2021.05.25************************/void LCD_Config(void){LCD_PortConfig();LCD_FSMCConfig();LCD_9341Config();LCD_Clear(WHITE);}/************************函数名称:LCD_PortConfig函数作用:LCD管脚初始化函数入口:无函数出口:无函数作者:WYC创建时间:2020.12.31修改时间:2020.12.31补充说明:管脚说明LCD_BLPD12通用推挽输出FSMC_NE1(CS)PD7复用推挽输出FSMC_NOEPD4复用推挽输出FSMC_NWEPD5复用推挽输出FSMC_A16(RS)PD11复用推挽输出FSMC_D0~D15复用推挽输出PD14PD15PD0PD1PE7PE8 PE9 PE10PE11PE12PE13PE14PE15PD8PD9 PD10************************/void LCD_PortConfig(void){GPIO_InitTypeDef GPIO_InitStructure;//PORT:D ERCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);//PD12背光板GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOD,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_14|GPIO_Pin_15;GPIO_Init(GPIOD,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;GPIO_Init(GPIOE,&GPIO_InitStructure);}/************************函数名称:LCD_FSMCConfig函数作用:LCD FSMC初始化函数入口:无函数出口:无函数作者:WYC创建时间:2020.12.31修改时间:2020.12.31************************/void LCD_FSMCConfig(void){FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStruct;FSMC_NORSRAMTimingInitTypeDef FSMC_NORSRAMTimingInitWirte;FSMC_NORSRAMTimingInitTypeDef FSMC_NORSRAMTimingInitRead;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC,ENABLE);//使能FSMC时钟FSMC_NORSRAMTimingInitRead.FSMC_AddressSetupTime = 0x01; //地址建立时间(ADDSET)为2个HCLK 1/36M=27nsFSMC_NORSRAMTimingInitRead.FSMC_AddressHoldTime = 0x00; //地址保持时间(ADDHLD)模式A未用到FSMC_NORSRAMTimingInitRead.FSMC_DataSetupTime = 0x0f; // 数据保存时间为16个HCLK,因为液晶驱动IC的读数据的时候,速度不能太快,尤其对1289这个IC。FSMC_NORSRAMTimingInitRead.FSMC_BusTurnAroundDuration = 0x00;FSMC_NORSRAMTimingInitRead.FSMC_CLKDivision = 0x00;FSMC_NORSRAMTimingInitRead.FSMC_DataLatency = 0x00;FSMC_NORSRAMTimingInitRead.FSMC_AccessMode = FSMC_AccessMode_A; //模式A FSMC_NORSRAMTimingInitWirte.FSMC_AddressSetupTime = 0x00; //地址建立时间(ADDSET)为1个HCLK FSMC_NORSRAMTimingInitWirte.FSMC_AddressHoldTime = 0x00; //地址保持时间(AFSMC_NORSRAMTimingInitWirte.FSMC_DataSetupTime = 0x03; ////数据保存时间为4个HCLKFSMC_NORSRAMTimingInitWirte.FSMC_BusTurnAroundDuration = 0x00;FSMC_NORSRAMTimingInitWirte.FSMC_CLKDivision = 0x00;FSMC_NORSRAMTimingInitWirte.FSMC_DataLatency = 0x00;FSMC_NORSRAMTimingInitWirte.FSMC_AccessMode = FSMC_AccessMode_A; //模式A FSMC_NORSRAMInitStruct.FSMC_Bank = FSMC_Bank1_NORSRAM1;// 这里我们使用NE1 ,也就对应BTCR[6],[7]。FSMC_NORSRAMInitStruct.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; // 不复用数据地址FSMC_NORSRAMInitStruct.FSMC_MemoryType =FSMC_MemoryType_SRAM;// FSMC_MemoryType_SRAM; FSMC_NORSRAMInitStruct.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;//存储器数据宽度为16bit FSMC_NORSRAMInitStruct.FSMC_BurstAccessMode =FSMC_BurstAccessMode_Disable;// FSMC_BurstAccessMode_Disable; FSMC_NORSRAMInitStruct.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;FSMC_NORSRAMInitStruct.FSMC_AsynchronousWait=FSMC_AsynchronousWait_Disable; FSMC_NORSRAMInitStruct.FSMC_WrapMode = FSMC_WrapMode_Disable; FSMC_NORSRAMInitStruct.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState; FSMC_NORSRAMInitStruct.FSMC_WriteOperation = FSMC_WriteOperation_Enable;// 存储器写使能FSMC_NORSRAMInitStruct.FSMC_WaitSignal = FSMC_WaitSignal_Disable; FSMC_NORSRAMInitStruct.FSMC_ExtendedMode = FSMC_ExtendedMode_Enable; // 读写使用不同的时序FSMC_NORSRAMInitStruct.FSMC_WriteBurst = FSMC_WriteBurst_Disable; FSMC_NORSRAMInitStruct.FSMC_ReadWriteTimingStruct=&FSMC_NORSRAMTimingInitRead;FSMC_NORSRAMInitStruct.FSMC_WriteTimingStruct=&FSMC_NORSRAMTimingInitWirte;FSMC_NORSRAMInit(&FSMC_NORSRAMInitStruct);FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1,ENABLE);}/************************函数名称:LCD_9341Config函数作用:LCD 驱动初始化函数入口:无函数出口:无函数作者:WYC创建时间:2021.05.25修改时间:2021.05.25************************/void LCD_9341Config(void){Delay_Nopnms(100);LCD_WR_REG(0xCF);LCD_WR_DATA(0x00);LCD_WR_DATA(0xC1);LCD_WR_DATA(0X30);LCD_WR_REG(0xED);LCD_WR_DATA(0x64);LCD_WR_DATA(0x03);LCD_WR_DATA(0X12);LCD_WR_DATA(0X81);LCD_WR_REG(0xE8);LCD_WR_DATA(0x85);LCD_WR_DATA(0x10);LCD_WR_DATA(0x7A);LCD_WR_REG(0xCB);LCD_WR_DATA(0x39);LCD_WR_DATA(0x2C);LCD_WR_DATA(0x00);LCD_WR_DATA(0x34);LCD_WR_DATA(0x02);LCD_WR_REG(0xF7);LCD_WR_DATA(0x20);LCD_WR_REG(0xEA);LCD_WR_DATA(0x00);LCD_WR_DATA(0x00);LCD_WR_REG(0xC0); //Power controlLCD_WR_DATA(0x1B); //VRH[5:0]LCD_WR_REG(0xC1); //Power controlLCD_WR_DATA(0x01); //SAP[2:0];BT[3:0]LCD_WR_REG(0xC5); //VCM controlLCD_WR_DATA(0x30); //3FLCD_WR_DATA(0x30); //3CLCD_WR_REG(0xC7); //VCM control2LCD_WR_DATA(0XB7);LCD_WR_REG(0x36); // Memory Access ControlLCD_WR_DATA(0x48);LCD_WR_REG(0x3A);LCD_WR_DATA(0x55);LCD_WR_REG(0xB1);LCD_WR_DATA(0x00);LCD_WR_DATA(0x1A);LCD_WR_REG(0xB6); // Display Function ControlLCD_WR_DATA(0x0A);LCD_WR_DATA(0xA2);LCD_WR_REG(0xF2); // 3Gamma Function DisableLCD_WR_DATA(0x00);LCD_WR_REG(0x26); //Gamma curve selectedLCD_WR_DATA(0x01);LCD_WR_REG(0xE0); //Set GammaLCD_WR_DATA(0x0F);LCD_WR_DATA(0x2A);LCD_WR_DATA(0x28);LCD_WR_DATA(0x08);LCD_WR_DATA(0x0E);LCD_WR_DATA(0x08);LCD_WR_DATA(0x54);LCD_WR_DATA(0XA9);LCD_WR_DATA(0x43);LCD_WR_DATA(0x0A);LCD_WR_DATA(0x0F);LCD_WR_DATA(0x00);LCD_WR_DATA(0x00);LCD_WR_DATA(0x00);LCD_WR_DATA(0x00);LCD_WR_REG(0XE1); //Set GammaLCD_WR_DATA(0x00);LCD_WR_DATA(0x15);LCD_WR_DATA(0x17);LCD_WR_DATA(0x07);LCD_WR_DATA(0x11);LCD_WR_DATA(0x06);LCD_WR_DATA(0x2B);LCD_WR_DATA(0x56);LCD_WR_DATA(0x3C);LCD_WR_DATA(0x05);LCD_WR_DATA(0x10);LCD_WR_DATA(0x0F);LCD_WR_DATA(0x3F);LCD_WR_DATA(0x3F);LCD_WR_DATA(0x0F);LCD_WR_REG(0x2B);LCD_WR_DATA(0x00);LCD_WR_DATA(0x00);LCD_WR_DATA(0x01);LCD_WR_DATA(0x3f);LCD_WR_REG(0x2A);LCD_WR_DATA(0x00);LCD_WR_DATA(0x00);LCD_WR_DATA(0x00);LCD_WR_DATA(0xef);LCD_WR_REG(0x11); //Exit SleepDelay_Nopnms(120);LCD_WR_REG(0x29); //display onLCD_WR_REG(0x36);LCD_WR_DATA(0x08);//II9341必须这样//整个LCD就初始化完成//lCD有个背光灯LCD_LED_ON;//打开LCD屏幕背光灯//REG :写命令//DATA:写数据LCD_WR_REG(0x2A);LCD_WR_DATA(0);LCD_WR_DATA(0); //LCD_WR_DATA(0);LCD_WR_DATA((240-1));//X轴是从 0开始 到240结束LCD_WR_REG(0x2B);LCD_WR_DATA(0);LCD_WR_DATA(0);LCD_WR_DATA((320-1)>>8);LCD_WR_DATA((320-1)&0xFF);//y轴的起始 0//y轴的结束 320LCD_WR_REG(0x2C);}/************************函数名称:LCD_WR_REG函数作用:LCD 读写命令函数函数入口:data写入指令函数出口:无函数作者:WYC创建时间:2021.05.25修改时间:2021.05.25************************/void LCD_WR_REG(volatile uint16_t regval){ regval=regval;//使用-O2优化的时候,必须插入的延时 TFTLCD->LCD_REG=regval;//写入要写的寄存器序号}/************************函数名称:LCD_WR_DATA函数作用:LCD 读写数据函数函数入口:data写入数据函数出口:无函数作者:WYC创建时间:2021.05.25修改时间:2021.05.25************************/void LCD_WR_DATA(volatile uint16_t data){ data=data;//使用-O2优化的时候,必须插入的延时 TFTLCD->LCD_RAM=data;//0110 1100 0000 0000 0000 1000 0000 0000}/************************函数名称:LCD_Clear函数作用:LCD 读写数据函数函数入口:Color清屏颜色(RGB565)函数出口:无函数作者:WYC创建时间:2021.05.25修改时间:2021.05.25************************/void LCD_Clear(u16 Color){u32 i=0;//设置列:0~240LCD_WR_REG(0x2A);LCD_WR_DATA(0);//起始列高八位LCD_WR_DATA(0);//起始列低八位LCD_WR_DATA(0);//停止列高八位LCD_WR_DATA((240-1));//停止列低八位//设置行:0~320LCD_WR_REG(0x2B);LCD_WR_DATA(0);LCD_WR_DATA(0);LCD_WR_DATA((320-1)>>8);//0000 0001 0011 1111 >> 8 = 0000 0001LCD_WR_DATA((320-1)&0xFF);//0000 0001 0011 1111 & 0000 0000 1111 1111=0011 1111//写数据LCD_WR_REG(0x2C);for(i=0;i> 8);LCD_WR_DATA(Line_x1 & 0XFF);LCD_WR_DATA(Line_x2 >> 8);LCD_WR_DATA(Line_x2 & 0XFF);LCD_WR_REG(0x2B);LCD_WR_DATA(Line_y1 >> 8);LCD_WR_DATA(Line_y1 & 0XFF);LCD_WR_DATA(Line_y2 >> 8);LCD_WR_DATA(Line_y2 & 0XFF);}/************************函数名称:LCD_Point函数作用:LCD 打点函数函数入口:(point_x,point_y)坐标Color颜色(RGB565)函数出口:无函数作者:WYC创建时间:2021.05.25修改时间:2021.05.25************************/void LCD_Point(uint16_t point_x,uint16_t point_y,u16 Color){LCD_WR_REG(0x2A);LCD_WR_DATA(point_x >> 8);LCD_WR_DATA(point_x & 0XFF);LCD_WR_DATA(point_x >> 8);LCD_WR_DATA(point_x & 0XFF);LCD_WR_REG(0x2B);LCD_WR_DATA(point_y >> 8);LCD_WR_DATA(point_y & 0XFF);LCD_WR_DATA(point_y >> 8);LCD_WR_DATA(point_y & 0XFF);LCD_WR_REG(0x2C);LCD_WR_DATA(Color);}/************************函数名称:LCD_Line函数作用:LCD 画线函数函数入口:Line_x1,Line_y1起始坐标Line_x2,Line_y2停止坐标Wight线宽Color颜色(RGB565)函数出口:无函数作者:WYC创建时间:2021.05.25修改时间:2021.05.25************************/void LCD_Line(uint16_t Line_x1,uint16_t Line_y1,uint16_t Line_x2,uint16_t Line_y2,uint16_t Wight,u16 Color){uint16_t Line_len = 0;uint16_t i = 0,j=0;uint16_t x=0,y=0;uint16_t offset= 0;float Slope = 0;if(Line_x1 == Line_x2)//竖线{Set_Dispaly(Line_x1-(Wight/2),Line_y1,Line_x1-(Wight/2)+Wight-1,Line_y2);if(Line_y1 > Line_y2)Line_len = Line_y1-Line_y2;elseLine_len = Line_y2-Line_y1;LCD_WR_REG(0x2C);for(i=0;i Line_x2)Line_len = Line_x1-Line_x2;elseLine_len = Line_x2-Line_x1;LCD_WR_REG(0x2C);for(i=0;i Line_x2)x = Line_x1-Line_x2;elsex = Line_x2-Line_x1;if(Line_y1 > Line_y2)y = Line_y1-Line_y2;elsey = Line_y2-Line_y1;Slope = y/(x*1.0);for(i=0;i<x;i++){offset = (Slope*i)-(Wight/2);for(j=0;j Line_x2)x = Line_x1-Line_x2;elsex = Line_x2-Line_x1;if(Line_y1 > Line_y2)y = Line_y1-Line_y2;elsey = Line_y2-Line_y1;Set_Dispaly(Line_x1,Line_y1,Line_x2,Line_y2);LCD_WR_REG(0x2C);for(i=0;i ((x*x)+(y*y)))return 1;elsereturn 0;}void LCD_Circular(uint16_t Circ_x,uint16_t Circ_y,uint16_t Circ_r,uint16_t Color){uint16_t i=0,j=0;for(i=Circ_x-Circ_r;i<Circ_x+Circ_r;i++){for(j=Circ_y-Circ_r;j<Circ_y+Circ_r;j++){if(Get_CircularPoint(Circ_x,Circ_y,i,j,Circ_r)==1)LCD_Point(i,j,Color);}}}/************************函数名称:LCD_DrawFont函数作用:LCD 写字函数函数入口:start_x,start_y起始坐标Font_wide 字宽Font_high字高Back_color背景颜色(RGB565)Font_color字体颜色(RGB565)Font_buf字模数组指针函数出口:无函数作者:WYC创建时间:2021.05.25修改时间:2021.05.25************************/void LCD_DrawFont(uint16_t start_x,uint16_t start_y,uint16_t Font_wide,uint16_t Font_high,uint16_t Back_color,uint16_t Font_color,uint8_t *Font_buf){uint16_t Display_Buf[4] = {0};uint16_t i=0,j=0;uint16_t Buf_Count = 0;Display_Buf[0] = start_x;Display_Buf[1] = start_y;Display_Buf[2] = start_x+Font_wide-1;Display_Buf[3] = start_y+Font_high-1;Buf_Count = Font_wide*Font_high/8;//画LCD显示范围Set_Dispaly(Display_Buf[0],Display_Buf[1],Display_Buf[2],Display_Buf[3]);LCD_WR_REG(0x2C);for(i=0;i<Buf_Count;i++){for(j=0;j<8;j++){//0XA5<<0 & 0X80 = 1010 0101 & 1000 0000 = 1000 0000//0XA5<<1 & 0X80 = 0100 1010 & 0000 0000 = 0000 0000if((Font_buf[i]<<j)&0X80)LCD_WR_DATA(Font_color);elseLCD_WR_DATA(Back_color);}}}/************************函数名称:LCD_DrawString函数作用:LCD 写字符串函数入口:start_x起始x坐标start_y起始y坐标Back_color背景色Font_color字体色Fontsize字体大小(Fontsize_16、Fontsize_24、Fontsize_32)*Font_buf字符串函数出口:无函数作者:WYC创建时间:2021.05.25修改时间:2021.05.25补充说明:横向取模字节不倒叙************************/void LCD_DrawString(uint16_t start_x,uint16_t start_y,uint16_t Back_color,uint16_t Font_color,_FontSize Fontsize,char *Font_buf){uint16_t x_offset = 0,y_offset = 0;int Font_offset = 0;x_offset = start_x;y_offset = start_y;//判断字符串是否结束while(*Font_buf != \'\\0\'){//判断汉字?字符?if(*Font_buf == 10){x_offset = start_x;start_y += 16;goto PP1;}if(*Font_buf = 0){switch(Fontsize){case Fontsize_16:if(x_offset > 232){x_offset = start_x;y_offset += 16;}LCD_DrawFont(x_offset,y_offset,8,16,Back_color,Font_color,&ASCII_Font16Buf[Font_offset*16]);x_offset += 8;break;case Fontsize_24:if(x_offset > 228){x_offset = start_x;y_offset += 24;}LCD_DrawFont(x_offset,y_offset,16,24,Back_color,Font_color,&ASCII_Font24Buf[Font_offset*48]);x_offset += 16;//此处改为12可以让字符紧密break;case Fontsize_32:if(x_offset > 226){x_offset = start_x;y_offset += 32;}LCD_DrawFont(x_offset,y_offset,16,32,Back_color,Font_color,&ASCII_Font32Buf[Font_offset*64]);x_offset += 16;break;}}PP1:Font_buf += 1;}else//汉字{switch(Fontsize){case Fontsize_16:if(x_offset > 224){x_offset = start_x;y_offset += 16;}Font_offset = GB2312_GetFont16Addr(*Font_buf,*(Font_buf+1));if(Font_offset >= 0)LCD_DrawFont(x_offset,y_offset,16,16,Back_color,Font_color,&GB2312_Font16Buf[Font_offset*32]);x_offset += 16;break;case Fontsize_24:if(x_offset > 212){x_offset = start_x;y_offset += 24;}Font_offset = GB2312_GetFont24Addr(*Font_buf,*(Font_buf+1));if(Font_offset >= 0)LCD_DrawFont(x_offset,y_offset,24,24,Back_color,Font_color,&GB2312_Font24Buf[Font_offset*72]);x_offset += 24;break;case Fontsize_32:if(x_offset > 208){x_offset = start_x;y_offset += 32;}Font_offset = GB2312_GetFont32Addr(*Font_buf,*(Font_buf+1));if(Font_offset >= 0)LCD_DrawFont(x_offset,y_offset,32,32,Back_color,Font_color,&GB2312_Font32Buf[Font_offset*128]);x_offset += 32;break;}Font_buf += 2;}}}/************************函数名称:LCD_DrawPhoto函数作用:LCD 画图函数函数入口:start_x,start_y起始坐标Photo_wide 图片宽Photo_high图片高Photo_buf图片数组指针函数出口:无函数作者:WYC创建时间:2021.05.25修改时间:2021.05.25补充说明:水平扫描高位在前************************/void LCD_DrawPhoto(uint16_t start_x,uint16_t start_y,uint16_t Photo_wide,uint16_t Photo_high,const unsigned char *Photo_buf){uint16_t Display_Buf[4] = {0};uint32_t i=0;uint32_t Buf_Count = 0,Color_Temp=0;Display_Buf[0] = start_x;Display_Buf[1] = start_y;Display_Buf[2] = start_x+Photo_wide-1;Display_Buf[3] = start_y+Photo_high-1;Buf_Count = Photo_wide*Photo_high*2;//画LCD显示范围Set_Dispaly(Display_Buf[0],Display_Buf[1],Display_Buf[2],Display_Buf[3]);LCD_WR_REG(0x2C);for(i=0;i<Buf_Count;i+=2){Color_Temp = (Photo_buf[i]<<8)|Photo_buf[i+1];LCD_WR_DATA(Color_Temp);}}

LCD头文件

#ifndef _TFT_LCD_H_#define _TFT_LCD_H_#include \"stm32f10x.h\"#include \"..\\User\\API\\Delay\\delay.h\"#include \"font.h\"//LCD在FSMC中寄存器操作//使用NOR/SRAM的 Bank1.sector1,地址位HADDR[27,26]=16 A16作为数据命令区分线 //1 1111 1111 1111 1110//01100000 000000X1 11111111 11111110//01100000 00000010 00000000 00000000//注意设置时STM32内部会右移一位对其!#define LCD_BASE ((u32)(0x60000000 | 0x0001FFFE))#define TFTLCD ((LCD_TypeDef *) LCD_BASE)//LCD寄存器结构体typedef struct{u16 LCD_REG;//命令寄存器u16 LCD_RAM;} LCD_TypeDef;//背光驱动#define LCD_LED_ON GPIO_SetBits(GPIOD,GPIO_Pin_12)#define LCD_LED_OFF GPIO_ResetBits(GPIOD,GPIO_Pin_12)typedef enum{Fontsize_16 = 0,//字符8*16,汉字16*16Fontsize_24,//字符16*24,汉字24*24Fontsize_32//字符16*32,汉字32*32}_FontSize; //RGB565部分颜色宏定义#define WHITE 0xFFFF#define BLACK 0x0000#define BLUE 0x001F#define BRED 0XF81F#define GRED 0XFFE0#define GBLUE 0X07FF#define RED  0xF800#define MAGENTA 0xF81F#define GREEN 0x07E0#define CYAN 0x7FFF#define YELLOW 0xFFE0#define BROWN 0XBC40 //棕色#define BRRED 0XFC07 //棕红色#define GRAY 0X8430 //灰色//初始化函数void LCD_Config(void);void LCD_PortConfig(void);void LCD_FSMCConfig(void);void LCD_9341Config(void);//底层接口函数void LCD_WR_REG(volatile uint16_t regval);void LCD_WR_DATA(volatile uint16_t data);void Set_Dispaly(uint16_t Line_x1,uint16_t Line_y1,uint16_t Line_x2,uint16_t Line_y2);//LCD常用函数void LCD_Clear(u16 Color);void LCD_Point(uint16_t point_x,uint16_t point_y,u16 Color);void LCD_Line(uint16_t Line_x1,uint16_t Line_y1,uint16_t Line_x2,uint16_t Line_y2,uint16_t Wight,u16 Color);void LCD_Rectangle(uint16_t Line_x1,uint16_t Line_y1,uint16_t Line_x2,uint16_t Line_y2,uint16_t Color);void LCD_Circular(uint16_t Circ_x,uint16_t Circ_y,uint16_t Circ_r,uint16_t Color);void LCD_DrawFont(uint16_t start_x,uint16_t start_y,uint16_t Font_wide,uint16_t Font_high,uint16_t Back_color,uint16_t Font_color,uint8_t *Font_buf);void LCD_DrawPhoto(uint16_t start_x,uint16_t start_y,uint16_t Photo_wide,uint16_t Photo_high,const unsigned char *Photo_buf);void LCD_DrawString(uint16_t start_x,uint16_t start_y,uint16_t Back_color,uint16_t Font_color,_FontSize Fontsize,char *Font_buf);#endif