HC32F460开发之spi lcd屏驱动


前言

显示屏作为人机交互的一个重要窗口,在各类电子产品中被广泛地应用。在同类产品中,一个好的人机界面设计往往能更加吸引用户的目光。而显示器作为其载体,同样也是不可或缺的存在。作为一名嵌入式软件工程师,屏驱和人机界面的开发在我们的工作中是经常会遇到的,掌握此项技能也是必不可少的。本文主要基于HC32F460,简单介绍 spi屏驱的开发。


一、硬件介绍

这里我使用的屏是之前在某宝上淘来的带SPI接口通讯的TFT彩屏,下面是该屏的部分资料说明
在这里插入图片描述
在这里插入图片描述
接口定义
在这里插入图片描述


二、驱动代码实现

1.IO模拟SPI驱动

关于驱动部分,卖家也给了一些不同平台下的驱动例程,这些例程都是通过IO口模拟SPI的时序来驱动lcd屏的。移植到HC平台,很快就顺利点亮lcd屏。

代码如下:

/* Define port and pin for SDA and SCL */
#define SPI_SCK_PORT        (PortD)
#define SPI_SCK_PIN         (Pin00)

#define SPI_MOSI_PORT        (PortD)
#define SPI_MOSI_PIN         (Pin01)

/* RES Port/Pin definition */
#define  RES_PORT           (PortE)
#define  RES_PIN            (Pin09)

/* DC Port/Pin definition */
#define  DC_PORT            (PortE)
#define  DC_PIN             (Pin11)

/* BLK Port/Pin definition */
#define  BLK_PORT           (PortE)
#define  BLK_PIN            (Pin13)


//-----------------LCD端口定义---------------- 
#define LCD_SCLK_Clr() M4_PORT->PORRD |= SPI_SCK_PIN    //SCL=SCLK
#define LCD_SCLK_Set() M4_PORT->POSRD |= SPI_SCK_PIN

#define LCD_MOSI_Clr() M4_PORT->PORRD |= SPI_MOSI_PIN    //SDA=MOSI
#define LCD_MOSI_Set() M4_PORT->POSRD |= SPI_MOSI_PIN

#define LCD_RES_Clr()  M4_PORT->PORRE |= RES_PIN        //RES
#define LCD_RES_Set()  M4_PORT->POSRE |= RES_PIN

#define LCD_DC_Clr()   M4_PORT->PORRE |= DC_PIN         //DC
#define LCD_DC_Set()   M4_PORT->POSRE |= DC_PIN

#define LCD_BLK_Clr()  PORT_ResetBits(BLK_PORT, BLK_PIN)    //BLK
#define LCD_BLK_Set()  PORT_SetBits(BLK_PORT, BLK_PIN)

void LCD_GPIO_Init(void)
{
	stc_port_init_t stcPortInit;

    /* configuration structure initialization */
    MEM_ZERO_STRUCT(stcPortInit);

    stcPortInit.enPinMode = Pin_Mode_Out;

    /* SCL Port/Pin initialization */
	LCD_SCLK_Clr();
    PORT_Init(SPI_SCK_PORT, SPI_SCK_PIN, &stcPortInit);
	/* SDA Port/Pin initialization */
	LCD_MOSI_Clr();
    PORT_Init(SPI_MOSI_PORT, SPI_MOSI_PIN, &stcPortInit);

    /* RES Port/Pin initialization */
	LCD_RES_Clr();
    PORT_Init(RES_PORT, RES_PIN, &stcPortInit);

    /* DC Port/Pin initialization */
	LCD_DC_Clr();
    PORT_Init(DC_PORT, DC_PIN, &stcPortInit);

    /* BLK Port/Pin initialization */
	LCD_BLK_Clr();
    PORT_Init(BLK_PORT, BLK_PIN, &stcPortInit);
}

/******************************************************************************
      函数说明:LCD串行数据写入函数
      入口数据:dat  要写入的串行数据
      返回值:  无
******************************************************************************/
void LCD_Writ_Bus(uint8_t dat) 
{	
	uint8_t counter;
	for(counter=0; counter<8; counter++)
	{ 
		LCD_SCLK_Clr();		  
		if((dat&0x80)==0)
		{
			LCD_MOSI_Clr();
		}
		else
		{ 
			LCD_MOSI_Set();
		}
		dat=dat<<1;	
		LCD_SCLK_Set();		
	}		
	LCD_SCLK_Clr();
}

/******************************************************************************
      函数说明:LCD写入数据
      入口数据:dat 写入的数据
      返回值:  无
******************************************************************************/
void LCD_WR_DATA8(uint8_t dat)
{
	LCD_DC_Set();
	LCD_Writ_Bus(dat);
}


/******************************************************************************
      函数说明:LCD写入数据
      入口数据:dat 写入的数据
      返回值:  无
******************************************************************************/
void LCD_WR_DATA(uint16_t dat)
{
	LCD_WR_DATA8(dat>>8);
	LCD_WR_DATA8(dat);
}


/******************************************************************************
      函数说明:LCD写入命令
      入口数据:dat 写入的命令
      返回值:  无
******************************************************************************/
void LCD_WR_REG(uint8_t dat)
{
	LCD_DC_Clr();
	LCD_Writ_Bus(dat);
}


