本次测试主要是通过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; }
效果如下: