stm32【 1.3寸LCD屏幕(1)】

1.3寸屏幕调试

测试平台:STM32F103RFT6
库版本:官方标准库3.5.0版本

屏幕:中景园1.3寸24Pin接插式LCD屏幕
分辨率:240*240像素
驱动芯片:ST7789
驱动方式:4线SPI

本文记录LCD屏幕的调试方法以及遇到的问题,选取STM32F103RFT6这款芯片主要是看重其高达96K的SRAM,在屏幕刷写时最多3次就能刷完整屏,同时也有SDIO功能,在后期还能加入SD卡读写功能(当然用SPI读写也可以),另外该芯片属于超大容量芯片,MDK配置的启动文件应选取【startup_stm32f10x_xl.s】
另外,为啥要表明该屏幕是中景园的呢?这里并不是打广告啊,本人早期也购买过其他店家的屏幕,但是几乎不提供技术支持,而且在商家发过来的例程中,明明白白的写着中景园几个字,我才知道有这么一家专门卖屏幕的,后来就只在该店购买了,一开始不太懂,调试一个0.96寸的小屏幕怎么也搞不定,还是联系客服找技术人员解决的,而且该商家有屏幕的生产资料以及配套的例程,这是极其重要的!!这个之后再解释。

注: 文中首次出现的代码块会标注[xxx.c]或[xxx.h],表明该代码是属于对应的文件,未标注的即为重复出现的
注: 附录中有驱动芯片下载网址

1、电路设计

如果你的LCD屏幕是带底板的模块,可以跳过该章节
如果你的LCD屏幕是裸屏的话,那么这章节的电路部分可以参考下

在中景园拿到的资料中有屏幕厂商的出厂参数,其中就有裸屏的尺寸参数以及驱动芯片的引出管脚功能描述:
▼尺寸参数,在PCB设计中可以保持屏幕居中

▼引脚描述(自行翻译,搞这些的一般都看得懂简单的英语吧)

▼电路设计参考

其实该屏幕的驱动芯片ST7789并不止【4-SPI】和【8-并口】这两种驱动功能,还有其他的例如【3-SPI】等,具体参考ST7789数据手册的第8章节(FUNCTION DESCRIPTION ),但是在屏幕生产厂商的设计下,只配置出这两种驱动方式,这也是为啥要强调屏幕的生产资料的重要性,对于其他尺寸的屏幕也是如此。

本文中采用【4-SPI】,4线SPI模式,具体的应用电路设计如下▼

背光:
图中的LEDA是背光的正极,LEDK是背光的负极,采用n-mos管【SI2302】驱动,在LCD_BKL输入高电平时n-mos管导通,LEDK接到GND,通过R13电阻可以调节背光的最大亮度,如果在LCD_BLK输入PWM,那么就可以调节屏幕亮度了
当然也可以用p-mos管,则R15接到VCC,在LCD_BKL输出低电平时导通,或者用三极管都是可以的。

信号接口:

SPI_MOSI	主出从入
SPI_CLK		时钟线
LCD_CS		LCD片选
LCD_DC		数据/命令端
LCD_RES		重启
LCD_TE		该管脚是用于处理屏幕撕裂效果的,目前调试没有用到,只是给出连接,以后估计会用到

没啥好说的,参考【引脚描述】连接上就行

这是我的PCB最终成品,大小是3x4cm,集成了SD卡,GT30L34S4W字库芯片,上方的排针是预留的接口,用来调试其他的屏幕:

电路设计基本上就这些,给用裸屏的小伙伴一个参考吧

2、程序设计

硬件连接:
PA0	->	LCD_TE
PA1	->	LCD_BLK
PA2	->	LCD_RES
PA3	->	LCD_DC
PA4	->	LCD_CS
PA5	->	SPI_CLK
PA6	->	没用到
PA7	->	SPI_MOSI

2.1、SPI配置(spi.c/spi.h)

SPI配置按常规配置就行,这里使用的是SPI1,需要注意的是时钟极性CPOL和时钟相位CPHA需根据ST7789数据手册的时序图确定
在datasheet的8.4.2章节有4-line的时序图

串行时钟稳态是高电平,下降沿捕获数据

