esp32(tftlcd) lvgl外部 SD 卡图片显示,lvgl动画演示,esp32wifi使用(获取时间,天气)

lv_img 就是一个图片控件,它就是根据你传入的图片源来显示你想要的图片,littleVGL
了提供最大的灵活性,它支持如下三种图片源方式:
1) 内部 C 数组,lv_img_dsc_t 结构体来进行描述
2) 外部存储文件,比如 SD 卡或者 U 盘上的图片文件
3) LV_SYMBOL_XXX 形式的图标字体或者文本,此时 lv_img 图片就相当于一个 lv_label 标签控件
如果你确定好图片源之后,就可以通过 lv_img_set_src(img, src)接口来显示此图片,此接口内部会自动判断出 src 是属于哪一种图片源方式,然后选择

相应的解析程序把图片给显示出来.

 

前面的博客使用了第一种方式,然后也将C 数组存储在sd卡,然后读取sd数据,构建 lv_img_dsc_t 结构体来进行描述。但是可能是构建的数据不对,或者

lvgl不太兼容这种方式,显示效果不好,图片有很多噪点,所以想了想,当图片数据在sd卡上时,还是的用第二种方式。

 

2 种外部存储文件图片源方式,它是把图片数据文件放到了外部的储存介质上,比如 SD 卡或者 U 盘上,所以这里你必须得另外用到 littleVGL 的文件系统
模块,然后对于这个外部存储文件的格式这里又可以分为俩大类,一类是图片的最原始格式,.png 文件,它不需要经过任何转换,你只需要把这张.png 图片直

接放到外部的存储介质上就可以了,但是对于这种方式,littleVGL是不能直接支持的,你必须得外加实现 png图片的解析库,
可以参考官方的 https://blog.littlevgl.com/2018-10-05/png_converter 资料,对于另外一类是.bin文件格式,它是需要经过在线图片转换工具的转换,拿到转换

后的.bin 文件后,你就可以调用 lv_img_set_src(img, "S:folder1/my_img.bin")接口把图片给显示出来了,不过前提就是你得先实现 littleVGL 的文件系统驱动哦!

我这里使用第二种方式。

 

当图片作为bin文件读取时,需要esp32运行文件系统,具体可以参考ESP-IDF demo中的spiffs。此外,还需要实现 lvgl 文件系统中的文件操作函数。具体

文件参考下图。

 

 

 Spiffs是一个用于嵌入式目标上的SPI NOR flash设备的文件系统。

Spiffs有以下几个特点:

1、小(嵌入式)目标,没有堆的少量RAM

2、只有大范围的数据(块)才能被删除

3、擦除将把所有块中的位重置为1

4、写操作把1变成0

5、0只能被擦除成1

6、磨损均衡

SPIFFS能做什么:

1、专门为低ram使用而设计

2、使用静态大小的ram缓冲区,与文件的数量无关

3、类可移植操作系统接口:打开、关闭、读、写、查找、统计等

4、它可以在任何NOR闪存上运行,不仅是SPI闪存——理论上也可以在微处理器的嵌入式闪存上运行

5、多个spiffs配置可以在相同的目标上运行—甚至可以在相同的SPI闪存设备上运行

6、实现静态磨损调平(也就是flash的寿命维护)

7、内置文件系统一致性检查

8、高度可配置的

 

用法:

 1,/*Copy this file as "lv_port_fs.h" and set this value to "1" to enable content*/,.c文件类似

2,然后在.h文件申明初始化函数,这个文件类容就这么多啦。

 

 

 3,.c文件,设置sd卡的名字,应该算是

 

 

 4,.c文件,这个结构体定义的话,直接使用ff.h里面的变量,如下一段代码所示:

/* Create a type to store the required data about your file.
 * If you are using a File System library
 * it already should have a File type.
 * For example FatFS has `FIL`. In this case use `typedef FIL file_t`*/
typedef struct {
    /*Add the data you need to store about a file*/
    uint32_t dummy1;
    uint32_t dummy2;
}file_t;

/*Similarly to `file_t` create a type for directory reading too */
typedef struct {
    /*Add the data you need to store about directory reading*/
    uint32_t dummy1;
    uint32_t dummy2;
}dir_t;
    /* Create a type to store the required data about your file.*/
typedef  FIL file_t;

/*Similarly to `file_t` create a type for directory reading too */
typedef  FF_DIR dir_t;

5,.c文件,初始化函数,也就是.h申明的函数的内容,这个函数参考下面这个修改,这里就实现 littleVGL 的文件系统驱动 。然后fs_init()函数的话,就是

初始化sd卡,所以可以不用在这个文件初始化,另外的文件去初始化即可,参考下一段代码,其实就是把sd卡挂载起来就行,怎么搞参考前面的博客。

void lv_fs_if_init(void)
{
    /*----------------------------------------------------
     * Initialize your storage device and File System
     * -------------------------------------------------*/
    fs_init();

    /*---------------------------------------------------
     * Register the file system interface  in LittlevGL
     *--------------------------------------------------*/

     /* Add a simple drive to open images */
    lv_fs_drv_t fs_drv;                         /*A driver descriptor*/
    lv_fs_drv_init(&fs_drv);

    /*Set up fields...*/
    fs_drv.file_size = sizeof(file_t);
    fs_drv.letter = DRIVE_LETTER;
    fs_drv.open_cb = fs_open;
    fs_drv.close_cb = fs_close;
    fs_drv.read_cb = fs_read;
    fs_drv.write_cb = fs_write;
    fs_drv.seek_cb = fs_seek;
    fs_drv.tell_cb = fs_tell;
    fs_drv.free_space_cb = fs_free;
    fs_drv.size_cb = fs_size;
    fs_drv.remove_cb = fs_remove;
    fs_drv.rename_cb = fs_rename;
    fs_drv.trunc_cb = fs_trunc;

    fs_drv.rddir_size = sizeof(dir_t);
    fs_drv.dir_close_cb = fs_dir_close;
    fs_drv.dir_open_cb = fs_dir_open;
    fs_drv.dir_read_cb = fs_dir_read;

    lv_fs_drv_register(&fs_drv);
}
bool SdCard::init()
{

    SPIClass* sd_spi = new SPIClass(HSPI); // another SPI
    if (!SD.begin(15, *sd_spi)) // SD-Card SS pin is 15
    {
        Serial.println("Card Mount Failed");
        return false;
    }
    uint8_t cardType = SD.cardType();

    if (cardType == CARD_NONE)
    {
        Serial.println("No SD card attached");
        return false;
    }

    Serial.print("SD Card Type: ");
    if (cardType == CARD_MMC)
    {
        Serial.println("MMC");
    }
    else if (cardType == CARD_SD)
    {
        Serial.println("SDSC");
    }
    else if (cardType == CARD_SDHC)
    {
        Serial.println("SDHC");
    }
    else
    {
        Serial.println("UNKNOWN");
    }

    uint64_t cardSize = SD.cardSize() / (1024 * 1024);
    Serial.printf("SD Card Size: %lluMB\n", cardSize);
    return true;
}

