最近用到了非常小众的TFT LCD屏幕,其尺寸只有0.85寸(128x160),驱动芯片是GC9107,通讯接口是半双工SPI,功能引脚为:

mosi 数据线,在半双工模式收和发都通过这一条数据线,同一时刻只能接受或者发送。

clk 时钟线,同步通信。

cs 片选线。

dc 写命令/数据控制线,有的地方叫rs。

可以用esp32的通用spi接口驱动或者用io口模拟spi通信驱动。

一、硬件spi驱动

spi_device_handle_t spi_dev;
void init_spi_3wire() 
{
    // 1. 配置SPI总线 
    spi_bus_config_t bus_cfg = {
        .mosi_io_num     = PIN_NUM_MOSI,    // MOSI/SDIO引脚 
        .miso_io_num     = -1,              // 禁用MISO 
        .sclk_io_num     = PIN_NUM_CLK,     // SCK时钟引脚 
        .quadhd_io_num   = -1,              // 禁用其他功能 
        .quadwp_io_num   = -1,
        .max_transfer_sz = 4096,            // 最大传输长度 
        .flags = SPICOMMON_BUSFLAG_DUAL     // 启用半双工标志 
    };
    spi_bus_initialize(SPI2_HOST, &bus_cfg, SPI_DMA_CH_AUTO);
 
    // 2. 添加从设备 
    spi_device_interface_config_t dev_cfg = {
        .clock_speed_hz = 10 * 1000 * 1000,               // 时钟频率10MHz 
        .mode           = 3,                              // SPI模式3 (CPOL=1, CPHA=1)
        .spics_io_num   = PIN_NUM_CS,                     // CS片选引脚 
        .queue_size     = 7,                              // 事务队列长度 
        // .pre_cb         = lcd_spi_pre_transfer_callback,  // SPI发送数据前执行的回调函数,一般用于SPI屏幕的D/C信号切换
        .flags          = SPI_DEVICE_HALFDUPLEX           // 半双工模式 
    };

    spi_bus_add_device(SPI2_HOST, &dev_cfg, &spi_dev);
}  

初始化:(初始化dc引脚,调用以上spi初始化)

//硬件SPI初始化
void SPI_COM_Init(void)
{
    gpio_config_t lcd_dc_cfg = {
            .pin_bit_mask = (1ULL<<PIN_NUM_DC),       //配置引脚
            .mode =GPIO_MODE_OUTPUT,                  //输出模式
            .pull_up_en = GPIO_PULLUP_DISABLE,        //不使能上拉
            .pull_down_en = GPIO_PULLDOWN_DISABLE,    //不使能下拉
            .intr_type = GPIO_INTR_DISABLE            //不使能引脚中断
    };
    gpio_config(&lcd_dc_cfg);

    init_spi_3wire();
}

发送:

// 发送命令(TX模式)
uint8_t cmd = 0x9F; 
spi_transaction_t t = {
    .length = 8,     // 命令长度(位)
    .tx_buffer = &cmd,
    .rx_buffer = NULL
};
spi_device_transmit(spi_dev, &t);

接收:(发送空白buffer会返回数据)spi通信本质是数据交换

// 切换为接收模式(RX模式)
uint8_t id_buf[3] = {0};
t.length  = 24;       // 3字节ID
t.tx_buffer  = NULL;
t.rx_buffer  = id_buf;
spi_device_transmit(spi_dev, &t); // 自动切换MOSI为输入

二、往lcd驱动gc9107寄存器写值

硬件spi写:(写命令/写数据通过设置dc引脚决定)

//写寄存器函数
//data:寄存器值
void LCD_WR_REG(u8 data)
{ 	
	LCD_RS_CLR;//dc引脚

    spi_transaction_t t = {
        .length = 8,     // 命令长度(位)
        .tx_buffer = &data,
        .rx_buffer = NULL 
    };
    spi_device_transmit(spi_dev, &t);

	
	LCD_RS_SET;
}

//写8位数据函数
void LCD_WR_DATA(u8 data)
{		
	LCD_RS_SET;

	spi_transaction_t t = {
        .length = 8,     // 命令长度(位)
        .tx_buffer = &data,
        .rx_buffer = NULL 
    };
    spi_device_transmit(spi_dev, &t);
		
}

软件spi(io口模拟):

//写寄存器函数
//data:寄存器值
void LCD_WR_REG(u8 data)
{ 	
	u8 i;
	
	LCD_RS_CLR;
	LCD_CS_CLR;

    //以下为软件SPI代码
 	for(i=0;i<8;i++)
 	{			  
 		LCD_CLK_CLR;
 		if(data&0x80)
 		{
 		   LCD_DATA_SET;
 		}
 		else
 		{
 		   LCD_DATA_CLR;
 		}
 		LCD_CLK_SET;
 		data<<=1;
	}	

    LCD_CS_SET;	
	LCD_RS_SET;
}

//写8位数据函数
//用宏定义,提高速度.
void LCD_WR_DATA(u8 data)
{		
	u8 i;
	
	LCD_RS_SET;
	LCD_CS_CLR;

	//以下为软件SPI代码	
 	for(i=0;i<8;i++)
 	{			  
 		LCD_CLK_CLR;
 		if(data&0x80)
 		{
 		   LCD_DATA_SET;
		}
 		else
 		{
 		   LCD_DATA_CLR;
 		}
 		LCD_CLK_SET;
 		data<<=1;
 	}	
	
  LCD_CS_SET;		
}

