STM32F4X TFTLCD ST7735S使用
- TFTLCD简介
- TFTLCD使用
- TFTLCD特点
- TFTLCD的概念
- TFTLCD色彩空间
- 三原色
- RGB颜色
- RGB565
- RGB666
- RGB888
- ST7735S驱动芯片
- ST7735S引脚定义
- ST7735S 4线SPI模式
- ST7735S显示原理
- ST7735S分辨率
- ST7735S显存结构
- ST7735S像素点扫描模式
- MCU操作ST7735S显存方法
- TFTLCD字库制作
- ASCII码字模制作
- 图片取模制作
- STM32F4X TFTLCD例程
- TFTLCD关键函数
- 硬件SPI初始化
- 数据传输函数
- 完整例程
TFTLCD简介
TFT-LCD 即薄膜晶体管液晶显示器。其英文全称为:Thin Film Transistor-Liquid Crystal Display。TFT-LCD与无源 TN-LCD、STN-LCD 的简单矩阵不同,它在液晶显示屏的每一个像素上都设置有一个薄膜晶体管(TFT),可有效地克服非选通时的串扰,使显示液晶屏的静态特性与扫描线数无关,因此大大提高了图像质量。TFT-LCD 也被叫做真彩液晶显示器。
TFTLCD使用
TFTLCD特点
- 分辨率高:常用的嵌入式TFTLCD屏幕分辨率有320 * 480、480 * 320、800 * 480、1024 * 600等。
- 颜色丰富:常见的TFTLCD支持的颜色有RGB565、RGB666和RGB888
- 操作接口丰富:TFTLCD支持并口的8080、串口的SPI、I2C等通信协议
- 自带触摸屏:TFTLCD可以外接触摸芯片,实现触摸屏的效果。
TFTLCD的概念
- 尺寸:尺寸是代表LCD的物理大小,其定义是屏幕对角线的距离。
- 分辨率:分辨率代表的是LCD能够显示多少个像素点,比如800 * 480分辨率的LCD,其水平距离可以显示800个像素点,垂直距离可以显示480个像素点,合计可以显示384000个像素点的大小。
- 颜色:LCD的颜色通常有RGB565、RGB666和RGB888,不同的颜色分类代表LCD的图像显示细腻度,其细腻度大小为RGB888>RGB666>RGB565。
TFTLCD色彩空间
不同于黑白LCD和OLED,前两者只能显示黑白的图像,而TFTLCD则可以显示彩色图像,根据不同的TFTLCD类型,主流的TFTLCD显示屏主要可以显示以下3种颜色,分别是RGB565、RGB666和RGB888。
三原色
在了解TFTLCD的颜色显示前,先了解一下什么是三原色。三原色指色彩中不能再分解的三种基本颜色,我们通常说的三原色,是色彩三原色以及光学三原色。三原色分别是指红、绿、蓝3种,可以配置三原色的不同比列来得到其他的颜色。
RGB颜色
颜色是一种物理量,但是在计算机中要怎样表达这种物理量呢,人们将物理世界中的颜色进行量化,利用二进制来表示不同的颜色,从而有了RGB的概念。RGB又分为RGB565、RGB666、RGB888.
RGB565
RGB565的意思是用5位表示R分量,6位表示G分量和5位表示B分量,每个像素的颜色用2个字节来表示。RGB565最多可以显示65536种颜色。
一个800 * 480分辨率的LCD,显示一张图片的数据大小为800 * 480 * 2 = 768000字节的数据。
RGB666
RGB666的意思是用6位表示R分量,6位表示G分量和6位表示B分量,每个像素的颜色用3个字节来表示,高6位为0,RGB666最多可以显示262144种颜色。
一个800 * 480分辨率的LCD,显示一张图片的数据大小为800 * 480 * 3 = 1152000字节的数据。
RGB888
RGB565的意思是用8位表示R分量,8位表示G分量和8位表示B分量,每个像素的颜色用3个字节来表示,RGB888最多可以显示16777216种颜色。
一个800 * 480分辨率的LCD,显示一张图片的数据大小为800 * 480 * 3 = 1152000字节的数据。
RGB565、RGB666和RGB888之间是可以互相转换的,但是需要注意的,低像素转换成高像素其精度不会损失,但是高像素转换成低像素,其精度会损失。
ST7735S驱动芯片
通常TFTLCD内部都会一块TFTLCD的驱动芯片,驱动芯片的作用是接收用户发送的数据,将数据进行管理,最后将数据显示到TFTLCD上。常见的TFTLCD驱动芯片有ILI9341、ILI9488、ST7789S、ST7796、ST7735S等,这些芯片虽然名字不一样,但是驱动基本类似。操作TFTLCD实际就是操作TFTLCD的驱动芯片。本次实验用的驱动芯片是ST7735S。
ST7735S引脚定义
ST7735S 4线SPI模式
因本章中使用的TFTLCD是4线SPI模式,所以下面就来说一下4线SPI RGB565模式的波形图,其他模式的波形图可以在ST7735S的数据手册中查看。
- IM2要为0,选择串行接口
- RESX为高电平
- 在开始传输数据前,要把CSX拉低,代表选中TFTLCD
- D/CX拉高,代表写数据
- 将颜色数据按照RGB格式进行组合
- 将组合好的RGB数据按照高位在前,低位在后的方式在SCL的高电平期间依次发送出去
ST7735S显示原理
ST7735S分辨率
ST7735S有两种分辨率,分别是128 * 160和132 * 162。这两种分辨率的选择是通过GM0和GM1两个引脚决定。实验中用到的ST7735S分辨率是132* 162。
ST7735S显存结构
由于LCD本身只是一块屏幕,只负责显示图像,其内部是没有存储图像数据的功能,这部分功能都是由LCD驱动芯片提供,下面就来看一下ST7735S的显存结构。以128 * 160分辨率、RGB565为例。
ST7735S把显存分成了128 * 160个格子,一共是20480个格子,每个格子代表一个像素,需要注意的是,因为我们使用的是RGB565的颜色格式,所以一个像素需要2个字节来表示,所以在分辨率为128 * 160,RGB565的模式下,ST7735S的显存大小一共是128 * 160 * 2 = 40960字节的数据大小。
ST7735S像素点扫描模式
通常TFTLCD的像素点的扫描模式都是从左到右,从上到下的模式进行扫描,当然用户也可以通过配置MV MX MY这几个寄存器位来修改像素点的扫描方向。
MCU操作ST7735S显存方法
针对ST7735S的显存存储结构,MCU每次修改数据的时候都需要将ST7735S的显存数据读出来,确保旧的数据跟新的数据没有冲突,但是这就有一个问题,每次修改数据的数据都需要读显存数据,这样就很浪费时间。所以在实际应用中,一般都会在MCU中定义一个128 * 160 * 2大小的二维数组,这个数组可以看做是ST7735S的外部显存,每次需要更新数据时,都会更新这个外部显存,最后再通过命令把整个显存数据刷新到ST7735S内部。
TFTLCD字库制作
由于ST7735S的内部没有字库,所以当我们需要在LCD上显示字符的时候,需要先自己制作字库,通过调用把字库中的字摸数据显示到LCD上。
ASCII码字模制作
ASCII码字模的制作软件是PCtoLCD2002,软件大家可以自行到网上进行下载。
我们需要制作常用的ASCII码字符,需要制作的字符如下,第一个ASCII码为空格
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
- 首先打开PCtoLCD2002,设置软件为字符模式
- 点击选项,进入设置模式,按照以下方法进行取模设置
- - 以生成8x16字符为例
在生成字符前先选择字符的高度和宽度,然后将需要生成的字符复制到输入框内,点击生成字模,最后点击保存字模,生成后的字模就会保存到文件中。
图片取模制作
图片取模的制作软件是Img2Lcd,软件大家可以自行到网上进行下载。
-
设置图片大小
打开windows的画图软件,设置图片大小为132* 162,并保存
-
打开Img2Lcd,按照下图方式设置
-
点击保存,将图片数据保存到文件中
STM32F4X TFTLCD例程
TFTLCD关键函数
硬件SPI初始化
由于LCD的传输协议为4线SPI协议,所以用到了STM32F4X的硬件SPI进行传输,如果是传输大批量数据,可以使用SPI DMA功能,加快传输速率。
void SPI1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1时钟
//GPIOFB3,4,5初始化设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5复用功能输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为 SPI1
GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4复用为 SPI1
GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5复用为 SPI1
//这里只针对SPI口初始化
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI1
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行同步时钟的空闲状态为高电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; //定义波特率预分频的值:波特率预分频值为2
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI1, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
SPI_Cmd(SPI1, ENABLE); //使能SPI外设
}
u8 SPI1_ReadWriteByte(u8 TxData)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET){}//等待发送区空
SPI_I2S_SendData(SPI1, TxData); //通过外设SPIx发送一个byte 数据
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完一个byte
return SPI_I2S_ReceiveData(SPI1); //返回通过SPIx最近接收的数据
}
void spi_dma_init(DMA_Stream_TypeDef* DMAy_Streamx,u32 dam_channel,u32 src,u32 dst,u32 len)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_DMA1,ENABLE);
DMA_DeInit(DMAy_Streamx);
DMA_Cmd(DMAy_Streamx, DISABLE);
DMA_InitStructure.DMA_Channel = dam_channel; //通道选择
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)dst; //DMA外设地址
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)src; //DMA 存储器0地址
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //存储器到外设模式
DMA_InitStructure.DMA_BufferSize = len; //数据传输量
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设非增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器增量模式
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据长度:8位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //存储器数据长度:8位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //使用普通模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //中等优先级
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //存储器突发单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设突发单次传输
DMA_Init(DMAy_Streamx, &DMA_InitStructure);
while (DMA_GetCmdStatus(DMAy_Streamx) != DISABLE){}
DMA_Cmd(DMAy_Streamx, ENABLE);
}
void spi_dma_cmd(unsigned char enable)
{
SPI_I2S_DMACmd(SPI1,SPI_I2S_DMAReq_Tx,enable);
}
数据传输函数
void tftlcd_send_data(unsigned char type,unsigned char data,unsigned int len)
{
if(SPI_TFTLCD_CMD == type) // 写命令
{
GPIO_ResetBits(TFTLCD_CS_PIN_PORT,TFTLCD_CS_PIN); // 拉低CS信号,选中LCD
GPIO_ResetBits(TFTLCD_DC_PIN_PORT,TFTLCD_DC_PIN); // 拉低DC信号,传输命令
SPI1_ReadWriteByte(data); // 通过硬件·SPI传输
GPIO_SetBits(TFTLCD_CS_PIN_PORT,TFTLCD_CS_PIN); // 拉高CS信号
GPIO_SetBits(TFTLCD_DC_PIN_PORT,TFTLCD_DC_PIN); // 拉高DC信号
}
else if(SPI_TFTLCD_DATA == type) // 写数据
{
GPIO_ResetBits(TFTLCD_CS_PIN_PORT,TFTLCD_CS_PIN); // 拉低CS信号,选中LCD
GPIO_SetBits(TFTLCD_DC_PIN_PORT,TFTLCD_DC_PIN); // 拉高DC信号,传输数据
SPI1_ReadWriteByte(data); // 通过硬件·SPI传输
GPIO_SetBits(TFTLCD_CS_PIN_PORT,TFTLCD_CS_PIN); // 拉高CS信号
GPIO_SetBits(TFTLCD_DC_PIN_PORT,TFTLCD_DC_PIN); // 拉高DC信号
}
else if(SPI_TFTLCD_DELAY == type)
delay_ms(data);
}
void tftlcd_send_data_dma(unsigned char type,unsigned char *data,unsigned int len)
{
if(SPI_TFTLCD_CMD == type)
{
GPIO_ResetBits(TFTLCD_CS_PIN_PORT,TFTLCD_CS_PIN);
GPIO_ResetBits(TFTLCD_DC_PIN_PORT,TFTLCD_DC_PIN);
spi_dma_cmd(DISABLE);
spi_dma_init(DMA2_Stream3,DMA_Channel_3,(u32)data,(u32)&SPI1->DR,len);
spi_dma_cmd(ENABLE);
while(DMA_GetFlagStatus(DMA2_Stream3,DMA_FLAG_TCIF3) != SET);
spi_dma_cmd(DISABLE);
GPIO_SetBits(TFTLCD_CS_PIN_PORT,TFTLCD_CS_PIN);
GPIO_SetBits(TFTLCD_DC_PIN_PORT,TFTLCD_DC_PIN);
}
else if(SPI_TFTLCD_DATA == type)
{
GPIO_ResetBits(TFTLCD_CS_PIN_PORT,TFTLCD_CS_PIN);
GPIO_SetBits(TFTLCD_DC_PIN_PORT,TFTLCD_DC_PIN);
spi_dma_cmd(DISABLE);
spi_dma_init(DMA2_Stream3,DMA_Channel_3,(u32)data,(u32)&SPI1->DR,len);
spi_dma_cmd(ENABLE);
while(DMA_GetFlagStatus(DMA2_Stream3,DMA_FLAG_TCIF3) != SET);
spi_dma_cmd(DISABLE);
GPIO_SetBits(TFTLCD_CS_PIN_PORT,TFTLCD_CS_PIN);
GPIO_SetBits(TFTLCD_DC_PIN_PORT,TFTLCD_DC_PIN);
}
else if(SPI_TFTLCD_DELAY == type)
delay_ms(data[0]);
}
完整例程
由于程序过大,请到百度网盘下载
注:本例程基于正点原子STM32F4X系列例程和LCD厂商提供的LCD驱动例程编写。
链接:https://pan.baidu.com/s/1I6OKBH7uD3ixRnXObmeYLw
提取码:usc8