[spi.c]
	SPI_InitStructure.SPI_BaudRatePrescaler = speed;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;						//数据捕获于第一个时钟沿
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;							//串行时钟稳态
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;					//每次收发数据大小
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;	//设置SPI单向/双向
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;						//设置SPI模式,主/从模式
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;					//指定数据传输从MSB位还是LSB位开始
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;							//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)控制
	SPI_InitStructure.SPI_CRCPolynomial = 7;							//CRC值计算的多项式

为了提高写入速度,这里编写一个SPI发送函数,直接操作寄存器

[spi.c]
void SPI1_WriteByte(u8 data)
{
	SPI1->DR = data;
}

2.2、LCD引脚配置(lcd.c/lcd.h)

就是普通的GPIO通用推挽输出配置,如果背光想要PWM调节,那么配置IO对应的TIM输出就行了,这里PA1接LCD_BLK,对应的是TIM5_CH2或者TIM2_CH2,选一个就行。本文不使用PWM调节。

2.3、LCD数据写入

需要编写三个函数:

[lcd.h]
#define LCD_WR_BYTE(Data)		SPI1_WriteByte(Data)	

void LCD_WR_Cmd(u8 Data);		//发送命令
void LCD_WR_Data8(u8 Data);		//发送1byte数据
void LCD_WR_Data16(u16 Data);	//发送2byte数据

函数原型:

[lcd.c]
/********************************************************************************
  * @brief  写入1byte指令
  * @param  Data	写入的数据
  * @retval none
  *******************************************************************************/
void LCD_WR_Cmd(u8 Data)
{
	//DC为0时发送命令
	LCD_DC_0;

	LCD_WR_BYTE(Data);
}
/********************************************************************************
  * @brief  写入1byte数据
  * @param  Data	写入的数据
  * @retval none
  *******************************************************************************/
void LCD_WR_Data8(u8 Data)
{
	//DC为1时发送数据
	LCD_DC_1;

	LCD_WR_BYTE(Data);
}
/********************************************************************************
  * @brief  写入2byte数据
  * @param  Data	写入的数据
  * @retval none
  *******************************************************************************/
void LCD_WR_Data16(u16 Data)
{
	//DC为1时发送数据
	LCD_DC_1;
	
	LCD_WR_BYTE(Data >> 8);	//先发送高8位
	LCD_WR_BYTE(Data);		//后发送低8位
}

LCD_WR_BYTE(Data)是通过宏定义替换的SPI1_WriteByte(Data),链接到SPI底层,
当LCD_DC = 0时,发送的是命令
当LCD_DC = 1时,发送的是数据
发送2byte数据主要是用在像素点颜色信息写入

LCD_DC_0和LCD_DC_1是宏定义,可以根据需求修改▼

[lcd.h]
//----------------- LCD端口定义 -----------------//
/****** 数据/命令控制端 ******/
//0:命令	1:数据
#define LCD_DC_0		(GPIOA->BRR = GPIO_Pin_3)
#define LCD_DC_1		(GPIOA->BSRR = GPIO_Pin_3)
/************ 片选 ***********/
//0:有效	1:无效
#define LCD_CS_0		(GPIOA->BRR = GPIO_Pin_4)
#define LCD_CS_1		(GPIOA->BSRR = GPIO_Pin_4)
/************ 重启 ***********/
//0:重启	1:正常
#define LCD_RES_0		(GPIOA->BRR = GPIO_Pin_2)
#define LCD_RES_1		(GPIOA->BSRR = GPIO_Pin_2)
/************ 背光 ***********/
//0:灭		1:亮	也可以用PWM调节亮度
#define LCD_BLK_0		(GPIOA->BRR = GPIO_Pin_1)
#define LCD_BLK_1		(GPIOA->BSRR = GPIO_Pin_1)

2.4、 LCD初始化

有了LCD数据发送函数,就可以对驱动芯片ST7789进行初始化了
▼屏幕重启

[lcd.c]
/********************************************************************************
  * @brief  重置屏幕
  * @param  none
  * @retval none
  *******************************************************************************/
void LCD_Reset(void)
{
	LCD_RES_0;
	delay_ms(50);
	LCD_RES_1;
	delay_ms(50);
}