/******************************************************************************
      函数说明:设置起始和结束地址
      入口数据:x1,x2 设置列的起始和结束地址
                y1,y2 设置行的起始和结束地址
      返回值:  无
******************************************************************************/
void LCD_Address_Set(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2)
{
	LCD_WR_REG(0x2a);     //Column address set
	LCD_WR_DATA(x1);    //start column
	LCD_WR_DATA(x2);    //end column

	LCD_WR_REG(0x2b);     //Row address set
	LCD_WR_DATA(y1);    //start row
	LCD_WR_DATA(y2);    //end row
	LCD_WR_REG(0x2C);     //Memory write
}

void LCD_Init(void)
{
	LCD_GPIO_Init();//初始化GPIO

	LCD_RES_Clr();
	Ddl_Delay1ms(120);
	LCD_RES_Set();
	Ddl_Delay1ms(120);
	LCD_SCLK_Set();			//特别注意!!

    LCD_WR_REG(0x11); 			//Sleep Out
	Ddl_Delay1ms(120);               //DELAY120ms 
	//-----------------------ST7789V Frame rate setting-----------------//
	//************************************************
	LCD_WR_REG(0x3A);        //65k mode
	LCD_WR_DATA8(0x05);

	LCD_WR_REG(0xC5); 		//VCOM
	LCD_WR_DATA8(0x1A);
	LCD_WR_REG(0x36);                 // 屏幕显示方向设置
	LCD_WR_DATA8(0x00);
	//-------------ST7789V Frame rate setting-----------//
	LCD_WR_REG(0xb2);		//Porch Setting
	LCD_WR_DATA8(0x05);
	LCD_WR_DATA8(0x05);
	LCD_WR_DATA8(0x00);
	LCD_WR_DATA8(0x33);
	LCD_WR_DATA8(0x33);

	LCD_WR_REG(0xb7);			//Gate Control
	LCD_WR_DATA8(0x05);			//12.2v   -10.43v
	//--------------ST7789V Power setting---------------//
	LCD_WR_REG(0xBB);//VCOM
	LCD_WR_DATA8(0x3F);

	LCD_WR_REG(0xC0); //Power control
	LCD_WR_DATA8(0x2c);

	LCD_WR_REG(0xC2);		//VDV and VRH Command Enable
	LCD_WR_DATA8(0x01);

	LCD_WR_REG(0xC3);			//VRH Set
	LCD_WR_DATA8(0x0F);		//4.3+( vcom+vcom offset+vdv)

	LCD_WR_REG(0xC4);			//VDV Set
	LCD_WR_DATA8(0x20);				//0v

	LCD_WR_REG(0xC6);				//Frame Rate Control in Normal Mode
	LCD_WR_DATA8(0X01);			//111Hz

	LCD_WR_REG(0xd0);				//Power Control 1
	LCD_WR_DATA8(0xa4);
	LCD_WR_DATA8(0xa1);

	LCD_WR_REG(0xE8);				//Power Control 1
	LCD_WR_DATA8(0x03);

	LCD_WR_REG(0xE9);				//Equalize time control
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x08);
	//---------------ST7789V gamma setting-------------//
	LCD_WR_REG(0xE0); //Set Gamma
	LCD_WR_DATA8(0xD0);
	LCD_WR_DATA8(0x05);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x08);
	LCD_WR_DATA8(0x14);
	LCD_WR_DATA8(0x28);
	LCD_WR_DATA8(0x33);
	LCD_WR_DATA8(0x3F);
	LCD_WR_DATA8(0x07);
	LCD_WR_DATA8(0x13);
	LCD_WR_DATA8(0x14);
	LCD_WR_DATA8(0x28);
	LCD_WR_DATA8(0x30);
		
	LCD_WR_REG(0XE1); //Set Gamma
	LCD_WR_DATA8(0xD0);
	LCD_WR_DATA8(0x05);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x08);
	LCD_WR_DATA8(0x03);
	LCD_WR_DATA8(0x24);
	LCD_WR_DATA8(0x32);
	LCD_WR_DATA8(0x32);
	LCD_WR_DATA8(0x3B);
	LCD_WR_DATA8(0x14);
	LCD_WR_DATA8(0x13);
	LCD_WR_DATA8(0x28);
	LCD_WR_DATA8(0x2F);

	LCD_WR_REG(0x21); 		//反显
	
	LCD_WR_REG(0x29);         //开启显示 
} 


/******************************************************************************
      函数说明:在指定区域填充颜色
      入口数据:xsta,ysta   起始坐标
                xend,yend   终止坐标
								color       要填充的颜色
      返回值:  无
******************************************************************************/
void LCD_Fill(uint16_t xsta,uint16_t ysta,uint16_t xend,uint16_t yend,uint16_t color)
{          
	uint16_t 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_DATA(color);
		}
	} 					  	    
}

编写代码简单地刷下屏,效果如下:
在这里插入图片描述
虽说能成功点亮,速度也勉强能接受,但是还是很明显能看到刷屏时“拉窗帘”的现象(CSDN博客好像没办法插入小视频,只能截图了–_–|||)。

2.硬件SPI驱动

IO模拟SPI虽然能驱动LCD屏,但是刷屏速度就很明显会偏慢点,若是后续开发想利用屏幕播放一些动图(如GIF等),显示效果看起来就会很差。HC32F460本身带有4路的SPI外设,因此我们可以直接使用MCU本身的硬件SPI来驱动LCD屏,刷屏显示效果则会好很多。
代码如下:

/* RES Port/Pin definition */
#define  RES_PORT           (PortE)
#define  RES_PIN            (Pin09)

/* DC Port/Pin definition */
#define  DC_PORT            (PortE)
#define  DC_PIN             (Pin11)

/* BLK Port/Pin definition */
#define  BLK_PORT           (PortE)
#define  BLK_PIN            (Pin13)

#define LCD_RES_Clr()  M4_PORT->PORRE |= RES_PIN        //RES
#define LCD_RES_Set()  M4_PORT->POSRE |= RES_PIN

#define LCD_DC_Clr()   M4_PORT->PORRE |= DC_PIN         //DC
#define LCD_DC_Set()   M4_PORT->POSRE |= DC_PIN

#define LCD_BLK_Clr()  PORT_ResetBits(BLK_PORT, BLK_PIN)    //BLK
#define LCD_BLK_Set()  PORT_SetBits(BLK_PORT, BLK_PIN)

/* SPI_SCK Port/Pin definition */
#define SPI_SCK_PORT                    (PortD)
#define SPI_SCK_PIN                     (Pin00)
#define SPI_SCK_FUNC                    (Func_Spi1_Sck)

/* SPI_MOSI Port/Pin definition */
#define SPI_MOSI_PORT                   (PortD)
#define SPI_MOSI_PIN                    (Pin01)
#define SPI_MOSI_FUNC                   (Func_Spi1_Mosi)

/* SPI unit and clock definition */
#define SPI_UNIT                        (M4_SPI1)
#define SPI_UNIT_CLOCK                  (PWC_FCG1_PERIPH_SPI1)
/* Choose SPI master or slave mode */
#define SPI_MASTER_MODE

void LCD_SPI_Write(uint8_t Data)
{
    /* Send data */
    SPI_SendData8(SPI_UNIT, Data);

    /* Wait tx buffer empty */
   while(Reset == SPI_GetFlag(SPI_UNIT, SpiFlagSendBufferEmpty));
}

void LCD_SPI_Init(void)
{
    stc_spi_init_t stcSpiInit;

    /* configuration structure initialization */
    MEM_ZERO_STRUCT(stcSpiInit);

    /* Configuration peripheral clock */
    PWC_Fcg1PeriphClockCmd(SPI_UNIT_CLOCK, Enable);

    /* Configuration SPI pin */
    PORT_SetFunc(SPI_SCK_PORT, SPI_SCK_PIN, SPI_SCK_FUNC, Disable);
    PORT_SetFunc(SPI_MOSI_PORT, SPI_MOSI_PIN, SPI_MOSI_FUNC, Disable);

    /* Configuration SPI structure */
    stcSpiInit.enClkDiv                 = SpiClkDiv2;
    stcSpiInit.enFrameNumber            = SpiFrameNumber1;
    stcSpiInit.enDataLength             = SpiDataLengthBit8;
    stcSpiInit.enFirstBitPosition       = SpiFirstBitPositionMSB;
    stcSpiInit.enSckPolarity            = SpiSckIdleLevelHigh;
    stcSpiInit.enSckPhase               = SpiSckOddChangeEvenSample;
    stcSpiInit.enReadBufferObject       = SpiReadReceiverBuffer;
    stcSpiInit.enWorkMode               = SpiWorkMode3Line;
    stcSpiInit.enTransMode              = SpiTransFullDuplex;
    stcSpiInit.enCommAutoSuspendEn      = Disable;
    stcSpiInit.enModeFaultErrorDetectEn = Disable;
    stcSpiInit.enParitySelfDetectEn     = Disable;
    stcSpiInit.enParityEn               = Disable;
    stcSpiInit.enParity                 = SpiParityEven;

    /* SPI master mode */
    stcSpiInit.enMasterSlaveMode                     = SpiModeMaster;
    stcSpiInit.stcDelayConfig.enSsSetupDelayOption   = SpiSsSetupDelayCustomValue;
    stcSpiInit.stcDelayConfig.enSsSetupDelayTime     = SpiSsSetupDelaySck1;
    stcSpiInit.stcDelayConfig.enSsHoldDelayOption    = SpiSsHoldDelayCustomValue;
    stcSpiInit.stcDelayConfig.enSsHoldDelayTime      = SpiSsHoldDelaySck1;
    stcSpiInit.stcDelayConfig.enSsIntervalTimeOption = SpiSsIntervalCustomValue;
    stcSpiInit.stcDelayConfig.enSsIntervalTime       = SpiSsIntervalSck6PlusPck2;
    stcSpiInit.stcSsConfig.enSsValidBit              = SpiSsValidChannel0;
    stcSpiInit.stcSsConfig.enSs0Polarity             = SpiSsLowValid;
    SPI_Init(SPI_UNIT, &stcSpiInit);

    SPI_Cmd(SPI_UNIT, Enable);	
}