三、完整驱动

lcd.c

#include "spi_lcd.h"
#include <string.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"

#include "stdlib.h"
#include "font.h" 
 

/////////////////////////////// SPI ////////////////////////////////
// void lcd_spi_pre_transfer_callback(spi_transaction_t *t)
// {
//     int dc=(int)t->user;
//     gpio_set_level(PIN_NUM_DC, dc);
// }

spi_device_handle_t spi_dev;
void init_spi_3wire() 
{
    // 1. 配置SPI总线 
    spi_bus_config_t bus_cfg = {
        .mosi_io_num     = PIN_NUM_MOSI,    // MOSI/SDIO引脚 
        .miso_io_num     = -1,              // 禁用MISO 
        .sclk_io_num     = PIN_NUM_CLK,     // SCK时钟引脚 
        .quadhd_io_num   = -1,              // 禁用其他功能 
        .quadwp_io_num   = -1,
        .max_transfer_sz = 4096,            // 最大传输长度 
        .flags = SPICOMMON_BUSFLAG_DUAL     // 启用半双工标志 
    };
    spi_bus_initialize(SPI2_HOST, &bus_cfg, SPI_DMA_CH_AUTO);
 
    // 2. 添加从设备 
    spi_device_interface_config_t dev_cfg = {
        .clock_speed_hz = 10 * 1000 * 1000,               // 时钟频率10MHz 
        .mode           = 3,                              // SPI模式3 (CPOL=1, CPHA=1)
        .spics_io_num   = PIN_NUM_CS,                     // CS片选引脚 
        .queue_size     = 7,                              // 事务队列长度 
        // .pre_cb         = lcd_spi_pre_transfer_callback,  // SPI发送数据前执行的回调函数,一般用于SPI屏幕的D/C信号切换
        .flags          = SPI_DEVICE_HALFDUPLEX           // 半双工模式 
    };

    spi_bus_add_device(SPI2_HOST, &dev_cfg, &spi_dev);
}   
	

/////////////////////////////// lcd ///////////////////////////////////
//画笔颜色,背景颜色
u16 POINT_COLOR = 0x0000,BACK_COLOR = 0xFFFF;  
u16 DeviceCode;	 					    

//////////////////////////////////////
//硬件SPI初始化
void SPI_COM_Init(void)
{
    gpio_config_t lcd_dc_cfg = {
            .pin_bit_mask = (1ULL<<PIN_NUM_DC),       //配置引脚
            .mode =GPIO_MODE_OUTPUT,                  //输出模式
            .pull_up_en = GPIO_PULLUP_DISABLE,        //不使能上拉
            .pull_down_en = GPIO_PULLDOWN_DISABLE,    //不使能下拉
            .intr_type = GPIO_INTR_DISABLE            //不使能引脚中断
    };
    gpio_config(&lcd_dc_cfg);

    init_spi_3wire();
}

//////////////////////////////////////

//写寄存器函数
//data:寄存器值
void LCD_WR_REG(u8 data)
{ 	
	// u8 i;
	
	LCD_RS_CLR;
	// LCD_CS_CLR;

    spi_transaction_t t = {
        .length = 8,     // 命令长度(位)
        .tx_buffer = &data,
        .rx_buffer = NULL 
    };
    spi_device_transmit(spi_dev, &t);

#if 0
//以下为软件SPI代码
// 	for(i=0;i<8;i++)
// 	{			  
// 		LCD_CLK_CLR;
// 		if(data&0x80)
// 		{
// 		   LCD_DATA_SET;
// 		}
// 		else
// 		{
// 		   LCD_DATA_CLR;
// 		}
// 		LCD_CLK_SET;
// 		data<<=1;
	}	
#endif

//   LCD_CS_SET;	
	LCD_RS_SET;
}

//写8位数据函数
//用宏定义,提高速度.
void LCD_WR_DATA(u8 data)
{		
	// u8 i;
	
	LCD_RS_SET;
	// LCD_CS_CLR;
	spi_transaction_t t = {
        .length = 8,     // 命令长度(位)
        .tx_buffer = &data,
        .rx_buffer = NULL 
    };
    spi_device_transmit(spi_dev, &t);

#if 0
	//以下为软件SPI代码	
// 	for(i=0;i<8;i++)
// 	{			  
// 		LCD_CLK_CLR;
// 		if(data&0x80)
// 		{
// 		   LCD_DATA_SET;
// 		}
// 		else
// 		{
// 		   LCD_DATA_CLR;
// 		}
// 		LCD_CLK_SET;
// 		data<<=1;
// 	}	
#endif
	
//   LCD_CS_SET;		
}

void LCD_WR_DATA16(u16 dat)
{
	LCD_WR_DATA(dat >> 8);
	LCD_WR_DATA(dat);
}

void LCD_Address_Set(u16 x1,u16 y1,u16 x2,u16 y2)
{
		LCD_WR_REG(0x2a);//?????
		LCD_WR_DATA16(x1);
		LCD_WR_DATA16(x2);
		LCD_WR_REG(0x2b);//?????
		LCD_WR_DATA16(y1);
		LCD_WR_DATA16(y2);
		LCD_WR_REG(0x2c);//????
}