然后就可以了,在主函数里include这个头文件,littleVGL 的文件系统驱动 算是搞好了,再在lvgl上移值好TFTLCD,主程序初始化好,sd卡,屏幕,文件系统即可使用,将

图片在https://lvgl.io/tools/imageconverter里面转换为.bin文件,然后存储到sd卡里面,即可使用,这个官方在线小工具是可以好多图片自动处理的。

显示图片参考下面代码:

       int len = sprintf(buf, "S:/hold/%04d.bin", frame_id++);
       buf[len] = 0;
       lv_img_set_src(img1,buf);
       if (frame_id == 311) frame_id = 0;

**********************

所有代码:

1,display.h,display.c

这里就是实现了TFTLCD移植到LVGL里面,然后TFTLCD的使用以及移植到LVGL参考前面的博客。

#include "display.h"
#include <TFT_eSPI.h>

/*
TFT pins should be set in path/to/Arduino/libraries/TFT_eSPI/User_Setups/Setup24_ST7789.h
*/
TFT_eSPI tft = TFT_eSPI();

static lv_disp_buf_t disp_buf;
static lv_color_t buf[LV_HOR_RES_MAX * 10];
 
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.pushColors(&color_p->full, w * h, true);
    tft.endWrite();

    lv_disp_flush_ready(disp);
}

void Display::init()
{
    lv_init();

    tft.begin(); /* TFT init */
    //tft.setRotation(4); /* mirror */

    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 = 240;
    disp_drv.ver_res = 240;
    disp_drv.flush_cb = my_disp_flush;
    disp_drv.buffer = &disp_buf;
    lv_disp_drv_register(&disp_drv);
}

void Display::routine()
{
    lv_task_handler();
}
#ifndef DISPLAY_H
#define DISPLAY_H

#include <lvgl.h>


class Display
{
private:


public:
    void init();
    void routine();
};

#endif

2,sd_card.h,sd_card.c

这里就是实现sd卡的初始化,具体怎么弄参考前面的博客,然后里面还有一些esp32操作sd卡的函数,跟前面博客中读取sd卡差不多,应该很好理解,我也是复制的别人的。

#include "sd_card.h"


bool SdCard::init()
{

    SPIClass* sd_spi = new SPIClass(HSPI); // another SPI
    if (!SD.begin(15, *sd_spi)) // SD-Card SS pin is 15
    {
        Serial.println("Card Mount Failed");
        return false;
    }
    uint8_t cardType = SD.cardType();

    if (cardType == CARD_NONE)
    {
        Serial.println("No SD card attached");
        return false;
    }

    Serial.print("SD Card Type: ");
    if (cardType == CARD_MMC)
    {
        Serial.println("MMC");
    }
    else if (cardType == CARD_SD)
    {
        Serial.println("SDSC");
    }
    else if (cardType == CARD_SDHC)
    {
        Serial.println("SDHC");
    }
    else
    {
        Serial.println("UNKNOWN");
    }

    uint64_t cardSize = SD.cardSize() / (1024 * 1024);
    Serial.printf("SD Card Size: %lluMB\n", cardSize);
    return true;
}



void SdCard::listDir(const char* dirname, uint8_t levels)
{
    Serial.printf("Listing directory: %s\n", dirname);

    File root = SD.open(dirname);
    if (!root)
    {
        Serial.println("Failed to open directory");
        return;
    }
    if (!root.isDirectory())
    {
        Serial.println("Not a directory");
        return;
    }

    File file = root.openNextFile();
    while (file)
    {
        if (file.isDirectory())
        {
            Serial.print("  DIR : ");
            Serial.println(file.name());
            if (levels)
            {
                listDir(file.name(), levels - 1);
            }
        }
        else
        {
            Serial.print("  FILE: ");
            Serial.print(file.name());
            Serial.print("  SIZE: ");
            Serial.println(file.size());
        }
        file = root.openNextFile();
    }
}

void SdCard::createDir(const char* path)
{
    Serial.printf("Creating Dir: %s\n", path);
    if (SD.mkdir(path))
    {
        Serial.println("Dir created");
    }
    else
    {
        Serial.println("mkdir failed");
    }
}

void SdCard::removeDir(const char* path)
{
    Serial.printf("Removing Dir: %s\n", path);
    if (SD.rmdir(path))
    {
        Serial.println("Dir removed");
    }
    else
    {
        Serial.println("rmdir failed");
    }
}

void SdCard::readFile(const char* path)
{
    Serial.printf("Reading file: %s\n", path);

    File file = SD.open(path);
    if (!file)
    {
        Serial.println("Failed to open file for reading");
        return;
    }

    Serial.print("Read from file: ");
    while (file.available())
    {
        Serial.write(file.read());
    }
    file.close();
}