void LCD_GPIO_Init(void)
{
	stc_port_init_t stcPortInit;

    /* configuration structure initialization */
    MEM_ZERO_STRUCT(stcPortInit);

    stcPortInit.enPinMode = Pin_Mode_Out;

    /* RES Port/Pin initialization */
	LCD_RES_Clr();
    PORT_Init(RES_PORT, RES_PIN, &stcPortInit);

    /* DC Port/Pin initialization */
	LCD_DC_Clr();
    PORT_Init(DC_PORT, DC_PIN, &stcPortInit);

    /* BLK Port/Pin initialization */
	LCD_BLK_Clr();
    PORT_Init(BLK_PORT, BLK_PIN, &stcPortInit);
}

/******************************************************************************
      函数说明:LCD串行数据写入函数
      入口数据:dat  要写入的串行数据
      返回值:  无
******************************************************************************/
void LCD_Writ_Bus(uint8_t dat) 
{	
	LCD_SPI_Write(dat);
}

void LCD_Init(void)
{
	LCD_GPIO_Init();//初始化GPIO

	LCD_RES_Clr();
	Ddl_Delay1ms(120);
	LCD_RES_Set();
	Ddl_Delay1ms(120);
	
	LCD_SPI_Init();
	
    LCD_WR_REG(0x11); 			//Sleep Out
	Ddl_Delay1ms(120);               //DELAY120ms 
	//-----------------------ST7789V Frame rate setting-----------------//
	//************************************************
	LCD_WR_REG(0x3A);        //65k mode
	LCD_WR_DATA8(0x05);

	LCD_WR_REG(0xC5); 		//VCOM
	LCD_WR_DATA8(0x1A);
	LCD_WR_REG(0x36);                 // 屏幕显示方向设置
	LCD_WR_DATA8(0x00);
	//-------------ST7789V Frame rate setting-----------//
	LCD_WR_REG(0xb2);		//Porch Setting
	LCD_WR_DATA8(0x05);
	LCD_WR_DATA8(0x05);
	LCD_WR_DATA8(0x00);
	LCD_WR_DATA8(0x33);
	LCD_WR_DATA8(0x33);

	LCD_WR_REG(0xb7);			//Gate Control
	LCD_WR_DATA8(0x05);			//12.2v   -10.43v
	//--------------ST7789V Power setting---------------//
	LCD_WR_REG(0xBB);//VCOM
	LCD_WR_DATA8(0x3F);

	LCD_WR_REG(0xC0); //Power control
	LCD_WR_DATA8(0x2c);

	LCD_WR_REG(0xC2);		//VDV and VRH Command Enable
	LCD_WR_DATA8(0x01);

	LCD_WR_REG(0xC3);			//VRH Set
	LCD_WR_DATA8(0x0F);		//4.3+( vcom+vcom offset+vdv)

	LCD_WR_REG(0xC4);			//VDV Set
	LCD_WR_DATA8(0x20);				//0v

	LCD_WR_REG(0xC6);				//Frame Rate Control in Normal Mode
	LCD_WR_DATA8(0X01);			//111Hz

	LCD_WR_REG(0xd0);				//Power Control 1
	LCD_WR_DATA8(0xa4);
	LCD_WR_DATA8(0xa1);

	LCD_WR_REG(0xE8);				//Power Control 1
	LCD_WR_DATA8(0x03);

	LCD_WR_REG(0xE9);				//Equalize time control
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x08);
	//---------------ST7789V gamma setting-------------//
	LCD_WR_REG(0xE0); //Set Gamma
	LCD_WR_DATA8(0xD0);
	LCD_WR_DATA8(0x05);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x08);
	LCD_WR_DATA8(0x14);
	LCD_WR_DATA8(0x28);
	LCD_WR_DATA8(0x33);
	LCD_WR_DATA8(0x3F);
	LCD_WR_DATA8(0x07);
	LCD_WR_DATA8(0x13);
	LCD_WR_DATA8(0x14);
	LCD_WR_DATA8(0x28);
	LCD_WR_DATA8(0x30);
		
	LCD_WR_REG(0XE1); //Set Gamma
	LCD_WR_DATA8(0xD0);
	LCD_WR_DATA8(0x05);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x08);
	LCD_WR_DATA8(0x03);
	LCD_WR_DATA8(0x24);
	LCD_WR_DATA8(0x32);
	LCD_WR_DATA8(0x32);
	LCD_WR_DATA8(0x3B);
	LCD_WR_DATA8(0x14);
	LCD_WR_DATA8(0x13);
	LCD_WR_DATA8(0x28);
	LCD_WR_DATA8(0x2F);

	LCD_WR_REG(0x21); 		//反显
	
	LCD_WR_REG(0x29);         //开启显示 
} 

编译重上电,可以看到刷屏效果明显改善了很多。前后波形对比图如下,可以看到,发送一个字节所耗费的时间快了10余倍,基本符合我们的预期。(好像SPI+DMA刷屏时间会更快,后续有时间可以再优化下)
在这里插入图片描述
在这里插入图片描述


总结

关于SPI屏驱动这部分的内容暂时就介绍到这里,后续有时间再详细介绍下lvgl库的移植。最后附上源代码链接:hc32f460petb_template.zip

版权声明:本文为CSDN博主「Top0_0lll」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_39597546/article/details/122478110


前言

显示屏作为人机交互的一个重要窗口,在各类电子产品中被广泛地应用。在同类产品中,一个好的人机界面设计往往能更加吸引用户的目光。而显示器作为其载体,同样也是不可或缺的存在。作为一名嵌入式软件工程师,屏驱和人机界面的开发在我们的工作中是经常会遇到的,掌握此项技能也是必不可少的。本文主要基于HC32F460,简单介绍 spi屏驱的开发。