void LCD_Fill(u16 xsta,u16 ysta,u16 xend,u16 yend,u16 color)
{          
	u16 i,j; 
// 	u8 retry=0;		
	
	LCD_Address_Set(xsta,ysta,xend-1,yend-1);//??????
	
//以下为提升帧频速度时考虑使用的,18Mhz时帧频大概16帧/S			
// 	LCD_RS_SET;
// 	LCD_CS_CLR;
	
	for(i=ysta;i<yend;i++)
	{													   	 	
		for(j=xsta;j<xend;j++)
		{
			LCD_WR_DATA16(color);
			
//以下为提升帧频速度时考虑使用的,18Mhz时帧频大概16帧/S			
// 		while((SPI1->SR&1<<1)==0)//等待发送区空	
// 		{
// 			retry++;
// 			if(retry>200)return;
// 		}			  
// 		SPI1->DR=color >> 8;	 	  //发送一个byte 
// 		
// 		retry=0;
// 		while((SPI1->SR&1<<1)==0)//等待发送区空	
// 		{
// 			retry++;
// 			if(retry>200)return ;
// 		}			  
// 		SPI1->DR=color;	 	  //发送一个byte 
		}
			
	} 					  	    
  LCD_CS_SET;		
}

void LCD_Fill_PIC(u16 xsta,u16 ysta,u16 xend,u16 yend,const u8 *data)
{          
	u16 i,j; 
// 	u8 retry=0;		
	
	LCD_Address_Set(xsta,ysta,xend-1,yend-1);//??????
	
//以下为提升帧频速度时考虑使用的,18Mhz时帧频大概16帧/S			
// 	LCD_RS_SET;
// 	LCD_CS_CLR;
	
	for(i=ysta;i<yend;i++)
	{													   	 	
		for(j=xsta;j<xend;j++)
		{
			LCD_WR_DATA(*data);
			data++;
			LCD_WR_DATA(*data);
			data++;
//以下为提升帧频速度时考虑使用的,18Mhz时帧频大概16帧/S			
// 		while((SPI1->SR&1<<1)==0)//等待发送区空	
// 		{
// 			retry++;
// 			if(retry>200)return;
// 		}			  
// 		SPI1->DR=color >> 8;	 	  //发送一个byte 
// 		
// 		retry=0;
// 		while((SPI1->SR&1<<1)==0)//等待发送区空	
// 		{
// 			retry++;
// 			if(retry>200)return ;
// 		}			  
// 		SPI1->DR=color;	 	  //发送一个byte 
		}
			
	} 					  	    
  LCD_CS_SET;		
}

// void LCD_Fill(u16 xsta,u16 ysta,u16 xend,u16 yend,u16 color)
// {          
// 	u16 i,j; 
// 	LCD_Address_Set(xsta,ysta,xend-1,yend-1);//??????
// 	for(i=ysta;i<yend;i++)
// 	{													   	 	
// 		for(j=xsta;j<xend;j++)
// 		{
// //			LCD_WR_DATA16(color);
// 				LCD_WR_DATA(color >> 8);
// 				LCD_WR_DATA(color);

// 		}
// 	} 					  	    
// }

//写寄存器
//LCD_Reg:寄存器编号
//LCD_RegValue:要写入的值
void LCD_WriteReg(u8 LCD_Reg,u16 LCD_RegValue)
{	
	LCD_WR_REG(LCD_Reg);  
	LCD_WR_DATA(LCD_RegValue);	    		 
}
	   

//开始写GRAM
void LCD_WriteRAM_Prepare(void)
{
	if(DeviceCode==0X9341)LCD_WR_REG(0x2C);//ILI9341是以0X2C开始的
	else LCD_WR_REG(R34);
}	
 
//LCD写GRAM
//RGB_Code:颜色值
void LCD_WriteRAM(u16 RGB_Code)
{							    
	LCD_WR_DATA(RGB_Code);//写十六位GRAM
}
//从ILI93xx读出的数据为GBR格式,而我们写入的时候为RGB格式。
//通过该函数转换
//c:GBR格式的颜色值
//返回值:RGB格式的颜色值

u16 LCD_BGR2RGB(u16 c)
{
	u16  r,g,b,rgb;   
	b=(c>>0)&0x1f;
	g=(c>>5)&0x3f;
	r=(c>>11)&0x1f;	 
	rgb=(b<<11)+(g<<5)+(r<<0);		 
	return(rgb);
}		 

//LCD开启显示
void LCD_DisplayOn(void)
{	
	if(DeviceCode==0X9341)LCD_WR_REG(0X29);	//开启显示
	else LCD_WriteReg(R7,0x0173); 			//开启显示
}	 