String SdCard::readFileLine(const char* path, int num = 1)
{
    Serial.printf("Reading file: %s line: %d\n", path, num);

    File file = SD.open(path);
    if (!file)
    {
        return ("Failed to open file for reading");
    }

    char* p = buf;
    while (file.available())
    {
        char c = file.read();
        if (c == '\n')
        {
            num--;
            if (num == 0)
            {
                *(p++) = '\0';
                String s(buf);
                s.trim();
                return s;
            }
        }
        else if (num == 1)
        {
            *(p++) = c;
        }
    }
    file.close();

    return  String("error parameter!");
}

void SdCard::writeFile(const char* path, const char* message)
{
    Serial.printf("Writing file: %s\n", path);

    File file = SD.open(path, FILE_WRITE);
    if (!file)
    {
        Serial.println("Failed to open file for writing");
        return;
    }
    if (file.print(message))
    {
        Serial.println("File written");
    }
    else
    {
        Serial.println("Write failed");
    }
    file.close();
}

void SdCard::appendFile(const char* path, const char* message)
{
    Serial.printf("Appending to file: %s\n", path);

    File file = SD.open(path, FILE_APPEND);
    if (!file)
    {
        Serial.println("Failed to open file for appending");
        return;
    }
    if (file.print(message))
    {
        Serial.println("Message appended");
    }
    else
    {
        Serial.println("Append failed");
    }
    file.close();
}

void SdCard::renameFile(const char* path1, const char* path2)
{
    Serial.printf("Renaming file %s to %s\n", path1, path2);
    if (SD.rename(path1, path2))
    {
        Serial.println("File renamed");
    }
    else
    {
        Serial.println("Rename failed");
    }
}

void SdCard::deleteFile(const char* path)
{
    Serial.printf("Deleting file: %s\n", path);
    if (SD.remove(path))
    {
        Serial.println("File deleted");
    }
    else
    {
        Serial.println("Delete failed");
    }
}

void SdCard::readBinFromSd(const char* path, uint8_t* buf)
{
    File file = SD.open(path);
    size_t len = 0;
    if (file)
    {
        len = file.size();
        size_t flen = len;

        while (len)
        {
            size_t toRead = len;
            if (toRead > 512)
            {
                toRead = 512;
            }
            file.read(buf, toRead);
            len -= toRead;
        }

        file.close();
    }
    else
    {
        Serial.println("Failed to open file for reading");
    }
}

void SdCard::writeBinToSd(const char* path, uint8_t* buf)
{
    File file = SD.open(path, FILE_WRITE);
    if (!file)
    {
        Serial.println("Failed to open file for writing");
        return;
    }

    size_t i;
    for (i = 0; i < 2048; i++)
    {
        file.write(buf, 512);
    }
    file.close();
}


void SdCard::fileIO(const char* path)
{
    File file = SD.open(path);
    static uint8_t buf[512];
    size_t len = 0;
    uint32_t start = millis();
    uint32_t end = start;
    if (file)
    {
        len = file.size();
        size_t flen = len;
        start = millis();
        while (len)
        {
            size_t toRead = len;
            if (toRead > 512)
            {
                toRead = 512;
            }
            file.read(buf, toRead);
            len -= toRead;
        }
        end = millis() - start;
        Serial.printf("%u bytes read for %u ms\n", flen, end);
        file.close();
    }
    else
    {
        Serial.println("Failed to open file for reading");
    }


    file = SD.open(path, FILE_WRITE);
    if (!file)
    {
        Serial.println("Failed to open file for writing");
        return;
    }

    size_t i;
    start = millis();
    for (i = 0; i < 2048; i++)
    {
        file.write(buf, 512);
    }
    end = millis() - start;
    Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end);
    file.close();
}
#ifndef SD_CARD_H
#define SD_CARD_H

#include "FS.h"
#include "SD.h"
#include "SPI.h"
 
class SdCard
{
private:
    char buf[128];

public:
    bool init();

    void listDir(  const char* dirname, uint8_t levels);

    void createDir( const char* path);

    void removeDir(  const char* path);

    void readFile(  const char* path);

    String readFileLine( const char* path, int num);

    void writeFile(  const char* path, const char* message);

    void appendFile(  const char* path, const char* message);

    void renameFile(  const char* path1, const char* path2);

    void deleteFile(  const char* path);

    void readBinFromSd(const char* path, uint8_t* buf);

    void writeBinToSd(const char* path, uint8_t* buf);

    void fileIO(  const char* path);

};

extern SdCard tf;

#endif

3,lv_port_fatfs.h,lv_port_fatfs.c

这个就是实现LVGL文件系统,按照上面介绍更改好就可以。

/**
 * @file lv_fs_fatfs.c
 * For ESP32
 */

 /*********************
  *      INCLUDES
  *********************/
#include "lv_port_fatfs.h"


  /*********************
   *      DEFINES
   *********************/
#define DRIVE_LETTER 'S'
   /**********************
    *      TYPEDEFS
    **********************/

    /* Create a type to store the required data about your file.*/
typedef  FIL file_t;

/*Similarly to `file_t` create a type for directory reading too */
typedef  FF_DIR dir_t;

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void fs_init(void);

static lv_fs_res_t fs_open(lv_fs_drv_t* drv, void* file_p, const char* path, lv_fs_mode_t mode);
static lv_fs_res_t fs_close(lv_fs_drv_t* drv, void* file_p);
static lv_fs_res_t fs_read(lv_fs_drv_t* drv, void* file_p, void* buf, uint32_t btr, uint32_t* br);
static lv_fs_res_t fs_write(lv_fs_drv_t* drv, void* file_p, const void* buf, uint32_t btw, uint32_t* bw);
static lv_fs_res_t fs_seek(lv_fs_drv_t* drv, void* file_p, uint32_t pos);
static lv_fs_res_t fs_size(lv_fs_drv_t* drv, void* file_p, uint32_t* size_p);
static lv_fs_res_t fs_tell(lv_fs_drv_t* drv, void* file_p, uint32_t* pos_p);
static lv_fs_res_t fs_remove(lv_fs_drv_t* drv, const char* path);
static lv_fs_res_t fs_trunc(lv_fs_drv_t* drv, void* file_p);
static lv_fs_res_t fs_rename(lv_fs_drv_t* drv, const char* oldname, const char* newname);
static lv_fs_res_t fs_free(lv_fs_drv_t* drv, uint32_t* total_p, uint32_t* free_p);
static lv_fs_res_t fs_dir_open(lv_fs_drv_t* drv, void* dir_p, const char* path);
static lv_fs_res_t fs_dir_read(lv_fs_drv_t* drv, void* dir_p, char* fn);
static lv_fs_res_t fs_dir_close(lv_fs_drv_t* drv, void* dir_p);