这里并不涉及数据传输,只是将对应的IO置低再置高

▼初始化

[lcd.c]
/******************** TFT液晶屏初始化函数 **************************/
//液晶屏初始化 驱动:ST7789S
void LCD_Config(void)
{	
	LCD_GPIO_Config();

	//Reset before LCD Init.
	LCD_Reset();

//----------- Display and color format setting ---------//
//图像翻转等操作,见9.1.29章节
#if USE_HORIZONTAL == 0
	LCD_WR_Cmd(0x36);
	LCD_WR_Data8(0x00);
#elif USE_HORIZONTAL == 1
	LCD_WR_Cmd(0x36);
	LCD_WR_Data8(0xC0);
#elif USE_HORIZONTAL == 2
	LCD_WR_Cmd(0x36);
	LCD_WR_Data8(0x70);
#elif USE_HORIZONTAL == 3
	LCD_WR_Cmd(0x36);
	LCD_WR_Data8(0xA0);
#endif

//颜色数据格式,见8.8和9.1.33章节
	LCD_WR_Cmd(0x3A);
	LCD_WR_Data8(0x05);

//----------- ST7789S Frame rate setting ---------//
	LCD_WR_Cmd(0xB2);
	LCD_WR_Data8(0x0C);
	LCD_WR_Data8(0x0C);
	LCD_WR_Data8(0x00);
	LCD_WR_Data8(0x33);
	LCD_WR_Data8(0x33);
	
	LCD_WR_Cmd(0xB7); 
	LCD_WR_Data8(0x35);
	  
//----------- ST7789S Power setting ---------//

	LCD_WR_Cmd(0xBB);
	LCD_WR_Data8(0x19);
	
	LCD_WR_Cmd(0xC0);
	LCD_WR_Data8(0x2C);
	
	LCD_WR_Cmd(0xC2);
	LCD_WR_Data8(0x01);
	
	LCD_WR_Cmd(0xC3);
	LCD_WR_Data8(0x12);   
	
	LCD_WR_Cmd(0xC4);
	LCD_WR_Data8(0x20);  
	
	LCD_WR_Cmd(0xC6); 
	LCD_WR_Data8(0x0F);    
	
	LCD_WR_Cmd(0xD0); 
	LCD_WR_Data8(0xA4);
	LCD_WR_Data8(0xA1);

//----------- Posistive Voltage Gamma Control ---------//
//9.2.19 伽马调整,用于调节色彩对比度等参数
	LCD_WR_Cmd(0xE0);
	LCD_WR_Data8(0xD0);
	LCD_WR_Data8(0x04);
	LCD_WR_Data8(0x0D);
	LCD_WR_Data8(0x11);
	LCD_WR_Data8(0x13);
	LCD_WR_Data8(0x2B);
	LCD_WR_Data8(0x3F);
	LCD_WR_Data8(0x54);
	LCD_WR_Data8(0x4C);
	LCD_WR_Data8(0x18);
	LCD_WR_Data8(0x0D);
	LCD_WR_Data8(0x0B);
	LCD_WR_Data8(0x1F);
	LCD_WR_Data8(0x23);

	LCD_WR_Cmd(0xE1);
	LCD_WR_Data8(0xD0);
	LCD_WR_Data8(0x04);
	LCD_WR_Data8(0x0C);
	LCD_WR_Data8(0x11);
	LCD_WR_Data8(0x13);
	LCD_WR_Data8(0x2C);
	LCD_WR_Data8(0x3F);
	LCD_WR_Data8(0x44);
	LCD_WR_Data8(0x51);
	LCD_WR_Data8(0x2F);
	LCD_WR_Data8(0x1F);
	LCD_WR_Data8(0x1F);
	LCD_WR_Data8(0x20);
	LCD_WR_Data8(0x23);

//----------- Setting ---------//
	LCD_WR_Cmd(0x21); 
	
	LCD_WR_Cmd(0x11);//关闭睡眠模式

	LCD_WR_Cmd(0x29);
}

LCD初始化配置是根据中景园例程修改的,电压设置和伽马色彩调整部分不解释,需要注意的是颜色数据格式和屏幕方向设置

