STM32软硬件驱动ST7735 STFT屏及问题解决指南

这是我第一次写帖子,由于毕设对于显示的需求,我选择使用2.8寸TFT来做触控显示,但是全网并没有2.8寸st735TFT的详细资料,导致我在完成毕设的时候特别艰难,在我完成的第一时间,我第一时间想到要写下我遇到的问题及解决方案。

注:本代码为本人毕设作品使用,他人用若用作文章发表还请注明出处。不想看问题分析只想看代码的可以直接跳过分析部分。

环境:keil MDK,2.8寸240*320像素ST7735+XTP2046触摸屏,STM32F103C8T6最小系统板。

问题分析

起初我在CSDN查找到商家优信电子发布的初始化官方代码,自己将软件及硬件SPI写好之后将代码烧录进STM32,发现其显示极其不稳定,时常屏闪。起初我一直以为是我程序问题我又将商家给的测试代码烧录测试,发现仍然如此,而且.官方测试代码为128*128分辨率,显示还X镜像了。

屏闪图片

ST7735 TFT屏闪视频

TFT正常显示

在多次更改代码,网上查资料没有结果之后,我详细的观看了一遍数据手册。之前我一度以为是我的屏幕质量有问题,在详细查询数据手册之后这个问题终于解决。

导致显示错误的问题有两个点,这里将结合优信电子给定的官方代码讲解。

    MCU_write_TFT_Byte(0xC0,TFT_COMMAND); 
	MCU_write_TFT_Byte(0xA2,TFT_DATA); 
	MCU_write_TFT_Byte(0x02,TFT_DATA); 
	MCU_write_TFT_Byte(0x84,TFT_DATA); 
	MCU_write_TFT_Byte(0xC1,TFT_COMMAND); 
	MCU_write_TFT_Byte(0xC5,TFT_DATA); 
————————————————
版权声明:本文为CSDN博主「优信电子」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_42250136/article/details/119909394

在上述代码0XC0,0XC1为配置电压命令,这里我不知道为什么官方没有配置为默认值。这里插入一段知识,首先要明确如果没有人为的配置命令,系统会使用默认配置,这里将默认配置更改之后就会出现屏闪问题。

商家官方原文给出的代码并没有地址配置,这也将导致问的出现。

我使用的为240*320分辨率的屏幕,行和列地址对应0xFO-1,0x140-1,代码配置为

    LCD_WR_REG(0X2A);
    LCD_WR_DATA(0x00); // 列地址起始
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x00); // 列地址结束
    LCD_WR_DATA(0xef);

    LCD_WR_REG(0X2B);
    LCD_WR_DATA(0x00); // 行地址起始
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x01); // 行地址结束
    LCD_WR_DATA(0x3f);

代码部分

其实大部分配置都维持默认即可,全部初始化代码如下

void LCD_Init(void)
{   
    SPI_Config();//spi初始化
    
    RES_LOW;
	delay_ms(500);	
	RES_HEG;
	delay_ms(500); //LCD 复位
    
    LCD_WR_REG(0x01);//软复位
	delay_ms (150);
    
    LCD_WR_REG(0x11);//退出睡眠
	delay_ms (600);	


	LCD_WR_REG(0xB4); //反色命令
	LCD_WR_DATA(0x07);//默认反色处理

	LCD_WR_REG(0x36); //MX, MY, RGB 模式
	LCD_WR_DATA(0x98); //"0x#0"BGR色 "0x#8"RGB色
                        //此处我使用为竖屏旋转180,从左向右刷新。
    LCD_WR_REG(0X2A);
    LCD_WR_DATA(0x00); // 列地址起始
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x00); // 列地址结束
    LCD_WR_DATA(0xef);

    LCD_WR_REG(0X2B);
    LCD_WR_DATA(0x00); // 行地址起始
    LCD_WR_DATA(0x00);
    LCD_WR_DATA(0x01); // 行地址结束
    LCD_WR_DATA(0x3f);

	LCD_WR_REG(0x3A); //65k mode 
	LCD_WR_DATA(0x05); 

    // 打开显示
    LCD_WR_REG(0X29);
    delay_ms (100);
}

硬件SPI配置,此配置为硬件SPI的极限速度,追求跟高速度可尝试使用DMA。