一、硬件介绍

这里我使用的屏是之前在某宝上淘来的带SPI接口通讯的TFT彩屏,下面是该屏的部分资料说明
在这里插入图片描述
在这里插入图片描述
接口定义
在这里插入图片描述


二、驱动代码实现

1.IO模拟SPI驱动

关于驱动部分,卖家也给了一些不同平台下的驱动例程,这些例程都是通过IO口模拟SPI的时序来驱动lcd屏的。移植到HC平台,很快就顺利点亮lcd屏。

代码如下:

/* Define port and pin for SDA and SCL */
#define SPI_SCK_PORT        (PortD)
#define SPI_SCK_PIN         (Pin00)

#define SPI_MOSI_PORT        (PortD)
#define SPI_MOSI_PIN         (Pin01)

/* RES Port/Pin definition */
#define  RES_PORT           (PortE)
#define  RES_PIN            (Pin09)

/* DC Port/Pin definition */
#define  DC_PORT            (PortE)
#define  DC_PIN             (Pin11)

/* BLK Port/Pin definition */
#define  BLK_PORT           (PortE)
#define  BLK_PIN            (Pin13)


//-----------------LCD端口定义---------------- 
#define LCD_SCLK_Clr() M4_PORT->PORRD |= SPI_SCK_PIN    //SCL=SCLK
#define LCD_SCLK_Set() M4_PORT->POSRD |= SPI_SCK_PIN

#define LCD_MOSI_Clr() M4_PORT->PORRD |= SPI_MOSI_PIN    //SDA=MOSI
#define LCD_MOSI_Set() M4_PORT->POSRD |= SPI_MOSI_PIN

#define LCD_RES_Clr()  M4_PORT->PORRE |= RES_PIN        //RES
#define LCD_RES_Set()  M4_PORT->POSRE |= RES_PIN

#define LCD_DC_Clr()   M4_PORT->PORRE |= DC_PIN         //DC
#define LCD_DC_Set()   M4_PORT->POSRE |= DC_PIN

#define LCD_BLK_Clr()  PORT_ResetBits(BLK_PORT, BLK_PIN)    //BLK
#define LCD_BLK_Set()  PORT_SetBits(BLK_PORT, BLK_PIN)

void LCD_GPIO_Init(void)
{
	stc_port_init_t stcPortInit;

    /* configuration structure initialization */
    MEM_ZERO_STRUCT(stcPortInit);

    stcPortInit.enPinMode = Pin_Mode_Out;

    /* SCL Port/Pin initialization */
	LCD_SCLK_Clr();
    PORT_Init(SPI_SCK_PORT, SPI_SCK_PIN, &stcPortInit);
	/* SDA Port/Pin initialization */
	LCD_MOSI_Clr();
    PORT_Init(SPI_MOSI_PORT, SPI_MOSI_PIN, &stcPortInit);

    /* RES Port/Pin initialization */
	LCD_RES_Clr();
    PORT_Init(RES_PORT, RES_PIN, &stcPortInit);

    /* DC Port/Pin initialization */
	LCD_DC_Clr();
    PORT_Init(DC_PORT, DC_PIN, &stcPortInit);

    /* BLK Port/Pin initialization */
	LCD_BLK_Clr();
    PORT_Init(BLK_PORT, BLK_PIN, &stcPortInit);
}

/******************************************************************************
      函数说明:LCD串行数据写入函数
      入口数据:dat  要写入的串行数据
      返回值:  无
******************************************************************************/
void LCD_Writ_Bus(uint8_t dat) 
{	
	uint8_t counter;
	for(counter=0; counter<8; counter++)
	{ 
		LCD_SCLK_Clr();		  
		if((dat&0x80)==0)
		{
			LCD_MOSI_Clr();
		}
		else
		{ 
			LCD_MOSI_Set();
		}
		dat=dat<<1;	
		LCD_SCLK_Set();		
	}		
	LCD_SCLK_Clr();
}

/******************************************************************************
      函数说明:LCD写入数据
      入口数据:dat 写入的数据
      返回值:  无
******************************************************************************/
void LCD_WR_DATA8(uint8_t dat)
{
	LCD_DC_Set();
	LCD_Writ_Bus(dat);
}


/******************************************************************************
      函数说明:LCD写入数据
      入口数据:dat 写入的数据
      返回值:  无
******************************************************************************/
void LCD_WR_DATA(uint16_t dat)
{
	LCD_WR_DATA8(dat>>8);
	LCD_WR_DATA8(dat);
}


/******************************************************************************
      函数说明:LCD写入命令
      入口数据:dat 写入的命令
      返回值:  无
******************************************************************************/
void LCD_WR_REG(uint8_t dat)
{
	LCD_DC_Clr();
	LCD_Writ_Bus(dat);
}


/******************************************************************************
      函数说明:设置起始和结束地址
      入口数据:x1,x2 设置列的起始和结束地址
                y1,y2 设置行的起始和结束地址
      返回值:  无
******************************************************************************/
void LCD_Address_Set(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2)
{
	LCD_WR_REG(0x2a);     //Column address set
	LCD_WR_DATA(x1);    //start column
	LCD_WR_DATA(x2);    //end column

	LCD_WR_REG(0x2b);     //Row address set
	LCD_WR_DATA(y1);    //start row
	LCD_WR_DATA(y2);    //end row
	LCD_WR_REG(0x2C);     //Memory write
}

