2-1嵌入式进阶指南:P169H002 基于 ST7789T3 和 CST816D 驱动的 LCD 触摸显示屏开发手册(3)
基于P169H002的LCD 触摸显示屏的驱动板制作
学会驱动板制作就可以将LCD 触摸显示屏添加到你的任何项目中。
本教程覆盖全面,看完即可自己从0完成,包含了:
硬件: 原理图、PCB 、器件选型。
软件:新建工程、LCD底层驱动、触摸屏底层驱动、LVGL的UI界面。
本文是基于P169H002的LCD触摸显示屏的第三部分。
如果有需要可以点击下方链接跳转
前一节:第二部分:软件底层基础实现
后一节:触摸屏的驱动程序 (未完待续。。。)
第三部分:LCD屏幕驱动程序编写
在这之前,我们已经完成了P169H002的主控板制作,通过软件验证了硬件没有问题之后,现在开始写LCD屏幕显示的底层驱动。完成底层驱动后可以自己设计UI界面或者利用LVGL实现界面显示。
1、PWM调光
在文章最开始提到了LED背光是共阴极,所以背光引脚给低电平点亮。
但为了能调节背光的亮度(即屏幕的亮度),所以用到了MOS管控制。
①结合MOS管的共阴极LED背光调光原理
图中用到的MOS管是 N—MOS。
当LCD_BLK输出2.5V以上时,此时N—MOS导通,此时FPC连接器的17引脚连接的是P169H002屏幕模块的LED背光共阴极引脚被接通到GND,LED点亮。
所以我们就可以通过输出高电平在2.5V以上的PWM来调整LCD屏幕的亮度了。
之所以不直接用PWM连接LCD的背光引脚(FPC连接器的17引脚),是因为MCU的GPIO的电流通过能力有限,LCD屏幕的背光LED正常工作需要比较大的电流,让MOS管去承担背光电流更加安全且高效。
②定时器PWM输出初始化
TIMER.h文件
#ifndef _TIMER_H_
#define _TIMER_H_#include "stm32f4xx.h"/* 宏定义 */
#define TIM3_PERIOD 300
#define TIM3_PULSE_INIT 0/* 外部变量 */
extern TIM_HandleTypeDef TIMER3_Handle;
/* 函数声明 */
void TIM3_PWM_Init(void);#endif
TIMER.c文件
#include "TIMER.h"TIM_HandleTypeDef TIMER3_Handle;void TIM3_PWM_Init(void)
{TIM_OC_InitTypeDef TIM_OC_InitStructure;/* 时基单元配置 */TIMER3_Handle.Instance = TIM3;TIMER3_Handle.Init.Prescaler = 100 - 1; //PSCTIMER3_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;TIMER3_Handle.Init.Period = TIM3_PERIOD; //ARRTIMER3_Handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;TIMER3_Handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;HAL_TIM_PWM_Init(&TIMER3_Handle);/* 配置PWM输出通道 */TIM_OC_InitStructure.OCMode = TIM_OCMODE_PWM1;TIM_OC_InitStructure.Pulse = TIM3_PULSE_INIT;TIM_OC_InitStructure.OCPolarity = TIM_OCPOLARITY_HIGH;TIM_OC_InitStructure.OCFastMode = TIM_OCFAST_DISABLE;HAL_TIM_PWM_ConfigChannel(&TIMER3_Handle,&TIM_OC_InitStructure,TIM_CHANNEL_3);
}void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{GPIO_InitTypeDef GPIO_InitStructure;if(htim->Instance == TIM3){__HAL_RCC_TIM3_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();GPIO_InitStructure.Pin = GPIO_PIN_0;GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;GPIO_InitStructure.Alternate = GPIO_AF2_TIM3;HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);}
}
2、通信驱动
P169H022这款屏幕采用的是ST7789T3 4线串行通信模式(可以用硬件SPI或软件模拟来实现),这样的优点是实现简单,但是串行通信的速率远低于并行通信。
①HAL库的SPI初始化
通过 4线串行通信 的时序图可以得出如下配置
(1)SPI模式配置:
采用硬件SPI进行如下配置:
SPI_MODE_MASTER:SPI主机模式。
SPI_DIRECTION_2LINES:只用到MOSI但这样配置适配4线串行通信。
SPI_DATASIZE_8BIT:一次传输8位
SPI_POLARITY_HIGH:CPOL = 1,时钟空闲时为高电平。
SPI_PHASE_2EDGE:CPHA = 1,数据在时钟的第二个边沿采样。
SPI_FIRSTBIT_MSB:高位先行。
SPI_NSS_SOFT:软件控制CS片选。
SPI_BAUDRATEPRESCALER_2:SPI时钟频率 = 系统时钟频率 / 2。
其他配置:
TIMode:禁用TI帧格式模式,用标准的摩托罗拉SPI帧格式。
CRCCalculation:禁用CRC校验字节,对于显示屏驱动通常不需要。
CRCPolynomial:即使CRC计算被禁用,这个值仍然需要设置。
(2)SPI外设相关文件
SPI.h文件
#ifndef _SPI_H_
#define _SPI_H_#include "stm32f4xx.h"/* ST7789T3底层驱动方式 */
#define DRIVER_MODE HARDWARE_SPI
#define HARDWARE_SPI 0
#define SOFTWARE_SPI 1/* 外部变量声明 */
extern SPI_HandleTypeDef SPI_ST7789T3;
extern DMA_HandleTypeDef DMA_SPI1_TX;/* 函数声明 */
void SPI_Init(void);
void SPI_Write_Byte(uint8_t Byte);
#endif
SPI.c文件
#include "SPI.h"SPI_HandleTypeDef SPI_ST7789T3;
DMA_HandleTypeDef DMA_SPI1_TX;void SPI_Init(void)
{SPI_ST7789T3.Instance = SPI1;SPI_ST7789T3.Init.Mode = SPI_MODE_MASTER;SPI_ST7789T3.Init.Direction = SPI_DIRECTION_2LINES;SPI_ST7789T3.Init.DataSize = SPI_DATASIZE_8BIT;SPI_ST7789T3.Init.CLKPolarity = SPI_POLARITY_HIGH;SPI_ST7789T3.Init.CLKPhase = SPI_PHASE_2EDGE;SPI_ST7789T3.Init.NSS = SPI_NSS_SOFT;SPI_ST7789T3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;SPI_ST7789T3.Init.FirstBit = SPI_FIRSTBIT_MSB;SPI_ST7789T3.Init.TIMode = SPI_TIMODE_DISABLE;SPI_ST7789T3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;SPI_ST7789T3.Init.CRCPolynomial = 10;HAL_SPI_Init(&SPI_ST7789T3);
}void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{GPIO_InitTypeDef GPIO_InitStructure;if(hspi->Instance == SPI1){__HAL_RCC_SPI1_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();__HAL_RCC_DMA2_CLK_ENABLE();//SDA和SCLK引脚配置GPIO_InitStructure.Pin = GPIO_PIN_3|GPIO_PIN_5;GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;GPIO_InitStructure.Pull = GPIO_NOPULL;GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStructure.Alternate = GPIO_AF5_SPI1;HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);//DMA配置DMA_SPI1_TX.Instance = DMA2_Stream2;DMA_SPI1_TX.Init.Channel = DMA_CHANNEL_2;DMA_SPI1_TX.Init.Direction = DMA_MEMORY_TO_PERIPH;DMA_SPI1_TX.Init.PeriphInc = DMA_PINC_DISABLE;DMA_SPI1_TX.Init.MemInc = DMA_MINC_ENABLE;DMA_SPI1_TX.Init.MemDataAlignment = DMA_PDATAALIGN_HALFWORD;DMA_SPI1_TX.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;DMA_SPI1_TX.Init.Mode = DMA_NORMAL;DMA_SPI1_TX.Init.Priority = DMA_PRIORITY_LOW;DMA_SPI1_TX.Init.FIFOMode = DMA_FIFOMODE_DISABLE;HAL_DMA_Init(&DMA_SPI1_TX);__HAL_LINKDMA(&SPI_ST7789T3,hdmatx,DMA_SPI1_TX);HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 5, 0);HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);}
}//*****************************
/*** @brief ST7789T3_4线串行Write函数* @param Byte:要写入的一字节数据* @retval None
*/
//*****************************
void SPI_Write_Byte(uint8_t Byte)
{#if DRIVER_MODE == HARDWARE_SPI //硬件SPIHAL_SPI_Transmit(&SPI_ST7789T3,&Byte,1,1);#else //软件SPIuint8_t i;for(i = 0; i < 8; i++){ST7789T3_SCLK_RESET(); //SCLK高读取数据,低写入数据if(Byte & 0x80) //高位先行 MSB{ST7789T3_SDA_SET(); }else{ST7789T3_SDA_RESET();}ST7789T3_SCLK_SET();Byte <<= 1; //左移一位,继续写入Bit}#endif//采用条件编译,让底层通信可选择软件或硬件来驱动LCD屏幕。
}
②ST7789T3驱动程序
用SPI与ST7789T3按芯片厂商的规定来通信。
(1)LCD显示方式
写一个LCD屏幕的驱动第一步就是考虑到屏幕显示方式的问题。
利用宏定义来定义好屏幕驱动的大小、方向(竖向横行)和显存写入顺序(eg:先行后列)。
(2)硬件接口宏定义
用宏定义替代具体的GPIO接口可以让程序复用性提高。
还有API宏来代替常用操作可以提高效率和可读性。
(3)阅读芯片手册控制LCD屏幕
利用芯片厂商提供的命令来让LCD显示我们希望的图像。
阅读芯片手册我们才知道初始化LCD屏幕我们需要配置哪些参数。
但是LCD显示时有很多的屏幕专业性参数,如果看不懂可以直接用默认值,不必纠结,除非你对显示性能有极高的要求(eg:低功耗,清晰度,屏幕刷新速度),这些要是展开的话就太多了,感兴趣可以自己仔细研究一下各各参数。
用到的命令配置后边我都写了注释,可以大致理解命令在干什么。
(4)ST7789T3驱动相关文件
ST7789T3.h文件
#ifndef _ST7789T3_H_
#define _ST7789T3_H_/* 头文件包含 */
#include "stm32f4xx.h"
#include "SPI.h"
#include "TIMER.h"/* LCD屏幕参数 */
#define USE_HORIZONTAL 0 //0,1为竖屏。2,3为横屏#if USE_HORIZONTAL == 0 || USE_HORIZONTAL == 1#define LCD_W 240#define LCD_H 280
#else#define LCD_W 280#define LCD_H 240
#endif/* -----------------ST7789T3硬件接口----------------- */
#define ST7789T3_SCLK_PORT GPIOB
#define ST7789T3_SCLK_PIN GPIO_PIN_3#define ST7789T3_SDA_PORT GPIOB
#define ST7789T3_SDA_PIN GPIO_PIN_5#define ST7789T3_RST_PORT GPIOB
#define ST7789T3_RST_PIN GPIO_PIN_7#define ST7789T3_DC_PORT GPIOB
#define ST7789T3_DC_PIN GPIO_PIN_9#define ST7789T3_CS_PORT GPIOB
#define ST7789T3_CS_PIN GPIO_PIN_8#define LCD_BLK_PORT GPIOB
#define LCD_BLK_PIN GPIO_PIN_0S//API宏定义
#define ST7789T3_SCLK_RESET() HAL_GPIO_WritePin(ST7789T3_SCLK_PORT,ST7789T3_SCLK_PIN,GPIO_PIN_RESET)
#define ST7789T3_SCLK_SET() HAL_GPIO_WritePin(ST7789T3_SCLK_PORT,ST7789T3_SCLK_PIN,GPIO_PIN_SET)#define ST7789T3_SDA_RESET() HAL_GPIO_WritePin(ST7789T3_SDA_PORT,ST7789T3_SDA_PIN,GPIO_PIN_RESET)
#define ST7789T3_SDA_SET() HAL_GPIO_WritePin(ST7789T3_SDA_PORT,ST7789T3_SDA_PIN,GPIO_PIN_SET)#define ST7789T3_RST_RESET() HAL_GPIO_WritePin(ST7789T3_RST_PORT,ST7789T3_RST_PIN,GPIO_PIN_RESET)
#define ST7789T3_RST_SET() HAL_GPIO_WritePin(ST7789T3_RST_PORT,ST7789T3_RST_PIN,GPIO_PIN_SET)#define ST7789T3_DC_RESET() HAL_GPIO_WritePin(ST7789T3_DC_PORT,ST7789T3_DC_PIN,GPIO_PIN_RESET)
#define ST7789T3_DC_SET() HAL_GPIO_WritePin(ST7789T3_DC_PORT,ST7789T3_DC_PIN,GPIO_PIN_SET)#define ST7789T3_CS_RESET() HAL_GPIO_WritePin(ST7789T3_CS_PORT,ST7789T3_CS_PIN,GPIO_PIN_RESET)
#define ST7789T3_CS_SET() HAL_GPIO_WritePin(ST7789T3_CS_PORT,ST7789T3_CS_PIN,GPIO_PIN_SET)#define LCD_BLK_RESET() HAL_GPIO_WritePin(ST7789T3_BLK_PORT,ST7789T3_BLK_PIN,GPIO_PIN_RESET)
#define LCD_BLK_SET() HAL_GPIO_WritePin(ST7789T3_BLK_PORT,ST7789T3_BLK_PIN,GPIO_PIN_SET)
/* --------------------END-------------------- *//*-----------------------ST778T3命令宏定义区------------------*/
#define ST7789T3_CMD_CASET 0X2A //Column Address Set, 列地址设置
#define ST7789T3_CMD_RASET 0X2B //Row Address Set, 行地址设置
#define ST7789T3_CMD_RAMWR 0X2C //Memory write, 内存写入#define ST7789T3_CMD_MADCTL 0X36 //Memory Data Access Control,内存数据访问控制
#define ST7789T3_CMD_COLMOD 0X3A //Interface Pixel Format,界面像素格式
#define ST7789T3_CMD_PORCTRL 0XB2 //Porch Setting,设置显示时序中的"空白间隔",这些间隔虽然不显示图像,但对于确保显示稳定、同步正确和图像定位至关重要
#define ST7789T3_CMD_GCTRL 0XB7 //Gate Control,配置栅极驱动电压
#define ST7789T3_CMD_VCOMS 0XBB //VCOMS Setting,公共电极电压,施加在液晶层公共端的参考电压,用于与像素电压形成电压差来驱动液晶分子。主要影响图像闪烁、对比度、均匀性
#define ST7789T3_CMD_LCMCTRL 0XC0 //LCM Control,硬件加速、实现屏幕自动旋转、实现屏幕自动旋转等
#define ST7789T3_CMD_VDVVRHEN 0XC2 //VDV and VRH Command Enable,VDV和VRH命令启用
#define ST7789T3_CMD_VRHS 0XC3 //VRH Set,电压调节器高电平设置,影响图像高亮部分该有多亮
#define ST7789T3_CMD_VDVS 0XC4 //VDV Set,VCOM驱动电压,主要用来消除屏幕闪烁和提高显示均匀性
#define ST7789T3_CMD_FRCTRL2 0XC6 //Frame Rate Control in Normal Mode,正常模式下的帧率控制,主要用来提升显示色彩深度和平滑度
#define ST7789T3_CMD_PWCTRL1 0XD0 //Power Control 1,通过调节AVDD、AVCL和VDS来优化显示性能和功耗平衡
#define ST7789T3_CMD_PVGAMCTRL 0XE0 //Positive Voltage Gamma Control,正电压伽玛控制,影响高亮细节、亮部对比度和整体图像的亮度分布,需要与负电压Gamma配合实现完整的伽马校正
#define ST7789T3_CMD_NVGAMCTRL 0XE1 //Negative Voltage Gamma Control ,负电压伽玛控制,主要影响黑色纯度、暗部细节、阴影层次和低亮度色彩准确性,与正电压Gamma控制共同构成完整的伽马校正
#define ST7789T3_CMD_INVON 0X21 //Display Inversion On,显示反转打开,用于夜间模式、特殊视觉效果或在特定光照条件下改善可读性,但会破坏正常的色彩表现
#define ST7789T3_CMD_DISPON 0X29 //Display On,开启显示,默认关闭
/************************END**************************//*-----------------------函数声明区------------------*/
void LCD_Init(void); //LCD初始化函数
void LCD_Address_Set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2); //LCD显示范围设置函数
void ST7789T3_Write_HalfWord(uint16_t Data_HalfWord); //ST7789T3写16位数据/************************END**************************/#endif
C函数的文件就不多说了,基本上每句都有注释。
ST7789T3.c文件
#include "ST7789T3.h"/* ------------------------------------------------ST7789T3函数-------------------------------------------------- */
//*****************************
/*** @brief ST7789T3硬件GPIO初始化* @param None* @retval None
*/
//*****************************
static void ST7789T3_GPIO_Init(void)
{/* RST(复位) D/C(数据/命令) CS(片选) 引脚初始化 */__HAL_RCC_GPIOB_CLK_ENABLE();GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.Pin = ST7789T3_RST_PIN | ST7789T3_DC_PIN | ST7789T3_CS_PIN;GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);/* RST低电平复位,CS低电平选中 */HAL_GPIO_WritePin(GPIOB,ST7789T3_RST_PIN | ST7789T3_DC_PIN | ST7789T3_CS_PIN,GPIO_PIN_SET);
}//*****************************
/*** @brief ST7789T3写入8位数据函数* @param Byte:要写入的8位数据* @retval None
*/
//*****************************
static void ST7789T3_Write_Byte(uint8_t Byte)
{SPI_Write_Byte(Byte);
}//*****************************
/*** @brief ST7789T3写入16位数据函数* @param DATA_HalfWord:要写入的16位数据* @retval None
*/
//*****************************
void ST7789T3_Write_HalfWord(uint16_t Data_HalfWord)
{#if DRIVER_MODE == HARDWARE_SPI //硬件SPIuint8_t Temp[2];Temp[0] = (Data_HalfWord >> 8) & 0XFF; //16位 高8位移动低8位 与上0XFF预防错误Temp[1] = Data_HalfWord & 0XFF; //Temp[0] 存储高8位,Temp[1] 存储低8位HAL_SPI_Transmit(&SPI_ST7789T3,Temp,2,1); //软件触发 SPI 发送#else/* 调用两次发送字节函数,发送16位数据 */SPI_Write_Byte((Data_HalfWord >> 8) & 0XFF);SPI_Write_Byte(Data_HalfWord & 0XFF);#endif
}//*****************************
/*** @brief ST7789T3写入一字节命令函数* @param Command:要写入一字节的命令* @retval None
*/
//*****************************
static void ST7789T3_Write_Command(uint8_t Command)
{ST7789T3_DC_RESET(); //DC低电平,写命令ST7789T3_Write_Byte(Command);ST7789T3_DC_SET(); //DC高电平,写完命令之后,直接切换写数据,因为一般MCU发送命令后,ST7789T3准备接收数据
}//*****************************
/*** @brief ST7789T3进入睡眠模式睡眠模式:此命令使液晶显示模块进入最低功耗模式。在此模式下,DC/DC 转换器停止工作,内部振荡器停止,面板扫描停止。MCU 接口和存储器仍在工作,存储器保持其内容。* @param None* @retval None
*/
//*****************************
static void ST7789T3_SleepIn(void)
{ST7789T3_Write_Command(0X10);//在发送此命令后,需要等待,才能向模块发送任何新命令,以便供电电压和时钟电路稳定。HAL_Delay(100);
}//*****************************
/*** @brief ST7789T3退出睡眠模式DC/DC转换器被启用,内部显示振荡器启动,并且面板扫描开始。* @param None* @retval None
*/
//*****************************
static void ST7789T3_SleepOut(void)
{ST7789T3_Write_Command(0X11);HAL_Delay(100);
}
/* ****************************************************END********************************************************** *//* ----------------------------------------------封装成LCD函数------------------------------------------------------ */
/* ----------------------------------------------封装成LCD函数------------------------------------------------------ */
//*****************************
/*** @brief LCD显示范围设置函数。在控制LCD显示之前都要规定好地址范围之后,填充各各点的像素。* @param X1,X2:设置列的起止地址Y1,Y2:设置行的起止地址* @retval None
*/
//*****************************
void LCD_Address_Set(uint16_t X1, uint16_t X2, uint16_t Y1, uint16_t Y2)
{/* 列地址设置,需要两个16位数据确定列地址的起止位置 */ST7789T3_Write_Command(ST7789T3_CMD_CASET); ST7789T3_Write_HalfWord(X1);ST7789T3_Write_HalfWord(X2);/* 行地址设置,也需要两个16位数据确定行地址的起止位置 */ST7789T3_Write_Command(ST7789T3_CMD_RASET);ST7789T3_Write_HalfWord(Y1);ST7789T3_Write_HalfWord(Y2);/* 内存写入 */ST7789T3_Write_Command(ST7789T3_CMD_RAMWR);/* 在发送该命令后,后续通过串行传输的所有数据,将按照此前设定的列地址、行地址依次写入帧内存的对应位置,直至发送新命令(如停止写入的命令)或地址范围结束*//* 发送 0X2C 命令后,驱动芯片进入 “数据接收就绪” 状态,此时 MCU 传输的 RGB 像素数据(如 16 位 / 18 位颜色数据)会被逐像素存储到帧内存中。 *//* 按 “先列后行” 或 “先行后列” 的顺序填充至(XE, YE)的地址范围*//* 若需写入连续区域的多个像素(如整屏图像),发送 0X2C 后可连续传输所有像素数据,无需重复发送命令,ST7789T3会自动递增地址指针,直至覆盖设定的地址范围。*/
}//*****************************
/*** @brief LCD初始化函数* @param None* @retval None
*/
//*****************************
void LCD_Init(void)
{/* 配置驱动芯片GPIO */ST7789T3_GPIO_Init();TIM3_PWM_Init();SPI_Init();/* 拉低GPIO片选,开始操作ST7789T3 */ST7789T3_CS_RESET();/* 复位操作 *///拉低复位引脚开始复位ST7789T3_RST_RESET();HAL_Delay(100);//拉高停止复位ST7789T3_RST_SET();HAL_Delay(100);/* 唤醒ST7789T3 */ST7789T3_SleepOut();/* 设置LCD画面显示方向 */ST7789T3_Write_Command(ST7789T3_CMD_MADCTL);if(USE_HORIZONTAL == 0){/* 默认方向:配置列地址从左到右递增(MX=0)行地址从上到下递增(MY=0)RGB 颜色顺序为默认(RGB=0)无地址交换(MV=0) */ST7789T3_Write_Byte(0x00);}else if(USE_HORIZONTAL == 1){/* 横向反转:通过位 7(MY=1,行地址反转,从上到下变为从下到上)和位 6(MX=1,列地址反转,从左到右变为从右到左)实现横向画面反转,同时保留 RGB 默认顺序 */ST7789T3_Write_Byte(0xC0);}else if(USE_HORIZONTAL == 2){/* 纵向正向:通过位 5(MV=1,行 / 列地址交换,实现纵向显示)位 4(ML=1,扫描方向调整)等组合适配纵向屏幕的像素扫描顺序 */ST7789T3_Write_Byte(0x70);}else{/* 备用横向方向:通过位 7(MY=1)位 4(ML=0)等组合,实现横向正向显示确保画面无反转、扫描顺序正确 */ST7789T3_Write_Byte(0xA0);}/* 配置像素格式:配置为 16 位像素格式(RGB565,65K 色) */ST7789T3_Write_Command(ST7789T3_CMD_COLMOD);ST7789T3_Write_Byte(0X05);/* Porch 设置为默认值 */ST7789T3_Write_Command(ST7789T3_CMD_PORCTRL);ST7789T3_Write_Byte(0X0C);ST7789T3_Write_Byte(0X0C);ST7789T3_Write_Byte(0X00);ST7789T3_Write_Byte(0X33);ST7789T3_Write_Byte(0X33);/* 配置栅极驱动电压为默认值 */ST7789T3_Write_Command(ST7789T3_CMD_GCTRL);ST7789T3_Write_Byte(0X35);/* 公共电极电压设置 */ST7789T3_Write_Command(ST7789T3_CMD_GCTRL);ST7789T3_Write_Byte(0X19);/* LCM Control 为默认值 */ST7789T3_Write_Command(ST7789T3_CMD_LCMCTRL);ST7789T3_Write_Byte(0X2C);/* VDV和VRH命令不启用 */ST7789T3_Write_Command(ST7789T3_CMD_VDVVRHEN);ST7789T3_Write_Byte(0X01);/* -4.45 +(vcom + vcom偏移 - vdv) */ST7789T3_Write_Command(ST7789T3_CMD_VRHS);ST7789T3_Write_Byte(0X12);/* 默认为0V */ST7789T3_Write_Command(ST7789T3_CMD_VDVS);ST7789T3_Write_Byte(0X20);/* 默认值 */ST7789T3_Write_Command(ST7789T3_CMD_FRCTRL2);ST7789T3_Write_Byte(0X0F);/* A4为默认,A1:模拟电源电压6.6V,模拟公共电压-4.6V */ST7789T3_Write_Command(ST7789T3_CMD_PWCTRL1);ST7789T3_Write_Byte(0XA4);ST7789T3_Write_Byte(0XA1);/* 根据数据手册填写 */ST7789T3_Write_Command(ST7789T3_CMD_PVGAMCTRL);ST7789T3_Write_Byte(0XD0);ST7789T3_Write_Byte(0X04);ST7789T3_Write_Byte(0X0D);ST7789T3_Write_Byte(0X11);ST7789T3_Write_Byte(0X13);ST7789T3_Write_Byte(0X2B);ST7789T3_Write_Byte(0X3F);ST7789T3_Write_Byte(0X54);ST7789T3_Write_Byte(0X4C);ST7789T3_Write_Byte(0X18);ST7789T3_Write_Byte(0x0D);ST7789T3_Write_Byte(0x0B);ST7789T3_Write_Byte(0x1F);ST7789T3_Write_Byte(0x23);/* 根据数据手册填写 */ST7789T3_Write_Command(ST7789T3_CMD_NVGAMCTRL);ST7789T3_Write_Byte(0xD0);ST7789T3_Write_Byte(0x04);ST7789T3_Write_Byte(0x0C);ST7789T3_Write_Byte(0x11);ST7789T3_Write_Byte(0x13);ST7789T3_Write_Byte(0x2C);ST7789T3_Write_Byte(0x3F);ST7789T3_Write_Byte(0x44);ST7789T3_Write_Byte(0x51);ST7789T3_Write_Byte(0x2F);ST7789T3_Write_Byte(0x1F);ST7789T3_Write_Byte(0x1F);ST7789T3_Write_Byte(0x20);ST7789T3_Write_Byte(0x23);/* 启动显示反相 */ST7789T3_Write_Command(ST7789T3_CMD_INVON);/* 开启显示,默认关闭 */ST7789T3_Write_Command(ST7789T3_CMD_DISPON);
}
/************************END**************************/
③LCD显示的简单驱动程序
(1)显存的地址偏移修正
用来校准屏幕显示位置,让图像显示在正确的位置上。
作用:有些LCD控制器内部有地址偏移;实际显示区域与逻辑坐标不一致;OFFSET_Y 用来补偿这个差异。
原因:不同LCD控制器设计不同;有些控制器从RAM某个位置开始显示;偏移量让程序员可以"骗过"控制器,实现精确定位。
OFFSET_Y就是个"位置修正值",让显示内容不会偏上或偏下,永远显示在正确位置
(2)LCD相关文件
LCD.h文件
#ifndef _LCD_H_
#define _LCD_H_/* 头文件包含 */
#include "stm32f4xx.h"
#include "ST7789T3.h"
#include "TIMER.h"/* 图像显示参数宏定义 */
#define OFFSET_Y 20 //屏幕Y轴地址偏移#define WHITE 0xFFFF
#define BLACK 0x0000
#define RED 0xF800
#define GREEN 0x07E0
#define BLUE 0x001F/* 外部变量声明 *//* 函数声明 */
void LCD_MonochromeFill(uint16_t X_Start, uint16_t X_End, uint16_t Y_Start, uint16_t Y_End, uint16_t Color);void LCD_SetLight(uint8_t BLK_Duty);
void LCD_CloseLight(void);
void LCD_OpenLight(void);
#endif
LCD.c文件
#include "LCD.h"
/*-----------------------------------------LCD背光控制------------------*/
//*****************************
/*** @brief LCD调节背光大小函数* @param BLK_Duty:设置背光的占空比 范围5 - 100* @retval None
*/
//*****************************
void LCD_SetLight(uint8_t BLK_Duty)
{if(BLK_Duty >= 5 && BLK_Duty <= 100) //保证数据在合理范围{//设置TIM3的通道3的CCR寄存器值__HAL_TIM_SetCompare(&TIMER3_Handle, TIM_CHANNEL_3, BLK_Duty * TIM3_PERIOD / 100);}
}//*****************************
/*** @brief LCD关闭背光函数* @param None* @retval None
*/
//*****************************
void LCD_CloseLight(void)
{//占空比设置为0,关闭背光————熄屏__HAL_TIM_SetCompare(&TIMER3_Handle, TIM_CHANNEL_3, 0);
}//*****************************
/*** @brief LCD开启背光* @param None* @retval None
*/
//*****************************
void LCD_OpenLight(void)
{//占空比设置为0,关闭背光————熄屏HAL_TIM_PWM_Start(&TIMER3_Handle, TIM_CHANNEL_3);
}
/**************************************************END**************************//*-----------------------LCD简单显示------------------*/
//*****************************
/*** @brief LCD单色填充函数,主要用于测试* @param XY的坐标的起止点* @retval None
*/
//*****************************
void LCD_MonochromeFill(uint16_t X_Start, uint16_t X_End, uint16_t Y_Start, uint16_t Y_End, uint16_t Color)
{uint16_t i,j;//显示范围LCD_Address_Set(X_Start,X_End,Y_Start + OFFSET_Y,Y_End + OFFSET_Y);//发送填充颜色数据for(i = Y_Start; i < Y_End; i++){for(j = X_Start; j < X_End; j++){ST7789T3_Write_HalfWord(Color);}}
}
/************************END**************************/
3、测试驱动程序 以及 程序问题的发现与解决
在完成以上步骤,我们就可以测试一下程序能不能正常驱动LCD屏幕了。
(1)、PWM调光测试:
直接在main函数中初始化定时器,随后给PWM的占空比直接给到100,看屏幕的背光亮不亮。
随后检查背光调节能不能实现。
注意:屏幕的背光亮不亮不能直接看屏幕,因为此时屏幕没有被驱动,所以要看背板有没有光发出来。
PWM测试程序
#include "main.h"uint32_t HCLK = 0;int main(void)
{/* 系统初始化 */System_Init();UART_Debug_Init();/* 启动正常提示 */HAL_Delay(50);HAL_UART_Transmit(&UART_Debug.Uart,(uint8_t *)"STM32F411RET6 开机\r\n",20,100);HAL_Delay(200);/* 初始化定时器 */TIM3_PWM_Init();HAL_Delay(100);//测试提示————————HAL_UART_Transmit(&UART_Debug.Uart,(uint8_t *)"已初始化完成\r\n",14,100);HAL_Delay(200);/* 开启背光 */LCD_OpenLight();LCD_SetLight(100);HAL_Delay(1000);//测试提示————————HAL_UART_Transmit(&UART_Debug.Uart,(uint8_t *)"背光已经开启\r\n",14,100);HAL_Delay(200);/* 测试背光调节 */LCD_SetLight(95);HAL_Delay(500);LCD_SetLight(85);HAL_Delay(500);LCD_SetLight(75);HAL_Delay(500);LCD_SetLight(65);HAL_Delay(500);LCD_SetLight(55);HAL_Delay(500);LCD_SetLight(45);HAL_Delay(500);LCD_SetLight(35);HAL_Delay(500);LCD_SetLight(25);HAL_Delay(500);LCD_SetLight(15);HAL_Delay(500);LCD_SetLight(5);HAL_Delay(500);//测试提示————————HAL_UART_Transmit(&UART_Debug.Uart,(uint8_t *)"准备关闭背光\r\n",14,100);HAL_Delay(200);/* 关闭背光 */LCD_CloseLight();HAL_Delay(500);//测试提示————————HAL_UART_Transmit(&UART_Debug.Uart,(uint8_t *)"已经关闭背光\r\n",14,100);HAL_Delay(200);
(2)、SPI通信测试
这里我用的是硬件方式验证,如果想用软件方式验证的话,可以读取ST7789T3寄存器的数据。例如通过读取芯片ID来验证SPI硬件或者软件4线串行的通信正确性。
读取模式(4线):
为了实现读取功能,MCU首先必须发送一个命令(读取 ID 或寄存器命令),然后接下来的字节将以相反的方向传输(MCU接收8bit)。
之后,CSX 必须在发送新命令之前变为高电平(见下图)。
驱动程序在 SCL 的下降沿对 SDA(输出数据)进行移位。因此,MCU要在 SCL 的上升沿进行读取。
在发送读状态命令后,SDA线必须在最后一个比特的SCL下降沿之前设置为高阻态模式。
4线串行协议(用于RDID1/RDID2/RDID3/0Ah/0Bh/0Ch/0Dh/0Eh/0Fh命令:8位读取):
(3)、LCD驱动测试程序
main.c
#include "main.h"uint32_t HCLK = 0;int main(void)
{/* 初始化 */System_Init();UART_Debug_Init();/* 启动正常提示 */HAL_Delay(50);HAL_UART_Transmit(&UART_Debug.Uart,(uint8_t *)"STM32F411RET6 开机\r\n",20,100);HAL_Delay(200);/* 初始化LCD */LCD_Init();HAL_Delay(100);//测试提示————————HAL_UART_Transmit(&UART_Debug.Uart,(uint8_t *)"已初始化完成\r\n",14,100);HAL_Delay(200);while(1){//测试提示————————HAL_UART_Transmit(&UART_Debug.Uart,(uint8_t *)"准备开启背光\r\n",14,100);HAL_Delay(200);/* LCD测试程序 *//* 开启背光 */LCD_OpenLight();LCD_SetLight(100);HAL_Delay(500);//测试提示————————HAL_UART_Transmit(&UART_Debug.Uart,(uint8_t *)"背光已经开启\r\n",14,100);HAL_Delay(200);/* 填充纯白 */LCD_MonochromeFill(0,240,0,280,WHITE);HAL_Delay(1000);//测试提示————————HAL_UART_Transmit(&UART_Debug.Uart,(uint8_t *)"白色已经填充\r\n",14,100);HAL_Delay(200);/* 填充纯黑 */LCD_MonochromeFill(0,240,0,280,BLACK);HAL_Delay(1000);//测试提示————————HAL_UART_Transmit(&UART_Debug.Uart,(uint8_t *)"黑色已经填充\r\n",14,100);HAL_Delay(200);/* 填充纯红 */LCD_MonochromeFill(0,240,0,280,RED);HAL_Delay(1000);//测试提示————————HAL_UART_Transmit(&UART_Debug.Uart,(uint8_t *)"红色已经填充\r\n",14,100);HAL_Delay(200);/* 填充纯绿 */LCD_MonochromeFill(0,240,0,280,GREEN);HAL_Delay(1000);//测试提示————————HAL_UART_Transmit(&UART_Debug.Uart,(uint8_t *)"绿色已经填充\r\n",14,100);HAL_Delay(200);/* 填充纯蓝 */LCD_MonochromeFill(0,240,0,280,BLUE);HAL_Delay(1000);//测试提示————————HAL_UART_Transmit(&UART_Debug.Uart,(uint8_t *)"蓝色已经填充\r\n",14,100);HAL_Delay(200);//测试提示————————HAL_UART_Transmit(&UART_Debug.Uart,(uint8_t *)"开始背光调节\r\n",14,100);HAL_Delay(200);/* 测试背光调节 */LCD_SetLight(85);HAL_Delay(500);LCD_SetLight(75);HAL_Delay(500);LCD_SetLight(65);HAL_Delay(500);LCD_SetLight(55);HAL_Delay(500);LCD_SetLight(45);HAL_Delay(500);LCD_SetLight(35);HAL_Delay(500);LCD_SetLight(25);HAL_Delay(500);LCD_SetLight(15);HAL_Delay(500);LCD_SetLight(5);HAL_Delay(500);//测试提示————————HAL_UART_Transmit(&UART_Debug.Uart,(uint8_t *)"准备关闭背光\r\n",14,100);HAL_Delay(200);/* 关闭背光 */LCD_CloseLight();HAL_Delay(500);//测试提示————————HAL_UART_Transmit(&UART_Debug.Uart,(uint8_t *)"已经关闭背光\r\n",14,100);HAL_Delay(200);}
}void System_Init(void)
{/* HAL库初始化 */HAL_Init();/* 选择内部高速时钟,配置为HCLK:100MHz*/HCLK = RCC_Init();/* 低功耗初始化 */LowPowerConsumption_Init();/* 初始化硬件看门狗 */HardwareWatchdog_Init();/* 关闭看门狗——1.6s复位 */XJJ_HARDWAREWATCHGOD_DISENABLE();
}
效果图片:
背光熄灭
背光亮起
LCD屏幕显示全白
至此,以上就完成了屏幕的简单驱动和测试,之后完成触摸屏的驱动程序后,会有UI界面设计的案例。
未完待续。。。
下一步部分完成触摸屏的驱动程序。
也可以收藏本文章,会接着本章内容进行更新。
可以关注我,第一时间获取最新内容。
本文结语:如果觉得不错可以点赞收藏一下,没准哪天你就用到了。