1. 完整封装并拓展了SD卡文件管理类,支持基本文件管理功能

2. 简单封装了LVGL渲染类,已经封装好了gif渲染功能
3. 修复了硬件厂商提供的驱动的Bug
4. 初步定义了宠物基类的抽象信息
This commit is contained in:
Misaki
2025-09-09 03:40:24 +08:00
parent 28ceb0caf5
commit 4985fee7c2
24 changed files with 996 additions and 22 deletions
@@ -0,0 +1,142 @@
//
// Created by misaki on 2025/9/8.
//
#include "ThreadManager.h"
#include "LVGLRender.h"
#include "I2C_Driver.h"
#include "ST77916.h"
#include "LVGL_Driver.h"
#include <esp_log.h>
LVGLRender* LVGLRender::LVGLRenderInstance = nullptr;
std::mutex LVGLRender::instance_mutex;
uint16_t LVGLRender::fps = 30;
LVGLRender* LVGLRender::getInstance() {
// 双检锁(DCLP),C++11 起 atomic+mutex 组合保证线程安全
LVGLRender* tmp = LVGLRenderInstance;
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(instance_mutex);
tmp = LVGLRenderInstance;
if (tmp == nullptr) {
tmp = new LVGLRender();
LVGLRenderInstance = tmp;
}
}
return tmp;
}
void LVGLRender::LVGL_Update() {
while (true) {
vTaskDelay(LVGL_DELAY_FROM_FPS(LVGLRender::fps));
lv_timer_handler();
}
}
// 构造函数
LVGLRender::LVGLRender() {
ESP_LOGI("LVGL_Render", "LVGL_Render构造函数...初始化媒体驱动...");
I2C_Init();
LCD_Init();
LVGL_Init();
ESP_LOGI("LVGL_Render", "LVGL_Render构造函数...初始化媒体驱动成功...");
ESP_LOGI("LVGL_Render", "LVGL_Render构造函数...创建LVGL心跳...");
ThreadConfig trickConfig;
trickConfig.core_id = 1; // 渲染分配给核1
trickConfig.name = "LVGL_Render_Heartbeat";
trickConfig.priority = 5;
trickConfig.stack_size = 4096; // 给LVGL一个较大的堆栈,避免栈溢出
std::thread tick_thread = ThreadManager::createMemberThread(trickConfig, this, &LVGLRender::LVGL_Update);
tick_thread.detach(); // 线程分离 生命周期跟随主线程结束,线程结束后自动销毁
ESP_LOGI("LVGL_Render", "LVGL_Render构造函数...创建LVGL心跳成功...");
}
LVGLRender::~LVGLRender() {
}
void LVGLRender::log() {
ESP_LOGI("LVGL_Render", "LVGL_Render log...");
}
// 静态:拼出完整路径
std::string LVGLRender::makeFullPath(const std::string& filename)
{
return "/sdcard/" + filename;
}
#include "SDFileManager.h"
std::vector<uint8_t> LVGLRender::readWholeFile(const std::string& path)
{
ESP_LOGI("LVGLRender", "开始读取文件: %s", path.c_str());
// 直接用 SDFileManager 同步读整个文件 TODO: 考虑修改为异步读
std::string content = SDFileManager::getInstance()->readFileSync(path.c_str());
if (content.empty()) {
ESP_LOGE("LVGLRender", "readFileSync 失败或文件为空");
return {};
}
// string -> vector,零拷贝 move
return {content.begin(), content.end()};
}
bool LVGLRender::getGifWH(const uint8_t* raw, uint32_t& w, uint32_t& h)
{
if (!raw || memcmp(raw, "GIF", 3) != 0) {
ESP_LOGE("LVGLRender", "不是合法 GIF 文件头");
return false;
}
// GIF87a/89a 宽高偏移 6~9 字节,小端
w = raw[6] | (raw[7] << 8);
h = raw[8] | (raw[9] << 8);
ESP_LOGI("LVGLRender", "GIF 尺寸: %lu x %lu", w, h);
return true;
}
void LVGLRender::renderGifInternal(const std::vector<uint8_t>& data,
uint32_t w, uint32_t h)
{
// 构造 lv_img_dsc_t —— 数据指针直接指向 vector 内部
static lv_img_dsc_t gif_desc;
gif_desc.header.cf = LV_IMG_CF_RAW_CHROMA_KEYED;
gif_desc.header.always_zero = 0;
gif_desc.header.reserved = 0;
gif_desc.header.w = (lv_coord_t)w;
gif_desc.header.h = (lv_coord_t)h;
gif_desc.data_size = data.size();
gif_desc.data = data.data(); // 指向 vector 内部
// 创建 lv_gif 对象
lv_obj_t* gif = lv_gif_create(lv_scr_act());
lv_gif_set_src(gif, &gif_desc);
lv_obj_center(gif);
ESP_LOGI("LVGLRender", "GIF 已渲染到屏幕");
}
void LVGLRender::RenderGif(const std::string &filename) {
std::string fullPath = makeFullPath(filename);
// 读文件
std::vector<uint8_t> gifBin = readWholeFile(fullPath);
if (gifBin.empty()) return;
// 校验并解析宽高
uint32_t w = 0, h = 0;
if (!getGifWH(gifBin.data(), w, h)) return;
// LVGL 渲染函数
renderGifInternal(gifBin, w, h);
}
@@ -0,0 +1,61 @@
//
// Created by misaki on 2025/9/8.
//
/**
* 本类为单例类,用于实现LVGL的渲染
* 封装了一整套的LVGL渲染流
* 同时兼顾了底层的显示驱动
*/
#pragma once
#define LVGL_DELAY_FROM_FPS(fps) pdMS_TO_TICKS(1000 / (fps))
#include <mutex>
#include <string>
class LVGLRender {
public:
static std::string makeFullPath(const std::string& filename);
public:
static LVGLRender* getInstance();
/**
* 全屏渲染gif动画
* @brief 渲染GIF文件
* @param filename GIF文件路径
*/
void RenderGif(const std::string &filename);
void log();
// gif渲染
private:
/* 同步读整个文件到 vector */
std::vector<uint8_t> readWholeFile(const std::string& path);
/* 从原始数据解析 GIF 宽高 */
bool getGifWH(const uint8_t* raw, uint32_t& w, uint32_t& h);
/* 真正的渲染实现(空壳,先打印日志) */
void renderGifInternal(const std::vector<uint8_t>& data,
uint32_t w, uint32_t h);
private:
explicit LVGLRender(); // 构造函数私有化
~LVGLRender();
LVGLRender(LVGLRender const&) = delete; // 拷贝构造函数私有化
LVGLRender& operator=(LVGLRender const&) = delete;// 赋值运算符私有化
void LVGL_Update(); // 渲染lvgl上下文(持久性线程)
private:
static LVGLRender* LVGLRenderInstance; /// 单例实例
static std::mutex instance_mutex; /// 单例锁
static uint16_t fps; /// 帧率
};