void LCD_Init(void)
{
	LCD_GPIO_Init();//初始化GPIO

	LCD_RES_Clr();
	Ddl_Delay1ms(120);
	LCD_RES_Set();
	Ddl_Delay1ms(120);
	LCD_SCLK_Set();			//特别注意!!

    LCD_WR_REG(0x11); 			//Sleep Out
	Ddl_Delay1ms(120);               //DELAY120ms 
	//-----------------------ST7789V Frame rate setting-----------------//
	//************************************************
	LCD_WR_REG(0x3A);        //65k mode
	LCD_WR_DATA8(0x05);

	LCD_WR_REG(0xC5); 		//VCOM
	LCD_WR_DATA8(0x1A);
	LCD_WR_REG(0x36);                 // 屏幕显示方向设置
	LCD_WR_DATA8(0x00);
	//-------------ST7789V Frame rate setting-----------//
	LCD_WR_REG(0xb2);		//Porch Setting
	LCD_WR_DATA8(0x05);
	LCD_WR_DATA8(0x05);
	LCD_WR_DATA8(0x00);
	LCD_WR_DATA8(0x33);
	LCD_WR_DATA8(0x33);

	LCD_WR_REG(0xb7);			//Gate Control
	LCD_WR_DATA8(0x05);			//12.2v   -10.43v
	//--------------ST7789V Power setting---------------//
	LCD_WR_REG(0xBB);//VCOM
	LCD_WR_DATA8(0x3F);

	LCD_WR_REG(0xC0); //Power control
	LCD_WR_DATA8(0x2c);

	LCD_WR_REG(0xC2);		//VDV and VRH Command Enable
	LCD_WR_DATA8(0x01);

	LCD_WR_REG(0xC3);			//VRH Set
	LCD_WR_DATA8(0x0F);		//4.3+( vcom+vcom offset+vdv)

	LCD_WR_REG(0xC4);			//VDV Set
	LCD_WR_DATA8(0x20);				//0v

	LCD_WR_REG(0xC6);				//Frame Rate Control in Normal Mode
	LCD_WR_DATA8(0X01);			//111Hz

	LCD_WR_REG(0xd0);				//Power Control 1
	LCD_WR_DATA8(0xa4);
	LCD_WR_DATA8(0xa1);

	LCD_WR_REG(0xE8);				//Power Control 1
	LCD_WR_DATA8(0x03);

	LCD_WR_REG(0xE9);				//Equalize time control
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x08);
	//---------------ST7789V gamma setting-------------//
	LCD_WR_REG(0xE0); //Set Gamma
	LCD_WR_DATA8(0xD0);
	LCD_WR_DATA8(0x05);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x08);
	LCD_WR_DATA8(0x14);
	LCD_WR_DATA8(0x28);
	LCD_WR_DATA8(0x33);
	LCD_WR_DATA8(0x3F);
	LCD_WR_DATA8(0x07);
	LCD_WR_DATA8(0x13);
	LCD_WR_DATA8(0x14);
	LCD_WR_DATA8(0x28);
	LCD_WR_DATA8(0x30);
		
	LCD_WR_REG(0XE1); //Set Gamma
	LCD_WR_DATA8(0xD0);
	LCD_WR_DATA8(0x05);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x08);
	LCD_WR_DATA8(0x03);
	LCD_WR_DATA8(0x24);
	LCD_WR_DATA8(0x32);
	LCD_WR_DATA8(0x32);
	LCD_WR_DATA8(0x3B);
	LCD_WR_DATA8(0x14);
	LCD_WR_DATA8(0x13);
	LCD_WR_DATA8(0x28);
	LCD_WR_DATA8(0x2F);

	LCD_WR_REG(0x21); 		//反显
	
	LCD_WR_REG(0x29);         //开启显示 
} 


/******************************************************************************
      函数说明:在指定区域填充颜色
      入口数据:xsta,ysta   起始坐标
                xend,yend   终止坐标
								color       要填充的颜色
      返回值:  无
******************************************************************************/
void LCD_Fill(uint16_t xsta,uint16_t ysta,uint16_t xend,uint16_t yend,uint16_t color)
{          
	uint16_t 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_DATA(color);
		}
	} 					  	    
}

编写代码简单地刷下屏,效果如下:
在这里插入图片描述
虽说能成功点亮,速度也勉强能接受,但是还是很明显能看到刷屏时“拉窗帘”的现象(CSDN博客好像没办法插入小视频,只能截图了–_–|||)。

2.硬件SPI驱动

IO模拟SPI虽然能驱动LCD屏,但是刷屏速度就很明显会偏慢点,若是后续开发想利用屏幕播放一些动图(如GIF等),显示效果看起来就会很差。HC32F460本身带有4路的SPI外设,因此我们可以直接使用MCU本身的硬件SPI来驱动LCD屏,刷屏显示效果则会好很多。
代码如下:

/* RES Port/Pin definition */
#define  RES_PORT           (PortE)
#define  RES_PIN            (Pin09)

/* DC Port/Pin definition */
#define  DC_PORT            (PortE)
#define  DC_PIN             (Pin11)

