本帖最后由 qiao--- 于 2024-1-7 20:57 编辑
我这次的测评点亮这个屏幕用了两种方法,一种是硬件spi驱动屏幕一种是用软件驱动屏幕。而软硬件驱动切换用了条件编译的方法切换。首先给大家展示一下我的屏幕
对于驱动屏幕熟悉的贴友,应该知道,驱动一个屏幕首先要实现向屏幕写一个字节的数据的函数,只要这个底层做好了,其余的直接移植屏幕出厂方的代码就行了。
1.初始化硬件并编写写一个字节数据的函数
#include "st7735.h"
SPI_HandleTypeDef SPI_Handle;
DMA_HandleTypeDef DMA_SPIT_Handle;
void SPI_GPIO_Init(void){
GPIO_InitTypeDef GPIO_Handle = {0};
/* Enable Clock */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* PA4 CS
PA5 SCK
PA12 MOSI
PA11 RST
PA7 DC
PA6 BLK
*/
#if SOFT
GPIO_Handle.Pin = GPIO_PIN_4 | GPIO_PIN_11 | GPIO_PIN_6 |GPIO_PIN_7 |GPIO_PIN_12 |GPIO_PIN_5;
#else
GPIO_Handle.Pin = GPIO_PIN_4 | GPIO_PIN_11 | GPIO_PIN_6 |GPIO_PIN_7 ;
#endif
GPIO_Handle.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Handle.Pull = GPIO_NOPULL;
GPIO_Handle.Drive = GPIO_DRIVE_LEVEL3;
GPIO_Handle.Alternate = GPIO_FUNCTION_0;
HAL_GPIO_Init(GPIOA, &GPIO_Handle);
LCD_CS_HIGH();
}
#if SOFT
//向SPI总线传输一个8位数据
void SPI_WriteData(uint8_t Data)
{
unsigned char i=0;
for(i=8;i>0;i--)
{
if(Data&0x80)
LCD_SDA_HIGH(); //输出数据
else LCD_SDA_LOW();
LCD_SCK_HIGH();
LCD_SCK_LOW();
Data<<=1;
}
}
#else
void SPI_Init(void){
SPI_GPIO_Init();
SPI_Handle.Instance = SPI1;
SPI_Handle.Init.SPI_Mode = SPI_MODE_MASTER;
SPI_Handle.Init.SPI_Work_Mode = SPI_WORK_MODE_0;
SPI_Handle.Init.X_Mode = SPI_1X_MODE;
SPI_Handle.Init.First_Bit = SPI_FIRSTBIT_MSB;
SPI_Handle.Init.BaudRate_Prescaler = SPI_BAUDRATE_PRESCALER_64;
HAL_SPI_Init(&SPI_Handle);
}
#endif
//向液晶屏写一个8位指令
void Lcd_WriteIndex(uint8_t Index)
{
//SPI 写命令时序开始
LCD_CS_LOW();
LCD_DC_LOW();
#if SOFT
SPI_WriteData(Index);
#else
HAL_SPI_Transmit(&SPI_Handle,&Index,1,HAL_MAX_DELAY);
#endif
LCD_CS_HIGH();
}
2.实现写一个像素的函数。因为我这里用的是RGB565的数据格式,所以写一个像素的数据是写进两个字节,这里可以引用我们写一个字节的函数。
//向液晶屏写一个16位数据
void LCD_WriteData_16Bit(uint16_t Data)
{
uint8_t tmp_1 = Data >>8;
uint8_t tmp_2 = Data;
LCD_CS_LOW();
LCD_DC_HIGH();
#if SOFT
SPI_WriteData(tmp_1);
SPI_WriteData(tmp_2);
#else
HAL_SPI_Transmit(&SPI_Handle,&tmp_1,1,HAL_MAX_DELAY);
HAL_SPI_Transmit(&SPI_Handle,&tmp_2,1,HAL_MAX_DELAY);
#endif
LCD_CS_HIGH();
}
3.修改屏幕官方的代码。上面实现好了后,我们可以把屏幕官方的代码的相关写数据的函数名改成我们自己编写的就OK了。有需要的贴友可以看下放的附件,工程已给出。
驱动部分整体代码如下
#include "st7735.h"
SPI_HandleTypeDef SPI_Handle;
DMA_HandleTypeDef DMA_SPIT_Handle;
void SPI_GPIO_Init(void){
GPIO_InitTypeDef GPIO_Handle = {0};
/* Enable Clock */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* PA4 CS
PA5 SCK
PA12 MOSI
PA11 RST
PA7 DC
PA6 BLK
*/
#if SOFT
GPIO_Handle.Pin = GPIO_PIN_4 | GPIO_PIN_11 | GPIO_PIN_6 |GPIO_PIN_7 |GPIO_PIN_12 |GPIO_PIN_5;
#else
GPIO_Handle.Pin = GPIO_PIN_4 | GPIO_PIN_11 | GPIO_PIN_6 |GPIO_PIN_7 ;
#endif
GPIO_Handle.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_Handle.Pull = GPIO_NOPULL;
GPIO_Handle.Drive = GPIO_DRIVE_LEVEL3;
GPIO_Handle.Alternate = GPIO_FUNCTION_0;
HAL_GPIO_Init(GPIOA, &GPIO_Handle);
LCD_CS_HIGH();
}
void DMA_SPITransmit_Init(void)
{
__HAL_RCC_DMA2_CLK_ENABLE();
DMA_SPIT_Handle.Instance = DMA2_Channel1;
DMA_SPIT_Handle.Init.DataFlow = DMA_DATAFLOW_M2P;
DMA_SPIT_Handle.Init.ReqID = DMA2_REQ47_SPI3_SEND;
DMA_SPIT_Handle.Init.SrcInc = DMA_SRCINC_ENABLE;
DMA_SPIT_Handle.Init.DestInc = DMA_DESTINC_DISABLE;
DMA_SPIT_Handle.Init.SrcWidth = DMA_SRCWIDTH_BYTE;
DMA_SPIT_Handle.Init.DestWidth = DMA_DESTWIDTH_BYTE;
/*-----------------------------------------------------------------------------------*/
/* Note:If user dons not apply interrupt, Set DMA_ITC_Callback?¢DMA_IE_Callback NULL */
/*-----------------------------------------------------------------------------------*/
DMA_SPIT_Handle.XferCpltCallback = NULL;
DMA_SPIT_Handle.XferErrorCallback = NULL;
HAL_DMA_Init(&DMA_SPIT_Handle);
SPI_Handle.HDMA_Tx = &DMA_SPIT_Handle;
}
#if SOFT
//向SPI总线传输一个8位数据
void SPI_WriteData(uint8_t Data)
{
unsigned char i=0;
for(i=8;i>0;i--)
{
if(Data&0x80)
LCD_SDA_HIGH(); //输出数据
else LCD_SDA_LOW();
LCD_SCK_HIGH();
LCD_SCK_LOW();
Data<<=1;
}
}
#else
void SPI_Init(void){
SPI_GPIO_Init();
SPI_Handle.Instance = SPI1;
SPI_Handle.Init.SPI_Mode = SPI_MODE_MASTER;
SPI_Handle.Init.SPI_Work_Mode = SPI_WORK_MODE_0;
SPI_Handle.Init.X_Mode = SPI_1X_MODE;
SPI_Handle.Init.First_Bit = SPI_FIRSTBIT_MSB;
SPI_Handle.Init.BaudRate_Prescaler = SPI_BAUDRATE_PRESCALER_64;
HAL_SPI_Init(&SPI_Handle);
}
#endif
//向液晶屏写一个8位指令
void Lcd_WriteIndex(uint8_t Index)
{
//SPI 写命令时序开始
LCD_CS_LOW();
LCD_DC_LOW();
#if SOFT
SPI_WriteData(Index);
#else
HAL_SPI_Transmit(&SPI_Handle,&Index,1,HAL_MAX_DELAY);
#endif
LCD_CS_HIGH();
}
//向液晶屏写一个8位数据
void Lcd_WriteData(uint8_t Index)
{
LCD_CS_LOW();
LCD_DC_HIGH();
#if SOFT
SPI_WriteData(Index);
#else
HAL_SPI_Transmit(&SPI_Handle,&Index,1,HAL_MAX_DELAY);
#endif
LCD_CS_HIGH();
}
//向液晶屏写一个16位数据
void LCD_WriteData_16Bit(uint16_t Data)
{
uint8_t tmp_1 = Data >>8;
uint8_t tmp_2 = Data;
LCD_CS_LOW();
LCD_DC_HIGH();
#if SOFT
SPI_WriteData(tmp_1);
SPI_WriteData(tmp_2);
#else
HAL_SPI_Transmit(&SPI_Handle,&tmp_1,1,HAL_MAX_DELAY);
HAL_SPI_Transmit(&SPI_Handle,&tmp_2,1,HAL_MAX_DELAY);
#endif
LCD_CS_HIGH();
}
void Lcd_WriteReg(uint8_t Index,uint8_t Data)
{
Lcd_WriteIndex(Index);
Lcd_WriteData(Data);
}
void Lcd_Reset(void)
{
LCD_RST_LOW();
HAL_Delay(100);
LCD_RST_HIGH();
HAL_Delay(100);
}
void Lcd_Init(void)
{
#if SOFT
SPI_GPIO_Init();
#else
SPI_Init();
#endif
Lcd_Reset(); //Reset before LCD Init.
//LCD Init For 1.44Inch LCD Panel with ST7735R.
Lcd_WriteIndex(0x11);//Sleep exit
HAL_Delay (120);
//ST7735R Frame Rate
Lcd_WriteIndex(0xB1);
Lcd_WriteData(0x01);
Lcd_WriteData(0x2C);
Lcd_WriteData(0x2D);
Lcd_WriteIndex(0xB2);
Lcd_WriteData(0x01);
Lcd_WriteData(0x2C);
Lcd_WriteData(0x2D);
Lcd_WriteIndex(0xB3);
Lcd_WriteData(0x01);
Lcd_WriteData(0x2C);
Lcd_WriteData(0x2D);
Lcd_WriteData(0x01);
Lcd_WriteData(0x2C);
Lcd_WriteData(0x2D);
Lcd_WriteIndex(0xB4); //Column inversion
Lcd_WriteData(0x07);
//ST7735R Power Sequence
Lcd_WriteIndex(0xC0);
Lcd_WriteData(0xA2);
Lcd_WriteData(0x02);
Lcd_WriteData(0x84);
Lcd_WriteIndex(0xC1);
Lcd_WriteData(0xC5);
Lcd_WriteIndex(0xC2);
Lcd_WriteData(0x0A);
Lcd_WriteData(0x00);
Lcd_WriteIndex(0xC3);
Lcd_WriteData(0x8A);
Lcd_WriteData(0x2A);
Lcd_WriteIndex(0xC4);
Lcd_WriteData(0x8A);
Lcd_WriteData(0xEE);
Lcd_WriteIndex(0xC5); //VCOM
Lcd_WriteData(0x0E);
Lcd_WriteIndex(0x36); //MX, MY, RGB mode
Lcd_WriteData(0xC0);
//ST7735R Gamma Sequence
Lcd_WriteIndex(0xe0);
Lcd_WriteData(0x0f);
Lcd_WriteData(0x1a);
Lcd_WriteData(0x0f);
Lcd_WriteData(0x18);
Lcd_WriteData(0x2f);
Lcd_WriteData(0x28);
Lcd_WriteData(0x20);
Lcd_WriteData(0x22);
Lcd_WriteData(0x1f);
Lcd_WriteData(0x1b);
Lcd_WriteData(0x23);
Lcd_WriteData(0x37);
Lcd_WriteData(0x00);
Lcd_WriteData(0x07);
Lcd_WriteData(0x02);
Lcd_WriteData(0x10);
Lcd_WriteIndex(0xe1);
Lcd_WriteData(0x0f);
Lcd_WriteData(0x1b);
Lcd_WriteData(0x0f);
Lcd_WriteData(0x17);
Lcd_WriteData(0x33);
Lcd_WriteData(0x2c);
Lcd_WriteData(0x29);
Lcd_WriteData(0x2e);
Lcd_WriteData(0x30);
Lcd_WriteData(0x30);
Lcd_WriteData(0x39);
Lcd_WriteData(0x3f);
Lcd_WriteData(0x00);
Lcd_WriteData(0x07);
Lcd_WriteData(0x03);
Lcd_WriteData(0x10);
Lcd_WriteIndex(0x2a);
Lcd_WriteData(0x00);
Lcd_WriteData(0x00);
Lcd_WriteData(0x00);
Lcd_WriteData(0x7f);
Lcd_WriteIndex(0x2b);
Lcd_WriteData(0x00);
Lcd_WriteData(0x00);
Lcd_WriteData(0x00);
Lcd_WriteData(0x9f);
Lcd_WriteIndex(0xF0); //Enable test command
Lcd_WriteData(0x01);
Lcd_WriteIndex(0xF6); //Disable ram power save mode
Lcd_WriteData(0x00);
Lcd_WriteIndex(0x3A); //65k mode
Lcd_WriteData(0x05);
Lcd_WriteIndex(0x29);//Display on
}
/*************************************************
函数名:LCD_Set_Region
功能:设置lcd显示区域,在此区域写点数据自动换行
入口参数:xy起点和终点
返回值:无
*************************************************/
void Lcd_SetRegion(uint16_t x_start,uint16_t y_start,uint16_t x_end,uint16_t y_end)
{
Lcd_WriteIndex(0x2a);
Lcd_WriteData(0x00);
Lcd_WriteData(x_start);//Lcd_WriteData(x_start+2);
Lcd_WriteData(0x00);
Lcd_WriteData(x_end+2);
Lcd_WriteIndex(0x2b);
Lcd_WriteData(0x00);
Lcd_WriteData(y_start+0);
Lcd_WriteData(0x00);
Lcd_WriteData(y_end+1);
Lcd_WriteIndex(0x2c);
}
/*************************************************
函数名:LCD_Set_XY
功能:设置lcd显示起始点
入口参数:xy坐标
返回值:无
*************************************************/
void Lcd_SetXY(uint16_t x,uint16_t y)
{
Lcd_SetRegion(x,y,x,y);
}
/*************************************************
函数名:LCD_DrawPoint
功能:画一个点
入口参数:无
返回值:无
*************************************************/
void Gui_DrawPoint(uint16_t x,uint16_t y,uint16_t Data)
{
Lcd_SetRegion(x,y,x+1,y+1);
LCD_WriteData_16Bit(Data);
}
/*****************************************
函数功能:读TFT某一点的颜色
出口参数:color 点颜色值
******************************************/
unsigned int Lcd_ReadPoint(uint16_t x,uint16_t y)
{
unsigned int Data;
Lcd_SetXY(x,y);
//Lcd_ReadData();//丢掉无用字节
//Data=Lcd_ReadData();
Lcd_WriteData(Data);
return Data;
}
/*************************************************
函数名:Lcd_Clear
功能:全屏清屏函数
入口参数:填充颜色COLOR
返回值:无
*************************************************/
void Lcd_Clear(uint16_t Color)
{
unsigned int i,m;
Lcd_SetRegion(0,0,X_MAX_PIXEL-1,Y_MAX_PIXEL-1);
Lcd_WriteIndex(0x2C);
for(i=0;i<X_MAX_PIXEL;i++)
for(m=0;m<Y_MAX_PIXEL;m++)
{
LCD_WriteData_16Bit(Color);
}
}
4.移植字库。
我们把屏幕原厂的字库移植到我们工程里,同时也要移植显示这些字库的函数文件。我的相关代码放在路径App\tft7735下面。
5.测试。
代码下进板子后我们上电看一下效果。
IMG_8161
总结:通过硬件SPI或者软件模拟时序可以成功的驱动1.8寸RGB_TFT液晶屏。