/**********************
 *  STATIC VARIABLES
 **********************/

 /**********************
  *      MACROS
  **********************/

  /**********************
   *   GLOBAL FUNCTIONS
   **********************/

void lv_fs_if_init(void)
{
    /*----------------------------------------------------
     * Initialize your storage device and File System
     * -------------------------------------------------*/
    fs_init();

    /*---------------------------------------------------
     * Register the file system interface  in LittlevGL
     *--------------------------------------------------*/

     /* Add a simple drive to open images */
    lv_fs_drv_t fs_drv;                         /*A driver descriptor*/
    lv_fs_drv_init(&fs_drv);

    /*Set up fields...*/
    fs_drv.file_size = sizeof(file_t);
    fs_drv.letter = DRIVE_LETTER;
    fs_drv.open_cb = fs_open;
    fs_drv.close_cb = fs_close;
    fs_drv.read_cb = fs_read;
    fs_drv.write_cb = fs_write;
    fs_drv.seek_cb = fs_seek;
    fs_drv.tell_cb = fs_tell;
    fs_drv.free_space_cb = fs_free;
    fs_drv.size_cb = fs_size;
    fs_drv.remove_cb = fs_remove;
    fs_drv.rename_cb = fs_rename;
    fs_drv.trunc_cb = fs_trunc;

    fs_drv.rddir_size = sizeof(dir_t);
    fs_drv.dir_close_cb = fs_dir_close;
    fs_drv.dir_open_cb = fs_dir_open;
    fs_drv.dir_read_cb = fs_dir_read;

    lv_fs_drv_register(&fs_drv);
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

 /* Initialize your Storage device and File system. */
static void fs_init(void)
{
    ///* Initialisation de la carte SD */
    //Serial.print(F("Init SD card... "));

    //SPIClass* sd_spi = new SPIClass(HSPI); // another SPI
    //if (!SD.begin(15, *sd_spi)) // SD-Card SS pin is 15
    //{
    //    Serial.println("Card Mount Failed");
    //    return;
    //}
}

/**
 * Open a file
 * @param drv pointer to a driver where this function belongs
 * @param file_p pointer to a file_t variable
 * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt)
 * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR
 * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
 */
static lv_fs_res_t fs_open(lv_fs_drv_t* drv, void* file_p, const char* path, lv_fs_mode_t mode)
{
    uint8_t flags = 0;

    if (mode == LV_FS_MODE_WR) flags = FA_WRITE | FA_OPEN_ALWAYS;
    else if (mode == LV_FS_MODE_RD) flags = FA_READ;
    else if (mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = FA_READ | FA_WRITE | FA_OPEN_ALWAYS;

    FRESULT res = f_open((file_t*)file_p, path, flags);

    if (res == FR_OK)
    {
        f_lseek((file_t*)file_p, 0);
        return LV_FS_RES_OK;
    }
    else
    {
        return LV_FS_RES_UNKNOWN;
    }
}


/**
 * Close an opened file
 * @param drv pointer to a driver where this function belongs
 * @param file_p pointer to a file_t variable. (opened with lv_ufs_open)
 * @return LV_FS_RES_OK: no error, the file is read
 *         any error from lv_fs_res_t enum
 */
static lv_fs_res_t fs_close(lv_fs_drv_t* drv, void* file_p)
{
    f_close((file_t*)file_p);
    return LV_FS_RES_OK;
}

/**
 * Read data from an opened file
 * @param drv pointer to a driver where this function belongs
 * @param file_p pointer to a file_t variable.
 * @param buf pointer to a memory block where to store the read data
 * @param btr number of Bytes To Read
 * @param br the real number of read bytes (Byte Read)
 * @return LV_FS_RES_OK: no error, the file is read
 *         any error from lv_fs_res_t enum
 */
static lv_fs_res_t fs_read(lv_fs_drv_t* drv, void* file_p, void* buf, uint32_t btr, uint32_t* br)
{
    FRESULT res = f_read((file_t*)file_p, buf, btr, (UINT*)br);
    if (res == FR_OK) return LV_FS_RES_OK;
    else return LV_FS_RES_UNKNOWN;
}

/**
 * Write into a file
 * @param drv pointer to a driver where this function belongs
 * @param file_p pointer to a file_t variable
 * @param buf pointer to a buffer with the bytes to write
 * @param btr Bytes To Write
 * @param br the number of real written bytes (Bytes Written). NULL if unused.
 * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
 */
static lv_fs_res_t fs_write(lv_fs_drv_t* drv, void* file_p, const void* buf, uint32_t btw, uint32_t* bw)
{
    FRESULT res = f_write((file_t*)file_p, buf, btw, (UINT*)bw);
    if (res == FR_OK) return LV_FS_RES_OK;
    else return LV_FS_RES_UNKNOWN;
}

/**
 * Set the read write pointer. Also expand the file size if necessary.
 * @param drv pointer to a driver where this function belongs
 * @param file_p pointer to a file_t variable. (opened with lv_ufs_open )
 * @param pos the new position of read write pointer
 * @return LV_FS_RES_OK: no error, the file is read
 *         any error from lv_fs_res_t enum
 */
static lv_fs_res_t fs_seek(lv_fs_drv_t* drv, void* file_p, uint32_t pos)
{
    f_lseek((file_t*)file_p, pos);
    return LV_FS_RES_OK;
}

/**
 * Give the size of a file bytes
 * @param drv pointer to a driver where this function belongs
 * @param file_p pointer to a file_t variable
 * @param size pointer to a variable to store the size
 * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
 */
static lv_fs_res_t fs_size(lv_fs_drv_t* drv, void* file_p, uint32_t* size_p)
{
    (*size_p) = f_size(((file_t*)file_p));
    return LV_FS_RES_OK;
}

/**
 * Give the position of the read write pointer
 * @param drv pointer to a driver where this function belongs
 * @param file_p pointer to a file_t variable.
 * @param pos_p pointer to to store the result
 * @return LV_FS_RES_OK: no error, the file is read
 *         any error from lv_fs_res_t enum
 */
static lv_fs_res_t fs_tell(lv_fs_drv_t* drv, void* file_p, uint32_t* pos_p)
{
    *pos_p = f_tell(((file_t*)file_p));
    return LV_FS_RES_OK;
}

/**
 * Delete a file
 * @param drv pointer to a driver where this function belongs
 * @param path path of the file to delete
 * @return  LV_FS_RES_OK or any error from lv_fs_res_t enum
 */
static lv_fs_res_t fs_remove(lv_fs_drv_t* drv, const char* path)
{
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;

    /* Add your code here*/

    return res;
}

/**
 * Truncate the file size to the current position of the read write pointer
 * @param drv pointer to a driver where this function belongs
 * @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_fs_open )
 * @return LV_FS_RES_OK: no error, the file is read
 *         any error from lv_fs_res_t enum
 */
static lv_fs_res_t fs_trunc(lv_fs_drv_t* drv, void* file_p)
{
    f_sync((file_t*)file_p);           /*If not syncronized fclose can write the truncated part*/
    f_truncate((file_t*)file_p);
    return LV_FS_RES_OK;
}

/**
 * Rename a file
 * @param drv pointer to a driver where this function belongs
 * @param oldname path to the file
 * @param newname path with the new name
 * @return LV_FS_RES_OK or any error from 'fs_res_t'
 */
static lv_fs_res_t fs_rename(lv_fs_drv_t* drv, const char* oldname, const char* newname)
{

    FRESULT res = f_rename(oldname, newname);

    if (res == FR_OK) return LV_FS_RES_OK;
    else return LV_FS_RES_UNKNOWN;
}

/**
 * Get the free and total size of a driver in kB
 * @param drv pointer to a driver where this function belongs
 * @param letter the driver letter
 * @param total_p pointer to store the total size [kB]
 * @param free_p pointer to store the free size [kB]
 * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
 */
static lv_fs_res_t fs_free(lv_fs_drv_t* drv, uint32_t* total_p, uint32_t* free_p)
{
    lv_fs_res_t res = LV_FS_RES_NOT_IMP;

    /* Add your code here*/

    return res;
}

/**
 * Initialize a 'fs_read_dir_t' variable for directory reading
 * @param drv pointer to a driver where this function belongs
 * @param dir_p pointer to a 'fs_read_dir_t' variable
 * @param path path to a directory
 * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
 */
static lv_fs_res_t fs_dir_open(lv_fs_drv_t* drv, void* dir_p, const char* path)
{
    FRESULT res = f_opendir((dir_t*)dir_p, path);
    if (res == FR_OK) return LV_FS_RES_OK;
    else return LV_FS_RES_UNKNOWN;
}

/**
 * Read the next filename form a directory.
 * The name of the directories will begin with '/'
 * @param drv pointer to a driver where this function belongs
 * @param dir_p pointer to an initialized 'fs_read_dir_t' variable
 * @param fn pointer to a buffer to store the filename
 * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
 */
static lv_fs_res_t fs_dir_read(lv_fs_drv_t* drv, void* dir_p, char* fn)
{
    FRESULT res;
    FILINFO fno;
    fn[0] = '\0';

    do
    {
        res = f_readdir((dir_t*)dir_p, &fno);
        if (res != FR_OK) return LV_FS_RES_UNKNOWN;

        if (fno.fattrib & AM_DIR)
        {
            fn[0] = '/';
            strcpy(&fn[1], fno.fname);
        }
        else strcpy(fn, fno.fname);

    } while (strcmp(fn, "/.") == 0 || strcmp(fn, "/..") == 0);

    return LV_FS_RES_OK;
}

/**
 * Close the directory reading
 * @param drv pointer to a driver where this function belongs
 * @param dir_p pointer to an initialized 'fs_read_dir_t' variable
 * @return LV_FS_RES_OK or any error from lv_fs_res_t enum
 */
static lv_fs_res_t fs_dir_close(lv_fs_drv_t* drv, void* dir_p)
{
    f_closedir((dir_t*)dir_p);
    return LV_FS_RES_OK;
}
/**
 * @file lv_port_fs_templ.h
 *
 */

 /*Copy this file as "lv_port_fs.h" and set this value to "1" to enable content*/
#if 1

#ifndef LV_PORT_FS_TEMPL_H
#define LV_PORT_FS_TEMPL_H

#ifdef __cplusplus
extern "C" {
#endif

    /*********************
     *      INCLUDES
     *********************/
#include "lvgl.h"
#include "ff.h" //FatFs(通用FAT文件系统模块)
     /*********************
      *      DEFINES
      *********************/
    void lv_fs_if_init(void);
    /**********************
     *      TYPEDEFS
     **********************/

     /**********************
      * GLOBAL PROTOTYPES
      **********************/

      /**********************
       *      MACROS
       **********************/


#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /*LV_PORT_FS_TEMPL_H*/

#endif /*Disable/Enable content*/

4,caiya_gui.h,caiya_gui.c,文件就是LVGL gui的相关设置了,跟前面博客使用的差不多,这里面耶加入了动画,注释应该可以看懂动画怎么用。LV_IMG_DECLARE(myimage1); LV_IMG_DECLARE(myimage2); LV_IMG_DECLARE(myimage3);是声明一些图片的c数组数据,也就是使用LVGL显示图片的第一种方式,数据在另外一个.c文件,就不贴了,数据太多,也都是在线转换的,参考前面的博客。

 

/*********************
 *      INCLUDES
 *********************/
#include "caiya_gui.h"
#include "stdio.h"

lv_obj_t* scr1;
lv_obj_t* scr2;
lv_obj_t* label2;
lv_obj_t* label3;
lv_obj_t* label4;
lv_obj_t* label5;
lv_obj_t* label6;
lv_obj_t* label7;
lv_obj_t* img1;
LV_IMG_DECLARE(myimage1);
LV_IMG_DECLARE(myimage2);
LV_IMG_DECLARE(myimage3);

static void ofs_y_anim(void * img, int32_t v)
{
lv_obj_align(img, NULL, LV_ALIGN_IN_TOP_LEFT, 200,v);
}

void set_init_gui(void){
    /*屏幕1*/
  static lv_style_t style;
  lv_style_init(&style);
  lv_style_set_border_width(&style, LV_STATE_DEFAULT, 4);
  lv_style_set_bg_color(&style, LV_STATE_DEFAULT, LV_COLOR_WHITE);
  lv_style_set_border_color(&style, LV_STATE_DEFAULT, LV_COLOR_BLUE);//设置控件边框颜色
  lv_obj_t* bar = lv_bar_create(lv_scr_act(), NULL);//在默认屏上创建obj对象
  lv_obj_add_style(bar, LV_LABEL_PART_MAIN, &style);
  lv_obj_set_size(bar, 200, 30);
  lv_obj_align(bar, NULL, LV_ALIGN_CENTER, 0, 0);
  lv_bar_set_anim_time(bar, 2000);
  lv_bar_set_value(bar, 100, LV_ANIM_ON); 

  static lv_style_t style1;
  lv_style_init(&style1);
  lv_style_set_border_width(&style1, LV_STATE_DEFAULT, 10);
  lv_style_set_border_color(&style1, LV_STATE_DEFAULT, LV_COLOR_BLUE);//设置控件边框颜色
  lv_style_set_pad_top(&style1, LV_STATE_DEFAULT, 20);//填充(Padding)可在边缘的内侧设置空间。
  lv_style_set_pad_bottom(&style1, LV_STATE_DEFAULT, 20);
  lv_style_set_pad_left(&style1, LV_STATE_DEFAULT, 20);
  lv_style_set_pad_right(&style1, LV_STATE_DEFAULT, 20);
  lv_style_set_text_font(&style1,LV_STATE_DEFAULT,&lv_font_montserrat_24);//12-14-16-18-20-22-24
  lv_style_set_text_color(&style1, LV_STATE_DEFAULT, LV_COLOR_RED);//设置字体颜色
  lv_obj_t * label = lv_label_create(lv_scr_act(), NULL);
  lv_obj_add_style(label, LV_LABEL_PART_MAIN, &style1);
  lv_label_set_long_mode(label, LV_LABEL_LONG_SROLL);
  lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
  lv_obj_set_pos(label, 20, 10);
  lv_obj_set_size(label, 200, 70);
  lv_label_set_text(label, "let us begining......");

  lv_obj_t * imglogo = lv_img_create(lv_scr_act(), NULL);
  lv_img_set_src(imglogo, &myimage2);
  /*功能:对象对齐
    参数:
      obj     操作对象
      obj_ref   参考对象,为 NULL 时,将对齐父对象。
      LV_ALIGN_...  对齐类型
      x_shift   对齐之后x轴偏移像素点
      y_shift   对齐之后y轴偏移像素点*/
  lv_obj_align(imglogo, NULL, LV_ALIGN_IN_TOP_LEFT, 45, 180);
}

void set_p_gui(void){
     /*屏幕2*/
  scr1 = lv_obj_create(NULL, NULL); // 创建新屏幕但未加载到显示
  img1 = lv_img_create(scr1, NULL);
  lv_img_set_src(img1, &myimage1);
  lv_obj_set_pos(scr1, 0, 0);
  lv_obj_set_size(scr1, 240, 240);
  /*****************************************/ 
  lv_obj_t *act_obj = lv_scr_act(); // 获取当前活动页
    if (act_obj == scr1)
        return;
    lv_obj_clean(act_obj); // 清空此前页面
    lv_scr_load_anim(scr1, LV_SCR_LOAD_ANIM_FADE_ON,500,0, false);
}

void set_mt_gui(void){
    /*屏幕3*/
  static lv_style_t style11;
  lv_style_init(&style11);
  lv_style_set_text_font(&style11,LV_STATE_DEFAULT,&lv_font_montserrat_20);//12-14-16-18-20-22-24
  lv_style_set_text_color(&style11, LV_STATE_DEFAULT, LV_COLOR_GREEN);//设置字体颜色
  scr2 = lv_obj_create(NULL, NULL);   // 创建新屏幕但未加载到显示
  label2 = lv_label_create(scr2, NULL);        // 创建label
  lv_obj_add_style(label2, LV_LABEL_PART_MAIN, &style11);
  lv_label_set_long_mode(label2, LV_LABEL_LONG_SROLL);
  lv_label_set_align(label2, LV_LABEL_ALIGN_LEFT);
  lv_obj_set_pos(label2, 4, 4);
  lv_obj_set_size(label2, 150, 35);
  lv_label_set_text(label2, "city:Chengdu");

  label3 = lv_label_create(scr2, NULL);        
  lv_obj_add_style(label3, LV_LABEL_PART_MAIN, &style11);
  lv_label_set_long_mode(label3, LV_LABEL_LONG_SROLL);
  lv_label_set_align(label3, LV_LABEL_ALIGN_LEFT);
  lv_obj_set_pos(label3, 4, 36);
  lv_obj_set_size(label3, 150, 35);
  lv_label_set_text(label3, "textDay:Sunny");

  label4 = lv_label_create(scr2, NULL);        
  lv_obj_add_style(label4, LV_LABEL_PART_MAIN, &style11);
  lv_label_set_long_mode(label4, LV_LABEL_LONG_SROLL);
  lv_label_set_align(label4, LV_LABEL_ALIGN_LEFT);
  lv_obj_set_pos(label4, 4, 71);
  lv_obj_set_size(label4, 150, 35);
  lv_label_set_text(label4, "temp high:27");

  label5 = lv_label_create(scr2, NULL);       
  lv_obj_add_style(label5, LV_LABEL_PART_MAIN, &style11);
  lv_label_set_long_mode(label5, LV_LABEL_LONG_SROLL);
  lv_label_set_align(label5, LV_LABEL_ALIGN_LEFT);
  lv_obj_set_pos(label5, 4, 106);
  lv_obj_set_size(label5, 150, 35);
  lv_label_set_text(label5, "temp low:15");

  label6 = lv_label_create(scr2, NULL);       
  lv_obj_add_style(label6, LV_LABEL_PART_MAIN, &style11);
  lv_label_set_long_mode(label6, LV_LABEL_LONG_SROLL);
  lv_label_set_align(label6, LV_LABEL_ALIGN_LEFT);
  lv_obj_set_pos(label6, 4, 141);
  lv_obj_set_size(label6, 150, 35);
  lv_label_set_text(label6, "humi:95");

  label7 = lv_label_create(scr2, NULL);       
  lv_obj_add_style(label7, LV_LABEL_PART_MAIN, &style11);
  lv_label_set_long_mode(label7, LV_LABEL_LONG_SROLL);
  lv_label_set_align(label7, LV_LABEL_ALIGN_LEFT);
  lv_obj_set_pos(label7, 4, 176);
  lv_obj_set_size(label7, 150, 35);
  lv_label_set_text(label7, "time: 11:40:49");
  
  /* Now create the actual image */
    lv_obj_t * img = lv_img_create(scr2, NULL);
    lv_img_set_src(img, &myimage3);
    lv_obj_align(img, NULL, LV_ALIGN_IN_TOP_LEFT, 200,20);

    /*You can automatically change the value of a variable between a start and an end value using animations. The animation
will happen by periodically calling an ”animator” function with the corresponding value parameter.*/
      lv_anim_t ma;
    lv_anim_init(&ma);
    /*Set the "animator" function*/
    lv_anim_set_var(&ma, img);
    /*Set the "animator" function*/
    lv_anim_set_exec_cb(&ma, (lv_anim_exec_xcb_t)ofs_y_anim);
    /*Set start and end values. E.g. 0, 150*/
    lv_anim_set_values(&ma, 0, 100);
    /*Length of the animation [ms]*/
    lv_anim_set_time(&ma, 1000);
    /*Number of repetitions. Default is 1. LV_ANIM_REPEAT_INFINIT for infinite repetition*/
    lv_anim_set_repeat_count(&ma, LV_ANIM_REPEAT_INFINITE);
    /*Play the animation backward too with this duration. Default is 0 (disabled) [ms]*/
    lv_anim_set_playback_time(&ma, 1000);
    /*Start the animation*/
    lv_anim_start(&ma);
    /****************************************************/
    lv_obj_t *act_obj = lv_scr_act(); // 获取当前活动页
    if (act_obj == scr2)
        return;
    lv_obj_clean(act_obj); // 清空此前页面
    lv_scr_load_anim(scr2, LV_SCR_LOAD_ANIM_FADE_ON,500,3000, false);
}



int frame_id = 0;
char buf[100];
void display_photo()
{
       int len = sprintf(buf, "S:/hold/%04d.bin", frame_id++);
    buf[len] = 0;
    lv_img_set_src(img1, buf);
    lv_obj_align(img1, NULL, LV_ALIGN_CENTER, 0, 0); 
    if (frame_id == 6) frame_id = 0; 
}

void photo_gui_del(void)
{
    if (NULL != scr1)
    {
        lv_obj_clean(scr1); // 清空此前页面
        scr1 = NULL;
    }
}

void mt_gui_del(void)
{
    if (NULL != scr2)
    {
        lv_obj_clean(scr2); // 清空此前页面
        scr2 = NULL;
    }
}

void mt_process(USER_DATA mtdata){
   if(mtdata.age==1) {
       lv_label_set_text_fmt(label2, "city:%s", mtdata.getcity); 
       lv_label_set_text_fmt(label3, "textDay:%s", mtdata.getweather); 
       lv_label_set_text_fmt(label4, "temp high:%s", mtdata.gethigh); 
       lv_label_set_text_fmt(label5, "temp low:%s", mtdata.getlow); 
       lv_label_set_text_fmt(label6, "humi:%s", mtdata.gethumi); 
       lv_label_set_text_fmt(label7, "time: %s", mtdata.gettime); 
        }
}
#ifndef CAIYA_GUI_H
#define CAIYA_GUI_H

#ifdef __cplusplus
extern "C" {  //extern "C"表示编译生成的内部符号名使用C约定。
#endif

#include "lvgl.h"


    
#define USER_EVENT_START 20
#define USER_EVENT_1 (USER_EVENT_START+1) //用户自定义事件 1
//构建一个用户自定义数据结构体,当然了,如果你的用户数据简单,可以不用结构体
typedef struct{
    char getcity[32];
    char getweather[64];
    char gethigh[32];
    char getlow[32];
    char gethumi[32];
    char gettime[32];
    unsigned char age;
}USER_DATA;



//extern lv_img_dsc_t screen_buffer;


void set_init_gui(void);
void set_p_gui(void);
void set_mt_gui(void);

void display_photo();
void photo_gui_del(void);
void mt_gui_del(void);
void mt_process(USER_DATA mtdata);

//static void btn_event_cb(lv_obj_t * obj,lv_event_t event);


#ifdef __cplusplus
} /* extern "C" */
#endif

#endif  

5,主程序,实现功能。

 

#include "display.h"
#include "sd_card.h"
#include "lv_port_fatfs.h"
#include "caiya_gui.h"
#include <NTPClient.h>
#include <WiFi.h>
#include <WiFiUdp.h>
#include <HTTPClient.h> 
#include <ArduinoJson.h>

/*** Component objects ***/
Display screen;
SdCard tf;


unsigned char time1flag=0;//定时器使用标记寄存器
hw_timer_t *timer = NULL;//定义hw_timer_t 结构类型的指针

int flag = 1;
unsigned char playflag=0;//播放视频标记位

USER_DATA user_data = {{"nodata"},{"nodata"},{"nodata"},{"nodata"},{"nodata"},{"nodata"},0};//初始化一下


//  函数名称:onTimer()
//  函数功能:中断服务的功能,它必须是一个返回void(空)且没有输入参数的函数
//  为使编译器将代码分配到IRAM内,中断处理程序应该具有 IRAM_ATTR 属性
void IRAM_ATTR TimerEvent()
{
  static int timercnt=0;//用于标记到点刷新图片
  if((timercnt==1000)&&(playflag==1)){
    time1flag=1;
    timercnt=0;
    }
  else if(playflag==1)timercnt++;
  else timercnt=0;
}

/*wifi*/
char mytime[32];
WiFiUDP ntpUDP;
void getTime(){
  NTPClient timeClient(ntpUDP, "ntp1.aliyun.com", 60 * 60 * 8, 30 * 60 * 1000);

  timeClient.begin();
  timeClient.update();
  
  String time_str = timeClient.getFormattedTime();
  strcpy(mytime,time_str.c_str());
}

const char *host = "api.seniverse.com";
const char *privateKey = "Scxilbk8z2ntaRPtN";
const char *city = "chengdu";
const char *language = "en";
struct WetherData
{
    char city[32];
    char weather[64];
    char high[32];
    char low[32];
    char humi[32];
};
struct WetherData weatherdata = {0};
void getWeather(){
  WiFiClient client;
  if (!client.connect(host, 80))
    {
        Serial.println("Connect host failed!");
        return;
    }
    Serial.println("host Conected!");
  String getUrl = "/v3/weather/daily.json?key=";
    getUrl += privateKey;
    getUrl += "&location=";
    getUrl += city;
    getUrl += "&language=";
    getUrl += language;
    client.print(String("GET ") + getUrl + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n");
//    Serial.println("Get send");

    char endOfHeaders[] = "\r\n\r\n";
    bool ok = client.find(endOfHeaders);
//    if (!ok)
//    {
//        Serial.println("No response or invalid response!");
//    }
//    Serial.println("Skip headers");

    String line="";

    line += client.readStringUntil('\n'); 
 
    DynamicJsonDocument doc(1400);
    DeserializationError error = deserializeJson(doc, line);
    if (error)
    {
        Serial.println("deserialize json failed");
        return;
    }
    Serial.println("deserialize json success");
    if(line.length()<300){
      Serial.println("limit");
    }
    else{ 
    strcpy(weatherdata.city, doc["results"][0]["location"]["name"].as<const char*>());
    strcpy(weatherdata.weather, doc["results"][0]["daily"][0]["text_day"].as<const char*>());
    strcpy(weatherdata.high, doc["results"][0]["daily"][0]["high"].as<const char*>());
    strcpy(weatherdata.low, doc["results"][0]["daily"][0]["low"].as<const char*>());
    strcpy(weatherdata.humi, doc["results"][0]["daily"][0]["humidity"].as<const char*>());
    }
    client.stop();
    strcpy(user_data.getcity,weatherdata.city);
    strcpy(user_data.getweather,weatherdata.weather);
    strcpy(user_data.gethigh,weatherdata.high);
    strcpy(user_data.getlow,weatherdata.low);
    strcpy(user_data.gethumi,weatherdata.humi);
    strcpy(user_data.gettime,mytime);
}
bool sdokflag=true;//sd卡挂载成功标记
void setup()
{
    Serial.begin(115200);
    pinMode(2,OUTPUT);
    digitalWrite(2,1);
    pinMode(0,INPUT_PULLUP);
    /*** Init screen ***/
    screen.init();
    
    /*** Init micro SD-Card ***/
    sdokflag=tf.init();
    if(sdokflag)Serial.println("sd ok");
    lv_fs_if_init();
      
    /*** Inflate GUI objects ***/
    set_init_gui();
    
    timer = timerBegin(0, 80, true);
    timerAttachInterrupt(timer, &TimerEvent, true);
    timerAlarmWrite(timer, 5000, true);
    timerAlarmEnable(timer); //  使能定时器

    WiFi.begin("漂亮丫头是大笨猪","lovexx1997andcaiya");
}
unsigned char wififlag=0;//wifi连接成功标记位
int aa;
void loop()
{
    if(((WiFi.status()!=WL_CONNECTED)==true)&&(wififlag==0)){
        wififlag=1;
        Serial.println("wifi ok");
    }  
    screen.routine();/* let the GUI do its work */
    if((time1flag==1)&&(playflag==1)&&(sdokflag==true)){
      aa=millis();
      display_photo();
      delay(300);
      time1flag=0;
      Serial.println(millis()-aa);
    }
    else if(sdokflag==false)Serial.println("sd not ok");
    /*按键处理*/
    if(digitalRead(0)==0)
      {
        while(digitalRead(0)==0);
        digitalWrite(2,!digitalRead(2));
        if(flag==1)flag=0;
        else flag++;
        if(flag==1){
          playflag=0;
          photo_gui_del();
          set_mt_gui();
          delay(300);
          if(wififlag==1){
            getTime();
            getWeather();
          }
          else Serial.println("wifi not ok");
          user_data.age=(unsigned char)flag; 
          mt_process(user_data);
        }
        else if(flag==0){
          mt_gui_del();
          set_p_gui(); 
          playflag=1;
        }    
      }     
}

 

posted @ 2022-03-19 12:06  菜芽caiya  阅读(5507)  评论(0编辑  收藏  举报