const u16 DelayTim=1000;
/*--------硬件SPI配置-------*/
void SPI_Config(void)
{//初始化结构体
	GPIO_InitTypeDef GPIO_InitStu;
    SPI_InitTypeDef SPI_InitStu;
	//开SPI对应GPIO时钟
	RCC_APB2PeriphClockCmd(SPI_GPIO_RCC, ENABLE); 
	//初始化SCL/MISO/MOSI/CS
	GPIO_InitStu.GPIO_Mode = GPIO_Mode_AF_PP ;
	GPIO_InitStu.GPIO_Pin = CLK_PIN|MOSI_PIN; 
	GPIO_InitStu.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(  SPI_GPIOX,  & GPIO_InitStu);
	 
	GPIO_InitStu.GPIO_Mode = GPIO_Mode_IN_FLOATING  ;
	GPIO_InitStu.GPIO_Pin = MISO_PIN; 
	GPIO_Init(  SPI_GPIOX,  & GPIO_InitStu);
    
    GPIO_InitStu.GPIO_Mode = GPIO_Mode_Out_PP ;
	GPIO_InitStu.GPIO_Pin = NSS_PIN; 
	GPIO_Init(  SPI_GPIOX,  & GPIO_InitStu);
    
	GPIO_InitStu.GPIO_Pin = TFT_RES_PIN|TFT_DC_PIN; 
	GPIO_Init(  SPI_GPIOX,  & GPIO_InitStu); 

    NSS_HEG; 
    
    RCC_APB2PeriphClockCmd(SPIX_RCC_CLK ,ENABLE);
	   
	SPI_InitStu.SPI_Direction = SPI_Direction_1Line_Tx;
	SPI_InitStu.SPI_Mode = SPI_Mode_Master;
	SPI_InitStu.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStu.SPI_CPOL = SPI_CPOL_High;
	SPI_InitStu.SPI_CPHA = SPI_CPHA_2Edge;
	SPI_InitStu.SPI_NSS = SPI_NSS_Soft;
	SPI_InitStu.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
	SPI_InitStu.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStu.SPI_CRCPolynomial = 7;
    
	SPI_Init(SPIX, &SPI_InitStu);
	//使能SPI1
	SPI_Cmd(SPIX, ENABLE);  
}
uint8_t SPI_WriteReadByet (uint8_t data) 
{
    u16 Tim;
	Tim=DelayTim;  
	while(SPI_I2S_GetFlagStatus  ( SPIX,  SPI_I2S_FLAG_TXE)==RESET )
	{
		if ((Tim--)==0) return 0;
	}
	SPI_I2S_SendData  ( SPIX,data);
	
    return 1;  
}

发送数据时序图

软件SPI代码,经测试软件SPI明显速度低于硬件,对于引脚被限制或移植性有要求的建议使用

/*----------软件SPI配置----------*/

void SPI_Config(void)
{
    GPIO_InitTypeDef GPIO_InitStu;
	//开SPI对应GPIO时钟
	RCC_APB2PeriphClockCmd(SPI_GPIO_RCC, ENABLE); 
	//初始化SCL/MISO/MOSI/CS	
	 
	GPIO_InitStu.GPIO_Mode = GPIO_Mode_IN_FLOATING  ;
	GPIO_InitStu.GPIO_Pin = MISO_PIN; 
	GPIO_Init(  SPI_GPIOX,  & GPIO_InitStu);
    
    GPIO_InitStu.GPIO_Mode = GPIO_Mode_Out_PP ;
	GPIO_InitStu.GPIO_Pin = CLK_PIN|MOSI_PIN|NSS_PIN; 
	GPIO_InitStu.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(  SPI_GPIOX,  & GPIO_InitStu);
    
	GPIO_InitStu.GPIO_Pin = TFT_RES_PIN|TFT_DC_PIN; 
	GPIO_Init(  SPI_GPIOX,  & GPIO_InitStu); 

    NSS_HEG; 
    CLK_HEG;    
}
//SPI读写函数
uint8_t SPI_WriteReadByet (uint8_t data)
{
    for (int i=0;i<8;i++)
    {
        CLK_LOW;
        if( 0x80&(data<<i) )DAT_HEG;
        else DAT_LOW;
        CLK_HEG;
    }
    return 0;
}

触摸IC XTP-2046的驱动代码如下:

触摸驱动代码最开始是从CSDN其他帖子复制的,但是我在直接使用中发现其读取触摸数据完全失败,我下方的代码是自己使用逻辑分析仪采用不断改进时序改进而来,我希望通过我的努力让后来的兴趣爱好者可以降低开发难度,可以不用走前人的坑,以至于慢慢失去兴趣。

