本次测试主要是通过SPI驱动显示单元:
在本次测试中,我们选用了1.54英寸的TFT液晶显示屏作为显示元件。该屏幕具备高清晰度特性,其主要参数为240×240的分辨率,能够呈现出细腻的图像和文字效果。该显示单元采用了7789作为主控芯片。这款芯片支持SPI和8080两种通信协议,具有高度的灵活性和可扩展性。
在本次测试中,我们选择了SPI协议作为通信方式。SPI(Serial Peripheral Interface)是一种同步串行外设接口,具有高速、简洁、低功耗的特点,非常适合用于显示单元与主控芯片之间的数据传输。
在驱动部分,我们分别尝试了使用IO口模拟和硬件SPI两种方式来驱动显示屏。首先,我们采用了IO口模拟SPI的方式进行屏幕驱动。这种方式通过软件编程,利用IO口的输入输出功能来模拟SPI通信协议的时序和数据传输过程。虽然这种方法相对复杂且速度可能受到一定限制,但它无需额外的硬件支持,成本较低,且具有较强的灵活性。
IO口模拟部分代码如下:
#define SPI1_MASTER SPI1
#define SPI1_MASTER_PERIPH RCC_APB2_PERIPH_SPI1
#define SPI1_MASTER_PERIPH_GPIO RCC_APB2Periph_GPIOA
#define SPI1_MASTER_GPIO GPIOA
#define SPI1_MOSI_GPIO_ALTERNATE GPIO_AF1_SPI1
#define SPI1_CLK_GPIO_ALTERNATE GPIO_AF2_SPI1
#define SPI1_MASTER_MISO_PIN GPIO_Pin_6
#define SPI1_MASTER_MOSI_PIN GPIO_Pin_7
#define SPI1_MASTER_CLK_PIN GPIO_Pin_5
#define SPI1_MASTER_NSS_PIN GPIO_Pin_4
#define TFT_BL_GPIO GPIOC
#define TFT_BL_PERIPH_GPIO RCC_APB2Periph_GPIOC
#define TFT_BL_PIN GPIO_Pin_7
#define TFT_RS_PIN GPIO_Pin_16
#define TFT_RESET_PIN GPIO_Pin_17
#define TFT_RS_reset GPIO_WriteBit(TFT_BL_GPIO, TFT_RS_PIN,Bit_RESET)
#define TFT_RS_set GPIO_WriteBit(TFT_BL_GPIO, TFT_RS_PIN,Bit_SET)
#define TFT_RESET_reset GPIO_WriteBit(TFT_BL_GPIO, TFT_RESET_PIN,Bit_RESET)
#define TFT_RESET_set GPIO_WriteBit(TFT_BL_GPIO, TFT_RESET_PIN,Bit_SET)
#define TFT_BL_SET GPIO_WriteBit(TFT_BL_GPIO, TFT_BL_PIN,Bit_SET)
#define TFT_BL_RESET GPIO_WriteBit(TFT_BL_GPIO, TFT_BL_PIN,Bit_RESET)
#define SPI1_CS_OUT0 GPIO_WriteBit(SPI1_MASTER_GPIO, SPI1_MASTER_NSS_PIN,Bit_RESET)
#define SPI1_CS_OUT1 GPIO_WriteBit(SPI1_MASTER_GPIO, SPI1_MASTER_NSS_PIN,Bit_SET)
#define SPI1_MOSI_OUT0 GPIO_WriteBit(SPI1_MASTER_GPIO, SPI1_MASTER_MOSI_PIN,Bit_RESET)
#define SPI1_MOSI_OUT1 GPIO_WriteBit(SPI1_MASTER_GPIO, SPI1_MASTER_MOSI_PIN,Bit_SET)
#define SPI1_CLK_OUT0 GPIO_WriteBit(SPI1_MASTER_GPIO, SPI1_MASTER_CLK_PIN,Bit_RESET)
#define SPI1_CLK_OUT1 GPIO_WriteBit(SPI1_MASTER_GPIO, SPI1_MASTER_CLK_PIN,Bit_SET)
void delay_us(uint32_t Data)
{
Delay_Us(Data);
}
void delay_ms(uint32_t Data)
{
Delay_Ms(Data);
}
void SPI1_IOInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = SPI1_MASTER_NSS_PIN | SPI1_MASTER_MOSI_PIN | SPI1_MASTER_CLK_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = TFT_BL_PIN |TFT_RS_PIN | TFT_RESET_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
SPI1_CLK_OUT0;
SPI1_MOSI_OUT0;
SPI1_CS_OUT1;
TFT_RS_reset;
TFT_RESET_reset;
TFT_BL_SET;
}
void SPI1_Send_Data(uint8_t Data)
{
SPI1_CS_OUT0; //选中该从机
delay_us(2);
uint8_t i;
for (i = 0;i < 8;i++)
{
if ((Data << i) & 0x80)
{
SPI1_MOSI_OUT1;
SPI1_CLK_OUT0;//rising edge
SPI1_CLK_OUT1;
}
else
{
SPI1_MOSI_OUT0;
SPI1_CLK_OUT0;//rising edge
SPI1_CLK_OUT1;
}
}
SPI1_CS_OUT1;
}显示屏的驱动初始化程序因芯片型号的不同而有所差异,因此在此不作详细介绍。只要能够成功实现8位数据的时序传输,基本上就可以保证驱动程序的正常工作。
目前我们使用的SPI接口中,CS(片选)、MOSI(主出从入)和CLK(时钟)分别对应复用功能的GPIOA的GPIO_Pin_4、GPIO_Pin_7和GPIO_Pin_5。而其他控制字引脚则采用普通的IO控制方式。具体来说,RS引脚用于切换数据/命令模式(对应GPIOC-GPIO_Pin_16),复位引脚用于重启显示屏(对应GPIOC-GPIO_Pin_17),而BL引脚则用于控制背光开关(对应GPIOC-GPIO_Pin_7)。
通过上述引脚配置和控制方式,我们实现了对显示屏的精确控制,取得了良好的显示效果:

经过初步的测试,我们可以确认显示屏的驱动已经成功实现。然而,对于240*240的高分辨率来说,当前的刷新速度并不尽如人意。这主要是由于主控芯片的主频限制在48MHz,从而影响了数据传输的速度和效率。
为了进一步提升刷新速度,我们接下来将尝试使用硬件SPI来进行屏幕刷新。硬件SPI相较于IO口模拟的方式,具有更高的数据传输速率和更低的延迟,因此有望显著提升显示屏的刷新效果。现在,让我们来体验一下使用硬件SPI进行刷屏的过程。通过配置和初始化硬件SPI接口,我们将能够直接利用硬件的特性,实现更高效的数据传输。这将使得显示屏在显示图像和文字时更加流畅和迅速,带来更加出色的视觉体验。
接下来,我们将展示相关的代码实现。这些代码将详细展示如何配置硬件SPI接口、发送数据以及控制显示屏的刷新过程。通过这些代码,我们可以深入了解硬件SPI的工作原理,并探索其在显示驱动中的应用潜力。
代码如下:
void SPI1_IOInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
SPI_InitTypeDef SPI_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_SPI1, ENABLE);
GPIO_InitStructure.GPIO_Pin = TFT_BL_PIN | TFT_RS_PIN | TFT_RESET_PIN ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = SPI1_MASTER_NSS_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SPI1_CS_OUT1;
TFT_RS_reset;
TFT_RESET_reset;
TFT_BL_SET;
SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);
}
void SPI1_Send_Data(uint8_t Data)
{
SPI1_CS_OUT0; //选中该从机
delay_us(1);
SPI_I2S_SendData(SPI1_MASTER, Data);
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
delay_us(1);
SPI1_CS_OUT1;
} 效果如下:

我要赚赏金
