这个屏幕用到的也是之前学过的SPI通信,这里再复习一下,SPl-Serial Peripheral interface的缩写意思是串行外围设备接口。SPI是具有时钟线,是一种高速、全双工的同步串行通信总线。
GD32E230支持SPI0与SPI1两个通信接口,在使用时,需要将对应的外设SPI引脚连接到GD32-SPI引脚上。才可使用硬件SPI。否则只能使引脚高低电平变化模拟SPI通信时序。

SPI是一主多从的通信结构,支持多个设备通信,主机通过片选线对从机设备进行选中,开始通信。
注意片选信号仅是一个高低电平,一般情况拉低片选信号即可代表选择该设备,所以片选信号可以直接使用通用GPIO引脚替代

SPI是一个环形总线结构,由四根线组成SSPSR是SPI设备内部的移位寄存器,根据SPI时钟信号状态,往SSPBUF中移入或移出数据注意这个环形的说法,这里当MOSI向从机发送一位数据后,从机的MISO也会移位数据给主机,只不过主机可以根据需要是否将其放置接收缓冲区中,比如,如果是主机发送0x80,从机可能会移位过来一位无意义的数据,这时主机不会管这些数据,会选择丢弃。如果主机需要接收数据,那就是发送无意义的数据给从机,以此来交换有效数据存储到接收缓冲区中去。

实验内容:本次实验将实现驱动1.8寸LCD屏幕显示白色背景+任意文字
实验原理:通过前面的基础知识说明,已经知道SPI通信原理与1.8寸屏幕的连接参数:驱动屏幕就是通过SPI发送对应的指令给屏幕内的控制芯片使其点亮或熄灭。一般情况下,屏幕驱动厂商都会写好参考驱动程序,需要开发者自行适配进行修改

这里引脚默认都是GPIO功能的,这里我们在数据手册中找到对应的引脚说明,如果需要使用SPI的功能的话,是需要将引脚设置为AF0这个复用功能的,再将其映射到SPI0的接口上,这个接口挂载在APB2总线上。其他的引脚片选信号使用软件片选,以及其余的命令控制、背光控制等等,均使用普通IO模式即可,它只是一个高低电平的变化,就不需要像SPI一样,遵循时序和发送

接下来就是代码的编写

SPI0引脚PA5和PA7:

#include “gd32e23x.h”                   // Device header
void Init_SPI0(void)
{
//SPI参数结构体
spi_parameter_struct spi_init_struct;
rcu_periph_clock_enable(RCU_GPIOA);//需要使用到SPI功能的只有PA5和PA7,所以只开启GPIOA的时钟
rcu_periph_clock_enable(RCU_SPI0);
//GPIO复用模式设置
gpio_af_set(GPIOA, GPIO_AF_0, GPIO_PIN_5 | GPIO_PIN_7);//原本引脚有PA6对应MISO,这里用到的屏幕不需要就删除了
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5 | GPIO_PIN_7);//这里只保留时钟线和MOSI
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 | GPIO_PIN_7);
//复位SPI0
spi_i2s_deinit(SPI0);
//初始化SPI参数结构体
spi_struct_para_init(&spi_init_struct);
//
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;//这里选择使用全双工模式
spi_init_struct.device_mode = SPI_MASTER;//SPI主机模式
spi_init_struct.frame_size= SPI_FRAMESIZE_8BIT;//8字节宽度
spi_init_struct.clock_polarity_phase= SPI_CK_PL_HIGH_PH_2EDGE;//GD32把相位和极性放到一起设置了,这里的HIGH就是空闲高电平起始,2EDGE就是第二个边沿开始捕获
spi_init_struct.nss= SPI_NSS_SOFT;//片选信号选择由软件控制
spi_init_struct.prescale= SPI_PSC_4;//选择时钟四分频
spi_init_struct.endian= SPI_ENDIAN_MSB;//数据传输高位在前
spi_init(SPI0, &spi_init_struct);
spi_enable(SPI0);
}
void spi_SendData(uint8_t data)
{
while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_TBE))
spi_i2s_data_transmit(SPI0, data);
}
现在SCL和SDA这两个引脚的代码已经写好了,剩下的CS,DC,RES,BLK都是通用的GPIO引脚,还要初始化:
void LCD_GPIO_Init(void)
{
rcu_periph_clock_enable(RCU_GPIOB);gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_5);
gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_6);
gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_7);
gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_8);

gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_7);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);

gpio_bit_set(GPIOB, GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8);
}

这里我们主要要写一下的就是lcdinit,比较底层的一些代码,因为厂家提供的代码会有点不一样,但也只限在底层部分,因为我们要使用通用GPIO口来控制除了SCK和SDA之外的引脚,所以参考厂家代码,在这几个引脚需要变化的时候,用gpio_bit_set函数还有gpio_bit_reset函数来操控就好了。
//LCD串行数据写入函数
void LCD_Write_Bus(uint8_t data)
{
gpio_bit_reset(GPIOB, GPIO_PIN_7);//拉低片选信号
spi_SendData(data);//发送数据
gpio_bit_set(GPIOB, GPIO_PIN_7); //拉高片选信号
}
//写入8字节数据
void LCD_WR_DATA8(uint8_t data)
{
LCD_Write_Bus(data);
}
//写入16字节数据
void LCD_WR_DATA(uint16_t data)
{
LCD_Write_Bus(data>>8);
LCD_Write_Bus(data);
}
//LCD写入命令
void LCD_WR_REG(uint8_t data)
{
gpio_bit_reset(GPIOB, GPIO_PIN_6);//拉低命令信号
LCD_Write_Bus(data);
gpio_bit_set(GPIOB, GPIO_PIN_6);//拉高命令信号
}
void LCD_Address_Set(uint16_t x1,uint16_t y1,uint16_t x2,uint16_t y2)
{
if(USE_HORIZONTAL==0)
{
LCD_WR_REG(0x2a);//列地址设置
LCD_WR_DATA(x1+2);
LCD_WR_DATA(x2+2);
LCD_WR_REG(0x2b);//行地址设置
LCD_WR_DATA(y1+1);
LCD_WR_DATA(y2+1);
LCD_WR_REG(0x2c);//储存器写
}
else if(USE_HORIZONTAL==1)
{
LCD_WR_REG(0x2a);//列地址设置
LCD_WR_DATA(x1+2);
LCD_WR_DATA(x2+2);
LCD_WR_REG(0x2b);//行地址设置
LCD_WR_DATA(y1+1);
LCD_WR_DATA(y2+1);
LCD_WR_REG(0x2c);//储存器写
}
else if(USE_HORIZONTAL==2)
{
LCD_WR_REG(0x2a);//列地址设置
LCD_WR_DATA(x1+1);
LCD_WR_DATA(x2+1);
LCD_WR_REG(0x2b);//行地址设置
LCD_WR_DATA(y1+2);
LCD_WR_DATA(y2+2);
LCD_WR_REG(0x2c);//储存器写
}
else
{
LCD_WR_REG(0x2a);//列地址设置
LCD_WR_DATA(x1+1);
LCD_WR_DATA(x2+1);
LCD_WR_REG(0x2b);//行地址设置
LCD_WR_DATA(y1+2);
LCD_WR_DATA(y2+2);
LCD_WR_REG(0x2c);//储存器写
}
}
void LCD_Init(void)
{
LCD_GPIO_Init();//初始化GPIO
Init_SPI0();    //初始化SPI1
gpio_bit_reset(GPIOB, GPIO_PIN_5);  //拉低复位信号
delay_1ms(100);
gpio_bit_set(GPIOB, GPIO_PIN_5);//拉高复位信号
delay_1ms(100);
gpio_bit_set(GPIOB, GPIO_PIN_8);//拉高背光信号
delay_1ms(100);
//************* Start Initial Sequence **********//
LCD_WR_REG(0x11); //Sleep out
delay_1ms(120);              //Delay 120ms
//————————————ST7735S Frame Rate—————————————–//
LCD_WR_REG(0xB1);
LCD_WR_DATA8(0x05);
LCD_WR_DATA8(0x3C);
LCD_WR_DATA8(0x3C);
LCD_WR_REG(0xB2);
LCD_WR_DATA8(0x05);
LCD_WR_DATA8(0x3C);
LCD_WR_DATA8(0x3C);
LCD_WR_REG(0xB3);
LCD_WR_DATA8(0x05);
LCD_WR_DATA8(0x3C);
LCD_WR_DATA8(0x3C);
LCD_WR_DATA8(0x05);
LCD_WR_DATA8(0x3C);
LCD_WR_DATA8(0x3C);
//————————————End ST7735S Frame Rate———————————//
LCD_WR_REG(0xB4); //Dot inversion
LCD_WR_DATA8(0x03);
//————————————ST7735S Power Sequence———————————//
LCD_WR_REG(0xC0);
LCD_WR_DATA8(0x28);
LCD_WR_DATA8(0x08);
LCD_WR_DATA8(0x04);
LCD_WR_REG(0xC1);
LCD_WR_DATA8(0XC0);
LCD_WR_REG(0xC2);
LCD_WR_DATA8(0x0D);
LCD_WR_DATA8(0x00);
LCD_WR_REG(0xC3);
LCD_WR_DATA8(0x8D);
LCD_WR_DATA8(0x2A);
LCD_WR_REG(0xC4);
LCD_WR_DATA8(0x8D);
LCD_WR_DATA8(0xEE);
//———————————End ST7735S Power Sequence————————————-//
LCD_WR_REG(0xC5); //VCOM
LCD_WR_DATA8(0x1A);
LCD_WR_REG(0x36); //MX, MY, RGB mode
if(USE_HORIZONTAL==0)LCD_WR_DATA8(0x00);
else if(USE_HORIZONTAL==1)LCD_WR_DATA8(0xC0);
else if(USE_HORIZONTAL==2)LCD_WR_DATA8(0x70);
else LCD_WR_DATA8(0xA0);
//————————————ST7735S Gamma Sequence———————————//
LCD_WR_REG(0xE0);
LCD_WR_DATA8(0x04);
LCD_WR_DATA8(0x22);
LCD_WR_DATA8(0x07);
LCD_WR_DATA8(0x0A);
LCD_WR_DATA8(0x2E);
LCD_WR_DATA8(0x30);
LCD_WR_DATA8(0x25);
LCD_WR_DATA8(0x2A);
LCD_WR_DATA8(0x28);
LCD_WR_DATA8(0x26);
LCD_WR_DATA8(0x2E);
LCD_WR_DATA8(0x3A);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x01);
LCD_WR_DATA8(0x03);
LCD_WR_DATA8(0x13);
LCD_WR_REG(0xE1);
LCD_WR_DATA8(0x04);
LCD_WR_DATA8(0x16);
LCD_WR_DATA8(0x06);
LCD_WR_DATA8(0x0D);
LCD_WR_DATA8(0x2D);
LCD_WR_DATA8(0x26);
LCD_WR_DATA8(0x23);
LCD_WR_DATA8(0x27);
LCD_WR_DATA8(0x27);
LCD_WR_DATA8(0x25);
LCD_WR_DATA8(0x2D);
LCD_WR_DATA8(0x3B);
LCD_WR_DATA8(0x00);
LCD_WR_DATA8(0x01);
LCD_WR_DATA8(0x04);
LCD_WR_DATA8(0x13);
//————————————End ST7735S Gamma Sequence—————————–//
LCD_WR_REG(0x3A); //65k mode
LCD_WR_DATA8(0x05);
LCD_WR_REG(0x29); //Display on
}
头文件需要额外用到的声明如下:

#ifndef __LCDINIT_H
#define __LCDINIT_H

void LCD_GPIO_Init(void);

#define USE_HORIZONTAL 2 //设置横屏或者竖屏显示 0或1为竖屏 2或3为横屏

#if USE_HORIZONTAL==0||USE_HORIZONTAL==1
#define LCD_W 128
#define LCD_H 160

#else
#define LCD_W 160
#define LCD_H 128
#endif

 

剩余更加高层的代码就可以直接复制过来,再检查一下有无格式上的一些错误就好了,还有就是有些函数会调用到之后才写出来的函数,可以把原头文件的声明都移植过来,然后在.c文件引用自身头文件即可,在.c文件前面提前函数声明也行。还有就是厂家提供的高层代码中可能会用到其他的文件里的函数,可以仔细观察它引用的头文件,可以将其也移植过来