目录
1 PlatformIO工程创建
1.1 安装platformio插件
1.2 新建platformIO工程
1.3 点亮LED
2 液晶屏驱动程序的准备
2.1 液晶屏资料
2.2 液晶屏驱动程序接口
3 LVGL移植
3.1 添加lvgl库文件
3.2 修改lv_conf.h中的宏定义
3.2.1 第23行横纵方向最大像素
3.2.2 第32行颜色深度
3.2.3 第86行lvgl使用的堆栈大小修改为16Kbytes
3.2.4 第303行使用用户时钟源,修改为1
3.2.5 第542行使能浮点数打印,从1修改为0
3.3 编写lvgl的显示驱动程序
3.3.1 参考lvgl的LVGL_Arduino示例文件
3.4 将lvgl库文件从工程依赖库位置移动为自定义库
4 LVGL内存泄露测试
4.1 Lvgl对象的内存使用
4.1.1 Lv_obj对象
4.1.2 Lv_style_t 风格
4.2 Lvgl的堆栈使用统计
5 附录
5.1 LCD驱动程序
5.1.1 Mod_tft_st7789.h
5.1.2 Mod_tft_st7789.cpp
5.2 LVGL移植驱动程序测试文件main.cpp
5.3 Lvgl堆栈泄露测试
1 PlatformIO工程创建
1.1 安装platformio插件
VS Code安装完成后,在“扩展”中搜索“platformio”,在搜索结果中点击”platformIO IDE“选择安装。
1.2 新建platformIO工程
PlatformIO安装完成后,在VS Code左侧出现PlatformIO的蚂蚁图标,点击Open打开PlatformIO的主页。点击“New Project”创建新工程。
随后,弹出创建工程向导“Project Wizard”,填写项目名称,选择硬件开发板类型和框架,可以自定义工程保存的位置。
填写好必要信息后,点击完成,platformIO会自动开发板相关的库文件,第一次创建需要一些时间下载。创建完成会自动跳转到新建工程的配置文件“platformio.ini”页面,后面移植lvgl文件时需要修改这个配置文件,限制先不用管,先写一些代码测试一下编辑和下载功能。
1.3 点亮LED
打开新创建的platformio工程文件,打开在src文件夹下main.cpp文件。
在setup()函数和loop()函数中编写LED闪烁的代码;
点击左下角对号或者快捷键(ctrl+alt+B),执行编译。编译成功后可看到RAM和falsh使用情况以及“SUCCESS“字符。
接入开发板,点击左下角的箭头可下载,下载成功显示“success”字符信息。
下载成功后观察开发板,可看到led在闪烁,说明创建的工程正常。
2 液晶屏驱动程序的准备
2.1 液晶屏资料
液晶屏选用中景园 1.54寸240x240分辨率,ips lcd显示屏,驱动芯片为st7789 ,spi接口。
引脚定义如下图:
2.2 液晶屏驱动程序接口
使用方法
1、调用初始化函数
tft.init(TFT_MAX_WIDTH, TFT_MAX_HEIGHT, SPI_MODE_3);
2、设置屏幕方向
tft.setRotation(ROTATION_2);
3、在区域内填充颜色
tft.startWrite();//使能 写功能
tft.setAddrWindow(0,0,40,40);//设置填充区域
tft.writePixels(color_buff,40*40); //写入颜色缓存和缓存大小
tft.endWrite();//关闭写功能
3 LVGL移植
3.1 添加lvgl库文件
如上图,搜索到LVGL库文件,点击“lvgl by LVGL“
选择最新的7.11.0版本,选择Example中LVGL_Arduino可以查看官方的移植例程。
点击“Add to Project“,弹出对话框,选择要添加工程项目,选好后点击”Add“。因为platform需要下载lvgl相关的库文件,因此第一次添加需要等待一段时间。
添加完成后,会自动修改工程的platformio.ini文件,添加了lvgl的库依赖文件。
点击编译按钮,编译后会提示错误,即找不到“lv_conf.h’文件。
此时需要修改lvgl库文件中的lv_conf文件。
在工程中找到lv_conf_template.h文件,修改文件名称为lv_conf.h.,并把lv_conf.h的第10行设置为1.,再次编译,编译成功。
3.2 修改lv_conf.h中的宏定义
3.2.1 第23行横纵方向最大像素
3.2.2 第32行颜色深度
3.2.3 第86行lvgl使用的堆栈大小修改为16Kbytes
3.2.4 第303行使用用户时钟源,修改为1
3.2.5 第542行使能浮点数打印,从1修改为0
其他内容保持默认。
再次编译,编译成功。
3.3 编写lvgl的显示驱动程序
3.3.1 参考lvgl的LVGL_Arduino示例文件
示例文件展示了日志输出功能,显示功能和触摸输入功能的注册,这里仅使用显示功能,其他两个功能不做移植实现。
为了简化移植过程,便于发现问题,直接再main.cpp文件中做移植测试,移植成功后再封装为独立的文件。
拷贝lvgl相关的头文件与函数到main.cpp文件。
成功编译后,下载,可观察到LCD将屏幕填充为白色。
3.4 将lvgl库文件从工程依赖库位置移动为自定义库
鼠标右击打开lvgl的库文件位置
这样做的好处是,防止platformio在后期对库文件更新时造成各个版本的库不兼容。
编译后,下载。运行成功。
4 LVGL内存泄露测试
4.1 Lvgl对象的内存使用
4.1.1 Lv_obj对象
在创建lvgl的显示对象时,需要先声明一个lv_obj_t 的指针,用于存放弧线、矩形、线条等显示对象的指针,在调用lv_cont_create()函数创建对象后,lvgl会从内部堆栈中省一块内存存放obj的属性,而这个内部堆栈的大小就是之前在lv_conf.h文件中定义的,如下图
因此,当不需要显示对象时,可以调用lv_obj_del()将对象申请的缓存释放掉,并且由于lv_conf.h中默认使用“附近空闲存储自动回收功能”,在删除对象后,lvgl会自动把堆栈中释放的小块内存连接为大块内存,为后面的程序申请内存提供便利。
4.1.2 Lv_style_t 风格
Lvgl的对象显示需要使用style修饰,在使用slyle之前,需要定义全局的或者静态style变量,而且在为style添加属性前需要调用lv_style_init()对style变量初始化,其内部仅是对style占用的内存做了赋值为零的处理,而这个变量内部仅为一个uint8_t的指针,并没有申请堆栈。
通过阅读lvgl关于style的源码,发现style是动态的增大内存的,即style中添加的属性越多,style占用的内存越大,在删除style时需要释放style申请的内存。
调用lv_style_reset()可以将style申请的内存释放掉。
4.2 Lvgl的堆栈使用统计
在lvgl的lv_mem.h文件中定义了一个结构体,
这个结构体中可以存放总堆栈大小,使用率等属性。
通过调用lv_mem_monitor()函数可以动态获取lvgl的堆栈使用情况。
在执行lvgl的图形创建和删除之前调用lv_mem_monitor()函数获取堆栈使用情况,在删除图形对象之后再次调用调用lv_mem_monitor()函数,比较两次或则的堆栈使用情况,可以得知是否发生内存泄露。
5 附录
5.1 LCD驱动程序
5.1.1 Mod_tft_st7789.h
/*
* @Description : mod_tft_st7789.h
* @Version : 1
* @Date Author Note
* 2021-03-16 BigAntHome firstVersion
*/
#ifndef __MOD_TFT_ST7789_H__
#define __MOD_TFT_ST7789_H__
#include <Arduino.h>
#define TFT_WITH_CS
#define TFT_MAX_WIDTH 240
#define TFT_MAX_HEIGHT 240
#if defined TFT_WITHOUT_CS
const int32_t TFT_CS = NC;
const int32_t TFT_RST = PB12;
#if defined TFT_WITH_CS
#error multify cs pin mode
#endif
#elif defined TFT_WITH_CS
const int32_t TFT_CS = PB12;
const int32_t TFT_RST = PB14;
#if defined TFT_WITHOUT_CS
#error multify cs pin mode
#endif
#else
#error no define cs pin mode
#endif
const int32_t TFT_PWR = PC6;
const int32_t TFT_DC = PC7;
const int32_t TFT_SCK = PB13;
const int32_t TFT_MISO = PB14;
const int32_t TFT_MOSI = PB15;
enum ROTATION
{
ROTATION_0 = 0,
ROTATION_1,
ROTATION_2,
ROTATION_3
};
enum SPI_MODE
{
SPI_MODE_0 = 0,
SPI_MODE_1,
SPI_MODE_2,
SPI_MODE_3
};
/* 此处未考虑代码的可移植性,直接默认SPI2
* PB13 ------> SPI2_SCK
* PB15 ------> SPI2_MOSI
*/
class Mod_TFT_ST7789
{
private:
bool _enableWriteFlag;
bool _initFlag;
int32_t _pwrPin;
int32_t _dcPin;
int32_t _rstPin;
int32_t _csPin;
uint16_t _width;
uint16_t _height;
uint16_t _width_shift;
uint16_t _height_shift;
enum ROTATION _rotation;
void initSPI(enum SPI_MODE);
void writeCommand(uint8_t cmd);
void writeByte(uint8_t data);
void writeWord(uint16_t data);
public:
Mod_TFT_ST7789();
~Mod_TFT_ST7789();
SPI_HandleTypeDef handleSPI2;
DMA_HandleTypeDef handleSPI2DMATx;
void init(uint16_t width, uint16_t height,enum SPI_MODE);
void setSPISpeed(uint32_t speed);
void setRotation(ROTATION rotation);
void fillScreen(uint16_t color);
void startWrite();
bool setAddrWindow(uint16_t xStart, uint16_t yStart, uint16_t width, uint16_t height);
void writePixels(uint16_t *color,uint16_t buffSize);
void endWrite();
};
extern Mod_TFT_ST7789 tft;
#endif
/*
使用方法
1、调用初始化函数
tft.init(TFT_MAX_WIDTH, TFT_MAX_HEIGHT, SPI_MODE_3);
2、设置屏幕方向
tft.setRotation(ROTATION_2);
3、在区域内填充颜色
tft.startWrite();//使能 写功能
tft.setAddrWindow(0+i,0+j,40,40);//设置填充区域
tft.writePixels(color_buff,40*40); //写入颜色缓存和缓存大小
tft.endWrite();//关闭写功能
*/
5.1.2 Mod_tft_st7789.cpp
/*
* @Description : mod_tft_st7789.c
* @Version : 1
* @Date Author Note
* 2021-03-16 BigAntHome firstVersion
*/
#include "mod_tft_st7789.h"
/* Control Registers and constant codes */
#define ST7789_NOP 0x00
#define ST7789_SWRESET 0x01
#define ST7789_RDDID 0x04
#define ST7789_RDDST 0x09
#define ST7789_SLPIN 0x10
#define ST7789_SLPOUT 0x11
#define ST7789_PTLON 0x12
#define ST7789_NORON 0x13
#define ST7789_INVOFF 0x20
#define ST7789_INVON 0x21
#define ST7789_DISPOFF 0x28
#define ST7789_DISPON 0x29
#define ST7789_CASET 0x2A
#define ST7789_RASET 0x2B
#define ST7789_RAMWR 0x2C
#define ST7789_RAMRD 0x2E
#define ST7789_PTLAR 0x30
#define ST7789_COLMOD 0x3A
#define ST7789_MADCTL 0x36
/**
* Memory Data Access Control Register (0x36H)
* MAP: D7 D6 D5 D4 D3 D2 D1 D0
* param: MY MX MV ML RGB MH - -
*/
/* Page Address Order ('0': Top to Bottom, '1': the opposite) */
#define ST7789_MADCTL_MY 0x80
/* Column Address Order ('0': Left to Right, '1': the opposite) */
#define ST7789_MADCTL_MX 0x40
/* Page/Column Order ('0' = Normal Mode, '1' = Reverse Mode) */
#define ST7789_MADCTL_MV 0x20
/* Line Address Order ('0' = LCD Refresh Top to Bottom, '1' = the opposite) */
#define ST7789_MADCTL_ML 0x10
/* RGB/BGR Order ('0' = RGB, '1' = BGR) */
#define ST7789_MADCTL_RGB 0x00
#define ST7789_PORCHSET 0xB2
#define ST7789_GATECTL 0xB7
#define ST7789_VCOMSET 0xBB
#define ST7789_LCMCTL 0xC0
#define ST7789_VDVVRHEN 0xC2
#define ST7789_VRHSET 0xC3
#define ST7789_VDVSET 0xC4
#define ST7789_FRAMRATE 0xC6
#define ST7789_PWRCTL 0xD0
#define ST7789_POSVOLGAMMA 0xE0
#define ST7789_NEGVOLGAMMA 0xE1
Mod_TFT_ST7789 tft;
extern "C" void SIP2_Init(enum SPI_MODE mode);
Mod_TFT_ST7789::Mod_TFT_ST7789()
{
_enableWriteFlag = false;
_initFlag = false;
_rotation = ROTATION_2;
_width = TFT_MAX_WIDTH;
_height = TFT_MAX_HEIGHT;
_width_shift = 0;
_height_shift = 0;
_pwrPin = TFT_PWR;
_rstPin = TFT_RST;
_dcPin = TFT_DC;
_csPin = TFT_CS;
}
Mod_TFT_ST7789::~Mod_TFT_ST7789()
{
}
void Mod_TFT_ST7789::init(uint16_t width, uint16_t height,enum SPI_MODE mode)
{
this->_width = width;
this->_height = height;
this->initSPI(mode);
digitalWrite(_csPin, LOW);//select chip
digitalWrite(this->_rstPin, LOW);
delay(100);
digitalWrite(this->_rstPin, HIGH);
delay(100);
/************* Start Initial Sequence **********/
writeCommand(ST7789_SLPOUT); // Sleep out
delay(120);
/************* Start Initial Sequence **********/
writeCommand(ST7789_MADCTL);
writeByte(ST7789_MADCTL_RGB);
writeCommand(ST7789_COLMOD); // Interface Pixel Format
writeByte(0x05); // 16bit/pixel
writeCommand(ST7789_PORCHSET); // Porch Setting
writeByte(0x0C); // Default config :power on sequence
writeByte(0x0C);
writeByte(0x00);
writeByte(0x33);
writeByte(0x33);
writeCommand(ST7789_GATECTL); // Gate control
writeByte(0x35); // Default config :power on sequence
writeCommand(ST7789_VCOMSET); // VCOM Setting
writeByte(0x19);
writeCommand(ST7789_LCMCTL); // LCM Control
writeByte(0x2C);
writeCommand(ST7789_VDVVRHEN); // VDV and VRH Command Enable
writeByte(0x01); // Default config :power on sequence
writeCommand(ST7789_VRHSET); // VRH Set
writeByte(0x12); // 4.45+( vcom+vcom offset+vdv)
writeCommand(ST7789_VDVSET); // VDV Set
writeByte(0x20); // Default config :power on sequence
writeCommand(ST7789_FRAMRATE); // Frame Rate Control in Normal Mode
writeByte(0x01);
writeCommand(ST7789_PWRCTL); // Power Control 1
writeByte(0xA4); // Default config :power on sequence
writeByte(0xA1);
writeCommand(ST7789_POSVOLGAMMA); // Positive Voltage Gamma Control
writeByte(0xD0);
writeByte(0x04);
writeByte(0x0D);
writeByte(0x11);
writeByte(0x13);
writeByte(0x2B);
writeByte(0x3F);
writeByte(0x54);
writeByte(0x4C);
writeByte(0x18);
writeByte(0x0D);
writeByte(0x0B);
writeByte(0x1F);
writeByte(0x23);
writeCommand(ST7789_NEGVOLGAMMA); //Negative Voltage Gamma Control
writeByte(0xD0);
writeByte(0x04);
writeByte(0x0C);
writeByte(0x11);
writeByte(0x13);
writeByte(0x2C);
writeByte(0x3F);
writeByte(0x44);
writeByte(0x51);
writeByte(0x2F);
writeByte(0x1F);
writeByte(0x1F);
writeByte(0x20);
writeByte(0x23);
writeCommand(ST7789_INVON); // Display Inversion On
writeCommand(ST7789_DISPON); // Display On
digitalWrite(_csPin, HIGH);//release chip
_initFlag = true;
}
void Mod_TFT_ST7789::setRotation(enum ROTATION rotation)
{
if(_initFlag!= true) return;
digitalWrite(_csPin, LOW);//select chip
writeCommand(ST7789_MADCTL);
switch (rotation)
{
case ROTATION_0:
{
this->_rotation = rotation;
this->_width_shift = 0;
this->_height_shift = 80;
writeByte(ST7789_MADCTL_MX | ST7789_MADCTL_MY | ST7789_MADCTL_RGB);
}
break;
case ROTATION_1:
{
this->_rotation = rotation;
this->_width_shift = 80;
this->_height_shift = 0;
writeByte(ST7789_MADCTL_MY | ST7789_MADCTL_MV | ST7789_MADCTL_RGB);
}
break;
case ROTATION_3:
{
this->_rotation = rotation;
this->_width_shift = 0;
this->_height_shift = 0;
writeByte(ST7789_MADCTL_MX | ST7789_MADCTL_MV | ST7789_MADCTL_RGB);
}
break;
case ROTATION_2:
{
this->_rotation = rotation;
this->_width_shift = 0;
this->_height_shift = 0;
writeByte(ST7789_MADCTL_RGB);
}
default:
break;
}
digitalWrite(_csPin, HIGH);//release chip
}
void Mod_TFT_ST7789::initSPI(enum SPI_MODE mode)
{
pinMode(this->_pwrPin, OUTPUT);
digitalWrite(this->_pwrPin, LOW); // Open the TFT power
pinMode(this->_dcPin, OUTPUT);
pinMode(this->_rstPin, OUTPUT);
pinMode(_csPin, OUTPUT);
digitalWrite(this->_dcPin, HIGH);
digitalWrite(this->_rstPin, HIGH);
digitalWrite(_csPin, HIGH);
SIP2_Init(mode);
}
#ifdef __cplusplus //注意这里的 __cplusplus是C++编译器本身的宏定义。
extern "C"
{
#endif
void SIP2_Init(enum SPI_MODE mode)
{
tft.handleSPI2.Instance = SPI2;
tft.handleSPI2.Init.Mode = SPI_MODE_MASTER;
tft.handleSPI2.Init.Direction = SPI_DIRECTION_2LINES;
tft.handleSPI2.Init.DataSize = SPI_DATASIZE_8BIT;
tft.handleSPI2.Init.NSS = SPI_NSS_SOFT;
tft.handleSPI2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
tft.handleSPI2.Init.FirstBit = SPI_FIRSTBIT_MSB;
tft.handleSPI2.Init.TIMode = SPI_TIMODE_DISABLE;
tft.handleSPI2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
tft.handleSPI2.Init.CRCPolynomial = 7;
tft.handleSPI2.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
tft.handleSPI2.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
switch (mode)
{
case SPI_MODE_0:
{
tft.handleSPI2.Init.CLKPolarity = SPI_POLARITY_LOW;
tft.handleSPI2.Init.CLKPhase = SPI_PHASE_1EDGE;
}
break;
case SPI_MODE_1:
{
tft.handleSPI2.Init.CLKPolarity = SPI_POLARITY_LOW;
tft.handleSPI2.Init.CLKPhase = SPI_PHASE_2EDGE;
}
break;
case SPI_MODE_3:
{
tft.handleSPI2.Init.CLKPolarity = SPI_POLARITY_HIGH;
tft.handleSPI2.Init.CLKPhase = SPI_PHASE_2EDGE;
}
break;
case SPI_MODE_2:
default:
{
tft.handleSPI2.Init.CLKPolarity = SPI_POLARITY_HIGH;
tft.handleSPI2.Init.CLKPhase = SPI_PHASE_1EDGE;
}
break;
}
if (HAL_SPI_Init(&tft.handleSPI2) != HAL_OK)
{
//Error_Handler();
}
}
/* SPI 相关硬件初始化(在HAL_SPI_Init()内部自动调用) */
void HAL_SPI_MspInit(SPI_HandleTypeDef *spiHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if (spiHandle->Instance == SPI2)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel5_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
/* USER CODE END SPI2_MspInit 0 */
/* SPI2 clock enable */
__HAL_RCC_SPI2_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**SPI2 GPIO Configuration
PB13 ------> SPI2_SCK
PB15 ------> SPI2_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_13 | GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* SPI2 DMA Init */
/* SPI2_TX Init */
tft.handleSPI2DMATx.Instance = DMA1_Channel5;
tft.handleSPI2DMATx.Init.Request = DMA_REQUEST_1;
tft.handleSPI2DMATx.Init.Direction = DMA_MEMORY_TO_PERIPH;
tft.handleSPI2DMATx.Init.PeriphInc = DMA_PINC_DISABLE;
tft.handleSPI2DMATx.Init.MemInc = DMA_MINC_ENABLE;
tft.handleSPI2DMATx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
tft.handleSPI2DMATx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
tft.handleSPI2DMATx.Init.Mode = DMA_NORMAL;
tft.handleSPI2DMATx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
if (HAL_DMA_Init(&tft.handleSPI2DMATx) != HAL_OK)
{
//Error_Handler();
}
__HAL_LINKDMA(&(tft.handleSPI2), hdmatx, tft.handleSPI2DMATx);
}
}
void HAL_SPI_MspDeInit(SPI_HandleTypeDef *spiHandle)
{
if (spiHandle->Instance == SPI2)
{
/* Peripheral clock disable */
__HAL_RCC_SPI2_CLK_DISABLE();
/**SPI2 GPIO Configuration
PB13 ------> SPI2_SCK
PB15 ------> SPI2_MOSI
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_13 | GPIO_PIN_15);
/* SPI2 DMA DeInit */
HAL_DMA_DeInit(spiHandle->hdmatx);
}
}
void DMA1_Channel5_IRQHandler(void)
{
HAL_DMA_IRQHandler(&tft.handleSPI2DMATx);
}
#ifdef __cplusplus
}
#endif // __cplusplus
void Mod_TFT_ST7789::writeByte(uint8_t data)
{
//digitalWrite(_csPin, LOW);
HAL_SPI_Transmit_DMA(&handleSPI2, &data, 1);
while (HAL_SPI_STATE_READY != HAL_SPI_GetState(&handleSPI2))
; //wait dma finish
//digitalWrite(_csPin, HIGH);
}
void Mod_TFT_ST7789::writeWord(uint16_t data)
{
writeByte((data >> 8) & 0xFF);
writeByte((data >> 0) & 0xFF);
}
void Mod_TFT_ST7789::writeCommand(uint8_t cmd)
{
digitalWrite(this->_dcPin, LOW);
writeByte(cmd);
digitalWrite(this->_dcPin, HIGH);
}
bool Mod_TFT_ST7789::setAddrWindow(uint16_t xStart, uint16_t yStart, uint16_t width, uint16_t height)
{
if(_initFlag!= true) return false;
uint16_t x_start = xStart + _width_shift;
uint16_t x_end = x_start + width -1;
uint16_t y_start = yStart + _height_shift;
uint16_t y_end = y_start + height -1;
if ((xStart > _width) || ((xStart+width) > this->_width))
{
return false;
}
if ((yStart > _height) || ((yStart+height) > _height))
{
return false;
}
writeCommand(ST7789_CASET); //列地址设置
writeWord(x_start);
writeWord(x_end);
writeCommand(ST7789_RASET); //行地址设置
writeWord(y_start);
writeWord(y_end);
writeCommand(ST7789_RAMWR); //储存器写
return true;
}
void Mod_TFT_ST7789::writePixels(uint16_t *color,uint16_t buffSize)
{
if(_initFlag!= true) return;
const uint32_t MAX_DMA_SIZE = 1024; //必须是偶数
uint8_t buff[MAX_DMA_SIZE];
uint32_t color_byte_size = 0;
uint32_t i, colorbuff_address = 0;
if(_enableWriteFlag != true)return;
//digitalWrite(_csPin, LOW);
color_byte_size = 2 * buffSize;
while (color_byte_size > MAX_DMA_SIZE)
{
for (i = 0; i < MAX_DMA_SIZE; i += 2)
{
buff[i] = (color[i / 2 + colorbuff_address] >> 8) & 0xff;
buff[i + 1] = (color[i / 2 + +colorbuff_address] >> 0) & 0xff;
}
HAL_SPI_Transmit_DMA(&handleSPI2, buff, MAX_DMA_SIZE);
while (HAL_SPI_STATE_READY != HAL_SPI_GetState(&handleSPI2))
; //等待传输完成
color_byte_size -= MAX_DMA_SIZE;
colorbuff_address += MAX_DMA_SIZE / 2;
}
for (i = 0; i < color_byte_size; i += 2)
{
buff[i] = (color[i / 2 + colorbuff_address] >> 8) & 0xff;
buff[i + 1] = (color[i / 2 + colorbuff_address] >> 0) & 0xff;
}
HAL_SPI_Transmit_DMA(&handleSPI2, buff, color_byte_size);
while (HAL_SPI_STATE_READY != HAL_SPI_GetState(&handleSPI2))
; //等待传输完成
//digitalWrite(_csPin, HIGH);
}
void Mod_TFT_ST7789::setSPISpeed(uint32_t speed)
{
if(_initFlag!= true) return;
}
void Mod_TFT_ST7789::fillScreen(uint16_t color)
{
if(_initFlag!= true) return;
if(_enableWriteFlag != true)return;
uint16_t hight = 0;
uint16_t width = 0;
uint16_t colorBuff[TFT_MAX_WIDTH];
for(width = 0;width<_width;width++)
{
colorBuff[width] = color;
}
for(hight= 0;hight<_height;hight++)
{
setAddrWindow(0,hight,_width,1);
writePixels(colorBuff,_width);
}
}
void Mod_TFT_ST7789::startWrite()
{
if(_initFlag!= true) return;
digitalWrite(_csPin, LOW);
_enableWriteFlag = true;
}
void Mod_TFT_ST7789::endWrite()
{
if(_initFlag!= true) return;
digitalWrite(_csPin, HIGH);
_enableWriteFlag = false;
}
5.2 LVGL移植驱动程序测试文件main.cpp
#include <Arduino.h>
#include <lvgl.h>
#include "mod_tft_st7789.h"
static lv_disp_buf_t disp_buf;
static lv_color_t buf[LV_HOR_RES_MAX * 10];
/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p)
{
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
tft.startWrite();//使能 写功能
tft.setAddrWindow(area->x1, area->y1, w, h);//设置填充区域
tft.writePixels(&color_p->full, w * h);//写入颜色缓存和缓存大小
tft.endWrite();//关闭写功能
lv_disp_flush_ready(disp);
}
void setup()
{
// put your setup code here, to run once:
pinMode(LED_BUILTIN, OUTPUT);
tft.init(TFT_MAX_WIDTH, TFT_MAX_HEIGHT, SPI_MODE_3);
tft.setRotation(ROTATION_2);
lv_init();
lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * 10);
/*Initialize the display*/
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = LV_HOR_RES_MAX;
disp_drv.ver_res = LV_VER_RES_MAX;
disp_drv.flush_cb = my_disp_flush;
disp_drv.buffer = &disp_buf;
lv_disp_drv_register(&disp_drv);
}
void loop()
{
lv_task_handler(); /* let the GUI do its work */
delay(5);
}
5.3 Lvgl堆栈泄露测试
void lv_mem_usage_print(lv_mem_monitor_t *mem)
{
Serial.printf("-- lv memory usage --\r\n");
Serial.printf(" -- Total heap size--%d\r\n", mem->total_size);
Serial.printf(" -- free_cnt--%d\r\n", mem->free_cnt);
Serial.printf(" -- Size of available memory--%d\r\n", mem->free_size);
Serial.printf(" -- free_biggest_size--%d\r\n", mem->free_biggest_size);
Serial.printf(" -- used_cnt--%d\r\n", mem->used_cnt);
Serial.printf(" -- Max size of Heap memory used--%d\r\n", mem->max_used);
Serial.printf(" -- Percentage used--%d%%\r\n", mem->used_pct);
Serial.printf(" -- Amount of fragmentation--%d\r\n", mem->frag_pct);
}
void rtu_pages_memory_leak_test(void)
{
Serial.printf("\r\n");
Serial.printf(" * rtu_pages_memory_leak_test * \r\n");
Serial.printf("------------------------------------\r\n");
lv_mem_monitor_t mon;
lv_mem_monitor(&mon);
lv_mem_usage_print(&mon);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)