/* BLK Port/Pin definition */
#define  BLK_PORT           (PortE)
#define  BLK_PIN            (Pin13)

#define LCD_RES_Clr()  M4_PORT->PORRE |= RES_PIN        //RES
#define LCD_RES_Set()  M4_PORT->POSRE |= RES_PIN

#define LCD_DC_Clr()   M4_PORT->PORRE |= DC_PIN         //DC
#define LCD_DC_Set()   M4_PORT->POSRE |= DC_PIN

#define LCD_BLK_Clr()  PORT_ResetBits(BLK_PORT, BLK_PIN)    //BLK
#define LCD_BLK_Set()  PORT_SetBits(BLK_PORT, BLK_PIN)

/* SPI_SCK Port/Pin definition */
#define SPI_SCK_PORT                    (PortD)
#define SPI_SCK_PIN                     (Pin00)
#define SPI_SCK_FUNC                    (Func_Spi1_Sck)

/* SPI_MOSI Port/Pin definition */
#define SPI_MOSI_PORT                   (PortD)
#define SPI_MOSI_PIN                    (Pin01)
#define SPI_MOSI_FUNC                   (Func_Spi1_Mosi)

/* SPI unit and clock definition */
#define SPI_UNIT                        (M4_SPI1)
#define SPI_UNIT_CLOCK                  (PWC_FCG1_PERIPH_SPI1)
/* Choose SPI master or slave mode */
#define SPI_MASTER_MODE

void LCD_SPI_Write(uint8_t Data)
{
    /* Send data */
    SPI_SendData8(SPI_UNIT, Data);

    /* Wait tx buffer empty */
   while(Reset == SPI_GetFlag(SPI_UNIT, SpiFlagSendBufferEmpty));
}

void LCD_SPI_Init(void)
{
    stc_spi_init_t stcSpiInit;

    /* configuration structure initialization */
    MEM_ZERO_STRUCT(stcSpiInit);

    /* Configuration peripheral clock */
    PWC_Fcg1PeriphClockCmd(SPI_UNIT_CLOCK, Enable);

    /* Configuration SPI pin */
    PORT_SetFunc(SPI_SCK_PORT, SPI_SCK_PIN, SPI_SCK_FUNC, Disable);
    PORT_SetFunc(SPI_MOSI_PORT, SPI_MOSI_PIN, SPI_MOSI_FUNC, Disable);

    /* Configuration SPI structure */
    stcSpiInit.enClkDiv                 = SpiClkDiv2;
    stcSpiInit.enFrameNumber            = SpiFrameNumber1;
    stcSpiInit.enDataLength             = SpiDataLengthBit8;
    stcSpiInit.enFirstBitPosition       = SpiFirstBitPositionMSB;
    stcSpiInit.enSckPolarity            = SpiSckIdleLevelHigh;
    stcSpiInit.enSckPhase               = SpiSckOddChangeEvenSample;
    stcSpiInit.enReadBufferObject       = SpiReadReceiverBuffer;
    stcSpiInit.enWorkMode               = SpiWorkMode3Line;
    stcSpiInit.enTransMode              = SpiTransFullDuplex;
    stcSpiInit.enCommAutoSuspendEn      = Disable;
    stcSpiInit.enModeFaultErrorDetectEn = Disable;
    stcSpiInit.enParitySelfDetectEn     = Disable;
    stcSpiInit.enParityEn               = Disable;
    stcSpiInit.enParity                 = SpiParityEven;

    /* SPI master mode */
    stcSpiInit.enMasterSlaveMode                     = SpiModeMaster;
    stcSpiInit.stcDelayConfig.enSsSetupDelayOption   = SpiSsSetupDelayCustomValue;
    stcSpiInit.stcDelayConfig.enSsSetupDelayTime     = SpiSsSetupDelaySck1;
    stcSpiInit.stcDelayConfig.enSsHoldDelayOption    = SpiSsHoldDelayCustomValue;
    stcSpiInit.stcDelayConfig.enSsHoldDelayTime      = SpiSsHoldDelaySck1;
    stcSpiInit.stcDelayConfig.enSsIntervalTimeOption = SpiSsIntervalCustomValue;
    stcSpiInit.stcDelayConfig.enSsIntervalTime       = SpiSsIntervalSck6PlusPck2;
    stcSpiInit.stcSsConfig.enSsValidBit              = SpiSsValidChannel0;
    stcSpiInit.stcSsConfig.enSs0Polarity             = SpiSsLowValid;
    SPI_Init(SPI_UNIT, &stcSpiInit);

    SPI_Cmd(SPI_UNIT, Enable);	
}

void LCD_GPIO_Init(void)
{
	stc_port_init_t stcPortInit;

    /* configuration structure initialization */
    MEM_ZERO_STRUCT(stcPortInit);

    stcPortInit.enPinMode = Pin_Mode_Out;

    /* RES Port/Pin initialization */
	LCD_RES_Clr();
    PORT_Init(RES_PORT, RES_PIN, &stcPortInit);

    /* DC Port/Pin initialization */
	LCD_DC_Clr();
    PORT_Init(DC_PORT, DC_PIN, &stcPortInit);

    /* BLK Port/Pin initialization */
	LCD_BLK_Clr();
    PORT_Init(BLK_PORT, BLK_PIN, &stcPortInit);
}

