LVGL+FreeRTOS实战项目:智能健康助手(lcd篇)
1.8寸彩色TFT显示屏简介
接线图
我们选用的是分辨率为128*160的彩色显示屏,采用的SPI接口,通过我们STM32的SPI外设,来和我们的屏幕进行通信,以显示我们需要显示的图片。
软件部分
#include "lcd_driver.h"
//液晶IO初始化配置
void LCD_Driver_Init(void)
{
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能 LCD_SPI 及GPIO 时钟 */
/*!< SPI_LCD_SPI_CS_GPIO, SPI_LCD_SPI_MOSI_GPIO,
SPI_LCD_SPI_MISO_GPIO,SPI_LCD_SPI_SCK_GPIO 时钟使能 */
RCC_AHB1PeriphClockCmd (LCD_SPI_SCK_GPIO_CLK|LCD_SPI_MOSI_GPIO_CLK|LCD_CS_GPIO_CLK, ENABLE);
/*!< SPI_LCD_SPI 时钟使能 */
LCD_SPI_CLK_INIT(LCD_SPI_CLK, ENABLE);
//设置引脚复用
GPIO_PinAFConfig(LCD_SPI_SCK_GPIO_PORT,LCD_SPI_SCK_PINSOURCE,LCD_SPI_SCK_AF);
GPIO_PinAFConfig(LCD_SPI_MOSI_GPIO_PORT,LCD_SPI_MOSI_PINSOURCE,LCD_SPI_MOSI_AF);
GPIO_PinAFConfig(LCD_CS_GPIO_PORT,LCD_SPI_CS_PINSOURCE,LCD_SPI_CS_AF);
/*!< 配置 SPI_LCD_SPI 引脚: SCK */
GPIO_InitStructure.GPIO_Pin = LCD_SPI_SCK_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(LCD_SPI_SCK_GPIO_PORT, &GPIO_InitStructure);
/*!< 配置 SPI_LCD_SPI 引脚: MOSI */
GPIO_InitStructure.GPIO_Pin = LCD_SPI_MOSI_PIN;
GPIO_Init(LCD_SPI_MOSI_GPIO_PORT, &GPIO_InitStructure);
/*!< 配置 SPI_LCD_SPI 引脚: CS */
GPIO_InitStructure.GPIO_Pin = LCD_CS_PIN;
GPIO_Init(LCD_CS_GPIO_PORT, &GPIO_InitStructure);
/*!< 配置 SPI_LCD_SPI 引脚: DC */
GPIO_InitStructure.GPIO_Pin = LCD_DC_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Init(LCD_DC_GPIO_PORT, &GPIO_InitStructure);
/*!< 配置 SPI_LCD_SPI 引脚: RES */
GPIO_InitStructure.GPIO_Pin = LCD_RES_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Init(LCD_RES_GPIO_PORT, &GPIO_InitStructure);
/*!< 配置 SPI_LCD_SPI 引脚: BLK */
GPIO_InitStructure.GPIO_Pin = LCD_BLK_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Init(LCD_BLK_GPIO_PORT, &GPIO_InitStructure);
SPI_LCD_BLK_HIGH();
/* 停止信号 LCD: CS引脚高电平*/
//SPI_LCD_CS_HIGH();
/* LCD_SPI 模式配置 */
// LCD芯片 支持SPI模式0及模式3,据此设置CPOL CPHA
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(LCD_SPI, &SPI_InitStructure);
/* 使能 LCD_SPI */
SPI_Cmd(LCD_SPI, ENABLE);
SPI_SSOutputCmd(LCD_SPI,ENABLE);
SPI_CalculateCRC(LCD_SPI,DISABLE);
SPI_ClearFlag(LCD_SPI,SPI_FLAG_TXE);
SPI_ClearFlag(LCD_SPI,SPI_FLAG_RXNE);
}
//向SPI总线传输一个8位数据
void SPI_WriteData(u8 Data)
{
while(SPI_I2S_GetFlagStatus(LCD_SPI,SPI_I2S_FLAG_TXE)==RESET);
SPI_SendData(LCD_SPI,Data);
// unsigned char i=0;
// for(i=8;i>0;i--)
// {
// if(Data&0x80)
// LCD_SDA_SET; //输出数据
// else LCD_SDA_CLR;
//
// LCD_SCL_CLR;
// LCD_SCL_SET;
// Data<<=1;
// }
}
//向液晶屏写一个8位指令
void Lcd_WriteIndex(u8 Index)
{
//SPI 写命令时序开始
SPI_LCD_CS_LOW();
SPI_LCD_DC_LOW();
SPI_WriteData(Index);
SPI_LCD_CS_HIGH();
}
//向液晶屏写一个8位数据
void Lcd_WriteData(u8 Data)
{
SPI_LCD_CS_LOW();
SPI_LCD_DC_HIGH();
SPI_WriteData(Data);
SPI_LCD_CS_HIGH();
}
//向液晶屏写一个16位数据
void LCD_WriteData_16Bit(u16 Data)
{
SPI_LCD_CS_LOW();
SPI_LCD_DC_HIGH();
SPI_WriteData(Data>>8); //写入高8位数据
SPI_WriteData(Data); //写入低8位数据
SPI_LCD_CS_HIGH();
}
void Lcd_WriteReg(u8 Index,u8 Data)
{
Lcd_WriteIndex(Index);
Lcd_WriteData(Data);
}
void Lcd_Reset(void)
{
SPI_LCD_RES_LOW();
delay_ms(100);
SPI_LCD_RES_HIGH();
delay_ms(50);
}
//LCD Init For 1.44Inch LCD Panel with ST7735R.
void Lcd_Init(void)
{
LCD_Driver_Init();
Lcd_Reset(); //Reset before LCD Init.
//LCD Init For 1.44Inch LCD Panel with ST7735R.
Lcd_WriteIndex(0x11);//Sleep exit
delay_ms (120);
//ST7735R Frame Rate
Lcd_WriteIndex(0xB1);
Lcd_WriteData(0x01);
Lcd_WriteData(0x2C);
Lcd_WriteData(0x2D);
Lcd_WriteIndex(0xB2);
Lcd_WriteData(0x01);
Lcd_WriteData(0x2C);
Lcd_WriteData(0x2D);
Lcd_WriteIndex(0xB3);
Lcd_WriteData(0x01);
Lcd_WriteData(0x2C);
Lcd_WriteData(0x2D);
Lcd_WriteData(0x01);
Lcd_WriteData(0x2C);
Lcd_WriteData(0x2D);
Lcd_WriteIndex(0xB4); //Column inversion
Lcd_WriteData(0x07);
//ST7735R Power Sequence
Lcd_WriteIndex(0xC0);
Lcd_WriteData(0xA2);
Lcd_WriteData(0x02);
Lcd_WriteData(0x84);
Lcd_WriteIndex(0xC1);
Lcd_WriteData(0xC5);
Lcd_WriteIndex(0xC2);
Lcd_WriteData(0x0A);
Lcd_WriteData(0x00);
Lcd_WriteIndex(0xC3);
Lcd_WriteData(0x8A);
Lcd_WriteData(0x2A);
Lcd_WriteIndex(0xC4);
Lcd_WriteData(0x8A);
Lcd_WriteData(0xEE);
Lcd_WriteIndex(0xC5); //VCOM
Lcd_WriteData(0x0E);
Lcd_WriteIndex(0x36); //MX, MY, RGB mode
Lcd_WriteData(0xC0);
//ST7735R Gamma Sequence
Lcd_WriteIndex(0xe0);
Lcd_WriteData(0x0f);
Lcd_WriteData(0x1a);
Lcd_WriteData(0x0f);
Lcd_WriteData(0x18);
Lcd_WriteData(0x2f);
Lcd_WriteData(0x28);
Lcd_WriteData(0x20);
Lcd_WriteData(0x22);
Lcd_WriteData(0x1f);
Lcd_WriteData(0x1b);
Lcd_WriteData(0x23);
Lcd_WriteData(0x37);
Lcd_WriteData(0x00);
Lcd_WriteData(0x07);
Lcd_WriteData(0x02);
Lcd_WriteData(0x10);
Lcd_WriteIndex(0xe1);
Lcd_WriteData(0x0f);
Lcd_WriteData(0x1b);
Lcd_WriteData(0x0f);
Lcd_WriteData(0x17);
Lcd_WriteData(0x33);
Lcd_WriteData(0x2c);
Lcd_WriteData(0x29);
Lcd_WriteData(0x2e);
Lcd_WriteData(0x30);
Lcd_WriteData(0x30);
Lcd_WriteData(0x39);
Lcd_WriteData(0x3f);
Lcd_WriteData(0x00);
Lcd_WriteData(0x07);
Lcd_WriteData(0x03);
Lcd_WriteData(0x10);
Lcd_WriteIndex(0x2a);
Lcd_WriteData(0x00);
Lcd_WriteData(0x00);
Lcd_WriteData(0x00);
Lcd_WriteData(0x7f);
Lcd_WriteIndex(0x2b);
Lcd_WriteData(0x00);
Lcd_WriteData(0x00);
Lcd_WriteData(0x00);
Lcd_WriteData(0x9f);
Lcd_WriteIndex(0xF0); //Enable test command
Lcd_WriteData(0x01);
Lcd_WriteIndex(0xF6); //Disable ram power save mode
Lcd_WriteData(0x00);
Lcd_WriteIndex(0x3A); //65k mode
Lcd_WriteData(0x05);
Lcd_WriteIndex(0x29);//Display on
}
/*************************************************
函数名:LCD_Set_Region
功能:设置lcd显示区域,在此区域写点数据自动换行
入口参数:xy起点和终点
返回值:无
*************************************************/
void Lcd_SetRegion(u16 x_start,u16 y_start,u16 x_end,u16 y_end)
{
Lcd_WriteIndex(0x2a);
Lcd_WriteData(0x00);
Lcd_WriteData(x_start);//Lcd_WriteData(x_start+2);
Lcd_WriteData(0x00);
Lcd_WriteData(x_end+2);
Lcd_WriteIndex(0x2b);
Lcd_WriteData(0x00);
Lcd_WriteData(y_start+0);
Lcd_WriteData(0x00);
Lcd_WriteData(y_end+1);
Lcd_WriteIndex(0x2c);
}
/*************************************************
函数名:LCD_Set_XY
功能:设置lcd显示起始点
入口参数:xy坐标
返回值:无
*************************************************/
void Lcd_SetXY(u16 x,u16 y)
{
Lcd_SetRegion(x,y,x,y);
}
/*************************************************
函数名:LCD_DrawPoint
功能:画一个点
入口参数:无
返回值:无
*************************************************/
void Gui_DrawPoint(u16 x,u16 y,u16 Data)
{
Lcd_SetRegion(x,y,x+1,y+1);
LCD_WriteData_16Bit(Data);
}
/*****************************************
函数功能:读TFT某一点的颜色
出口参数:color 点颜色值
******************************************/
unsigned int Lcd_ReadPoint(u16 x,u16 y)
{
unsigned int Data;
Lcd_SetXY(x,y);
//Lcd_ReadData();//丢掉无用字节
//Data=Lcd_ReadData();
Lcd_WriteData(Data);
return Data;
}
/*************************************************
函数名:Lcd_Clear
功能:全屏清屏函数
入口参数:填充颜色COLOR
返回值:无
*************************************************/
void Lcd_Clear(u16 Color)
{
unsigned int i,m;
Lcd_SetRegion(0,0,X_MAX_PIXEL-1,Y_MAX_PIXEL-1);
Lcd_WriteIndex(0x2C);
for(i=0;i<X_MAX_PIXEL;i++)
for(m=0;m<Y_MAX_PIXEL;m++)
{
LCD_WriteData_16Bit(Color);
}
}
void LCD_Show_Info(uint16_t x, uint16_t y, const lv_font_t *font, const lv_color_t value, const char *format, ...)
{
static lv_obj_t * label = NULL;
char buf[128]; // 定义一个足够大的缓冲区用于存储格式化后的字符串
va_list args;
va_start(args, format); // 初始化可变参数列表
vsnprintf(buf, sizeof(buf), format, args); // 将格式化后的字符串存储到缓冲区
va_end(args); // 结束可变参数处理
if (label != NULL) {
// 清除旧标签内容
lv_obj_del(label); // 删除标签
label = NULL;
}
//lv_obj_t * label = lv_label_create(lv_scr_act());
label = lv_label_create(lv_scr_act());
lv_label_set_text(label, buf);
static lv_style_t label_style;
lv_style_init(&label_style);
lv_style_set_text_font(&label_style, font); // 设置字体
lv_style_set_text_color(&label_style, value); // 设置字体颜色
lv_obj_add_style(label, &label_style, LV_PART_MAIN);
lv_obj_align(label, LV_ALIGN_OUT_LEFT_TOP, x, y);
//Gui_DrawFont_GBK16(x, y, BLUE, GRAY0, buf); // 调用显示函数,将格式化后的字符串显示到 LCD 上
}
液晶驱动初始化
LCD_Driver_Init 函数是液晶屏初始化的关键,负责:
启用SPI时钟和GPIO时钟。
设置SPI接口的工作模式。
配置液晶的复位、显示控制等GPIO引脚。
配置SPI工作模式:设置数据方向、数据位宽、时钟极性和相位等。
LCD初始化
Lcd_Init 函数通过对LCD发送一系列初始化命令,配置LCD的显示模式、亮度、对比度等参数。包括:
退出休眠模式,恢复显示。
设置显示区域、显示方向、RGB模式。
配置驱动IC的Gamma曲线,以提高显示质量。
设置显示区域
Lcd_SetRegion 函数用于设置LCD的显示区域。通过传递起始和结束坐标来指定需要显示内容的区域,减少不必要的刷新,提高显示效率。
绘制点
Gui_DrawPoint 函数用于在指定坐标位置绘制一个点。首先通过 Lcd_SetRegion 设置显示区域,然后使用 LCD_WriteData_16Bit 向LCD写入颜色数据。颜色值以16位数据格式(RGB565)表示。
清屏功能
Lcd_Clear 函数实现了全屏清除功能。它通过设置整个屏幕为指定颜色来清除显示内容。此函数可以用来在显示新内容之前清空屏幕。
显示文本
LCD_Show_Info 函数用于将格式化的文本显示在LCD屏幕上。它通过 vsnprintf 将传入的文本格式化后显示,支持动态变化的文本内容。
总结
其实这个非常简单,这些代码也不可能是我们全部自己手敲的,我们买到屏幕,向厂家要个驱动代码就行,由于代码封装的非常好,我们只需要根据我们的接线,修改对应的宏就行了,修改宏地方如下:
移植完之后,我们之后只要调用对应的函数来显示我们的内容即可,由于我们用的是LVGL,我们只需要告诉LVGL打点函数,之后他就会根据我们给的打点函数,来绘制出我们需要的GUI界面了,这个我们在学习LVGL的时候应该学习过,如图所示:
做完这最后一步,我们就成功移植了我们的屏幕了到LVGL上面了,非常简单。