前言
之前在某宝买过一块spi接口的TFT彩屏(类似于下图中这种),但是买回来后就一直吃灰了,这几天想起来就决定先把屏给点亮。
硬件介绍
关于屏的硬件资料,卖家仅是提供了一份stm32的驱动代码,剩下的就是在商品介绍页面的接口定义说明了。
驱动代码实现
关于硬件接口的介绍,就是我们平时很常见的SPI接口。卖家提供的驱动示例都是使用IO去模拟SPI点屏的,因此这里开始我们分为两步来介绍,先使用IO模拟SPI时序来点亮lcd屏,再使用GD32L233C自带的硬件SPI来点亮LCD屏。
如果使用IO模拟SPI来驱动LCD屏的话,一般来说,只要开发板上没被使用过的IO引脚一般都可以拿来用,但是为了考虑到后续能够同硬件SPI驱动兼容,SPI的时钟线和数据线我们需要选择能够复用成SPI_SCK和SPI_MOSI功能的引脚。参考数据手册以及原理图,最终PA5和PB5分别作为SPI的时钟引脚和数据引脚。
其他引脚的话就没有什么特别需要注意的了,最终引脚定义如下:
#define SPI_SCK_RCU (RCU_GPIOA)
#define SPI_MOSI_RCU (RCU_GPIOB)
#define SPI_GPIO_PORT1 (GPIOA)
#define SPI_GPIO_PORT2 (GPIOB)
/* Define port and pin for SDA and SCL */
#define SPI_SCK_PORT (GPIOA)
#define SPI_SCK_PIN (GPIO_PIN_5)
#define SPI_MOSI_PORT (GPIOB)
#define SPI_MOSI_PIN (GPIO_PIN_5)
/* RES Port/Pin definition */
#define RES_PORT (GPIOA)
#define RES_PIN (GPIO_PIN_1)
/* DC Port/Pin definition */
#define DC_PORT (GPIOA)
#define DC_PIN (GPIO_PIN_4)
/* BLK Port/Pin definition */
#define BLK_PORT (GPIOA)
#define BLK_PIN (GPIO_PIN_6)
//-----------------LCD端口定义----------------
#define LCD_SCLK_Clr() GPIO_BC(SPI_SCK_PORT) = (uint32_t)SPI_SCK_PIN //SCL=SCLK
#define LCD_SCLK_Set() GPIO_BOP(SPI_SCK_PORT) = (uint32_t)SPI_SCK_PIN
#define LCD_MOSI_Clr() GPIO_BC(SPI_MOSI_PORT) = (uint32_t)SPI_MOSI_PIN //SDA=MOSI
#define LCD_MOSI_Set() GPIO_BOP(SPI_MOSI_PORT) = (uint32_t)SPI_MOSI_PIN
#define LCD_RES_Clr() GPIO_BC(RES_PORT) = (uint32_t)RES_PIN //RES
#define LCD_RES_Set() GPIO_BOP(RES_PORT) = (uint32_t)RES_PIN
#define LCD_DC_Clr() GPIO_BC(DC_PORT) = (uint32_t)DC_PIN //DC
#define LCD_DC_Set() GPIO_BOP(DC_PORT) = (uint32_t)DC_PIN
#define LCD_BLK_Clr() GPIO_BC(BLK_PORT) = (uint32_t)BLK_PIN //BLK
#define LCD_BLK_Set() GPIO_BOP(BLK_PORT) = (uint32_t)BLK_PIN
GPIO引脚初始化
void LCD_GPIO_Init(void)
{
/* enable the LED GPIO clock */
rcu_periph_clock_enable(SPI_SCK_RCU);
rcu_periph_clock_enable(SPI_MOSI_RCU);
/* configure LED GPIO pin */
gpio_mode_set(SPI_GPIO_PORT1, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SPI_SCK_PIN | RES_PIN | DC_PIN | BLK_PIN);
gpio_output_options_set(SPI_GPIO_PORT1, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, SPI_SCK_PIN | RES_PIN | DC_PIN | BLK_PIN);
gpio_mode_set(SPI_GPIO_PORT2, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SPI_MOSI_PIN);
gpio_output_options_set(SPI_GPIO_PORT2, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, SPI_MOSI_PIN);
/* reset LED GPIO pin */
gpio_bit_reset(SPI_GPIO_PORT1, SPI_SCK_PIN | RES_PIN | DC_PIN | BLK_PIN);
gpio_bit_reset(SPI_GPIO_PORT2, SPI_MOSI_PIN);
}
IO模拟SPI时序写入一个字节
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();
}
剩下的就是一些初始化参数的写入,以及一些写屏的代码,基本都可以从直接把卖家的代码拿过来使用。最后效果如下:
从视频上看,刷屏看起来是很慢的,有着很明显的“拉窗帘”的现象的。
GD32L233C本身带有SPI硬件外设,因此,我们在驱动带有这一类接口的模块时,是可以直接使用MCU自带的SPI硬件外设来驱动,对比IO模拟,通讯速度也可以快上很多。
对于硬件SPI的应用,我们先从产品的用户手册和数据手册上入手。
从手册上我们可以了解到,GD32L233C共有两路SPI外设,其中SPI0支持SPI四线主机模式,也就是QSPI模式。这里我们选用SPI0的普通模式来使用。
首先是宏定义:
#define SPI_SCK_RCU (RCU_GPIOA)
#define SPI_MOSI_RCU (RCU_GPIOB)
#define SPI_GPIO_PORT1 (GPIOA)
#define SPI_GPIO_PORT2 (GPIOB)
/* RES Port/Pin definition */
#define RES_PORT (GPIOA)
#define RES_PIN (GPIO_PIN_1)
/* DC Port/Pin definition */
#define DC_PORT (GPIOA)
#define DC_PIN (GPIO_PIN_4)
/* BLK Port/Pin definition */
#define BLK_PORT (GPIOA)
#define BLK_PIN (GPIO_PIN_6)
#define LCD_RES_Clr() GPIO_BC(RES_PORT) = (uint32_t)RES_PIN //RES
#define LCD_RES_Set() GPIO_BOP(RES_PORT) = (uint32_t)RES_PIN
#define LCD_DC_Clr() GPIO_BC(DC_PORT) = (uint32_t)DC_PIN //DC
#define LCD_DC_Set() GPIO_BOP(DC_PORT) = (uint32_t)DC_PIN
#define LCD_BLK_Clr() GPIO_BC(BLK_PORT) = (uint32_t)BLK_PIN //BLK
#define LCD_BLK_Set() GPIO_BOP(BLK_PORT) = (uint32_t)BLK_PIN
#define SPI_RCU_PERIPH (RCU_SPI0)
#define SPI_PERIPH (SPI0)
/* Define port and pin for SDA and SCL */
#define SPI_SCK_PORT (GPIOA)
#define SPI_SCK_PIN (GPIO_PIN_5)
#define SPI_MOSI_PORT (GPIOB)
#define SPI_MOSI_PIN (GPIO_PIN_5)
接着就是对SPI的初始化,关于SPI初始化,在用户手册上也有较为详细的说明
代码如下:
void LCD_SPI_Init(void)
{
spi_parameter_struct spi_init_struct;
rcu_periph_clock_enable(SPI_SCK_RCU);
rcu_periph_clock_enable(SPI_MOSI_RCU);
rcu_periph_clock_enable(SPI_RCU_PERIPH);
/* SPI0_CLK(PA5) GPIO pin configuration */
gpio_af_set(SPI_SCK_PORT, GPIO_AF_5, SPI_SCK_PIN);
gpio_mode_set(SPI_SCK_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, SPI_SCK_PIN);
gpio_output_options_set(SPI_SCK_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, SPI_SCK_PIN);
/* SPI0_MOSI(PB5) GPIO pin configuration */
gpio_af_set(SPI_MOSI_PORT, GPIO_AF_5, SPI_MOSI_PIN);
gpio_mode_set(SPI_MOSI_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, SPI_MOSI_PIN);
gpio_output_options_set(SPI_MOSI_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, SPI_MOSI_PIN);
/* SPI1 parameter config */
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
spi_init_struct.device_mode = SPI_MASTER;
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT;
spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;
spi_init_struct.nss = SPI_NSS_SOFT;
spi_init_struct.prescale = SPI_PSC_2;
spi_init_struct.endian = SPI_ENDIAN_MSB;
spi_init(SPI_PERIPH, &spi_init_struct);
/* configure SPI0 byte access to FIFO */
spi_fifo_access_size_config(SPI_PERIPH, SPI_BYTE_ACCESS);
/* set crc polynomial */
spi_crc_polynomial_set(SPI_PERIPH, 7);
/* quad wire SPI_IO2 and SPI_IO3 pin output enable */
qspi_io23_output_enable(SPI_PERIPH);
/* enable SPI0 */
spi_enable(SPI_PERIPH);
}
硬件SPI写一个字节
void LCD_SPI_Write(uint8_t Data)
{
/* Send data */
spi_i2s_data_transmit(SPI_PERIPH, Data);
/* Wait tx buffer empty */
while(RESET == spi_i2s_flag_get(SPI_PERIPH, SPI_FLAG_TBE));
}
最后刷屏效果如下:
可以看到,对比起IO模拟SPI时序,使用硬件SPI驱动LCD屏的显示效果更好。
总结
关于点屏的内容今天就暂时介绍到这里(有在网上看到资料说使用SPI+DMA刷屏效果会更好,后续有时间在搞搞看),如果所讲内容有什么错误,欢迎大家评论指出,有什么问题,也欢迎大家提问,谢谢。最后关于LCD屏部分驱动代码见附件。
本帖最后由 emmnn 于 2022-2-3 15:17 编辑引用: Jacktang 发表于 2022-2-6 23:42 看来自带的硬件SPI来点亮LCD屏效果确实可以 IO模拟SPI时序也比较麻烦
是的,但是因为主频最高只能到64M的原因,看起来还是有点慢,后面找时间看看SPI+DMA的效果,不行就上串口屏了。