//LCD关闭显示
void LCD_DisplayOff(void)
{	   
	if(DeviceCode==0X9341)LCD_WR_REG(0X28);	//关闭显示
	else LCD_WriteReg(R7, 0x0);//关闭显示 
}   
//设置光标位置
//Xpos:横坐标
//Ypos:纵坐标
__inline void LCD_SetCursor(u16 Xpos, u16 Ypos)
{
#if USE_HORIZONTAL==1 
	if(DeviceCode==0X8989)
	{
		Xpos=319-Xpos;
		LCD_WriteReg(0X4E, Ypos);
		LCD_WriteReg(0X4F, Xpos);
    }else if(DeviceCode==0X9341)//9341,设置坐标
	{			 
		LCD_WR_REG(0x2B); 
		LCD_WR_DATA(Ypos>>8); 
		LCD_WR_DATA(Ypos&0XFF);			 
		LCD_WR_REG(0x2A); 
		LCD_WR_DATA(Xpos>>8); 
		LCD_WR_DATA(Xpos&0XFF);
	}else
	{
		Xpos=319-Xpos;
		LCD_WriteReg(R32,Ypos);
		LCD_WriteReg(R33,Xpos);
	}							   
#else
	if(DeviceCode==0X8989)
	{
		LCD_WriteReg(0X4E, Xpos);
		LCD_WriteReg(0X4F, Ypos);
    }else if(DeviceCode==0X9341)
	{		    
		LCD_WR_REG(0x2A); 
		LCD_WR_DATA(Xpos>>8); 
		LCD_WR_DATA(Xpos&0XFF);	 
		LCD_WR_REG(0x2B); 
		LCD_WR_DATA(Ypos>>8); 
		LCD_WR_DATA(Ypos&0XFF);
	}else
	{
		LCD_WriteReg(R32, Xpos);
		LCD_WriteReg(R33, Ypos);
	}						    
#endif
}
//设置LCD的自动扫描方向
//注意:其他函数可能会受到此函数设置的影响(尤其是9341这个奇葩),所以,
//一般设置为L2R_U2D即可,如果设置为其他扫描方式,可能导致显示不正常.
//0~7:代表8个方向(具体定义见lcd.h)
//9320/9325/9328/4531/4535/1505/b505/8989/5408/9341等IC已经实际测试	   
void LCD_Scan_Dir(u8 dir)
{
	u16 regval=0;
	u8 dirreg=0;
#if USE_HORIZONTAL//使用横屏
	switch(dir)//方向转换
	{
		case 0:dir=6;break;
		case 1:dir=7;break;
		case 2:dir=4;break;
		case 3:dir=5;break;
		case 4:dir=1;break;
		case 5:dir=0;break;
		case 6:dir=3;break;
		case 7:dir=2;break;	     
	}
#endif
	if(DeviceCode==0x9341)//9341,很特殊
	{
		switch(dir)
		{
			case L2R_U2D://从左到右,从上到下
				regval|=(0<<7)|(0<<6)|(0<<5); 
				break;
			case L2R_D2U://从左到右,从下到上
				regval|=(1<<7)|(0<<6)|(0<<5); 
				break;
			case R2L_U2D://从右到左,从上到下
				regval|=(0<<7)|(1<<6)|(0<<5); 
				break;
			case R2L_D2U://从右到左,从下到上
				regval|=(1<<7)|(1<<6)|(0<<5); 
				break;	 
			case U2D_L2R://从上到下,从左到右
				regval|=(0<<7)|(0<<6)|(1<<5); 
				break;
			case U2D_R2L://从上到下,从右到左
				regval|=(0<<7)|(1<<6)|(1<<5); 
				break;
			case D2U_L2R://从下到上,从左到右
				regval|=(1<<7)|(0<<6)|(1<<5); 
				break;
			case D2U_R2L://从下到上,从右到左
				regval|=(1<<7)|(1<<6)|(1<<5); 
				break;	 
		}
		dirreg=0X36;
 		regval|=0X08;//BGR   
		LCD_WriteReg(dirreg,regval);
		if(regval&0X20)
		{
 	 		LCD_WR_REG(0x2A); 
			LCD_WR_DATA(0);LCD_WR_DATA(0);
			LCD_WR_DATA(319>>8);LCD_WR_DATA(319&0XFF);
	 		LCD_WR_REG(0x2B); 
			LCD_WR_DATA(0);LCD_WR_DATA(0);
			LCD_WR_DATA(239>>8);LCD_WR_DATA(239&0XFF);
		}else 
		{
 	 		LCD_WR_REG(0x2A); 
			LCD_WR_DATA(0);LCD_WR_DATA(0);
			LCD_WR_DATA(239>>8);LCD_WR_DATA(239&0XFF);
	 		LCD_WR_REG(0x2B); 
			LCD_WR_DATA(0);LCD_WR_DATA(0);
			LCD_WR_DATA(319>>8);LCD_WR_DATA(319&0XFF);
		}  
 	}else 
	{
		switch(dir)
		{
			case L2R_U2D://从左到右,从上到下
				regval|=(1<<5)|(1<<4)|(0<<3); 
				break;
			case L2R_D2U://从左到右,从下到上
				regval|=(0<<5)|(1<<4)|(0<<3); 
				break;
			case R2L_U2D://从右到左,从上到下
				regval|=(1<<5)|(0<<4)|(0<<3);
				break;
			case R2L_D2U://从右到左,从下到上
				regval|=(0<<5)|(0<<4)|(0<<3); 
				break;	 
			case U2D_L2R://从上到下,从左到右
				regval|=(1<<5)|(1<<4)|(1<<3); 
				break;
			case U2D_R2L://从上到下,从右到左
				regval|=(1<<5)|(0<<4)|(1<<3); 
				break;
			case D2U_L2R://从下到上,从左到右
				regval|=(0<<5)|(1<<4)|(1<<3); 
				break;
			case D2U_R2L://从下到上,从右到左
				regval|=(0<<5)|(0<<4)|(1<<3); 
				break;	 
		}
		if(DeviceCode==0x8989)//8989 IC
		{
			dirreg=0X11;
			regval|=0X6040;	//65K   
	 	}else//其他驱动IC		  
		{
			dirreg=0X03;
			regval|=1<<12;  
		}
		LCD_WriteReg(dirreg,regval);
	}
}  
//画点
//x:0~239
//y:0~319
//POINT_COLOR:此点的颜色
void LCD_DrawPoint(u16 x,u16 y)
{
// 	LCD_SetCursor(x,y);		//设置光标位置 
// 	LCD_WriteRAM_Prepare();	//开始写入GRAM
// 	LCD_WR_DATA(POINT_COLOR); 
		LCD_Address_Set(x,y,x,y);//??????
		LCD_WR_DATA16(POINT_COLOR);

}  	  
//初始化lcd

void LCD_Init(void)
{ 
 	// RCC->APB2ENR|=1<<2;//先使能外设PORTA时钟
 	// RCC->APB2ENR|=1<<4;//先使能外设PORTC时钟

//	RCC->APB2ENR|=1<<0;    //开启辅助时钟
//	JTAG_Set(SWD_ENABLE);  //开启SWD

	//运行指示灯-推挽输出 	 
	// GPIOC->CRH&=0XFF0FFFFF;
	// GPIOC->CRH|=0X00300000;  
	
	//PORTA3~7复用推挽输出 	 
// 	GPIOA->CRL&=0X00000000;
// 	GPIOA->CRL|=0X33333333;  
// 	GPIOA->ODR|=0X00FF; 	 

	// GPIOA->CRL&=0XFFFF0000;
	// GPIOA->CRL|=0X00003333;  
	// GPIOA->ODR|=0X00FF; 	 

	// LCD_RES_SET;
	// delay_ms(10); //
	// LCD_RES_CLR;
	// delay_ms(10); 
	// LCD_RES_SET;
	// delay_ms(150); 

	LCD_WR_REG(0x11);    

	vTaskDelay(120 / portTICK_PERIOD_MS);                //Delay 120ms
	
	LCD_WR_REG(0xFE);			
	LCD_WR_REG(0xEF);

	LCD_WR_REG(0xB0);		
	LCD_WR_DATA(0xC0); 
	LCD_WR_REG(0xB2);			
	LCD_WR_DATA(0x2F); 
	LCD_WR_REG(0xB3);		
	LCD_WR_DATA(0x03);
	LCD_WR_REG(0xB6);		
	LCD_WR_DATA(0x19); 
	LCD_WR_REG(0xB7);		
	LCD_WR_DATA(0x01);  

	LCD_WR_REG(0xAC);
	LCD_WR_DATA(0xCB);
	LCD_WR_REG(0xAB); 
	LCD_WR_DATA(0x0e);
		
	LCD_WR_REG(0xB4);	
	LCD_WR_DATA(0x04);

	LCD_WR_REG(0xA8);
	LCD_WR_DATA(0x19);

	LCD_WR_REG(0x3A);		
	LCD_WR_DATA(0x05); 

	LCD_WR_REG(0xb8);
	LCD_WR_DATA(0x08);

	LCD_WR_REG(0xE8);
	LCD_WR_DATA(0x24);

	LCD_WR_REG(0xE9);
	LCD_WR_DATA(0x48);

	LCD_WR_REG(0xea);	
	LCD_WR_DATA(0x22);

			
	LCD_WR_REG(0xC6);
	LCD_WR_DATA(0x30);
	LCD_WR_REG(0xC7);
	LCD_WR_DATA(0x18);

	LCD_WR_REG(0xF0);
	LCD_WR_DATA(0x1F);
	LCD_WR_DATA(0x28);
	LCD_WR_DATA(0x04);
	LCD_WR_DATA(0x3E);
	LCD_WR_DATA(0x2A);
	LCD_WR_DATA(0x2E);
	LCD_WR_DATA(0x20);
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(0x0C);
	LCD_WR_DATA(0x06);
	LCD_WR_DATA(0x00);
	LCD_WR_DATA(0x1C);
	LCD_WR_DATA(0x1F);
	LCD_WR_DATA(0x0f);

	LCD_WR_REG(0xF1); 
	LCD_WR_DATA(0X00);
	LCD_WR_DATA(0X2D);
	LCD_WR_DATA(0X2F);
	LCD_WR_DATA(0X3C);
	LCD_WR_DATA(0X6F);
	LCD_WR_DATA(0X1C);
	LCD_WR_DATA(0X0B);
	LCD_WR_DATA(0X00);
	LCD_WR_DATA(0X00);
	LCD_WR_DATA(0X00);
	LCD_WR_DATA(0X07);
	LCD_WR_DATA(0X0D);
	LCD_WR_DATA(0X11);
	LCD_WR_DATA(0X0f);

	LCD_WR_REG(0x21);
	vTaskDelay(120 / portTICK_PERIOD_MS);
	LCD_WR_REG(0x29);
	vTaskDelay(120 / portTICK_PERIOD_MS);
}  		  
  
