LVGL移植到基于VSCode的platformio工程的STM32L476 Nucleo开发板(st7789显示驱动)

2023-05-16

 

目录

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(使用前将#替换为@)

LVGL移植到基于VSCode的platformio工程的STM32L476 Nucleo开发板(st7789显示驱动) 的相关文章

随机推荐