颜色数据格式(0x3A):

//颜色数据格式,见8.8和9.1.33章节
	LCD_WR_Cmd(0x3A);
	LCD_WR_Data8(0x05);

对应的寄存器是0x3A
在ST7789的datasheet中8.8章节DATA COLOR CODING 对各种控制方式下的色彩模式有详述的描写,在8.8.40小节是关于4-line SPI的色彩描述:

配置的参数是0x05,就是配置成65k真彩色,颜色格式是5:6:5的RGB比例
如果要显示图片,取模时就要注意按照这个颜色格式来取模了。

还记得在LCD数据写入小节中,发送2byte数据的写入函数吗?那个函数是配合这个格式来使用的,5-6-5的颜色是16bit(2byte),色彩也比4k彩色多,而262k则需要18bit的大小,数据传输不方便(如果用模拟SPI的话可以自己定制单次传输的数据长度)

屏幕方向(0x36):

//----------- Display and color format setting ---------//
//图像翻转等操作,见9.1.28章节
#if USE_HORIZONTAL == 0
	LCD_WR_Cmd(0x36);
	LCD_WR_Data8(0x00);
#elif USE_HORIZONTAL == 1
	LCD_WR_Cmd(0x36);
	LCD_WR_Data8(0xC0);
#elif USE_HORIZONTAL == 2
	LCD_WR_Cmd(0x36);
	LCD_WR_Data8(0x70);
#elif USE_HORIZONTAL == 3
	LCD_WR_Cmd(0x36);
	LCD_WR_Data8(0xA0);
#endif

这部分介绍见9.1.28章节▼

主要是对[7:1]位进行配置,具体效果大家可以一位位的尝试,最终可以得出四个方向对应的值,分别是
0x00
0xC0
0x70
0xA0
当然,正确的数据配置还能实现X/Y轴翻转,实现镜像等操作,在8.12章节有很直观的图片说明。

▼下面是用来判断使用哪个方向的宏定义,该屏幕是240x240的分辨率,在横竖屏下长宽都一样,不同尺寸的屏幕还需注意下,长宽在屏幕显示方向设置中很重要

[lcd.h]
//----------------- 屏幕方向 -----------------//
#define USE_HORIZONTAL 1  //设置横屏或者竖屏显示 0或1为竖屏 2或3为横屏

#if USE_HORIZONTAL == 0 || USE_HORIZONTAL == 1
#define LCD_W 240
#define LCD_H 240

#else
#define LCD_W 240
#define LCD_H 240
#endif

初始化配置其他寄存器的值不再深入描述,想了解的看datasheet的第9章节【COMMAND 】

2.5、LCD显示

在完成LCD的配置后,要想在屏幕上显示东西,只须实现两个步骤
(1)设置显示区域
(2)发送显示内容

设置显示区域
[lcd.h]
/********************************************************************************
  * @brief  设置显示区域
  * @param  (x0,y0)		起点坐标
  * @param  (x1,y1)		终点坐标
  * @retval none
  *******************************************************************************/
void LCD_SetRegion(u16 x0, u16 y0, u16 x1, u16 y1)
{
#if USE_HORIZONTAL == 1
	
	y0 = y0 + 80;		//Y轴中心点偏移80格
	y1 = y1 + 80;
	
#elif USE_HORIZONTAL == 3

	x0 = x0 + 80;		//X轴中心点偏移80格
	x1 = x1 + 80;
	
#endif
	
	LCD_WR_Cmd(0x2a);//列地址设置
	LCD_WR_Data16(x0);
	LCD_WR_Data16(x1);
	LCD_WR_Cmd(0x2b);//行地址设置
	LCD_WR_Data16(y0);
	LCD_WR_Data16(y1);
	LCD_WR_Cmd(0x2c);//储存器写
	
}

首先忽略【#if】到【#endif】之间的内容,可以看到发送了三个寄存器地址:
【0x2a】见9.1.20章节
【0x2b】见9.1.21章节
【0x2c】见9.1.22章节

▼发送0x2a指令,再发送两个数据,包含4byte数据,设置显示的起始列和结束列

▼发送0x2b指令,再发送两个数据,包含4byte数据,设置显示的起始行和结束行