//清屏函数
//Color:要清屏的填充色
void LCD_Clear(u16 Color)
{
	u32 index=0;      
	LCD_SetCursor(0x00,0x0000);//设置光标位置 
	LCD_WriteRAM_Prepare();     //开始写入GRAM	 	  
	for(index=0;index<(240*240);index++)
	{
		LCD_WR_DATA(Color);    
	}
}  
//在指定区域内填充指定颜色
//区域大小:(xend-xsta+1)*(yend-ysta+1)
//xsta
//color:要填充的颜色
// void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color)
// {          
// 	u16 i,j;
// 	u16 xlen=0;
// 	LCD_Scan_Dir(L2R_U2D);
// #if USE_HORIZONTAL==1
// 	xlen=ey-sy+1;	   
// 	for(i=sx;i<=ex;i++)
// 	{
// 	 	LCD_SetCursor(i,sy);      				//设置光标位置 
// 		LCD_WriteRAM_Prepare();     			//开始写入GRAM	  
// 		for(j=0;j<xlen;j++)LCD_WR_DATA(color);	//设置光标位置 	  
// 	}
// #else
// 	xlen=ex-sx+1;	   
// 	for(i=sy;i<=ey;i++)
// 	{
// 	 	LCD_SetCursor(sx,i);      				//设置光标位置 
// 		LCD_WriteRAM_Prepare();     			//开始写入GRAM	  
// 		for(j=0;j<xlen;j++)LCD_WR_DATA(color);	//设置光标位置 	    
// 	}
// #endif
// 	LCD_Scan_Dir(DFT_SCAN_DIR);
// }  

//画线
//x1,y1:起点坐标
//x2,y2:终点坐标  
void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2)
{
	u16 t; 
	int xerr=0,yerr=0,delta_x,delta_y,distance; 
	int incx,incy,uRow,uCol; 

	delta_x=x2-x1; //计算坐标增量 
	delta_y=y2-y1; 
	uRow=x1; 
	uCol=y1; 
	if(delta_x>0)incx=1; //设置单步方向 
	else if(delta_x==0)incx=0;//垂直线 
	else {incx=-1;delta_x=-delta_x;} 
	if(delta_y>0)incy=1; 
	else if(delta_y==0)incy=0;//水平线 
	else{incy=-1;delta_y=-delta_y;} 
	if( delta_x>delta_y)distance=delta_x; //选取基本增量坐标轴 
	else distance=delta_y; 
	for(t=0;t<=distance+1;t++ )//画线输出 
	{  
		LCD_DrawPoint(uRow,uCol);//画点 
		xerr+=delta_x ; 
		yerr+=delta_y ; 
		if(xerr>distance) 
		{ 
			xerr-=distance; 
			uRow+=incx; 
		} 
		if(yerr>distance) 
		{ 
			yerr-=distance; 
			uCol+=incy; 
		} 
	}  
}    
//画矩形
void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2)
{
	LCD_DrawLine(x1,y1,x2,y1);
	LCD_DrawLine(x1,y1,x1,y2);
	LCD_DrawLine(x1,y2,x2,y2);
	LCD_DrawLine(x2,y1,x2,y2);
}
//在指定位置画一个指定大小的圆
//(x,y):中心点
//r    :半径
void Draw_Circle(u16 x0,u16 y0,u8 r)
{
	int a,b;
	int di;
	a=0;b=r;	  
	di=3-(r<<1);             //判断下个点位置的标志
	while(a<=b)
	{
		LCD_DrawPoint(x0-b,y0-a);             //3           
		LCD_DrawPoint(x0+b,y0-a);             //0           
		LCD_DrawPoint(x0-a,y0+b);             //1       
		LCD_DrawPoint(x0-b,y0-a);             //7           
		LCD_DrawPoint(x0-a,y0-b);             //2             
		LCD_DrawPoint(x0+b,y0+a);             //4               
		LCD_DrawPoint(x0+a,y0-b);             //5
		LCD_DrawPoint(x0+a,y0+b);             //6 
		LCD_DrawPoint(x0-b,y0+a);             
		a++;
		//使用Bresenham算法画圆     
		if(di<0)di +=4*a+6;	  
		else
		{
			di+=10+4*(a-b);   
			b--;
		} 
		LCD_DrawPoint(x0+a,y0+b);
	}
} 
//在指定位置显示一个字符
//x:0~234
//y:0~308
//num:要显示的字符:" "--->"~"
//size:字体大小 12/16
//mode:叠加方式(1)还是非叠加方式(0)
//在指定位置显示一个字符
//x:0~234
//y:0~308
//num:要显示的字符:" "--->"~"
//size:字体大小 12/16
//mode:叠加方式(1)还是非叠加方式(0)
void LCD_ShowChar(u16 x,u16 y,u8 num,u8 size,u8 mode)
{  
#if USE_HORIZONTAL==1
#define MAX_CHAR_POSX (YPOS_END - 8)
#define MAX_CHAR_POSY (XPOS_END - 8) 
#else     
#define MAX_CHAR_POSX (XPOS_END - 8)
#define MAX_CHAR_POSY (YPOS_END - 8)
#endif 
    u8 temp;
    u8 pos,t;
	u16 x0=x;
	u16 colortemp=POINT_COLOR;      
    if(x>MAX_CHAR_POSX||y>MAX_CHAR_POSY)return;	    
	//设置窗口		   
	num=num-' ';//得到偏移后的值
	if(!mode) //非叠加方式
	{
		for(pos=0;pos<size;pos++)
		{
			if(size==12)temp=asc2_1206[num][pos];//调用1206字体
			else temp=asc2_1608[num][pos];		 //调用1608字体
			for(t=0;t<size/2;t++)
		    {                 
		        if(temp&0x01)POINT_COLOR=colortemp;
				else POINT_COLOR=BACK_COLOR;
				LCD_DrawPoint(x,y);	
				temp>>=1; 
				x++;
		    }
			x=x0;
			y++;
		}	
	}else//叠加方式
	{
		for(pos=0;pos<size;pos++)
		{
			if(size==12)temp=asc2_1206[num][pos];//调用1206字体
			else temp=asc2_1608[num][pos];		 //调用1608字体
			for(t=0;t<size/2;t++)
		    {                 
		        if(temp&0x01)LCD_DrawPoint(x+t,y+pos);//画一个点     
		        temp>>=1; 
		    }
		}
	}
	POINT_COLOR=colortemp;	    	   	 	  
}   
//m^n函数
u32 mypow(u8 m,u8 n)
{
	u32 result=1;	 
	while(n--)result*=m;    
	return result;
}			 
//显示2个数字
//x,y :起点坐标	 
//len :数字的位数
//size:字体大小
//color:颜色
//num:数值(0~4294967295);	 
void LCD_ShowNum(u16 x,u16 y,u32 num,u8 len,u8 size)
{         	
	u8 t,temp;
	u8 enshow=0;						   
	for(t=0;t<len;t++)
	{
		temp=(num/mypow(10,len-t-1))%10;
		if(enshow==0&&t<(len-1))
		{
			if(temp==0)
			{
				LCD_ShowChar(x+(size/2)*t,y,' ',size,0);
				continue;
			}else enshow=1; 
		 	 
		}
	 	LCD_ShowChar(x+(size/2)*t,y,temp+'0',size,0); 
	}
} 
//显示2个数字
//x,y:起点坐标
//size:字体大小
//mode:模式	0,填充模式;1,叠加模式
//num:数值(0~99);	 
void LCD_Show2Num(u16 x,u16 y,u16 num,u8 len,u8 size,u8 mode)
{         	
	u8 t,temp;						   
	for(t=0;t<len;t++)
	{
		temp=(num/mypow(10,len-t-1))%10;
	 	LCD_ShowChar(x+(size/2)*t,y,temp+'0',size,mode); 
	}
} 
//显示字符串
//x,y:起点坐标  
//*p:字符串起始地址
//用16字体
void LCD_ShowString(u16 x,u16 y,const u8 *p)
{         
    while(*p!='\0')
    {       
        if(x>MAX_CHAR_POSX){x=0;y+=16;}
        if(y>MAX_CHAR_POSY){y=x=0;LCD_Fill(XPOS_START,YPOS_START,XPOS_END,YPOS_END,WHITE);}
        LCD_ShowChar(x,y,*p,16,1);
        x+=8;
        p++;
    }  
}






























