[GD32L233C-START 评测] 【GD32L233C-START评测】3、软件GPIO模拟+硬件SPI驱动lcd屏

emmnn   2022-2-3 15:17 楼主

前言

    之前在某宝买过一块spi接口的TFT彩屏(类似于下图中这种),但是买回来后就一直吃灰了,这几天想起来就决定先把屏给点亮。

b7fd4debdfe8e8101a31d9a81009ff9.jpg


硬件介绍

    关于屏的硬件资料,卖家仅是提供了一份stm32的驱动代码,剩下的就是在商品介绍页面的接口定义说明了。

image.png   image.png  


驱动代码实现

     关于硬件接口的介绍,就是我们平时很常见的SPI接口。卖家提供的驱动示例都是使用IO去模拟SPI点屏的,因此这里开始我们分为两步来介绍,先使用IO模拟SPI时序来点亮lcd屏,再使用GD32L233C自带的硬件SPI来点亮LCD屏。

  • IO模拟SPI驱动

    如果使用IO模拟SPI来驱动LCD屏的话,一般来说,只要开发板上没被使用过的IO引脚一般都可以拿来用,但是为了考虑到后续能够同硬件SPI驱动兼容,SPI的时钟线和数据线我们需要选择能够复用成SPI_SCKSPI_MOSI功能的引脚。参考数据手册以及原理图,最终PA5PB5分别作为SPI的时钟引脚和数据引脚。

image.png

 

image.png

 
其他引脚的话就没有什么特别需要注意的了,最终引脚定义如下:

    #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();
}

剩下的就是一些初始化参数的写入,以及一些写屏的代码,基本都可以从直接把卖家的代码拿过来使用。最后效果如下:


从视频上看,刷屏看起来是很慢的,有着很明显的“拉窗帘”的现象的。


  • 硬件SPI驱动

GD32L233C本身带有SPI硬件外设,因此,我们在驱动带有这一类接口的模块时,是可以直接使用MCU自带的SPI硬件外设来驱动,对比IO模拟,通讯速度也可以快上很多。

对于硬件SPI的应用,我们先从产品的用户手册和数据手册上入手。

image.png  

从手册上我们可以了解到,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初始化,在用户手册上也有较为详细的说明

image.png  

代码如下:

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 编辑

    lcd.zip (2022-2-3 15:15 上传)

    14.63 KB, 下载次数: 21

回复评论 (2)

看来自带的硬件SPI来点亮LCD屏效果确实可以

IO模拟SPI时序也比较麻烦

 

点赞  2022-2-6 23:42
引用: Jacktang 发表于 2022-2-6 23:42 看来自带的硬件SPI来点亮LCD屏效果确实可以 IO模拟SPI时序也比较麻烦  

是的,但是因为主频最高只能到64M的原因,看起来还是有点慢,后面找时间看看SPI+DMA的效果,不行就上串口屏了。

点赞  2022-2-7 09:45
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复