写在前面:
屏幕供应商提供的GC9A01驱动代码过于简陋,使用的话需要修改的东西比较多,还好看到一篇文章,而且博主分享了驱动代码。文章地址
以下软件SPI部分是通过博主分享的驱动代码得来的,稍微改一下就可以用,就不多说了。
使用起来发现使用软件模拟SPI的话,屏幕刷新速度特别慢(RGB565),刷新一张240*240
的图片(115200字节,MCU主频使用40MHz),需要1秒多。然后就想着要提高一下刷新速度,先更改一下代码。
因为要操作的信号线除了SPI用到的CLK和MOSI之外,还有片选线CS,数据/寄存器选择线RS,如果直接使用HAL库的GPIO写函数的话会有冗余代码。
所以直接用宏定义替换HAL_GPIO_WritePin()函数,直接改GPIO寄存器。
#define LCD_RESET_HIGH LCD_RESET_GPIO_Port->BSRR = (uint32_t)LCD_RESET_Pin;
#define LCD_RESET_LOW LCD_RESET_GPIO_Port->BRR = (uint32_t)LCD_RESET_Pin;
#define LCD_CS_HIGH LCD_CS_GPIO_Port->BSRR = (uint32_t)LCD_CS_Pin;
#define LCD_CS_LOW LCD_CS_GPIO_Port->BRR = (uint32_t)LCD_CS_Pin;
#define LCD_RS_HIGH LCD_RS_GPIO_Port->BSRR = (uint32_t)LCD_RS_Pin;
#define LCD_RS_LOW LCD_RS_GPIO_Port->BRR = (uint32_t)LCD_RS_Pin;
#define LCD_TE_HIGH LCD_TE_GPIO_Port->BSRR = (uint32_t)LCD_TE_Pin;
#define LCD_TE_LOW LCD_TE_GPIO_Port->BRR = (uint32_t)LCD_TE_Pin;
#define LCD_CLK_HIGH LCD_CLK_GPIO_Port->BSRR = (uint32_t)LCD_CLK_Pin;
#define LCD_CLK_LOW LCD_CLK_GPIO_Port->BRR = (uint32_t)LCD_CLK_Pin;
#define LCD_MOSI_HIGH LCD_MOSI_GPIO_Port->BSRR = (uint32_t)LCD_MOSI_Pin;
#define LCD_MOSI_LOW LCD_MOSI_GPIO_Port->BRR = (uint32_t)LCD_MOSI_Pin;
修改之后刷新一张图片需要650ms左右,这些时间只是个大概,因为是看串口的时间戳,没有用示波器去抓,只是做个对比。
LCD_ShowPicture()和LCD_WR_DATA8()函数都有for循环,LCD_WR_DATA8()函数是通过移位来发送字节的,那么就改成不移位,直接发送。
void LCD_Writ_Bus_8(uint8_t dat)
{
LCD_CLK_LOW;
if(dat&0x80)
{
LCD_MOSI_HIGH;
}
else
{
LCD_MOSI_LOW;
}
LCD_CLK_HIGH;
LCD_CLK_LOW;
if(dat&0x40)
{
LCD_MOSI_HIGH;
}
else
{
LCD_MOSI_LOW;
}
LCD_CLK_HIGH;
LCD_CLK_LOW;
if(dat&0x20)
{
LCD_MOSI_HIGH;
}
else
{
LCD_MOSI_LOW;
}
LCD_CLK_HIGH;
LCD_CLK_LOW;
if(dat&0x10)
{
LCD_MOSI_HIGH;
}
else
{
LCD_MOSI_LOW;
}
LCD_CLK_HIGH;
LCD_CLK_LOW;
if(dat&0x08)
{
LCD_MOSI_HIGH;
}
else
{
LCD_MOSI_LOW;
}
LCD_CLK_HIGH;
LCD_CLK_LOW;
if(dat&0x04)
{
LCD_MOSI_HIGH;
}
else
{
LCD_MOSI_LOW;
}
LCD_CLK_HIGH;
LCD_CLK_LOW;
if(dat&0x02)
{
LCD_MOSI_HIGH;
}
else
{
LCD_MOSI_LOW;
}
LCD_CLK_HIGH;
LCD_CLK_LOW;
if(dat&0x01)
{
LCD_MOSI_HIGH;
}
else
{
LCD_MOSI_LOW;
}
LCD_CLK_HIGH;
}
这样下来刷新一张图片大概要350ms左右,相似的也可以一次发送16位,24位等,但是会发现速度基本没有提升。
LCD_Writ_Bus_16(*(uint16_t*)(pic+i));
LCD_Writ_Bus_32(*(uint32_t*)(pic+i));
然后就是主频的问题,原本用的40MHz,改为80MHz之后刷新一张图片大概要170ms左右。
之后就想着使用硬件SPI对比一下,SPI初始化好之后改一下原有的发送函数就可以了。
#ifdef HARDWARE_SPI
void LCD_Writ_Bus(uint8_t dat)
{
LCD_CS_LOW;
HAL_SPI_Transmit(&hspi1,&dat,1,0xfff);
LCD_CS_HIGH;
}
#else
void LCD_Writ_Bus(uint8_t dat)
{
uint8_t i;
LCD_CS_LOW;
for(i=0;i<8;i++)
{
LCD_CLK_LOW;
if(dat&0x80)
{
LCD_MOSI_HIGH;
}
else
{
LCD_MOSI_LOW;
}
LCD_CLK_HIGH;
dat<<=1;
}
LCD_CS_HIGH;
}
#endif
在显示图片函数里面将原有的两个for循环改为HAL_SPI_Transmit()函数,需要注意一下长度的问题,一共有240*240*2
(115200)个字节需要发送,但是HAL_SPI_Transmit()函数参数中大小的类型为uint16_t,所以要分两次发送,另外就是要设置一下片选线CS状态。
void LCD_ShowPicture_Fast(uint16_t x,uint16_t y,uint16_t length,uint16_t width,const uint8_t pic[])
{
uint32_t i,j;
uint32_t k=0;
LCD_Address_Set(x,y,x+length-1,y+width-1);
LCD_CS_LOW;
HAL_SPI_Transmit(&hspi1,(uint8_t *)pic,57600,0xfff);
HAL_SPI_Transmit(&hspi1,(uint8_t *)(pic+57600),57600,0xfff);
LCD_CS_HIGH;
}
之所以可以这么写,是因为手册中有一个连续写内存命令。
命令描述大概就是在写内存开始命令和连续写命令之后,会根据设置的范围自动换行或者换列,超出范围的数据会被忽略,所以就可以直接用HAL_SPI_Transmit()函数发送。在设置好显示区域之后,需要设置0x2C或者0x3C寄存器,后面直接跟数据就可以。
改为硬件SPI之后,刷新一张图片大概需要60ms左右(MCU主频使用40MHz),这么一对比的话还是直接用硬件SPI吧。