lcd.h

#pragma once
 
#include "stdlib.h"
#include "driver/gpio.h"

typedef unsigned long  u32;
typedef unsigned short u16;
typedef unsigned char  u8;

#define LCD_HOST    SPI2_HOST
//半双工
#define PIN_NUM_MOSI 4
#define PIN_NUM_CLK  3
#define PIN_NUM_CS   19
#define PIN_NUM_DC   5

#define LCD_RS_CLR gpio_set_level(PIN_NUM_DC, 0)
#define LCD_RS_SET gpio_set_level(PIN_NUM_DC, 1)
#define LCD_CS_SET gpio_set_level(PIN_NUM_CS, 1)

/////////////////////////////////////用户配置区///////////////////////////////////	 
//以下2个宏定义,定义屏幕的显示方式及IO速度
#define USE_HORIZONTAL  0	//定义是否使用横屏 		0,不使用.1,使用.
#define LCD_FAST_IO     0 	//定义是否使用快速IO	0,不实用.1,使用
//////////////////////////////////////////////////////////////////////////////////	 
 
//TFTLCD部分外要调用的函数		   
extern u16  POINT_COLOR;//默认红色    
extern u16  BACK_COLOR; //背景颜色.默认为白色
//定义LCD的尺寸
#if USE_HORIZONTAL==1	//使用横屏
#define LCD_W 160
#define LCD_H 128
#else
#define LCD_W 128
#define LCD_H 160
#endif

#define XPOS_START 0
#define XPOS_END 160
#define YPOS_START 0
#define YPOS_END 160


//////////////////////////////////////////////////////////////////////
//扫描方向定义
#define L2R_U2D  0 //从左到右,从上到下
#define L2R_D2U  1 //从左到右,从下到上
#define R2L_U2D  2 //从右到左,从上到下
#define R2L_D2U  3 //从右到左,从下到上

#define U2D_L2R  4 //从上到下,从左到右
#define U2D_R2L  5 //从上到下,从右到左
#define D2U_L2R  6 //从下到上,从左到右
#define D2U_R2L  7 //从下到上,从右到左