void TFT_TP_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStu;	
	EXTI_InitTypeDef EXTI_InitStu; 
   
	RCC_APB2PeriphClockCmd( TFT_TP_GPIO_CLK, ENABLE);
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//打开AFIO重映射PB4,PB3,使其成为普通IO口
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//关闭JTAG功能(PB3/4),只使用SWD(PA13/14)调试
	
	GPIO_InitStu.GPIO_Pin = TFT_TP_MOSI|TFT_TP_CLK|TFT_TP_CS;
	GPIO_InitStu.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽输出 
	GPIO_InitStu.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(TFT_TP_GPIO, &GPIO_InitStu);	

	GPIO_InitStu.GPIO_Pin = TFT_TP_IRQ|TFT_TP_MISO; 
	GPIO_InitStu.GPIO_Mode = GPIO_Mode_IPD; ;  //上拉输入
	GPIO_Init(TFT_TP_GPIO, &GPIO_InitStu);
 
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO, ENABLE); 
    GPIO_EXTILineConfig( GPIO_PortSourceGPIOB, GPIO_PinSource8);

    //使用中断来及时读取触摸数据,接口为PB8
    
    EXTI_InitStu.EXTI_Line = EXTI_Line8;
    EXTI_InitStu.EXTI_LineCmd = ENABLE;
    EXTI_InitStu.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStu.EXTI_Trigger = EXTI_Trigger_Falling; 
    EXTI_Init(&EXTI_InitStu);
    
    TFT_TP_CS_HIG;		//释放片选
    TFT_TP_CLK_LOW;
    TFT_TP_MOSI_LOW;
    
    TFT_TP_ITconfig( ENABLE);
}
void TFT_TP_ITconfig(FunctionalState ED)
{
    NVIC_InitTypeDef NVIC_InitStu;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断分组
	NVIC_InitStu.NVIC_IRQChannel = EXTI9_5_IRQn;//设置EXTI8中断
	NVIC_InitStu.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级为2
	NVIC_InitStu.NVIC_IRQChannelSubPriority = 2;//响应优先级为1
	NVIC_InitStu.NVIC_IRQChannelCmd = ED;//使能中断源
    NVIC_Init(&NVIC_InitStu);
}
static void delay_us(u32 us)
{//对于stm32f1系列 72mhz大致是1us
		u16 i = 0;
		while(us--)
		{
			i = 2;
			while(i--);
		}
 }
void TFT_TP_SPI_WriteByet(u8 CMD)
{
	u8 count=0;   
	for(count=0;count<8;count++)  
	{ 	  
		if(CMD&0x80)TFT_TP_MOSI_HIG;  
		else TFT_TP_MOSI_LOW;   
		CMD<<=1;
        delay_us(1);
		TFT_TP_CLK_LOW;
		TFT_TP_CLK_HIG;		//上升沿有效 
	}		 			    
} 
u16 TFT_TP_SPI_ReadXY(u8 CMD)
{
    u8 count=0; 	  
	u16 Num=0; 
	TFT_TP_CLK_LOW;		    //先拉低时钟 	 
	TFT_TP_MOSI_LOW; 	    //拉低数据线
	TFT_TP_CS_LOW; 		    //选中触摸屏IC
	TFT_TP_SPI_WriteByet(CMD);//发送命令字
    
    TFT_TP_CLK_LOW; 
	delay_us(6);//ADS7846的转换时间最少为6us  
	
    
	for(count=0;count<16;count++)//读出16位数据,只有高12位有效 
	{ 				  
		Num<<=1;        
        
        TFT_TP_CLK_HIG;       
        TFT_TP_CLK_LOW;	//下降沿有效
        delay_us(1);
        if(  GPIO_ReadInputDataBit(TFT_TP_GPIO,TFT_TP_MISO) )Num+=1;
	}  	
	Num>>=4;   	//只有高12位有效.
	TFT_TP_CS_HIG;		//释放片选
        
	return Num; 		 			    
} 
u16  TFT_TP_Read_XorY(u8 CMD)
{
    u16 i, j;
	u16 buf[5];//读取5次
	u16 sum=0;
	u16 temp;
	for(i=0;i<5;i++)buf[i]=TFT_TP_SPI_ReadXY(CMD);		 		    
	for(i=0;i<4; i++)//排序
	{
		for(j=i+1;j<5;j++)
		{
			if(buf[i]>buf[j])//升序排列
			{
				temp=buf[i];
				buf[i]=buf[j];
				buf[j]=temp;
			}
		}
	}	  
	sum=0;
	for(i=1;i<4;i++)sum+=buf[i];//去掉一个最大值一个最小值
	temp=sum/3;
	return temp;
}
void TFT_TP_ReadXY(XYcoordType *XY) 
{	 

    XY->x = TFT_TP_Read_XorY(0XD0);
    XY->y = TFT_TP_Read_XorY(0X90); 
    
	XY->x = 240 -( (XY->x) * 240)/4096;//这里用240减是为了将数据取反,
                                        //因为我将对角屏幕倒置使用
	XY->y = ( (XY->y) * 320)/4096; 	 	//除4096因为触摸数据为12位需要对应于像素。										   
}

作者:MOV_A256

物联沃分享整理
物联沃-IOTWORD物联网 » STM32软硬件驱动ST7735 STFT屏及问题解决指南

发表评论