▼发送0x2a指令,则是告诉ST7789,下次进来的数据是用来更新显存RAM的数据,也就是更新每个像素点的颜色信息


中心点偏移:
现在回到【#if】到【#endif】这段内容,为啥在不同的方向下要偏移80格?
这就是一个比较有趣的问题了,先了解一些内容:
(1) 宏定义▼

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

(2) 在初始化中关于方向设置里提到的8.12章节里有对显示方向的描述,这里是其中一部分的截图▼

当【USE_HORIZONTAL】的值为0的时候,对应的是上图的【Normal】情况
当【USE_HORIZONTAL】的值为1的时候,对应的是上图的【X-Mirror Y-Mirror】情况,"F"旋转180°

这里定义这个方向为竖屏

(3) ST7789芯片描述▼

很明显ST7789是可以驱动240x320像素点的一款芯片,而该屏幕只用到240x240,还剩下了240x80个

相信现在大家能知道这80格偏移是怎么来的了吧

以【Normal】为竖屏的正方向来说明(下图左),实际在屏幕设计生产时,芯片的显存RAM的映射到对应到屏幕的点阵是固定好的,无法通过软件修改,是硬件上的连接▼

由于映射是固定的,在修改显示方向的时候(上图右),RAM的起点(0,0)移到右下角,RAM上对应的有效显示区域起点不再是(0,0),而是(0,80),因此在Y方向上需要偏移80格,把第一个像素点的颜色数据写到RAM上的(0,80)上,才能正常显示

对于横屏也是如此,可根据8.12章节图表自行分析

需要注意的是,这个偏移是由屏幕生产厂商决定的,不同厂商的映射地址不一定是相同的,对于上图左边的正常模式,也有可能是RAM的(0,20)点对应屏幕的(0,0)点,因此配套的资料和例程很重要,不然得自己去测试究竟偏移了多少

▲上图右边是本人手上另一块屏幕,中景园的0.96寸彩色LCD,ST7735驱动芯片,最大可支持像素点为132x162,但是屏幕分辨率为80x160,RAM和屏幕点阵映射对应关系如图(在配套的例程中有给出),是RAM的(26,1)对应屏幕点阵的(0,0),因此四个方向的中心点偏移都是不同的

到此,显示的第一步:设置显示区域完成

发送显示内容

设置好区域后,只需要往里边发送数据,就能在该区域内显示,每2byte数据对应一个像素点的颜色信息,数据是【从左到右,从上到下】地连续写入每个像素点,在显示区域自动换行,直到显示区域的最后一个像素点,若此时继续写入数据,则从显示区域的第一个像素点开始重新写入

[lcd.c]
/********************************************************************************
  * @brief  整屏刷单色
  * @param  color	颜色
  * @retval none
  *******************************************************************************/
void LCD_Single_Color(u16 color)
{
 	u8 i,j;
	LCD_SetRegion(0,0,LCD_W,LCD_H);
 	for (i = 0; i < LCD_W; i++)
    	for (j = 0; j < LCD_H; j++)
        	LCD_WR_Data16(color);
}

这是一个整屏刷单色的实现函数,其中LCD_W和LCD_H是之前出现的屏幕高宽宏定义

3、总结

除去SPI配置和LCD对应IO口配置外,本文给出了以下函数

void LCD_WR_Cmd(u8 Data);
void LCD_WR_Data8(u8 Data);
void LCD_WR_Data16(u16 Data);
void LCD_Config(void);
void LCD_SetRegion(u16 x0, u16 y0, u16 x1, u16 y1);
void LCD_Single_Color(u16 color);

在main函数里配置好 LCD_Config();后
调用函数LCD_Single_Color(YELLOW);即可以整屏刷颜色

这是对LCD屏幕的基础配置,对于不同尺寸的LCD屏幕配置大同小异,查看驱动芯片数据手册屏幕出厂手册,了解了其中的各种细节,基本上不会有问题

对于这个1.3寸屏幕,本文只是进行配置以及简单的单色刷写,其价值并没有体现出来,下一篇将会讲解如何显示图片。

4、附录

资料下载地址
ST7789_datasheet

  • 24
    点赞
  • 132
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值