#define DFT_SCAN_DIR  L2R_U2D  //默认的扫描方向


//画笔颜色
#define WHITE         	 0x0000//0xFFFF
#define BLACK         	 0xFFFF//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 //灰色
//GUI颜色

#define DARKBLUE      	 0X01CF	//深蓝色
#define LIGHTBLUE      	 0X7D7C	//浅蓝色  
#define GRAYBLUE       	 0X5458 //灰蓝色
//以上三色为PANEL的颜色 
 
#define LIGHTGREEN     	 0X841F //浅绿色
//#define LIGHTGRAY        0XEF5B //浅灰色(PANNEL)
#define LGRAY 			 0XC618 //浅灰色(PANNEL),窗体背景色

#define LGRAYBLUE        0XA651 //浅灰蓝色(中间层颜色)
#define LBBLUE           0X2B12 //浅棕蓝色(选择条目的反色)
	    															  
extern u16 BACK_COLOR, POINT_COLOR ;  

void SPI_COM_Init(void);

void LCD_Init(void);
void LCD_DisplayOn(void);
void LCD_DisplayOff(void);
void LCD_Clear(u16 Color);	 
void LCD_SetCursor(u16 Xpos, u16 Ypos);
void LCD_Scan_Dir(u8 dir);
void LCD_DrawPoint(u16 x,u16 y);//画点
u16  LCD_ReadPoint(u16 x,u16 y); //读点
void Draw_Circle(u16 x0,u16 y0,u8 r);
void LCD_DrawLine(u16 x1, u16 y1, u16 x2, u16 y2);
void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2);		   
void LCD_Fill(u16 sx,u16 sy,u16 ex,u16 ey,u16 color);
void LCD_Fill_PIC(u16 sx,u16 sy,u16 ex,u16 ey,const u8 *data);
void LCD_ShowChar(u16 x,u16 y,u8 num,u8 size,u8 mode);//显示一个字符
void LCD_ShowNum(u16 x,u16 y,u32 num,u8 len,u8 size);  //显示一个数字
void LCD_Show2Num(u16 x,u16 y,u16 num,u8 len,u8 size,u8 mode);//显示2个数字
void LCD_ShowString(u16 x,u16 y,const u8 *p);		 //显示一个字符串,16字体

u16 LCD_RD_DATA(void);//读取LCD数据									    
void LCD_WriteReg(u8 LCD_Reg, u16 LCD_RegValue);
u16 LCD_ReadReg(u8 LCD_Reg);
void LCD_WriteRAM_Prepare(void);
void LCD_WriteRAM(u16 RGB_Code);
u16 LCD_ReadRAM(void);		   
u16 LCD_BGR2RGB(u16 c);

																			 
//9320/9325 LCD寄存器  
#define R0             0x00
#define R1             0x01
#define R2             0x02
#define R3             0x03
#define R4             0x04
#define R5             0x05
#define R6             0x06
#define R7             0x07
#define R8             0x08
#define R9             0x09
#define R10            0x0A
#define R12            0x0C
#define R13            0x0D
#define R14            0x0E
#define R15            0x0F
#define R16            0x10
#define R17            0x11
#define R18            0x12
#define R19            0x13
#define R20            0x14
#define R21            0x15
#define R22            0x16
#define R23            0x17
#define R24            0x18
#define R25            0x19
#define R26            0x1A
#define R27            0x1B
#define R28            0x1C
#define R29            0x1D
#define R30            0x1E
#define R31            0x1F
#define R32            0x20
#define R33            0x21
#define R34            0x22
#define R36            0x24
#define R37            0x25
#define R40            0x28
#define R41            0x29
#define R43            0x2B
#define R45            0x2D
#define R48            0x30
#define R49            0x31
#define R50            0x32
#define R51            0x33
#define R52            0x34
#define R53            0x35
#define R54            0x36
#define R55            0x37
#define R56            0x38
#define R57            0x39
#define R59            0x3B
#define R60            0x3C
#define R61            0x3D
#define R62            0x3E
#define R63            0x3F
#define R64            0x40
#define R65            0x41
#define R66            0x42
#define R67            0x43
#define R68            0x44
#define R69            0x45
#define R70            0x46
#define R71            0x47
#define R72            0x48
#define R73            0x49
#define R74            0x4A
#define R75            0x4B
#define R76            0x4C
#define R77            0x4D
#define R78            0x4E
#define R79            0x4F
#define R80            0x50
#define R81            0x51
#define R82            0x52
#define R83            0x53
#define R96            0x60
#define R97            0x61
#define R106           0x6A
#define R118           0x76
#define R128           0x80
#define R129           0x81
#define R130           0x82
#define R131           0x83
#define R132           0x84
#define R133           0x85
#define R134           0x86
#define R135           0x87
#define R136           0x88
#define R137           0x89
#define R139           0x8B
#define R140           0x8C
#define R141           0x8D
#define R143           0x8F
#define R144           0x90
#define R145           0x91
#define R146           0x92
#define R147           0x93
#define R148           0x94
#define R149           0x95
#define R150           0x96
#define R151           0x97
#define R152           0x98
#define R153           0x99
#define R154           0x9A
#define R157           0x9D
#define R192           0xC0
#define R193           0xC1
#define R229           0xE5							  		 

	 
	 



Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