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