/******************************************************************************
      函数说明:LCD串行数据写入函数
      入口数据:dat  要写入的串行数据
      返回值:  无
******************************************************************************/
void LCD_Writ_Bus(uint8_t dat) 
{	
	LCD_SPI_Write(dat);
}

void LCD_Init(void)
{
	LCD_GPIO_Init();//初始化GPIO

	LCD_RES_Clr();
	Ddl_Delay1ms(120);
	LCD_RES_Set();
	Ddl_Delay1ms(120);
	
	LCD_SPI_Init();
	
    LCD_WR_REG(0x11); 			//Sleep Out
	Ddl_Delay1ms(120);               //DELAY120ms 
	//-----------------------ST7789V Frame rate setting-----------------//
	//************************************************
	LCD_WR_REG(0x3A);        //65k mode
	LCD_WR_DATA8(0x05);

	LCD_WR_REG(0xC5); 		//VCOM
	LCD_WR_DATA8(0x1A);
	LCD_WR_REG(0x36);                 // 屏幕显示方向设置
	LCD_WR_DATA8(0x00);
	//-------------ST7789V Frame rate setting-----------//
	LCD_WR_REG(0xb2);		//Porch Setting
	LCD_WR_DATA8(0x05);
	LCD_WR_DATA8(0x05);
	LCD_WR_DATA8(0x00);
	LCD_WR_DATA8(0x33);
	LCD_WR_DATA8(0x33);

	LCD_WR_REG(0xb7);			//Gate Control
	LCD_WR_DATA8(0x05);			//12.2v   -10.43v
	//--------------ST7789V Power setting---------------//
	LCD_WR_REG(0xBB);//VCOM
	LCD_WR_DATA8(0x3F);

	LCD_WR_REG(0xC0); //Power control
	LCD_WR_DATA8(0x2c);

	LCD_WR_REG(0xC2);		//VDV and VRH Command Enable
	LCD_WR_DATA8(0x01);

	LCD_WR_REG(0xC3);			//VRH Set
	LCD_WR_DATA8(0x0F);		//4.3+( vcom+vcom offset+vdv)

	LCD_WR_REG(0xC4);			//VDV Set
	LCD_WR_DATA8(0x20);				//0v

	LCD_WR_REG(0xC6);				//Frame Rate Control in Normal Mode
	LCD_WR_DATA8(0X01);			//111Hz

	LCD_WR_REG(0xd0);				//Power Control 1
	LCD_WR_DATA8(0xa4);
	LCD_WR_DATA8(0xa1);

	LCD_WR_REG(0xE8);				//Power Control 1
	LCD_WR_DATA8(0x03);

	LCD_WR_REG(0xE9);				//Equalize time control
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x08);
	//---------------ST7789V gamma setting-------------//
	LCD_WR_REG(0xE0); //Set Gamma
	LCD_WR_DATA8(0xD0);
	LCD_WR_DATA8(0x05);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x08);
	LCD_WR_DATA8(0x14);
	LCD_WR_DATA8(0x28);
	LCD_WR_DATA8(0x33);
	LCD_WR_DATA8(0x3F);
	LCD_WR_DATA8(0x07);
	LCD_WR_DATA8(0x13);
	LCD_WR_DATA8(0x14);
	LCD_WR_DATA8(0x28);
	LCD_WR_DATA8(0x30);
		
	LCD_WR_REG(0XE1); //Set Gamma
	LCD_WR_DATA8(0xD0);
	LCD_WR_DATA8(0x05);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x09);
	LCD_WR_DATA8(0x08);
	LCD_WR_DATA8(0x03);
	LCD_WR_DATA8(0x24);
	LCD_WR_DATA8(0x32);
	LCD_WR_DATA8(0x32);
	LCD_WR_DATA8(0x3B);
	LCD_WR_DATA8(0x14);
	LCD_WR_DATA8(0x13);
	LCD_WR_DATA8(0x28);
	LCD_WR_DATA8(0x2F);

	LCD_WR_REG(0x21); 		//反显
	
	LCD_WR_REG(0x29);         //开启显示 
} 

编译重上电,可以看到刷屏效果明显改善了很多。前后波形对比图如下,可以看到,发送一个字节所耗费的时间快了10余倍,基本符合我们的预期。(好像SPI+DMA刷屏时间会更快,后续有时间可以再优化下)
在这里插入图片描述
在这里插入图片描述


总结

关于SPI屏驱动这部分的内容暂时就介绍到这里,后续有时间再详细介绍下lvgl库的移植。最后附上源代码链接:hc32f460petb_template.zip

版权声明:本文为CSDN博主「Top0_0lll」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_39597546/article/details/122478110

生成海报
点赞 0

Top0_0lll

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

HC32F460开发之spi lcd屏驱动

前言 显示屏作为人机交互的一个重要窗口,在各类电子产品中被广泛地应用。在同类产品中,一个好的人机界面设计往往能更加吸引用户的目光。而显示器作为其载体,同样也是不可或缺的存在。作为一名嵌入式软件工程师&

十六届全向组硬件开源(二)

前言:算法开源已经开始第一章信号的预处理 这一章更新下麦轮的驱动方案,使用过CH32的同学都知道,这款单片机的定时器很少,所以推荐的方案中都是采用DRV8701方案来解决一个PWM口控制一